omniJS – magnet-like point at which one line connects to another?

Where a red arrow connects to the upper left line, the Magnets view shows a point of contact.

Not sure what the term for this is, and the omniJS API doesn’t expose it in the value of the .magnets property.

( Copy As JS reroutes the connection to from Magnet 0 of the lower curve to Magnet 0 of the upper curve. )

Using the omniJS interface directly (making sure that head/tail are set before headMagnet/tailMagnet) we can restore the attachment to Magnet 2 of the lower curve, ( diagram at top right ) but we can’t I think, reproduce the original connection …

I wonder whether these line-to-line points of attachment (pink magnet-like circle between (zero-based) magnets 2 and 3 at top left, are modelled in a way that omniJS could get access to ?

Hi Rob,
There does look to be a bug here in the code produced when using Copy as Javascript, but in my testing, it appears the original connection can be reproduced. To do so, the headMagnet and tailMagnet just need to be set after both the head and tail shapes have been set. So this is what worked for me:

  // Floating point values in this script may be rounded, resulting in minor visual differences from the original
    var canvas = document.windows[0].selection.canvas
    var g1 = canvas.newLine()
    var g2 = canvas.newLine()
    var g3 = canvas.newLine()
    g1.tail = g3
    g1.name = "Arrow"
    g1.lineType = LineType.Curved
    g1.head = g2
    g1.headType = "FilledArrow"
    g1.shadowColor = null
    g1.strokeColor = Color.RGB(1.0, 0.149131, 0.0)
    g1.points = [new Point(309.30, 205.68), new Point(248.71, 67.33)]
    g2.name = "Dest"
    g2.lineType = LineType.Curved
    g2.shadowColor = null
    g2.strokeColor = Color.RGB(0.647058844566345, 0.647058844566345, 0.647058844566345)
    g2.points = [new Point(162.99, 70.67), new Point(191.99, 107.61), new Point(248.71, 67.33), new Point(329.69, 88.61)]
    g3.name = "Source"
    g3.lineType = LineType.Curved
    g3.shadowColor = null
    g3.strokeColor = Color.RGB(0.647058844566345, 0.647058844566345, 0.647058844566345)
    g3.points = [new Point(118.83, 299.06), new Point(162.10, 192.15), new Point(309.30, 205.68), new Point(313.93, 118.21)]
    g1.tailMagnet = 2
    g1.headMagnet = 2

Not quite the same :-)

There are two different issues here:

  1. The problem that setting head or tail clears the value of headMagnets or tailMagnets (reported in another thread). As you say, the workaround there is to reverse the code sequence.
  2. The connection of one line to a non-Magnet connection point on another line.

These non-magnet points of arbitrary connection:

  1. Do display as pink circles in Magnet view, but
  2. do not appear in Array of magnets returned by the omniJS Graphic.magnets property, and
  3. are not accessible in any other way through the omniJS interface, as far as I can see

The original is on the left, and the version regenerated by your (sequence-adjusted) code is on the right:

Here is a full dump of what omniJS can see on the left hand side - the arbitrary non-magnet pink circle connection point is not, I think, to be found:

