UNPKG

gojs

Version:

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

284 lines (253 loc) 13.1 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/> <meta name="description" content="Interactive demonstration of physics layout features by the ForceDirectedLayout class."/> <link rel="stylesheet" href="../assets/css/style.css"/> <!-- Copyright 1998-2023 by Northwoods Software Corporation. --> <title>Force Directed Layout</title> </head> <body> <!-- This top nav is not part of the sample code --> <nav id="navTop" class="w-full z-30 top-0 text-white bg-nwoods-primary"> <div class="w-full container max-w-screen-lg mx-auto flex flex-wrap sm:flex-nowrap items-center justify-between mt-0 py-2"> <div class="md:pl-4"> <a class="text-white hover:text-white no-underline hover:no-underline font-bold text-2xl lg:text-4xl rounded-lg hover:bg-nwoods-secondary " href="../"> <h1 class="my-0 p-1 ">GoJS</h1> </a> </div> <button id="topnavButton" class="rounded-lg sm:hidden focus:outline-none focus:ring" aria-label="Navigation"> <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6"> <path id="topnavOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path> <path id="topnavClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path> </svg> </button> <div id="topnavList" class="hidden sm:block items-center w-auto mt-0 text-white p-0 z-20"> <ul class="list-reset list-none font-semibold flex justify-end flex-wrap sm:flex-nowrap items-center px-0 pb-0"> <li class="p-1 sm:p-0"><a class="topnav-link" href="../learn/">Learn</a></li> <li class="p-1 sm:p-0"><a class="topnav-link" href="../samples/">Samples</a></li> <li class="p-1 sm:p-0"><a class="topnav-link" href="../intro/">Intro</a></li> <li class="p-1 sm:p-0"><a class="topnav-link" href="../api/">API</a></li> <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/products/register.html">Register</a></li> <li class="p-1 sm:p-0"><a class="topnav-link" href="../download.html">Download</a></li> <li class="p-1 sm:p-0"><a class="topnav-link" href="https://forum.nwoods.com/c/gojs/11">Forum</a></li> <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/contact.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a></li> <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/sales/index.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a></li> </ul> </div> </div> <hr class="border-b border-gray-600 opacity-50 my-0 py-0" /> </nav> <div class="md:flex flex-col md:flex-row md:min-h-screen w-full max-w-screen-xl mx-auto"> <div id="navSide" class="flex flex-col w-full md:w-48 text-gray-700 bg-white flex-shrink-0"></div> <!-- * * * * * * * * * * * * * --> <!-- Start of GoJS sample code --> <script src="../release/go.js"></script> <div id="allSampleContent" class="p-4 w-full"> <script id="code"> // define a custom ForceDirectedLayout for this sample class DemoForceDirectedLayout extends go.ForceDirectedLayout { // Override the makeNetwork method to also initialize // ForceDirectedVertex.isFixed from the corresponding Node.isSelected. makeNetwork(coll) { // call base method for standard behavior const net = super.makeNetwork(coll); net.vertexes.each(vertex => { const node = vertex.node; if (node !== null) vertex.isFixed = node.isSelected; }); return net; } } // end DemoForceDirectedLayout class function init() { // Since 2.2 you can also author concise templates with method chaining instead of GraphObject.make // For details, see https://gojs.net/latest/intro/buildingObjects.html const $ = go.GraphObject.make; // for conciseness in defining templates myDiagram = new 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() { let minNodes = document.getElementById("minNodes").value; minNodes = parseInt(minNodes, 10); let maxNodes = document.getElementById("maxNodes").value; maxNodes = parseInt(maxNodes, 10); let minChil = document.getElementById("minChil").value; minChil = parseInt(minChil, 10); let 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) { const nodeArray = []; if (isNaN(min) || min < 0) min = 0; if (isNaN(max) || max < min) max = min; const numNodes = Math.floor(Math.random() * (max - min + 1)) + min; for (let 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++) { const swap = Math.floor(Math.random() * nodeArray.length); const 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; const linkArray = []; // make two Lists of nodes to keep track of where links already exist const nit = myDiagram.nodes; const nodes = new go.List(/*go.Node*/); nodes.addAll(nit); const available = new go.List(/*go.Node*/); available.addAll(nodes); for (let i = 0; i < nodes.length; i++) { const next = nodes.get(i); available.remove(next) const children = Math.floor(Math.random() * (max - min + 1)) + min; for (let j = 1; j <= children; j++) { if (available.length === 0) break; const to = available.get(0); available.remove(to); // get keys from the Node.text strings const nextKey = parseInt(next.text, 10); const 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"); const lay = myDiagram.layout; let maxIter = document.getElementById("maxIter").value; maxIter = parseInt(maxIter, 10); lay.maxIterations = maxIter; let epsilon = document.getElementById("epsilon").value; epsilon = parseFloat(epsilon, 10); lay.epsilonDistance = epsilon; let infinity = document.getElementById("infinity").value; infinity = parseFloat(infinity, 10); lay.infinityDistance = infinity; let arrangement = document.getElementById("arrangement").value; let arrangementSpacing = new go.Size(); arrangement = arrangement.split(" ", 2); arrangementSpacing.width = parseFloat(arrangement[0], 10); arrangementSpacing.height = parseFloat(arrangement[1], 10); lay.arrangementSpacing = arrangementSpacing; let charge = document.getElementById("charge").value; charge = parseFloat(charge, 10); lay.defaultElectricalCharge = charge; let mass = document.getElementById("mass").value; mass = parseFloat(mass, 10); lay.defaultGravitationalMass = mass; let stiffness = document.getElementById("stiffness").value; stiffness = parseFloat(stiffness, 10); lay.defaultSpringStiffness = stiffness; let length = document.getElementById("length").value; length = parseFloat(length, 10); lay.defaultSpringLength = length; myDiagram.commitTransaction("changed Layout"); } window.addEventListener('DOMContentLoaded', init); </script> <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> </div> <!-- * * * * * * * * * * * * * --> <!-- End of GoJS sample code --> </div> </body> <!-- This script is part of the gojs.net website, and is not needed to run the sample --> <script src="../assets/js/goSamples.js"></script> </html>