|
39 | 39 | } |
40 | 40 | console.log('Topology data :', data); |
41 | 41 |
|
42 | | - // Calculate preset positions for deterministic layout |
| 42 | + // Calculate preset positions for deterministic layout with adaptive scaling |
43 | 43 | function calculatePresetPositions(topologyData) { |
44 | 44 | const positions = {}; |
45 | 45 | const serviceNodes = topologyData.nodes.filter(n => n.data.type === 'service'); |
46 | 46 | const segmentNodes = topologyData.nodes.filter(n => n.data.type === 'segment'); |
47 | 47 | const siteNodes = topologyData.nodes.filter(n => n.data.type === 'site'); |
48 | 48 | const circuitNodes = topologyData.nodes.filter(n => n.data.type === 'circuit'); |
49 | 49 |
|
50 | | - // Calculate center X for the canvas |
51 | | - const centerX = 500; |
| 50 | + // Analyze topology complexity to determine spacing |
| 51 | + const maxSitesInSegment = Math.max(...segmentNodes.map(seg => |
| 52 | + siteNodes.filter(s => s.data.parent === seg.data.id).length |
| 53 | + ), 1); |
| 54 | + const maxCircuitsInSegment = Math.max(...segmentNodes.map(seg => |
| 55 | + circuitNodes.filter(c => c.data.parent === seg.data.id).length |
| 56 | + ), 1); |
| 57 | + |
| 58 | + // Adaptive spacing based on complexity |
| 59 | + // For larger topologies, use tighter spacing |
| 60 | + let baseNodeSpacing = 180; // Base spacing between nodes |
| 61 | + let baseSegmentSpacing = 500; // Base spacing between segments |
| 62 | + |
| 63 | + // Scale down spacing for complex topologies |
| 64 | + if (maxSitesInSegment > 3 || maxCircuitsInSegment > 3) { |
| 65 | + const complexityFactor = Math.max(maxSitesInSegment, maxCircuitsInSegment); |
| 66 | + baseNodeSpacing = Math.max(140, 180 - (complexityFactor - 3) * 15); |
| 67 | + baseSegmentSpacing = Math.max(400, 500 - (complexityFactor - 3) * 30); |
| 68 | + } |
| 69 | + |
| 70 | + // Calculate total width needed for each segment |
| 71 | + const segmentWidths = segmentNodes.map(segment => { |
| 72 | + const segmentId = segment.data.id; |
| 73 | + const segmentSites = siteNodes.filter(s => s.data.parent === segmentId); |
| 74 | + const segmentCircuits = circuitNodes.filter(c => c.data.parent === segmentId); |
| 75 | + |
| 76 | + const maxNodesInRow = Math.max(segmentSites.length, segmentCircuits.length); |
| 77 | + return maxNodesInRow > 1 ? (maxNodesInRow - 1) * baseNodeSpacing : 0; |
| 78 | + }); |
| 79 | + |
| 80 | + // Calculate total layout width and determine segment positions |
| 81 | + const totalWidth = segmentWidths.reduce((sum, w) => sum + w, 0) + |
| 82 | + (segmentNodes.length - 1) * baseSegmentSpacing; |
| 83 | + |
| 84 | + // Calculate center X for the canvas (use larger value for wide topologies) |
| 85 | + const centerX = Math.max(500, totalWidth / 2); |
52 | 86 |
|
53 | 87 | // Service path at top center |
54 | 88 | if (serviceNodes.length > 0) { |
55 | 89 | positions[serviceNodes[0].data.id] = { x: centerX, y: 50 }; |
56 | 90 | } |
57 | 91 |
|
58 | | - // Segments positioning |
59 | | - let segmentSpacing = 500; |
60 | | - |
61 | | - // If only one segment, center it |
62 | | - if (segmentNodes.length === 1) { |
63 | | - positions[segmentNodes[0].data.id] = { x: centerX, y: 220 }; |
64 | | - } else { |
65 | | - // Multiple segments - space them out horizontally |
66 | | - const segmentStartX = centerX - (segmentNodes.length - 1) * segmentSpacing / 2; |
67 | | - segmentNodes.forEach((segment, i) => { |
68 | | - positions[segment.data.id] = { |
69 | | - x: segmentStartX + i * segmentSpacing, |
70 | | - y: 220 |
71 | | - }; |
72 | | - }); |
73 | | - } |
| 92 | + // Position segments |
| 93 | + let currentX = centerX - totalWidth / 2; |
74 | 94 |
|
75 | | - // Sites and circuits within segments |
76 | 95 | segmentNodes.forEach((segment, segmentIndex) => { |
77 | 96 | const segmentId = segment.data.id; |
78 | | - const segmentX = positions[segmentId].x; |
| 97 | + const segmentWidth = segmentWidths[segmentIndex]; |
| 98 | + const segmentCenterX = currentX + segmentWidth / 2; |
| 99 | + |
| 100 | + positions[segmentId] = { x: segmentCenterX, y: 220 }; |
79 | 101 |
|
80 | 102 | // Get sites and circuits for this segment |
81 | 103 | const segmentSites = siteNodes.filter(s => s.data.parent === segmentId); |
82 | 104 | const segmentCircuits = circuitNodes.filter(c => c.data.parent === segmentId); |
83 | 105 |
|
84 | | - // Adjust spacing based on number of sites and segments |
85 | | - let siteSpacing = 180; |
86 | | - |
87 | | - // For single segment layouts, use more compact spacing |
88 | | - if (segmentNodes.length === 1) { |
89 | | - if (segmentSites.length === 2) { |
90 | | - siteSpacing = 220; |
91 | | - } else if (segmentSites.length === 1) { |
92 | | - siteSpacing = 0; |
93 | | - } |
| 106 | + // Position sites horizontally within segment |
| 107 | + if (segmentSites.length === 1) { |
| 108 | + positions[segmentSites[0].data.id] = { |
| 109 | + x: segmentCenterX, |
| 110 | + y: 360 |
| 111 | + }; |
94 | 112 | } else { |
95 | | - // Multiple segments |
96 | | - if (segmentSites.length === 2) { |
97 | | - siteSpacing = 250; |
98 | | - } else if (segmentSites.length === 1) { |
99 | | - siteSpacing = 0; |
100 | | - } |
| 113 | + const siteStartX = segmentCenterX - (segmentSites.length - 1) * baseNodeSpacing / 2; |
| 114 | + segmentSites.forEach((site, i) => { |
| 115 | + positions[site.data.id] = { |
| 116 | + x: siteStartX + i * baseNodeSpacing, |
| 117 | + y: 360 |
| 118 | + }; |
| 119 | + }); |
101 | 120 | } |
102 | 121 |
|
103 | | - // Position sites horizontally within segment, centered around segmentX |
104 | | - const siteStartX = segmentX - (segmentSites.length - 1) * siteSpacing / 2; |
105 | | - segmentSites.forEach((site, i) => { |
106 | | - positions[site.data.id] = { |
107 | | - x: siteStartX + i * siteSpacing, |
108 | | - y: 360 // Sites above circuits |
109 | | - }; |
110 | | - }); |
111 | | - |
112 | | - // Adjust circuit spacing based on number of circuits |
113 | | - let circuitSpacing = 180; |
114 | | - |
| 122 | + // Position circuits horizontally within segment |
115 | 123 | if (segmentCircuits.length === 1) { |
116 | | - // Single circuit - center it |
117 | | - circuitSpacing = 0; |
118 | | - } else if (segmentCircuits.length === 2) { |
119 | | - circuitSpacing = segmentNodes.length === 1 ? 220 : 250; |
| 124 | + positions[segmentCircuits[0].data.id] = { |
| 125 | + x: segmentCenterX, |
| 126 | + y: 460 |
| 127 | + }; |
| 128 | + } else { |
| 129 | + const circuitStartX = segmentCenterX - (segmentCircuits.length - 1) * baseNodeSpacing / 2; |
| 130 | + segmentCircuits.forEach((circuit, i) => { |
| 131 | + positions[circuit.data.id] = { |
| 132 | + x: circuitStartX + i * baseNodeSpacing, |
| 133 | + y: 460 |
| 134 | + }; |
| 135 | + }); |
120 | 136 | } |
121 | 137 |
|
122 | | - // Position circuits between/below sites, centered around segmentX |
123 | | - const circuitStartX = segmentX - (segmentCircuits.length - 1) * circuitSpacing / 2; |
124 | | - segmentCircuits.forEach((circuit, i) => { |
125 | | - positions[circuit.data.id] = { |
126 | | - x: circuitStartX + i * circuitSpacing, |
127 | | - y: 460 // Circuits below sites |
128 | | - }; |
129 | | - }); |
| 138 | + // Move to next segment position |
| 139 | + currentX += segmentWidth + baseSegmentSpacing; |
130 | 140 | }); |
131 | 141 |
|
132 | 142 | return positions; |
|
0 commit comments