@@ -531,7 +531,11 @@ <h2>{{ title }}</h2>
531531 . attr ( "fill" , textPrimary ) ;
532532 } ) ;
533533
534- // Function to get position for an entry
534+ // Function to get position for an entry with collision detection
535+ const usedPositions = [ ] ;
536+ const minDistance = 30 ; // Minimum distance between points
537+ const maxAttempts = 50 ; // Maximum attempts to find a non-overlapping position
538+
535539 function getEntryPosition ( entry ) {
536540 const quadrantIndex = entry . quadrant ;
537541 const ringIndex = entry . ring ;
@@ -540,13 +544,75 @@ <h2>{{ title }}</h2>
540544 const baseAngle = quadrantIndex * Math . PI / 2 ;
541545 const quadrantAngle = Math . PI / 2 ;
542546
543- // Random angle within the quadrant
544- const angle = baseAngle + ( Math . random ( ) - 0.5 ) * quadrantAngle * 0.8 ;
547+ // Calculate ring boundaries
548+ const innerRadius = ringIndex > 0 ? ringRadii [ ringIndex - 1 ] : 0 ;
549+ const outerRadius = ringRadii [ ringIndex ] ;
550+
551+ let bestPosition = null ;
552+ let attempts = 0 ;
553+
554+ while ( attempts < maxAttempts ) {
555+ // Generate random angle within the quadrant
556+ const angle = baseAngle + ( Math . random ( ) - 0.5 ) * quadrantAngle * 0.8 ;
557+
558+ // Generate random radius within the ring
559+ const entryRadius = innerRadius + ( outerRadius - innerRadius ) * ( 0.2 + Math . random ( ) * 0.6 ) ;
560+
561+ const candidatePosition = {
562+ x : center . x + entryRadius * Math . cos ( angle - Math . PI / 2 ) ,
563+ y : center . y + entryRadius * Math . sin ( angle - Math . PI / 2 )
564+ } ;
565+
566+ // Check for collisions with existing positions
567+ let hasCollision = false ;
568+ for ( const usedPos of usedPositions ) {
569+ const distance = Math . sqrt (
570+ Math . pow ( candidatePosition . x - usedPos . x , 2 ) +
571+ Math . pow ( candidatePosition . y - usedPos . y , 2 )
572+ ) ;
573+ if ( distance < minDistance ) {
574+ hasCollision = true ;
575+ break ;
576+ }
577+ }
578+
579+ if ( ! hasCollision ) {
580+ bestPosition = candidatePosition ;
581+ break ;
582+ }
583+
584+ attempts ++ ;
585+ }
586+
587+ // If we couldn't find a collision-free position, use grid-based fallback
588+ if ( ! bestPosition ) {
589+ bestPosition = getGridBasedPosition ( quadrantIndex , ringIndex ) ;
590+ }
591+
592+ // Store the position for future collision detection
593+ usedPositions . push ( bestPosition ) ;
594+ return bestPosition ;
595+ }
596+
597+ // Fallback grid-based positioning for when collision detection fails
598+ function getGridBasedPosition ( quadrantIndex , ringIndex ) {
599+ const entriesInQuadrantRing = config . entries . filter ( e =>
600+ e . quadrant === quadrantIndex && e . ring === ringIndex
601+ ) ;
602+ const entryIndex = entriesInQuadrantRing . length ;
603+
604+ // Calculate angle for the quadrant
605+ const baseAngle = quadrantIndex * Math . PI / 2 ;
606+ const quadrantAngle = Math . PI / 2 ;
607+
608+ // Distribute entries evenly within the quadrant
609+ const angleStep = quadrantAngle * 0.8 / Math . max ( entriesInQuadrantRing . length + 1 , 4 ) ;
610+ const angle = baseAngle - quadrantAngle * 0.4 + angleStep * ( entryIndex + 1 ) ;
545611
546- // Random radius within the ring
612+ // Calculate ring boundaries
547613 const innerRadius = ringIndex > 0 ? ringRadii [ ringIndex - 1 ] : 0 ;
548614 const outerRadius = ringRadii [ ringIndex ] ;
549- const entryRadius = innerRadius + ( outerRadius - innerRadius ) * ( 0.2 + Math . random ( ) * 0.6 ) ;
615+ const entryRadius = innerRadius + ( outerRadius - innerRadius ) * 0.5 ;
550616
551617 return {
552618 x : center . x + entryRadius * Math . cos ( angle - Math . PI / 2 ) ,
0 commit comments