[
  {
    "id": 10,
    "type": "Line",
    "actionURL": null,
    "alignsEdgesToGrid": true,
    "allowsConnections": true,
    "automationAction": [],
    "bezierPoints": [],
    "connectedLines": [
      8
    ],
    "cornerRadius": 0,
    "flippedHorizontally": false,
    "flippedVertically": false,
    "geometry": {
      "x": 59.527559595220666,
      "y": 111.12523275800547,
      "width": 211.72737674823043,
      "height": 180.85
    },
    "head": null,
    "headMagnet": 0,
    "headScale": 1,
    "headType": "",
    "hopType": "none",
    "incomingLines": [],
    "layer": "Layer 1",
    "lineType": "curved",
    "locked": false,
    "name": "Source",
    "notes": "",
    "outgoingLines": [
      8
    ],
    "plasticCurve": null,
    "plasticHighlightAngle": null,
    "points": [
      {
        "x": 60.388629155304045,
        "y": 291.97523275800546
      },
      {
        "x": 103.65862915530408,
        "y": 185.0652327580055
      },
      {
        "x": 250.8586291553039,
        "y": 198.59523275800547
      },
      {
        "x": 255.4886291553039,
        "y": 111.12523275800547
      }
    ],
    "rotation": 0,
    "shadowColor": null,
    "shadowFuzziness": 3,
    "shadowVector": {
      "x": 0,
      "y": 2
    },
    "strokeCap": "round",
    "strokeColor": {
      "r": 0.647058844566345,
      "g": 0.647058844566345,
      "b": 0.647058844566345,
      "a": 1
    },
    "strokeJoin": "round",
    "strokePattern": "solid",
    "strokeThickness": 1,
    "strokeType": "single",
    "tail": null,
    "tailMagnet": 0,
    "tailScale": 1,
    "tailType": "",
    "userData": {}
  },
  {
    "id": 9,
    "type": "Line",
    "actionURL": null,
    "alignsEdgesToGrid": true,
    "allowsConnections": true,
    "automationAction": [],
    "bezierPoints": [],
    "connectedLines": [
      8
    ],
    "cornerRadius": 0,
    "flippedHorizontally": false,
    "flippedVertically": false,
    "geometry": {
      "x": 104.54862915530407,
      "y": 59.527559595220666,
      "width": 166.69999999999993,
      "height": 41.01510736840808
    },
    "head": null,
    "headMagnet": 0,
    "headScale": 1,
    "headType": "",
    "hopType": "none",
    "incomingLines": [
      8
    ],
    "layer": "Layer 1",
    "lineType": "curved",
    "locked": false,
    "name": "Dest",
    "notes": "",
    "outgoingLines": [],
    "plasticCurve": null,
    "plasticHighlightAngle": null,
    "points": [
      {
        "x": 104.54862915530407,
        "y": 63.58523275800548
      },
      {
        "x": 133.54862915530407,
        "y": 100.52523275800547
      },
      {
        "x": 190.26862915530404,
        "y": 60.245232758005486
      },
      {
        "x": 271.248629155304,
        "y": 81.52523275800547
      }
    ],
    "rotation": 0,
    "shadowColor": null,
    "shadowFuzziness": 3,
    "shadowVector": {
      "x": 0,
      "y": 2
    },
    "strokeCap": "round",
    "strokeColor": {
      "r": 0.647058844566345,
      "g": 0.647058844566345,
      "b": 0.647058844566345,
      "a": 1
    },
    "strokeJoin": "round",
    "strokePattern": "solid",
    "strokeThickness": 1,
    "strokeType": "single",
    "tail": null,
    "tailMagnet": 0,
    "tailScale": 1,
    "tailType": "",
    "userData": {}
  },
  {
    "id": 8,
    "type": "Line",
    "actionURL": null,
    "alignsEdgesToGrid": true,
    "allowsConnections": true,
    "automationAction": [],
    "bezierPoints": [],
    "connectedLines": [],
    "cornerRadius": 0,
    "flippedHorizontally": false,
    "flippedVertically": false,
    "geometry": {
      "x": 221.37463509244404,
      "y": 61.68595172181787,
      "width": 29.483994062859864,
      "height": 136.9092810361876
    },
    "head": 9,
    "headMagnet": 0,
    "headScale": 1,
    "headType": "FilledArrow",
    "hopType": "none",
    "incomingLines": [],
    "layer": "Layer 1",
    "lineType": "curved",
    "locked": false,
    "name": "Arrow",
    "notes": "",
    "outgoingLines": [],
    "plasticCurve": null,
    "plasticHighlightAngle": null,
    "points": [
      {
        "x": 250.8586291553039,
        "y": 198.59523275800547
      },
      {
        "x": 221.37463509244404,
        "y": 61.68595172181787
      }
    ],
    "rotation": 0,
    "shadowColor": null,
    "shadowFuzziness": 3,
    "shadowVector": {
      "x": 0,
      "y": 2
    },
    "strokeCap": "round",
    "strokeColor": {
      "r": 1,
      "g": 0.149131,
      "b": 0,
      "a": 1
    },
    "strokeJoin": "round",
    "strokePattern": "solid",
    "strokeThickness": 1,
    "strokeType": "single",
    "tail": 10,
    "tailMagnet": 2,
    "tailScale": 1,
    "tailType": "",
    "userData": {}
  }
]

