Enterprise Architect -> Как получить границу конечного узла с помощью SQL-запроса в .eap-файле (.mdb)

#sql #ms-access #enterprise-architect

#sql #ms-access #enterprise-architect

Вопрос:

Я должен нарисовать несколько диаграмм EA, используя только файл .eap без установленного EA на сервере. Итак, я открываю его как MDB-файл через ODBC.

Я знаю, что есть атрибут t_diagramlinks.Geometry (с ребром ={1,2,3,4}) и атрибут t_connector.Start_Edge , и атрибут t_connector.End_Edge .

Таблица, t_diagramlinks с атрибутом Geometry , зависит от диаграммы. Таблица t_connector с атрибутами .Start_Edge и .End_Edge не зависит от диаграммы -> могут быть соединения, которые не были нарисованы на схеме.

Я знаю, что SX, SY, EX, EY из t_diagramlinks являются координатами относительно начала координат каждого узла, который нарисован на диаграмме.

Проблема: EX / EY иногда равен нулю и не выводит конечную строку на край узла. Я предполагаю, что это как-то связано с положением отпускания мыши.

«Моя интерпретация» ниже — это то, что выдает мой рендерер на основе моих предположений.

«Интерпретация EA» — это то, что EA на самом деле визуализирует и что я хотел бы видеть в моем средстве визуализации.

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

Вопросы

  • Я использую ребро со значением csv в t_diagramlinks.Geometry — но где мне найти это для конечного узла?

  • Для какой цели атрибуты Start_Edge являются End_Edge в таблице t_connector , если они не зависят от диаграммы?

Комментарии:

1. Итак, «Моя интерпретация» — это действительно то, что вы хотели бы видеть от EA? Соединитель, ведущий к внутренней части элемента??

2. Привет, нет — в настоящее время это то, что рисует мое веб-приложение из-за EX = 0 и EY = 0 — я хотел бы видеть это так, как это рисуется в EA.

3. Ну ладно. Я попытаюсь подробнее рассказать о том, как EA выполняет его рендеринг. Пожалуйста, также проясните вопрос на форуме Sparx (вы также можете сослаться на эту тему).

Ответ №1:

Я использую ссылки в t_diagramlinks.Геометрия ребра в формате csv-значение — но где мне найти это для конечного узла?

Вам нужно использовать Euclide. SX, SY / EX, EY — относительные сдвиги от кратчайшего центрального соединения между начальным и конечным элементом.

Для какой цели атрибуты Start_Edge являются End_Edge в таблице «t_connector», если они не зависят от диаграммы?

Они используются для определенных свойств.

Редактировать: чтобы подробнее остановиться на вашей основной проблеме. t_diagramlinks.path содержит точки изгиба для соединителя (если таковые указаны). Итак, чтобы найти точку, где соединитель фактически соединяет элемент, вам нужно найти ближайший изгиб к этому элементу. Теперь между этим изгибом и серединой элемента у вас будет естественная точка прикрепления. Относительно этого добавляются SX-Y (/ EX-Y), чтобы создать вручную смещенную отображаемую точку вложения.

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

2-я правка: Теперь, когда я знаю, что «Моя интерпретация» — это то, что выдает ваш рендерер на основе ваших предположений, вот (наиболее вероятная; см. Выше) история. Для визуализации соединителя EA будет использовать следующую информацию:

  • координаты фрейма двух соединенных элементов
  • вычисляется по координатам средней точки элементов
  • свойство path соединителя (если не пустое)
  • ближайшая точка изгиба к соответствующим элементам (если не пуста)
  • коэффициенты сдвига SX-Y и EX-Y для соединителя

Начиная со средней точки начального элемента, вы проводите виртуальную линию либо до ближайшей точки изгиба, либо до середины конечного элемента (если не указано ниже). Таким образом, вы можете вычислить виртуальную точку прикрепления в прямоугольной рамке элемента (даже варианты использования имеют прямоугольную рамку). Теперь вы сдвигаете эту точку на SX-Y, что будет (/ должно?) всегда перемещайтесь по краю рамки элемента. Теперь у вас есть виртуальная точка вложения для начального элемента.

С другой стороны (конечный элемент; мое «если» сверху) вы бы сделали аналогичную вещь, чтобы вычислить виртуальное вложение для конца. Чего я не знаю, так это реального порядка, в котором EA это делает (у меня нет понимания кода). Итак, если у вас есть ручные смещения с обеих сторон, вычисление даст разные результаты в зависимости от порядка подключения виртуального соединения к другой стороне (итак: соблюдается ли сдвиг на другой стороне или нет). В принципе, я думаю, вы можете пренебречь этим в 99,9% всех случаев, а остальное — просто неуместный шум.

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

