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.0 ">
6+ < title > 3D Route Planning with Constraints and Keepouts</ title >
7+ < style >
8+ body {
9+ margin : 0 ;
10+ overflow : hidden;
11+ font-family : Arial, sans-serif;
12+ }
13+ # container {
14+ position : relative;
15+ width : 100vw ;
16+ height : 100vh ;
17+ }
18+ # info {
19+ position : absolute;
20+ top : 10px ;
21+ left : 10px ;
22+ background : rgba (255 , 255 , 255 , 0.8 );
23+ padding : 10px ;
24+ border-radius : 5px ;
25+ max-width : 300px ;
26+ z-index : 100 ;
27+ }
28+ # controls {
29+ position : absolute;
30+ bottom : 10px ;
31+ left : 10px ;
32+ background : rgba (255 , 255 , 255 , 0.8 );
33+ padding : 10px ;
34+ border-radius : 5px ;
35+ z-index : 100 ;
36+ }
37+ button {
38+ margin : 5px ;
39+ padding : 5px 10px ;
40+ cursor : pointer;
41+ }
42+ .legend {
43+ display : flex;
44+ align-items : center;
45+ margin : 5px 0 ;
46+ }
47+ .legend-color {
48+ width : 12px ;
49+ height : 12px ;
50+ margin-right : 5px ;
51+ border : 1px solid # 000 ;
52+ }
53+ </ style >
54+ </ head >
55+ < body >
56+ < div id ="container ">
57+ < div id ="info ">
58+ < h3 > 3D Route Planning</ h3 >
59+ < p > This visualization shows a 3D routing solution with constraints and keepouts.</ p >
60+ < div class ="legend ">
61+ < div class ="legend-color " style ="background-color: #ff0000; "> </ div >
62+ < span > Source Node</ span >
63+ </ div >
64+ < div class ="legend ">
65+ < div class ="legend-color " style ="background-color: #0000ff; "> </ div >
66+ < span > Steiner Node</ span >
67+ </ div >
68+ < div class ="legend ">
69+ < div class ="legend-color " style ="background-color: #00ff00; "> </ div >
70+ < span > Terminal Node</ span >
71+ </ div >
72+ < div class ="legend ">
73+ < div class ="legend-color " style ="background-color: #ffc0cb; "> </ div >
74+ < span > Keepout Zone</ span >
75+ </ div >
76+ < p > < strong > Wirelength:</ strong > 7592.00 units</ p >
77+ < p > < strong > Total Nodes:</ strong > 13</ p >
78+ < p > < strong > Terminals:</ strong > 7</ p >
79+ < p > < strong > Steiner Points:</ strong > 5</ p >
80+ </ div >
81+ < div id ="controls ">
82+ < button id ="toggleKeepouts "> Toggle Keepouts</ button >
83+ < button id ="toggleLabels "> Toggle Labels</ button >
84+ < button id ="resetView "> Reset View</ button >
85+ </ div >
86+ </ div >
87+
88+ < script src ="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js "> </ script >
89+ < script src ="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js "> </ script >
90+ < script >
91+ // Initialize Three.js scene
92+ const scene = new THREE . Scene ( ) ;
93+ scene . background = new THREE . Color ( 0xf0f0f0 ) ;
94+
95+ const camera = new THREE . PerspectiveCamera ( 75 , window . innerWidth / window . innerHeight , 0.1 , 1000 ) ;
96+ camera . position . set ( 50 , 50 , 50 ) ;
97+
98+ const renderer = new THREE . WebGLRenderer ( { antialias : true } ) ;
99+ renderer . setSize ( window . innerWidth , window . innerHeight ) ;
100+ document . getElementById ( 'container' ) . appendChild ( renderer . domElement ) ;
101+
102+ const controls = new THREE . OrbitControls ( camera , renderer . domElement ) ;
103+ controls . enableDamping = true ;
104+ controls . dampingFactor = 0.05 ;
105+
106+ // Add lighting
107+ const ambientLight = new THREE . AmbientLight ( 0xffffff , 0.6 ) ;
108+ scene . add ( ambientLight ) ;
109+
110+ const directionalLight = new THREE . DirectionalLight ( 0xffffff , 0.8 ) ;
111+ directionalLight . position . set ( 50 , 50 , 50 ) ;
112+ scene . add ( directionalLight ) ;
113+
114+ // Define node types
115+ const NODE_TYPES = {
116+ SOURCE : { color : 0xff0000 , size : 1.2 } ,
117+ STEINER : { color : 0x0000ff , size : 1.0 } ,
118+ TERMINAL : { color : 0x00ff00 , size : 1.0 }
119+ } ;
120+
121+ // Define connection types
122+ const CONNECTION_TYPES = {
123+ DIRECT : { color : 0x00ff00 , width : 0.5 } ,
124+ ROUTED : { color : 0xff0000 , width : 0.3 } ,
125+ CONSTRAINT : { color : 0xffa500 , width : 0.3 } ,
126+ ALTERNATE : { color : 0x0000ff , width : 0.3 }
127+ } ;
128+
129+ // Define keepout zones
130+ const keepouts = [
131+ {
132+ position : { x : 567.7 , y : 0 , z : 367.9 } ,
133+ size : { x : 90.1 , y : 5 , z : 150.2 } ,
134+ visible : true
135+ } ,
136+ {
137+ position : { x : 237.2 , y : 0 , z : 217.7 } ,
138+ size : { x : 90.1 , y : 5 , z : 90.1 } ,
139+ visible : true
140+ }
141+ ] ;
142+
143+ // Define nodes based on the SVG data
144+ const nodes = [
145+ { id : 'S' , type : NODE_TYPES . SOURCE , position : { x : 27 , y : 0 , z : 1728 } } ,
146+ { id : 'T1' , type : NODE_TYPES . TERMINAL , position : { x : 2106 , y : 0 , z : 704 } } ,
147+ { id : 'S1' , type : NODE_TYPES . STEINER , position : { x : 1620 , y : 0 , z : 704 } } ,
148+ { id : 'T2' , type : NODE_TYPES . TERMINAL , position : { x : 1620 , y : 0 , z : 320 } } ,
149+ { id : 'T3' , type : NODE_TYPES . TERMINAL , position : { x : 1863 , y : 0 , z : 1856 } } ,
150+ { id : 'S2' , type : NODE_TYPES . STEINER , position : { x : 1134 , y : 0 , z : 832 } } ,
151+ { id : 'T4' , type : NODE_TYPES . TERMINAL , position : { x : 1134 , y : 0 , z : 832 } } ,
152+ { id : 'S3' , type : NODE_TYPES . STEINER , position : { x : 1134 , y : 0 , z : 704 } } ,
153+ { id : 'T5' , type : NODE_TYPES . TERMINAL , position : { x : 648 , y : 0 , z : 192 } } ,
154+ { id : 'S4' , type : NODE_TYPES . STEINER , position : { x : 1134 , y : 0 , z : 1216 } } ,
155+ { id : 'T6' , type : NODE_TYPES . TERMINAL , position : { x : 1377 , y : 0 , z : 1216 } } ,
156+ { id : 'S5' , type : NODE_TYPES . STEINER , position : { x : 405 , y : 0 , z : 1344 } } ,
157+ { id : 'T7' , type : NODE_TYPES . TERMINAL , position : { x : 405 , y : 0 , z : 1344 } }
158+ ] ;
159+
160+ // Define connections based on the SVG data
161+ const connections = [
162+ { from : 'S' , to : 'T3' , type : CONNECTION_TYPES . DIRECT } ,
163+ { from : 'S' , to : 'S5' , type : CONNECTION_TYPES . ROUTED } ,
164+ { from : 'S5' , to : 'S4' , type : CONNECTION_TYPES . ROUTED } ,
165+ { from : 'S5' , to : 'S5' , type : CONNECTION_TYPES . CONSTRAINT } ,
166+ { from : 'S4' , to : 'S2' , type : CONNECTION_TYPES . ROUTED } ,
167+ { from : 'S4' , to : 'T6' , type : CONNECTION_TYPES . CONSTRAINT } ,
168+ { from : 'S2' , to : 'S2' , type : CONNECTION_TYPES . ALTERNATE } ,
169+ { from : 'S2' , to : 'S3' , type : CONNECTION_TYPES . ROUTED } ,
170+ { from : 'S3' , to : 'T1' , type : CONNECTION_TYPES . ROUTED } ,
171+ { from : 'S3' , to : 'T5' , type : CONNECTION_TYPES . ROUTED } ,
172+ { from : 'S1' , to : 'T1' , type : CONNECTION_TYPES . ALTERNATE } ,
173+ { from : 'S1' , to : 'T2' , type : CONNECTION_TYPES . ROUTED }
174+ ] ;
175+
176+ // Create a lookup for nodes by ID
177+ const nodeMap = { } ;
178+ nodes . forEach ( node => {
179+ nodeMap [ node . id ] = node ;
180+ } ) ;
181+
182+ // Scale factor to normalize coordinates for 3D visualization
183+ const scaleFactor = 0.01 ;
184+
185+ // Create and add nodes to the scene
186+ const nodeObjects = [ ] ;
187+ nodes . forEach ( node => {
188+ const geometry = new THREE . SphereGeometry ( node . type . size , 16 , 16 ) ;
189+ const material = new THREE . MeshPhongMaterial ( { color : node . type . color } ) ;
190+ const sphere = new THREE . Mesh ( geometry , material ) ;
191+
192+ // Scale and position the node
193+ sphere . position . set (
194+ node . position . x * scaleFactor ,
195+ node . position . y ,
196+ node . position . z * scaleFactor
197+ ) ;
198+
199+ // Store reference to the node
200+ sphere . userData = { nodeId : node . id , nodeType : node . type } ;
201+ nodeObjects . push ( sphere ) ;
202+ scene . add ( sphere ) ;
203+
204+ // Add label
205+ const canvas = document . createElement ( 'canvas' ) ;
206+ const context = canvas . getContext ( '2d' ) ;
207+ canvas . width = 128 ;
208+ canvas . height = 64 ;
209+ context . fillStyle = '#000000' ;
210+ context . font = '24px Arial' ;
211+ context . textAlign = 'center' ;
212+ context . fillText ( node . id , 64 , 40 ) ;
213+
214+ const texture = new THREE . CanvasTexture ( canvas ) ;
215+ const labelMaterial = new THREE . SpriteMaterial ( { map : texture } ) ;
216+ const label = new THREE . Sprite ( labelMaterial ) ;
217+ label . position . set (
218+ node . position . x * scaleFactor ,
219+ node . position . y + 2 ,
220+ node . position . z * scaleFactor
221+ ) ;
222+ label . scale . set ( 4 , 2 , 1 ) ;
223+ label . userData = { nodeId : node . id } ;
224+ scene . add ( label ) ;
225+ } ) ;
226+
227+ // Create and add connections to the scene
228+ const connectionObjects = [ ] ;
229+ connections . forEach ( connection => {
230+ const fromNode = nodeMap [ connection . from ] ;
231+ const toNode = nodeMap [ connection . to ] ;
232+
233+ if ( fromNode && toNode ) {
234+ // Create a tube geometry for the connection
235+ const points = [ ] ;
236+ points . push ( new THREE . Vector3 (
237+ fromNode . position . x * scaleFactor ,
238+ fromNode . position . y ,
239+ fromNode . position . z * scaleFactor
240+ ) ) ;
241+
242+ // For self-connections, create a loop
243+ if ( connection . from === connection . to ) {
244+ points . push ( new THREE . Vector3 (
245+ fromNode . position . x * scaleFactor + 2 ,
246+ fromNode . position . y + 3 ,
247+ fromNode . position . z * scaleFactor
248+ ) ) ;
249+ points . push ( new THREE . Vector3 (
250+ fromNode . position . x * scaleFactor ,
251+ fromNode . position . y + 3 ,
252+ fromNode . position . z * scaleFactor + 2
253+ ) ) ;
254+ points . push ( new THREE . Vector3 (
255+ fromNode . position . x * scaleFactor - 2 ,
256+ fromNode . position . y + 3 ,
257+ fromNode . position . z * scaleFactor
258+ ) ) ;
259+ }
260+
261+ points . push ( new THREE . Vector3 (
262+ toNode . position . x * scaleFactor ,
263+ toNode . position . y ,
264+ toNode . position . z * scaleFactor
265+ ) ) ;
266+
267+ const curve = new THREE . CatmullRomCurve3 ( points ) ;
268+ const geometry = new THREE . TubeGeometry ( curve , 20 , connection . type . width , 8 , false ) ;
269+ const material = new THREE . MeshPhongMaterial ( { color : connection . type . color } ) ;
270+ const tube = new THREE . Mesh ( geometry , material ) ;
271+
272+ connectionObjects . push ( tube ) ;
273+ scene . add ( tube ) ;
274+ }
275+ } ) ;
276+
277+ // Create and add keepout zones to the scene
278+ const keepoutObjects = [ ] ;
279+ keepouts . forEach ( keepout => {
280+ const geometry = new THREE . BoxGeometry (
281+ keepout . size . x * scaleFactor ,
282+ keepout . size . y ,
283+ keepout . size . z * scaleFactor
284+ ) ;
285+ const material = new THREE . MeshPhongMaterial ( {
286+ color : 0xffc0cb ,
287+ transparent : true ,
288+ opacity : 0.5
289+ } ) ;
290+ const box = new THREE . Mesh ( geometry , material ) ;
291+ box . position . set (
292+ keepout . position . x * scaleFactor ,
293+ keepout . position . y ,
294+ keepout . position . z * scaleFactor
295+ ) ;
296+ box . userData = { keepoutId : keepoutObjects . length } ;
297+ keepoutObjects . push ( box ) ;
298+ scene . add ( box ) ;
299+ } ) ;
300+
301+ // Add a grid to help with orientation
302+ const gridHelper = new THREE . GridHelper ( 30 , 30 , 0x444444 , 0x888888 ) ;
303+ scene . add ( gridHelper ) ;
304+
305+ // Add axes helper
306+ const axesHelper = new THREE . AxesHelper ( 10 ) ;
307+ scene . add ( axesHelper ) ;
308+
309+ // Animation loop
310+ function animate ( ) {
311+ requestAnimationFrame ( animate ) ;
312+ controls . update ( ) ;
313+ renderer . render ( scene , camera ) ;
314+ }
315+ animate ( ) ;
316+
317+ // Handle window resize
318+ window . addEventListener ( 'resize' , ( ) => {
319+ camera . aspect = window . innerWidth / window . innerHeight ;
320+ camera . updateProjectionMatrix ( ) ;
321+ renderer . setSize ( window . innerWidth , window . innerHeight ) ;
322+ } ) ;
323+
324+ // Control handlers
325+ document . getElementById ( 'toggleKeepouts' ) . addEventListener ( 'click' , ( ) => {
326+ keepoutObjects . forEach ( keepout => {
327+ keepout . visible = ! keepout . visible ;
328+ } ) ;
329+ } ) ;
330+
331+ document . getElementById ( 'toggleLabels' ) . addEventListener ( 'click' , ( ) => {
332+ scene . children . forEach ( child => {
333+ if ( child . type === 'Sprite' ) {
334+ child . visible = ! child . visible ;
335+ }
336+ } ) ;
337+ } ) ;
338+
339+ document . getElementById ( 'resetView' ) . addEventListener ( 'click' , ( ) => {
340+ camera . position . set ( 50 , 50 , 50 ) ;
341+ controls . reset ( ) ;
342+ } ) ;
343+ </ script >
344+ </ body >
345+ </ html >
0 commit comments