UNPKG

gojs

Version:

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

411 lines (360 loc) 21.4 kB
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Tree Layout</title> <meta name="description" content="Interactive demonstration of tree layout features by the TreeLayout 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 --> <style type="text/css"> input[type="number"] { width: 45px; } </style> <script id="code"> 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.UniformToFill, layout: $(go.TreeLayout, { comparer: go.LayoutVertex.smartComparer }) // have the comparer sort by numbers as well as letters // other properties are set by the layout function, defined below }); // define the Node template myDiagram.nodeTemplate = $(go.Node, "Spot", { locationSpot: go.Spot.Center }, new go.Binding("text", "text"), // for sorting $(go.Shape, "Ellipse", { fill: "lightgray", // the initial value, but data binding may provide different value stroke: null, desiredSize: new go.Size(30, 30) }, new go.Binding("desiredSize", "size"), new go.Binding("fill", "fill")), $(go.TextBlock, new go.Binding("text", "text")) ); // define the Link template myDiagram.linkTemplate = $(go.Link, { routing: go.Link.Orthogonal, selectable: false }, $(go.Shape, { strokeWidth: 3, stroke: "#333" })); // generate a tree with 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); var hasRandomSizes = document.getElementById("randomSizes").checked; // create and assign a new model var nodeDataArray = generateNodeData(minNodes, maxNodes, minChil, maxChil, hasRandomSizes); myDiagram.model = new go.TreeModel(nodeDataArray); // update the diagram layout customized by the various control values layout(); } // Creates a random number (between MIN and MAX) of randomly colored nodes. function generateNodeData(minNodes, maxNodes, minChil, maxChil, hasRandomSizes) { var nodeArray = []; if (minNodes === undefined || isNaN(minNodes) || minNodes < 1) minNodes = 1; if (maxNodes === undefined || isNaN(maxNodes) || maxNodes < minNodes) maxNodes = minNodes; // Create a bunch of node data var numNodes = Math.floor(Math.random() * (maxNodes - minNodes + 1)) + minNodes; for (var i = 0; i < numNodes; i++) { nodeArray.push({ key: i, // the unique identifier // "parent" is set by code below that assigns children text: i.toString(), // some text to be shown by the node template fill: go.Brush.randomColor(), // a color to be shown by the node template size: (hasRandomSizes) ? new go.Size(Math.random() * 50 + 20, Math.random() * 50 + 20) : new go.Size(30, 30) }); } // 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; } // Takes the random collection of node data 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. if (nodeArray.length > 1) { if (minChil === undefined || isNaN(minChil) || minChil < 0) minChil = 0; if (maxChil === undefined || isNaN(maxChil) || maxChil < minChil) maxChil = minChil; // keep the Set of node data that do not yet have a parent var available = new go.Set(); available.addAll(nodeArray); for (var i = 0; i < nodeArray.length; i++) { var parent = nodeArray[i]; available.remove(parent); // assign some number of node data as children of this parent node data var children = Math.floor(Math.random() * (maxChil - minChil + 1)) + minChil; for (var j = 0; j < children; j++) { var child = available.first(); if (child === null) break; // oops, ran out already available.remove(child); // have the child node data refer to the parent node data by its key child.parent = parent.key; } if (available.count === 0) break; // nothing left? } } return nodeArray; } // Update the layout from the controls, and then perform the layout again function layout() { myDiagram.startTransaction("change Layout"); var lay = myDiagram.layout; var style = document.getElementById("style").value; if (style === "Layered") lay.treeStyle = go.TreeLayout.StyleLayered; else if (style === "Alternating") lay.treeStyle = go.TreeLayout.StyleAlternating; else if (style === "LastParents") lay.treeStyle = go.TreeLayout.StyleLastParents; else if (style === "RootOnly") lay.treeStyle = go.TreeLayout.StyleRootOnly; var layerStyle = document.getElementById("layerStyle").value; if (layerStyle === "Individual") lay.layerStyle = go.TreeLayout.LayerIndividual; else if (layerStyle === "Siblings") lay.layerStyle = go.TreeLayout.LayerSiblings; else if (layerStyle === "Uniform") lay.layerStyle = go.TreeLayout.LayerUniform; var angle = getRadioValue("angle"); angle = parseFloat(angle, 10); lay.angle = angle; var align = document.getElementById("align").value; if (align === "CenterChildren") lay.alignment = go.TreeLayout.AlignmentCenterChildren; else if (align === "CenterSubtrees") lay.alignment = go.TreeLayout.AlignmentCenterSubtrees; else if (align === "Start") lay.alignment = go.TreeLayout.AlignmentStart; else if (align === "End") lay.alignment = go.TreeLayout.AlignmentEnd; else if (align === "Bus") lay.alignment = go.TreeLayout.AlignmentBus; else if (align === "BusBranching") lay.alignment = go.TreeLayout.AlignmentBusBranching; else if (align === "TopLeftBus") lay.alignment = go.TreeLayout.AlignmentTopLeftBus; else if (align === "BottomRightBus") lay.alignment = go.TreeLayout.AlignmentBottomRightBus; var nodeSpacing = document.getElementById("nodeSpacing").value; nodeSpacing = parseFloat(nodeSpacing, 10); lay.nodeSpacing = nodeSpacing; var nodeIndent = document.getElementById("nodeIndent").value; nodeIndent = parseFloat(nodeIndent, 10); lay.nodeIndent = nodeIndent; var nodeIndentPastParent = document.getElementById("nodeIndentPastParent").value; nodeIndentPastParent = parseFloat(nodeIndentPastParent, 10); lay.nodeIndentPastParent = nodeIndentPastParent; var layerSpacing = document.getElementById("layerSpacing").value; layerSpacing = parseFloat(layerSpacing, 10); lay.layerSpacing = layerSpacing; var layerSpacingParentOverlap = document.getElementById("layerSpacingParentOverlap").value; layerSpacingParentOverlap = parseFloat(layerSpacingParentOverlap, 10); lay.layerSpacingParentOverlap = layerSpacingParentOverlap; var sorting = document.getElementById("sorting").value; if (sorting === "Forwards") lay.sorting = go.TreeLayout.SortingForwards; else if (sorting === "Reverse") lay.sorting = go.TreeLayout.SortingReverse; else if (sorting === "Ascending") lay.sorting = go.TreeLayout.SortingAscending; else if (sorting === "Descending") lay.sorting = go.TreeLayout.SortingDescending; var compaction = getRadioValue("compaction"); if (compaction === "Block") lay.compaction = go.TreeLayout.CompactionBlock; else if (compaction === "None") lay.compaction = go.TreeLayout.CompactionNone; var breadthLimit = document.getElementById("breadthLimit").value; breadthLimit = parseFloat(breadthLimit, 10); lay.breadthLimit = breadthLimit; var rowSpacing = document.getElementById("rowSpacing").value; rowSpacing = parseFloat(rowSpacing, 10); lay.rowSpacing = rowSpacing; var rowIndent = document.getElementById("rowIndent").value; rowIndent = parseFloat(rowIndent, 10); lay.rowIndent = rowIndent; var setsPortSpot = document.getElementById("setsPortSpot").checked; lay.setsPortSpot = setsPortSpot; var setsChildPortSpot = document.getElementById("setsChildPortSpot").checked; lay.setsChildPortSpot = setsChildPortSpot; if (style !== "Layered") { var altAngle = getRadioValue("altAngle"); altAngle = parseFloat(altAngle, 10); lay.alternateAngle = altAngle; var altAlign = document.getElementById("altAlign").value; if (altAlign === "CenterChildren") lay.alternateAlignment = go.TreeLayout.AlignmentCenterChildren; else if (altAlign === "CenterSubtrees") lay.alternateAlignment = go.TreeLayout.AlignmentCenterSubtrees; else if (altAlign === "Start") lay.alternateAlignment = go.TreeLayout.AlignmentStart; else if (altAlign === "End") lay.alternateAlignment = go.TreeLayout.AlignmentEnd; else if (altAlign === "Bus") lay.alternateAlignment = go.TreeLayout.AlignmentBus; else if (altAlign === "BusBranching") lay.alternateAlignment = go.TreeLayout.AlignmentBusBranching; else if (altAlign === "TopLeftBus") lay.alternateAlignment = go.TreeLayout.AlignmentTopLeftBus; else if (altAlign === "BottomRightBus") lay.alternateAlignment = go.TreeLayout.AlignmentBottomRightBus; var altNodeSpacing = document.getElementById("altNodeSpacing").value; altNodeSpacing = parseFloat(altNodeSpacing, 10); lay.alternateNodeSpacing = altNodeSpacing; var altNodeIndent = document.getElementById("altNodeIndent").value; altNodeIndent = parseFloat(altNodeIndent, 10); lay.alternateNodeIndent = altNodeIndent; var altNodeIndentPastParent = document.getElementById("altNodeIndentPastParent").value; altNodeIndentPastParent = parseFloat(altNodeIndentPastParent, 10); lay.alternateNodeIndentPastParent = altNodeIndentPastParent; var altLayerSpacing = document.getElementById("altLayerSpacing").value; altLayerSpacing = parseFloat(altLayerSpacing, 10); lay.alternateLayerSpacing = altLayerSpacing; var altLayerSpacingParentOverlap = document.getElementById("altLayerSpacingParentOverlap").value; altLayerSpacingParentOverlap = parseFloat(altLayerSpacingParentOverlap, 10); lay.alternateLayerSpacingParentOverlap = altLayerSpacingParentOverlap; var altSorting = document.getElementById("altSorting").value; if (altSorting === "Forwards") lay.alternateSorting = go.TreeLayout.SortingForwards; else if (altSorting === "Reverse") lay.alternateSorting = go.TreeLayout.SortingReverse; else if (altSorting === "Ascending") lay.alternateSorting = go.TreeLayout.SortingAscending; else if (altSorting === "Descending") lay.alternateSorting = go.TreeLayout.SortingDescending; var altCompaction = getRadioValue("altCompaction"); if (altCompaction === "Block") lay.alternateCompaction = go.TreeLayout.CompactionBlock; else if (altCompaction === "None") lay.alternateCompaction = go.TreeLayout.CompactionNone; var altBreadthLimit = document.getElementById("altBreadthLimit").value; altBreadthLimit = parseFloat(altBreadthLimit, 10); lay.alternateBreadthLimit = altBreadthLimit; var altRowSpacing = document.getElementById("altRowSpacing").value; altRowSpacing = parseFloat(altRowSpacing, 10); lay.alternateRowSpacing = altRowSpacing; var altRowIndent = document.getElementById("altRowIndent").value; altRowIndent = parseFloat(altRowIndent, 10); lay.alternateRowIndent = altRowIndent; var altSetsPortSpot = document.getElementById("altSetsPortSpot").checked; lay.alternateSetsPortSpot = altSetsPortSpot; var altSetsChildPortSpot = document.getElementById("altSetsChildPortSpot").checked; lay.alternateSetsChildPortSpot = altSetsChildPortSpot; } myDiagram.commitTransaction("change Layout"); } function getRadioValue(name) { var radio = document.getElementsByName(name); for (var i = 0; i < radio.length; i++) if (radio[i].checked) return radio[i].value; } </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>Tree Style</b><br /> <select name="style" id="style" onchange="layout()"> <option value="Layered" selected="selected">Layered</option> <option value="Alternating">Alternating</option> <option value="LastParents">LastParents</option> <option value="RootOnly">RootOnly</option> </select> <br /> <b>Layer Style</b><br /> <select name="layerStyle" id="layerStyle" onchange="layout()"> <option value="Individual" selected="selected">Individual</option> <option value="Siblings">Siblings</option> <option value="Uniform">Uniform</option> </select> <br /> <br /> <b>New Tree</b><br /> MinNodes: <input type="number" width="2" id="minNodes" value="20" /><br /> MaxNodes: <input type="number" width="2" id="maxNodes" value="100" /><br /> MinChildren: <input type="number" width="2" id="minChil" value="1" /><br /> MaxChildren: <input type="number" width="2" id="maxChil" value="3" /><br /> Random Sizes: <input type="checkbox" id="randomSizes" /><br /> <button type="button" onclick="rebuildGraph()">Generate Tree</button> </span> <span style="display: inline-block; vertical-align: top; padding: 5px"> <b>Default Properties</b><br /> Angle: <input type="radio" name="angle" onclick="layout()" value="0" checked="checked" />Right <input type="radio" name="angle" onclick="layout()" value="90" />Down <input type="radio" name="angle" onclick="layout()" value="180" />Left <input type="radio" name="angle" onclick="layout()" value="270" />Up<br /> Alignment: <select name="align" id="align" onchange="layout()"> <option value="CenterChildren" selected="selected">CenterChildren</option> <option value="CenterSubtrees">CenterSubtrees</option> <option value="Start">Start</option> <option value="End">End</option> <option value="Bus">Bus</option> <option value="BusBranching">BusBranching</option> <option value="TopLeftBus">TopLeftBus</option> <option value="BottomRightBus">BottomRightBus</option> </select> <br /> NodeSpacing: <input type="number" size="2" id="nodeSpacing" value="20" onchange="layout()" /> (negative causes overlaps)<br /> NodeIndent: <input type="number" size="2" id="nodeIndent" value="0" onchange="layout()" /> (when Start or End; >= 0)<br /> NodeIndentPastParent: <input type="number" size="2" id="nodeIndentPastParent" value="0" onchange="layout()" /> (fraction; 0-1)<br /> LayerSpacing: <input type="number" size="2" id="layerSpacing" value="50" onchange="layout()" /> (negative causes overlaps)<br /> LayerSpacingParentOverlap: <input type="number" size="2" id="layerSpacingParentOverlap" value="0" onchange="layout()" /> (fraction; 0-1)<br /> Sorting: <select name="sorting" id="sorting" onchange="layout()"> <option value="Forwards" selected="selected">Forwards</option> <option value="Reverse">Reverse</option> <option value="Ascending">Ascending</option> <option value="Descending">Descending</option> </select> <br /> Compaction: <input type="radio" name="compaction" onclick="layout()" value="Block" checked="checked" /> Block <input type="radio" name="compaction" onclick="layout()" value="None" /> None<br /> BreadthLimit: <input type="number" size="2" id="breadthLimit" value="0" onchange="layout()" /> (0 means no limit)<br /> RowSpacing: <input type="number" size="2" id="rowSpacing" value="25" onchange="layout()" /> (negative causes overlaps)<br /> RowIndent: <input type="number" size="2" id="rowIndent" value="10" onchange="layout()" /> (>= 0)<br /> SetsPortSpot: <input type="checkbox" id="setsPortSpot" checked="checked" onclick="layout()" /> SetsChildPortSpot: <input type="checkbox" id="setsChildPortSpot" checked="checked" onclick="layout()" /><br /> </span> <span style="display: inline-block; vertical-align: top; padding: 5px"> <b>Alternates</b> (only when TreeStyle is not Layered) <br /> Angle: <input type="radio" name="altAngle" onclick="layout()" value="0" checked="checked" />Right <input type="radio" name="altAngle" onclick="layout()" value="90" />Down <input type="radio" name="altAngle" onclick="layout()" value="180" />Left <input type="radio" name="altAngle" onclick="layout()" value="270" />Up<br /> Alignment: <select name="altAlign" id="altAlign" onchange="layout()"> <option value="CenterChildren" selected="selected">CenterChildren</option> <option value="CenterSubtrees">CenterSubtrees</option> <option value="Start">Start</option> <option value="End">End</option> <option value="Bus">Bus</option> <option value="BusBranching">BusBranching</option> <option value="TopLeftBus">TopLeftBus</option> <option value="BottomRightBus">BottomRightBus</option> </select> <br /> NodeSpacing: <input type="number" size="2" id="altNodeSpacing" value="20" onchange="layout()" /> (negative causes overlaps)<br /> NodeIndent: <input type="number" size="2" id="altNodeIndent" value="0" onchange="layout()" /> (when Start or End; >= 0)<br /> NodeIndentPastParent: <input type="number" size="2" id="altNodeIndentPastParent" value="0" onchange="layout()" /> (fraction; 0-1)<br /> LayerSpacing: <input type="number" size="2" id="altLayerSpacing" value="50" onchange="layout()" /> (negative causes overlaps)<br /> LayerSpacingParentOverlap: <input type="number" size="2" id="altLayerSpacingParentOverlap" value="0" onchange="layout()" /> (fraction; 0-1)<br /> Sorting: <select name="altSorting" id="altSorting" onchange="layout()"> <option value="Forwards" selected="selected">Forwards</option> <option value="Reverse">Reverse</option> <option value="Ascending">Ascending</option> <option value="Descending">Descending</option> </select> <br /> Compaction: <input type="radio" name="altCompaction" onclick="layout()" value="Block" checked="checked" /> Block <input type="radio" name="altCompaction" onclick="layout()" value="None" /> None<br /> BreadthLimit: <input type="number" size="2" id="altBreadthLimit" value="0" onchange="layout()" /> (0 means no limit)<br /> RowSpacing: <input type="number" size="2" id="altRowSpacing" value="25" onchange="layout()" /> (negative causes overlaps)<br /> RowIndent: <input type="number" size="2" id="altRowIndent" value="10" onchange="layout()" /> (>= 0)<br /> SetsPortSpot: <input type="checkbox" id="altSetsPortSpot" checked="checked" onclick="layout()" /> SetsChildPortSpot: <input type="checkbox" id="altSetsChildPortSpot" checked="checked" onclick="layout()" /><br /> </span> </div> <div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 500px"></div> <p> For information on <b>TreeLayout</b> and its properties, see the <a>TreeLayout</a> documentation page. </p> </div> </body> </html>