Программная работа с графическими схемами. Готовое решение

18.02.18

Разработка - Инструментарий разработчика

Работоспособное, проверенное на практике, простое и удобное программное управление графическими схемами.

Скачать файлы

Наименование Файл Версия Размер
Программная работа с графическими схемами. Готовое решение.:
.epf 86,47Kb
29
.epf 86,47Kb 29 Скачать

После множества публикаций, где идея была продемонстрирована, развита и обсуждена, мне по работе довелось-таки создать полноценное решение и погонять его всерьёз. Останавливаться на подробностях самой идеи не буду, она высказана много где (начиная с //infostart.ru/public/320691/ и заканчивая моей предыдущей). Концепцию взял у //infostart.ru/public/551576/ и допилил под свой извращённый вкус. Здесь просто выкладываю работающий исходник и обработку, позволяющую всё это эксплуатировать уже на уровне шаблона решения. То есть, можно взять обработку и довольно легко "допилить" под свои нужды. Собственно, она и делалась для себя любимого как универсал)

Обработка предусматривает просмотр, построение, изменение схем согласно любым исходным данным. Там есть специальные шлюзы-интерфейсы, куда можно подключить собственную конкретную механику. Я не стал особенно заморачиваться красотой расположения фигур на схеме - желающие могут почитать теорию графов сами или адаптировать публикацию Ильдаровича, я сделал лишь "квадратно-гнездовое" расположение (с возможностью группировки фигур в блоки или без) и "типа-древовидное", с распихиванием фигур по уровням сначала по длине пути к ним в графе, а потом по незанятым областям.

Фигуры размещаются каждая в свою "зону", линии отрисовываются автоматом. Можно показывать одиночные фигуры. Можно обрабатывать события схемы и её элементов. Для графов со многими исходящими есть варианты показа. Есть удобные визуальные прибамбасы. Форма работает в режиме просмотра и правки схемы.

Также в коде обработки предусмотрен вызов при открытии; есть механизмы работы с таблицей вершин, таблицей рёбер, матрицей переходов - кому что окажется удобнее. Ещё есть механизм отслеживания изменений и их индикации, сравнения данных из БД с данными из самой схемы, схема умеет записываться с сохранением всех внутренних данных и восстанавливаться из ранее записанного. Обработка на УФ для несинхрона.

Часть работы идёт в технике управления xml-json, часть уже в объектной технике графической схемы.

Код снабжён пояснениями, поэтому тут особенно много расписывать не буду. Если у кого возникнут вопросы - постараюсь в теме более подробно рассказать, т.к. заранее вообще не представляю, кого что заинтересует.

Всё сделано сугубо штатными возможностями 1С и промышленно используется уже несколько месяцев. Замечены несколько случаев вроде бы валидного наполнения, которые десериализатор не любит вплоть до того, что падает платформа, но никакой системы в них выявить не удалось. При необходимости также опишу подробнее.

У формы обработки есть встроенная справка, правда, в основном с точки зрения пользователя, но основные фишки там описаны.

Ещё раз подчеркну, что "готовым решением" является в первую очередь код управления схемой, а обработка - это шаблон, заготовка без заточки под конкретные нужды.

 

Пример вызова (а вообще их много в обработке):        

рИмяФигуры="Фигура1";
        рТипЭлемента=Тип("ЭлементГрафическойСхемыДействие");
        рСхема=Обработки.РаботаСГрафическойСхемой.ИнициализироватьСхему();
        
        эгсФигура=Обработки.РаботаСГрафическойСхемой.ИнициализироватьЭлементСхемы(рСхема,рТипЭлемента,рИмяФигуры);
        Если эгсФигура=Неопределено Тогда Возврат КонецЕсли;
        //эгсФигура.Вставить("explanation",рИмяФигуры);
        //
        рКоординаты=Новый Структура;
        рКоординаты.Вставить("Лево",40);
        рКоординаты.Вставить("Верх",40);
        рКоординаты.Вставить("Ширина",300);
        рКоординаты.Вставить("Высота",100);
        Обработки.РаботаСГрафическойСхемой.УстановитьКоординатыЭлементаСхемы(эгсФигура,рКоординаты);
        //
        Обработки.РаботаСГрафическойСхемой.УстановитьЗаголовокЭлементаСхемы(эгсФигура,"Некий заголовок");
        //
        идФигуры=Обработки.РаботаСГрафическойСхемой.ДобавитьЭлементВСхему(рСхема,эгсФигура);
        Если не ЗначениеЗаполнено(идФигуры) Тогда Возврат КонецЕсли;
        
        Если выбзнч.Значение<>Тип("ЭлементГрафическойСхемыДекорация") Тогда
            рИмяЛинии="Линия1";
            эгсЛиния=Обработки.РаботаСГрафическойСхемой.ИнициализироватьЭлементСхемы(рСхема,Тип("ЭлементГрафическойСхемыСоединительнаяЛиния"),рИмяЛинии);
            //
            рПараметры=Новый Структура("Схема",рСхема);
            рПараметры.Вставить("Начало",идФигуры);
            //рПараметры.Вставить("Конец",0); // стрелка не ведёт никуда
            рПараметры.Вставить("НачалоСторона",ТипСтороныЭлементаГрафическойСхемы.Низ);
            Обработки.РаботаСГрафическойСхемой.УстановитьКоординатыЭлементаСхемы(эгсЛиния,рПараметры);
            //
            идЛинии=Обработки.РаботаСГрафическойСхемой.ДобавитьЭлементВСхему(рСхема,эгсЛиния);
            //Если не ЗначениеЗаполнено(идЛинии) Тогда            
        КонецЕсли;
        
        гс=Обработки.РаботаСГрафическойСхемой.СобратьГрафическуюСхему(рСхема);


Собственно код самой главной исполняемой части:

#Область УправлениеГрафическойСхемой

#Область ОбщееСлужебное

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

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

// Используется в первичной и вторичной механике для определения, чем будет коллекция - структурой или соответствием. По умолчанию структура.
Функция НовыйСоответствиеСтруктура()
    Возврат Новый Структура;
КонецФункции

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

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


#Область ДействияПервичногоПостроения

// Действия первичного построения необходимы для построения схемы на основе атомарных данных, компонуют коллекцию коллекций свойств объектов схемы,
// которая затем будет через JSON преобразована в строку и десериализована в объект "ГрафическаяСхема".
// Рекомендуется ИнициализироватьСхему, затем создать нужные элементы, используя ИнициализироватьЭлементСхемы, УстановитьКоординатыЭлементаСхемы
// и УстановитьЗаголовокЭлементаСхемы, затем СобратьГрафическуюСхему.
// Обычно при первичном построении большинство коллекций это структуры, а не соответствия.
// Важно! ИнициализироватьСхему это единственная функция, которая применима только на начальном этапе, т.к. работает всегда со структурой, а не соответствием.

// Возвращает соответствие с одним элементом #value, типа Структура; состав структуры аналогичен сериализованным свойствам графической схемы.
// При ошибке возвращает Неопределено.
//
// Параметры:
//    рПараметры - структура, описывающая настройки схемы; необязательный, если не указан, схема создаётся с настройками по умолчанию;
//    параметры можно заполнять из данных уже имеющейся ГС с помощью ЗаполнитьЗначенияСвойств.
//    Ключи структуры:
//        ИспользоватьСетку (булево), по умолчанию Истина;
//        РежимОтрисовкиСетки (свойство ГС), по умолчанию Линии;
//        ГоризонтальныйШагСетки (число), по умолчанию 20;
//        ВертикальныйШагСетки (число), по умолчанию 20.
//
Функция ИнициализироватьСхему(рПараметры=Неопределено) Экспорт
Попытка
    Если ТипЗнч(рПараметры)<>Тип("Структура") Тогда рПараметры=Новый Структура КонецЕсли;
    
    рИспользоватьСетку=?(рПараметры.Свойство("ИспользоватьСетку"),рПараметры.ИспользоватьСетку,Истина);
    рРежимОтрисовкиСетки=?(рПараметры.Свойство("РежимОтрисовкиСетки"),рПараметры.РежимОтрисовкиСетки,РежимОтрисовкиСеткиГрафическойСхемы.Линии);
    рГоризонтальныйШагСетки=?(рПараметры.Свойство("ГоризонтальныйШагСетки"),рПараметры.ГоризонтальныйШагСетки,20);
    рВертикальныйШагСетки=?(рПараметры.Свойство("ВертикальныйШагСетки"),рПараметры.ВертикальныйШагСетки,20);
    //
    Если рИспользоватьСетку Тогда
        Если рРежимОтрисовкиСетки=РежимОтрисовкиСеткиГрафическойСхемы.Линии Тогда
            рОтрисовка="Lines";
        ИначеЕсли рРежимОтрисовкиСетки=РежимОтрисовкиСеткиГрафическойСхемы.Точки Тогда
            рОтрисовка="Dots";
        ИначеЕсли рРежимОтрисовкиСетки=РежимОтрисовкиСеткиГрафическойСхемы.ШахматнаяСетка Тогда
            рОтрисовка="Chess";
        Иначе
            рОтрисовка="None";
        КонецЕсли;
    Иначе
        рОтрисовка="None";
    КонецЕсли;    
    
    струСхема=Новый Структура;
    струСхема.Вставить("backColor", Новый Соответствие);
    струСхема.backColor.Вставить("#type", "jv8ui:Color");
    струСхема.backColor.Вставить("#value", "{http://v8.1c.ru/8.1/data/ui/style}FieldBackColor");
    струСхема.Вставить("enableGrid",рИспользоватьСетку);
    струСхема.Вставить("drawGridMode",рОтрисовка);
    струСхема.Вставить("gridHorizontalStep",рГоризонтальныйШагСетки);
    струСхема.Вставить("gridVerticalStep",рВертикальныйШагСетки);
    струСхема.Вставить("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",струСхема);
    значСхема.Вставить("СтруктурыТиповЭлементов",рСтруктурыТиповЭлементовГС);
    Возврат значСхема;
    
Исключение
    Сообщить("ИнициализироватьСхему, общая ошибка: "+ОписаниеОшибки());
    Возврат Неопределено;
КонецПопытки;
КонецФункции

// Возвращает массив строковых кратких названий элементов ГС.
//
Функция ПостроитьНотациюТиповЭлементовСхемы()
    спТиповЭлементовГС=Новый СписокЗначений;
    спТиповЭлементовГС.Добавить(0,"Декорация");
    //спТиповЭлементовГС.Добавить(1,"ДекоративнаяЛиния"); // потом продумать вопрос, когда по коду определяется тип - неоднозначная ситуация!
    спТиповЭлементовГС.Добавить(1,"СоединительнаяЛиния");
    спТиповЭлементовГС.Добавить(2,"Старт");
    спТиповЭлементовГС.Добавить(3,"Завершение");
    спТиповЭлементовГС.Добавить(4,"Условие");
    спТиповЭлементовГС.Добавить(5,"Действие");
    спТиповЭлементовГС.Добавить(6,"ВыборВарианта");
    спТиповЭлементовГС.Добавить(7,"Разделение");
    спТиповЭлементовГС.Добавить(8,"Слияние");
    спТиповЭлементовГС.Добавить(9,"Обработка");
    спТиповЭлементовГС.Добавить(10,"ВложенныйБизнесПроцесс");
    Возврат спТиповЭлементовГС;
КонецФункции

// Возвращает число, соотвтетствующее типу стороны элемента ГС.
// При отсутствии подходящих значений или при ошибке возвращает Неопределено.
//
// Параметры:
//    рТип - значение типа ТипСтороныЭлементаГрафическойСхемы, если задание стороны делается для любого элемента ГС, кроме Выбора,
//        или структура с обязательными ключами ТипСтороны и НомерВарианта, если задание стороны делается для варианта элемента Выбор.
//
Функция ПреобразоватьТипСтороныЭлементаВЧисло(рТипИлиВариант)
Попытка
    // Кроме основных 1-5, используются стороны вариантов (только для элемента ВыборВарианта):
    //// чётные - левые стороны, нечётные - правые
    //// поэтому 1 вариант левая сторона - это 6, а 1 вариант правая сторона - 7
    //// для 2 варианта: 8 (лево) и 9 (право) соответственно
    Если ТипЗнч(рТипИлиВариант)=Тип("Структура") Тогда
        // это указание на вариант выбора
        Если рТипИлиВариант.ТипСтороны=ТипСтороныЭлементаГрафическойСхемы.Лево Тогда
            коэф=0;
        ИначеЕсли рТипИлиВариант.ТипСтороны=ТипСтороныЭлементаГрафическойСхемы.Право Тогда
            коэф=1;
        Иначе
            Возврат Неопределено;
        КонецЕсли;
        Возврат 2*(3+рТипИлиВариант.НомерВарианта)+коэф;
    Иначе
        Если рТипИлиВариант=ТипСтороныЭлементаГрафическойСхемы.Лево Тогда Возврат 1
        ИначеЕсли рТипИлиВариант=ТипСтороныЭлементаГрафическойСхемы.Верх Тогда Возврат 2
        ИначеЕсли рТипИлиВариант=ТипСтороныЭлементаГрафическойСхемы.Право Тогда Возврат 3
        ИначеЕсли рТипИлиВариант=ТипСтороныЭлементаГрафическойСхемы.Низ Тогда Возврат 4
        ИначеЕсли рТипИлиВариант=ТипСтороныЭлементаГрафическойСхемы.Центр Тогда Возврат 5
        Иначе Возврат Неопределено;        
        КонецЕсли;
    КонецЕсли;    
Исключение
    Сообщить("ПреобразоватьТипСтороныЭлементаВЧисло, общая ошибка: "+ОписаниеОшибки());
    Возврат Неопределено;
КонецПопытки;
КонецФункции

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

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

    струЭлемента=Новый Структура; // здесь это может быть структурой; потом оно при необходимости преобразуется в соответствие
    
    струЭлемента.Вставить("itemType",                рКодТипаЭлемента);
    
    струЭлемента.Вставить("lineColor",                Новый Соответствие);
    струЭлемента.lineColor.Вставить("#type",        "jv8ui:Color");    
    струЭлемента.lineColor.Вставить("#value",        "{http://v8.1c.ru/8.1/data/ui/style}BorderColor");
    //
    струЭлемента.Вставить("backColor",                Новый Соответствие);
    струЭлемента.backColor.Вставить("#type",        "jv8ui:Color");
    струЭлемента.backColor.Вставить("#value",        "auto");
    //
    струЭлемента.Вставить("textColor",                Новый Соответствие);
    струЭлемента.textColor.Вставить("#type",        "jv8ui:Color");
    струЭлемента.textColor.Вставить("#value",        "{http://v8.1c.ru/8.1/data/ui/style}FormTextColor");
    
    струЭлемента.Вставить("alignHor",                "Center");
    струЭлемента.Вставить("alignVer",                "Center");
    струЭлемента.Вставить("currentLanguage",        "#");
    струЭлемента.Вставить("picturePlacement",        "Left");
    струЭлемента.Вставить("textFont",                Новый Структура("kind","AutoFont"));
    струЭлемента.Вставить("tipText",                Новый Соответствие);
    струЭлемента.Вставить("transparent",            Ложь);
    струЭлемента.Вставить("hyperlink",                Ложь);
    струЭлемента.Вставить("itemTitle",                Новый Массив);
    струЭлемента.Вставить("groupNum",                0);
    
    
    Если рКодТипаЭлемента=0 Тогда
        струЭлемента.Вставить("angle",                    Новый Соответствие);
        струЭлемента.angle.Вставить("#type",    "jxs:decimal");
        струЭлемента.angle.Вставить("#value",    0);
        струЭлемента.Вставить("flipMode",                0);
        струЭлемента.Вставить("shape",                    "Block");
    Иначе
        струЭлемента.Вставить("border",                Новый Структура("width,gap,style",Новый Соответствие,Ложь,Новый Соответствие));
        струЭлемента.border.width.Вставить("#type",    "jxs:decimal");
        струЭлемента.border.width.Вставить("#value",    1);
        струЭлемента.border.style.Вставить("#type",    "jsch:ConnectorLineType");
        струЭлемента.border.style.Вставить("#value",    "Solid");
        струЭлемента.Вставить("point",                    Новый Массив);
    КонецЕсли;
    
    Если рКодТипаЭлемента=1 Тогда
        струЭлемента.Вставить("beginArrowStyle",        "None");
        струЭлемента.Вставить("connectFromItemId",        -1); // Если decorativeLine=Истина, то можно и из ниоткуда
        струЭлемента.Вставить("connectFromPortIndex",    0);
        струЭлемента.Вставить("connectToItemId",        -1);
        струЭлемента.Вставить("decorativeLine",        Истина); // Если Ложь, то будет неубираемая "пристегнутая" линия к объекту
        струЭлемента.Вставить("endArrowStyle",            "Filled");
        струЭлемента.Вставить("portIndexFrom",            4);
        струЭлемента.Вставить("portIndexTo",            0);
        струЭлемента.Вставить("textPos",                "FirstSegment");
    Иначе
        струЭлемента.Вставить("rectBottom",            40);
        струЭлемента.Вставить("rectLeft",                60);
        струЭлемента.Вставить("rectRight",                80);
        струЭлемента.Вставить("rectTop",                20);
        струЭлемента.Вставить("picture",                Новый Соответствие);
        струЭлемента.Вставить("pictureStyle",            4);
    КонецЕсли;
    
    Если рКодТипаЭлемента >= 2 Тогда
        струЭлемента.Вставить("pointUUID",                Строка(Новый УникальныйИдентификатор));
        струЭлемента.Вставить("passageState",            0);
        струЭлемента.Вставить("tableCode",                0);
    КонецЕсли;
    
    Если рКодТипаЭлемента=4 Тогда
        струЭлемента.Вставить("falsePortIndex",        1);
        струЭлемента.Вставить("truePortIndex",            3);
    ИначеЕсли рКодТипаЭлемента=5 Тогда
        струЭлемента.Вставить("addrZoneDivideYPos",    16);
        струЭлемента.Вставить("groupAddressing",        Ложь);
        струЭлемента.Вставить("isAddrZoneDivideValid",    Истина);
        струЭлемента.Вставить("explanation",            "");
    ИначеЕсли рКодТипаЭлемента=6 Тогда
        струЭлемента.Вставить("transition",            Новый Массив);
    ИначеЕсли рКодТипаЭлемента=10 Тогда
        струЭлемента.Вставить("subprocessUUID",        "00000000-0000-0000-0000-000000000000");
    КонецЕсли;
    
    Возврат струЭлемента;
    
Исключение
    Сообщить("ПостроитьСтруктуруДляТипаЭлементаСхемы, общая ошибка: "+ОписаниеОшибки(),СтатусСообщения.Важное);
    Возврат Неопределено;
КонецПопытки;
КонецФункции

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

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

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

// Расставляет все точки, нужные для отрисовки элемента ГС, согласно указанным координатам, определяя прямоугольную область "обитания" элемента ГС и его фигуру.
//
// Параметры:
//    струЭлемента - структура/соответствие, соответствующая типу элемента (определяемая ранее в ПостроитьСтруктуруДляТипаЭлементаСхемы);
//    рПараметры - структура, где:
//        для не-линий обязательны числовые: ключи Лево, Верх, Ширина, Высота;
//        для линий обязательны ключи: Схема (структура), Начало и Конец (структуры или id); и необязательны ДекоративнаяЛиния (булево),
//        НачалоСторона и КонецСторона (типа ТипСтороныЭлементаГрафическойСхемы).
//        При этом, если нужно, чтобы стрелка никуда не вела, следует НЕ указывать вообще ключ "Конец".
//
Процедура УстановитьКоординатыЭлементаСхемы(струЭлемента,рПараметры=Неопределено) Экспорт
Попытка
    рТипЭлемента=СоотСвойство(струЭлемента,"Тип");
    //
    Если рТипЭлемента=Тип("ЭлементГрафическойСхемыДекоративнаяЛиния") 
    или рТипЭлемента=Тип("ЭлементГрафическойСхемыСоединительнаяЛиния") 
    Тогда    
        рНачало=?(ТипЗнч(рПараметры.Начало)=Тип("Структура"),рПараметры.Начало.ItemId,рПараметры.Начало);
        Если рПараметры.Свойство("Конец") Тогда
            рКонец=?(ТипЗнч(рПараметры.Конец)=Тип("Структура"),рПараметры.Конец.ItemId,рПараметры.Конец);
        Иначе
            // линия никуда не ведёт
            рКонец=0;
        КонецЕсли;        
        //
        струЭлемента.Вставить("decorativeLine",?(рПараметры.Свойство("ДекоративнаяЛиния"),рПараметры.ДекоративнаяЛиния,Ложь));
        струЭлемента.Вставить("connectFromItemId",рНачало);
        струЭлемента.Вставить("connectToItemId",рКонец);
        //
        рТипСтороныНач=?(рПараметры.Свойство("НачалоСторона"),ПреобразоватьТипСтороныЭлементаВЧисло(рПараметры.НачалоСторона),Неопределено);
        рТипСтороныНач=?(рТипСтороныНач=Неопределено,1,рТипСтороныНач); // лево
        струЭлемента.Вставить("portIndexFrom",рТипСтороныНач);
        //
        рТипСтороныКон=?(рПараметры.Свойство("КонецСторона"),ПреобразоватьТипСтороныЭлементаВЧисло(рПараметры.КонецСторона),Неопределено);
        Если рКонец<>0 Тогда
            рТипСтороныКон=?(рТипСтороныКон=Неопределено,2,рТипСтороныКон); // верх
            струЭлемента.Вставить("portIndexTo",рТипСтороныКон);
        КонецЕсли;
        
        рПортВарианта=СоотСвойство(струЭлемента,"connectFromPortIndex");
        //
        // Рисуем только начало и окончание. Остальное система дополнит сама при отрисовке.        
        рКоординатыНач=РассчитатьКоординатыЭлемента(рПараметры.Схема,рНачало,рТипСтороныНач,рПортВарианта);
        Если рКонец<>0 Тогда
            рКоординатыКон=РассчитатьКоординатыЭлемента(рПараметры.Схема,рКонец,рТипСтороныКон);
            Если рКоординатыНач=Неопределено или рКоординатыКон=Неопределено Тогда
                Сообщить("УстановитьКоординатыЭлементаСхемы, ошибка расчёта координат элемента "+СокрЛП(струЭлемента.Имя)+"!");
                Возврат;
            КонецЕсли;
        КонецЕсли;
        //
        струЭлемента.Вставить("connectFromPortIndex",рПортВарианта);
        
        мКоординат=СоотСвойство(струЭлемента,"point");
        Если ТипЗнч(мКоординат)<>Тип("Массив") Тогда мКоординат=Новый Массив КонецЕсли;
        соотНач=НовыйСоответствиеСтруктура();
        соотНач.Вставить("x",рКоординатыНач.x);
        соотНач.Вставить("y",рКоординатыНач.y);
        мКоординат.Добавить(соотНач);
        Если рКонец<>0 Тогда
            соотКон=НовыйСоответствиеСтруктура();
            соотКон.Вставить("x",рКоординатыКон.x);
            соотКон.Вставить("y",рКоординатыКон.y);
            мКоординат.Добавить(соотКон);
        Иначе
            соотКон=НовыйСоответствиеСтруктура();
            соотКон.Вставить("x",рКоординатыНач.x);
            соотКон.Вставить("y",рКоординатыНач.y+20);
            мКоординат.Добавить(соотКон);
        КонецЕсли;
        //
        // случай, когда линия идет снизу на верхнюю границу (ставим точку середины)
        Если рКонец<>0 и СоотСвойство(мКоординат[0],"y","Число") > СоотСвойство(мКоординат[1],"y","Число") И СоотСвойство(струЭлемента,"portIndexTo","Число")=2 Тогда
            х1=РассчитатьКоординатыЭлемента(рПараметры.Схема,СоотСвойство(струЭлемента,"connectFromItemId","Число"),3).x;
            х2=РассчитатьКоординатыЭлемента(рПараметры.Схема,СоотСвойство(струЭлемента,"connectToItemId","Число"),1).x;
            Если х1<>Неопределено и х2<>Неопределено Тогда
                xmid=Цел((х1+х2)/2);
                рВертШагСетки=СоотСвойство(СоотСвойство(рПараметры.Схема,"#value"),"gridVerticalStep");
                ymid=СоотСвойство(мКоординат[1],"y","Число")-рВертШагСетки;
                соотСред=НовыйСоответствиеСтруктура();
                соотСред.Вставить("x",xmid);
                соотСред.Вставить("y",ymid);
                мКоординат.Вставить(1,соотСред);
            КонецЕсли;            
        КонецЕсли;
        //
        струЭлемента.Вставить("point",мКоординат);
        
    Иначе
        // координаты и размер
        //Если рКоординаты.Лево=Неопределено И рКоординаты.Верх<>Неопределено Тогда // ищем максимальный X
        //    рКоординаты.Лево=ПолучитьМаксимальноеЗначениеКлючаВМассивеСтруктур(струСхемы.item,"rectRight") + струСхемы.gridHorizontalStep;
        //КонецЕсли;
        //Если рКоординаты.Верх=Неопределено И рКоординаты.Лево<>Неопределено Тогда // ищем максимальный Y
        //    рКоординаты.Верх=ПолучитьМаксимальноеЗначениеКлючаВМассивеСтруктур(струСхемы.item,"rectBottom") + струСхемы.gridVerticalStep;
        //КонецЕсли;
        струЭлемента.Вставить("rectLeft",рПараметры.Лево);
        струЭлемента.Вставить("rectTop",рПараметры.Верх);
        струЭлемента.Вставить("rectRight",рПараметры.Лево+рПараметры.Ширина);
        струЭлемента.Вставить("rectBottom",рПараметры.Верх+рПараметры.Высота);
        РасставитьТочкиФигурыЭлемента(струЭлемента);
        
    КонецЕсли;
    
Исключение
    Сообщить("УстановитьКоординатыЭлементаСхемы, общая ошибка: "+ОписаниеОшибки(),СтатусСообщения.Важное);
КонецПопытки;
КонецПроцедуры    

// Собственно готови данные для отрисовки конкретной фигуры элемента ГС. Вспомогательная для УстановитьКоординатыЭлементаСхемы.
//
// Параметры:
//    струЭлемента - структура/соответствие, соответствующая типу элемента (определяемая ранее в ПостроитьСтруктуруДляТипаЭлементаСхемы);
//    рМодификатор - число; до 10 включительно совпадает с нотацией типа элемента ГС, значения с 11 по 13 - нетипичные геометрические фигуры.
//
Процедура РасставитьТочкиФигурыЭлемента(струЭлемента,рМодификатор=Неопределено)
Попытка
    itemType=?(рМодификатор=Неопределено,СоотСвойство(струЭлемента,"itemType","Число"),рМодификатор);
    //
    rectLeft=СоотСвойство(струЭлемента,"rectLeft","Число");
    rectTop=СоотСвойство(струЭлемента,"rectTop","Число");
    rectRight=СоотСвойство(струЭлемента,"rectRight","Число");
    rectBottom=СоотСвойство(струЭлемента,"rectBottom","Число");
    
    мТочек1=Новый Массив;
    
    Если itemType=0 ИЛИ itemType=5 ИЛИ itemType=9 ИЛИ itemType=10 Тогда // Декорация/Действие/Обработка/ВложенныйПроцесс: Прямоугольник
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1,rectBottom-1));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,rectBottom-1));
        //
    ИначеЕсли itemType=2 Тогда // Старт: Прямоугольник + треугольник снизу (высота треугольника=5)
        dy=Цел((rectRight-rectLeft)/2/Sqrt(3));
        dy=?(rectBottom-dy<=rectTop,Цел((rectBottom-rectTop)/2),dy); // корректировка запредельных значений
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1,rectBottom-dy));
        мТочек1.Добавить(Новый Структура("x,y",Цел((rectLeft+rectRight)/2),rectBottom-1));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,rectBottom-dy));
        //
    ИначеЕсли itemType=3 Тогда // Завершение: Прямоугольник + треугольник сверху (высота треугольника=5)
        dy=Цел((rectRight-rectLeft)/2/Sqrt(3));
        dy=?(rectTop+dy>=rectBottom,Цел((rectRight-rectLeft)/2),dy); // корректировка запредельных значений
        мТочек1.Добавить(Новый Структура("x,y",Цел((rectLeft+rectRight)/2),rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1,rectTop+dy));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1,rectBottom-1));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,rectBottom-1));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,rectTop+dy));
        //
    ИначеЕсли itemType=4 Тогда // Условие: Шестиугольник (расчет середины высоты:dy/2,угол 60 градусов) 
        dx=Цел((rectBottom-rectTop)/2/Sqrt(3)); // ДельтаX при 60 градусном уклоне
        dx=?(2*dx>(rectRight-rectLeft),Цел((rectRight-rectLeft)/4),dx); // корректировка запредельных значений
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,Цел((rectTop+rectBottom)/2)));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+dx,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1-dx,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1,Цел((rectTop+rectBottom)/2)));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1-dx,rectBottom-1));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+dx,rectBottom-1));
        //
    ИначеЕсли itemType=6 Тогда // ВыборВарианта: Прямоугольник
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1,rectBottom-1));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,rectBottom-1));
        //
    ИначеЕсли itemType=7 Тогда // Разделение: Треугольник, острый угол вниз
        dx=Цел((rectRight-rectLeft)/2)-1;
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+2*dx,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+dx,rectBottom-1));
        //
    ИначеЕсли itemType=8 Тогда // Слияние: Треугольник, острый угол вверх
        dx=Цел((rectRight-rectLeft)/2)-1;
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,rectBottom-1));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+2*dx,rectBottom-1));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+dx,rectTop));    
        //
    ИначеЕсли itemType=11 Тогда // (нетиповые фигуры) Галстук - бабочка
        dx=Цел((rectBottom-rectTop)/2/Sqrt(3)); // ДельтаX при 60 градусном уклоне
        dx=?(2*dx>(rectRight-rectLeft),Цел((rectRight-rectLeft)/4),dx); // корректировка запредельных значений
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,Цел((rectTop+rectBottom)/2)));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+dx,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",Цел((rectLeft+rectRight)/2),Цел((rectTop+rectBottom)/2)));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1-dx,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1,Цел((rectTop+rectBottom)/2)));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1-dx,rectBottom-1));
        мТочек1.Добавить(Новый Структура("x,y",Цел((rectLeft+rectRight)/2),Цел((rectTop+rectBottom)/2)));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+dx,rectBottom-1));
        //
    ИначеЕсли itemType=12 Тогда // (нетиповые фигуры) Звезда шерифа
        // сторона маленького треугольника
        dc=Цел((rectRight-rectLeft)/3);
        dchalf=Цел(dc/2);
        dchigh=Цел(dchalf*Sqrt(3));
        //dx=Цел((rectBottom-rectTop)/2/Sqrt(3)); // ДельтаX при 60 градусном уклоне
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,rectTop+dchigh));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+dc,rectTop+dchigh));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+dc+dchalf,rectTop));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1-dc,rectTop+dchigh));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1,rectTop+dchigh));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1-dchalf,rectTop+2*dchigh));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1,rectTop+3*dchigh));
        мТочек1.Добавить(Новый Структура("x,y",rectRight-1-dc,rectTop+3*dchigh));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+dc+dchalf,rectBottom-1));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+dc,rectTop+3*dchigh));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft,rectTop+3*dchigh));
        мТочек1.Добавить(Новый Структура("x,y",rectLeft+dchalf,rectTop+2*dchigh));
        //
    ИначеЕсли itemType=13 Тогда // (нетиповые фигуры) Круг
        // сторона маленького треугольника
        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);
            мТочек1.Добавить(Новый Структура("x,y",xcenter+x,ycenter+y));
            у=у + ШагГрад;
        КонецЦикла;
        //
    КонецЕсли;
    
    Если ТипЗнч(НовыйСоответствиеСтруктура())=Тип("Соответствие") Тогда // преобразуем
        мТочек2=Новый Массив;
        Для каждого рТочка1 Из мТочек1 Цикл
            соот=Новый Соответствие;
            Для каждого киз Из рТочка1 Цикл
                соот.Вставить(киз.Ключ,киз.Значение);
            КонецЦикла;
            мТочек2.Добавить(соот);
        КонецЦикла;
    Иначе
        мТочек2=мТочек1;
    КонецЕсли;
    
    струЭлемента.Вставить("point",мТочек2);
    