Ah, I see. Your GIF helped clarify what I had missed initial which is the headMagnet is not a defined point in the head line itself, and does not get defined when using Copy as Javascript. I’ll get a separate bug written up for that too. Thanks for clarifying.

1 Like

Thanks !

A classic motivating example would be a fishbone diagram:

which I would like to be able to generate using omniJS
(including the line-to-line connections, for subsequent adjustment and flexibility).

It would be very helpful, for example, to be derive a line to line connection from either:

  1. A relative position along a target straight line, combined with the head or tail of a connecting line, or
  2. the nearest position on any target line to the head or tail position of a connecting line.

Without an omniJS interface to these non-magnet line-to-line connections, Copy As > JavaScript is, for the moment, only able to render the diagram above as:

from:

// Floating point values in this script may be rounded, resulting in minor visual differences from the original
var canvas = document.windows[0].selection.canvas;
var g1 = canvas.newShape();
var g2 = canvas.newLine();
var g3 = canvas.newLine();
var g4 = canvas.newLine();
var g5 = canvas.newLine();
var g6 = canvas.newLine();
var g7 = canvas.newLine();
var g8 = canvas.newLine();
var g9 = canvas.newLine();
var g10 = canvas.newLine();
var g11 = canvas.newLine();
var g12 = canvas.newLine();
var g13 = canvas.newLine();
var g14 = canvas.newLine();
g1.text = "Outcome";
g1.cornerRadius = 5;
g1.textVerticalPadding = 0;
g1.autosizing = TextAutosizing.Clip;
g1.textSize = 12;
g1.strokeType = null;
g1.geometry = new Rect(450.71, 144.57, 82.20, 53.86);
g1.strokeColor = null;
g1.magnets = [new Point(1.00, 0.00), new Point(-1.00, 0.00)];
g1.fontName = "Helvetica";
g2.strokeThickness = 2;
g2.head = g1;
g2.headType = "FilledArrow";
g2.shadowColor = null;
g2.strokeColor = Color.RGB(1.0, 0.149131417274475, 0.0);
g2.points = [new Point(131.33, 172.74), new Point(450.71, 171.50)];
g3.strokeThickness = 2;
g3.head = g2;
g3.headType = "FilledArrow";
g3.shadowColor = null;
g3.strokeColor = Color.RGB(1.0, 0.149131, 0.0);
g3.points = [new Point(358.58, 52.44), new Point(404.13, 171.68)];
g4.strokeThickness = 2;
g4.head = g2;
g4.headType = "FilledArrow";
g4.shadowColor = null;
g4.strokeColor = Color.RGB(1.0, 0.149131, 0.0);
g4.points = [new Point(177.17, 48.19), new Point(228.49, 172.36)];
g5.rotation = 249;
g5.strokeThickness = 2;
g5.head = g2;
g5.headType = "FilledArrow";
g5.shadowColor = null;
g5.strokeColor = Color.RGB(1.0, 0.149131, 0.0);
g5.points = [new Point(272.90, 291.94), new Point(342.67, 171.92)];
g6.rotation = 249;
g6.strokeThickness = 2;
g6.head = g2;
g6.headType = "FilledArrow";
g6.shadowColor = null;
g6.strokeColor = Color.RGB(1.0, 0.149131, 0.0);
g6.points = [new Point(103.72, 283.98), new Point(185.85, 172.53)];
g7.strokeThickness = 2;
g7.head = g4;
g7.headType = "FilledArrow";
g7.shadowColor = null;
g7.strokeColor = Color.RGB(1.0, 0.149131, 0.0);
g7.points = [new Point(119.06, 83.62), new Point(191.76, 83.51)];
g8.strokeThickness = 2;
g8.head = g4;
g8.headType = "FilledArrow";
g8.shadowColor = null;
g8.strokeColor = Color.RGB(1.0, 0.149131, 0.0);
g8.points = [new Point(141.73, 127.56), new Point(209.68, 126.84)];
g9.strokeThickness = 2;
g9.head = g3;
g9.headType = "FilledArrow";
g9.shadowColor = null;
g9.strokeColor = Color.RGB(1.0, 0.149131, 0.0);
g9.points = [new Point(277.80, 82.20), new Point(370.17, 82.78)];
g10.strokeThickness = 2;
g10.head = g3;
g10.headType = "FilledArrow";
g10.shadowColor = null;
g10.strokeColor = Color.RGB(1.0, 0.149131, 0.0);
g10.points = [new Point(300.47, 126.14), new Point(386.29, 124.98)];
g11.strokeThickness = 2;
g11.head = g6;
g11.headType = "FilledArrow";
g11.shadowColor = null;
g11.strokeColor = Color.RGB(1.0, 0.149131, 0.0);
g11.points = [new Point(65.20, 212.60), new Point(155.65, 213.52)];
g12.strokeThickness = 2;
g12.head = g6;
g12.headType = "FilledArrow";
g12.shadowColor = null;
g12.strokeColor = Color.RGB(1.0, 0.149131, 0.0);
g12.points = [new Point(31.18, 242.36), new Point(134.57, 242.13)];
g13.strokeThickness = 2;
g13.head = g5;
g13.headType = "FilledArrow";
g13.shadowColor = null;
g13.strokeColor = Color.RGB(1.0, 0.149131, 0.0);
g13.points = [new Point(225.35, 212.60), new Point(319.69, 211.45)];
g14.strokeThickness = 2;
g14.head = g5;
g14.headType = "FilledArrow";
g14.shadowColor = null;
g14.strokeColor = Color.RGB(1.0, 0.149131, 0.0);
g14.points = [new Point(208.35, 242.36), new Point(301.57, 242.63)];

