gifts2017

Программное создание графических схем

Опубликовал Яков Коган (Yashazz) в раздел Программирование - Практика программирования

Пример динамического создания графических схем, добавления элементов любых видов. Любые схемы без бизнес-процессов. Программная работа со схемой.

Сначала я собирался накатать большую и умную статью по итогам исследования внутреннего формата различных общих объектов 1С - всяких там географических карт, дендрограмм и прочих мокселей. О том, как оно устроено, что означает тот или иной кусок и какая польза может от того получиться. Ряд манипуляций возможен только с сериализованным текстом, ряд действий просто быстрее и удобнее. Так вот, а потом мне здраво указали, что теория нафиг не нужна и вообще подавай конкретные примеры.

Сразу оговорюсь: приоритет этой идеи принадлежит безвестным гениям доледникового периода, а я, позорно протормозив, просто сделал для себя то, о чём писал Diversus ещё в лохматые годы. Но, поскольку сие может служить иллюстрацией, и к тому же более-менее работоспособно, то выкладываю в виде кода некоторые наработки как пример. Пример сериализации графической схемы в текст, работы с текстом, и последующей обратной серализации. Причём сделано это всё примитивно и топорно, безо всяких DOM.

К недостаткам схемы стоит отнести сложность позиционирования (вам самостоятельно придётся прикидывать, куда "сунуть" каждый новый объект), и капризная чувствительность к правке. Перетаскивать добавленные объекты, связанные добавленными стрелочками, не советую - всё рискует разъехаться. Но для "только просмотр" это сгодится более чем. Также, напомню, графическая схема поддерживает событие расшифровки, что делает её не просто иллюстративным инструментом, но и навигационным.

Где это может пригодиться? Начиная от построения простейших отчётов-связок, вроде "Структуры подчинённости", и заканчивая отражением связей метаданных, как это делали в механизмах, изучающих внутреннюю структуру конфигураций. Для цели отрисовки графов может пригодиться; правда, опять же, позиционирование надо продумывать самим, в отличие от стороннего софта, обычно используемого в таких случаях.

Собственно, код и все пояснения по формату в описаниях функций. Особенно см. подробное описание функции ДобавитьФигуру.

#Область РаботаСГрафическимиСхемами

&НаСервереБезКонтекста
Функция СериализоватьГС(рГС)
    зап=Новый ЗаписьXML;
    зап.УстановитьСтроку("UTF-8");
    //СериализаторXDTO.ЗаписатьXML(зап,ГС,НазначениеТипаXML.Явное,ФормаXML.Атрибут);
    СериализаторXDTO.ЗаписатьXML(зап,рГС);
    Возврат зап.Закрыть();
КонецФункции

&НаСервереБезКонтекста
Функция ДесериализоватьГС(рСтрока)
    чтен=Новый ЧтениеXML;
    чтен.УстановитьСтроку(рСтрока);
    Возврат СериализаторXDTO.ПрочитатьXML(чтен,Тип("ГрафическаяСхема"));
КонецФункции

// Преобразует значение системного перечисления в его англоязычный вариант представления
&НаКлиенте
Функция АнглИмя(рЗначение)
    Возврат СокрЛП(СериализаторXDTO.XMLСтрока(рЗначение));
КонецФункции

&НаКлиенте
// Возвращает список значений, где значение - тип (тип элемента граф.схемы),
// а порядковый номер в списке значений соответствует коду сериализации.
//
Функция ПолучитьТипыЭлементов()
    сптэ=Новый СписокЗначений;
    сптэ.Добавить(Тип("ЭлементГрафическойСхемыДекорация"),"Декорация");
    сптэ.Добавить(Тип("ЭлементГрафическойСхемыДекоративнаяЛиния"),"Линия");
    сптэ.Добавить(Тип("ЭлементГрафическойСхемыСтарт"),"Начало");
    сптэ.Добавить(Тип("ЭлементГрафическойСхемыЗавершение"),"Завершение");
    сптэ.Добавить(Тип("ЭлементГрафическойСхемыУсловие"),"Условие");
    сптэ.Добавить(Тип("ЭлементГрафическойСхемыДействие"),"Действие");
    сптэ.Добавить(Тип("ЭлементГрафическойСхемыВыборВарианта"),"Выбор варианта");
    сптэ.Добавить(Тип("ЭлементГрафическойСхемыРазделение"),"Разделение");
    сптэ.Добавить(Тип("ЭлементГрафическойСхемыСлияние"),"Слияние");
    сптэ.Добавить(Тип("ЭлементГрафическойСхемыОбработка"),"Обработка");
    сптэ.Добавить(Тип("ЭлементГрафическойСхемыВложенныйБизнесПроцесс"),"Вложенный БП");
    Возврат сптэ;
КонецФункции

&НаКлиенте
// Возвращает соответствие, где ключ - значение типа ТипСтороныЭлементаГрафическойСхемы, а значение - число.
// Значения чисел и их смысл: 
//    1 - влево, 2 - вверх, 
//    3, 5, 7, 9, 11 итд - вправо (3 в блоках и условиях, 5 в обычных блоках, 7 и более в выборе вариантов);
//    4 - вниз, 6 и 8 - ???
//
Функция ПолучитьТипыСторонЭлементов()
    соот=Новый Соответствие;
    соот.Вставить(ТипСтороныЭлементаГрафическойСхемы.Лево,1);
    соот.Вставить(ТипСтороныЭлементаГрафическойСхемы.Верх,2);
    соот.Вставить(ТипСтороныЭлементаГрафическойСхемы.Право,3);
    соот.Вставить(ТипСтороныЭлементаГрафическойСхемы.Низ,4);
    Возврат соот;
КонецФункции

&НаКлиенте
// Разбирает текст по элементам с именем itemId, рассматривает их как числа и возвращает значение наибольшего.
//
// Параметры:
//    рТекст - рассматриваемый текст сериализованной граф.схемы.
//
Функция ПолучитьСтаршийИдентификатор(рТекст) Экспорт
    чтен=Новый ЧтениеXML;
    чтен.УстановитьСтроку(рТекст);
    постр=Новый ПостроительDOM;
    докДом=постр.Прочитать(чтен);
    спис=докДом.ПолучитьЭлементыПоИмени("itemId");
    максИд=0;
    Для каждого знч Из спис Цикл
        Попытка чисИд=Число(СокрЛП(знч.ТекстовоеСодержимое)) Исключение чисИд=0 КонецПопытки;
        максИд=Макс(максИд,чисИд);
    КонецЦикла;
    Возврат максИд+1;
КонецФункции

