UNPKG

gojs

Version:

Interactive diagrams, charts, and graphs, such as trees, flowcharts, orgcharts, UML, BPMN, or business diagrams

237 lines (209 loc) 9.8 kB
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Force Directed Layout</title> <meta name="description" content="Interactive demonstration of physics layout features by the ForceDirectedLayout class." /> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Copyright 1998-2020 by Northwoods Software Corporation. --> <script src="../release/go.js"></script> <script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework --> <script id="code"> // define a custom ForceDirectedLayout for this sample function DemoForceDirectedLayout() { go.ForceDirectedLayout.call(this); } go.Diagram.inherit(DemoForceDirectedLayout, go.ForceDirectedLayout); // Override the makeNetwork method to also initialize // ForceDirectedVertex.isFixed from the corresponding Node.isSelected. DemoForceDirectedLayout.prototype.makeNetwork = function(coll) { // call base method for standard behavior var net = go.ForceDirectedLayout.prototype.makeNetwork.call(this, coll); net.vertexes.each(function(vertex) { var node = vertex.node; if (node !== null) vertex.isFixed = node.isSelected; }); return net; }; // end DemoForceDirectedLayout class function init() { if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this var $ = go.GraphObject.make; // for conciseness in defining templates myDiagram = $(go.Diagram, "myDiagramDiv", // must be the ID or reference to div { initialAutoScale: go.Diagram.Uniform, // zoom to make everything fit in the viewport layout: new DemoForceDirectedLayout() // use custom layout // other Layout properties are set by the layout function, defined below }); // define the Node template myDiagram.nodeTemplate = $(go.Node, "Spot", // make sure the Node.location is different from the Node.position { locationSpot: go.Spot.Center }, new go.Binding("text", "text"), // for sorting $(go.Shape, "Ellipse", { fill: "lightgray", stroke: null, desiredSize: new go.Size(30, 30) }, new go.Binding("fill", "fill")), $(go.TextBlock, new go.Binding("text", "text")) ); // define the Link template myDiagram.linkTemplate = $(go.Link, { selectable: false }, $(go.Shape, { strokeWidth: 3, stroke: "#333" })); // generate a tree using the default values rebuildGraph(); } function rebuildGraph() { var minNodes = document.getElementById("minNodes").value; minNodes = parseInt(minNodes, 10); var maxNodes = document.getElementById("maxNodes").value; maxNodes = parseInt(maxNodes, 10); var minChil = document.getElementById("minChil").value; minChil = parseInt(minChil, 10); var maxChil = document.getElementById("maxChil").value; maxChil = parseInt(maxChil, 10); generateTree(minNodes, maxNodes, minChil, maxChil); } function generateTree(minNodes, maxNodes, minChil, maxChil) { myDiagram.startTransaction("generateTree"); // replace the diagram's model's nodeDataArray generateNodes(minNodes, maxNodes); // replace the diagram's model's linkDataArray generateLinks(minChil, maxChil); // perform a diagram layout with the latest parameters layout(); myDiagram.commitTransaction("generateTree"); } // Creates a random number of randomly colored nodes. function generateNodes(min, max) { var nodeArray = []; if (isNaN(min) || min < 0) min = 0; if (isNaN(max) || max < min) max = min; var numNodes = Math.floor(Math.random() * (max - min + 1)) + min; for (var i = 0; i < numNodes; i++) { nodeArray.push({ key: i, text: i.toString(), fill: go.Brush.randomColor() }); } // randomize the node data for (i = 0; i < nodeArray.length; i++) { var swap = Math.floor(Math.random() * nodeArray.length); var temp = nodeArray[swap]; nodeArray[swap] = nodeArray[i]; nodeArray[i] = temp; } // set the nodeDataArray to this array of objects myDiagram.model.nodeDataArray = nodeArray; } // Takes the random collection of nodes and creates a random tree with them. // Respects the minimum and maximum number of links from each node. // (The minimum can be disregarded if we run out of nodes to link to) function generateLinks(min, max) { if (myDiagram.nodes.count < 2) return; if (isNaN(min) || min < 1) min = 1; if (isNaN(max) || max < min) max = min; var linkArray = []; // make two Lists of nodes to keep track of where links already exist var nit = myDiagram.nodes; var nodes = new go.List(/*go.Node*/); nodes.addAll(nit); var available = new go.List(/*go.Node*/); available.addAll(nodes); for (var i = 0; i < nodes.length; i++) { var next = nodes.get(i); available.remove(next) var children = Math.floor(Math.random() * (max - min + 1)) + min; for (var j = 1; j <= children; j++) { if (available.length === 0) break; var to = available.get(0); available.remove(to); // get keys from the Node.text strings var nextKey = parseInt(next.text, 10); var toKey = parseInt(to.text, 10); linkArray.push({ from: nextKey, to: toKey }); } } myDiagram.model.linkDataArray = linkArray; } // Update the layout from the controls. // Changing the properties will invalidate the layout. function layout() { myDiagram.startTransaction("changed Layout"); var lay = myDiagram.layout; var maxIter = document.getElementById("maxIter").value; maxIter = parseInt(maxIter, 10); lay.maxIterations = maxIter; var epsilon = document.getElementById("epsilon").value; epsilon = parseFloat(epsilon, 10); lay.epsilonDistance = epsilon; var infinity = document.getElementById("infinity").value; infinity = parseFloat(infinity, 10); lay.infinityDistance = infinity; var arrangement = document.getElementById("arrangement").value; var arrangementSpacing = new go.Size(); arrangement = arrangement.split(" ", 2); arrangementSpacing.width = parseFloat(arrangement[0], 10); arrangementSpacing.height = parseFloat(arrangement[1], 10); lay.arrangementSpacing = arrangementSpacing; var charge = document.getElementById("charge").value; charge = parseFloat(charge, 10); lay.defaultElectricalCharge = charge; var mass = document.getElementById("mass").value; mass = parseFloat(mass, 10); lay.defaultGravitationalMass = mass; var stiffness = document.getElementById("stiffness").value; stiffness = parseFloat(stiffness, 10); lay.defaultSpringStiffness = stiffness; var length = document.getElementById("length").value; length = parseFloat(length, 10); lay.defaultSpringLength = length; myDiagram.commitTransaction("changed Layout"); } </script> </head> <body onload="init()"> <div id="sample"> <div style="margin-bottom: 5px; padding: 5px; background-color: aliceblue"> <span style="display: inline-block; vertical-align: top; padding: 5px"> <b>New Tree</b><br /> MinNodes: <input type="text" size="3" id="minNodes" value="20" /><br /> MaxNodes: <input type="text" size="3" id="maxNodes" value="100" /><br /> MinChildren: <input type="text" size="3" id="minChil" value="1" /><br /> MaxChildren: <input type="text" size="3" id="maxChil" value="10" /><br /> <button type="button" onclick="rebuildGraph()">Generate Tree</button> </span> <span style="display: inline-block; vertical-align: top; padding: 5px"> <b>ForceDirectedLayout Properties</b><br /> Max Iterations: <input type="text" size="5" id="maxIter" value="100" onchange="layout()" /><br /> Epsilon: <input type="text" size="5" id="epsilon" value="1" onchange="layout()" /><br /> Infinity: <input type="text" size="5" id="infinity" value="1000" onchange="layout()" /><br /> ArrangementSpacing: <input type="text" size="8" id="arrangement" value="100 100" onchange="layout()" /><br /> </span> <span style="display: inline-block; vertical-align: top; padding: 5px"> <b>Vertex Properties</b><br /> Electrical Charge: <input type="text" size="5" id="charge" value="150" onchange="layout()" /><br /> Gravitational Mass: <input type="text" size="5" id="mass" value="0" onchange="layout()" /><br /> </span> <span style="display: inline-block; vertical-align: top; padding: 5px"> <b>Edge Properties</b><br /> Spring Stiffness: <input type="text" size="5" id="stiffness" value="0.05" onchange="layout()" /><br /> Spring Length: <input type="text" size="5" id="length" value="50" onchange="layout()" /><br /> </span> </div> <div id="myDiagramDiv" style="background: white; border: solid 1px black; width: 100%; height: 500px"></div> <p> For information on <b>ForceDirectedLayout</b> and its properties, see the <a>ForceDirectedLayout</a> documentation page. </p> </div> </body> </html>