singhn9 commited on
Commit
57a2d92
·
verified ·
1 Parent(s): 99ebb1a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -43
app.py CHANGED
@@ -225,7 +225,6 @@ function draw() {
225
 
226
  const radius = Math.min(w, h) * 0.36;
227
 
228
- // Compute node positions with abbrev
229
  const n = NODES.length;
230
  function angleFor(i){ return (i / n) * 2 * Math.PI; }
231
 
@@ -244,27 +243,32 @@ function draw() {
244
  const nameToIndex = {};
245
  NODES.forEach((nm, i) => nameToIndex[nm] = i);
246
 
247
- // --------------------------------------------------------------
248
- // 1. Draw circles FIRST (bottom layer)
249
- // --------------------------------------------------------------
250
  const nodeCircleGroup = svg.append("g")
251
  .attr("class", "node-circles")
252
  .selectAll("g")
253
  .data(nodePos)
254
  .enter()
255
  .append("g")
256
- .attr("transform", d => `translate(${d.x},${d.y})`);
 
257
 
 
 
 
258
  nodeCircleGroup.append("circle")
259
  .attr("r", 16)
260
  .attr("fill", d => NODE_TYPE[d.name] === "amc" ? "#2b6fa6" : "#f2c88d")
261
  .attr("stroke", "#222")
262
  .attr("stroke-width", 1)
263
- .style("cursor", "pointer");
 
264
 
265
- // --------------------------------------------------------------
266
- // ARC DRAWING (middle layers)
267
- // --------------------------------------------------------------
268
  function bezierPath(x0,y0,x1,y1,above=true){
269
  const mx = (x0+x1)/2;
270
  const my = (y0+y1)/2;
@@ -333,8 +337,6 @@ function draw() {
333
  .attr("data-tgt", tname);
334
  });
335
 
336
-
337
- // original loop arcs (keep logic but style to violet and slightly thicker)
338
  const loopGroup = svg.append("g").attr("class", "loops");
339
  LOOPS.forEach(lp => {
340
  const a = lp[0], c = lp[1], b = lp[2];
@@ -352,16 +354,13 @@ function draw() {
352
  loopGroup.append("path")
353
  .attr("d", path)
354
  .attr("fill", "none")
355
- .attr("stroke", "#9b59b6") // violet to stand out
356
  .attr("stroke-width", 4.2)
357
  .attr("opacity", 1)
358
  .attr("stroke-linecap", "round")
359
  .attr("stroke-linejoin", "round");
360
  });
361
 
362
- // --------------------------------------------------------------
363
- // Overlay group for dynamic highlighted loop paths (drawn on click)
364
- // --------------------------------------------------------------
365
  const loopPathGroup = svg.append("g").attr("class", "loop-path-highlight");
366
 
367
  function drawLoopPathsFor(nodeName) {
@@ -373,11 +372,10 @@ function draw() {
373
  const pA = nodePos[nameToIndex[amcA]];
374
  const pC = nodePos[nameToIndex[company]];
375
  const pB = nodePos[nameToIndex[amcB]];
376
- // draw two-segment polyline that follows straight segments A->C and C->B
377
  loopPathGroup.append("path")
378
  .attr("d", `M${pA.x},${pA.y} L${pC.x},${pC.y} L${pB.x},${pB.y}`)
379
  .attr("fill", "none")
380
- .attr("stroke", "#8e44ad") // slightly different violet for highlight
381
  .attr("stroke-width", 4.5)
382
  .attr("opacity", 0.98)
383
  .attr("stroke-linecap", "round")
@@ -385,9 +383,9 @@ function draw() {
385
  });
386
  }
387
 
388
- // --------------------------------------------------------------
389
- // 2. LABELS AT THE TOP (always visible)
390
- // --------------------------------------------------------------
391
  const labelGroup = svg.append("g")
392
  .attr("class", "node-labels")
393
  .selectAll("text")
@@ -404,11 +402,12 @@ function draw() {
404
  const deg = (d.angle * 180 / Math.PI);
405
  return (deg>-90 && deg<90) ? "start" : "end";
406
  })
 
407
  .text(d => d.abbrev);
408
 
409
- // --------------------------------------------------------------
410
- // LABEL SWITCHING + OPACITY CONTROLS
411
- // --------------------------------------------------------------
412
  function getConnections(nodeName){
413
  let buys = BUYS.filter(x=>x[0]===nodeName || x[1]===nodeName).map(x=>x[0]===nodeName?x[1]:x[0]);
414
  let sells = SELLS.filter(x=>x[0]===nodeName || x[1]===nodeName).map(x=>x[0]===nodeName?x[1]:x[0]);
@@ -443,16 +442,7 @@ function draw() {
443
  sellGroup.selectAll("path").style("opacity", function(){ return isConn(this)?0.98:0.06; });
444
  transferGroup.selectAll("path").style("opacity", function(){ return isConn(this)?0.98:0.06; });
445
 
446
- // keep loopGroup visible but slightly dim non-relevant ones
447
- loopGroup.selectAll("path").style("opacity", function(){
448
- // loops don't have data-src/data-tgt; approximate by checking endpoints
449
- try {
450
- const dstr = this.getAttribute("d") || "";
451
- // simple heuristic: if path contains nodeName coordinate string, highlight it
452
- // (we'll just keep them at 0.9 for readability)
453
- return 0.9;
454
- } catch(e){ return 0.9; }
455
- });
456
  }
457
 
458
  function resetOpacity(){
@@ -463,7 +453,7 @@ function draw() {
463
  transferGroup.selectAll("path").style("opacity",0.7);
464
  loopGroup.selectAll("path").style("opacity",1);
465
 
466
- loopPathGroup.selectAll("*").remove(); // clear highlight overlay
467
  updateLabels(null, new Set());
468
  document.getElementById("info-box").innerHTML =
469
  "<b>Click a node</b> to view details here.";
@@ -497,35 +487,44 @@ function draw() {
497
  showInfo(name);
498
  const connected = getConnections(name);
499
  updateLabels(name, connected);
500
- drawLoopPathsFor(name); // draw overlay highlights for loops involving this node
501
  }
502
 
 
 
 
503
  nodeCircleGroup.on("click", function(event, d){
504
  selectNode(d);
505
  event.stopPropagation();
506
  });
 
507
  labelGroup.on("click", function(event, d){
508
  selectNode(d);
509
  event.stopPropagation();
510
  });
511
 
512
-
513
  document.getElementById("arc-reset").onclick = resetOpacity;
514
- svg.on("click", function(evt){
515
- // Only reset when clicking empty background, not circles, not labels, not paths
516
- if (evt.target === this) {
517
- resetOpacity();
518
- }
519
- });
520
 
 
 
 
521
 
522
  }
523
 
524
  draw();
525
- window.addEventListener("resize", draw);
 
 
 
 
 
 
 
 
526
  </script>
527
  """
528
 
 
529
  def make_arc_html(nodes, node_type, buys, sells, transfers, loops):
530
  nodes_json = json.dumps(nodes)
531
  node_type_json = json.dumps(node_type)
 
225
 
226
  const radius = Math.min(w, h) * 0.36;
227
 
 
228
  const n = NODES.length;
229
  function angleFor(i){ return (i / n) * 2 * Math.PI; }
230
 
 
243
  const nameToIndex = {};
244
  NODES.forEach((nm, i) => nameToIndex[nm] = i);
245
 
246
+ //--------------------------------------------------------------------
247
+ // NODES — FIX #1: disable pointer events on <g>
248
+ //--------------------------------------------------------------------
249
  const nodeCircleGroup = svg.append("g")
250
  .attr("class", "node-circles")
251
  .selectAll("g")
252
  .data(nodePos)
253
  .enter()
254
  .append("g")
255
+ .attr("transform", d => `translate(${d.x},${d.y})`)
256
+ .style("pointer-events", "none"); // ★ FIX ADDED
257
 
258
+ //--------------------------------------------------------------------
259
+ // CIRCLES — FIX #2: reenable pointer events on <circle>
260
+ //--------------------------------------------------------------------
261
  nodeCircleGroup.append("circle")
262
  .attr("r", 16)
263
  .attr("fill", d => NODE_TYPE[d.name] === "amc" ? "#2b6fa6" : "#f2c88d")
264
  .attr("stroke", "#222")
265
  .attr("stroke-width", 1)
266
+ .style("cursor", "pointer")
267
+ .style("pointer-events", "all"); // ★ FIX ADDED
268
 
269
+ //--------------------------------------------------------------------
270
+ // ARC drawing (unchanged)
271
+ //--------------------------------------------------------------------
272
  function bezierPath(x0,y0,x1,y1,above=true){
273
  const mx = (x0+x1)/2;
274
  const my = (y0+y1)/2;
 
337
  .attr("data-tgt", tname);
338
  });
339
 
 
 
340
  const loopGroup = svg.append("g").attr("class", "loops");
341
  LOOPS.forEach(lp => {
342
  const a = lp[0], c = lp[1], b = lp[2];
 
354
  loopGroup.append("path")
355
  .attr("d", path)
356
  .attr("fill", "none")
357
+ .attr("stroke", "#9b59b6")
358
  .attr("stroke-width", 4.2)
359
  .attr("opacity", 1)
360
  .attr("stroke-linecap", "round")
361
  .attr("stroke-linejoin", "round");
362
  });
363
 
 
 
 
364
  const loopPathGroup = svg.append("g").attr("class", "loop-path-highlight");
365
 
366
  function drawLoopPathsFor(nodeName) {
 
372
  const pA = nodePos[nameToIndex[amcA]];
373
  const pC = nodePos[nameToIndex[company]];
374
  const pB = nodePos[nameToIndex[amcB]];
 
375
  loopPathGroup.append("path")
376
  .attr("d", `M${pA.x},${pA.y} L${pC.x},${pC.y} L${pB.x},${pB.y}`)
377
  .attr("fill", "none")
378
+ .attr("stroke", "#8e44ad")
379
  .attr("stroke-width", 4.5)
380
  .attr("opacity", 0.98)
381
  .attr("stroke-linecap", "round")
 
383
  });
384
  }
385
 
386
+ //--------------------------------------------------------------------
387
+ // LABELS FIX #3: enable pointer events
388
+ //--------------------------------------------------------------------
389
  const labelGroup = svg.append("g")
390
  .attr("class", "node-labels")
391
  .selectAll("text")
 
402
  const deg = (d.angle * 180 / Math.PI);
403
  return (deg>-90 && deg<90) ? "start" : "end";
404
  })
405
+ .style("pointer-events", "all") // ★ FIX ADDED
406
  .text(d => d.abbrev);
407
 
408
+ //--------------------------------------------------------------------
409
+ // CLICK / HIGHLIGHT logic
410
+ //--------------------------------------------------------------------
411
  function getConnections(nodeName){
412
  let buys = BUYS.filter(x=>x[0]===nodeName || x[1]===nodeName).map(x=>x[0]===nodeName?x[1]:x[0]);
413
  let sells = SELLS.filter(x=>x[0]===nodeName || x[1]===nodeName).map(x=>x[0]===nodeName?x[1]:x[0]);
 
442
  sellGroup.selectAll("path").style("opacity", function(){ return isConn(this)?0.98:0.06; });
443
  transferGroup.selectAll("path").style("opacity", function(){ return isConn(this)?0.98:0.06; });
444
 
445
+ loopGroup.selectAll("path").style("opacity", 0.9);
 
 
 
 
 
 
 
 
 
446
  }
447
 
448
  function resetOpacity(){
 
453
  transferGroup.selectAll("path").style("opacity",0.7);
454
  loopGroup.selectAll("path").style("opacity",1);
455
 
456
+ loopPathGroup.selectAll("*").remove();
457
  updateLabels(null, new Set());
458
  document.getElementById("info-box").innerHTML =
459
  "<b>Click a node</b> to view details here.";
 
487
  showInfo(name);
488
  const connected = getConnections(name);
489
  updateLabels(name, connected);
490
+ drawLoopPathsFor(name);
491
  }
492
 
493
+ //--------------------------------------------------------------------
494
+ // CLICK HANDLERS — working now
495
+ //--------------------------------------------------------------------
496
  nodeCircleGroup.on("click", function(event, d){
497
  selectNode(d);
498
  event.stopPropagation();
499
  });
500
+
501
  labelGroup.on("click", function(event, d){
502
  selectNode(d);
503
  event.stopPropagation();
504
  });
505
 
 
506
  document.getElementById("arc-reset").onclick = resetOpacity;
 
 
 
 
 
 
507
 
508
+ svg.on("click", function(event){
509
+ if (event.target === this) resetOpacity();
510
+ });
511
 
512
  }
513
 
514
  draw();
515
+
516
+ //-----------------------------------------------------------
517
+ // RESIZE DEBOUNCE (ANTI-DOUBLE-CLICK ISSUE)
518
+ //-----------------------------------------------------------
519
+ let rz;
520
+ window.addEventListener("resize", () => {
521
+ clearTimeout(rz);
522
+ rz = setTimeout(draw, 120);
523
+ });
524
  </script>
525
  """
526
 
527
+
528
  def make_arc_html(nodes, node_type, buys, sells, transfers, loops):
529
  nodes_json = json.dumps(nodes)
530
  node_type_json = json.dumps(node_type)