(To prevent the graphic collapse, we would have to delete the .head property assignments, but that would leave us without any connections at all, and each arrow would have to be positioned separately and independently in any adjustment of the diagram)

Essentially, at the moment (OG 7.5 test (v181.3 r293952)) omniJS lets us keep either

  1. geometry (with loss of connections),
  2. or connections (with loss of geometry).

Original (two parallel lines connected to a third)

Copy As JS (keeps some connection, loses significant geometry)

.head, .tail and .headMagnet, .tailMagnet properties deleted by a script when the connection target is another Line, but the connection is to no defined magnet on that line.
(keeps all geometry, loses all connection)

If we do want to preserve geometry by discarding connection, one approach is to use Object.assign:

    // CONNECTION OF LINES TO HEADS, TAILS, AND ANY SPECIFIC MAGNETS
    // Note : As omniJS doesn't yet model non-magnet line-to-line
    // connection points, we erase head|tail and headMagnet|tailMagnet values
    // here if the .head | .tail graphic is another Line object, and
    // not to any specific magnet point on that line.
    // This prevent diagrammatic collapse by line ends repositioning to
    // attachthemselves to 'magnet 0' on the target line.

    // The *cost* is that although the lines retain proper positioning,
        // they *lose* all connection to line at their head | tail.
    return (
        map(line => {
                const [blnToLine, blnFromLine] = map(
                    k => {
                        const g = dctGraphics[line[k]];
                        return g && (g.constructor.name === 'Line') &&
                            !Boolean(line[k + 'Magnet']);
                    }, ['head', 'tail']
                );
                return Object.assign(
                    dctGraphics[line.id],
                    // Hack - head, headMagnet discarded where connection
                    // is to no specific magnet on another Line.
                    blnToLine ? {} : {
                        head: dctGraphics[line.head],
                        headMagnet: (line.headMagnet || 0)
                    },
                    // Hack - tail, tailMagnet discarded where connection
                    // is to no specific magnet on another Line.
                    blnFromLine ? {} : {
                        tail: dctGraphics[line.tail],
                        tailMagnet: (line.tailMagnet || 0)
                    }
                );
            },
            dctRender.lines
        ),
        dctRender.graphics
    );