Skip to content Skip to sidebar Skip to footer

Seeking Javascript Library For Displaying And Editing Networks Of Nodes And Edges

This is my first question, so I hope I have asked it clearly. Apologies in advance if it has already been answered and I just haven't managed to work out the right thing to search

Solution 1:

Here's most of your app: https://gojs.net/extras/splicing.html. The complete implementation code is on the page -- just do a "View Page Source" browser command. But I'll duplicate it here anyway:

<!DOCTYPE html><html><head><metaname="viewport"content="width=device-width, initial-scale=1"><title>Simple Splicing Sample for GoJS</title><metaname="description"content="Splicing a new node into a selected link, and splicing out an existing node in the path of a link chain." /><!-- Copyright 1998-2018 by Northwoods Software Corporation. --><metacharset="UTF-8"><scriptsrc="https://unpkg.com/gojs"></script><scriptsrc="../assets/js/goSamples.js"></script><!-- this is only for the GoJS Samples framework --><scriptid="code">functioninit() {
      if (window.goSamples) goSamples();  // init for these samples -- you don't need to call thisvar $ = go.GraphObject.make;

      myDiagram =
        $(go.Diagram, "myDiagramDiv",
          {
            initialContentAlignment: go.Spot.Center,
            // create a node upon double-click in background:"clickCreatingTool.archetypeNodeData": { text: "a new node" },
            "undoManager.isEnabled": true
          });

      myDiagram.nodeTemplate =
        $(go.Node, "Auto",
          { locationSpot: go.Spot.Center },
          new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
          {
            selectionAdornmentTemplate:
              $(go.Adornment, "Spot",
                $(go.Panel, "Auto",
                  $(go.Shape, { fill: null, stroke: "dodgerblue", strokeWidth: 2 }),
                  $(go.Placeholder, { margin: 1 })
                ),
                $(go.Shape, "XLine",
                  {
                    isActionable: true,  // so that click works in an Adornmentwidth: 14, height: 14, stroke: "orange", strokeWidth: 4, background: "transparent",
                    alignment: go.Spot.TopRight,
                    click: function(e, shape) {
                      var node = shape.part.adornedPart;
                      spliceNodeOutFromLinkChain(node);
                    },
                    cursor: "pointer"
                  },
                  new go.Binding("visible", "", function(ad) {
                    var node = ad.adornedPart;
                    returnmaySpliceOutNode(node);
                  }).ofObject())
              )
          },
          $(go.Shape,
            {
              fill: "lightgray", strokeWidth: 0,
              portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer"
            },
            new go.Binding("fill", "color")),
          $(go.TextBlock, "some text",
            { margin: 8, editable: true },
            new go.Binding("text").makeTwoWay())
        );

      myDiagram.linkTemplate =
        $(go.Link,
          {
            relinkableFrom: true, relinkableTo: true,
            reshapable: true, resegmentable: true,
            selectionAdornmentTemplate:
              $(go.Adornment,
                $(go.Shape, { isPanelMain: true, stroke: "dodgerblue", strokeWidth: 2 }),
                $(go.Shape, "PlusLine",
                  {
                    isActionable: true,  // so that click works in an Adornmentwidth: 16, height: 16, stroke: "green", strokeWidth: 4, background: "transparent",
                    segmentOffset: new go.Point(8, 0),
                    click: function(e, shape) {
                      var link = shape.part.adornedPart;
                      var p0 = link.getPoint(0);
                      var p1 = link.getPoint(link.pointsCount - 1);
                      var pt = new go.Point((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
                      spliceNewNodeIntoLink(link, pt);
                    },
                    cursor: "pointer"
                  })
              ),
            toShortLength: 1
          },
          new go.Binding("points").makeTwoWay(),
          $(go.Shape, { strokeWidth: 2 }),
          $(go.Shape, { toArrow: "OpenTriangle" })
        );

      functionspliceNewNodeIntoLink(link, pt) {
        link.diagram.commit(function(diag) {
          var tokey = link.toNode.key;
          // add a new nodevar newnodedata = { text: "on link", location: go.Point.stringify(pt) };
          diag.model.addNodeData(newnodedata);
          // and splice it in by changing the existing link to refer to the new node
          diag.model.setToKeyForLinkData(link.data, newnodedata.key);
          // and by adding a new link from the new node to the original "toNode"
          diag.model.addLinkData({ from: newnodedata.key, to: tokey });
          // optional: select the new node
          diag.select(diag.findNodeForData(newnodedata));
        }, "spliced in node on a link");
      }

      functionmaySpliceOutNode(node) {
        return node.findLinksInto().count === 1 &&
          node.findLinksOutOf().count === 1 &&
          node.findLinksInto().first() !== node.findLinksOutOf().first();
      }

      functionspliceNodeOutFromLinkChain(node) {
        if (maySpliceOutNode(node)) {
          node.diagram.commit(function(diag) {
            var inlink = node.findLinksInto().first();
            var outlink = node.findLinksOutOf().first();
            // reconnect the existing incoming link
            inlink.toNode = outlink.toNode;
            // remove the node and the outgoing link
            diag.removeParts([node, outlink], false);
            // optional: select the original link
            diag.select(inlink);
          }, "spliced out node from chain of links");
        }
      }

      load();
    }

    // Show the diagram's model in JSON format that the user may editfunctionsave() {
      document.getElementById("mySavedModel").value = myDiagram.model.toJson();
      myDiagram.isModified = false;
    }
    functionload() {
      myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
    }
  </script></head><bodyonload="init()"><divid="sample"><divid="myDiagramDiv"style="border: solid 1px black; width:100%; height:400px"></div><div><buttonid="SaveButton"onclick="save()">Save</button><buttononclick="load()">Load</button>
      Diagram Model saved in JSON format:
    </div><textareaid="mySavedModel"style="width:100%;height:300px">
{ "class": "GraphLinksModel",
  "nodeDataArray": [ 
{"key":1, "text":"Alpha", "color":"lightblue", "location":"-40 -10"},
{"key":2, "text":"Beta", "color":"orange", "location":"106 -58"},
{"key":3, "text":"Gamma", "color":"lightgreen", "location":"-8 105"},
{"key":4, "text":"Delta", "color":"pink", "location":"110 51"}
 ],
  "linkDataArray": [ 
{"from":1, "to":2, "points":[-15.5,-18.0,84.5,-50.9]},
{"from":1, "to":3, "points":[-35.7,5.1,-12.2,89.85]},
{"from":2, "to":2, "points":[116.7,-42.8,150.3,4.3,59.3,0.3,93.8,-42.8]},
{"from":3, "to":4, "points":[23,90.8,87,61.5]},
{"from":4, "to":1, "points":[87,41.6,-15.5,0.0]}
 ]}
  </textarea><p>
      When a Link is selected it shows a green "+" button that when clicked creates a new Node and splices it into the link.
      It does that by reconnecting the existing link to the new node and creating a new link to go from the new node to the original
      destination of the selected link.
    </p><p>
      When a Node is selected, if it has exactly one Link coming into it and exactly one Link going out of it,
      then it shows an orange "X" button.  When clicked it removes the node and the outgoing link and reconnects the incoming
      link to connect with the original destination of the outgoing link.
    </p><p>
      All links are relinkable and resegmentable.
    </p></div></body></html>

Double-click in the background to create a new node. That's implemented by:

"clickCreatingTool.archetypeNodeData": { text: "a new node" },

Or Control-drag or Control-C/Control-V to copy the selection.

Drag from just inside the edge of a node in order to draw a new link to a node. That's implemented by:

portId:"",fromLinkable:true,toLinkable:true,cursor:"pointer"

on the Shape in the Node.

Click on the text of a selected node in order to start editing it. That's implemented by:

editable:true

on the TextBlock in the Node.

Each link, when selected, allows the user to either drag an end in order to reconnect it, or to add or remove points by dragging the middle handle(s). That's implemented by:

relinkableFrom:true,relinkableTo:true,reshapable:true,resegmentable:true

on the Link.

Each link also has a custom selection Adornment that shows a green "+" button that creates a new node and splices it into the link:

            selectionAdornmentTemplate:
              $(go.Adornment,
                $(go.Shape, { isPanelMain: true, stroke: "dodgerblue", strokeWidth: 2 }),
                $(go.Shape, "PlusLine",
                  {
                    isActionable: true,  // so that click works in an Adornment
                    width: 16, height: 16, stroke: "green", strokeWidth: 4, background: "transparent",
                    segmentOffset: newgo.Point(8, 0),
                    click: function(e, shape) {
                      var link = shape.part.adornedPart;
                      var p0 = link.getPoint(0);
                      var p1 = link.getPoint(link.pointsCount - 1);
                      var pt = newgo.Point((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
                      spliceNewNodeIntoLink(link, pt);
                    },
                    cursor: "pointer"
                  })
              ),

Each Node that has exactly one Link coming into it and one Link going out of it (that is not the same Link) has a selection Adornment including an orange "X" button that allows that node to be spliced out of their path. That is implemented as:

selectionAdornmentTemplate:
              $(go.Adornment, "Spot",
                $(go.Panel, "Auto",
                  $(go.Shape, { fill: null, stroke: "dodgerblue", strokeWidth: 2 }),
                  $(go.Placeholder, { margin: 1 })
                ),
                $(go.Shape, "XLine",
                  {
                    isActionable: true,  // so that click works in an Adornmentwidth: 14, height: 14, stroke: "orange", strokeWidth: 4, background: "transparent",
                    alignment: go.Spot.TopRight,
                    click: function(e, shape) {
                      var node = shape.part.adornedPart;
                      spliceNodeOutFromLinkChain(node);
                    },
                    cursor: "pointer"
                  },
                  new go.Binding("visible", "", function(ad) {
                    var node = ad.adornedPart;
                    returnmaySpliceOutNode(node);
                  }).ofObject())
              )

Selecting a Node and the deleting it with the Delete key will not only delete the node but also all links connecting with it.

There's full support for undo and redo, of course. That's implemented by:

"undoManager.isEnabled":true

Everything should work on touch devices, too, although I haven't tried it with this sample.

And serialization and deserialization is done by calling Model.toJson and Model.fromJson in the save and load functions:

document.getElementById("mySavedModel").value = myDiagram.model.toJson();

myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);

Of course you'll want to send and receive the JSON-formatted text to and from your server rather than showing it on the page in a <textarea>.

Post a Comment for "Seeking Javascript Library For Displaying And Editing Networks Of Nodes And Edges"