@graphty/layout 1.2.3 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/dist/src/algorithms/index.d.ts +5 -0
  2. package/dist/src/algorithms/index.js +6 -0
  3. package/dist/src/algorithms/index.js.map +1 -0
  4. package/dist/src/algorithms/optimization/index.d.ts +7 -0
  5. package/dist/src/algorithms/optimization/index.js +7 -0
  6. package/dist/src/algorithms/optimization/index.js.map +1 -0
  7. package/dist/src/algorithms/optimization/kamada-kawai-solver.d.ts +32 -0
  8. package/dist/src/algorithms/optimization/kamada-kawai-solver.js +176 -0
  9. package/dist/src/algorithms/optimization/kamada-kawai-solver.js.map +1 -0
  10. package/dist/src/algorithms/optimization/lbfgs.d.ts +13 -0
  11. package/dist/src/algorithms/optimization/lbfgs.js +56 -0
  12. package/dist/src/algorithms/optimization/lbfgs.js.map +1 -0
  13. package/dist/src/algorithms/optimization/line-search.d.ts +15 -0
  14. package/dist/src/algorithms/optimization/line-search.js +37 -0
  15. package/dist/src/algorithms/optimization/line-search.js.map +1 -0
  16. package/dist/src/algorithms/optimization/types.d.ts +5 -0
  17. package/dist/src/algorithms/optimization/types.js +5 -0
  18. package/dist/src/algorithms/optimization/types.js.map +1 -0
  19. package/dist/src/algorithms/planarity/check.d.ts +17 -0
  20. package/dist/src/algorithms/planarity/check.js +30 -0
  21. package/dist/src/algorithms/planarity/check.js.map +1 -0
  22. package/dist/src/algorithms/planarity/embedding.d.ts +29 -0
  23. package/dist/src/algorithms/planarity/embedding.js +179 -0
  24. package/dist/src/algorithms/planarity/embedding.js.map +1 -0
  25. package/dist/src/algorithms/planarity/index.d.ts +7 -0
  26. package/dist/src/algorithms/planarity/index.js +8 -0
  27. package/dist/src/algorithms/planarity/index.js.map +1 -0
  28. package/dist/src/algorithms/planarity/lr-test.d.ts +15 -0
  29. package/dist/src/algorithms/planarity/lr-test.js +54 -0
  30. package/dist/src/algorithms/planarity/lr-test.js.map +1 -0
  31. package/dist/src/algorithms/planarity/special-graphs.d.ts +28 -0
  32. package/dist/src/algorithms/planarity/special-graphs.js +108 -0
  33. package/dist/src/algorithms/planarity/special-graphs.js.map +1 -0
  34. package/dist/src/generators/basic.d.ts +28 -0
  35. package/dist/src/generators/basic.js +79 -0
  36. package/dist/src/generators/basic.js.map +1 -0
  37. package/dist/src/generators/bipartite.d.ts +16 -0
  38. package/dist/src/generators/bipartite.js +40 -0
  39. package/dist/src/generators/bipartite.js.map +1 -0
  40. package/dist/src/generators/grid.d.ts +11 -0
  41. package/dist/src/generators/grid.js +37 -0
  42. package/dist/src/generators/grid.js.map +1 -0
  43. package/dist/src/generators/index.d.ts +8 -0
  44. package/dist/src/generators/index.js +9 -0
  45. package/dist/src/generators/index.js.map +1 -0
  46. package/dist/src/generators/random.d.ts +12 -0
  47. package/dist/src/generators/random.js +34 -0
  48. package/dist/src/generators/random.js.map +1 -0
  49. package/dist/src/generators/scale-free.d.ts +12 -0
  50. package/dist/src/generators/scale-free.js +62 -0
  51. package/dist/src/generators/scale-free.js.map +1 -0
  52. package/dist/src/index.d.ts +10 -0
  53. package/dist/src/index.js +15 -0
  54. package/dist/src/index.js.map +1 -0
  55. package/dist/src/layouts/basic/index.d.ts +4 -0
  56. package/dist/src/layouts/basic/index.js +5 -0
  57. package/dist/src/layouts/basic/index.js.map +1 -0
  58. package/dist/src/layouts/basic/random.d.ts +14 -0
  59. package/dist/src/layouts/basic/random.js +27 -0
  60. package/dist/src/layouts/basic/random.js.map +1 -0
  61. package/dist/src/layouts/force-directed/arf.d.ts +13 -0
  62. package/dist/src/layouts/force-directed/arf.js +103 -0
  63. package/dist/src/layouts/force-directed/arf.js.map +1 -0
  64. package/dist/src/layouts/force-directed/forceatlas2.d.ts +22 -0
  65. package/dist/src/layouts/force-directed/forceatlas2.js +322 -0
  66. package/dist/src/layouts/force-directed/forceatlas2.js.map +1 -0
  67. package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts +19 -0
  68. package/dist/src/layouts/force-directed/fruchterman-reingold.js +131 -0
  69. package/dist/src/layouts/force-directed/fruchterman-reingold.js.map +1 -0
  70. package/dist/src/layouts/force-directed/index.d.ts +8 -0
  71. package/dist/src/layouts/force-directed/index.js +9 -0
  72. package/dist/src/layouts/force-directed/index.js.map +1 -0
  73. package/dist/src/layouts/force-directed/kamada-kawai.d.ts +15 -0
  74. package/dist/src/layouts/force-directed/kamada-kawai.js +87 -0
  75. package/dist/src/layouts/force-directed/kamada-kawai.js.map +1 -0
  76. package/dist/src/layouts/force-directed/spring.d.ts +19 -0
  77. package/dist/src/layouts/force-directed/spring.js +23 -0
  78. package/dist/src/layouts/force-directed/spring.js.map +1 -0
  79. package/dist/src/layouts/geometric/circular.d.ts +14 -0
  80. package/dist/src/layouts/geometric/circular.js +67 -0
  81. package/dist/src/layouts/geometric/circular.js.map +1 -0
  82. package/dist/src/layouts/geometric/index.d.ts +6 -0
  83. package/dist/src/layouts/geometric/index.js +7 -0
  84. package/dist/src/layouts/geometric/index.js.map +1 -0
  85. package/dist/src/layouts/geometric/shell.d.ts +15 -0
  86. package/dist/src/layouts/geometric/shell.js +67 -0
  87. package/dist/src/layouts/geometric/shell.js.map +1 -0
  88. package/dist/src/layouts/geometric/spiral.d.ts +16 -0
  89. package/dist/src/layouts/geometric/spiral.js +73 -0
  90. package/dist/src/layouts/geometric/spiral.js.map +1 -0
  91. package/dist/src/layouts/hierarchical/bfs.d.ts +12 -0
  92. package/dist/src/layouts/hierarchical/bfs.js +62 -0
  93. package/dist/src/layouts/hierarchical/bfs.js.map +1 -0
  94. package/dist/src/layouts/hierarchical/bipartite.d.ts +16 -0
  95. package/dist/src/layouts/hierarchical/bipartite.js +72 -0
  96. package/dist/src/layouts/hierarchical/bipartite.js.map +1 -0
  97. package/dist/src/layouts/hierarchical/index.d.ts +6 -0
  98. package/dist/src/layouts/hierarchical/index.js +7 -0
  99. package/dist/src/layouts/hierarchical/index.js.map +1 -0
  100. package/dist/src/layouts/hierarchical/multipartite.d.ts +12 -0
  101. package/dist/src/layouts/hierarchical/multipartite.js +73 -0
  102. package/dist/src/layouts/hierarchical/multipartite.js.map +1 -0
  103. package/dist/src/layouts/index.d.ts +8 -0
  104. package/dist/src/layouts/index.js +9 -0
  105. package/dist/src/layouts/index.js.map +1 -0
  106. package/dist/src/layouts/specialized/index.d.ts +5 -0
  107. package/dist/src/layouts/specialized/index.js +6 -0
  108. package/dist/src/layouts/specialized/index.js.map +1 -0
  109. package/dist/src/layouts/specialized/planar.d.ts +14 -0
  110. package/dist/src/layouts/specialized/planar.js +47 -0
  111. package/dist/src/layouts/specialized/planar.js.map +1 -0
  112. package/dist/src/layouts/specialized/spectral.d.ts +14 -0
  113. package/dist/src/layouts/specialized/spectral.js +106 -0
  114. package/dist/src/layouts/specialized/spectral.js.map +1 -0
  115. package/dist/src/types/embedding.d.ts +9 -0
  116. package/dist/src/types/embedding.js +5 -0
  117. package/dist/src/types/embedding.js.map +1 -0
  118. package/dist/src/types/graph.d.ts +10 -0
  119. package/dist/src/types/graph.js +5 -0
  120. package/dist/src/types/graph.js.map +1 -0
  121. package/dist/src/types/index.d.ts +6 -0
  122. package/dist/src/types/index.js +7 -0
  123. package/dist/src/types/index.js.map +1 -0
  124. package/dist/src/types/layout.d.ts +6 -0
  125. package/dist/src/types/layout.js +5 -0
  126. package/dist/src/types/layout.js.map +1 -0
  127. package/dist/src/utils/graph.d.ts +34 -0
  128. package/dist/src/utils/graph.js +69 -0
  129. package/dist/src/utils/graph.js.map +1 -0
  130. package/dist/src/utils/index.d.ts +8 -0
  131. package/dist/src/utils/index.js +9 -0
  132. package/dist/src/utils/index.js.map +1 -0
  133. package/dist/src/utils/numpy.d.ts +17 -0
  134. package/dist/src/utils/numpy.js +101 -0
  135. package/dist/src/utils/numpy.js.map +1 -0
  136. package/dist/src/utils/params.d.ts +17 -0
  137. package/dist/src/utils/params.js +22 -0
  138. package/dist/src/utils/params.js.map +1 -0
  139. package/dist/src/utils/random.d.ts +14 -0
  140. package/dist/src/utils/random.js +42 -0
  141. package/dist/src/utils/random.js.map +1 -0
  142. package/dist/src/utils/rescale.d.ts +21 -0
  143. package/dist/src/utils/rescale.js +119 -0
  144. package/dist/src/utils/rescale.js.map +1 -0
  145. package/dist/vitest.config.js +1 -1
  146. package/dist/vitest.config.js.map +1 -1
  147. package/package.json +14 -1
  148. package/.env.example +0 -11
  149. package/.github/workflows/ci.yml +0 -101
  150. package/.releaserc.json +0 -22
  151. package/CHANGELOG.md +0 -52
  152. package/CLAUDE.md +0 -104
  153. package/CONTRIBUTING.md +0 -1
  154. package/DEPLOYMENT.md +0 -59
  155. package/build-examples-script.js +0 -26
  156. package/commitlint.config.js +0 -37
  157. package/examples/3d-force-directed.html +0 -611
  158. package/examples/3d-kamada-kawai.html +0 -379
  159. package/examples/3d-layout-comparison.html +0 -448
  160. package/examples/3d-spherical-layout.html +0 -319
  161. package/examples/arf-layout.html +0 -524
  162. package/examples/bfs-layout.html +0 -487
  163. package/examples/bipartite-layout.html +0 -382
  164. package/examples/circular-layout.html +0 -292
  165. package/examples/forceatlas2-layout.html +0 -588
  166. package/examples/index.html +0 -241
  167. package/examples/kamada-kawai-layout.html +0 -425
  168. package/examples/layout-helpers.js +0 -23
  169. package/examples/layout.js +0 -2304
  170. package/examples/multipartite-layout.html +0 -410
  171. package/examples/planar-layout.html +0 -388
  172. package/examples/random-layout.html +0 -328
  173. package/examples/shell-layout.html +0 -323
  174. package/examples/spectral-layout.html +0 -335
  175. package/examples/spiral-layout.html +0 -447
  176. package/examples/spring-layout.html +0 -313
  177. package/test/arf-layout.test.ts +0 -443
  178. package/test/bfs-layout.test.ts +0 -427
  179. package/test/bipartite-layout.test.ts +0 -344
  180. package/test/circular-layout.test.ts +0 -442
  181. package/test/forceatlas2-layout.test.ts +0 -405
  182. package/test/fruchterman-reingold-layout.test.ts +0 -477
  183. package/test/graph-generators.test.ts +0 -450
  184. package/test/kamada-kawai-layout.test.ts +0 -623
  185. package/test/multipartite-layout.test.ts +0 -404
  186. package/test/planar-layout.test.ts +0 -266
  187. package/test/random-layout.test.ts +0 -254
  188. package/test/rescale-layout.test.ts +0 -373
  189. package/test/shell-layout.test.ts +0 -347
  190. package/test/spectral-layout.test.ts +0 -378
  191. package/test/spiral-layout.test.ts +0 -338
  192. package/test/spring-layout.test.ts +0 -241
  193. package/test/test-utils.ts +0 -34
  194. package/test/utils-graph.test.ts +0 -155
  195. package/test/utils-index.test.ts +0 -77
  196. package/test/utils-numpy.test.ts +0 -272
  197. package/test/utils-params.test.ts +0 -99
  198. package/test/utils-random.test.ts +0 -229
  199. package/tsconfig.json +0 -16
  200. package/vite.config.js +0 -36
  201. package/vitest.config.ts +0 -30
