Создано по мотивам статьи: Программное создание графических схем от Yashazz
Увидев решение об XML-сериализации ГрафическойСхемы (и десериализации ее) мелькнула мысль: "А ведь можно сделать и JSON-сериализацию". Но ведь JSON можно и десериализовать в Массивы и Соответствия. Вот она - объектная модель! Оперируем Структурами, Массивами и Соответствиями, далее делаем JSON сериализацию и десериализацию в ГрафическуюСхему.
Мой код, создающий ту же самую схему из статьи:
// Здесь только для понимания. Вообще везде передаются коды типов, цифры
ТипЭлементаГрафическойСхемы = Новый Структура;
ТипЭлементаГрафическойСхемы.Вставить("Декорация",0); //0 "Декорация"
ТипЭлементаГрафическойСхемы.Вставить("ДекоративнаяЛиния",1);//1 "ДекоративнаяЛиния"
ТипЭлементаГрафическойСхемы.Вставить("Старт",2); //2 "Старт"
ТипЭлементаГрафическойСхемы.Вставить("Завершение",3); //3 "Завершение"
ТипЭлементаГрафическойСхемы.Вставить("Условие",4); //4 "Условие"
ТипЭлементаГрафическойСхемы.Вставить("Действие",5); //5 "Действие"
ТипЭлементаГрафическойСхемы.Вставить("ВыборВарианта",6); //6 "ВыборВарианта"
ТипЭлементаГрафическойСхемы.Вставить("Разделение",7); //7 "Разделение"
ТипЭлементаГрафическойСхемы.Вставить("Слияние",8); //8 "Слияние"
ТипЭлементаГрафическойСхемы.Вставить("Обработка",9); //9 "Обработка"
ТипЭлементаГрафическойСхемы.Вставить("ВложенныйПроцесс",10);//10 "ВложенныйПроцесс"
// Инициализация
Схема = ИнициализироватьСхему();
// Добавляем элементы один за другим
_старт = ПолучитьСтруктуруТипа(ТипЭлементаГрафическойСхемы.Старт,"ТестСтарта");
_старт.itemTitle.Добавить(Новый Структура("lang,content","#","Погнали!"));
_id_старт = ДобавитьЭлемент(Схема,_старт,100,30,100,40);
_действие = ПолучитьСтруктуруТипа(ТипЭлементаГрафическойСхемы.Действие,"ТестДействия");
_действие.itemTitle.Добавить(Новый Структура("lang,content","#","Это действие"));
_действие.taskDescription = "Некое действие в системе";
_действие.explanation = "И.И.Иванов";
_id_действие = ДобавитьЭлемент(Схема,_действие,100,120,100,60);
_условие = ПолучитьСтруктуруТипа(ТипЭлементаГрафическойСхемы.Условие,"ТестУсловия");
_условие.itemTitle.Добавить(Новый Структура("lang,content","#","А я знаю?"));
_id_условие = ДобавитьЭлемент(Схема,_условие,100,220,100,40);
_действие2 = ПолучитьСтруктуруТипа(ТипЭлементаГрафическойСхемы.Действие,"ТестДействия2");
_действие2.itemTitle.Добавить(Новый Структура("lang,content","#","Тоже что-то"));
_действие2.taskDescription = "Некое действие в системе";
_действие2.explanation = "Петров";
_id_действие2 = ДобавитьЭлемент(Схема,_действие2,10,280,100,60);
_линия2 = ПодготовитьСтруктуруЛинии(Схема,_id_условие,_id_действие2,1,2,Ложь,"ТестЛинии2");
_линия2.itemTitle.Добавить(Новый Структура("lang,content","#","Нет"));
ДобавитьЭлемент(Схема,_линия2);
_обработка = ПолучитьСтруктуруТипа(ТипЭлементаГрафическойСхемы.Обработка,"ТестОбработки");
_обработка.itemTitle.Добавить(Новый Структура("lang,content","#","Зашли сюды"));
_id_обработка = ДобавитьЭлемент(Схема,_обработка,180,290,100,60);
_линия3 = ПодготовитьСтруктуруЛинии(Схема,_id_условие,_id_обработка,3,2,Ложь,"ТестЛинии3");
_линия3.itemTitle.Добавить(Новый Структура("lang,content","#","Да"));
ДобавитьЭлемент(Схема,_линия3);
_завершение = ПолучитьСтруктуруТипа(ТипЭлементаГрафическойСхемы.Завершение,"ТестСтопа");
_завершение.itemTitle.Добавить(Новый Структура("lang,content","#","Тпрууу!"));
_id_завершение = ДобавитьЭлемент(Схема,_завершение,740,210,100,40);
_процесс = ПолучитьСтруктуруТипа(ТипЭлементаГрафическойСхемы.ВложенныйПроцесс,"ТестБП");
_процесс.itemTitle.Добавить(Новый Структура("lang,content","#","Вложенный БП"));
_процесс.taskDescription = "Нечто внутри!";
_id_процесс = ДобавитьЭлемент(Схема,_процесс,560,290,100,60);
_выбор = ПолучитьСтруктуруТипа(ТипЭлементаГрафическойСхемы.ВыборВарианта,"ТестВарианта");
_выбор.itemTitle.Добавить(Новый Структура("lang,content","#","Так как, собственно?"));
__вар1 = Новый Структура("name,description,backColor","Вариант1",Новый Массив,_выбор.backColor);
__вар1.description.Добавить(Новый Структура("lang,content","#","Превосходно!"));
__вар2 = Новый Структура("name,description,backColor","Вариант2",Новый Массив,_выбор.backColor);
__вар2.description.Добавить(Новый Структура("lang,content","#","Замечательно!"));
_выбор.transition.Добавить(__вар1);
_выбор.transition.Добавить(__вар2);
_id_выбор = ДобавитьЭлемент(Схема,_выбор,440,100,140,100);
ДобавитьЭлемент(Схема,ПодготовитьСтруктуруЛинии(Схема,_id_выбор,_id_завершение,7,2,Ложь)); // тут 7 это правая сторона 1-го варианта
ДобавитьЭлемент(Схема,ПодготовитьСтруктуруЛинии(Схема,_id_выбор,_id_процесс,9,2,Ложь)); // тут 9 это правая сторона 2-го варианта
// про 7 и 9 порты нужно пояснить...
// у каждого элемента есть 5 основных портов (точек соединения): 1,2,3,4,5 (Лево, Верх, Право, Низ, Центр)
// А далее идут стороны вариантов (только для элемента ВыборВарианта):
// четные - левые стороны, нечетные - правые
// поэтому 1 вариант левая сторона - это 6, а 1 вариант правая сторона - 7
// для 2 варианта: 8 (лево) и 9 (право) соответственно
// общая формула вычисления стороны (1 или 3 - лево или право) для портов > 5 следующая: portIndex = 1 + (portIndex%2)*2;
_линия4 = ПодготовитьСтруктуруЛинии(Схема,_id_обработка,_id_выбор,3,2,Ложь,"ТестЛинии4");
ДобавитьЭлемент(Схема,_линия4);
ДобавитьЭлемент(Схема,ПодготовитьСтруктуруЛинии(Схема,_id_старт,_id_действие,4,2,Ложь));
ДобавитьЭлемент(Схема,ПодготовитьСтруктуруЛинии(Схема,_id_действие,_id_условие,4,2,Ложь));
// Вывод схемы
ЗаписьJSON = Новый ЗаписьJSON;
ЧтениеJSON = Новый ЧтениеJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON,Схема);
стрJSON = ЗаписьJSON.Закрыть();
Сообщить(стрJSON);
ЧтениеJSON.УстановитьСтроку(стрJSON);
зн = СериализаторXDTO.ПрочитатьJSON(ЧтениеJSON,Тип("ГрафическаяСхема")); // Вот он результат!
ЭлементыФормы.ГС.УстановитьСхему(зн); // Мой элемент на форме называется ГС
Можно оценить экономию места в модуле.
Модуль, который реализует API для работы с ГрафическимиСхемами
#Область ОБЪЕКТНАЯ_МОДЕЛЬ
Функция ИнициализироватьСхему() Экспорт
стрСхема = Новый Структура;
стрСхема.Вставить("backColor" , Новый Соответствие);
стрСхема.backColor.Вставить("#type" , "jv8ui:Color");
стрСхема.backColor.Вставить("#value" , "{http://v8.1c.ru/8.1/data/ui/style}FieldBackColor");
стрСхема.Вставить("enableGrid" , Истина); // Включить сетку
стрСхема.Вставить("drawGridMode" , "Lines"); // Сетка. Варианты: None,Dots,Chess,Lines
стрСхема.Вставить("gridVerticalStep" , 20);
стрСхема.Вставить("gridHorizontalStep" , 20);
стрСхема.Вставить("bpUUID" , "00000000-0000-0000-0000-000000000000");
стрСхема.Вставить("useOutput" , "Auto");
стрСхема.Вставить("printPropItem" , Новый Массив);
стрСхема.printPropItem.Добавить(Новый Структура("key,val",6 ,10 ));
стрСхема.printPropItem.Добавить(Новый Структура("key,val",7 ,10 ));
стрСхема.printPropItem.Добавить(Новый Структура("key,val",8 ,10 ));
стрСхема.printPropItem.Добавить(Новый Структура("key,val",9 ,10 ));
стрСхема.printPropItem.Добавить(Новый Структура("key,val",13,0 ));
стрСхема.printPropItem.Добавить(Новый Структура("key,val",16,0 ));
//стрСхема.Вставить("item" , Новый Массив); // добавлять только если есть хотя бы один элемент
значСхема = Новый Соответствие;
значСхема.Вставить("#value",стрСхема);
Возврат значСхема;
КонецФункции
Функция ДобавитьЭлемент(Схема,СтруктураЭлемента,x=Неопределено,y=Неопределено,dx=40,dy=40) Экспорт // dx,dy - тут для упрощения
структураСхемы = Схема.Получить("#value");
Если НЕ структураСхемы.Свойство("item") Тогда
структураСхемы.Вставить("item", Новый Массив); // добавлять только если есть хотя бы один элемент
КонецЕсли;
Если НайтиПоЗначениюКлючаВМассивеСтруктур(структураСхемы.item,"itemCode",СтруктураЭлемента.itemCode) = СтруктураЭлемента Тогда
Возврат СтруктураЭлемента.itemid;
КонецЕсли;
// Ищем максимальный itemid
СтруктураЭлемента.itemid = ПолучитьМаксимальноеЗначениеКлючаВМассивеСтруктур(структураСхемы.item,"itemid") + 1;
// Ищем такой же itemCode
Если НайтиПоЗначениюКлючаВМассивеСтруктур(структураСхемы.item,"itemCode",СтруктураЭлемента.itemCode) <> Неопределено Тогда
_базоваяЧасть = СтруктураЭлемента.itemCode;
_нс = 1;
Пока НайтиПоЗначениюКлючаВМассивеСтруктур(структураСхемы.item,"itemCode",_базоваяЧасть + Формат(_нс,"ЧГ=")) <> Неопределено Цикл
_нс = _нс + 1;
КонецЦикла;
СтруктураЭлемента.itemCode = _базоваяЧасть + Формат(_нс,"ЧГ=");
КонецЕсли;
// Ищем максимальный itemTabOrder
СтруктураЭлемента.itemTabOrder = ПолучитьМаксимальноеЗначениеКлючаВМассивеСтруктур(структураСхемы.item,"itemTabOrder") + 5; // пока так. Это порядок обхода. Возможно нужно более продвинутое вычисление сделать (считать все подчиненные и соединенные элементы последнего и прибавлять на их количество к максимальному)
// Ищем максимальный zOrder
СтруктураЭлемента.zOrder = ПолучитьМаксимальноеЗначениеКлючаВМассивеСтруктур(структураСхемы.item,"zOrder",-1) + 1;
// *** Устанавливаем координаты и размер (опциоально)
Если СтруктураЭлемента.itemType <> 1 Тогда // если не Линия
Если x = Неопределено И y <> Неопределено Тогда // ищем максимальный X
x = ПолучитьМаксимальноеЗначениеКлючаВМассивеСтруктур(структураСхемы.item,"rectRight") + структураСхемы.gridHorizontalStep;
КонецЕсли;
Если y = Неопределено И x <> Неопределено Тогда // ищем максимальный Y
y = ПолучитьМаксимальноеЗначениеКлючаВМассивеСтруктур(структураСхемы.item,"rectBottom") + структураСхемы.gridVerticalStep;
КонецЕсли;
ЗаполнитьЗначенияСвойств(СтруктураЭлемента,Новый Структура("rectLeft,rectTop,rectRight,rectBottom",x,y,x+dx,y+dy));
РасставитьТочкиФигуры(СтруктураЭлемента);
КонецЕсли;
// ДОБАВЛЕНИЕ ПОДГОТОВЛЕННОГО ЭЛЕМЕНТА
структураСхемы.item.Добавить(СтруктураЭлемента);
Возврат СтруктураЭлемента.itemid;
КонецФункции
// Рисуем фигуру элемента по точкам. Вообще говоря, как расставим точки,
// так и будет выглядеть фигура. Тут - типовой вариант, без изысков.
// А можно и фантазию включить... )) (см. скриншоты к статье) Модификатор - как раз для этого
Процедура РасставитьТочкиФигуры(СтруктураЭлемента,Модификатор=Неопределено) Экспорт
Перем rectLeft,rectTop,rectRight,rectBottom,itemType;
itemType = ?(Модификатор=Неопределено,СтруктураЭлемента.itemType,Модификатор);
rectLeft = СтруктураЭлемента.rectLeft;
rectTop = СтруктураЭлемента.rectTop;
rectRight = СтруктураЭлемента.rectRight;
rectBottom = СтруктураЭлемента.rectBottom;
Если itemType = 5 ИЛИ itemType = 9 ИЛИ itemType = 10 Тогда // Действие/Обработка/ВложенныйПроцесс: Прямоугольник
СтруктураЭлемента.point.Очистить();
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1,rectBottom-1));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,rectBottom-1));
ИначеЕсли itemType = 2 Тогда // Старт: Прямоугольник + треугольник снизу (высота треугольника = 5)
dy = Цел((rectRight-rectLeft)/2/Sqrt(3));
dy = ?(rectBottom-dy<=rectTop,Цел((rectBottom-rectTop)/2),dy); // корректировка запредельных значений
СтруктураЭлемента.point.Очистить();
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1,rectBottom-dy));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",Цел((rectLeft+rectRight)/2),rectBottom-1));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,rectBottom-dy));
ИначеЕсли itemType = 3 Тогда // Завершение: Прямоугольник + треугольник сверху (высота треугольника = 5)
dy = Цел((rectRight-rectLeft)/2/Sqrt(3));
dy = ?(rectTop+dy>=rectBottom,Цел((rectRight-rectLeft)/2),dy); // корректировка запредельных значений
СтруктураЭлемента.point.Очистить();
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",Цел((rectLeft+rectRight)/2),rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1,rectTop+dy));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1,rectBottom-1));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,rectBottom-1));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,rectTop+dy));
ИначеЕсли itemType = 4 Тогда // Условие: Шестиугольник (расчет середины высоты:dy/2,угол 60 градусов)
СтруктураЭлемента.point.Очистить();
dx = Цел((rectBottom-rectTop)/2/Sqrt(3)); // ДельтаX при 60 градусном уклоне
dx = ?(2*dx>(rectRight-rectLeft),Цел((rectRight-rectLeft)/4),dx); // корректировка запредельных значений
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,Цел((rectTop+rectBottom)/2)));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+dx,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1-dx,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1,Цел((rectTop+rectBottom)/2)));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1-dx,rectBottom-1));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+dx,rectBottom-1));
ИначеЕсли itemType = 6 Тогда // ВыборВарианта: Прямоугольник
СтруктураЭлемента.point.Очистить();
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1,rectBottom-1));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,rectBottom-1));
ИначеЕсли itemType = 7 Тогда // Разделение: Треугольник, острый угол вниз
dx = Цел((rectRight-rectLeft)/2)-1;
СтруктураЭлемента.point.Очистить();
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+2*dx,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+dx,rectBottom-1));
ИначеЕсли itemType = 8 Тогда // Слияние: Треугольник, острый угол вверх
dx = Цел((rectRight-rectLeft)/2)-1;
СтруктураЭлемента.point.Очистить();
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,rectBottom-1));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+2*dx,rectBottom-1));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+dx,rectTop));
ИначеЕсли itemType = 11 Тогда // (нетиповые фигуры) Галстук - бабочка
СтруктураЭлемента.point.Очистить();
dx = Цел((rectBottom-rectTop)/2/Sqrt(3)); // ДельтаX при 60 градусном уклоне
dx = ?(2*dx>(rectRight-rectLeft),Цел((rectRight-rectLeft)/4),dx); // корректировка запредельных значений
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,Цел((rectTop+rectBottom)/2)));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+dx,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",Цел((rectLeft+rectRight)/2),Цел((rectTop+rectBottom)/2)));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1-dx,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1,Цел((rectTop+rectBottom)/2)));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1-dx,rectBottom-1));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",Цел((rectLeft+rectRight)/2),Цел((rectTop+rectBottom)/2)));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+dx,rectBottom-1));
ИначеЕсли itemType = 12 Тогда // (нетиповые фигуры) Звезда шерифа
СтруктураЭлемента.point.Очистить();
// сторона маленького треугольника
dc = Цел((rectRight-rectLeft)/3);
dchalf = Цел(dc/2);
dchigh = Цел(dchalf*Sqrt(3));
//dx = Цел((rectBottom-rectTop)/2/Sqrt(3)); // ДельтаX при 60 градусном уклоне
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,rectTop+dchigh));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+dc,rectTop+dchigh));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+dc+dchalf,rectTop));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1-dc,rectTop+dchigh));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1,rectTop+dchigh));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1-dchalf,rectTop+2*dchigh));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1,rectTop+3*dchigh));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectRight-1-dc,rectTop+3*dchigh));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+dc+dchalf,rectBottom-1));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+dc,rectTop+3*dchigh));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft,rectTop+3*dchigh));
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",rectLeft+dchalf,rectTop+2*dchigh));
ИначеЕсли itemType = 13 Тогда // (нетиповые фигуры) Круг
СтруктураЭлемента.point.Очистить();
// сторона маленького треугольника
radius = МИН(Цел((rectRight-rectLeft)/2),Цел((rectBottom-rectTop)/2));
xcenter = rectLeft + radius;
ycenter = rectTop + radius;
pi = 3.141592635897;
НачУгол = -20;
КонУгол = 200;
ШагГрад = 10;
у = НачУгол;
Пока у<=КонУгол Цикл
Угол = у*pi/180;
x = Окр(Cos(Угол)*radius);
y = Окр(Sin(Угол)*radius);
СтруктураЭлемента.point.Добавить(Новый Структура("x,y",xcenter+x,ycenter+y));
у = у + ШагГрад;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Функция ПолучитьМаксимальноеЗначениеКлючаВМассивеСтруктур(массивДляПоиска,ключСтруктуры,СтартовоеЗначениеКлюча=0)
МаксимальноеЗначениеКлюча = СтартовоеЗначениеКлюча;
Для каждого элемент из массивДляПоиска Цикл
МаксимальноеЗначениеКлюча = Макс(МаксимальноеЗначениеКлюча,элемент[ключСтруктуры]);
КонецЦикла;
Возврат МаксимальноеЗначениеКлюча;
КонецФункции
Функция НайтиПоЗначениюКлючаВМассивеСтруктур(массивДляПоиска,ключСтруктуры,значениеКлюча)
Ответ = Неопределено;
Для каждого элемент из массивДляПоиска Цикл
Если элемент[ключСтруктуры] = значениеКлюча Тогда
Ответ = элемент;
Прервать;
КонецЕсли;
КонецЦикла;
Возврат Ответ;
КонецФункции
Функция ПолучитьСтруктуруТипа(ТипЭлементаГрафическойСхемы,itemCode=Неопределено) Экспорт
МассивТиповЭлементовГрафическойСхемы = Новый Массив;
МассивТиповЭлементовГрафическойСхемы.Добавить("Декорация"); //0 "Декорация"
МассивТиповЭлементовГрафическойСхемы.Добавить("ДекоративнаяЛиния"); //1 "ДекоративнаяЛиния"
МассивТиповЭлементовГрафическойСхемы.Добавить("Старт"); //2 "Старт"
МассивТиповЭлементовГрафическойСхемы.Добавить("Завершение"); //3 "Завершение"
МассивТиповЭлементовГрафическойСхемы.Добавить("Условие"); //4 "Условие"
МассивТиповЭлементовГрафическойСхемы.Добавить("Действие"); //5 "Действие"
МассивТиповЭлементовГрафическойСхемы.Добавить("ВыборВарианта"); //6 "ВыборВарианта"
МассивТиповЭлементовГрафическойСхемы.Добавить("Разделение"); //7 "Разделение"
МассивТиповЭлементовГрафическойСхемы.Добавить("Слияние"); //8 "Слияние"
МассивТиповЭлементовГрафическойСхемы.Добавить("Обработка"); //9 "Обработка"
МассивТиповЭлементовГрафическойСхемы.Добавить("ВложенныйПроцесс"); //10 "ВложенныйПроцесс"
Если itemCode = Неопределено Тогда
itemCode = МассивТиповЭлементовГрафическойСхемы[ТипЭлементаГрафическойСхемы];
КонецЕсли;
Ответ = Новый Структура;
Ответ.Вставить("itemType", ТипЭлементаГрафическойСхемы);
Ответ.Вставить("itemCode", itemCode); // Должно быть задано при добавлении, д.б. уникально
Ответ.Вставить("itemId", Неопределено); // Вычислить при добавлении
Ответ.Вставить("itemTabOrder", Неопределено); // Вычислить при добавлении
Ответ.Вставить("zOrder", Неопределено); // Вычислить при добавлении
Ответ.Вставить("lineColor", Новый Соответствие);
Ответ.lineColor.Вставить("#type", "jv8ui:Color");
Ответ.lineColor.Вставить("#value", "{http://v8.1c.ru/8.1/data/ui/style}BorderColor");
Ответ.Вставить("alignHor", "Center");
Ответ.Вставить("alignVer", "Center");
Ответ.Вставить("backColor", Новый Соответствие);
Ответ.backColor.Вставить("#type", "jv8ui:Color");
Ответ.backColor.Вставить("#value", "auto");
Ответ.Вставить("currentLanguage", "#");
Ответ.Вставить("picturePlacement", "Left");
Ответ.Вставить("textColor", Новый Соответствие);
Ответ.textColor.Вставить("#type", "jv8ui:Color");
Ответ.textColor.Вставить("#value", "{http://v8.1c.ru/8.1/data/ui/style}FormTextColor");
Ответ.Вставить("textFont", Новый Структура("kind","AutoFont"));
Ответ.Вставить("tipText", Новый Соответствие);
Ответ.Вставить("transparent", Ложь);
Ответ.Вставить("hyperlink", Ложь);
Ответ.Вставить("itemTitle", Новый Массив);
Ответ.Вставить("groupNum", 0);
Если ТипЭлементаГрафическойСхемы <> 0 Тогда
Ответ.Вставить("border", Новый Структура("width,gap,style",Новый Соответствие,Ложь,Новый Соответствие));
Ответ.border.width.Вставить("#type", "jxs:decimal");
Ответ.border.width.Вставить("#value", 1);
Ответ.border.style.Вставить("#type", "jsch:ConnectorLineType");
Ответ.border.style.Вставить("#value", "Solid");
Ответ.Вставить("point", Новый Массив);
КонецЕсли;
Если ТипЭлементаГрафическойСхемы <> 1 Тогда
Ответ.Вставить("rectBottom", 40);
Ответ.Вставить("rectLeft", 60);
Ответ.Вставить("rectRight", 80);
Ответ.Вставить("rectTop", 20);
Ответ.Вставить("picture", Новый Соответствие);
Ответ.Вставить("pictureStyle", 4);
КонецЕсли;
Если ТипЭлементаГрафическойСхемы >= 2 Тогда
Ответ.Вставить("pointUUID", Строка(Новый УникальныйИдентификатор));
Ответ.Вставить("passageState", 0);
Ответ.Вставить("tableCode", 0);
КонецЕсли;
Если ТипЭлементаГрафическойСхемы = 0 Тогда
Ответ.Вставить("angle", Новый Соответствие);
Ответ.angle.Вставить("#type", "jxs:decimal");
Ответ.angle.Вставить("#value", 0);
Ответ.Вставить("flipMode", 0);
Ответ.Вставить("shape", "Block");
КонецЕсли;
Если ТипЭлементаГрафическойСхемы = 1 Тогда
Ответ.Вставить("beginArrowStyle", "None");
Ответ.Вставить("connectFromItemId", -1); // Если decorativeLine = Истина, то можно и из ниоткуда
Ответ.Вставить("connectFromPortIndex", 0);
Ответ.Вставить("connectToItemId", -1);
Ответ.Вставить("decorativeLine", Истина); // Если Ложь, то будет неубираемая "пристегнутая" линия к объекту
Ответ.Вставить("endArrowStyle", "Filled");
Ответ.Вставить("portIndexFrom", 4);
Ответ.Вставить("portIndexTo", 0);
Ответ.Вставить("textPos", "FirstSegment");
КонецЕсли;
Если ТипЭлементаГрафическойСхемы = 4 Тогда
Ответ.Вставить("falsePortIndex", 1);
Ответ.Вставить("truePortIndex", 3);
КонецЕсли;
Если ТипЭлементаГрафическойСхемы = 5 ИЛИ ТипЭлементаГрафическойСхемы = 10 Тогда
Ответ.Вставить("taskDescription", itemCode); // Должно быть задано при добавлении; возможно = itemCode
КонецЕсли;
Если ТипЭлементаГрафическойСхемы = 5 Тогда
Ответ.Вставить("addrZoneDivideYPos", 16);
Ответ.Вставить("groupAddressing", Ложь);
Ответ.Вставить("isAddrZoneDivideValid", Истина);
Ответ.Вставить("explanation", "");
КонецЕсли;
Если ТипЭлементаГрафическойСхемы = 6 Тогда
Ответ.Вставить("transition", Новый Массив);
КонецЕсли;
Если ТипЭлементаГрафическойСхемы = 10 Тогда
Ответ.Вставить("subprocessUUID", "00000000-0000-0000-0000-000000000000");
КонецЕсли;
Возврат Ответ;
КонецФункции
Функция ПодготовитьСтруктуруЛинии(Схема,connectFrom,connectTo,portIndexFrom=Неопределено,portIndexTo=Неопределено,decorativeLine=Истина,itemCode=Неопределено) Экспорт
Ответ = ПолучитьСтруктуруТипа(1,itemCode);
Ответ.decorativeLine = decorativeLine; // не декоративная, а прицепленная линия
Ответ.connectFromItemId = ?(ТипЗнч(connectFrom)=Тип("Структура"),connectFrom.ItemId,connectFrom);
Ответ.connectToItemId = ?(ТипЗнч(connectTo)=Тип("Структура"),connectTo.ItemId,connectTo);
Ответ.portIndexFrom = ?(portIndexFrom=Неопределено,1,portIndexFrom); // лево
Ответ.portIndexTo = ?(portIndexTo=Неопределено,2,portIndexTo); // верх
// Рисуем только начало и окончание. Остальное система дополнит сама при отрисовке.
Ответ.point.Добавить(ПолучитьКоординатыЭлемента(Схема,Ответ.connectFromItemId,portIndexFrom,Ответ.connectFromPortIndex));
Ответ.point.Добавить(ПолучитьКоординатыЭлемента(Схема,Ответ.connectToItemId,portIndexTo));
// Багфикс: случай когда линия идет снизу на верхнюю границу
Если Ответ.point[0].y > Ответ.point[1].y И Ответ.portIndexTo = 2 Тогда
//Ответ.point.Вставить(1,Новый Структура("x,y",Ответ.point[1].x,Ответ.point[1].y-Схема["#value"].gridVerticalStep));
xmid = Цел((ПолучитьКоординатыЭлемента(Схема,Ответ.connectFromItemId,3).x + ПолучитьКоординатыЭлемента(Схема,Ответ.connectToItemId,1).x)/2);
Ответ.point.Вставить(1,Новый Структура("x,y",xmid,Ответ.point[1].y-Схема["#value"].gridVerticalStep));
КонецЕсли;
Возврат Ответ;
КонецФункции
Функция ПолучитьКоординатыЭлемента(Схема,itemId,portIndex,connectFromPortIndex=0)
Ответ = Новый Структура("x,y",0,0);
структураСхемы = Схема.Получить("#value");
Если НЕ структураСхемы.Свойство("item") Тогда
Возврат Ответ;
КонецЕсли;
СтруктураЭлемента = НайтиПоЗначениюКлючаВМассивеСтруктур(структураСхемы.item,"itemid",itemId);
Если СтруктураЭлемента = Неопределено Тогда
Возврат Ответ;
КонецЕсли;
// Порты:
//1: Лево
//2: Верх
//3: Право
//4: Низ
//5: Центр
//6: Вариант 1 Лево
//7: Вариант 1 Право
//8: Вариант 2 Лево
//9: Вариант 2 Право
// Каждый вариант - это 18 точек по шкале Y от rectBottom. Середина = rectBottom - (18/2)
dy = 0;
Если portIndex > 5 Тогда
caseCount = СтруктураЭлемента.transition.Количество();
connectFromPortIndex = Цел((portIndex-6)/2);
portIndex = 1 + (portIndex%2)*2;
dy = (caseCount - connectFromPortIndex - 1)*18 + 18/2;
КонецЕсли;
Если portIndex = 1 Тогда
Ответ.x = СтруктураЭлемента.rectLeft;
Ответ.y = ?(dy>0,СтруктураЭлемента.rectBottom-1-dy,Цел((СтруктураЭлемента.rectTop+СтруктураЭлемента.rectBottom+1)/2));
ИначеЕсли portIndex = 2 Тогда
Ответ.x = Цел((СтруктураЭлемента.rectLeft+СтруктураЭлемента.rectRight+1)/2);
Ответ.y = СтруктураЭлемента.rectTop;
ИначеЕсли portIndex = 3 Тогда
Ответ.x = СтруктураЭлемента.rectRight;
Ответ.y = ?(dy>0,СтруктураЭлемента.rectBottom-1-dy,Цел((СтруктураЭлемента.rectTop+СтруктураЭлемента.rectBottom+1)/2));
ИначеЕсли portIndex = 4 Тогда
Ответ.x = Цел((СтруктураЭлемента.rectLeft+СтруктураЭлемента.rectRight+1)/2);
Ответ.y = СтруктураЭлемента.rectBottom;
ИначеЕсли portIndex = 5 Тогда
Ответ.x = Цел((СтруктураЭлемента.rectLeft+СтруктураЭлемента.rectRight+1)/2);
Ответ.y = Цел((СтруктураЭлемента.rectTop+СтруктураЭлемента.rectBottom+1)/2);
КонецЕсли;
Возврат Ответ;
КонецФункции
Функция СформироватьГрафическуюСхему(Схема) Экспорт
// Вывод схемы
ЗаписьJSON = Новый ЗаписьJSON;
ЧтениеJSON = Новый ЧтениеJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON,Схема);
стрJSON = ЗаписьJSON.Закрыть();
Сообщить(стрJSON);
ЧтениеJSON.УстановитьСтроку(стрJSON);
Возврат СериализаторXDTO.ПрочитатьJSON(ЧтениеJSON,Тип("ГрафическаяСхема")); // Вот он результат!
КонецФункции
#КонецОбласти
Я сам пока не готов сказать, что тут "всё сделано", наверняка что-то не учел. Хотел преподнести, как говорится, с пылу - с жару. Поэтому прошу сообщать о найденных багах. Сразу воспроизведу, поправлю, или приму pullRequest по части кода.
Всем спасибо за внимание, плюсы, лайки, баг-репорты...!
P.S.
Заметили в коде возможности рисования? Второй скрин - это как раз результат. В процедуре вывода добавил следующий код (с модификатором фигур):
Модуль вывода дополнительных фигур
_разделение = ПолучитьСтруктуруТипа(ТипЭлементаГрафическойСхемы.Разделение,"ТестБП");
_разделение.itemTitle.Добавить(Новый Структура("lang,content","#","Разделяем"));
//_разделение.taskDescription = "Пишем свое описание";
_id_разделение = ДобавитьЭлемент(Схема,_разделение,585,380,50,30);
ДобавитьЭлемент(Схема,ПодготовитьСтруктуруЛинии(Схема,_id_процесс,_id_разделение,4,2,Ложь));
_действие5 = ПолучитьСтруктуруТипа(ТипЭлементаГрафическойСхемы.Действие);
_действие5.itemTitle.Добавить(Новый Структура("lang,content","#","Произвольное
|задание
|"));
_id_условие5 = ДобавитьЭлемент(Схема,_действие5,400,400,100,100);
_действие5.explanation = "С.Ю.Сидоров";
//РасставитьТочкиФигуры(_условие5,11); // галстук-бабочка
РасставитьТочкиФигуры(_действие5,13); // круг
ДобавитьЭлемент(Схема,ПодготовитьСтруктуруЛинии(Схема,_id_разделение,_id_условие5,4,2,Ложь));
_процесс5 = ПолучитьСтруктуруТипа(ТипЭлементаГрафическойСхемы.Завершение);
_процесс5.alignVer = "Top";
_процесс5.itemTitle.Добавить(Новый Структура("lang,content","#","Звезда
|шерифа
|для победителя"));
_id_процесс5 = ДобавитьЭлемент(Схема,_процесс5,700,400,100,100);
РасставитьТочкиФигуры(_процесс5,12); // звезда шерифа
ДобавитьЭлемент(Схема,ПодготовитьСтруктуруЛинии(Схема,_id_разделение,_id_процесс5,4,2,Ложь));
В планах упрощение API, вынос параметров координат в подготовку структуры элемента, из структуры элемента исключить вычисляемые поля (itemId, zOrder, itemTabOrder) для исключения ошибок, в процедуру ПодготовитьСтруктуруЛинии() добавить возможность обработки в качестве Источника и Приемника готовые структуры Элементов, а не только их itemId (Число) - опять же для удобства и сокращения кода. Результаты представлю по мере готовности...