Исключение
    Сообщить("РасставитьТочкиФигурыЭлемента, общая ошибка: "+ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры

// Устанавливает элементу ГС заголовок без локализации языка, по умолчанию.
//
// Параметры:
//    струЭлемента - структура/соответствие, соответствующая типу элемента (определяемая ранее в ПостроитьСтруктуруДляТипаЭлементаСхемы);
//    рЗаголовок - строка, произвольная надпись на элементе.
//
Процедура УстановитьЗаголовокЭлементаСхемы(струЭлемента,рЗаголовок) Экспорт
    соот=НовыйСоответствиеСтруктура();
    соот.Вставить("lang","#");
    соот.Вставить("content",рЗаголовок);
    мЗаголовка=СоотСвойство(струЭлемента,"itemTitle");
    Если ТипЗнч(мЗаголовка)<>Тип("Массив") Тогда мЗаголовка=Новый Массив КонецЕсли;
    мЗаголовка.Добавить(соот);
    струЭлемента.Вставить("itemTitle",мЗаголовка);
КонецПроцедуры

// Добавляет структуру, описывающую элемент ГС, в соответствующую коллекцию элементов самой структуры ГС.
// Возвращает уникальный в пределах схемы id добавленного элемента, 1 и более. При ошибке возвращает Неопределено.
//
// Параметры:
//    рСхема - соответствие структур, массивов и прочих коллекций, созданное ИнициализироватьСхему и заполненное в ходе построения схемы;
//    струЭлемента - структура/соответствие, соответствующая типу элемента (определяемая ранее в ПостроитьСтруктуруДляТипаЭлементаСхемы).
//
Функция ДобавитьЭлементВСхему(рСхема,струЭлемента) Экспорт
Попытка
    рИмяЭлемента=СокрЛП(СоотСвойство(струЭлемента,"itemCode"));
    струИмеющегося=ПолучитьЭлементСхемыПоИмени(рСхема,рИмяЭлемента);
    Если струИмеющегося<>Неопределено Тогда // такой уже есть
        Возврат СоотСвойство(струИмеющегося,"itemId","Число");
    КонецЕсли;
    
    струСхемы=Неопределено;
    мЭлементовГС=ПолучитьКоллекциюЭлементовСхемы(рСхема,струСхемы);
    
    // Ищем максимальный itemId
    струЭлемента.Вставить("itemId",ПолучитьМаксимальноеЗначениеСвойства(мЭлементовГС,"itemId")+1);
    
    // Ищем такой же itemCode (заимствованный код; возможно, нужен рефакторинг!)
    //Если НайтиПоЗначениюКлючаВМассивеСтруктур(струСхемы.item,"itemCode",струЭлемента.itemCode) <> Неопределено Тогда
    //    _базоваяЧасть=струЭлемента.itemCode;
    //    _нс=1;
    //    Пока НайтиПоЗначениюКлючаВМассивеСтруктур(струСхемы.item,"itemCode",_базоваяЧасть + Формат(_нс,"ЧГ=")) <> Неопределено Цикл
    //        _нс=_нс + 1;
    //    КонецЦикла;
    //    струЭлемента.itemCode=_базоваяЧасть + Формат(_нс,"ЧГ=");
    //КонецЕсли;
    
    // Ищем максимальный itemTabOrder
    // пока так. Это порядок обхода. Возможно нужно более продвинутое вычисление сделать 
    // (считать все подчиненные и соединенные элементы последнего и прибавлять на их количество к максимальному)
    струЭлемента.Вставить("itemTabOrder",ПолучитьМаксимальноеЗначениеСвойства(мЭлементовГС,"itemTabOrder")+5);
    
    // Ищем максимальный zOrder
    струЭлемента.Вставить("zOrder",ПолучитьМаксимальноеЗначениеСвойства(мЭлементовГС,"zOrder",-1)+1);
    
    // собственно добавление в коллекцию элементов схемы
    мЭлементовГС.Добавить(струЭлемента);
    
    струСхемы.Вставить("item",мЭлементовГС);
    рСхема.Вставить("#value",струСхемы);
    
    Возврат СоотСвойство(струЭлемента,"itemId","Число");
Исключение
    Сообщить("ДобавитьЭлементВСхему, общая ошибка: "+ОписаниеОшибки(),СтатусСообщения.Важное);
    Возврат Неопределено;
КонецПопытки;
КонецФункции

// Добавляет вариант в элемент типа "Выбор". Элемент уже должен быть инициализирован. Возвращает порядковый номер варианта, начиная с 0.
// При ошибке возвращает Неопределено.
//
// Параметры:
//    струЭлемента - структура/соответствие, соответствующая типу элемента (определяемая ранее в ПостроитьСтруктуруДляТипаЭлементаСхемы);
//    рИмяВарианта - строка, имя варианта, уникальное в пределах выбора;
//    рЗаголовокВарианта - строка, произвольная надпись на варианте.
//
Функция ДобавитьВариантВыбора(струЭлемента,рИмяВарианта,рЗаголовокВарианта) Экспорт
Попытка
    соотЗаголовка=НовыйСоответствиеСтруктура();
    соотЗаголовка.Вставить("lang","#");
    соотЗаголовка.Вставить("content",рЗаголовокВарианта);
    мЗаголовка=Новый Массив;
    мЗаголовка.Добавить(соотЗаголовка);
    
    соотВарианта=НовыйСоответствиеСтруктура();
    соотВарианта.Вставить("name",рИмяВарианта);
    соотВарианта.Вставить("description",мЗаголовка);    
    соотВарианта.Вставить("backColor",СоотСвойство(струЭлемента,"backColor"));
    
    мВариантов=СоотСвойство(струЭлемента,"transition");
    Если ТипЗнч(мВариантов)<>Тип("Массив") Тогда мВариантов=Новый Массив КонецЕсли;
    мВариантов.Добавить(соотВарианта);
    
    струЭлемента.Вставить("transition",мВариантов);
    Возврат мВариантов.Количество()-1;
Исключение
    Сообщить("ДобавитьВариантВыбора, общая ошибка: "+ОписаниеОшибки());
    Возврат Неопределено;
КонецПопытки;
КонецФункции

// Получает координаты для работы с портом (точкой входа/выхода соединительной линии); возвращает структуру с ключами x,y;
// используется при первичной работе со схемой (далее можно обращаться к свойствам элемента как объекта);
// При ошибке возвращает Неопределено.
//
// Параметры:
//    рСхема - соответствие структур, массивов и прочих коллекций, созданное ИнициализироватьСхему и заполненное в ходе построения схемы;
//    itemId - уникальный в пределах схемы идентификатор элемента;
//    portIndex - код типа стороны элемента получателя (см. ПреобразоватьТипСтороныЭлементаВЧисло), ни в коем случае убирать "Знач"!
//    connectFromPortIndex - код типа стороны элемента отправителя (см. ПреобразоватьТипСтороныЭлементаВЧисло), изменяется внутри функции.
//
Функция РассчитатьКоординатыЭлемента(рСхема,itemId,Знач portIndex,connectFromPortIndex=0)
Попытка
    рРезультат=Новый Структура("x,y",0,0);
    
    струСхемы=Неопределено;
    мЭлементовГС=ПолучитьКоллекциюЭлементовСхемы(рСхема,струСхемы);
    Если мЭлементовГС.Количество()=0 Тогда
        Сообщить("Внутренняя ошибка: коллекция элементов схемы пуста!",СтатусСообщения.Важное);
    Иначе
        струЭлемента=НайтиПоЗначениюКлючаВМассивеСтруктур(мЭлементовГС,"itemId",itemId);
        Если струЭлемента=Неопределено Тогда
            Сообщить("Внутренняя ошибка: элемент не найден по своему id "+СокрЛП(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;
    КонецЕсли;
    
    рЛево=СоотСвойство(струЭлемента,"rectLeft","Число");
    рВерх=СоотСвойство(струЭлемента,"rectTop","Число");
    рПраво=СоотСвойство(струЭлемента,"rectRight","Число");
    рНиз=СоотСвойство(струЭлемента,"rectBottom","Число");
    
    Если portIndex=1 Тогда
        рРезультат.x=рЛево;
        рРезультат.y=?(dy>0,рНиз-1-dy,Цел((рВерх+рНиз+1)/2));
    ИначеЕсли portIndex=2 Тогда
        рРезультат.x=Цел((рЛево+рПраво+1)/2);
        рРезультат.y=рВерх;
    ИначеЕсли portIndex=3 Тогда
        рРезультат.x=рПраво;
        рРезультат.y=?(dy>0,рНиз-1-dy,Цел((рВерх+рНиз+1)/2));
    ИначеЕсли portIndex=4 Тогда
        рРезультат.x=Цел((рЛево+рПраво+1)/2);
        рРезультат.y=рНиз;
    ИначеЕсли portIndex=5 Тогда
        рРезультат.x=Цел((рЛево+рПраво+1)/2);
        рРезультат.y=Цел((рВерх+рНиз+1)/2);
    КонецЕсли;
    
    Возврат рРезультат;
Исключение
    Сообщить("РассчитатьКоординатыЭлемента, общая ошибка: "+ОписаниеОшибки());
    Возврат Неопределено;
КонецПопытки;
КонецФункции

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


#Область ДействияВторичнойНастройки

// Действия вторичной настройки полезны, когда объект "ГрафическаяСхема" уже есть, и надо изменить свойство элемента, не имеющее во встроенном языке 
// возможности записи, т.е. которые только на чтение. Важно: вторичная работа ведётся уже с соответствиями, а не со структурами, т.е. все коллекции данных,
// бывшие структурами при первичном построении, теперь будут являться соответствиями.
// Используется сериализация схемы в строку, чтение строки через JSON в коллекцию коллекций, и работа с их элементами и значениями.
// Рекомендуется использовать РазобратьГрафическуюСхему, затем выполнить настройку нужных свойств, затем СобратьГрафическуюСхему.

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

// Устанавливает ZOrder (порядок плана отображения относительно других перекрывающих фигур) для всех элементов указанного типа.
//
// Параметры:
//    рСхема - соответствие структур, массивов и прочих коллекций, созданное ИнициализироватьСхему и заполненное в ходе построения схемы, или сериализованное из ГС;
//    рТипЭлемента - число (от 0 до 10), строковое представление типа или Тип элемента ГС (например, Тип("ЭлементГрафическойСхемыДействие"));
//    ZOrder - число, приоритет по возрастанию (старший накрывает младших), изменяется внутри процедуры.
//
Процедура УстановитьZOrderВсемЭлементам(рСхема,рТипЭлемента,ZOrder) Экспорт
Попытка        
    рКодТипаЭлемента=ПолучитьКодТипаЭлементаСхемы(рТипЭлемента);
    Если рКодТипаЭлемента=Неопределено Тогда Возврат КонецЕсли;
    
    знчСхемы=Неопределено;
    мЭлементовГС=ПолучитьКоллекциюЭлементовСхемы(рСхема,знчСхемы);
    Если мЭлементовГС.Количество()=0 Тогда Возврат КонецЕсли; // нет ни одного элемента
    //
    Для каждого рЭлементГС из мЭлементовГС цикл
        Если СоотСвойство(рЭлементГС,"itemType","Число")=рКодТипаЭлемента Тогда
            рЭлементГС.Вставить("zOrder",ZOrder);
            ZOrder=ZOrder+1;
        КонецЕсли;
    КонецЦикла;
    
    знчСхемы.Вставить("item",мЭлементовГС);
    рСхема.Вставить("#value",знчСхемы);
Исключение
    Сообщить("УстановитьZOrderВсемЭлементам, общая ошибка: "+ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры

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

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

// Удаляет элемент из коллекции элементов схемы, в т.ч. при необходимости и связанные с ним.
//
// Параметры:
//    рСхема - соответствие структур, массивов и прочих коллекций, созданное ИнициализироватьСхему и заполненное в ходе построения схемы, или сериализованное из ГС;
//    рИмяЭлемента - строка, уникальное имя элемента в пределах схемы;
//    рУдалятьСвязанныеЛинии - булево; имеет смысл только при удалении фигуры (не Линии), по умолчанию Ложь;
//        если Истина, то линии, входящие в фигуру и исходящие из неё, также будут удалены;
//        если Ложь, то линии, входящие в фигуру и исходящие из неё, будут вести "в никуда" (в связочные id ставится -1).
//
Процедура УдалитьЭлементСхемыПоИмени(рСхема,рИмяЭлемента,рУдалятьСвязанныеЛинии=Ложь) Экспорт
Попытка
    знчСхемы=Неопределено;
    мЭлементовГС=ПолучитьКоллекциюЭлементовСхемы(рСхема,знчСхемы);
    Если мЭлементовГС.Количество()=0 Тогда Возврат КонецЕсли; // нет ни одного элемента
    
    мНенужных=Новый Массив;
    пози=Неопределено;
    Для й=0 По мЭлементовГС.Количество()-1 Цикл
        Если СокрЛП(СоотСвойство(мЭлементовГС[й],"itemCode"))=рИмяЭлемента Тогда
            мНенужных.Добавить(мЭлементовГС.Получить(й)); Прервать;
        КонецЕсли;
    КонецЦикла;
    Если мНенужных.Количество()=1 Тогда
        рУдаляемый=мНенужных.Получить(0);
        рТипЭлемента=СоотСвойство(рУдаляемый,"itemType","Число");
        Если рТипЭлемента=Неопределено Тогда Возврат КонецЕсли;
        Если рТипЭлемента=1 Тогда // это линия
            // пока ничего не делаем
        Иначе
            идФигуры=СоотСвойство(рУдаляемый,"itemId","Число");
            Для каждого рЭлемент Из мЭлементовГС Цикл
                Если СоотСвойство(рЭлемент,"itemType","Число")<>1 Тогда Продолжить КонецЕсли; // не линия
                Если СоотСвойство(рЭлемент,"connectFromItemId","Число")=идФигуры Тогда
                    Если рУдалятьСвязанныеЛинии Тогда
                        мНенужных.Добавить(рЭлемент);
                    Иначе
                        рЭлемент.Вставить("connectFromItemId",-1); // обрезаем
                    КонецЕсли;                    
                КонецЕсли;
                Если СоотСвойство(рЭлемент,"connectToItemId","Число")=идФигуры Тогда
                    Если рУдалятьСвязанныеЛинии Тогда
                        мНенужных.Добавить(рЭлемент);
                    Иначе
                        рЭлемент.Вставить("connectToItemId",-1); // обрезаем
                    КонецЕсли;                    
                КонецЕсли;
            КонецЦикла; // по поиску линий, связанных с удаляемой фигурой
        КонецЕсли; // если удаляется фигура/линия
    КонецЕсли;
    //
    // собственно удаляем
    Для каждого рЭлемент Из мНенужных Цикл
        мЭлементовГС.Удалить(мЭлементовГС.Найти(рЭлемент));
    КонецЦикла;
    
    знчСхемы.Вставить("item",мЭлементовГС);
    рСхема.Вставить("#value",знчСхемы);
Исключение
    Сообщить("УдалитьЭлементСхемыПоИмени, общая ошибка: "+ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры

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

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

// Удаляет элемент ВариантаВыбора по её имени или порядковому номеру среди вариантов из массива вариантов выбора.
// Если варианта с таким именем/номером нет, ничего не делает.
//
// Параметры:
//    рЭлемент - структура/соответствие, соответствующая типу элемента (определяемая ранее в ПостроитьСтруктуруДляТипаЭлементаСхемы);
//    рИмяИлиНомер - строковое имя или числовой номер (от 0 по возрастанию в порядке №№ вариантов в выборе).
//
Процедура УдалитьВариантВыбораПоИмениИлиНомеру(рЭлемент,рИмяИлиНомер) Экспорт
Попытка
    мВариантов=СоотСвойство(рЭлемент,"transition");
    Если ТипЗнч(мВариантов)<>Тип("Массив") Тогда Возврат КонецЕсли;
    //
    Если ТипЗнч(рИмяИлиНомер)=Тип("Строка") Тогда // по имени
        пози=Неопределено;
        Для й=0 По мВариантов.Количество()-1 Цикл
            рВариант=мВариантов.Получить(й);
            Если СокрЛП(СоотСвойство(рВариант,"name"))=СокрЛП(рИмяИлиНомер) Тогда
                пози=й; Прервать;
            КонецЕсли;
        КонецЦикла;        
    ИначеЕсли ТипЗнч(рИмяИлиНомер)=Тип("Число") Тогда
        Если мВариантов.Количество()<=рИмяИлиНомер Тогда Возврат КонецЕсли;
        пози=рИмяИлиНомер;
    Иначе
        Возврат;
    КонецЕсли;
    //
    Если пози<>Неопределено Тогда
        мВариантов.Удалить(пози);
        рЭлемент.Вставить("transition",мВариантов);
    КонецЕсли;    
    //
Исключение
    Сообщить("УдалитьВариантВыбораПоИмениИлиНомеру, общая ошибка: "+ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры    
    
#КонецОбласти


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

// Преобразует графическую схему в соответствие соответствий и массивов. Возвращает соответствие.
// Внимание! Даже если ранее в свойствах коллекций были структуры, они заменятся на соответствия, т.к. только такое восстановление из JSON возможно.
// Дописывает служебные поля: в коллекцию самой схемы дописывает СтруктурыТиповЭлементов (кэш структур всех типов элементов ГС),
// в коллекцию каждого элемента ГС дописывает Имя (строковое имя элемента) и Тип (тип в нотации 1С, например, Тип("ЭлементГрафическойСхемыДействие")).
// При ошибке возвращает Неопределено.
//
// Параметры:
//    рГС - объект типа "Графическая схема".
//
Функция РазобратьГрафическуюСхему(рГС) Экспорт
Попытка
    // нормализуем ГС (по неизвестным причинам, обратная сериализация не понимает, например. жирный шрифт)
    рШрифт=Новый Шрифт; // по умолчанию (восстанавливать оформление, если надо, потом и другими средствами)
    Для каждого рЭлементГС Из рГС.ЭлементыГрафическойСхемы Цикл
        Попытка рЭлементГС.Шрифт=рШрифт Исключение КонецПопытки;
    КонецЦикла;
    
    // сериализуем ГС
    рЗапись=Новый ЗаписьJSON;
    рЗапись.УстановитьСтроку();
    СериализаторXDTO.ЗаписатьJSON(рЗапись,рГС,НазначениеТипаXML.Явное);
    //
    строСхемы=рЗапись.Закрыть();
    //сообщить("Разборка: "+Символы.ПС+стросхемы);
    //
    // приводим из строки JSON в коллекцию (все структуры будут отражены как соответствия!)
    рЧтение=Новый ЧтениеJSON;
    рЧтение.УстановитьСтроку(строСхемы);
    рСхема=ПрочитатьJSON(рЧтение,Истина);
    
    Если ТипЗнч(рСхема)<>Тип("Соответствие") Тогда Возврат Неопределено КонецЕсли;
    
    // добавляем служебные поля
    рСхема.Вставить("СтруктурыТиповЭлементов",ПостроитьКэшСтруктурВсехТиповЭлементовСхемы());
    
    // добавляем служебные тип и имя к каждому элементу и вносим всё обратно в схему
    знчСхемы=Неопределено;
    мЭлементовГС=ПолучитьКоллекциюЭлементовСхемы(рСхема,знчСхемы);
    Если мЭлементовГС.Количество()>0 Тогда
        спТипов=ПостроитьНотациюТиповЭлементовСхемы();
        Для каждого рЭлементГС Из мЭлементовГС Цикл
            рЭлементГС.Вставить("Имя",СоотСвойство(рЭлементГС,"itemCode"));
            знчТип=спТипов.НайтиПоЗначению(СоотСвойство(рЭлементГС,"itemType","Число"));
            Если знчТип<>Неопределено Тогда
                рЭлементГС.Вставить("Тип",    Тип("ЭлементГрафическойСхемы"+СокрЛП(знчТип.Представление)));
            Иначе
                рЭлементГС.Вставить("Тип",Неопределено);
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    //            
    знчСхемы.Вставить("item",мЭлементовГС);
    рСхема.Вставить("#value",знчСхемы);
    
    Возврат рСхема;    
Исключение
    Сообщить("РазобратьГрафическуюСхему, общая ошибка: "+ОписаниеОшибки());
    Возврат Неопределено;
КонецПопытки;
КонецФункции    

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

Кому пригодится или кого заинтересует - будет хорошо.

Ещё раз спасибо гигантам, на плечах которых стоит это решение. В первую очередь Diversus'у, высказавшему идею ещё в далёкие времена.

 

 

Графическая схема; Программное изменение графической схемы; Построение графической схемы

См. также

Infostart Toolkit: Инструменты разработчика 1С 8.3 на управляемых формах

Инструментарий разработчика Роли и права Запросы СКД Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Конфигурации 1cv8 Платные (руб)

Набор инструментов программиста и специалиста 1С для всех конфигураций на управляемых формах. В состав входят инструменты: Консоль запросов, Консоль СКД, Консоль кода, Редактор объекта, Анализ прав доступа, Метаданные, Поиск ссылок, Сравнение объектов, Все функции, Подписки на события и др. Редактор запросов и кода с раскраской и контекстной подсказкой. Доработанный конструктор запросов тонкого клиента. Продукт хорошо оптимизирован и обладает самым широким функционалом среди всех инструментов, представленных на рынке.

13000 руб.

02.09.2020    119910    656    389    

701

Infostart PrintWizard

Пакетная печать Печатные формы Инструментарий разработчика Платформа 1С v8.3 Запросы 1С:Зарплата и кадры бюджетного учреждения 1С:Конвертация данных 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 Платные (руб)

Инструмент, позволяющий абсолютно по-новому взглянуть на процесс разработки печатных форм. Благодаря конструктору можно значительно снизить затраты времени на разработку печатных форм, повысить качество и "прозрачность" разработки, а также навести порядок в многообразии корпоративных печатных форм.

18000 руб.

06.10.2023    7007    20    6    

37

Infostart УДиФ: Управление данными и формами

Инструменты администратора БД Инструментарий разработчика Роли и права Платформа 1С v8.3 Конфигурации 1cv8 Россия Платные (руб)

Расширение позволяет без изменения кода конфигурации выполнять проверки при вводе данных, скрывать от пользователя недоступные ему данные, выполнять код в обработчиках. Не изменяет данные конфигурации, легко устанавливается практически на любую конфигурацию на управляемых формах.

10000 руб.

10.11.2023    3244    10    1    

31

SALE! 30%

PowerTools

Инструментарий разработчика Инструменты администратора БД Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Россия Платные (руб)

Универсальный инструмент программиста для администрирования конфигураций. Сборник наиболее часто используемых обработок под единым интерфейсом.

3600 2520 руб.

14.01.2013    177341    1070    0    

846

Многопоточность. Универсальный «Менеджер потоков» 2.1

Инструментарий разработчика Платформа 1С v8.3 Конфигурации 1cv8 Россия Платные (руб)

Восстановление партий или взаиморасчетов, расчет зарплаты, пакетное формирование документов или отчетов - теперь все это стало доступнее. * Есть желание повысить скорость работы медленных алгоритмов! Но... * Нет времени думать о реализации многопоточности? * о запуске и остановке потоков? * о поддержании потоков в рабочем состоянии? * о передаче данных в потоки и как получить ответ из потока? * об организации последовательности? Тогда ЭТО - то что надо!!!

5000 руб.

07.02.2018    99203    239    97    

296

[ЕХТ] Фреймворк для Расширений 1С

Инструментарий разработчика Платформа 1С v8.3 Управляемые формы Платные (руб)

"Фреймворк для Расширений 1С" это универсальное и многофункциональное решение, упрощающее разработку и поддержку создаваемых Расширений. Поставляется в виде комплекта из нескольких Расширений с открытым исходным кодом. Работает в любых Конфигурациях в режиме Управляемого приложения с режимом совместимости 8.3.12 и выше без необходимости внесения изменений в Конфигурацию.

3000 руб.

27.08.2019    17910    6    8    

38

1С HTML Шаблоны / HTML Templates

Инструментарий разработчика Платформа 1С v8.3 Конфигурации 1cv8 Платные (руб)

Быстрая и удобная обработка для работы с шаблонами HTML. Позволяет легко и быстро формировать код HTML.

2040 руб.

27.12.2017    27945    3    10    

14

Выполнение произвольного кода или запроса с параметрами через Web-сервис (замена COM-подключений)

Инструментарий разработчика Обмен между базами 1C Платформа 1С v8.3 Платные (руб)

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

2400 руб.

24.09.2019    23488    15    15    

31
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. genayo 18.02.18 17:48 Сейчас в теме
А можно рассказать о практическом применении, если не трудно?
2. triviumfan 91 18.02.18 18:36 Сейчас в теме
(1)
Концепцию взял у //infostart.ru/public/551576/ и допилил под свой извращённый вкус.

Вроде все понятно, любит извращения. А для рисования есть другое ПО :)
3. Yashazz 4707 18.02.18 20:17 Сейчас в теме
(1) Ну, одно из интересных применений, авось, в ближайшие недели 2-3 выложу как отдельную публикацию, а вообще, например, визуализация сценариев работы сотрудников колл-центра. Последовательности вопросов при общении с клиентами. А вот что стало самым главным заказом, я рассказать не смогу, т.к. заказчиком выступает ФНС и всё секретно)

(2) Для рисования таких схем есть Visio, но плюсы схем в 1С, думаю, очевидны. Какое ещё ПО вы знаете? Можете ли предложить готовое решение работы с ним?
4. pm74 199 18.02.18 20:45 Сейчас в теме
(0) тема интересная плюсанул , поразбираюсь на досуге
с визуализацией в 1с плохо - это факт
поэтому на ИС традиционно высок интерес к разным штуковинам на javascript или наподобие этой
мои 5 коп. на тему применимости см. рис. (до правильной программной компоновки тоже руки пока не дошли)
Прикрепленные файлы:
5. Yashazz 4707 19.02.18 11:18 Сейчас в теме
(4) А вот этот замечательный скриншот - он руками был сделан? Можно подробнее в личку?
6. pm74 199 19.02.18 11:48 Сейчас в теме
(5)
он руками был сделан

нет ну что вы , конечно программно
сделал по мотивам какой то публикации, к сожалению не помню авторства
используется для формирования схемы ДКАпо таблице переходов

&НаСервере
Функция НоваяТочкаXDTO(x,y) 	Точка=СериализаторXDTO.Фабрика.Создать(СериализаторXDTO.Фабрика.Тип("http://v8.1c.ru/8.2/data/graphscheme","Point"));
	Точка.x=x;
	Точка.y=y;	
	Возврат Точка;
КонецФункции
&НаСервере
Функция НовыйОбъектXDTO(Тип,ИмяЭлемента,Верх,Лево,Высота,Ширина,Содержание,Фигура,Цвет=Неопределено,Шрифт=Неопределено,LastID=0)Экспорт
	НО=СериализаторXDTO.Фабрика.Создать(СериализаторXDTO.Фабрика.Тип("http://v8.1c.ru/8.2/data/graphscheme","GraphSchemeItem"));
	НО.itemType=Тип;
	НО.itemCode=ИмяЭлемента;
	НО.itemId=LastID;
	НО.zOrder=LastID;
	LastID=LastID+1;
	НО.itemTabOrder=1;
	НО.rectLeft=Лево;
	НО.rectRight=Лево+Ширина;
	НО.rectTop=Верх;
	НО.rectBottom=Верх+Высота;
	НО.Border=СериализаторXDTO.ЗаписатьXDTO(Новый Линия(ТипСоединительнойЛинии.Сплошная,1));
	НО.Point.Добавить(НоваяТочкаXDTO(НО.rectLeft,НО.rectTop));
	НО.Point.Добавить(НоваяТочкаXDTO(НО.rectRight-1,НО.rectTop));
	НО.Point.Добавить(НоваяТочкаXDTO(НО.rectRight-1,НО.rectBottom-1));
	НО.Point.Добавить(НоваяТочкаXDTO(НО.rectLeft,НО.rectBottom-1));
	Заголовки=СериализаторXDTO.Фабрика.Создать(СериализаторXDTO.Фабрика.Тип("http://v8.1c.ru/8.1/data/core","LocalStringType"));
	ЗаголовокЭлемента=СериализаторXDTO.Фабрика.Создать(СериализаторXDTO.Фабрика.Тип("http://v8.1c.ru/8.1/data/core","LocalStringItemType"));
	ЗаголовокЭлемента.lang="#";
	ЗаголовокЭлемента.content=Содержание;
	Заголовки.Item.Добавить(ЗаголовокЭлемента);
	НО.itemTitle=Заголовки;
	Примечания=СериализаторXDTO.Фабрика.Создать(СериализаторXDTO.Фабрика.Тип("http://v8.1c.ru/8.1/data/core","LocalStringType"));
	Примечание=СериализаторXDTO.Фабрика.Создать(СериализаторXDTO.Фабрика.Тип("http://v8.1c.ru/8.1/data/core","LocalStringItemType"));
	Примечание.lang="#";
	Примечание.content=Содержание;
	Примечания.Item.Добавить(Примечание);
	НО.tipText=Примечания;
	НО.currentLanguage="#";
	НО.shape=Фигура;
	Если Цвет=Неопределено Тогда
		НО.backColor=Новый Цвет;
	Иначе
		НО.backColor=Новый Цвет(Цвет[0],Цвет[1],Цвет[2]);
	КонецЕсли;
	
	Если Не Шрифт=Неопределено Тогда
		НО.textFont=СериализаторXDTO.ЗаписатьXDTO(Шрифт);
		
	КонецЕсли;	
	Возврат НО;
КонецФункции
&НаСервере
Функция НоваяЛинияXDTO(ГрафСхемаXDTO,ПервыйЭлемент,ВторойЭлемент,Содержание,Подсказка,ПортВыхода=Неопределено,ПортВхода=Неопределено,LastID=0,Цвет=Неопределено)Экспорт
	НО=СериализаторXDTO.Фабрика.Создать(СериализаторXDTO.Фабрика.Тип("http://v8.1c.ru/8.2/data/graphscheme","GraphSchemeItem"));
	НО.itemType=1;
	НО.itemId=LastID+100;
	//НО.zOrder=LastID+100;
	LastID=LastID+1;
	НО.itemTabOrder=2;
	НО.itemCode=Подсказка;
	Заголовки=СериализаторXDTO.Фабрика.Создать(СериализаторXDTO.Фабрика.Тип("http://v8.1c.ru/8.1/data/core","LocalStringType"));
	ЗаголовокЭлемента=СериализаторXDTO.Фабрика.Создать(СериализаторXDTO.Фабрика.Тип("http://v8.1c.ru/8.1/data/core","LocalStringItemType"));
	ЗаголовокЭлемента.lang="#";
	ЗаголовокЭлемента.content=Содержание;
	Заголовки.Item.Добавить(ЗаголовокЭлемента);
	НО.itemTitle=Заголовки;
	НО.currentLanguage="#";
	НО.textPos="Middle";
	НО.beginArrowStyle=СтильСтрелки.Нет;
	НО.endArrowStyle=СтильСтрелки.Незаполненная;
	НО.lineColor=Цвет;
	
	ДанныеПервый=ПортыЭлементаXDTO(ГрафСхемаXDTO,ПервыйЭлемент);
	ДанныеВторой=ПортыЭлементаXDTO(ГрафСхемаXDTO,ВторойЭлемент);
	
	НО.connectFromItemId=ДанныеПервый.itemId;
	НО.connectToItemId=ДанныеВторой.itemId;
	НО.connectFromPortIndex=0;
	
	Если ПортВыхода=Неопределено Или ПортВхода=Неопределено Тогда
		Если ДанныеПервый.itemId<ДанныеВторой.itemId Тогда
			ПортВыхода=2;ПортВхода=2;
		ИначеЕсли ДанныеПервый.itemId>ДанныеВторой.itemId Тогда 
			ПортВыхода=4; ПортВхода=4;
		Иначе 
			ПортВыхода=1; ПортВхода=2;
		КонецЕсли;
	КонецЕсли;	
	
	НО.portIndexFrom=ПортВыхода;
	НО.portIndexTo=ПортВхода;
	НО.backColor=новый Цвет;
	НО.decorativeLine=Истина;
	НО.textFont=СериализаторXDTO.ЗаписатьXDTO(новый Шрифт("Calibri",10,,Истина));
	НО.textColor=Цвет;
	НО.Border=СериализаторXDTO.ЗаписатьXDTO(новый Линия(ТипСоединительнойЛинии.Сплошная,1));
	НО.Point.Добавить(НоваяТочкаXDTO(ДанныеПервый["port"+ПортВыхода+"x"],ДанныеПервый["port"+ПортВыхода+"y"]));
	НО.Point.Добавить(НоваяТочкаXDTO(ДанныеВторой["port"+ПортВхода+"x"],ДанныеВторой["port"+ПортВхода+"y"]));	
	Возврат НО;
КонецФункции
&НаСервере
Функция ПортыЭлементаXDTO(ГрафСхемаXDTO,itemCode)Экспорт
	Данные=Новый Структура;
	Данные.Вставить("itemId",0);
	Данные.Вставить("port1x",0);
	Данные.Вставить("port1y",0);
	Данные.Вставить("port2x",0);
	Данные.Вставить("port2y",0);
	Данные.Вставить("port3x",0);
	Данные.Вставить("port3y",0);
	Данные.Вставить("port4x",0);
	Данные.Вставить("port4y",0);
	Для Каждого Элемент Из ГрафСхемаXDTO.item Цикл
		Если СокрЛП(НРег(Элемент.itemCode))=СокрЛП(НРег(itemCode)) Тогда
			Данные.Вставить("itemId",Элемент.itemId);
			Данные.Вставить("port1x",Элемент.rectLeft);
			Данные.Вставить("port1y",Элемент.rectTop+(Элемент.rectBottom-Элемент.rectTop)/2);
			Данные.Вставить("port2x",Элемент.rectLeft+(Элемент.rectRight-Элемент.rectLeft)/2);
			Данные.Вставить("port2y",Элемент.rectTop);
			Данные.Вставить("port3x",Элемент.rectRight);
			Данные.Вставить("port3y",Элемент.rectTop+(Элемент.rectBottom-Элемент.rectTop)/2);
			Данные.Вставить("port4x",Элемент.rectLeft+(Элемент.rectRight-Элемент.rectLeft)/2);
			Данные.Вставить("port4y",Элемент.rectBottom);
			Прервать;
		КонецЕсли;	  
	КонецЦикла;
	Возврат Данные;
КонецФункции
&НаСервере
Функция ИндексЭлементаXDTO(ГрафСхемаXDTO,itemCode)Экспорт
	Для Й=0 По ГрафСхемаXDTO.item.Количество()-1 Цикл
		Если СокрЛП(НРег(ГрафСхемаXDTO.item[Й].itemCode))=СокрЛП(НРег(itemCode)) Тогда
			Возврат Й;
		КонецЕсли;	  
	КонецЦикла;
	Возврат -1;
КонецФункции
&НаСервере
Функция НайтиЭлементXDTO(ГрафСхемаXDTO,itemCode)Экспорт
	Для Каждого Элемент Из ГрафСхемаXDTO.item Цикл
		Если СокрЛП(НРег(Элемент.itemCode))=СокрЛП(НРег(itemCode)) Тогда
			Возврат Элемент;
		КонецЕсли;	  
	КонецЦикла;
	Возврат Неопределено;
КонецФункции
&НаСервере
Процедура УдалитьЭлементXDTO(ГрафСхемаXDTO,itemCode)Экспорт
	ГрафСхемаXDTO.item.Удалить(ИндексЭлементаXDTO(ГрафСхемаXDTO,itemCode));
КонецПроцедуры
&НаСервере
Функция ПолучитьТекстЭлементаXDTO(ГрафСхемаXDTO,itemCode)Экспорт
	Элемент=НайтиЭлементXDTO(ГрафСхемаXDTO,itemCode);
	Возврат Элемент.itemTitle.item[0].content;
КонецФункции
&НаСервере
Процедура ИзменитьТекстЭлементаXDTO(ГрафСхемаXDTO,itemCode,НовыйТекст)Экспорт
	Элемент=НайтиЭлементXDTO(ГрафСхемаXDTO,itemCode);
	Элемент.itemTitle.item[0].content=НовыйТекст;
КонецПроцедуры


Показать


к сожалению работает не идеально , порты линий нужно проставить вручную , линии переходов иногда "склеиваются " , чтобы их разделить нужно немного "пошевелить " элемент
7. pm74 199 19.02.18 12:14 Сейчас в теме
(5)с графической схемой работать удобно , единственный минус(имо) - нет расшифровки , поэтому сделать полностью интеракивной (например как рис.) сложновато
Прикрепленные файлы:
8. Yashazz 4707 19.02.18 12:50 Сейчас в теме
В предложенной публикации порты управляются программно, их можно перевешивать как угодно. А что линии склеиваются, так это штатная манера самой 1С, для созданных вручную всё так же.
нет расшифровки

Это почему? Есть расшифровка, у меня часть бизнес-логики именно на ней основана. Очень даже есть.
9. pm74 199 19.02.18 12:57 Сейчас в теме
(8)
Есть расшифровка

интересненько , я пока внимательно не прочитал (имел в виду "родную" расшифровку как в ТД )
интерфейс наподобие обработки расшифровки тоже есть ?

(8)
управляются программно
т.е. есть алгоритм для "красивого" расположения входных и выходных портов ?
10. Yashazz 4707 19.02.18 17:49 Сейчас в теме
(9) Конечно есть. Роднее некуда. Собственно, это штатная возможность граф.схем, я ею пользовался, но ничего сверхъестественного не делал, чтобы она работала, она как раз и сама по себе в платформе очень даже.

Алгоритма нет, есть параметр, который этим управляет. Свойство с типом, как в платформе. Делать алгоритм для красоты, скажу честно, было тупо некогда)
11. pm74 199 19.02.18 19:43 Сейчас в теме
(10)
Собственно, это штатная возможность граф.схем, я ею пользовался, но ничего сверхъестественного не делал

Я и впрямь что-то упустил. (связанную с элементами ГС какую- то коллекцию-реквизит пока не рассматриваем) , вроде в ГС нет параметров из которых можно напрямую извлечь ссылку на объект базы данных.
Не могли бы пояснить , а то чего-то недопонимаю.
(10)
есть параметр, который этим управляет
это понятно , имел в виду вручную (либо по умолчанию) назначение номеров входов / выходов

Под "красивой" расстановкой подразумевалось полностью программное расположение элементов , обеспечивающее наилучшую компактность и читабельность схемы при наименьшем пересечении линий.
12. Serg O. 224 21.02.18 07:00 Сейчас в теме
Листинг на 5 стр. Зачем? Читать код тут...никто не будет.
Сами же пишете, что у Вас готовое для допила решение
13. Yashazz 4707 21.02.18 19:23 Сейчас в теме
(12) Читать не будут. Копипастить будут) Особенно те, у кого стартманей нету)) Читайте внимательнее: обработка - для допила, а код - уже боевой.
imaster; Nerr; Зеленоград; XelOla; +4 Ответить
Оставьте свое сообщение