&НаКлиенте
// Возвращает строку, содержащую теги, описывающие фигуру элемента графической схемы согласно её типу.
// Обычно вызывается из ДобавитьФигуру и самостоятельно не употребляется.
// При ошибке возвращает пустую строку.
//
// Параметры:
//    рТипЭлемента - числовой тип элемента граф.схемы; должен быть ранее преобразован из типа Тип, если надо;
//    рКоординатыКонтура - структура, обязательно уже содержащая ключи Верх, Низ, Лево, Право с числовыми значениями;
//        если этих значений нет, то выполняется попытка их автоопределить (для линии их можно пропускать, найдёт сама по
//        данным объекта-откуда и объекта-куда). Вместо "Низ" можно указывать "Высота"; вместо "Право" можно "Ширина";
//        для линий можно задать только ключи "СерединаГор" и "СерединаВерт", координаты средней точки линии, что бывает
//        нужно для диагонально-ориентированных линий.
//
Функция ДобавитьКоординатыФигуры(рТипЭлемента,рКоординатыКонтура)
Попытка
    рВерх=рКоординатыКонтура.Верх;
    рНиз=рКоординатыКонтура.Низ;
    рЛево=рКоординатыКонтура.Лево;
    рПраво=рКоординатыКонтура.Право;
    рСерединаГор=рЛево+(рПраво-рЛево)/2;
    рСерединаВер=рВерх+(рНиз-рВерх)/2;
    //
    сс="";
    Если рТипЭлемента=1 Тогда // декор.линия
        х=рЛево; у=рВерх;     сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        Если рКоординатыКонтура.Свойство("СерединаГор") Тогда
            х=рКоординатыКонтура.СерединаГор;
        Иначе
            х=рСерединаГор; 
        КонецЕсли;
        Если рКоординатыКонтура.Свойство("СерединаВерт") Тогда
            у=рКоординатыКонтура.СерединаВерт;
        Иначе
            у=рСерединаВер;
        КонецЕсли;
        сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рПраво-1; у=рНиз-1; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
    ИначеЕсли рТипЭлемента=2 Тогда // начало
        х=рЛево; у=рВерх;     сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";        
        х=рПраво-1; у=рВерх; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рПраво-1; у=рСерединаВер; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рСерединаГор; у=рНиз-1; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рЛево; у=рСерединаВер; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
    ИначеЕсли рТипЭлемента=3 Тогда // завершение
        х=рСерединаГор; у=рВерх; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рПраво-1; у=рСерединаВер; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рПраво-1; у=рНиз-1; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рЛево; у=рНиз-1; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рЛево; у=рСерединаВер; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
    ИначеЕсли рТипЭлемента=4 Тогда // условие
        х=рЛево; у=рСерединаВер; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рЛево+11; у=рВерх; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рПраво-12; у=рВерх; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рПраво-1; у=рСерединаВер; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рПраво-12; у=рНиз-1; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рЛево+11; у=рНиз-1; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
    ИначеЕсли рТипЭлемента=7 Тогда // разделение
        х=рЛево; у=рВерх; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рПраво-1; у=рВерх; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рСерединаГор; у=рНиз-1; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
    ИначеЕсли рТипЭлемента=8 Тогда // слияние
        х=рЛево; у=рНиз-1; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рПраво-1; у=рНиз-1; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рСерединаГор; у=рВерх; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
    Иначе // действие, вариант, обработка, вложенный БП
        х=рЛево; у=рВерх; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рПраво-1; у=рВерх; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рПраво-1; у=рНиз-1; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
        х=рЛево; у=рНиз-1; сс=сс+"<point><x>"+Строка(х)+"</x><y>"+Строка(у)+"</y></point>";
    КонецЕсли;
    //
    Возврат сс;
Исключение
    Сообщить("ДобавитьКоординатыФигуры, ошибка: "+ОписаниеОшибки());
    Возврат "";
КонецПопытки;
КонецФункции

&НаКлиенте
// Готовит фрагмент xml-текста согласно указанным параметрам; внутри себя дописывает этот фрагмент в общую строку всей схемы.
// Возвращает строку фрагмента, соответствующую добавленному элементу графической схемы (фигуре).
// При ошибке возвращает пустую строку.
//
// Параметры:
//    строГС - строка сериализованной схемы в целом; изменяется внутри функции;
//    пар (структура), описание см.ниже в области "ОписаниеПараметров":
//
#Область ОписаниеПараметров

#Область ОбщиеДляВсехЭлементов
    //   Имя (строка), 
    //   ТипЭлемента (тип, строка или число от 0 до 10 - имя типа элемента ГС в нотации 1С или числовой идентификатор)
    //   [Идентификатор] (число, полученное, например, с помощью ПолучитьСтаршийИдентификатор, itemId), 
    //   ТекстЗаголовка (строка), 
    //   [ТекстПодсказки] (строка), 
    //   [НомерВОбходе] (число; может совпадать с Идентификатор), 
    //   [Гиперссылка] (булево),
    //   [Прозрачность] (булево),
    //   [ГоризонтальноеПоложение] (ГоризонтальноеПоложение),
    //   [ВертикальноеПоложение] (ВертикальноеПоложение),
    //   [ПоложениеКартинки] (ПоложениеКартинкиЭлементаГрафическойСхемы),
    //   КоординатыКонтура (структура с числовыми значениями ключей: Лево,Верх,Право,Низ или Лево,Верх,Ширина,Высота, причём
    //       при задании Право, Низ правая граница считается относительно 0, а не левой, и нижняя граница - относительно 0, а не верхней.
    //       для линий это необязательный параметр; при его отсутствии линия пытается определиться по Откуда/Куда 
    //       и НаправлениеОткуда/НаправлениеКуда; при его наличии Лево,Верх это начальная точка линии, а Право,Низ - конечная; причём
    //       задание через Ширина и Высота также приведёт к нужному результату.
    //    Изменение координат контура выполняется с изменением коллекции параметров.
    //
#КонецОбласти

#Область ДляДекорации
    //   Фигура (ФигурыГрафическойСхемы)
#КонецОбласти

#Область ДляЛинии
    //   Откуда (число) - идентификатор (itemId) элемента, откуда идёт линия, если ниоткуда не идёт, то -1
    //   Куда (число) - дентификатор (itemId) элемента, куда идёт линия, если никуда не идёт, то -1
    //   ВариантОткуда (для линий, выходящих из выбора варианта, № варианта (нач.с 0),
    //   [ДекоративнаяЛиния] (булево),
    //   [ПоложениеТекста] (ПоложениеТекстаСоединительнойЛинии),
    //   [СтильСтрелкиНачало] (СтильСтрелки),
    //   [СтильСтрелкиКонец] (СтильСтрелки),
    //   НаправлениеОткуда (числовой номер визуального направления отправителя; край, откуда выходит линия),
    //   НаправлениеКуда (числовой номер визуального направления получателя; край, куда входит линия),
    //         т.е. это стороны объектов, связанных линией, куда прикрепляется линия тем или иным своим концом:
    //         1 - влево, 2 - вверх, 
    //         3, 5, 7, 9, 11 итд - вправо (3 в блоках и условиях, 5 в обычных блоках, 7 и более в выборе вариантов);
    //         4 - вниз, 6 и 8 - ???
    //         или значение типа ТипСтороныЭлементаГрафическойСхемы.
    //   Если указано направление типа ТипСтороныЭлементаГрафическойСхемы, то оно преобразуется в число также и в параметрах.
#КонецОбласти

#Область ДляУсловия
    // выход ветки (1 - влево, 3 - вправо)
    //   [НаправлениеВеткиДа] (число),
    //   [НаправлениеВеткиНет] (число).
#КонецОбласти

#Область ДляДействия
    //   ОписаниеДействия (строка), 
    //   АдресацияДействия (строка), 
    //   [ГрупповаяАдресация] (булево), 
    //   [ВысотаОбластиАдресации] (число)
#КонецОбласти

#Область ДляВыбораВарианта
    //  Варианты - массив структур: Идентификатор (строка), ТекстЗаголовка (строка), Куда (идентификатор получателя стрелки), НаправлениеКуда (его боковина)
#КонецОбласти

#Область ДляВложенногоБП
    //   ТекстЗаголовкаБП (строка)
#КонецОбласти