@@ -1,448 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <!-- Eruda Mobile Console -->
5
- <script src="https://cdn.jsdelivr.net/npm/eruda@3/eruda.min.js"></script>
6
- <script>
7
- // Initialize Eruda console for mobile debugging
8
- if (typeof eruda !== "undefined") {
9
- eruda.init();
10
- // Auto-show on mobile devices
11
- if (/mobile|android|ios|iphone|ipad|ipod/i.test(navigator.userAgent.toLowerCase())) {
12
- eruda.show();
13
- }
14
- }
15
- </script>
16
- <meta charset="UTF-8">
17
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
18
- <title>3D Layout Comparison - @graphty/layout</title>
19
- <style>
20
- body {
21
- margin: 0;
22
- font-family: Arial, sans-serif;
23
- overflow: hidden;
24
- background: #0a0a0a;
25
- }
26
- #container {
27
- display: grid;
28
- grid-template-columns: 1fr 1fr;
29
- grid-template-rows: 1fr 1fr;
30
- width: 100vw;
31
- height: 100vh;
32
- gap: 2px;
33
- }
34
- .viewport {
35
- position: relative;
36
- background: #111;
37
- }
38
- .viewport canvas {
39
- width: 100% !important;
40
- height: 100% !important;
41
- }
42
- .viewport-label {
43
- position: absolute;
44
- top: 10px;
45
- left: 10px;
46
- color: white;
47
- background: rgba(0, 0, 0, 0.7);
48
- padding: 8px 12px;
49
- border-radius: 4px;
50
- font-size: 14px;
51
- font-weight: bold;
52
- pointer-events: none;
53
- }
54
- #controls {
55
- position: absolute;
56
- top: 50%;
57
- left: 50%;
58
- transform: translate(-50%, -50%);
59
- background: rgba(0, 0, 0, 0.9);
60
- padding: 20px;
61
- border-radius: 8px;
62
- color: white;
63
- border: 1px solid #333;
64
- z-index: 100;
65
- }
66
- #info {
67
- position: absolute;
68
- bottom: 10px;
69
- left: 10px;
70
- color: white;
71
- background: rgba(0, 0, 0, 0.7);
72
- padding: 10px;
73
- border-radius: 5px;
74
- font-size: 12px;
75
- }
76
- button {
77
- display: block;
78
- margin: 10px 0;
79
- padding: 8px 16px;
80
- cursor: pointer;
81
- width: 100%;
82
- background: #2a2a2a;
83
- color: white;
84
- border: 1px solid #444;
85
- border-radius: 4px;
86
- }
87
- button:hover {
88
- background: #3a3a3a;
89
- }
90
- label {
91
- display: block;
92
- margin: 10px 0;
93
- }
94
- select {
95
- width: 100%;
96
- padding: 5px;
97
- margin-top: 5px;
98
- background: #2a2a2a;
99
- color: white;
100
- border: 1px solid #444;
101
- border-radius: 4px;
102
- }
103
- .sync-indicator {
104
- display: inline-block;
105
- width: 8px;
106
- height: 8px;
107
- background: #4f4;
108
- border-radius: 50%;
109
- margin-left: 5px;
110
- vertical-align: middle;
111
- }
112
- </style>
113
- </head>
114
- <body>
115
- <div id="container">
116
- <div class="viewport" id="viewport1">
117
- <div class="viewport-label">Spherical Layout</div>
118
- </div>
119
- <div class="viewport" id="viewport2">
120
- <div class="viewport-label">Kamada-Kawai Layout</div>
121
- </div>
122
- <div class="viewport" id="viewport3">
123
- <div class="viewport-label">Spring Layout</div>
124
- </div>
125
- <div class="viewport" id="viewport4">
126
- <div class="viewport-label">ForceAtlas2 Layout</div>
127
- </div>
128
- </div>
129
-
130
- <div id="controls">
131
- <h3>3D Layout Comparison <span class="sync-indicator"></span></h3>
132
- <label>
133
- Graph Type:
134
- <select id="graphType">
135
- <option value="grid">Grid Graph (4×4)</option>
136
- <option value="complete">Complete Graph (K8)</option>
137
- <option value="cycle">Cycle Graph (C20)</option>
138
- <option value="star">Star Graph (S15)</option>
139
- <option value="wheel">Wheel Graph (W12)</option>
140
- <option value="bipartite">Bipartite Graph</option>
141
- <option value="scalefree">Scale-Free Graph</option>
142
- <option value="random">Random Graph</option>
143
- </select>
144
- </label>
145
- <button id="regenerate">Generate New Graph</button>
146
- <button id="syncCameras">Sync All Cameras</button>
147
- <label>
148
- <input type="checkbox" id="autoRotate" checked> Auto Rotate
149
- </label>
150
- </div>
151
-
152
- <div id="info">
153
- <div>Drag to rotate • Scroll to zoom • Click center panel to change graph</div>
154
- <div>Nodes: <span id="nodeCount">0</span> • Edges: <span id="edgeCount">0</span></div>
155
- </div>
156
-
157
- <!-- Import map for module resolution -->
158
- <script type="importmap">
159
- {
160
- "imports": {
161
- "three": "https://unpkg.com/three@0.157.0/build/three.module.js",
162
- "three/addons/": "https://unpkg.com/three@0.157.0/examples/jsm/"
163
- }
164
- }
165
- </script>
166
-
167
- <script type="module">
168
- import * as THREE from 'https://unpkg.com/three@0.157.0/build/three.module.js';
169
- import { OrbitControls } from 'https://unpkg.com/three@0.157.0/examples/jsm/controls/OrbitControls.js';
170
- import {
171
- circularLayout,
172
- kamadaKawaiLayout,
173
- springLayout,
174
- forceatlas2Layout,
175
- completeGraph,
176
- cycleGraph,
177
- starGraph,
178
- wheelGraph,
179
- gridGraph,
180
- bipartiteGraph,
181
- scaleFreeGraph,
182
- randomGraph
183
- } from './layout.js';
184
-
185
- // Viewport configurations
186
- const viewports = [
187
- { id: 'viewport1', layout: 'spherical', scene: null, camera: null, renderer: null, controls: null },
188
- { id: 'viewport2', layout: 'kamadaKawai', scene: null, camera: null, renderer: null, controls: null },
189
- { id: 'viewport3', layout: 'spring', scene: null, camera: null, renderer: null, controls: null },
190
- { id: 'viewport4', layout: 'forceatlas2', scene: null, camera: null, renderer: null, controls: null }
191
- ];
192
-
193
- // Shared graph data
194
- let currentGraph = null;
195
- let sharedNodeMaterial = null;
196
- let sharedEdgeMaterial = null;
197
-
198
- // Create materials
199
- function createMaterials() {
200
- sharedNodeMaterial = new THREE.MeshPhongMaterial({
201
- vertexColors: true,
202
- emissive: 0x222222,
203
- shininess: 100
204
- });
205
-
206
- sharedEdgeMaterial = new THREE.LineBasicMaterial({
207
- color: 0x4466ff,
208
- transparent: true,
209
- opacity: 0.3
210
- });
211
- }
212
-
213
- // Initialize viewport
214
- function initViewport(viewport) {
215
- const container = document.getElementById(viewport.id);
216
- const rect = container.getBoundingClientRect();
217
-
218
- // Scene
219
- viewport.scene = new THREE.Scene();
220
- viewport.scene.background = new THREE.Color(0x111111);
221
-
222
- // Camera
223
- viewport.camera = new THREE.PerspectiveCamera(
224
- 60,
225
- rect.width / rect.height,
226
- 0.1,
227
- 2000
228
- );
229
- viewport.camera.position.set(300, 200, 400);
230
-
231
- // Renderer
232
- viewport.renderer = new THREE.WebGLRenderer({ antialias: true });
233
- viewport.renderer.setSize(rect.width, rect.height);
234
- container.appendChild(viewport.renderer.domElement);
235
-
236
- // Controls
237
- viewport.controls = new OrbitControls(viewport.camera, viewport.renderer.domElement);
238
- viewport.controls.enableDamping = true;
239
- viewport.controls.dampingFactor = 0.05;
240
-
241
- // Lighting
242
- const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
243
- viewport.scene.add(ambientLight);
244
-
245
- const directionalLight = new THREE.DirectionalLight(0xffffff, 0.4);
246
- directionalLight.position.set(1, 1, 1);
247
- viewport.scene.add(directionalLight);
248
-
249
- // Groups
250
- viewport.nodeGroup = new THREE.Group();
251
- viewport.edgeGroup = new THREE.Group();
252
- viewport.scene.add(viewport.nodeGroup);
253
- viewport.scene.add(viewport.edgeGroup);
254
-
255
- }
256
-
257
- // Create graph
258
- function createGraph(type) {
259
- switch (type) {
260
- case 'complete':
261
- return completeGraph(8);
262
- case 'cycle':
263
- return cycleGraph(20);
264
- case 'star':
265
- return starGraph(15);
266
- case 'wheel':
267
- return wheelGraph(12);
268
- case 'grid':
269
- return gridGraph(4, 4);
270
- case 'bipartite':
271
- return bipartiteGraph(8, 7, 0.4, Date.now());
272
- case 'scalefree':
273
- return scaleFreeGraph(20, 2, Date.now());
274
- case 'random':
275
- return randomGraph(20, 0.15, Date.now());
276
- default:
277
- return gridGraph(4, 4);
278
- }
279
- }
280
-
281
- // Get layout positions
282
- function getLayoutPositions(layoutType, graph) {
283
- const scale = 150;
284
- const center = [0, 0, 0];
285
-
286
- switch (layoutType) {
287
- case 'spherical':
288
- return circularLayout(graph, scale, center, 3);
289
- case 'kamadaKawai':
290
- return kamadaKawaiLayout(graph, null, null, 'weight', scale/150, center, 3);
291
- case 'spring':
292
- return springLayout(graph, 1, null, null, 50, scale/150, center, 3);
293
- case 'forceatlas2':
294
- return forceatlas2Layout(graph, null, 100, 1.0, 2.0, 1.0, false, false, null, null, null, false, false, null, 3);
295
- default:
296
- return circularLayout(graph, scale, center, 3);
297
- }
298
- }
299
-
300
- // Visualize graph in viewport
301
- function visualizeInViewport(viewport, graph, positions) {
302
- // Clear previous
303
- viewport.nodeGroup.clear();
304
- viewport.edgeGroup.clear();
305
-
306
- const nodes = graph.nodes();
307
- const edges = graph.edges();
308
-
309
- // Calculate node colors based on degree
310
- const degrees = {};
311
- nodes.forEach(node => degrees[node] = 0);
312
- edges.forEach(([u, v]) => {
313
- degrees[u]++;
314
- degrees[v]++;
315
- });
316
- const maxDegree = Math.max(...Object.values(degrees), 1);
317
-
318
- // Create nodes
319
- const nodeGeometry = new THREE.SphereGeometry(6, 16, 16);
320
- const nodeColors = [];
321
- const color = new THREE.Color();
322
-
323
- nodes.forEach((node, i) => {
324
- const degree = degrees[node];
325
- color.setHSL(0.6 - (degree / maxDegree) * 0.6, 0.8, 0.5);
326
-
327
- for (let j = 0; j < nodeGeometry.attributes.position.count; j++) {
328
- nodeColors.push(color.r, color.g, color.b);
329
- }
330
- });
331
-
332
- nodeGeometry.setAttribute('color', new THREE.Float32BufferAttribute(nodeColors, 3));
333
-
334
- nodes.forEach((node, i) => {
335
- const mesh = new THREE.Mesh(nodeGeometry.clone(), sharedNodeMaterial.clone());
336
- const pos = positions[node];
337
- mesh.position.set(pos[0], pos[1], pos[2]);
338
- viewport.nodeGroup.add(mesh);
339
- });
340
-
341
- // Create edges
342
- edges.forEach(([source, target]) => {
343
- const sourcePos = positions[source];
344
- const targetPos = positions[target];
345
-
346
- const points = [
347
- new THREE.Vector3(sourcePos[0], sourcePos[1], sourcePos[2]),
348
- new THREE.Vector3(targetPos[0], targetPos[1], targetPos[2])
349
- ];
350
-
351
- const geometry = new THREE.BufferGeometry().setFromPoints(points);
352
- const line = new THREE.Line(geometry, sharedEdgeMaterial);
353
- viewport.edgeGroup.add(line);
354
- });
355
- }
356
-
357
- // Update all viewports
358
- function updateAllViewports() {
359
- if (!currentGraph) return;
360
-
361
- const nodes = currentGraph.nodes();
362
- const edges = currentGraph.edges();
363
- document.getElementById('nodeCount').textContent = nodes.length;
364
- document.getElementById('edgeCount').textContent = edges.length;
365
-
366
- viewports.forEach(viewport => {
367
- const positions = getLayoutPositions(viewport.layout, currentGraph);
368
- visualizeInViewport(viewport, currentGraph, positions);
369
- });
370
- }
371
-
372
- // Sync cameras
373
- function syncCameras() {
374
- const sourceCamera = viewports[0].camera;
375
- const sourceControls = viewports[0].controls;
376
-
377
- viewports.slice(1).forEach(viewport => {
378
- viewport.camera.position.copy(sourceCamera.position);
379
- viewport.camera.quaternion.copy(sourceCamera.quaternion);
380
- viewport.controls.target.copy(sourceControls.target);
381
- viewport.controls.update();
382
- });
383
- }
384
-
385
- // Initialize
386
- createMaterials();
387
- viewports.forEach(viewport => initViewport(viewport));
388
-
389
- // Create initial graph
390
- currentGraph = createGraph('grid');
391
- updateAllViewports();
392
-
393
- // Controls
394
- const controlPanel = document.getElementById('controls');
395
- const graphTypeSelect = document.getElementById('graphType');
396
- const autoRotateCheckbox = document.getElementById('autoRotate');
397
-
398
- document.getElementById('regenerate').addEventListener('click', () => {
399
- currentGraph = createGraph(graphTypeSelect.value);
400
- updateAllViewports();
401
- });
402
-
403
- document.getElementById('syncCameras').addEventListener('click', syncCameras);
404
-
405
- graphTypeSelect.addEventListener('change', () => {
406
- currentGraph = createGraph(graphTypeSelect.value);
407
- updateAllViewports();
408
- });
409
-
410
- // Hide controls when clicking outside
411
- let controlsVisible = true;
412
- document.addEventListener('click', (e) => {
413
- if (e.target.closest('#controls')) return;
414
- controlsVisible = !controlsVisible;
415
- controlPanel.style.display = controlsVisible ? 'block' : 'none';
416
- });
417
-
418
- // Animation loop
419
- function animate() {
420
- requestAnimationFrame(animate);
421
-
422
- viewports.forEach(viewport => {
423
- if (autoRotateCheckbox.checked) {
424
- viewport.nodeGroup.rotation.y += 0.001;
425
- viewport.edgeGroup.rotation.y += 0.001;
426
- }
427
-
428
- viewport.controls.update();
429
- viewport.renderer.render(viewport.scene, viewport.camera);
430
- });
431
- }
432
-
433
- // Handle window resize
434
- window.addEventListener('resize', () => {
435
- viewports.forEach(viewport => {
436
- const container = document.getElementById(viewport.id);
437
- const rect = container.getBoundingClientRect();
438
-
439
- viewport.camera.aspect = rect.width / rect.height;
440
- viewport.camera.updateProjectionMatrix();
441
- viewport.renderer.setSize(rect.width, rect.height);
442
- });
443
- });
444
-
445
- animate();
446
- </script>
447
- </body>
448
- </html>