UNPKG

create-gojs-kit

Version:

A CLI for downloading GoJS samples, extensions, and docs

771 lines (689 loc) 41.2 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." /> <meta itemprop="description" content="Interactive demonstration of physics layout features by the ForceDirectedLayout class." /> <meta property="og:description" content="Interactive demonstration of physics layout features by the ForceDirectedLayout class." /> <meta name="twitter:description" content="Interactive demonstration of physics layout features by the ForceDirectedLayout class." /> <link rel="preconnect" href="https://rsms.me/"> <link rel="stylesheet" href="../assets/css/style.css"> <!-- Copyright 1998-2025 by Northwoods Software Corporation. --> <meta itemprop="name" content="Force Directed Layout Demonstration of ForceDirectedLayout Options" /> <meta property="og:title" content="Force Directed Layout Demonstration of ForceDirectedLayout Options" /> <meta name="twitter:title" content="Force Directed Layout Demonstration of ForceDirectedLayout Options" /> <meta property="og:image" content="https://gojs.net/latest/assets/images/screenshots/fdlayout.png" /> <meta itemprop="image" content="https://gojs.net/latest/assets/images/screenshots/fdlayout.png" /> <meta name="twitter:image" content="https://gojs.net/latest/assets/images/screenshots/fdlayout.png" /> <meta property="og:url" content="https://gojs.net/latest/samples/fdLayout.html" /> <meta property="twitter:url" content="https://gojs.net/latest/samples/fdLayout.html" /> <meta name="twitter:card" content="summary_large_image" /> <meta property="og:type" content="website" /> <meta property="twitter:domain" content="gojs.net" /> <title> Force Directed Layout Demonstration of ForceDirectedLayout Options | GoJS Diagramming Library </title> </head> <body> <!-- This top nav is not part of the sample code --> <nav id="navTop" class=" w-full h-[var(--topnav-h)] z-30 bg-white border-b border-b-gray-200"> <div class="max-w-screen-xl mx-auto flex flex-wrap items-start justify-between px-4"> <a class="text-white bg-nwoods-primary font-bold !leading-[calc(var(--topnav-h)_-_1px)] my-0 px-2 text-4xl lg:text-5xl logo" href="../"> GoJS </a> <div class="relative"> <button id="topnavButton" class="h-[calc(var(--topnav-h)_-_1px)] px-2 m-0 text-gray-900 bg-inherit shadow-none md:hidden hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation"> <svg class="h-7 w-7 block" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> <div id="topnavList" class="hidden md:block"> <div class="absolute right-0 z-30 flex flex-col items-end rounded border border-gray-200 p-4 pl-12 shadow bg-white text-gray-900 font-semibold md:flex-row md:space-x-4 md:items-start md:border-0 md:p-0 md:shadow-none md:bg-inherit"> <a href="../learn/">Learn</a> <a href="../samples/">Samples</a> <a href="../intro/">Intro</a> <a href="../api/">API</a> <a href="../download.html">Download</a> <a href="https://forum.nwoods.com/c/gojs/11" target="_blank" rel="noopener">Forum</a> <a id="tc" href="https://nwoods.com/contact.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a> <a id="tb" href="https://nwoods.com/sales/index.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a> </div> </div> </div> </div> </nav> <script> window.addEventListener("DOMContentLoaded", function () { // topnav var topButton = document.getElementById("topnavButton"); var topnavList = document.getElementById("topnavList"); if (topButton && topnavList) { topButton.addEventListener("click", function (e) { topnavList .classList .toggle("hidden"); e.stopPropagation(); }); document.addEventListener("click", function (e) { // if the clicked element isn't the list, close the list if (!topnavList.classList.contains("hidden") && !e.target.closest("#topnavList")) { topButton.click(); } }); // set active <a> element var url = window .location .href .toLowerCase(); var aTags = topnavList.getElementsByTagName('a'); for (var i = 0; i < aTags.length; i++) { var lowerhref = aTags[i] .href .toLowerCase(); if (lowerhref.endsWith('.html')) lowerhref = lowerhref.slice(0, -5); if (url.startsWith(lowerhref)) { aTags[i] .classList .add('active'); break; } } } }); </script> <div class="flex flex-col prose"> <div class="w-full max-w-screen-xl mx-auto"> <!-- * * * * * * * * * * * * * --> <!-- Start of GoJS sample code --> <script src="https://cdn.jsdelivr.net/npm/gojs@3.1.0"></script> <link rel="stylesheet" href="../assets/css/prism.css"/> <script src="../assets/js/prism.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 { constructor(init) { super(); if (init) Object.assign(this, init); } // 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() { myDiagram = new go.Diagram('myDiagramDiv', { initialAutoScale: go.AutoScale.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 = new go.Node('Spot', { // make sure the Node.location is different from the Node.position locationSpot: go.Spot.Center }) .bind('text', 'text') // for sorting .add( new go.Shape('Ellipse', { fill: 'lightgray', stroke: null, desiredSize: new go.Size(30, 30) }) .bind('fill', 'layer', c => randomLayerColors[c % 25]), new go.TextBlock() .bind('text', 'text') ) // define the Link template myDiagram.linkTemplate = new go.Link({ selectable: false }) .add( new go.Shape({ strokeWidth: 3, stroke: '#333' }) ); // generate a tree using the default values buildSettingsMenu(); 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(); shuffleColors(); // Shuffles palette to look like new random colors layerNodes(); // Sets layer value for all the new nodes 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; } function buildSettingsMenu() { // Builds the setting menu through calls to respective builder buildButtonRow('maxIterations', 10, 300, 'Max Iterations','table1', 0); buildButtonRow('epsilonDistance', 0.05, 1, 'Epsilon','table1', 0.05, null, 2); buildButtonRow('infinityDistance', 100, 1000, 'Infinity','table1', 0); buildButtonRow('arrangementWidth', 10, 100, 'Spacing Width','table1', 0); buildButtonRow('arrangementHeight', 5, 100, 'Spacing Height','table1', 0); buildButtonRow('defaultElectricalCharge', 10, 150, 'Electrical Charge','table2', 0); buildButtonRow('defaultGravitationalMass', 10, 0, 'Gravitational Mass','table2', 0); buildButtonRow('defaultSpringStiffness', 0.01, 0.05, 'Spring Stiffness','table2', 0, null, 2); buildButtonRow('defaultSpringLength', 10, 50, 'Spring Length','table2', 0); buildButtonRow('prelayoutQuality', 0.1, '', 'Prelayout Quality','table2', 0, null, 1); buildButtonRow('prelayoutSpread', 1, 10, 'Prelayout Spread','table2', 0); document.querySelectorAll('.info').forEach(element => { element.addEventListener('mouseover', () => { iframe(element); }) element.addEventListener('mouseout', () => { document.getElementById('frame').style.display = 'none'; document.getElementById('description').innerHTML = ''; }) }) } function layerNodes(nodes = myDiagram.findTreeRoots(), layer = 0) { if (nodes.count == 0) return; nodes.each(node => { //recursively goes through all children of the graph sending a layer down to be set on each myDiagram.model.setDataProperty(node.data, 'layer', layer); layerNodes(node.findTreeChildrenNodes(), layer + 1); }); } function shuffleColors() { var tempList = []; while (randomLayerColors.length > 0) { // Goes through the color palette randomly pulling out colors to reorder them let item = randomLayerColors.splice(Math.round(Math.random() * randomLayerColors.length - 1), 1); tempList.push(item[0]); } randomLayerColors = tempList; } var randomLayerColors = [ // Color Palette used to color the layers of the graph "hsl(54, 76%, 84%)", "hsl(208, 55%, 76%)", "hsl(44, 82%, 83%)", "hsl(30, 89%, 83%)", "hsl(26, 90%, 80%)", "hsl(10, 85%, 81%)", "hsl(354, 83%, 84%)", "hsl(340, 80%, 84%)", "hsl(319, 43%, 79%)", "hsl(287, 30%, 75%)", "hsl(255, 32%, 74%)", "hsl(220, 77%, 86%)", "hsl(187, 53%, 75%)", "hsl(175, 38%, 73%)", "hsl(136, 37%, 76%)", "hsl(98, 41%, 79%)", "hsl(72, 51%, 81%)", "hsl(84, 67%, 81%)", "hsl(88, 89%, 90%)", "hsl(44, 95%, 92%)", "hsl(24, 96%, 90%)", "hsl(2, 100%, 88%)", "hsl(351, 94%, 93%)", "hsl(192, 59%, 91%)", "hsl(191, 65%, 88%)" ]; // Update the layout from the controls. // Changing the properties will invalidate the layout. function layout() { myDiagram.startTransaction('changed Layout'); const lay = myDiagram.layout; lay.arrangesToOrigin = true; d = document; var code = 'new DemoForceDirectedLayout({' let maxIter = d.getElementById('maxIterations').value; maxIter = parseInt(maxIter, 10); lay.maxIterations = maxIter; if (maxIter != d.getElementById('maxIterations').name) code += `\n maxIterations: ${maxIter},`; let epsilon = d.getElementById('epsilonDistance').value; epsilon = parseFloat(epsilon, 10); lay.epsilonDistance = epsilon; if (epsilon != d.getElementById('epsilonDistance').name) code += `\n epsilonDistance: ${epsilon},`; let infinity = d.getElementById('infinityDistance').value; infinity = parseFloat(infinity, 10); lay.infinityDistance = infinity; if (infinity != d.getElementById('infinityDistance').name) code += `\n infinityDistance: ${infinity},`; let arrangementWidth = d.getElementById('arrangementWidth').value; let arrangementHeight = d.getElementById('arrangementHeight').value; lay.arrangementSpacing = new go.Size(parseFloat(arrangementWidth, 10), parseFloat(arrangementHeight, 10)); if (arrangementWidth != d.getElementById('arrangementWidth').name || arrangementHeight != d.getElementById('arrangementHeight').name) code += `\n arrangementSpacing: new go.Size(${arrangementWidth}, ${arrangementHeight}),`; let charge = d.getElementById('defaultElectricalCharge').value; charge = parseFloat(charge, 10); lay.defaultElectricalCharge = charge; if (charge != d.getElementById('defaultElectricalCharge').name) code += `\n defaultElectricalCharge: ${charge},`; let mass = d.getElementById('defaultGravitationalMass').value; mass = parseFloat(mass, 10); lay.defaultGravitationalMass = mass; if (mass != d.getElementById('defaultGravitationalMass').name) code += `\n defaultGravitationalMass: ${mass},`; let stiffness = d.getElementById('defaultSpringStiffness').value; stiffness = parseFloat(stiffness, 10); lay.defaultSpringStiffness = stiffness; if (stiffness != d.getElementById('defaultSpringStiffness').name) code += `\n defaultSpringStiffness: ${stiffness},`; let length = d.getElementById('defaultSpringLength').value; length = parseFloat(length, 10); lay.defaultSpringLength = length; if (length != d.getElementById('defaultSpringLength').name) code += `\n defaultSpringLength: ${length},`; let preQuality = d.getElementById('prelayoutQuality').value; if (preQuality === '') preQuality = NaN; else preQuality = parseFloat(preQuality, 10); lay.prelayoutQuality = preQuality; if (!isNaN(preQuality)) code += `\n prelayoutQuality: ${preQuality},`; let prelayoutSpread = d.getElementById('prelayoutSpread').value; prelayoutSpread = parseFloat(prelayoutSpread, 10); lay.prelayoutSpread = prelayoutSpread; if (prelayoutSpread != d.getElementById('prelayoutSpread').name) code += `\n prelayoutSpread: ${prelayoutSpread},`; if (code === 'new DemoForceDirectedLayout({') code = 'new DemoForceDirectedLayout()'; // If no changes made to layout else code = code.slice(0, -1) + '\n})'; // Removes last comma and adds closing bracket d.getElementById('layoutBuilder').textContent = code; if (window.Prism) window.Prism.highlightAll(); myDiagram.commitTransaction('changed Layout'); myDiagram.zoomToFit(); } function changeNodes(max, value) { // Allows for Node property to be changed and to make sure max and min never cross each other e = document; if (max) { e.getElementById('maxNodes').value = Math.max(e.getElementById('maxNodes').value - -value, 0); } else { e.getElementById('minNodes').value = Math.max(e.getElementById('minNodes').value - -value, 0); } if (parseInt(e.getElementById('minNodes').value) > parseInt(e.getElementById('maxNodes').value)) { (max)? e.getElementById('minNodes').value = e.getElementById('maxNodes').value : e.getElementById('maxNodes').value = e.getElementById('minNodes').value; } } function changeChildren(max, value) { // Makes sure children number properties max and min don't cross e = document; if (max) { e.getElementById('maxChil').value = Math.max(e.getElementById('maxChil').value - -value, 0); } else { e.getElementById('minChil').value = Math.max(e.getElementById('minChil').value - -value, 0); } if (parseInt(e.getElementById('minChil').value) > parseInt(e.getElementById('maxChil').value)) { (max)? e.getElementById('minChil').value = e.getElementById('maxChil').value : e.getElementById('maxChil').value = e.getElementById('minChil').value; } } function buildButtonRow(id, change, start, title, table, min = null, max = null, decimal = 0) { var template = document.getElementById('buttonRow'); var clone = template.content.cloneNode(true); clone.querySelector('.button3').name = change; clone.querySelector('.button2').name = -change; clone.querySelector('.inputBox').name = start; clone.querySelector('.inputBox').id = id; clone.querySelector('.inputBox').value = start; clone.querySelector('.maximum').name = max; clone.querySelector('.minimum').name = min; clone.querySelector('.decimal').name = decimal; clone.querySelector('.title').textContent = title; clone.querySelector('.row').id = (id + 'Row') document.getElementById(table).appendChild(clone); } function resetButton(element) { // Sets property back to original and changes related divs accordingly e = element.parentNode.querySelector('.input'); e.value = e.name; element.style = "background-color: lightgray;"; layout(); } function changeInput(element, change = true) { // Changes value and checks for default equality for the reset button e = element.parentNode.querySelector('.inputBox'); change? number = element.name: number = 0; const max = element.parentNode.querySelector('.maximum').name; const min = element.parentNode.querySelector('.minimum').name; const decimal = element.parentNode.querySelector('.decimal').name; var value = (e.value - -number); // Changes by recorded interval if (max !== null) value = Math.min(value, max); // Checks for max and min values if (min !== null) value = Math.max(value, min); e.value = (value).toFixed(((value - value.toFixed(0)) == 0)? 0 : decimal); // Rounds to assigned place element.parentNode.querySelector('.button1').style = (e.value != e.name)? "background-color: lightcoral;" : "background-color: lightgray;"; layout(); } function copyCode() { navigator.clipboard.writeText(document.getElementById('layoutBuilder').textContent); document.getElementById('default').style.display = 'none' document.getElementById('clicked').style.display = 'inline-flex' document.getElementById('copyButton').style.pointerEvents = 'none' setTimeout(() => { document.getElementById('default').style.display = 'inline-flex' document.getElementById('clicked').style.display = 'none' document.getElementById('copyButton').style.pointerEvents = 'auto' }, 1500); } function iframe(element) { var iframe = document.getElementById('apiFrame'); var frame = document.getElementById('frame'); frame.style.display = 'block'; var id = element.parentElement.querySelector('.input').id; if (id === 'arrangementWidth' || id === 'arrangementHeight') id = 'arrangementSpacing'; document.getElementById('description').innerHTML = (iframe.contentWindow.document.getElementById(id).parentElement.children[2].firstChild.firstChild.innerHTML); document.getElementById('name').textContent = id; frame.style.left = (element.getBoundingClientRect().left) + 'px'; frame.style.top = (element.getBoundingClientRect().top - 10 + window.pageYOffset) + 'px'; } window.addEventListener('DOMContentLoaded', init); </script> <style> .table { display: table; width: 100%; } .row { display: table-row; } .cell { display: table-cell; vertical-align: middle; } .celldif { display: table-cell; } .sampleWrapper { display: flex; flex-direction: column; @media (min-width: 800px) { flex-direction: row; } & > div:first-child { margin-bottom: 0.5rem; @media (min-width: 800px) { margin-right: 0; margin-bottom: 0; } } } .info { display: inline-block; cursor: pointer; } .copyButton:hover { background-color: #e9e9e9 !important; color: #000000 !important; } </style> <template id="buttonRow"> <div class="row tooltip" > <svg onclick="" class="cell info shrink-0 inline w-4 h-4 align-middle" style="margin-right: 2px; margin-bottom: 6px;" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/> </svg> <div class="title cell whitespace-nowrap p-1 select-none" style="font-size: large;"></div> <div class="cell"> <div class="relative flex items-center"> <div class="maximum" ></div> <div class="minimum" ></div> <div class="decimal" ></div> <button type="button" onclick="resetButton(this)" class="button1 rounded-s-lg rounded-e-none h-7 m-0 pr-[2px] pl-[2px]" style="background-color: lightgray;"> <svg fill="#000000" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12px" height="12px" viewBox="0 0 528.919 528.919" xml:space="preserve"> <path id="reset" stroke-width="2" d= "M70.846,324.059c3.21,3.926,8.409,3.926,11.619,0l69.162-84.621c3.21-3.926,1.698-7.108-3.372-7.108h-36.723 c-5.07,0-8.516-4.061-7.427-9.012c18.883-85.995,95.625-150.564,187.207-150.564c105.708,0,191.706,85.999,191.706,191.706 c0,105.709-85.998,191.707-191.706,191.707c-12.674,0-22.95,10.275-22.95,22.949s10.276,22.949,22.95,22.949 c131.018,0,237.606-106.588,237.606-237.605c0-131.017-106.589-237.605-237.606-237.605 c-116.961,0-214.395,84.967-233.961,196.409c-0.878,4.994-5.52,9.067-10.59,9.067H5.057c-5.071,0-6.579,3.182-3.373,7.108 L70.846,324.059z"/> </svg> </button> <button type="button" onclick="changeInput(this)" class="button2 bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-none h-7 m-0 pr-[11px] pl-[6px]"> <svg class="w-3 h-3 text-gray-900" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 2"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h16"/> </svg> </button> <input type="text" onchange="changeInput(this, false)" class="input inputBox bg-gray-50 border-x-1 border-gray-300 h-7 text-center outline-none text-gray-900 text-sm w-10 m-[-4.5px] z-10" required /> <button type="button" onclick="changeInput(this)" class="button3 bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-e-lg h-7 m-0 pr-[7px] pl-[12px]"> <svg class="w-3 h-3 text-gray-900" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 1v16M1 9h16"/> </svg> </button> </div> </div> </div> </template> <div id="sample"> <iframe id="apiFrame" src="https://gojs.net/latest/api/symbols/ForceDirectedLayout.html" style="display: none;"></iframe> <div id="frame" style="z-index: 100; pointer-events: none; position: absolute; display: none; "> <div id="container" style="border: 1px solid #ddd; border-radius: 5px; padding: 15px; max-width: 600px;margin: 20px auto; font-family: Arial, sans-serif; background-color: #fff;"> <div id="name" class="name" style="font-weight: bold; font-size: 16px; display: flex; align-items: center;"></div> <div id="description" style=" margin-top: 10px; font-size: 14px; color: #000;"></div> </div> </div> <div class="sampleWrapper"> <div id="myDiagramDiv" style="flex-grow: 1; height: 550px; border: solid 1px black; margin-right: 10px;"></div> <div style="flex: 1;"> <section class="grid grid-cols-1 gap-y-3 divide-y"> <details open class="group py-1 text-lg"> <summary class="flex cursor-pointer flex-row items-center whitespace-nowrap justify-between py-1 font-semibold marker:[font-size:0px] select-none">Nodes and Links <svg class="h-6 w-6 rotate-0 transform text-gray-400 group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"> <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path> </svg> </summary> <span class="sampleWrapper"> <div class="table " style="padding-right: 15px;"> <div class="row" > <!--Min Nodes--> <div class="cell whitespace-nowrap p-1 select-none" style="font-size: large; padding-right: 15px;">Minimum Nodes </div> <div class="cell max-w-xs"> <!--Min Nodes--> <label for="numNodes" class="items-center cursor-pointer"> <div class="relative flex items-center max-w-[6.5rem] "> <button type="button" id="decrement-button" onclick="changeNodes(false,-5)" class="bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-s-lg h-7 focus:ring-gray-100 focus:ring-2 focus:outline-none m-0 pr-[12px] pl-[7px]"> <svg class="w-3 h-3 text-gray-900" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 2"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h16"/> </svg> </button> <input type="text" id="minNodes" onclick="changeNodes(false, 0)" onchange="changeNodes(false, 0)" aria-describedby="helper-text-explanation" class="bg-gray-50 border-x-1 border-gray-300 h-7 text-center outline-none text-gray-900 text-sm w-11 m-[-4.5px] z-10" value="20" required /> <button type="button" id="increment-button" onclick="changeNodes(false, 5)" class="bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-e-lg h-7 focus:ring-gray-100 focus:ring-2 focus:outline-none m-0 pr-[7px] pl-[12px]"> <svg class="w-3 h-3 text-gray-900" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 1v16M1 9h16"/> </svg> </button> </div> </label> </div> </div> <div class="row"> <!--Max Nodes--> <div id= "nodeWidthText" class="cell whitespace-nowrap p-1 select-none" style="font-size: large">Maximum Nodes</div> <div class="cell max-w-xs" id="nodeWidthForm"> <!--Max Nodes--> <label for="minLinks" class="items-center cursor-pointer"> <div class="relative flex items-center max-w-[6.5rem]"> <button type="button" id="decrement-button" onclick="changeNodes(true, -5)" class="bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-s-lg h-7 focus:ring-gray-100 focus:ring-2 focus:outline-none m-0 pr-[12px] pl-[7px]"> <svg class="w-3 h-3 text-gray-900" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 2"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h16"/> </svg> </button> <input type="text" id="maxNodes" onclick="changeNodes(true, 0)" onchange="changeNodes(true, 0)" aria-describedby="helper-text-explanation" onchange="" class="bg-gray-50 border-x-1 border-gray-300 h-7 text-center text-gray-900 text-sm outline-none w-11 m-[-4.5px] z-10" value="100" required /> <button type="button" id="increment-button" onclick="changeNodes(true, 5)" class="bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-e-lg h-7 focus:ring-gray-100 focus:ring-2 focus:outline-none m-0 pr-[7px] pl-[12px]"> <svg class="w-3 h-3 text-gray-900" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 1v16M1 9h16"/> </svg> </button> </div> </label> </div> </div> <div class="row"> <!--min children--> <div id= "nodeHeightText" class="cell whitespace-nowrap p-1 select-none" style="font-size: large">Minumum Children</div> <div class="cell max-w-xs" id="nodeHeightForm"> <!--min children--> <label for="minLinks" class="items-center cursor-pointer"> <div class="relative flex items-center max-w-[6.5rem]"> <button type="button" id="decrement-button" onclick="changeChildren(false, -1)" class="bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-s-lg h-7 focus:ring-gray-100 focus:ring-2 focus:outline-none m-0 pr-[12px] pl-[7px]"> <svg class="w-3 h-3 text-gray-900" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 2"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h16"/> </svg> </button> <input type="text" id="minChil" onclick="changeChildren(false, 0)" onchange="changeChildren(false, 0)" aria-describedby="helper-text-explanation" onchange="" class="bg-gray-50 border-x-1 outline-none border-gray-300 h-7 text-center text-gray-900 text-sm block w-11 m-[-4.5px] z-10" value="1" required /> <button type="button" id="increment-button" onclick="changeChildren(false, 1)" class="bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-e-lg h-7 focus:ring-gray-100 focus:ring-2 focus:outline-none m-0 pr-[7px] pl-[12px]"> <svg class="w-3 h-3 text-gray-900" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 1v16M1 9h16"/> </svg> </button> </div> </label> </div> </div> <div class="row"> <!--max children--> <div id="minLinkText" class="cell whitespace-nowrap p-1 select-none" style="font-size: large; ">Maximum Children</div> <div class="cell max-w-xs" id="minLinksForm"> <!--max children--> <label for="minLinks" class="items-center cursor-pointer"> <div class="relative flex items-center max-w-[6.5rem]"> <button type="button" id="decrement-button" onclick="changeChildren(true, -1)" class="bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-s-lg h-7 focus:ring-gray-100 focus:ring-2 focus:outline-none m-0 pr-[12px] pl-[7px]"> <svg class="w-3 h-3 text-gray-900" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 2"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h16"/> </svg> </button> <input type="text" id="maxChil" aria-describedby="helper-text-explanation" onchange="changeChildren(true, 0)" onclick="changeChildren(true, 0)" class="bg-gray-50 border-x-1 outline-none border-gray-300 h-7 text-center text-gray-900 text-sm block w-11 m-[-4.5px] z-10" value="10" required /> <button type="button" id="increment-button" onclick="changeChildren(true, 1)" class="bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-e-lg h-7 focus:ring-gray-100 focus:ring-2 focus:outline-none m-0 pr-[7px] pl-[12px]"> <svg class="w-3 h-3 text-gray-900" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 1v16M1 9h16"/> </svg> </button> </div> </label> </div> </div> </div> <span style="display: inline-block; vertical-align: top; padding: 5px"> <button style="white-space: nowrap;" type="button" onclick="rebuildGraph()">Generate Tree</button> </span> </span> </details> <details open class="group py-1 text-lg"> <summary class="flex cursor-pointer flex-row items-center justify-between py-1 font-semibold marker:[font-size:0px] select-none">Layout Properties <svg class="h-6 w-6 rotate-0 transform text-gray-400 group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path></svg> </summary> <span class="sampleWrapper"> <div class="table" id="table1"></div> <div class="table" id="table2"></div> </span> </details> <details open class="group py-1 text-lg" > <summary class="flex cursor-pointer flex-row items-center justify-between py-1 font-semibold marker:[font-size:0px] select-none">Force Directed Layout Builder <svg class="h-6 w-6 rotate-0 transform text-gray-400 group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path></svg> </summary> <div class="p-1" style="position: relative;"><!--Layout Output--> <pre style="overflow: auto;"> <code id="layoutBuilder" class="lang-js" style="font-size: large;"></code> </pre> <button onclick="copyCode()" style="position: absolute; top: 14px; right: 8px; width: 35px; height: 35px;" id="copyButton" class="copyButton text-gray-900 m-1 rounded-lg py-2 px-2.5 inline-flex items-center justify-center bg-white border-gray-200 border"> <svg id="default" class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 20"> <path d="M16 1h-3.278A1.992 1.992 0 0 0 11 0H7a1.993 1.993 0 0 0-1.722 1H2a2 2 0 0 0-2 2v15a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2Zm-3 14H5a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2Zm0-4H5a1 1 0 0 1 0-2h8a1 1 0 1 1 0 2Zm0-5H5a1 1 0 0 1 0-2h2V2h4v2h2a1 1 0 1 1 0 2Z"/> </svg> <svg id="clicked" class="w-4 h-4" style="display: none;" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 12"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5.917 5.724 10.5 15 1.5"/> </svg> </button> </div> </details> </section> </div> </div> <p style="flex-grow: 1;">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> <div id="allTagDescriptions" class="p-4 w-full max-w-screen-xl mx-auto"> <hr/> <h3 class="text-xl">GoJS Features in this sample</h3> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Collections</h4> <p> <b>GoJS</b> provides its own collection classes: <a href="../api/symbols/List.html" target="api">List</a>, <a href="../api/symbols/Set.html" target="api">Set</a>, and <a href="../api/symbols/Map.html" target="api">Map</a>. You can iterate over a collection by using an <a href="../api/symbols/Iterator.html" target="api">Iterator</a>. More information can be found in the <a href="../intro/collections.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#collections">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Force Directed Layout</h4> <p> This predefined layout treats the graph as if it were a system of physical bodies with forces acting on and between them. The layout iteratively moves nodes and links to minimize the total sum of forces on each node. The resulting layout will normally not contain overlapping Nodes, excluding cases where the graph is densely interconnected. More information can be found in the <a href="../intro/layouts.html#ForceDirectedLayout">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#force-directed">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>HTML Interaction</h4> <p> GoJS Diagrams can be used alongside other HTML elements in a webapp. For custom Text Editors, Context Menus, and ToolTips, which are invoked and hidden via GoJS tool operations, it is best to use the <a href="../api/symbols/HTMLInfo.html" target="api">HTMLInfo</a> class. </p> <p> More information can be found in the <a href="../intro/HTMLinteraction.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#html">Related samples</a> </p> <hr> </div> </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>