Опять же: все с долей сомнения. Это просто наблюдение извне, но, вероятно, не слишком далеко. Существует также тот факт, что у вас есть разные стили линий со скругленными краями (здесь не учитываются) и линии Безье (еще больше dragon land).

Комментарии:

1. Я проголосую «против» за эту аннотацию запроса. Удалите его, и голосование «против» будет заменено голосованием «за» для остальных.

2. Проблема в том, что «Euclide» рисует конечные узлы где-то, но не на ребре — например, и ey иногда равен нулю. Инструкция SQL работает для меня, но я не знаю, где найти ребро конечного узла — я получаю Geometry и Start_Edge и End_Edge — но t_connector. End_Edge не зависит от диаграммы. Я удалил SQL-запрос, как вы просили, хотя он работал.

3. Проблема: EX / EY иногда равен нулю и не выводит конечную строку на ребро.

4. Об удаленном SQL: я сделал так много подзапросов, потому что у меня была проблема с тем, что «обычные» соединения съедали несколько строк. С этими подзапросами это сработало после некоторых экспериментов. Вместо него я добавил картинку.

5. Я подробно остановился на том, как работает средство визуализации EA (насколько мне известно).

Ответ №2:

Большое вам спасибо. Я решил вычислить конечное ребро математически:

Конечный узел Edgge

Моя проблема заключалась в том, что я не смог определить конечную границу целевого конечного узла в отношении ссылки. Кроме того, существует 8 различных типов ссылок, которые определяют расположение ссылок. Итак, как упоминал Томас, мне пришлось определить последнюю точку перед конечной точкой. Если это путь, то последний узел пути является точкой перед конечной точкой. Если путь не указан, начальной точкой startnodes является последняя точка перед конечной точкой. Но если путь определен и режим связи установлен в 1, я могу не обработать путь подключения, потому что свойство Conn_Path содержит настроенную строку — но после настройки пользователь выбрал прямую ссылку (она не будет удалена).

Математическая основа используется в виде линейной функции y = m * x b, а линии описываются 4 прямыми линиями, которые соответствуют краям конечного узла.

Итак, вы можете выполнить следующий алгоритм:

Первая страница алгоритма

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

Полный алгоритм использует следующий подход:

1.) Определите прямую линию между начальным и конечным узлами (есть 2 особых случая, если линия полностью горизонтальна или вертикально параллельна системе координат)

2.) Создайте прямоугольник, состоящий из четырех прямых линий (2 вертикальные / 2 горизонтальные линии)

3.) Определите пересечение первой прямой с линиями прямоугольников

4.) Исключить точки, которые не входят в прямоугольник

5.) Определите точку на прямоугольнике с наименьшим расстоянием => это искомая конечная точка ребра

Написанный код javascript, который я использовал для выполнения маршрутизации, следующий:

                 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Erzeuge eine eigene Link-Klasse für das Routing der Pfeile, die von Hand gezogen wurden
// und über spezielle Attribute in der EAP-Datei definiert werden
// Ruft die Superklasse von go.Link im Konstruktor auf
function MultiNodePathLink() {
    go.Link.call(this);
}
go.Diagram.inherit(MultiNodePathLink, go.Link); // Erben von go.Link

