opencroc 1.4.2 → 1.4.3
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.
- package/dist/web/index.html +44 -13
- package/package.json +1 -1
package/dist/web/index.html
CHANGED
|
@@ -201,36 +201,67 @@ function layoutGraph(){
|
|
|
201
201
|
const w=c.clientWidth||800,h=c.clientHeight||500;
|
|
202
202
|
// Group non-module nodes by module
|
|
203
203
|
const mods=new Map();
|
|
204
|
-
S.modMeta=new Map();
|
|
204
|
+
S.modMeta=new Map();
|
|
205
205
|
for(const n of nodes){
|
|
206
206
|
if(n.type==='module') continue;
|
|
207
207
|
const m=n.module||'other';
|
|
208
208
|
if(!mods.has(m)) mods.set(m,[]);
|
|
209
209
|
mods.get(m).push(n);
|
|
210
210
|
}
|
|
211
|
+
// Sort by size descending
|
|
211
212
|
const keys=[...mods.keys()].sort((a,b)=>(mods.get(b).length-mods.get(a).length));
|
|
212
213
|
const nMods=keys.length||1;
|
|
213
|
-
// Module color palette (10 distinct colors, cycle)
|
|
214
214
|
const palette=['#4ecca3','#e94560','#f39c12','#3498db','#9b59b6','#1abc9c','#e67e22','#2ecc71','#e84393','#00cec9'];
|
|
215
|
-
//
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
const RING_CAP=24; // max nodes per ring layer
|
|
216
|
+
|
|
217
|
+
// Calculate each module's outer radius (for spacing)
|
|
218
|
+
const modRadii=[];
|
|
219
|
+
for(let i=0;i<keys.length;i++){
|
|
220
|
+
const cnt=mods.get(keys[i]).length;
|
|
221
|
+
const rings=Math.ceil(cnt/RING_CAP);
|
|
222
|
+
const outerR=rings===1?Math.max(40,cnt*6):rings*32+20;
|
|
223
|
+
modRadii.push(outerR);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Adaptive base ring: sum of all module diameters / (2*PI) gives minimum perimeter
|
|
227
|
+
const totalDiam=modRadii.reduce((s,r)=>s+r*2+60,0); // 60px gap between modules
|
|
228
|
+
const baseRadius=Math.max(400, totalDiam/(Math.PI*2));
|
|
229
|
+
|
|
230
|
+
// Place modules with angular spacing proportional to their size
|
|
231
|
+
const totalWeight=modRadii.reduce((s,r)=>s+r+30,0);
|
|
232
|
+
let angle=-Math.PI/2;
|
|
218
233
|
for(let i=0;i<keys.length;i++){
|
|
219
234
|
const mn=mods.get(keys[i]); if(!mn)continue;
|
|
220
|
-
const
|
|
235
|
+
const cnt=mn.length;
|
|
236
|
+
const col=palette[i%palette.length];
|
|
237
|
+
const rings=Math.ceil(cnt/RING_CAP);
|
|
238
|
+
const outerR=modRadii[i];
|
|
239
|
+
|
|
240
|
+
// Angular span for this module proportional to its size
|
|
241
|
+
const span=((outerR+30)/totalWeight)*Math.PI*2;
|
|
242
|
+
const modAngle=angle+span/2;
|
|
243
|
+
angle+=span;
|
|
244
|
+
|
|
221
245
|
const mcx=w/2+Math.cos(modAngle)*baseRadius;
|
|
222
246
|
const mcy=h/2+Math.sin(modAngle)*baseRadius;
|
|
223
|
-
|
|
224
|
-
const col=palette[i%palette.length];
|
|
225
|
-
// Store module meta for cluster rendering
|
|
247
|
+
|
|
226
248
|
const modNodeId='module:'+keys[i];
|
|
227
249
|
S.nodePos.set(modNodeId,{x:mcx,y:mcy});
|
|
228
|
-
S.modMeta.set(keys[i],{cx:mcx,cy:mcy,radius:
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
250
|
+
S.modMeta.set(keys[i],{cx:mcx,cy:mcy,radius:outerR+20,color:col,count:cnt});
|
|
251
|
+
|
|
252
|
+
// Spread nodes across concentric rings
|
|
253
|
+
let placed=0;
|
|
254
|
+
for(let ring=0;ring<rings;ring++){
|
|
255
|
+
const nodesInRing=Math.min(RING_CAP,cnt-placed);
|
|
256
|
+
const r=rings===1?Math.max(40,nodesInRing*6):(ring+1)*32;
|
|
257
|
+
for(let j=0;j<nodesInRing;j++){
|
|
258
|
+
const na=(j/nodesInRing)*Math.PI*2;
|
|
259
|
+
S.nodePos.set(mn[placed].id,{x:mcx+Math.cos(na)*r,y:mcy+Math.sin(na)*r});
|
|
260
|
+
placed++;
|
|
261
|
+
}
|
|
232
262
|
}
|
|
233
263
|
}
|
|
264
|
+
|
|
234
265
|
// Auto-center
|
|
235
266
|
if(nodes.length>0&&!S._userPanned){
|
|
236
267
|
let minX=Infinity,maxX=-Infinity,minY=Infinity,maxY=-Infinity;
|