#КонецОбласти
//
//    также по ходу работы в структуру "пар" добавляются или могут для оптимизации быть указаны заранее:
//        СписокТиповЭлементов - список значений, согласно функции ПолучитьТипыЭлементов();
//        ТипыСторонЭлементов - соответствие, согласно функции ПолучитьТипыСторонЭлементов(); вносится только при добавлении линий.
//    соотДобавленных - соответствие, где ключи - числовые ID элементов, а значения - структуры параметров их построения.
//        
Функция ДобавитьФигуру(строГС,пар,соотДобавленных=Неопределено)
Попытка
    Если ТипЗнч(соотДобавленных)<>Тип("Соответствие") Тогда соотДобавленных=Новый Соответствие КонецЕсли;
    
    #Область ОбработкаТипаИИдентификатора
    
    рТипЭлемента=ТипЗнч(пар.ТипЭлемента);
    Если рТипЭлемента=Тип("Строка") Тогда
        Попытка чисТип=Число(СокрЛП(пар.ТипЭлемента)) Исключение чисТип=-1 КонецПопытки;
        Если чисТип=-1 Тогда
            // передано имя типа элемента в нотации 1С, преобразуем в идентификатор
            Если пар.Свойство("СписокТиповЭлементов") Тогда
                сптэ=пар.СписокТиповЭлементов;
            Иначе // получим "по месту" и занесём в параметры
                сптэ=ПолучитьТипыЭлементов();
                пар.Вставить("СписокТиповЭлементов",сптэ);
            КонецЕсли;
            пози=сптэ.НайтиПоЗначению(Тип(СокрЛП(пар.ТипЭлемента)));
            Если пози=Неопределено Тогда
                Сообщить("ДобавитьФигуру: не удалось найти идентификатор типа """+СокрЛП(пар.ТипЭлемента)+", фигура не добавляется!",СтатусСообщения.Важное);
                Возврат "";
            КонецЕсли;        
            рТипЭлемента=сптэ.Индекс(пози);
        Иначе
            // передан сразу идентификатор; проверять не будем, сразу вставляем
            рТипЭлемента=чисТип;
        КонецЕсли;        
    ИначеЕсли рТипЭлемента=Тип("Тип") Тогда
        // передан тип элемента (удобнее писать с использованием подсказок 1С), преобразуем в идентификатор
        Если пар.Свойство("СписокТиповЭлементов") Тогда
            сптэ=пар.СписокТиповЭлементов;
        Иначе // получим "по месту" и занесём в параметры
            сптэ=ПолучитьТипыЭлементов();
            пар.Вставить("СписокТиповЭлементов",сптэ);
        КонецЕсли;
        пози=сптэ.НайтиПоЗначению(пар.ТипЭлемента);
        Если пози=Неопределено Тогда
            Сообщить("ДобавитьФигуру: не удалось найти идентификатор типа """+СокрЛП(пар.ТипЭлемента)+", фигура не добавляется!",СтатусСообщения.Важное);
            Возврат "";
        КонецЕсли;        
        рТипЭлемента=сптэ.Индекс(пози);
    ИначеЕсли рТипЭлемента=Тип("Число") Тогда
        // передан сразу идентификатор; проверять не будем, сразу вставляем
        рТипЭлемента=пар.ТипЭлемента;
    КонецЕсли;
    Если рТипЭлемента<0 или рТипЭлемента>10 Тогда Возврат "" КонецЕсли;
    //
    Если не пар.Свойство("Идентификатор") Тогда // получаем "по месту" как старшего и занесём в параметры
        // при такой идеологии работы важно потом переустановить/сбросить значение этого параметра!
        рИдентификатор=ПолучитьСтаршийИдентификатор(строГС);
        пар.Вставить("Идентификатор",рИдентификатор);
    Иначе
        рИдентификатор=пар.Идентификатор;
    КонецЕсли;    
    
    #КонецОбласти
    
    фБул="БЛ=false; БИ=true";
    
    сс="
    |<item>";
    
    #Область ДобавлениеОбщихСвойств
        
    // общие свойства
    сс=сс+"
    |<itemType>"+СокрЛП(рТипЭлемента)+"</itemType>
    |<itemId>"+СокрЛП(рИдентификатор)+"</itemId>
    |<currentLanguage>#</currentLanguage>
    |<itemTitle><item xmlns=""http://v8.1c.ru/8.1/data/core""><lang>#</lang>
    |<content>"+пар.ТекстЗаголовка+"</content></item></itemTitle>
    |<tipText><item xmlns=""http://v8.1c.ru/8.1/data/core""><lang>#</lang>
    |<content>"+?(пар.Свойство("ТекстПодсказки"),пар.ТекстПодсказки,"")+"</content></item></tipText>
    |<itemCode>"+пар.Имя+"</itemCode>
    |<itemTabOrder>"+СокрЛП(?(пар.Свойство("НомерВОбходе"),пар.НомерВОбходе,рИдентификатор))+"</itemTabOrder>
    // // пар.Оформление.ЦветФона,  // пар.Оформление.ЦветТекста,  // пар.Оформление.ЦветЛинии,  // пар.Оформление.Шрифт
    |<backColor>auto</backColor>
    |<textColor xmlns:d3p1=""http://v8.1c.ru/8.1/data/ui/style"">d3p1:FormTextColor</textColor>
    |<lineColor xmlns:d3p1=""http://v8.1c.ru/8.1/data/ui/style"">d3p1:BorderColor</lineColor>
    |<groupNum>0</groupNum>
    |<zOrder>"+СокрЛП(?(пар.Свойство("НомерВОбходе"),пар.НомерВОбходе,рИдентификатор))+"</zOrder>
    |<hyperlink>"+?(пар.Свойство("Гиперссылка"),Формат(пар.Гиперссылка,фБул),"false")+"</hyperlink>
    |<transparent>"+?(пар.Свойство("Прозрачность"),Формат(пар.Прозрачность,фБул),"false")+"</transparent>
    |<textFont kind=""AutoFont""/>
    |<alignHor>"+?(пар.Свойство("ГоризонтальноеПоложение"),АнглИмя(пар.ГоризонтальноеПоложение),"Center")+"</alignHor>
    |<alignVer>"+?(пар.Свойство("ВертикальноеПоложение"),АнглИмя(пар.ВертикальноеПоложение),"Center")+"</alignVer>
    |<picturePlacement>"+?(пар.Свойство("ПоложениеКартинки"),АнглИмя(пар.ПоложениеКартинки),"Left")+"</picturePlacement>
    // работу с картинками пока не реализуем
    |<picture/>
    |<pictureStyle>4</pictureStyle>
    // pictureStyle - порядковый номер значения сис.перечисления РазмерКартинки, как он задан не в СП, а в списке выбора палитры свойств, 4 - АвтоРазмер
    //<pointUUID>GUIDОбъекта</pointUUID>
    |";
    
    #КонецОбласти
    
    #Область ДополнительнаяПодготовка
    
    Если рТипЭлемента=1 Тогда
        рНаправлениеОткуда=пар.НаправлениеОткуда;
        Если ТипЗнч(рНаправлениеОткуда)=Тип("ТипСтороныЭлементаГрафическойСхемы") Тогда
            Если пар.Свойство("ТипыСторонЭлементов") Тогда
                соотНапр=пар.ТипыСторонЭлементов;
            Иначе
                соотНапр=ПолучитьТипыСторонЭлементов();
                пар.Вставить("ТипыСторонЭлементов",соотНапр);
            КонецЕсли;
            рНаправлениеОткуда=соотНапр.Получить(рНаправлениеОткуда);
            Если рНаправлениеОткуда=Неопределено Тогда рНаправлениеОткуда=4 КонецЕсли; // из нижней
            пар.Вставить("НаправлениеОткуда",рНаправлениеОткуда);
        КонецЕсли;
        //
        рНаправлениеКуда=пар.НаправлениеКуда;
        Если ТипЗнч(рНаправлениеКуда)=Тип("ТипСтороныЭлементаГрафическойСхемы") Тогда
            Если пар.Свойство("ТипыСторонЭлементов") Тогда
                соотНапр=пар.ТипыСторонЭлементов;
            Иначе
                соотНапр=ПолучитьТипыСторонЭлементов();
                пар.Вставить("ТипыСторонЭлементов",соотНапр);
            КонецЕсли;
            рНаправлениеКуда=соотНапр.Получить(рНаправлениеКуда);
            Если рНаправлениеКуда=Неопределено Тогда рНаправлениеКуда=4 КонецЕсли; // из нижней
            пар.Вставить("НаправлениеКуда",рНаправлениеКуда);            
        КонецЕсли;
        //
    Иначе
        // эти параметры задём статично (более частные свойства)
        //сс=сс+"
        //|<passageState>0</passageState>
        //|<tableCode>34</tableCode>
        //|<bpPointValue xmlns:d3p1="http://v8.1c.ru/8.1/data/enterprise/current-config" xsi:type="d3p1:BusinessProcessRoutePointRef.БизнесПроцесс1">18c0a78b-c146-4790-86b7-9f833864fadf</bpPointValue>
        //|";
        //
    КонецЕсли;
    
    #КонецОбласти
    
    #Область ДобавлениеКонтураИКоординат
    
    // очерчиваем область, где будет расположен элемент (для линии могут быть не заданы)
    рКоординатыБылиЗаданы=Истина;
    рКоординатыОткудаЗаданы=Истина; 
    рКоординатыКудаЗаданы=Истина;
    //
    Если не пар.Свойство("КоординатыКонтура") Тогда
        рКоординатыБылиЗаданы=Ложь;
        рКоординатыОткудаЗаданы=Ложь; 
        рКоординатыКудаЗаданы=Ложь;
        пар.Вставить("КоординатыКонтура",Новый Структура);
    Иначе
        Если не пар.КоординатыКонтура.Свойство("Ширина") 
        и не пар.КоординатыКонтура.Свойство("Высота")
        и не пар.КоординатыКонтура.Свойство("Лево")
        и не пар.КоординатыКонтура.Свойство("Право")
        и не пар.КоординатыКонтура.Свойство("Верх")
        и не пар.КоординатыКонтура.Свойство("Низ")
        Тогда
            // переданы, возможно, только координаты середины линии, а остальное надо автоопределить
            рКоординатыБылиЗаданы=Ложь;
        КонецЕсли;
        Если не пар.КоординатыКонтура.Свойство("Лево") и не пар.КоординатыКонтура.Свойство("Верх") Тогда
            рКоординатыОткудаЗаданы=Ложь;
        КонецЕсли;        
        Если (не пар.КоординатыКонтура.Свойство("Ширина") и не пар.КоординатыКонтура.Свойство("Право"))
        или (не пар.КоординатыКонтура.Свойство("Высота") и не пар.КоординатыКонтура.Свойство("Низ"))
        Тогда 
            рКоординатыКудаЗаданы=Ложь;
        КонецЕсли;
    КонецЕсли;
    //
    Если пар.КоординатыКонтура.Свойство("Ширина") и не пар.КоординатыКонтура.Свойство("Право") Тогда 
        пар.КоординатыКонтура.Вставить("Право",пар.КоординатыКонтура.Лево+пар.КоординатыКонтура.Ширина);
    КонецЕсли;
    Если пар.КоординатыКонтура.Свойство("Высота") и не пар.КоординатыКонтура.Свойство("Низ") Тогда 
        пар.КоординатыКонтура.Вставить("Низ",пар.КоординатыКонтура.Верх+пар.КоординатыКонтура.Высота);
    КонецЕсли;
    //
    Если рТипЭлемента=1 Тогда // линия
        Если не рКоординатыБылиЗаданы или не рКоординатыОткудаЗаданы Тогда
            Попытка рОткуда=соотДобавленных.Получить(пар.Откуда) Исключение рОткуда=Неопределено КонецПопытки;
            Если рОткуда<>Неопределено Тогда
                рЛево=-1; рВерх=-1;
                Если пар.НаправлениеОткуда=1 Тогда // выходим влево, нужна середина левой границы
                    рЛево=рОткуда.КоординатыКонтура.Лево;
                    рВерх=рОткуда.КоординатыКонтура.Верх+(рОткуда.КоординатыКонтура.Низ-рОткуда.КоординатыКонтура.Верх)/2;
                ИначеЕсли пар.НаправлениеОткуда=2 Тогда // выходим вверх, нужна середина верхней границы
                    рЛево=рОткуда.КоординатыКонтура.Лево+(рОткуда.КоординатыКонтура.Право-рОткуда.КоординатыКонтура.Лево)/2;
                    рВерх=рОткуда.КоординатыКонтура.Верх;
                ИначеЕсли пар.НаправлениеОткуда=3 или пар.НаправлениеОткуда=5 Тогда // выходим вправо стандартно, середина правой границы
                    рЛево=рОткуда.КоординатыКонтура.Право;
                    рВерх=рОткуда.КоординатыКонтура.Верх+(рОткуда.КоординатыКонтура.Низ-рОткуда.КоординатыКонтура.Верх)/2;
                ИначеЕсли пар.НаправлениеОткуда=4 Тогда // выходим вниз, нужна середина нижней границы
                    рЛево=рОткуда.КоординатыКонтура.Лево+(рОткуда.КоординатыКонтура.Право-рОткуда.КоординатыКонтура.Лево)/2;
                    рВерх=рОткуда.КоординатыКонтура.Низ;
                КонецЕсли;
                Если рЛево<>-1 Тогда пар.КоординатыКонтура.Вставить("Лево",рЛево) КонецЕсли;
                Если рВерх<>-1 Тогда пар.КоординатыКонтура.Вставить("Верх",рВерх) КонецЕсли;
            КонецЕсли;
        КонецЕсли;
        //
        Если не рКоординатыБылиЗаданы или не рКоординатыКудаЗаданы Тогда            
            Попытка рКуда=соотДобавленных.Получить(пар.Куда) Исключение рКуда=Неопределено КонецПопытки;
            Если рКуда<>Неопределено Тогда
                рПраво=-1; рНиз=-1;
                Если пар.НаправлениеКуда=1 Тогда // входим влево, нужна середина левой границы
                    рПраво=рКуда.КоординатыКонтура.Лево;
                    рНиз=рКуда.КоординатыКонтура.Верх+(рКуда.КоординатыКонтура.Низ-рКуда.КоординатыКонтура.Верх)/2;
                ИначеЕсли пар.НаправлениеКуда=2 Тогда // входим вверх, нужна середина верхней границы
                    рПраво=рКуда.КоординатыКонтура.Лево+(рКуда.КоординатыКонтура.Право-рКуда.КоординатыКонтура.Лево)/2;
                    рНиз=рКуда.КоординатыКонтура.Верх;
                ИначеЕсли пар.НаправлениеКуда=3 или пар.НаправлениеКуда=5 Тогда // входим вправо стандартно, середина правой границы
                    рПраво=рКуда.КоординатыКонтура.Право;
                    рНиз=рКуда.КоординатыКонтура.Верх+(рКуда.КоординатыКонтура.Низ-рКуда.КоординатыКонтура.Верх)/2;
                ИначеЕсли пар.НаправлениеКуда=4 Тогда // входим вниз, нужна середина нижней границы
                    рПраво=рКуда.КоординатыКонтура.Лево+(рКуда.КоординатыКонтура.Право-рКуда.КоординатыКонтура.Лево)/2;
                    рНиз=рКуда.КоординатыКонтура.Низ;
                КонецЕсли;
                Если рПраво<>-1 Тогда пар.КоординатыКонтура.Вставить("Право",рПраво) КонецЕсли;
                Если рНиз<>-1 Тогда пар.КоординатыКонтура.Вставить("Низ",рНиз) КонецЕсли;
            КонецЕсли;
        КонецЕсли;
        //
    Иначе
        // не линия, т.е. имеет контур
        Попытка
            сс=сс+"
            |<rectLeft>"+СокрЛП(пар.КоординатыКонтура.Лево)+"</rectLeft>
            |<rectRight>"+СокрЛП(пар.КоординатыКонтура.Право)+"</rectRight> 
            |<rectTop>"+СокрЛП(пар.КоординатыКонтура.Верх)+"</rectTop>
            |<rectBottom>"+СокрЛП(пар.КоординатыКонтура.Низ)+"</rectBottom>";
        Исключение
            Сообщить("Ошибка получения координат контура: "+ОписаниеОшибки());
            Возврат "";
        КонецПопытки;        
    КонецЕсли;
    // рисуем реальные координаты фигур (актуально для всех, в т.ч. прямоугольных и линии)
    сс=сс+ДобавитьКоординатыФигуры(рТипЭлемента,пар.КоординатыКонтура);
    
    #КонецОбласти
    
    // возможно, доделать это место!
    сс=сс+"
    |<border width=""1"" gap=""false"">
    |  <style xmlns=""http://v8.1c.ru/8.1/data/ui"" xmlns:d4p1=""http://v8.1c.ru/8.2/data/graphscheme"" xsi:type=""d4p1:ConnectorLineType"">Solid</style>
    |</border>";    

    #Область ДобавлениеСпецифическихСвойств
    
    Если рТипЭлемента=0 Тогда // декорация
        сс=сс+"        
        |<shape>"+АнглИмя(пар.Фигура)+"</shape>
        |<flipMode>0</flipMode>
        |<angle xsi:type=""xs:decimal"">0</angle>"; // вероятно, поворот вокруг своей оси
        
    ИначеЕсли рТипЭлемента=1 Тогда // линия
        сс=сс+"
        |<connectFromItemId>"+СокрЛП(пар.Откуда)+"</connectFromItemId>
        |<connectFromPortIndex>"+СокрЛП(пар.ВариантОткуда)+"</connectFromPortIndex>
        |<connectToItemId>"+СокрЛП(пар.Куда)+"</connectToItemId>
        |<decorativeLine>"+?(пар.Свойство("ДекоративнаяЛиния"),Формат(пар.ДекоративнаяЛиния,фБул),"false")+"</decorativeLine>
        |<portIndexFrom>"+СокрЛП(пар.НаправлениеОткуда)+"</portIndexFrom>
        |<portIndexTo>"+СокрЛП(пар.НаправлениеКуда)+"</portIndexTo>
        |<textPos>"+?(пар.Свойство("ПоложениеТекста"),АнглИмя(пар.ПоложениеТекста),"FirstSegment")+"</textPos>
        |<beginArrowStyle>"+?(пар.Свойство("СтильСтрелкиНачало"),АнглИмя(пар.СтильСтрелкиНачало),"None")+"</beginArrowStyle>
        |<endArrowStyle>"+?(пар.Свойство("СтильСтрелкиКонец"),АнглИмя(пар.СтильСтрелкиКонец),"Filled")+"</endArrowStyle>";
        
    ИначеЕсли рТипЭлемента=2 Тогда // начало
        // специальных нет
        
    ИначеЕсли рТипЭлемента=3 Тогда // завершение
        // специальных нет
        
    ИначеЕсли рТипЭлемента=4 Тогда // условие
        сс=сс+"        
        //<eventHandler>
        //   <id>0</id>
        //   <name>БлокУсловияПроверкаУсловия</name>
        ///eventHandler>        
        // выход ветки Нет (1 - влево, 3 - вправо)
        |<truePortIndex>"+?(пар.Свойство("НаправлениеВеткиДа"),Строка(пар.НаправлениеВеткиДа),"3")+"</truePortIndex>
        |<falsePortIndex>"+?(пар.Свойство("НаправлениеВеткиНет"),Строка(пар.НаправлениеВеткиНет),"1")+"</falsePortIndex>";
        
    ИначеЕсли рТипЭлемента=5 Тогда // действие
        сс=сс+"
        |<taskDescription>"+пар.ОписаниеДействия+"</taskDescription>
        |<explanation>"+пар.АдресацияДействия+"</explanation>
        |<groupAddressing>"+?(пар.Свойство("ГрупповаяАдресация"),Формат(пар.ГрупповаяАдресация,фБул),"false")+"</groupAddressing>
        // значение разделения на область адресации и область описания, ставим всегда Истина
        |<isAddrZoneDivideValid>true</isAddrZoneDivideValid>
        // позиция разделителя по высоте между областью адресации и описанием действия
        |<addrZoneDivideYPos>"+Строка(?(пар.Свойство("ВысотаОбластиАдресации"),пар.ВысотаОбластиАдресации,16))+"</addrZoneDivideYPos>
        |";
        
    ИначеЕсли рТипЭлемента=6 Тогда // выбор варианта
        квоВариантов=пар.Варианты.Количество();
        Для й=0 По квоВариантов-1 Цикл
            рВариант=пар.Варианты.Получить(й);
            сс=сс+"
            |<transition>
            |<name>"+рВариант.Идентификатор+"</name>
            |<description><item xmlns=""http://v8.1c.ru/8.1/data/core""><lang>#</lang>
            |<content>"+рВариант.ТекстЗаголовка+"</content></item></description>
            |<backColor>auto</backColor>
            |</transition>";
            //
            // добавляем линию с координатой, начинающейся от середины варианта
            //парПолучателяЛинии=соотДобавленных.Получить(рВариант.Куда); - если надо получитьэти данные
            парЛВ=Новый Структура;
            парЛВ.Вставить("Имя","ЛинияВарианта"+рВариант.Идентификатор);
            парЛВ.Вставить("ТипЭлемента","ЭлементГрафическойСхемыДекоративнаяЛиния");
            парЛВ.Вставить("Идентификатор",ПолучитьСтаршийИдентификатор(строГС));
            парЛВ.Вставить("ТекстЗаголовка","");
            парЛВ.Вставить("Откуда",рИдентификатор);
            парЛВ.Вставить("Куда",рВариант.Куда); // идентификатор элемента, куда должна вести стрелка от варианта
            парЛВ.Вставить("ВариантОткуда",0);
            парЛВ.Вставить("ДекоративнаяЛиния",Истина);
            парЛВ.Вставить("ПоложениеТекста",ПоложениеТекстаСоединительнойЛинии.СерединаЛинии);
            парЛВ.Вставить("НаправлениеОткуда",ТипСтороныЭлементаГрафическойСхемы.Право); // всегда
            парЛВ.Вставить("НаправлениеКуда",?(ЗначениеЗаполнено(рВариант.НаправлениеКуда),рВариант.НаправлениеКуда,ТипСтороныЭлементаГрафическойСхемы.Верх));
            рКоординатыЛинии=Новый Структура;
            рКоординатыЛинии.Вставить("Лево",пар.КоординатыКонтура.Право);
            рКоординатыЛинии.Вставить("Верх",пар.КоординатыКонтура.Низ-9-18*(квоВариантов-1-й));
            парЛВ.Вставить("КоординатыКонтура",рКоординатыЛинии);
            ДобавитьФигуру(строГС,парЛВ,соотДобавленных);
        КонецЦикла;
        
        //<eventHandler>
        //    <id>0</id>
        //    <name>БлокВыборВариантаОбработкаВыбораВарианта</name>
        //</eventHandler>
        
    ИначеЕсли рТипЭлемента=7 Тогда // точка разделения
        // специальных нет
        
    ИначеЕсли рТипЭлемента=8 Тогда // точка слияния
        // специальных нет
        
    ИначеЕсли рТипЭлемента=9 Тогда // обработка
        // специальных нет
        
    ИначеЕсли рТипЭлемента=10 Тогда // вложенный процесс
        //<subprocessUUID>a5f642e1-d2ab-4106-b6dc-d6f85222c430</subprocessUUID>
        сс=сс+"
        |<taskDescription>"+пар.ТекстЗаголовкаБП+"</taskDescription>";
        
    КонецЕсли;
    
    #КонецОбласти

    сс=сс+"
    |</item>";
    
    строГС=СтрЗаменить(строГС,"<!--ReplacingPoint-->",сс+Символы.ПС+"<!--ReplacingPoint-->");
    
    соотДобавленных.Вставить(рИдентификатор,пар);

    Возврат сс;
    
