UNPKG

12.7 kBHTMLView Raw
1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="utf-8"/>
5<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
6<meta name="description" content="The JavaScript class hierarchy defined by the GoJS library, arranged in nested circles."/>
7<link rel="stylesheet" href="../assets/css/style.css"/>
8<!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
9<title>GoJS Packed Class Hierarchy</title>
10</head>
11
12<body>
13 <!-- This top nav is not part of the sample code -->
14 <nav id="navTop" class="w-full z-30 top-0 text-white bg-nwoods-primary">
15 <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">
16 <div class="md:pl-4">
17 <a class="text-white hover:text-white no-underline hover:no-underline
18 font-bold text-2xl lg:text-4xl rounded-lg hover:bg-nwoods-secondary " href="../">
19 <h1 class="mb-0 p-1 ">GoJS</h1>
20 </a>
21 </div>
22 <button id="topnavButton" class="rounded-lg sm:hidden focus:outline-none focus:ring" aria-label="Navigation">
23 <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
24 <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>
25 <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>
26 </svg>
27 </button>
28 <div id="topnavList" class="hidden sm:block items-center w-auto mt-0 text-white p-0 z-20">
29 <ul class="list-reset list-none font-semibold flex justify-end flex-wrap sm:flex-nowrap items-center px-0 pb-0">
30 <li class="p-1 sm:p-0"><a class="topnav-link" href="../learn/">Learn</a></li>
31 <li class="p-1 sm:p-0"><a class="topnav-link" href="../samples/">Samples</a></li>
32 <li class="p-1 sm:p-0"><a class="topnav-link" href="../intro/">Intro</a></li>
33 <li class="p-1 sm:p-0"><a class="topnav-link" href="../api/">API</a></li>
34 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/products/register.html">Register</a></li>
35 <li class="p-1 sm:p-0"><a class="topnav-link" href="../download.html">Download</a></li>
36 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://forum.nwoods.com/c/gojs/11">Forum</a></li>
37 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/contact.html"
38 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a></li>
39 <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/sales/index.html"
40 target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a></li>
41 </ul>
42 </div>
43 </div>
44 <hr class="border-b border-gray-600 opacity-50 my-0 py-0" />
45 </nav>
46 <div class="md:flex flex-col md:flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
47 <div id="navSide" class="flex flex-col w-full md:w-48 text-gray-700 bg-white flex-shrink-0"></div>
48 <!-- * * * * * * * * * * * * * -->
49 <!-- Start of GoJS sample code -->
50
51
52 <div class="p-4 w-full">
53 <div id="sample">
54 <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:700px"></div>
55 <p>
56 Circle packing can be a useful way to visualize hierarchical data, as demonstrated here
57 with a visualization of the class hierarchy of the GoJS library. This layout is performed
58 automatically by the <a href="../extensions/PackedLayout.html">PackedLayout</a> extension. Nodes
59 are sized according to how many properties their corresponding class has, or has inherited.
60 As a result, larger nodes generally represent more complex classes. Mouse over nodes to see
61 their full name and the number of properties on their corresponding class.
62 </p>
63 <p>
64 This sample is very similar to the <a href="../samples/classHierarchy.html" target="_blank">Class Hierarchy</a> sample,
65 except that instead of showing the class hierarchy as a tree, it is displayed using nested circles.
66 Opening the API page is achieved by double-clicking on a node, rather than using a "HyperlinkText".
67 </p>
68 </div>
69
70 <script type="module" id="code">
71 import * as go from "../release/go-module.js";
72 import { PackedLayout } from "./PackedLayout.js";
73
74 if (window.goSamples) window.goSamples(); // init for these samples -- you don't need to call this
75 const $ = go.GraphObject.make; // for conciseness in defining templates
76
77 // subclass PackedLayout to change default properties and override commitLayout function
78 class HierarchyLayout extends PackedLayout {
79 constructor() {
80 super();
81 this.packShape = PackedLayout.Spiral;
82 this.hasCircularNodes = true;
83 this.sortMode = PackedLayout.Area,
84 this.comparer = function(na, nb) {
85 // ensure label is placed last
86 if (na.data.isLabel) {
87 return 1;
88 }
89 if (nb.data.isLabel) {
90 return -1;
91 }
92 // otherwise sort in ascending order by size (all nodes are circular, so using width or height here doesn't matter)
93 return (na.actualBounds.width - nb.actualBounds.width);
94 }
95 }
96
97 /* after each group has had its layout applied, size and position it according
98 * to the smallest enclosing circle which goes around all of its nodes */
99 commitLayout() {
100 if (this.group !== null) {
101 const groupData = keyToNodeDataMap.get(this.group.key);
102
103 const enclosingCircle = this.enclosingCircle;
104 const actualBounds = this.actualBounds;
105
106 const size = new go.Size(enclosingCircle.width, enclosingCircle.width);
107 this.diagram.model.setDataProperty(groupData, "size", size);
108
109 const dx = enclosingCircle.centerX - actualBounds.centerX;
110 const dy = enclosingCircle.centerY - actualBounds.centerY;
111 const position = new go.Point((actualBounds.width - enclosingCircle.width) / 2 + dx, (actualBounds.height - enclosingCircle.height) / 2 + dy);
112 this.diagram.model.setDataProperty(groupData, "position", position);
113 }
114 }
115 } // end HierarchyLayout
116
117
118 const myDiagram =
119 $(go.Diagram, "myDiagramDiv", // must be the ID or reference to div
120 {
121 layout: $(HierarchyLayout), // defined above
122 "animationManager.isEnabled": false,
123 isReadOnly: true,
124 initialAutoScale: go.Diagram.Uniform
125 });
126
127 // common definitions for both Nodes and Groups
128 const toolTipTemplate =
129 $(go.Adornment, "Auto",
130 $(go.Shape, { fill: "white" }),
131 $(go.TextBlock, { margin: 4 },
132 new go.Binding("text", "tooltip"))
133 );
134
135 const selectionAdornmentTemplate =
136 $(go.Adornment, "Auto",
137 $(go.Shape, "Circle",
138 { fill: null, stroke: "dodgerblue", strokeWidth: 3,
139 spot1: go.Spot.TopLeft, spot2: go.Spot.BottomRight }),
140 $(go.Placeholder)
141 );
142
143 function commonStyle() {
144 return [
145 {
146 toolTip: toolTipTemplate,
147 selectionAdornmentTemplate: selectionAdornmentTemplate,
148 doubleClick: function(e, node) {
149 const url = "../../api/symbols/" + node.data.key + ".html";
150 window.open(url, "_blank");
151 }
152 }
153 ];
154 }
155
156 myDiagram.nodeTemplate =
157 $(go.Node, "Auto", commonStyle(),
158 new go.Binding("width", "diameter"),
159 new go.Binding("height", "diameter"),
160 $(go.Shape,
161 { figure: "Circle", fill: "#1F4963", strokeWidth: 0, spot1: go.Spot.TopLeft, spot2: go.Spot.BottomRight },
162 new go.Binding("fill")),
163 $(go.TextBlock,
164 { font: "12px Helvetica, Arial, sans-serif", stroke: "white", maxLines: 1 },
165 new go.Binding("text"),
166 new go.Binding("font"),
167 new go.Binding("stroke", "fill", function(f) { return go.Brush.isDark(f) ? "white" : "black"; }))
168 );
169
170 myDiagram.groupTemplate =
171 $(go.Group, commonStyle(),
172 { layout: $(HierarchyLayout) }, // defined above
173 $(go.Shape, "Circle",
174 { fill: "rgba(128,128,128,0.33)" },
175 new go.Binding("desiredSize", "size"),
176 new go.Binding("position")),
177 $(go.Placeholder) // represents area for all member parts
178 );
179
180 // Collect all of the data for the model of the class hierarchy
181 const nodeDataArray = [
182 { key: "GoJS", text: "GoJS", children: [] },
183 // large label to be placed at the very end
184 {
185 key: "GoJS label",
186 group: "GoJS",
187 text: "GoJS",
188 tooltip: "GoJS",
189 fill: null,
190 font: "64px Helvetica, bold Arial, sans-serif",
191 isLabel: true,
192 children: []
193 }
194 ];
195
196 const keyToNodeDataMap = new go.Map();
197 keyToNodeDataMap.add("GoJS", nodeDataArray[0]);
198
199 // Iterate over all of the classes in "go"
200 for (let k in go) {
201 const cls = go[k];
202 if (!cls) continue;
203 const proto = cls.prototype;
204 if (!proto) continue;
205 proto.constructor.className = k; // remember name
206
207 let propCount = 0; // count number of properties on the class
208 for (let prop in proto) {
209 if (proto.hasOwnProperty(prop)) {
210 propCount++;
211 }
212 }
213
214 const data = { key: k, text: k, propCount: propCount, children: [] };
215 if (keyToNodeDataMap.has(k)) {
216 data.children = keyToNodeDataMap.get(k).children;
217 }
218 keyToNodeDataMap.add(k, data); // will replace existing key if there is one
219
220 // find base class constructor
221 const base = Object.getPrototypeOf(proto).constructor;
222 if (base === Object) { // "root" node?
223 data.group = "GoJS";
224 keyToNodeDataMap.get("GoJS").children.push(data);
225 nodeDataArray.push(data);
226 } else {
227 // add a node for this class and set its group to its parent
228 data.group = base.className;
229 if (keyToNodeDataMap.has(base.className)) {
230 keyToNodeDataMap.get(base.className).children.push(data);
231 } else {
232 keyToNodeDataMap.add(base.className, {children: [data]});
233 }
234
235 nodeDataArray.push(data);
236 }
237 }
238
239 // create groups and add labels to groups with only 1 child
240 for (let i = nodeDataArray.length - 1; i >= 0; i--) {
241 const n = nodeDataArray[i];
242 if (n.children.length > 0) {
243 n.isGroup = true;
244 }
245
246 // add tooltip and/or size node using the total number of properties it has and has inherited
247 let totalCount = n.propCount;
248 let parentKey = n.group;
249 let parentData;
250 while ((parentData = keyToNodeDataMap.get(parentKey)) !== null && parentData.propCount !== undefined) {
251 totalCount += parentData.propCount;
252 parentKey = parentData.group;
253 }
254
255 if (totalCount === undefined) { // applies to the root GoJS group only
256 n.tooltip = n.text;
257 } else {
258 n.tooltip = n.text + ": " + totalCount; // add tooltip
259 }
260 if (!n.isGroup && !n.isLabel) { // only set size of node if it is not a group
261 // calculate size by scaling totalCount logarithmically to produce more visually appealing results
262 n.diameter = 20 * Math.log(0.5 * (totalCount + 5.5));
263 }
264
265 // add label to groups that only have one child
266 if (n.children.length === 1) {
267 nodeDataArray.push({
268 text: n.text,
269 tooltip: n.tooltip,
270 group: n.key,
271 fill: null,
272 font: "20px Helvetica, bold Arial, sans-serif"
273 });
274 }
275
276 delete n.children;
277 }
278
279 myDiagram.model = new go.GraphLinksModel(nodeDataArray);
280 </script>
281 </div>
282 <!-- * * * * * * * * * * * * * -->
283 <!-- End of GoJS sample code -->
284 </div>
285</body>
286<!-- This script is part of the gojs.net website, and is not needed to run the sample -->
287<script src="../assets/js/goSamples.js"></script>
288</html>