// ignores this.routing, this.adjusting, this.corner, this.smoothness, this.curviness
/** @override */
MultiNodePathLink.prototype.computePoints = function () {

    // Die this Referenz ist hier ist ein geerbter ein go.Link. der bei Links
    var startNode = this.fromNode;
    var startNodeX = startNode.location.M;  // X-Koordinate vom Startknoten
    var startNodeY = startNode.location.N; // Y-Koordinate vom Startknoten

    var endNode = this.toNode;
    var endNodeX = endNode.location.M;  // X-Koordinate vom Startknoten
    var endNodeY = endNode.location.N; // Y-Koordinate vom Startknoten

    var startNodeData = startNode.data; // Das sind die Daten
    var endNodeData = endNode.data; // Das sind die Daten

    // Die Link-Daten
    var linkProperties = this.data;
    //** Das Feld Style in [t_diagramlink] bestimmt die Connector-Darstellung  **/
    // http://www.capri-soft.de/blog/?p=2904
    /*
     *  1 = Direct                    Mode=1
     *  2 = Auto Routing              Mode=2
     *  3 = Custom Line               Mode=3
     *  4 = Tree Vertical             Mode=3;TREE=V
     *  5 = Tree Horizontal           Mode=3;TREE=H
     *  6 = Lateral Vertical          Mode=3;TREE=LV
     *  7 = Lateral Horizontal        Mode=3;TREE=LH
     *  8 = Orthogonal Square         Mode=3;TREE=OS
     *  9 = Orthogonal Rounded        Mode=3;TREE=OR
     */
    var styleStringArray = linkProperties.style.split(";");
    var mode = -1;
    var tree = '';
    for (var i = 0; i < styleStringArray.length; i  ) {
        if (styleStringArray[i].trim().indexOf('Mode=') > -1) {
            mode = styleStringArray[i].replace('Mode=', '');
        }

        if (styleStringArray[i].trim().indexOf('TREE=') > -1) {
            tree = styleStringArray[i].replace('TREE=', '');
        }
    }



    // In der Tabelle t_diagramlinks in der Freitextspalte "Geometry" wird in einem CSV-String
    // gespeichert, wie der Link letztendlich auf dem Diagram gezogen wurde
    var geometryString = linkProperties.geometry.split(";");
    // SX and SY are relative to the centre of the start object
    var sx = geometryString[0].replace("SX=", "");
    var sy = geometryString[1].replace("SY=", "");
    // EX and EY are relative to the centre of the end object
    var ex = geometryString[2].replace("EX=", "");
    var ey = geometryString[3].replace("EY=", "");

    // SX=-67;SY=-43;EX=-12;EY=-40;EDGE=3;$LLB=;
    // LLT=;LMT=;LMB=CX=30:CY=13:OX=11:OY=-2:HDN=0:BLD=0:ITA=0:UND=0:CLR=-1:ALN=1:DIR=0:ROT=0;
    // LRT=;LRB=;IRHS=;ILHS=;

    // EDGE ranges in value from 1-4, with 1=Top, 2=Right, 3=Bottom, 4=Left (Outgoing Point of the Start Object)
    var edge = geometryString[4].replace("EDGE=", "");

    // Hier beginnt das Custom-Routing
    this.clearPoints();
    if (linkProperties.start_object_name == 'System Verification Test Reports' amp;amp; linkProperties.end_object_name == 'System test specifications') {
        var test = 'irrsinn';
    }
    // Hier werden die Wege definiert für das gecustomizte Link Routing
    // Geht der Link nach oben oder unten wird die Y-Koordinate des Startknotens genutzt (Weil Orthogonales Routing)
    var startConnX = null;
    var startConnY = null;
    if (edge == 1) { // Ecke oben
        startConnX = Math.abs(startNodeX)   Math.abs((startNode.actualBounds.width / 2)   new Number(sx));
        startConnY = Math.abs(startNodeY);
    }
    else if (edge == 3) { // Ecke unten
        startConnX = Math.abs(startNodeX)   Math.abs((startNode.actualBounds.width / 2)   new Number(sx));
        startConnY = Math.abs(startNodeY)   new Number(startNode.actualBounds.height);
    }
    else if (edge == 2) { // Ecke rechts
        startConnX = Math.abs(startNodeX)   startNode.actualBounds.width;
        startConnY = Math.abs(startNodeY)   Math.abs((startNode.actualBounds.height / 2) - new Number(sy));
    }
    else if (edge == 4) { // Ecke links
        startConnX = new Number(Math.abs(startNodeX));
        startConnY = Math.round(startNodeY)   Math.round((startNode.actualBounds.height / 2) - new Number(sy));
    }
    else {
        alert('Die Edge konnte nicht entdeckt werden! Ist der Geometry String in der EAP Datei richtig?');
    }

    this.addPoint(new go.Point(Math.round(startConnX), Math.round(startConnY)));

    // Abfrage: Gibt es einen letzten Path Punkt?
    var lastPathPunkt=false;
    var lastPathPunktX, lastPathPunktY;

    if (mode != 1)
    {
        // Routing über die Zwischenwege
        if (typeof linkProperties.conn_path !== "undefined" amp;amp; linkProperties.conn_path !== "") {
            var splittedArray = linkProperties.conn_path.split(";");
            if (splittedArray.length > 1) {
                // Hier ist mindestens ein Wert vorhanden da auch der erste mit Semikolon abgeschlossen wird im Path vom EA
                for (var i = 0; i < splittedArray.length - 1; i  ) {
                    var einMittelPunkt = splittedArray[i];
                    var mittelPunktArray = einMittelPunkt.split(":");
                    this.addPoint(new go.Point(Math.abs(new Number(mittelPunktArray[0])), Math.abs(new Number(mittelPunktArray[1]))))
                    lastPathPunktX = Math.abs(new Number(mittelPunktArray[0]));
                    lastPathPunktY = Math.abs(new Number(mittelPunktArray[1]));
                    lastPathPunkt = true;
                }
            }
        }
    }

    // Wenn es keinen Pfad gab,muss der letzte Punkt mit dem Startknoten identisch sein
    if (lastPathPunkt == false) {
        lastPathPunktX = Math.abs(Math.round(startConnX));
        lastPathPunktY = Math.abs(Math.round(startConnY));
    }

    // End-Routing
    // Der Endpunkt in EA in Document Coordinates
    var endConnX = Math.abs(endNodeX)   Math.abs((endNode.actualBounds.width / 2)   new Number(ex));
    var endConnY = Math.abs(endNodeY)   Math.abs((endNode.actualBounds.height / 2) - new Number(ey));

    // Spezialfälle bei horizontalen und vertikalen Linien:
    if (endConnX == lastPathPunktX) {
        // Es liegt eine vertikale Gerade (z.B. von oben nach unten) vor
        this.addPoint(new go.Point(Math.round(lastPathPunktX), Math.round(lastPathPunktY)));
        this.addPoint(new go.Point(Math.round(endConnX), Math.round(endConnY)));

    } else if (endConnY == lastPathPunktY) {
        // Es liegt eine horizontale Gerade (z.B. von rechts nach links) vor
        this.addPoint(new go.Point(Math.round(lastPathPunktX), Math.round(lastPathPunktY)));
        this.addPoint(new go.Point(Math.round(endConnX), Math.round(endConnY)));
    } else {
        // Es ist keine Gerade sondern ein Gerade, die mit y=m*x b beschrieben werden kann

        // 1.) Gerade zwischen Start- und Endpunkt ermittelnhn
        //      Ye-Ys
        //  m = -----    b=Ys-m*Xs oder b=Ye-m*Xe
        //      Xe-Xs
        var m = (endConnY - lastPathPunktY) / (endConnX - lastPathPunktX);
        var b = lastPathPunktY - m * lastPathPunktX

        // 2.) Ermittlung der horizontalen und vertikalen Geraden des Rechteckes und dem Schnittpunkten
        // Die Geraden, die das Rechteck definieren:
        var rY1 = endNodeY;
        var rY2 = endNodeY   endNode.actualBounds.height;
        var rX1 = endNodeX;
        var rX2 = endNodeX   endNode.actualBounds.width;
        // (rX1, rY1) -zu-> (rX2, rY2)

        // Horizontale Geraden:
        //     y - b
        // x = -----
        //       m


        var lengthToPoint = [];

        var sX1 = (rY1 - b) / m; // S1(sX1|rY1)
        if (sX1 >= rX1 amp;amp; sX1 <= rX2) {
            // Der Schnittpunkt sX1 ist am Rechteck
            // Distanz: d=SQRT((y2-y1)^2 (x2-x1)^2)
            var dS1 = Math.sqrt(Math.pow(rY1 - lastPathPunktY, 2)   Math.pow(sX1 - lastPathPunktX, 2));

            lengthToPoint.push({
                "distanz": dS1,
                "x": sX1,
                "y": rY1
            });

        }

        var sX2 = (rY2 - b) / m; // S2(sX2|rY2)
        if (sX2 >= rX1 amp;amp; sX2 <= rX2) {
            // Der Schnittpunkt sX2 ist am Rechteck
            // Distanz: d=SQRT((y2-y1)^2 (x2-x1)^2)
            var dS2 = Math.sqrt(Math.pow(rY2 - lastPathPunktY, 2)   Math.pow(sX2 - lastPathPunktX, 2));

            lengthToPoint.push({
                "distanz": dS2,
                "x": sX2,
                "y": rY2
            });
        }

        // Vertikale Geraden:
        //
        // y = m*x   b

        var sY1 = m * rX1   b; // S3(rX1|sY1)
        if (sY1 >= rY1 amp;amp; sY1 <= rY2) {
            // Der Schnittpunkt sY1 ist am Rechteck
            // Distanz: d=SQRT((y2-y1)^2 (x2-x1)^2)
            var dS3 = Math.sqrt(Math.pow(sY1 - lastPathPunktY, 2)   Math.pow(rX1 - lastPathPunktX, 2));

            lengthToPoint.push({
                "distanz": dS3,
                "x": rX1,
                "y": sY1
            });
        }

        var sY2 = m * rX2   b; // S4(rX2|sY2)
        if (sY2 >= rY1 amp;amp; sY2 <= rY2) {
            // Der Schnittpunkt sY2 ist am Rechteck
            // Distanz: d=SQRT((y2-y1)^2 (x2-x1)^2)
            var dS4 = Math.sqrt(Math.pow(sY2 - lastPathPunktY, 2)   Math.pow(rX2 - lastPathPunktX, 2));

            lengthToPoint.push({
                "distanz": dS4,
                "x": rX2,
                "y": sY2
            });
        }

        // Sortiere alle Punkte nach Distanz - der mit der kleinsten Entfernung isses
        lengthToPoint.sort(function (a, b) { return a.distanz - b.distanz });

        if (lengthToPoint.length > 0)
        {
            this.addPoint(new go.Point(Math.round(lengthToPoint[0].x), Math.round(lengthToPoint[0].y)));
        }
        else
        {
            this.addPoint(new go.Point(Math.round(lastPathPunktX), Math.round(lastPathPunktY)));
        }


    }

    return true;
};
// end MultiNodePathLink class