Исключение
    Сообщить("ДобавитьФигуру, ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
    Возврат "";
КонецПопытки;
КонецФункции

&НаКлиенте
// Добавляет упрощённый вариант соединительной декоративной линии между элементами ГС.
// Все значения линии ставятся по умолчанию.
// Возвращает кусок строки, соответствующий линии, аналогично ДобавитьФигуру для неё.
// При ошибке возвращает пустую строку.
//
// Параметры:
//    строГС - строка сериализованной схемы в целом; изменяется внутри функции;
//    соотДобавленных - соответствие, где ключи - числовые ID элементов, а значения - структуры параметров их построения;
//    рОткуда - идентификатор или строковое имя элемента, откуда начинается линия;
//    рКуда - идентификатор или строковое имя элемента, где заканчивается линия;
//    рНаправление - строковое указание, как в целом направлена линия (допустимы Вверх, Вниз, Влево, Вправо).
//
Функция СоединитьФигурыЛинией(строГС,соотДобавленных,рОткуда,рКуда,рНаправление="Вниз")
Попытка
    пар=Новый Структура;
    //
    Если рНаправление="Влево" Тогда
        пар.Вставить("НаправлениеОткуда",ТипСтороныЭлементаГрафическойСхемы.Право);
        пар.Вставить("НаправлениеКуда",ТипСтороныЭлементаГрафическойСхемы.Лево);
    ИначеЕсли рНаправление="Вправо" Тогда
        пар.Вставить("НаправлениеОткуда",ТипСтороныЭлементаГрафическойСхемы.Лево);
        пар.Вставить("НаправлениеКуда",ТипСтороныЭлементаГрафическойСхемы.Право);
    ИначеЕсли рНаправление="Вниз" Тогда
        пар.Вставить("НаправлениеОткуда",ТипСтороныЭлементаГрафическойСхемы.Низ);
        пар.Вставить("НаправлениеКуда",ТипСтороныЭлементаГрафическойСхемы.Верх);
    ИначеЕсли рНаправление="Вверх" Тогда
        пар.Вставить("НаправлениеОткуда",ТипСтороныЭлементаГрафическойСхемы.Верх);
        пар.Вставить("НаправлениеКуда",ТипСтороныЭлементаГрафическойСхемы.Низ);
    Иначе
        Возврат "";
    КонецЕсли;
    //
    Если ТипЗнч(рОткуда)=Тип("Строка") Тогда // это имя откуда, получаем ид
        Для каждого киз Из соотДобавленных Цикл
            Если киз.Значение.Имя=рОткуда Тогда рОткуда=киз.Ключ; Прервать КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    Если ТипЗнч(рКуда)=Тип("Строка") Тогда // это имя куда, получаем ид
        Для каждого киз Из соотДобавленных Цикл
            Если киз.Значение.Имя=рКуда Тогда рКуда=киз.Ключ; Прервать КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    //
    ид=ПолучитьСтаршийИдентификатор(строГС);
    пар.Вставить("Имя","Линия"+Строка(ид));
    пар.Вставить("ТипЭлемента","ЭлементГрафическойСхемыДекоративнаяЛиния");
    пар.Вставить("Идентификатор",ид);
    пар.Вставить("ТекстЗаголовка","");
    пар.Вставить("Откуда",рОткуда);
    пар.Вставить("Куда",рКуда);
    пар.Вставить("ВариантОткуда",0);
    пар.Вставить("ДекоративнаяЛиния",Истина);
    Возврат ДобавитьФигуру(строГС,пар,соотДобавленных);
Исключение
    Сообщить("СоединитьФигурыЛинией, ошибка: "+ОписаниеОшибки());
    Возврат "";
КонецПопытки;
КонецФункции

&НаКлиенте
// Готовит общее объявление пустой графической схемы с общими свойствами
//   [ИспользоватьСетку] (булево),
//   [РежимОтрисовкиСетки] (РежимОтрисовкиСеткиГрафическойСхемы), если не указан, и ИспользоватьСетку=Истина, то Lines.
//   [ГоризонтальныйШагСетки] (число),
//   [ВертикальныйШагСетки] (число),
//   [Вывод] (ИспользованиеВывода)
//
Функция ИнициализироватьГрафическуюСхему(пар=Неопределено)
    Если ТипЗнч(пар)<>Тип("Структура") Тогда пар=Новый Структура КонецЕсли;
    Если пар.Свойство("ИспользоватьСетку") и пар.ИспользоватьСетку и не пар.Свойство("РежимОтрисовкиСетки") Тогда
        рРежимОтрисовки="Lines";
    Иначе
        рРежимОтрисовки=?(пар.Свойство("РежимОтрисовкиСетки"),пар.РежимОтрисовкиСетки,"None");
    КонецЕсли;
    сс="<FlowchartContextType xmlns=""http://v8.1c.ru/8.2/data/graphscheme"" xmlns:xs=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
    |<backColor xmlns:d2p1=""http://v8.1c.ru/8.1/data/ui/style"">d2p1:FieldBackColor</backColor>
    |<enableGrid>"+?(пар.Свойство("ИспользоватьСетку"),Формат(пар.ИспользоватьСетку,"БЛ=false; БИ=true"),"false")+"</enableGrid>
    |<drawGridMode>"+рРежимОтрисовки+"</drawGridMode>
    |<gridHorizontalStep>"+Строка(?(пар.Свойство("ГоризонтальныйШагСетки"),СокрЛП(пар.ГоризонтальныйШагСетки),20))+"</gridHorizontalStep>
    |<gridVerticalStep>"+Строка(?(пар.Свойство("ВертикальныйШагСетки"),СокрЛП(пар.ВертикальныйШагСетки),20))+"</gridVerticalStep>
    |<bpUUID>00000000-0000-0000-0000-000000000000</bpUUID>
    |<useOutput>"+?(пар.Свойство("Вывод"),АнглИмя(пар.Вывод),"Auto")+"</useOutput>
    |<printPropItem><key>6</key><val>10</val></printPropItem>
    |<printPropItem><key>7</key><val>10</val></printPropItem>
    |<printPropItem><key>8</key><val>10</val></printPropItem>
    |<printPropItem><key>9</key><val>10</val></printPropItem>
    |<printPropItem><key>13</key><val>0</val></printPropItem>
    |<printPropItem><key>16</key><val>0</val></printPropItem>
    |<!--ReplacingPoint-->
    |</FlowchartContextType>";
    Возврат сс;
КонецФункции

#КонецОбласти



// Пример использования (он и порождает схему, представленную на картинке к публикации):

&НаКлиенте
Процедура ВывестиСхему()

    соотДобавленных=Новый Соответствие;
    
    пар=Новый Структура;
    строГС=ИнициализироватьГрафическуюСхему(пар);
    //
    идСтарт=ПолучитьСтаршийИдентификатор(строГС);
    пар=Новый Структура;
    пар.Вставить("Имя","ТестСтарта");
    пар.Вставить("ТипЭлемента","ЭлементГрафическойСхемыСтарт");
    пар.Вставить("Идентификатор",идСтарт);
    пар.Вставить("ТекстЗаголовка","Погнали!");
    пар.Вставить("КоординатыКонтура",Новый Структура("Лево,Верх,Ширина,Высота",100,30,100,40));
    ДобавитьФигуру(строГС,пар,соотДобавленных);
    
    идДействие1=ПолучитьСтаршийИдентификатор(строГС);
    пар=Новый Структура;
    пар.Вставить("Имя","ТестДействия");
    пар.Вставить("ТипЭлемента",Тип("ЭлементГрафическойСхемыДействие"));
    пар.Вставить("Идентификатор",идДействие1);
    пар.Вставить("ТекстЗаголовка","Это действие");
    пар.Вставить("КоординатыКонтура",Новый Структура("Лево,Верх,Ширина,Высота",100,120,100,60));
    пар.Вставить("ОписаниеДействия","Некое действие в системе");
    пар.Вставить("АдресацияДействия","И.И.Иванов");
    ДобавитьФигуру(строГС,пар,соотДобавленных);
    
    идУсловие=ПолучитьСтаршийИдентификатор(строГС);
    пар=Новый Структура;
    пар.Вставить("Имя","ТестУсловия");
    пар.Вставить("ТипЭлемента",Тип("ЭлементГрафическойСхемыУсловие"));
    пар.Вставить("Идентификатор",идУсловие);
    пар.Вставить("ТекстЗаголовка","А я знаю?");
    пар.Вставить("КоординатыКонтура",Новый Структура("Лево,Верх,Ширина,Высота",100,220,100,40));
    пар.Вставить("НаправлениеВеткиДа",3); // вправо
    пар.Вставить("НаправлениеВеткиНет",1); // влево
    ДобавитьФигуру(строГС,пар,соотДобавленных);
    
    идДействие2=ПолучитьСтаршийИдентификатор(строГС);
    пар=Новый Структура;
    пар.Вставить("Имя","ТестДействия2");
    пар.Вставить("ТипЭлемента",Тип("ЭлементГрафическойСхемыДействие"));
    пар.Вставить("Идентификатор",идДействие2);
    пар.Вставить("ТекстЗаголовка","Тоже что-то");
    пар.Вставить("КоординатыКонтура",Новый Структура("Лево,Верх,Ширина,Высота",10,280,100,60));
    пар.Вставить("ОписаниеДействия","Некое действие в системе");
    пар.Вставить("АдресацияДействия","Петров");
    ДобавитьФигуру(строГС,пар,соотДобавленных);
    
    пар=Новый Структура;
    пар.Вставить("Имя","ТестЛинии2");
    пар.Вставить("ТипЭлемента","ЭлементГрафическойСхемыДекоративнаяЛиния");
    пар.Вставить("Идентификатор",ПолучитьСтаршийИдентификатор(строГС));
    пар.Вставить("ТекстЗаголовка","Нет");
    пар.Вставить("Откуда",идУсловие);
    пар.Вставить("Куда",идДействие2);
    пар.Вставить("ВариантОткуда",0);
    пар.Вставить("ДекоративнаяЛиния",Истина);
    пар.Вставить("ПоложениеТекста",ПоложениеТекстаСоединительнойЛинии.ПервыйСегмент);
    пар.Вставить("СтильСтрелкиНачало",СтильСтрелки.Нет);
    пар.Вставить("СтильСтрелкиКонец",СтильСтрелки.Заполненная);
    пар.Вставить("НаправлениеОткуда",ТипСтороныЭлементаГрафическойСхемы.Лево);
    пар.Вставить("НаправлениеКуда",ТипСтороныЭлементаГрафическойСхемы.Верх);
    пар.Вставить("КоординатыКонтура",Новый Структура("СерединаГор,СерединаВерт",60,240));
    ДобавитьФигуру(строГС,пар,соотДобавленных);
    
    идОбр=ПолучитьСтаршийИдентификатор(строГС);
    пар=Новый Структура;
    пар.Вставить("Имя","ТестОбработки");
    пар.Вставить("ТипЭлемента",Тип("ЭлементГрафическойСхемыОбработка"));
    пар.Вставить("Идентификатор",идОбр);
    пар.Вставить("ТекстЗаголовка","Зашли сюды");
    пар.Вставить("КоординатыКонтура",Новый Структура("Лево,Верх,Ширина,Высота",180,290,100,60));
    ДобавитьФигуру(строГС,пар,соотДобавленных);
    
    пар=Новый Структура;
    пар.Вставить("Имя","ТестЛинии3");
    пар.Вставить("ТипЭлемента","ЭлементГрафическойСхемыДекоративнаяЛиния");
    пар.Вставить("Идентификатор",ПолучитьСтаршийИдентификатор(строГС));
    пар.Вставить("ТекстЗаголовка","Да");
    пар.Вставить("Откуда",идУсловие);
    пар.Вставить("Куда",идОбр);
    пар.Вставить("ВариантОткуда",0);
    пар.Вставить("ДекоративнаяЛиния",Истина);
    пар.Вставить("ПоложениеТекста",ПоложениеТекстаСоединительнойЛинии.ПервыйСегмент);
    пар.Вставить("СтильСтрелкиНачало",СтильСтрелки.Нет);
    пар.Вставить("СтильСтрелкиКонец",СтильСтрелки.Заполненная);
    пар.Вставить("НаправлениеОткуда",ТипСтороныЭлементаГрафическойСхемы.Право);
    пар.Вставить("НаправлениеКуда",ТипСтороныЭлементаГрафическойСхемы.Верх);
    ДобавитьФигуру(строГС,пар,соотДобавленных);
    
    идСтоп=ПолучитьСтаршийИдентификатор(строГС);
    пар=Новый Структура;
    пар.Вставить("Имя","ТестСтопа");
    пар.Вставить("ТипЭлемента","ЭлементГрафическойСхемыЗавершение");
    пар.Вставить("Идентификатор",идСтоп);
    пар.Вставить("ТекстЗаголовка","Тпрууу!");
    пар.Вставить("КоординатыКонтура",Новый Структура("Лево,Верх,Ширина,Высота",740,210,100,40));
    ДобавитьФигуру(строГС,пар,соотДобавленных);
    
    идБП1=ПолучитьСтаршийИдентификатор(строГС);
    пар=Новый Структура;
    пар.Вставить("Имя","ТестБП");
    пар.Вставить("ТипЭлемента",Тип("ЭлементГрафическойСхемыВложенныйБизнесПроцесс"));
    пар.Вставить("Идентификатор",идБП1);
    пар.Вставить("ТекстЗаголовка","Вложенный БП");
    пар.Вставить("ТекстЗаголовкаБП","Нечто внутри!");
    пар.Вставить("КоординатыКонтура",Новый Структура("Лево,Верх,Ширина,Высота",560,290,100,60));
    ДобавитьФигуру(строГС,пар,соотДобавленных);
    
    идВар=ПолучитьСтаршийИдентификатор(строГС);
    мвар=Новый Массив;
    мвар.Добавить(Новый Структура("Идентификатор,ТекстЗаголовка,Куда,НаправлениеКуда","Вариант1","Превосходно!",идСтоп));
    мвар.Добавить(Новый Структура("Идентификатор,ТекстЗаголовка,Куда,НаправлениеКуда","Вариант2","Замечательно!",идБП1));
    пар=Новый Структура;
    пар.Вставить("Имя","ТестВарианта");
    пар.Вставить("ТипЭлемента",Тип("ЭлементГрафическойСхемыВыборВарианта"));
    пар.Вставить("Идентификатор",идВар);
    пар.Вставить("ТекстЗаголовка","Так как, собственно?");
    пар.Вставить("КоординатыКонтура",Новый Структура("Лево,Верх,Ширина,Высота",440,100,140,100));
    пар.Вставить("Варианты",мвар);
    ДобавитьФигуру(строГС,пар,соотДобавленных);
    
    пар=Новый Структура;
    пар.Вставить("Имя","ТестЛинии4");
    пар.Вставить("ТипЭлемента","ЭлементГрафическойСхемыДекоративнаяЛиния");
    пар.Вставить("Идентификатор",ПолучитьСтаршийИдентификатор(строГС));
    пар.Вставить("ТекстЗаголовка","");
    пар.Вставить("Откуда",идОбр);
    пар.Вставить("Куда",идВар);
    пар.Вставить("ВариантОткуда",0);
    пар.Вставить("ДекоративнаяЛиния",Истина);
    пар.Вставить("ПоложениеТекста",ПоложениеТекстаСоединительнойЛинии.ПервыйСегмент);
    пар.Вставить("СтильСтрелкиНачало",СтильСтрелки.Нет);
    пар.Вставить("СтильСтрелкиКонец",СтильСтрелки.Заполненная);
    пар.Вставить("НаправлениеОткуда",ТипСтороныЭлементаГрафическойСхемы.Право);
    пар.Вставить("НаправлениеКуда",ТипСтороныЭлементаГрафическойСхемы.Верх);
    пар.Вставить("КоординатыКонтура",Новый Структура("СерединаГор,СерединаВерт",400,80));
    ДобавитьФигуру(строГС,пар,соотДобавленных);
    
    СоединитьФигурыЛинией(строГС,соотДобавленных,идСтарт,идДействие1,"Вниз");
    СоединитьФигурыЛинией(строГС,соотДобавленных,идДействие1,идУсловие,"Вниз");
    //СоединитьФигурыЛинией(строГС,соотДобавленных,идУсловие,идДействие2,"Влево");
    //СоединитьФигурыЛинией(строГС,соотДобавленных,идУсловие,идСтоп,"Вниз");
    
    ГС=ДесериализоватьГС(строГС);
КонецПроцедуры

Если теги всё-таки побьются, пишите в личку - пришлю исходник как файл.

Кому пригодится - буду рад. Если возникнут пожелания по доработке, тоже отписывайтесь в теме или в личке. А то ведь метода УдалитьФигуру я делать пока не стал, лень-матушка.

 

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Александр Полетаев (Alias) 19.07.16 16:54
Тэги не побились, всё работает.
Вернее, почти всё... в случае если региональные установки разделителя целой и дробной является запятая (Россия), в XML попадают числа типа 191,5 (с запятой), что делает результирующую XML-строку некорректной. Дроби получились от деления на 2 (когда ищем середину).
Заменил Строка() на XMLСтрока() в функции ДобавитьКоординатыФигуры(), после чего стало совсем замечательно.

P.S. Линии декоративные вместо нормально-привязанных (например, от выбора варианта), это недоработка или особенность?
2. Яков Коган (Yashazz) 19.07.16 19:28
(1) Alias, блин, насчёт декоративных линий - это я в итоге мучений с публикацией не ту версию скопипастил. Конечно, там должны быть привязываемые. Это рулится параметром "ДекоративнаяЛиния". Извиняюсь. А вот насчёт формата - тупо прохлопал. Постараюсь обновить публикацию, спасибо.
3. Александр Анисков (vandalsvq) 19.07.16 22:55
(2) Yashazz, прикрепи обработку, у кого мани есть могут скачать. Все лучше чем со статьи копипастить ))))
4. Виталий Попов (Сурикат) 20.07.16 08:44
Огромное спасибо за публикацию!
5. Яков Коган (Yashazz) 20.07.16 09:28
Коллеги-товарищи, я собираюсь эту штуку развивать дальше, поэтому очень приветствуется критика. Где-какие косяки и недоработки, пишите.
6. Денис (konstruktiv) 20.07.16 13:59
(5) Yashazz, тоже делал такое, также использовал XDTO-сериализацию. В своем время разобрался со многими вещами, даже которые нельзя было сделать интерактивно (перевернуть фигуру на 75 градусов, фон заштриховать). Бросил это дело из-за одной мелочи, поэтому хочу сразу предостеречь. Но возможно я просто не нашел решения.
1. Получается, что стрелки можно сделать только параллельно осям с изгибом только в 90 градусов. Тут вроде бы не страшно. Не так уж плохо и выглядит.
2. Когда связываешь стрелку с фигурой - то есть при интерактивном перетаскивании фигуры стрелка не отклеивается - так вот стрелка может примыкать только к одному из 4 секторов, только к одной точке. А это уже порождает ограничения.
Например разными стрелками будем обозначать товар, деньги и т.п. Соответственно из-за описанных выше ограничений стрелки разного вида сольются в одну. Можно сделать чтобы при первом формировании они не склеивались, но как только передвинешь что-то мышкой, они склеиваются. А интерактивность все-таки лучше оставить в таком продукте.
7. Денис (konstruktiv) 20.07.16 14:03
(5) Yashazz, с этими ключами тоже разобрался:
<printPropItem><key>6</key><val>10</val></printPropItem>
|<printPropItem><key>7</key><val>10</val></printPropItem>
|<printPropItem><key>8</key><val>10</val></printPropItem>
|<printPropItem><key>9</key><val>10</val></printPropItem>
|<printPropItem><key>13</key><val>0</val></printPropItem>
|<printPropItem><key>16</key><val>0</val></printPropItem>
Я не скачивал обработку, но я использовал только сериализованные объекты, которые дает платформа, сам текст XML не парсил и не собирал.
А вообще ощущение, что сами 1сники забросили этот объект, а жаль...
8. Яков Коган (Yashazz) 20.07.16 14:35
(6) Ага, копаю в эту сторону.
Кстати, в процессе изучения выяснилось, что рисовать можно ЛЮБЫЕ фигуры. Любой формы. Если задать координаты, так получаются жуткие звёзды, кривые косоугольники и прочая прелесть. Ну и декорации со всякими овалами тоже))) Тут да, простор для творчества. Правда, надпись при этом может отказаться не внутри многоугольника, а где-то вообще мимо.

