2022-11-24 05:04:14 +07:00
import Victor from "https://cdn.skypack.dev/victor@1.1.0" ;
2022-11-22 05:40:30 +07:00
2022-11-26 04:38:23 +07:00
'use strict' ;
2022-11-01 06:19:17 +07:00
/ * *
* @ author Arsen Mirzaev Tatyano - Muradovich < arsen @ mirzaev . sexy >
* /
class graph {
2022-12-03 12:05:28 +07:00
// Идентификатор HTML-элемента-оболочки (instanceof HTMLElement)
# id = 'graph' ;
// Прочитать идентификатор HTML-элемента-оболочки (instanceof HTMLElement)
get id ( ) {
return this . # id ;
}
// Классы которые будут записаны в HTML-элементы
classes = {
node : {
shell : [ 'nodes' ] ,
element : [ 'node' ] ,
onmouseenter : [ 'onmouseenter' ] ,
title : [ 'title' ] ,
2022-12-03 14:13:30 +07:00
cover : [ 'cover' ] ,
description : {
both : [ 'description' ] ,
hidden : [ 'hidden' ] ,
shown : [ 'shown' ]
} ,
close : {
both : [ 'close' ] ,
hidden : [ 'hidden' ] ,
shown : [ 'shown' ]
} ,
2022-12-03 12:05:28 +07:00
wrappers : {
both : [ 'wrapper' ] ,
left : [ 'left' ] ,
right : [ 'right' ]
2022-12-03 14:13:30 +07:00
}
2022-12-03 12:05:28 +07:00
} ,
connection : {
shell : [ 'connections' ] ,
element : [ 'connection' ]
}
} ;
2022-11-01 06:19:17 +07:00
// Оболочка (instanceof HTMLElement)
2022-12-03 12:05:28 +07:00
# shell = document . getElementById ( this . id ) ;
2022-11-22 05:40:30 +07:00
get shell ( ) {
return this . # shell ;
}
2022-11-01 06:19:17 +07:00
// Реестр узлов
2022-11-22 06:43:57 +07:00
# nodes = new Set ( ) ;
get nodes ( ) {
return this . # nodes ;
}
2022-11-01 06:19:17 +07:00
// Реестр соединений
2022-11-22 06:43:57 +07:00
# connections = new Set ( ) ;
get connections ( ) {
return this . # connections ;
}
2022-11-01 06:19:17 +07:00
2022-12-04 02:13:10 +07:00
// Статус активации функций взаимодействий узлов
actions = {
collision : false ,
pushing : true ,
pulling : true
}
2022-11-01 06:19:17 +07:00
// Класс узла
2022-11-22 06:43:57 +07:00
# node = class node {
2022-11-01 06:19:17 +07:00
// Реестр входящих соединений
2022-11-22 06:43:57 +07:00
# inputs = new Set ( ) ;
2022-12-04 02:13:10 +07:00
// Прочитать реестр входящих соединений
2022-11-22 05:40:30 +07:00
get inputs ( ) {
return this . # inputs ;
}
2022-11-01 06:19:17 +07:00
// Реестр исходящих соединений
2022-11-22 06:43:57 +07:00
# outputs = new Set ( ) ;
2022-12-04 02:13:10 +07:00
// Прочитать реестр исходящих соединений
2022-11-22 05:40:30 +07:00
get outputs ( ) {
return this . # outputs ;
}
// Оператор
# operator ;
2022-12-04 02:13:10 +07:00
// Прочитать оператора
2022-11-22 05:40:30 +07:00
get operator ( ) {
return this . # operator ;
}
2022-12-03 12:05:28 +07:00
// HTML-элемент-оболочка
# shell ;
// Прочитать HTML-элемент-оболочка
get shell ( ) {
return this . # shell ;
}
2022-11-22 05:40:30 +07:00
// HTML-элемент
# element ;
2022-12-03 12:05:28 +07:00
// Прочитать HTML-элемент
2022-11-22 05:40:30 +07:00
get element ( ) {
return this . # element ;
}
// Наблюдатель
# observer = null ;
2022-12-04 02:13:10 +07:00
// Прочитать наблюдатель
2022-11-22 05:40:30 +07:00
get observer ( ) {
return this . # observer ;
}
// Реестр запрещённых к изменению параметров
2022-12-03 12:29:29 +07:00
# block = new Set ( [ 'events' ] ) ;
2022-12-04 02:13:10 +07:00
// Прочитать реестр запрещённых к изменению параметров
2022-11-22 05:40:30 +07:00
get block ( ) {
return this . # block ;
}
2022-11-01 06:19:17 +07:00
2022-11-22 18:31:26 +07:00
// Диаметр узла
2022-11-01 06:19:17 +07:00
# diameter = 100 ;
2022-12-04 02:13:10 +07:00
// Прочитать диаметр узла
2022-11-22 05:40:30 +07:00
get diameter ( ) {
return this . # diameter ;
}
2022-11-22 18:31:26 +07:00
// Степень увеличения диаметра
# increase = 0 ;
2022-12-04 02:13:10 +07:00
// Прочитать степень увеличения диаметра
2022-11-22 05:40:30 +07:00
get increase ( ) {
return this . # increase ;
}
2022-11-22 18:31:26 +07:00
// Величина степени увеличения диаметра
# addition = 12 ;
2022-12-04 02:13:10 +07:00
// Прочитать величину степени увеличения диаметра
2022-11-22 18:31:26 +07:00
get addition ( ) {
return this . # addition ;
}
2022-12-04 02:13:10 +07:00
// Величина степени увеличения притягивания и отталкивания
# shift = 0 ;
// Прочитать величину степени увеличения притягивания и отталкивания
get shift ( ) {
return this . # shift ;
}
// Глобальный счётчик итераций
2022-11-23 23:50:13 +07:00
iteration = 0 ;
2022-12-04 02:13:10 +07:00
// Ограничение максимального количества всех итераций
2022-11-23 23:50:13 +07:00
limit = 3000 ;
2022-12-04 02:13:10 +07:00
/ * *
* Обработка событий
*
* max - максимум итераций в процессе
* current - текущая итерация в процессе
* flow - максимум итераций в потоке
* /
2022-11-22 18:31:26 +07:00
actions = {
2022-12-04 02:13:10 +07:00
collision : {
max : 100 ,
current : 0 ,
flow : {
medium : 30 ,
hard : 300
}
} ,
pushing : {
max : 100 ,
current : 0 ,
flow : {
medium : 30 ,
hard : 300
}
} ,
pulling : {
max : 100 ,
current : 0 ,
flow : {
medium : 30 ,
hard : 300
}
}
2022-11-22 18:31:26 +07:00
} ;
2022-12-04 14:15:17 +07:00
/ * *
* Столкновения
*
* Реестр узлов которые обработали столкновения с целевым узлом в потоке
* /
collisions = new Set ( ) ;
/ * *
* Отталкивания
*
* Реестр узлов которые обработали столкновения с целевым узлом в потоке
* /
pushings = new Set ( ) ;
/ * *
* Притягивания
*
* Реестр узлов которые обработали притягивание с целевым узлом в потоке
* /
pullings = new Set ( ) ;
2022-12-04 02:13:10 +07:00
/ * *
* Конструктор узла
*
* @ param { object } operator Инстанция оператора ( графика )
* @ param { object } data Данные для генерации
* /
2022-11-22 06:43:57 +07:00
constructor ( operator , data ) {
2022-12-03 12:05:28 +07:00
// Запись в свойство
this . # operator = operator ;
// Инициализация ссылки на ядро
const _this = this ;
// Инициализация HTML-элемента-оболочки узлов
if ( ( this . # shell = document . getElementById ( this . # operator . id + '_nodes' ) ) instanceof HTMLElement ) ;
else {
// Н е найден HTML-элемент-оболочки узлов
// Инициализация HTML-элемента-оболочки узлов
const shell = document . createElement ( 'section' ) ;
shell . id = this . # operator . id + '_nodes' ;
shell . classList . add ( ... this . # operator . classes . node . shell ) ;
// Запись в документ
this . # operator . shell . appendChild ( shell ) ;
// Запись в свойство
this . # shell = shell ;
}
2022-11-22 18:31:26 +07:00
// Инициализация HTML-элемента узла
2022-12-03 12:29:29 +07:00
const article = document . createElement ( 'article' ) ;
2022-12-03 12:05:28 +07:00
article . id = this . # operator . id + '_node_' + this . # operator . nodes . size ;
2022-12-03 12:08:21 +07:00
if ( typeof data . color === 'string' ) article . classList . add ( data . color ) ;
article . classList . add ( ... _this . operator . classes . node . element ) ;
2022-12-03 12:29:29 +07:00
if ( typeof data . href === 'string' ) {
2022-11-24 14:24:23 +07:00
article . href = data . href ;
}
2022-11-26 23:40:46 +07:00
// Запись анимации "выделение обводкой" (чтобы не проигрывалась при открытии страницы)
article . onmouseenter = fn => {
// Запись класса с анимацией
2022-12-03 12:05:28 +07:00
article . classList . add ( ... _this . # operator . classes . node . onmouseenter ) ;
2022-11-26 23:40:46 +07:00
} ;
2022-11-26 04:38:23 +07:00
// Инициализация заголовка
2022-12-03 12:29:29 +07:00
const title = document . createElement ( 'h4' ) ;
2022-12-03 12:05:28 +07:00
title . classList . add ( ... _this . # operator . classes . node . title ) ;
2022-11-26 05:04:45 +07:00
title . innerText = data . title ? ? '' ;
2022-11-01 06:19:17 +07:00
2022-11-24 14:24:23 +07:00
// Запись в оболочку
article . appendChild ( title ) ;
2022-11-26 04:38:23 +07:00
// Инициализация описания
2022-12-03 12:29:29 +07:00
const description = document . createElement ( 'div' ) ;
2022-12-03 14:13:30 +07:00
description . classList . add ( ... _this . # operator . classes . node . description . both , ... _this . # operator . classes . node . description . hidden ) ;
2022-11-26 05:04:45 +07:00
if ( typeof data . popup === 'string' ) description . title = data . popup ;
2022-11-01 06:19:17 +07:00
2022-11-26 23:40:46 +07:00
// Запись анимации "выделение обводкой" (чтобы не проигрывалась при открытии страницы)
description . onmouseenter = fn => {
// Запись класса с анимацией
2022-12-03 12:05:28 +07:00
description . classList . add ( ... _this . # operator . classes . node . onmouseenter ) ;
2022-11-26 23:40:46 +07:00
} ;
2022-11-26 04:38:23 +07:00
// Запись блокировки открытия описания в случае, если был перемещён узел
title . onmousedown = ( onmousedown ) => {
2022-11-26 06:18:53 +07:00
// Инициализация координат
let x = onmousedown . pageX ;
let y = onmousedown . pageY ;
// Запись события открытия описания
title . onclick = ( onclick ) => {
// Отображение описания
2022-12-03 12:05:28 +07:00
_this . show ( ) ;
2022-11-26 06:18:53 +07:00
// Удаление событий
title . onclick = title . onmousemove = null ;
// Реинициализация координат
x = onclick . pageX ;
y = onclick . pageY ;
// Удаление иконки курсора
title . style . cursor = null ;
return true ;
}
2022-11-26 04:38:23 +07:00
title . onmousemove = ( onmousemove ) => {
// К у р с о р сдвинут более чем на 15 пикселей?
2022-11-26 06:18:53 +07:00
if ( Math . abs ( x - onmousemove . pageX ) > 15 || Math . abs ( y - onmousemove . pageY ) > 15 ) {
2022-11-26 04:38:23 +07:00
// Запись иконки курсора
title . style . cursor = 'grabbing' ;
2022-11-26 06:18:53 +07:00
// Запись события для переноса узла
title . onclick = ( onclick ) => {
2022-11-26 04:38:23 +07:00
// Удаление событий
2022-11-26 06:18:53 +07:00
title . onclick = title . onmousemove = null ;
// Реинициализация координат
x = onclick . pageX ;
y = onclick . pageY ;
2022-11-26 04:38:23 +07:00
// Удаление иконки курсора
title . style . cursor = null ;
2022-11-26 06:18:53 +07:00
return false ;
2022-11-26 04:38:23 +07:00
}
2022-11-26 06:18:53 +07:00
} else {
// Запись события открытия описания
title . onclick = ( onclick ) => {
// Отображение описания
2022-12-03 12:05:28 +07:00
_this . show ( ) ;
2022-11-26 04:38:23 +07:00
2022-11-26 06:18:53 +07:00
// Удаление событий
title . onclick = title . onmousemove = null ;
// Реинициализация координат
x = onclick . pageX ;
y = onclick . pageY ;
// Удаление иконки курсора
title . style . cursor = null ;
return true ;
} ;
}
2022-11-26 04:38:23 +07:00
}
} ;
// Запись в оболочку
article . appendChild ( description ) ;
// Инициализация левой фигуры для обёртки текста
2022-12-03 12:29:29 +07:00
const left = document . createElement ( 'span' ) ;
2022-12-03 12:05:28 +07:00
left . classList . add ( ... _this . # operator . classes . node . wrappers . both , ... _this . # operator . classes . node . wrappers . left ) ;
2022-11-26 04:38:23 +07:00
// Запись в описание
description . appendChild ( left ) ;
// Инициализация правой фигуры для обёртки текста
2022-12-03 12:29:29 +07:00
const right = document . createElement ( 'span' ) ;
2022-12-03 12:05:28 +07:00
right . classList . add ( ... _this . # operator . classes . node . wrappers . both , ... _this . # operator . classes . node . wrappers . right ) ;
2022-11-26 04:38:23 +07:00
// Запись в описание
description . appendChild ( right ) ;
// Инициализация ссылки на источник
2022-12-03 12:29:29 +07:00
const a = document . createElement ( 'a' ) ;
2022-11-26 05:04:45 +07:00
if ( typeof data . link === 'object' && typeof data . link . name === 'string' ) a . innerText = data . link . name ;
if ( typeof data . link === 'object' && typeof data . link . href === 'string' ) a . href = data . link . href ;
if ( typeof data . link === 'object' && typeof data . link . class === 'object' ) a . classList . add ( ... data . link . class ) ;
if ( typeof data . link === 'object' && typeof data . link . title === 'string' ) a . title = data . link . title ;
2022-11-26 04:38:23 +07:00
// Блокировка событий браузера (чтобы не мешать переноса узла)
a . ondragstart = a . onselectstart = fn => { return false } ;
// Запись блокировки перехода по ссылке в случае, если был перемещён узел
a . onmousedown = ( onmousedown ) => {
2022-11-26 06:18:53 +07:00
// Инициализация координат
let x = onmousedown . pageX ;
let y = onmousedown . pageY ;
// Запись события открытия описания
a . onclick = ( onclick ) => {
// Удаление событий
a . onclick = a . onmousemove = null ;
// Реинициализация координат
x = onclick . pageX ;
y = onclick . pageY ;
// Удаление иконки курсора
a . style . cursor = null ;
return true ;
}
2022-11-26 04:38:23 +07:00
a . onmousemove = ( onmousemove ) => {
// К у р с о р сдвинут более чем на 15 пикселей?
2022-11-26 06:18:53 +07:00
if ( Math . abs ( x - onmousemove . pageX ) > 15 || Math . abs ( y - onmousemove . pageY ) > 15 ) {
2022-11-26 04:38:23 +07:00
// Запись иконки курсора
a . style . cursor = 'grabbing' ;
2022-11-26 06:18:53 +07:00
// Запись события для переноса узла
a . onclick = ( onclick ) => {
2022-11-26 04:38:23 +07:00
// Удаление событий
2022-11-26 06:18:53 +07:00
a . onclick = a . onmousemove = null ;
// Реинициализация координат
x = onclick . pageX ;
y = onclick . pageY ;
2022-11-26 04:38:23 +07:00
// Удаление иконки курсора
a . style . cursor = null ;
2022-11-26 06:18:53 +07:00
return false ;
2022-11-26 04:38:23 +07:00
}
2022-11-26 06:18:53 +07:00
} else {
// Запись события открытия описания
a . onclick = ( onclick ) => {
// Удаление событий
a . onclick = a . onmousemove = null ;
// Реинициализация координат
x = onclick . pageX ;
y = onclick . pageY ;
2022-11-26 04:38:23 +07:00
2022-11-26 06:18:53 +07:00
// Удаление иконки курсора
a . style . cursor = null ;
return true ;
} ;
}
2022-11-26 04:38:23 +07:00
}
} ;
// Запись в описание
description . appendChild ( a ) ;
// Запись текста в описание
2022-12-03 12:29:29 +07:00
const text = document . createElement ( 'p' ) ;
2022-11-26 05:04:45 +07:00
if ( typeof data . description === 'string' ) text . innerText = data . description ;
2022-11-26 04:38:23 +07:00
// Запись в оболочку
description . appendChild ( text ) ;
if (
2022-12-03 12:29:29 +07:00
typeof data . cover === 'string'
2022-11-26 04:38:23 +07:00
) {
2022-11-26 05:04:45 +07:00
// Получено изображение-обложка
2022-11-26 04:38:23 +07:00
2022-11-26 05:04:45 +07:00
// Инициализация изображения-обложки
2022-12-03 12:29:29 +07:00
const cover = document . createElement ( 'img' ) ;
2022-11-26 05:04:45 +07:00
if ( typeof cover . src === 'string' ) cover . src = data . cover ;
if ( typeof cover . alt === 'string' ) cover . alt = data . title ;
2022-12-03 12:05:28 +07:00
cover . classList . add ( ... _this . # operator . classes . node . cover ) ;
2022-11-26 04:38:23 +07:00
// Запись в описание
description . appendChild ( cover ) ;
2022-11-24 14:24:23 +07:00
}
if (
2022-12-03 12:29:29 +07:00
typeof data . append === 'HTMLCollection' ||
typeof data . append === 'HTMLElement'
2022-11-24 14:24:23 +07:00
) {
2022-11-26 04:38:23 +07:00
// Получены другие HTML-элементы
2022-11-01 06:19:17 +07:00
// Запись в оболочку
2022-11-24 14:24:23 +07:00
article . appendChild ( data . append ) ;
2022-11-01 06:19:17 +07:00
}
2022-11-26 05:50:15 +07:00
// Инициализация кнопки закрытия
const close = document . createElement ( 'i' ) ;
2022-12-03 14:13:30 +07:00
close . classList . add ( ... _this . # operator . classes . node . close . both , ... _this . # operator . classes . node . close . hidden ) ;
2022-11-26 05:50:15 +07:00
2022-11-26 22:48:25 +07:00
// Запись блокировки закрытия в случае, если был перемещён узел
close . onmousedown = ( onmousedown ) => {
// Инициализация координат
let x = onmousedown . pageX ;
let y = onmousedown . pageY ;
// Запись события открытия описания
close . onclick = ( onclick ) => {
// Скрытие описания
2022-12-03 12:05:28 +07:00
_this . hide ( ) ;
2022-11-26 22:48:25 +07:00
// Удаление событий
close . onclick = close . onmousemove = null ;
// Реинициализация координат
x = onclick . pageX ;
y = onclick . pageY ;
// Удаление иконки курсора
close . style . cursor = null ;
return true ;
}
close . onmousemove = ( onmousemove ) => {
// К у р с о р сдвинут более чем на 15 пикселей?
if ( Math . abs ( x - onmousemove . pageX ) > 15 || Math . abs ( y - onmousemove . pageY ) > 15 ) {
// Запись иконки курсора
close . style . cursor = 'grabbing' ;
// Запись события для переноса узла
close . onclick = ( onclick ) => {
// Удаление событий
close . onclick = close . onmousemove = null ;
// Реинициализация координат
x = onclick . pageX ;
y = onclick . pageY ;
// Удаление иконки курсора
close . style . cursor = null ;
return false ;
}
} else {
// Запись события открытия описания
close . onclick = ( onclick ) => {
// Скрытие описания
2022-12-03 12:05:28 +07:00
_this . hide ( ) ;
2022-11-26 22:48:25 +07:00
// Удаление событий
close . onclick = close . onmousemove = null ;
// Реинициализация координат
x = onclick . pageX ;
y = onclick . pageY ;
// Удаление иконки курсора
close . style . cursor = null ;
return true ;
} ;
}
}
} ;
2022-11-26 05:50:15 +07:00
// Запись в оболочку
article . appendChild ( close ) ;
2022-11-22 18:31:26 +07:00
// Запись в документ
2022-12-03 12:05:28 +07:00
this . # shell . appendChild ( article ) ;
2022-11-22 18:31:26 +07:00
2022-11-26 04:38:23 +07:00
// Запись диаметра описания в зависимости от размера заголовка (чтобы вмещался)
description . style . width = description . style . height = ( a . offsetWidth === 0 ? 50 : a . offsetWidth ) * 3 + 'px' ;
// Запись отступа заголовка (чтобы был по центру описания)
a . style . left = description . offsetWidth / 2 - a . offsetWidth / 2 + 'px' ;
2022-11-26 05:50:15 +07:00
/ * *
* Показать описание
* /
2022-12-03 14:13:30 +07:00
this . show = fn => {
// Отображение описания
description . classList . add ( ... _this . # operator . classes . node . description . shown ) ;
description . classList . remove ( ... _this . # operator . classes . node . description . hidden ) ;
// Отображение кнопки закрытия
close . classList . add ( ... _this . # operator . classes . node . close . shown ) ;
close . classList . remove ( ... _this . # operator . classes . node . close . hidden ) ;
2022-11-26 05:50:15 +07:00
// Сдвиг кнопки закрытия описания
close . style . top = close . style . right = - ( ( ( description . offsetWidth - article . offsetWidth ) / 4 ) + description . offsetWidth / 8 ) + 'px' ;
// Размер кнопки закрытия описания
close . style . scale = 1.3 ;
// Прозрачность кнопки закрытия описания (плавное появление)
close . style . opacity = 1 ;
// Расположение выше остальных узлов
article . style . zIndex = close . style . zIndex = 1000 ;
2022-12-04 02:13:10 +07:00
// Инициализация сдвига отталкивания и притяжения соединённых узлов
_this . # shift = description . offsetWidth - article . offsetWidth ;
2022-12-04 14:15:17 +07:00
// С б р о с данных потока
_this . reset ( ) ;
2022-12-04 02:13:10 +07:00
// Обработка сдвига
2022-12-04 14:15:17 +07:00
_this . move ( null , null , true ) ;
2022-11-26 05:50:15 +07:00
}
/ * *
* Скрыть описание
* /
2022-12-03 14:13:30 +07:00
this . hide = fn => {
// Скрытие описания
description . classList . add ( ... _this . # operator . classes . node . description . hidden ) ;
description . classList . remove ( ... _this . # operator . classes . node . description . shown ) ;
// Скрытие кнопки закрытия
close . classList . add ( ... _this . # operator . classes . node . close . hidden ) ;
close . classList . remove ( ... _this . # operator . classes . node . close . shown ) ;
2022-11-26 05:50:15 +07:00
// Удаление всех изменённых аттрибутов
close . style . top = close . style . right = article . style . zIndex = close . style . zIndex = close . style . scale = close . style . opacity = null ;
2022-12-04 02:13:10 +07:00
// Деинициализация сдвига отталкивания и притяжения соединённых узлов
_this . # shift = 0 ;
2022-12-04 14:15:17 +07:00
// С б р о с данных потока
_this . reset ( ) ;
2022-12-04 02:13:10 +07:00
// Обработка сдвига
2022-12-04 14:15:17 +07:00
_this . move ( null , null , true ) ;
2022-11-26 05:50:15 +07:00
}
2022-11-01 06:19:17 +07:00
// Запись в свойство
2022-11-24 14:24:23 +07:00
this . # element = article ;
2022-11-22 05:40:30 +07:00
2022-11-01 06:19:17 +07:00
// Инициализация
this . init ( ) ;
2022-11-22 18:31:26 +07:00
2022-12-04 14:15:17 +07:00
// С б р о с данных потока
this . reset ( ) ;
2022-11-22 18:31:26 +07:00
// Перемещение
this . move (
2022-12-03 12:05:28 +07:00
this . # operator . shell . offsetWidth / 2 -
2022-11-26 04:38:23 +07:00
this . # diameter / 2 +
( 0.5 - Math . random ( ) ) * 500 ,
2022-12-03 12:05:28 +07:00
this . # operator . shell . offsetHeight / 2 -
2022-11-26 04:38:23 +07:00
this . # diameter / 2 +
2022-12-04 14:15:17 +07:00
( 0.5 - Math . random ( ) ) * 500
2022-11-22 18:31:26 +07:00
) ;
2022-11-01 06:19:17 +07:00
}
2022-11-22 05:40:30 +07:00
init ( increase = 0 ) {
2022-11-22 18:31:26 +07:00
// Запись в свойство
this . # increase = increase ;
2022-11-22 05:40:30 +07:00
2022-11-22 18:31:26 +07:00
// Инициализация диаметра
if ( this . # increase !== 0 )
this . # diameter += this . # addition * * this . # increase ;
// Инициализация размера HTML-элемента
2022-11-01 06:19:17 +07:00
this . element . style . width = this . element . style . height =
2022-12-03 12:29:29 +07:00
this . # diameter + 'px' ;
2022-11-22 05:40:30 +07:00
2022-11-26 04:38:23 +07:00
// Инициализация описания
const description = this . element . getElementsByClassName ( 'description' ) [ 0 ] ;
// Запись отступа описания (чтобы был по центру узла)
description . style . marginLeft = description . style . marginTop = ( this . element . offsetWidth - description . offsetWidth ) / 2 + 'px' ;
2022-11-22 05:40:30 +07:00
// Инициализация ссылки на ядро
const _this = this ;
// Инициализация наблюдателя
this . # observer = new MutationObserver ( function ( mutations ) {
for ( const mutation of mutations ) {
2022-12-03 12:29:29 +07:00
if ( mutation . type === 'attributes' ) {
2022-11-22 18:31:26 +07:00
// Перехвачено изменение аттрибута
2022-11-22 05:40:30 +07:00
// Запись параметра в инстанцию бегущей строки
_this . configure ( mutation . attributeName ) ;
}
}
} ) ;
// Активация наблюдения
this . observer . observe ( this . element , {
attributes : true ,
attributeOldValue : true
} ) ;
}
2022-12-04 02:13:10 +07:00
/ * *
* Переместить узел
*
* @ param { * } x Координата X ( относительно левого верхнего края )
* @ param { * } y Координата Y ( относительно левого верхнего края )
* @ param { * } hard Увеличить количество итераций для процесса ?
* /
2022-12-04 14:15:17 +07:00
move ( x , y , hard = false ) {
// console.log(this.element.id, this.pushings && !this.pushings.has(this), this.pullings && !this.pullings.has(this));
console . log ( this . element . id , this . pushings , this . pullings ) ;
2022-12-04 02:13:10 +07:00
// Проверка входящих параметров
if ( typeof x !== 'number' ) x = this . element . getAttribute ( 'data-x' ) ? ? 0 ;
2022-12-04 14:15:17 +07:00
else {
// Запись отступа
this . element . style . left = x + 'px' ;
2022-12-04 02:13:10 +07:00
2022-12-04 14:15:17 +07:00
// Запись аттрибута с координатой
this . element . setAttribute ( 'data-x' , x ) ;
}
2022-11-22 05:40:30 +07:00
2022-12-04 14:15:17 +07:00
if ( typeof y !== 'number' ) y = this . element . getAttribute ( 'data-y' ) ? ? 0 ;
else {
// Запись отступа
this . element . style . top = y + 'px' ;
2022-11-22 05:40:30 +07:00
2022-12-04 14:15:17 +07:00
// Запись аттрибута с координатой
this . element . setAttribute ( 'data-y' , y ) ;
}
2022-11-23 23:50:13 +07:00
// Обработка столкновений
2022-12-04 14:15:17 +07:00
if ( this . collisions && ! this . collisions . has ( this ) ) this . collision ( this . # operator . nodes , hard ) ;
2022-11-23 23:50:13 +07:00
// Инициализация буфера реестра узлов
2022-12-03 12:05:28 +07:00
const registry = new Set ( this . # operator . nodes ) ;
2022-11-23 23:50:13 +07:00
2022-12-04 14:15:17 +07:00
if ( this . pushings && ! this . pushings . has ( this ) ) {
2022-11-23 23:50:13 +07:00
// Активно отталкивание
2022-12-04 14:15:17 +07:00
for ( const connection of this . outputs ) {
// Перебор исходящих соединений
2022-11-23 23:50:13 +07:00
// Ограничение выполнения
2022-12-04 02:13:10 +07:00
if ( ++ this . actions . pushing . current >= this . actions . pushing . max ) break ;
2022-11-23 23:50:13 +07:00
// Защита от повторной обработки
2022-12-04 14:15:17 +07:00
if ( this . pushings . has ( connection . to ) ) continue ;
2022-11-23 23:50:13 +07:00
// Удаление из буфера реестра узлов
2022-12-04 14:15:17 +07:00
registry . delete ( connection . to ) ;
2022-11-23 23:50:13 +07:00
// Обработка отталкивания
2022-12-04 14:15:17 +07:00
this . pushing ( new Set ( [ connection . to ] ) , 0 , hard ) ;
2022-11-23 23:50:13 +07:00
}
2022-12-04 14:15:17 +07:00
for ( const connection of this . inputs ) {
// Перебор входящих соединений
2022-11-23 23:50:13 +07:00
// Ограничение выполнения
2022-12-04 02:13:10 +07:00
if ( ++ this . actions . pushing . current >= this . actions . pushing . max ) break ;
2022-11-23 23:50:13 +07:00
// Защита от повторной обработки
2022-12-04 14:15:17 +07:00
if ( this . pushings . has ( connection . from ) ) continue ;
2022-11-23 23:50:13 +07:00
// Удаление из буфера реестра узлов
2022-12-04 14:15:17 +07:00
registry . delete ( connection . from ) ;
2022-11-23 23:50:13 +07:00
// Обработка отталкивания
2022-12-04 14:15:17 +07:00
this . pushing ( new Set ( [ connection . from ] ) , 0 , hard ) ;
2022-11-23 23:50:13 +07:00
}
}
2022-12-04 14:15:17 +07:00
if ( this . pullings && ! this . pullings . has ( this ) ) {
2022-11-23 23:50:13 +07:00
// Активно притягивание
2022-12-04 14:15:17 +07:00
for ( const connection of this . outputs ) {
// Перебор исходящих соединений
2022-11-23 23:50:13 +07:00
// Ограничение выполнения
2022-12-04 02:13:10 +07:00
if ( ++ this . actions . pulling . current >= this . actions . pulling . max ) break ;
2022-11-23 23:50:13 +07:00
// Защита от повторной обработки
2022-12-04 14:15:17 +07:00
if ( this . pullings . has ( connection . to ) ) continue ;
2022-11-23 23:50:13 +07:00
// Удаление из буфера реестра узлов
2022-12-04 14:15:17 +07:00
registry . delete ( connection . to ) ;
2022-11-23 23:50:13 +07:00
// Обработка притягивания
2022-12-04 14:15:17 +07:00
this . pulling ( new Set ( [ connection . to ] ) , 0 , hard ) ;
2022-11-23 23:50:13 +07:00
}
2022-12-04 14:15:17 +07:00
for ( const connection of this . inputs ) {
2022-11-23 23:50:13 +07:00
// Перебор входящих соединений
// Ограничение выполнения
2022-12-04 02:13:10 +07:00
if ( ++ this . actions . pulling . current >= this . actions . pulling . max ) break ;
2022-11-23 23:50:13 +07:00
// Защита от повторной обработки
2022-12-04 14:15:17 +07:00
if ( this . pullings . has ( connection . from ) ) continue ;
2022-11-23 23:50:13 +07:00
// Удаление из буфера реестра узлов
2022-12-04 14:15:17 +07:00
registry . delete ( connection . from ) ;
2022-11-23 23:50:13 +07:00
// Обработка притягивания
2022-12-04 14:15:17 +07:00
this . pulling ( new Set ( [ connection . from ] ) , 0 , hard ) ;
2022-11-23 23:50:13 +07:00
}
}
// Обработка отталкивания остальных узлов
2022-12-04 14:15:17 +07:00
if ( this . pushings ) this . pushing ( registry , 0 , hard ) ;
2022-11-23 23:50:13 +07:00
// Синхронизация местоположения исходящих соединений
2022-12-03 12:05:28 +07:00
for ( const connection of this . outputs ) connection . synchronize ( this ) ;
2022-11-22 05:40:30 +07:00
2022-11-22 18:31:26 +07:00
// Синхронизация местоположения входящих соединений
2022-12-03 12:05:28 +07:00
for ( const connection of this . inputs ) connection . synchronize ( this ) ;
2022-11-22 05:40:30 +07:00
}
2022-12-04 14:15:17 +07:00
collision ( nodes , hard = false ) {
// Проверка на превышение ограничения по числу итераций у целевого узла
if ( ++ this . iteration >= this . limit ) return this . iteration = 0 ;
2022-11-22 05:40:30 +07:00
// Инициализация буфера реестра узлов
2022-11-22 18:31:26 +07:00
const registry = new Set ( nodes ) ;
2022-11-22 05:40:30 +07:00
// Удаление текущего узла из буфера
2022-11-22 18:31:26 +07:00
registry . delete ( this ) ;
2022-11-22 05:40:30 +07:00
2022-11-23 23:50:13 +07:00
// Обработка столкновения с узлами
2022-11-22 18:31:26 +07:00
for ( const node of registry ) {
2022-11-22 05:40:30 +07:00
// Перебор узлов в реестре
2022-11-23 23:50:13 +07:00
// Защита от повторной обработки узла
2022-12-04 14:15:17 +07:00
if ( typeof this . collisions === 'object' && this . collisions . has ( node ) ) continue ;
2022-11-23 23:50:13 +07:00
2022-11-22 05:40:30 +07:00
// Инициализация вектора между узлами
let between ;
// Инициализация ускорения
let increase = 0 ;
2022-12-04 02:13:10 +07:00
// Инициализация счётчика итераций
let iterations = 0 ;
2022-11-22 05:40:30 +07:00
do {
// Произошла коллизия (границы кругов перекрылись)
2022-12-04 14:15:17 +07:00
// Проверка на превышение ограничения по числу итераций у целевого узла
if ( ++ this . iteration >= this . limit ) return this . iteration = 0 ;
2022-11-23 23:50:13 +07:00
2022-12-04 14:15:17 +07:00
// Проверка на превышение ограничения по числу итераций у обрабатываемого узла
if ( ++ node . iteration >= node . limit ) return node . iteration = 0 ;
2022-11-23 23:50:13 +07:00
2022-11-22 18:31:26 +07:00
// Инициализация универсального буфера
let buffer ;
2022-11-22 05:40:30 +07:00
// Инициализация координат целевого узла
2022-11-22 18:31:26 +07:00
let x1 =
( isNaN ( ( buffer = parseInt ( node . element . style . left ) ) ) ? 0 : buffer ) +
node . element . offsetWidth / 2 ;
let y1 =
( isNaN ( ( buffer = parseInt ( node . element . style . top ) ) ) ? 0 : buffer ) +
node . element . offsetHeight / 2 ;
2022-11-22 05:40:30 +07:00
// Инициализация координат обрабатываемого узла
2022-11-22 18:31:26 +07:00
let x2 =
( isNaN ( ( buffer = parseInt ( this . element . style . left ) ) ) ? 0 : buffer ) +
this . element . offsetWidth / 2 ;
let y2 =
( isNaN ( ( buffer = parseInt ( this . element . style . top ) ) ) ? 0 : buffer ) +
this . element . offsetHeight / 2 ;
2022-11-22 05:40:30 +07:00
// Реинициализация вектора между узлами
2022-11-24 05:04:14 +07:00
between = new Victor ( x1 - x2 , y1 - y2 ) ;
2022-11-22 05:40:30 +07:00
2022-12-04 02:13:10 +07:00
// Узлы преодолели расстояние столкновения? (ограничение выполнения)
2022-11-22 05:40:30 +07:00
if (
2022-12-04 02:13:10 +07:00
this . actions . collision . current >= this . actions . collision . max ||
2022-12-04 14:15:17 +07:00
between . length ( ) > node . diameter / 2 + this . diameter / 2 ||
++ iterations > ( hard ? this . actions . collision . flow . hard : this . actions . collision . flow . medium )
2022-11-22 05:40:30 +07:00
)
break ;
2022-11-22 05:55:45 +07:00
// Инициализация координат вектора (узла с которым произошло столкновение)
2022-11-24 05:04:14 +07:00
let vector = new Victor ( x1 , y1 )
. add ( new Victor ( between . x , between . y ) . norm ( ) . unfloat ( ) )
2022-11-22 05:40:30 +07:00
. subtract (
2022-11-24 05:04:14 +07:00
new Victor (
2022-11-22 05:40:30 +07:00
node . element . offsetWidth / 2 ,
node . element . offsetHeight / 2
)
) ;
2022-12-04 02:13:10 +07:00
if ( this . actions . collision . current < this . actions . collision . max ) {
2022-11-23 23:50:13 +07:00
// Активно столкновение узлов
2022-12-04 14:15:17 +07:00
// Реинициализация реестра обработанных узлов и запись целевого узла
node . collisions = node . # operator . actions . collision ? new Set ( [ _this ] ) : null ;
2022-12-04 02:13:10 +07:00
2022-12-04 14:15:17 +07:00
// Реинициализация счётчиков итераций
node . actions . collision . current = 0 ;
2022-11-23 23:50:13 +07:00
2022-12-04 14:15:17 +07:00
// Запись целевого в реестр обработанных узлов в потоке
node . collisions . add ( _this ) ;
2022-11-23 23:50:13 +07:00
// Перемещение узла
2022-12-04 14:15:17 +07:00
node . move ( vector . x , vector . y ) ;
2022-11-23 23:50:13 +07:00
}
2022-11-22 18:31:26 +07:00
// Проверка на столкновение узлов
} while (
2022-12-04 02:13:10 +07:00
this . actions . collision . current < this . actions . collision . max &&
2022-11-22 18:31:26 +07:00
between . length ( ) <= node . diameter / 2 + this . diameter / 2
) ;
}
}
2022-12-04 14:15:17 +07:00
pushing ( nodes = [ ] , add , hard = false ) {
// Проверка на превышение ограничения по числу итераций у целевого узла
if ( ++ this . iteration >= this . limit ) return this . iteration = 0 ;
2022-11-23 23:50:13 +07:00
2022-11-22 18:31:26 +07:00
// Инициализация буфера реестра узлов
const registry = new Set ( nodes ) ;
// Удаление текущего узла из буфера
registry . delete ( this ) ;
// Инициализация ссылки на ядро
const _this = this ;
2022-11-23 23:50:13 +07:00
// Увеличение дистанции для проверки
const distance = 100 ;
// Обработка отталкивания узлов
2022-11-22 18:31:26 +07:00
for ( const node of registry ) {
// Перебор узлов в буфере реестра
2022-11-23 23:50:13 +07:00
// Защита от повторной обработки узла
2022-12-04 14:15:17 +07:00
if ( typeof this . pushings === 'object' && this . pushings . has ( node ) ) continue ;
2022-11-23 23:50:13 +07:00
2022-11-22 18:31:26 +07:00
// Инициализация вектора между узлами
let between ;
2022-12-04 02:13:10 +07:00
// Инициализация счётчика итераций
let iterations = 0 ;
2022-11-22 18:31:26 +07:00
function move ( ) {
2022-12-04 14:15:17 +07:00
// Проверка на превышение ограничения по числу итераций у целевого узла
if ( ++ _this . iteration >= _this . limit ) return _this . iteration = 0 ;
2022-11-23 23:50:13 +07:00
2022-12-04 14:15:17 +07:00
// Проверка на превышение ограничения по числу итераций у обрабатываемого узла
if ( ++ node . iteration >= node . limit ) return node . iteration = 0 ;
2022-11-23 23:50:13 +07:00
2022-11-22 18:31:26 +07:00
// Инициализация универсального буфера
let buffer ;
// Инициализация координат целевого узла
let x1 =
( isNaN ( ( buffer = parseInt ( node . element . style . left ) ) ) ? 0 : buffer ) +
node . element . offsetWidth / 2 ;
let y1 =
( isNaN ( ( buffer = parseInt ( node . element . style . top ) ) ) ? 0 : buffer ) +
node . element . offsetHeight / 2 ;
// Инициализация координат обрабатываемого узла
let x2 =
( isNaN ( ( buffer = parseInt ( _this . element . style . left ) ) )
? 0
: buffer ) +
_this . element . offsetWidth / 2 ;
let y2 =
( isNaN ( ( buffer = parseInt ( _this . element . style . top ) ) ) ? 0 : buffer ) +
_this . element . offsetHeight / 2 ;
// Реинициализация вектора между узлами
2022-11-24 05:04:14 +07:00
between = new Victor ( x1 - x2 , y1 - y2 ) ;
2022-11-22 18:31:26 +07:00
2022-11-23 23:50:13 +07:00
// Инициализация увеличения
let increase =
2022-12-04 02:13:10 +07:00
_this . shift + node . shift +
( _this . diameter + node . diameter ) /
2022-11-23 23:50:13 +07:00
2 * * ( _this . increase + node . increase ) ;
// Узлы преодолели расстояние отталкивания?
2022-11-22 18:31:26 +07:00
if (
2022-12-04 02:13:10 +07:00
_this . actions . pushing . current >= _this . actions . pushing . max ||
2022-12-04 14:15:17 +07:00
between . length ( ) >
( node . diameter + _this . diameter ) / 2 +
distance +
increase +
( typeof add === 'number' ? add : 0 ) ||
++ iterations > ( hard ? _this . actions . pushing . flow . hard : _this . actions . pushing . flow . medium )
2022-11-22 18:31:26 +07:00
)
return ;
// Инициализация координат вектора (узла с которым произошло столкновение)
2022-11-24 05:04:14 +07:00
let vector = new Victor ( x1 , y1 )
. add ( new Victor ( between . x , between . y ) . norm ( ) . unfloat ( ) )
2022-11-22 18:31:26 +07:00
. subtract (
2022-11-24 05:04:14 +07:00
new Victor (
2022-11-22 18:31:26 +07:00
node . element . offsetWidth / 2 ,
node . element . offsetHeight / 2
)
) ;
2022-12-04 02:13:10 +07:00
if ( _this . actions . pushing . current < _this . actions . pushing . max ) {
2022-11-23 23:50:13 +07:00
// Активно притягивание узла
2022-12-04 14:15:17 +07:00
// Реинициализация реестра обработанных узлов и запись целевого узла
node . pushings = node . # operator . actions . pushing ? new Set ( [ _this ] ) : null ;
2022-11-23 23:50:13 +07:00
2022-12-04 14:15:17 +07:00
// Реинициализация счётчиков итераций
node . actions . pushing . current = 0 ;
2022-11-23 23:50:13 +07:00
// Перемещение узла
2022-12-04 14:15:17 +07:00
node . move ( vector . x , vector . y ) ;
2022-11-23 23:50:13 +07:00
}
2022-11-22 18:31:26 +07:00
// Проверка расстояния
if (
2022-12-04 02:13:10 +07:00
_this . actions . pushing . current < _this . actions . pushing . max &&
2022-11-22 18:31:26 +07:00
between . length ( ) <=
2022-11-26 04:38:23 +07:00
( node . diameter + _this . diameter ) / 2 +
distance +
increase +
2022-12-03 12:29:29 +07:00
( typeof add === 'number' ? add : 0 )
2022-11-22 18:31:26 +07:00
)
2022-12-04 14:15:17 +07:00
return setTimeout ( move , between . length ( ) / 100 ) ;
2022-11-22 18:31:26 +07:00
}
2022-11-23 23:50:13 +07:00
// Повторная обработка (вход в рекурсию)
2022-12-04 02:13:10 +07:00
if ( _this . actions . pushing . current < _this . actions . pushing . max ) move ( ) ;
2022-11-23 23:50:13 +07:00
}
}
2022-12-04 14:15:17 +07:00
pulling ( nodes = [ ] , add , hard = false ) {
// Проверка на превышение ограничения по числу итераций у целевого узла
if ( ++ this . iteration >= this . limit ) return this . iteration = 0 ;
2022-11-23 23:50:13 +07:00
// Инициализация буфера реестра узлов
const registry = new Set ( nodes ) ;
// Удаление текущего узла из буфера
registry . delete ( this ) ;
// Инициализация ссылки на ядро
const _this = this ;
2022-12-04 02:13:10 +07:00
// Увеличение дистанции для проверки !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2022-11-23 23:50:13 +07:00
const distance = 150 ;
// Обработка притягивания узлов
for ( const node of registry ) {
// Перебор узлов в буфере реестра
// Защита от повторной обработки узла
2022-12-04 14:15:17 +07:00
if ( typeof this . pullings === 'object' && this . pullings . has ( node ) ) continue ;
2022-11-23 23:50:13 +07:00
// Инициализация вектора между узлами
let between ;
2022-12-04 02:13:10 +07:00
// Инициализация счётчика итераций
let iterations = 0 ;
2022-11-23 23:50:13 +07:00
function move ( ) {
2022-12-04 14:15:17 +07:00
// Проверка на превышение ограничения по числу итераций у целевого узла
if ( ++ _this . iteration >= _this . limit ) return _this . iteration = 0 ;
2022-11-23 23:50:13 +07:00
2022-12-04 14:15:17 +07:00
// Проверка на превышение ограничения по числу итераций у обрабатываемого узла
if ( ++ node . iteration >= node . limit ) return node . iteration = 0 ;
2022-11-23 23:50:13 +07:00
// Инициализация универсального буфера
let buffer ;
// Инициализация координат целевого узла
let x1 =
( isNaN ( ( buffer = parseInt ( node . element . style . left ) ) ) ? 0 : buffer ) +
node . element . offsetWidth / 2 ;
let y1 =
( isNaN ( ( buffer = parseInt ( node . element . style . top ) ) ) ? 0 : buffer ) +
node . element . offsetHeight / 2 ;
// Инициализация координат обрабатываемого узла
let x2 =
( isNaN ( ( buffer = parseInt ( _this . element . style . left ) ) )
? 0
: buffer ) +
_this . element . offsetWidth / 2 ;
let y2 =
( isNaN ( ( buffer = parseInt ( _this . element . style . top ) ) ) ? 0 : buffer ) +
_this . element . offsetHeight / 2 ;
// Реинициализация вектора между узлами
2022-11-24 05:04:14 +07:00
between = new Victor ( x1 - x2 , y1 - y2 ) ;
2022-11-23 23:50:13 +07:00
// Инициализация увеличения
let increase =
2022-12-04 02:13:10 +07:00
_this . shift + node . shift +
2022-12-04 14:15:17 +07:00
( _this . diameter + node . diameter ) /
2022-11-23 23:50:13 +07:00
2 * * ( _this . increase + node . increase ) ;
// Узлы преодолели расстояние притягивания?
if (
2022-12-04 02:13:10 +07:00
_this . actions . pulling . current >= _this . actions . pulling . max ||
2022-12-04 14:15:17 +07:00
between . length ( ) <=
( node . diameter + _this . diameter ) / 2 +
distance +
increase +
( typeof add === 'number' ? add : 0 ) ||
++ iterations > ( hard ? _this . actions . pulling . flow . hard : _this . actions . pulling . flow . medium )
2022-11-23 23:50:13 +07:00
)
return ;
// Инициализация координат вектора (узла с которым произошло столкновение)
2022-11-24 05:04:14 +07:00
let vector = new Victor ( x1 , y1 )
. add ( new Victor ( between . x , between . y ) . norm ( ) . invert ( ) . unfloat ( ) )
2022-11-23 23:50:13 +07:00
. subtract (
2022-11-24 05:04:14 +07:00
new Victor (
2022-11-23 23:50:13 +07:00
node . element . offsetWidth / 2 ,
node . element . offsetHeight / 2
)
) ;
2022-12-04 02:13:10 +07:00
if ( _this . actions . pulling . current < _this . actions . pulling . max ) {
2022-11-23 23:50:13 +07:00
// Активно притягивание узлов
2022-12-04 14:15:17 +07:00
// Реинициализация реестра обработанных узлов и запись целевого узла
node . pullings = node . # operator . actions . pulling ? new Set ( [ _this ] ) : null ;
2022-12-04 02:13:10 +07:00
2022-12-04 14:15:17 +07:00
// Реинициализация счётчиков итераций
node . actions . pulling . current = 0 ;
2022-11-23 23:50:13 +07:00
// Перемещение узла
2022-12-04 14:15:17 +07:00
node . move ( vector . x , vector . y ) ;
2022-11-23 23:50:13 +07:00
}
2022-12-04 14:15:17 +07:00
// Проверка расстояния
2022-11-23 23:50:13 +07:00
if (
2022-12-04 02:13:10 +07:00
_this . actions . pulling . current < _this . actions . pulling . max &&
2022-11-23 23:50:13 +07:00
between . length ( ) >
2022-11-26 04:38:23 +07:00
( node . diameter + _this . diameter ) / 2 +
distance +
increase +
2022-12-03 12:29:29 +07:00
( typeof add === 'number' ? add : 0 )
2022-11-23 23:50:13 +07:00
)
return setTimeout (
move ,
between . length ( ) / 10 - between . length ( ) / 10
) ;
}
// Повторная обработка (вход в рекурсию)
2022-12-04 02:13:10 +07:00
if ( _this . actions . pulling . current < _this . actions . pulling . max ) move ( ) ;
2022-11-22 18:31:26 +07:00
}
}
configure ( attribute ) {
// Инициализация названия параметра
2022-12-03 12:29:29 +07:00
const parameter = ( /^data-(\w+)$/ . exec ( attribute ) ? ? [ , null ] ) [ 1 ] ;
2022-11-22 18:31:26 +07:00
2022-12-03 12:29:29 +07:00
if ( typeof parameter === 'string' ) {
2022-11-22 18:31:26 +07:00
// Параметр найден
// Проверка на разрешение изменения
if ( this . # block . has ( parameter ) ) return ;
// Инициализация значения параметра
const value = this . element . getAttribute ( attribute ) ;
if ( typeof value !== undefined || typeof value !== null ) {
// Найдено значение
// Запрошено изменение координаты: x
2022-12-03 12:29:29 +07:00
if ( parameter === 'x' ) this . element . style . left = value + 'px' ;
2022-11-22 18:31:26 +07:00
// Запрошено изменение координаты: y
2022-12-03 12:29:29 +07:00
if ( parameter === 'y' ) this . element . style . top = value + 'px' ;
2022-11-22 18:31:26 +07:00
// Инициализация буфера для временных данных
let buffer ;
// Запись параметра
this [ parameter ] = isNaN ( ( buffer = parseFloat ( value ) ) )
2022-12-03 12:29:29 +07:00
? value === 'true'
2022-11-22 18:31:26 +07:00
? true
2022-12-03 12:29:29 +07:00
: value === 'false'
2022-11-26 04:38:23 +07:00
? false
: value
2022-11-22 18:31:26 +07:00
: buffer ;
}
2022-11-22 05:40:30 +07:00
}
2022-11-01 06:19:17 +07:00
}
2022-12-04 14:15:17 +07:00
/ * *
* С б р о с данных потока
* /
reset = fn => {
// Реинициализация реестров обработанных узлов
this . collisions = this . # operator . actions . collision ? new Set ( ) : null ;
this . pushings = this . # operator . actions . pushing ? new Set ( ) : null ;
this . pullings = this . # operator . actions . pulling ? new Set ( ) : null ;
// Реинициализация счётчиков итераций
this . actions . collision . current = this . actions . pushing . current = this . actions . pulling . current = 0 ;
}
2022-11-01 06:19:17 +07:00
} ;
2022-12-03 12:05:28 +07:00
// Прочитать класс узла
2022-11-22 06:43:57 +07:00
get node ( ) {
return this . # node ;
}
// Класс соединения
# connection = class connection {
2022-12-03 12:05:28 +07:00
// HTML-элемент-оболочка
# shell ;
// Прочитать HTML-элемент-оболочку
get shell ( ) {
return this . # shell ;
}
// HTML-элемент соединения
2022-11-22 06:43:57 +07:00
# element ;
2022-12-03 12:05:28 +07:00
// Прочитать HTML-элемент соединения
2022-11-22 06:43:57 +07:00
get element ( ) {
return this . # element ;
}
2022-12-03 12:05:28 +07:00
// Инстанция this.operator.node от которой начинается соединение
2022-11-22 06:43:57 +07:00
# from ;
2022-12-03 12:05:28 +07:00
// Прочитать инстанцию this.operator.node от которой начинается соединение
2022-11-22 06:43:57 +07:00
get from ( ) {
return this . # from ;
}
2022-12-03 12:05:28 +07:00
// Инстанция this.operator.node на которой заканчивается соединение
2022-11-22 06:43:57 +07:00
# to ;
2022-12-03 12:05:28 +07:00
// Прочитать инстанцию this.operator.node на которой заканчивается соединение
2022-11-22 06:43:57 +07:00
get to ( ) {
return this . # to ;
}
// Оператор
# operator ;
2022-12-03 12:05:28 +07:00
// Прочитать оператора
2022-11-22 06:43:57 +07:00
get operator ( ) {
return this . # operator ;
}
2022-12-04 02:13:10 +07:00
/ * *
* Конструктор соединения
*
* @ param { object } operator Инстанция оператора ( графика )
* @ param { object } from Инстанция узла от которого идёт соединение
* @ param { object } to Инстанция узла к которому идёт соединения
* /
2022-11-22 06:43:57 +07:00
constructor ( operator , from , to ) {
// Запись свойства
this . # operator = operator ;
// Запись свойства
this . # from = from ;
// Запись свойства
this . # to = to ;
2022-12-03 12:05:28 +07:00
// Инициализация HTML-элемента-оболочки соединений
if ( ( this . # shell = document . getElementById ( this . # operator . id + '_connections' ) ) instanceof SVGElement ) ;
else {
// Н е найден HTML-элемент-оболочки соединений
// Инициализация HTML-элемента-оболочки соединений
2022-12-03 12:29:29 +07:00
const shell = document . createElementNS ( 'http://www.w3.org/2000/svg' , 'svg' ) ;
2022-12-03 12:05:28 +07:00
shell . id = this . # operator . id + '_connections' ;
shell . classList . add ( ... this . # operator . classes . connection . shell ) ;
// Запись в документ
this . # operator . shell . appendChild ( shell ) ;
// Запись в свойство
this . # shell = shell ;
}
2022-11-22 06:43:57 +07:00
// Инициализация универсального буфера
let buffer ;
// Инициализация оболочки
const line = document . createElementNS (
2022-12-03 12:29:29 +07:00
'http://www.w3.org/2000/svg' ,
'line'
2022-11-22 06:43:57 +07:00
) ;
line . setAttribute (
2022-12-03 12:29:29 +07:00
'x1' ,
2022-11-22 06:43:57 +07:00
( isNaN ( ( buffer = parseInt ( from . element . style . left ) ) ) ? 0 : buffer ) +
2022-11-26 04:38:23 +07:00
from . element . offsetWidth / 2
2022-11-22 06:43:57 +07:00
) ;
line . setAttribute (
2022-12-03 12:29:29 +07:00
'y1' ,
2022-11-22 06:43:57 +07:00
( isNaN ( ( buffer = parseInt ( from . element . style . top ) ) ) ? 0 : buffer ) +
2022-11-26 04:38:23 +07:00
from . element . offsetHeight / 2
2022-11-22 06:43:57 +07:00
) ;
line . setAttribute (
2022-12-03 12:29:29 +07:00
'x2' ,
2022-11-22 06:43:57 +07:00
( isNaN ( ( buffer = parseInt ( to . element . style . left ) ) ) ? 0 : buffer ) +
2022-11-26 04:38:23 +07:00
to . element . offsetWidth / 2
2022-11-22 06:43:57 +07:00
) ;
line . setAttribute (
2022-12-03 12:29:29 +07:00
'y2' ,
2022-11-22 06:43:57 +07:00
( isNaN ( ( buffer = parseInt ( to . element . style . top ) ) ) ? 0 : buffer ) +
2022-11-26 04:38:23 +07:00
to . element . offsetHeight / 2
2022-11-22 06:43:57 +07:00
) ;
2022-12-03 12:29:29 +07:00
line . setAttribute ( 'stroke' , 'grey' ) ;
line . setAttribute ( 'stroke-width' , '8px' ) ;
2022-12-03 12:05:28 +07:00
line . id = this . # operator . id + '_connection_' + operator . connections . size ;
line . classList . add ( ... this . operator . classes . connection . element ) ;
2022-12-03 12:29:29 +07:00
line . setAttribute ( 'data-from' , from . element . id ) ;
line . setAttribute ( 'data-to' , to . element . id ) ;
2022-11-22 06:43:57 +07:00
// Запись в оболочку
2022-12-03 12:05:28 +07:00
this . shell . append ( line ) ;
2022-11-22 06:43:57 +07:00
2022-12-03 12:05:28 +07:00
// Запись в свойство
this . # element = line ;
2022-11-22 06:43:57 +07:00
}
/ * *
2022-11-22 18:31:26 +07:00
* Синхронизировать местоположение с о связанным узлом
2022-11-22 06:43:57 +07:00
*
* @ param { node } node Инстанция узла ( связанного с соединением )
* /
2022-12-03 12:05:28 +07:00
synchronize ( node ) {
2022-11-22 06:43:57 +07:00
// Инициализация названий аттрибутов
2022-12-03 12:29:29 +07:00
let x = 'x' ,
y = 'y' ;
2022-11-22 06:43:57 +07:00
if ( node === this . from ) {
// Исходящее соединение
// Запись названий аттрибутов
2022-11-22 18:31:26 +07:00
x += 1 ;
y += 1 ;
2022-11-22 06:43:57 +07:00
} else if ( node === this . to ) {
// Входящее соединение
// Запись названий аттрибутов
2022-11-22 18:31:26 +07:00
x += 2 ;
y += 2 ;
2022-11-22 06:43:57 +07:00
} else return ;
// Инициализация универсального буфера
let buffer ;
// Запись отступа (координаты по горизонтали)
2022-12-03 12:05:28 +07:00
this . element . setAttribute (
2022-11-22 06:43:57 +07:00
x ,
2022-12-03 12:29:29 +07:00
- this . # shell . getAttribute ( 'data-x' ) +
2022-11-22 06:43:57 +07:00
( isNaN ( ( buffer = parseInt ( node . element . style . left ) ) ) ? 0 : buffer ) +
2022-11-26 04:38:23 +07:00
node . element . offsetWidth / 2
2022-11-22 06:43:57 +07:00
) ;
// Запись отступа (координаты по вертикали)
2022-12-03 12:05:28 +07:00
this . element . setAttribute (
2022-11-22 06:43:57 +07:00
y ,
2022-12-03 12:29:29 +07:00
- this . # shell . getAttribute ( 'data-y' ) +
2022-11-22 06:43:57 +07:00
( isNaN ( ( buffer = parseInt ( node . element . style . top ) ) ) ? 0 : buffer ) +
2022-11-26 04:38:23 +07:00
node . element . offsetHeight / 2
2022-11-22 06:43:57 +07:00
) ;
}
} ;
2022-12-03 12:05:28 +07:00
// Прочитать класс соединения
2022-11-22 06:43:57 +07:00
get connection ( ) {
return this . # connection ;
}
2022-12-03 12:05:28 +07:00
// Разрешено перемещать узлы?
2022-11-01 06:19:17 +07:00
# move = true ;
2022-12-03 12:05:28 +07:00
// Разрешено перемещать камеру? (svg-элементы-соединения - рёбра)
2022-11-01 06:19:17 +07:00
# camera = true ;
2022-12-04 02:13:10 +07:00
/ * *
* Конструктор графика
*
* @ param { HTMLElement | string } shell HTML - элемент - оболочка для графика , либо е г о идентификатор
* @ param { boolean } body Перенос работает на теле документа ? ( иначе на HTML - элементе - оболочке )
* @ param { boolean } camera Активировать перемещение камеры ?
* /
2022-12-03 12:05:28 +07:00
constructor ( shell , body = true , camera = true ) {
2022-11-01 06:19:17 +07:00
// Запись оболочки
2022-11-24 05:04:14 +07:00
if ( shell instanceof HTMLElement ) this . # shell = shell ;
2022-12-03 12:05:28 +07:00
else if ( typeof shell === 'string' ) this . # shell = document . getElementById ( shell ) ;
// Проверка на инициализированность HTML-элемента-оболочки
if ( typeof this . # shell === undefined ) return false ;
// Запись идентификатора
this . # id = this . # shell . id ;
2022-11-01 06:19:17 +07:00
// Инициализация ссылки на обрабатываемый объект
const _this = this ;
2022-12-03 12:05:28 +07:00
// Инициализация цели для переноса
const target = body ? document . body : shell ;
2022-11-01 06:19:17 +07:00
// Перемещение камеры
if ( camera === true ) {
2022-12-03 12:05:28 +07:00
target . onmousedown = function ( onmousedown ) {
2022-11-01 06:19:17 +07:00
// Начало переноса
if ( _this . # camera ) {
// Разрешено двигать камеру (оболочку)
2022-12-03 12:05:28 +07:00
// Запись иконки курсора
target . style . cursor = 'move' ;
2022-11-01 06:19:17 +07:00
// Инициализация координат
const coords = _this . shell . getBoundingClientRect ( ) ;
2022-12-03 12:05:28 +07:00
const x = onmousedown . pageX - coords . left + scrollX ;
const y = onmousedown . pageY - coords . top + scrollY ;
2022-12-03 12:29:29 +07:00
// Инициализация HTML-элемента-оболочки соединений
const connections = document . getElementById ( _this . # id + '_connections' ) ;
2022-11-01 06:19:17 +07:00
2022-11-22 05:40:30 +07:00
// Инициализация функции переноса полотна
function move ( onmousemove ) {
2022-12-03 12:29:29 +07:00
// Инициализация буфера
let buffer ;
// Запись нового отступа от лева для HTML-элемента оболочки графика
_this . shell . style . left = ( buffer = onmousemove . pageX - x ) + 'px' ;
// Запись нового отступа от лева для HTML-элемента оболочки соединений
connections . style . left = - buffer + 'px' ;
2022-11-22 05:40:30 +07:00
2022-12-03 12:29:29 +07:00
// Запись аттрибута с координатами для HTML-элемента оболочки соединений
connections . setAttribute ( 'data-x' , - buffer ) ;
2022-12-03 12:05:28 +07:00
2022-12-03 12:29:29 +07:00
// Запись нового отступа от верха для HTML-элемента оболочки графика
_this . shell . style . top = ( buffer = onmousemove . pageY - y ) + 'px' ;
2022-12-03 12:05:28 +07:00
2022-12-03 12:29:29 +07:00
// Запись нового отступа от верха для HTML-элемента оболочки соединений
connections . style . top = - buffer + 'px' ;
2022-12-03 12:05:28 +07:00
2022-12-03 12:29:29 +07:00
// Запись аттрибута с координатами для HTML-элемента оболочки соединений
connections . setAttribute ( 'data-y' , - buffer ) ;
for ( const connection of _this . connections ) {
// Перебор соединений
// Синхронизация
connection . synchronize ( connection . from ) ;
connection . synchronize ( connection . to ) ;
}
2022-11-01 06:19:17 +07:00
}
2022-11-22 05:40:30 +07:00
// Запись слушателя события: "перенос полотна"
2022-12-03 12:05:28 +07:00
target . onmousemove = move ;
2022-11-01 06:19:17 +07:00
}
// Конец переноса
2022-12-03 12:05:28 +07:00
target . onmouseup = function ( ) {
target . onmousemove = null ;
target . onmouseup = null ;
// Запись иконки курсора
target . style . cursor = null ;
2022-11-01 06:19:17 +07:00
} ;
} ;
2022-11-26 04:38:23 +07:00
// Блокировка событий браузера (чтобы не дёргалось)
2022-12-03 12:05:28 +07:00
target . ondragstart = null ;
2022-11-01 06:19:17 +07:00
}
}
write = function ( data = { } ) {
2022-12-03 12:29:29 +07:00
if ( typeof data === 'object' ) {
2022-11-01 06:19:17 +07:00
// Получен обязательный входной параметр в правильном типе
// Инициализация узла
2022-11-22 06:43:57 +07:00
const node = new this . node ( this , data ) ;
2022-11-01 06:19:17 +07:00
// Инициализация ссылки на обрабатываемый объект
const _this = this ;
// Запрет движения камеры при наведении на узел (чтобы двигать узел)
node . element . onmouseover = function ( e ) {
_this . # camera = false ;
} ;
// Снятие запрета движения камеры
node . element . onmouseout = function ( e ) {
_this . # camera = true ;
} ;
if ( this . # move ) {
// Разрешено перемещать узлы
2022-12-04 02:13:10 +07:00
// Инициализация переноса узла
2022-11-01 06:19:17 +07:00
node . element . onmousedown = function ( onmousedown ) {
// Начало переноса
2022-11-26 22:41:43 +07:00
// Инициализация буфера позиционирования
const z = node . element . style . zIndex ;
2022-11-01 06:19:17 +07:00
// Позиционирование над остальными узлами
2022-11-26 04:38:23 +07:00
node . element . style . zIndex = 5000 ;
2022-11-01 06:19:17 +07:00
2022-12-04 02:13:10 +07:00
// Инициализация буферов значения количества столкновений, притягиваний и отталкиваний
let collision , pushing , pulling ;
2022-11-01 06:19:17 +07:00
if ( ! _this . # camera ) {
// Запрещено двигать камеру (оболочку)
// Инициализация координат
const n = node . element . getBoundingClientRect ( ) ;
const s = _this . shell . getBoundingClientRect ( ) ;
2022-11-22 05:40:30 +07:00
// Инициализация функции переноса узла
2022-11-01 06:19:17 +07:00
function move ( onmousemove ) {
2022-12-04 14:15:17 +07:00
// С б р о с данных потока
node . reset ( ) ;
2022-12-04 02:13:10 +07:00
// Перемещение узла
2022-11-22 05:40:30 +07:00
node . move (
2022-11-01 06:19:17 +07:00
onmousemove . pageX -
2022-12-04 02:13:10 +07:00
( onmousedown . pageX - n . left + s . left + scrollX ) ,
2022-11-22 05:40:30 +07:00
onmousemove . pageY -
2022-12-04 14:15:17 +07:00
( onmousedown . pageY - n . top + s . top + scrollY )
2022-11-22 05:40:30 +07:00
) ;
2022-11-01 06:19:17 +07:00
}
2022-11-22 05:40:30 +07:00
// Запись слушателя события: "перенос узла"
2022-11-01 06:19:17 +07:00
document . onmousemove = move ;
}
// Конец переноса
node . element . onmouseup = function ( ) {
// Очистка обработчиков событий
document . onmousemove = null ;
node . element . onmouseup = null ;
2022-11-26 22:41:43 +07:00
// Возвращение позиционирования
node . element . style . zIndex = z ;
2022-11-01 06:19:17 +07:00
} ;
} ;
// Перещапись событий браузера (чтобы не дёргалось)
node . element . ondragstart = null ;
}
// Запись в реестр
this . nodes . add ( node ) ;
return node ;
}
} ;
connect = function ( from , to ) {
if ( from instanceof this . node && to instanceof this . node ) {
// Получены обязательные входные параметры в правильном типе
2022-11-22 06:43:57 +07:00
// Инициализация соединения
const connection = new this . connection ( this , from , to ) ;
2022-11-01 06:19:17 +07:00
// Запись соединений в реестры узлов
2022-11-22 06:43:57 +07:00
from . outputs . add ( connection ) ;
to . inputs . add ( connection ) ;
2022-11-01 06:19:17 +07:00
// Запись в реестр ядра
2022-11-22 06:43:57 +07:00
this . connections . add ( connection ) ;
2022-11-01 06:19:17 +07:00
// Реинициализация узла-получателя
2022-11-22 05:40:30 +07:00
to . init ( 1 ) ;
2022-11-01 06:19:17 +07:00
2022-11-22 06:43:57 +07:00
return connection ;
2022-11-01 06:19:17 +07:00
}
} ;
}
2022-11-22 05:40:30 +07:00
document . dispatchEvent (
2022-12-03 12:29:29 +07:00
new CustomEvent ( 'graph.loaded' , {
2022-11-22 05:40:30 +07:00
detail : { graph }
} )
) ;