(7) Справедливости ради, я где-то в планах 1С видел программное изменение коллекций фигур схемы, но под таким пятизначным приоритетом, что ещё год-полтора ждать точно...
9. Денис (konstruktiv) 20.07.16 15:19
(8) Yashazz, про любые фигуры тоже проходили)) вроде скрытых возможностей дофига, но как только пытаешься приложить к какой-нибудь реальной задаче, так руки опускаются
10. Николай Крылов (Nikola23) 21.07.16 09:08
(5) Зачем тратить силы на собирание строк XML вручную, если то же самое прекрасно работает с использованием XDTO? Объектная модель рулит.
Чем принципиально эта публикация отличается от еще нескольких подобных? Много кода, но зачем?
11. Яков Коган (Yashazz) 21.07.16 15:45
(10) Nikola23, покажите мне эти подобные, пожалуйста. Кроме работы Diversus'а, конечно. И объясните насчёт XDTO - вы предлагаете оперировать абстракциями на уровне элементов схемы? Ну, можно и так, конечно, только вот у меня несколько попыток подобного подхода обламывались - схема не собиралась обратно.

Чем ещё отличается - полной доступностью. Можно воспользоваться безо всяких стартманей.
12. Николай Крылов (Nikola23) 27.07.16 00:53
(11) Yashazz, Никаких хитрых абстракций.
Предлагаю возможность настраивать свойства (в т..ч. методом тыка) с использованием привычной объектной модели.
Если задача сводится к нарисовать - то Ваш метод может и подойдет, а если необходимо проанализировать структуру связей схемы и построить по нарисованной картинке автоматизацию?
Если не нашли решения на инфостарте, это значит, либо плохо искали, либо оно куда-то делось.
Я пользуюсь наработками скачанными отсюда.
13. Яков Коган (Yashazz) 27.07.16 17:44
(12) Nikola23, а, так это я тоже практикую. В публикации http://infostart.ru/public/531533/ я именно так и поступил - создал только самое необходимое, дальше донастроил штатными методами и свойствами языка.
А вот за отсутствие пруфлинка и при этом фразу "плохо искали" - мой вам респект, ибо отсутствие ссылки значит, что аналогов у моей публикации всё-таки нету)))
14. Гоша (user595212_go.blin2014) 25.08.16 22:39
Ценная статья, благодарю.
16. Яков Коган (Yashazz) 08.09.16 09:55
(15) DrAku1a, да, я указал её как первоисточник. Я от этой идеи собственно и отталкивался. Ещё раз Diversus'у спасибо.
Правда, там в комментах один товарищ указывает, что это всё было изобретено ещё раньше. Что, в общем, не удивительно)
17. Serg (serg_infostart) 22.09.16 16:38
Нужен жесткий контроль за itemid, нельзя допускать повторения - иначе будут глюки (срывается соединение между элементами при любой подвижке элемента).
В вышеуказанном блоке изменить:
				// добавляем линию с координатой, начинающейся от середины варианта
				//парПолучателяЛинии=соотДобавленных.Получить(рВариант.Куда); - если надо получитьэти данные
				парЛВ=Новый Структура;
				парЛВ.Вставить("Имя","ЛинияВарианта"+рВариант.Идентификатор);
				парЛВ.Вставить("ТипЭлемента","ЭлементГрафическойСхемыДекоративнаяЛиния");
				парЛВ.Вставить("Идентификатор",ПолучитьСтаршийИдентификатор(строГС));
...Показать Скрыть

на:
				// добавляем линию с координатой, начинающейся от середины варианта
				//парПолучателяЛинии=соотДобавленных.Получить(рВариант.Куда); - если надо получитьэти данные
				парЛВ=Новый Структура;
				парЛВ.Вставить("Имя","ЛинияВарианта"+рВариант.Идентификатор);
				парЛВ.Вставить("ТипЭлемента","ЭлементГрафическойСхемыДекоративнаяЛиния");
				парЛВ.Вставить("Идентификатор",пар.Идентификатор + й + 1); // Вот тут нужно изменить - иначе добавляет существующие itemid 
...Показать Скрыть

В остальном, все хорошо.
Напрашивается оптимизация кода, возможно через использование ФабрикиXDTO. И выпустить в виде отдельного серверного модуля.
Я же хочу на вход подавать простейшую структуру взаимосвязей, на выходе получать ГрафическуюСхему - как раз в процессе производства.
И конечно же благодаря статье сэкономлена куча времени. Автору и предшественникам благодарность.
18. Serg (serg_infostart) 22.09.16 17:01
Да, еще нюанс.
Системные перечисления сторон (portIndex):
1: Лево
2: Верх
3: Право
4: Низ
5: Центр
6: Вариант 1 Лево
7: Вариант 1 Право
8: Вариант 2 Лево
9: Вариант 2 Право
10: ..... и т.д.

Поэтому, придется исправить еще и порт источника для Декоративной линии варианта.
Кусок кода
				парЛВ.Вставить("НаправлениеОткуда",ТипСтороныЭлементаГрафическойСхемы.Право); // всегда

меняем на
				парЛВ.Вставить("НаправлениеОткуда",7+й*2); // всегда
19. Serg (serg_infostart) 22.09.16 17:09
Не обязательно, т.к. работает и без этого, но...
Для чистоты преобразований заменим
парЛВ.Вставить("ВариантОткуда",0);
заменить на
парЛВ.Вставить("ВариантОткуда",й);
20. Serg (serg_infostart) 27.09.16 15:29
Сделал объектную модель. Ссылка на эту статью имеется - без этой статьи не решился бы создать свой вариант реализации.
http://infostart.ru/public/551576/ (скоро будет доступна).
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа