@@ -192,9 +192,15 @@ function setupControls() {
192192 document . getElementById ( 'animate-flow' ) . addEventListener ( 'change' , ( e ) => {
193193 state . animateFlow = e . target . checked ;
194194 // Re-render to show/hide arrows vs particles (only if paths are shown)
195- if ( currentFlowData && state . showPaths ) {
196- renderPaths ( currentFlowData ) ;
197- updateEdgeColors ( ) ; // Restore proper edge coloring after re-render
195+ if ( state . showPaths ) {
196+ if ( isCycleMode && currentResult && currentResult . cycles ) {
197+ // Cycle mode: re-render cycle paths
198+ renderCyclePaths ( currentResult . cycles ) ;
199+ } else if ( currentFlowData ) {
200+ // Geodesic mode: re-render geodesic paths
201+ renderPaths ( currentFlowData ) ;
202+ updateEdgeColors ( ) ; // Restore proper edge coloring after re-render
203+ }
198204 }
199205 } ) ;
200206
@@ -1086,38 +1092,46 @@ function renderCyclePaths(cycles) {
10861092 }
10871093 }
10881094
1089- // Create arrowheads for directed edges
1095+ // Create arrowheads or flow particles for directed edges
10901096 cycles . forEach ( ( cycle , cycleIdx ) => {
10911097 const color = new THREE . Color ( CONFIG . colors . pathPalette [ cycleIdx % CONFIG . colors . pathPalette . length ] ) ;
1092- const directedEdges = cycle . directed_edges || [ ] ;
1093-
1094- for ( const edge of directedEdges ) {
1095- const [ from , to ] = edge ;
1096- const fromPos = new THREE . Vector3 (
1097- vertices [ from ] [ 0 ] * scale ,
1098- vertices [ from ] [ 1 ] * scale ,
1099- vertices [ from ] [ 2 ] * scale
1100- ) ;
1101- const toPos = new THREE . Vector3 (
1102- vertices [ to ] [ 0 ] * scale ,
1103- vertices [ to ] [ 1 ] * scale ,
1104- vertices [ to ] [ 2 ] * scale
1105- ) ;
1106-
1107- // Arrow at midpoint pointing toward 'to'
1108- const mid = new THREE . Vector3 ( ) . addVectors ( fromPos , toPos ) . multiplyScalar ( 0.5 ) ;
1109- const direction = new THREE . Vector3 ( ) . subVectors ( toPos , fromPos ) . normalize ( ) ;
1110-
1111- const coneGeom = new THREE . ConeGeometry ( 0.03 , 0.08 , 8 ) ;
1112- const coneMat = new THREE . MeshBasicMaterial ( { color : color , transparent : true , opacity : 0.8 } ) ;
1113- const cone = new THREE . Mesh ( coneGeom , coneMat ) ;
11141098
1115- cone . position . copy ( mid ) ;
1116- const up = new THREE . Vector3 ( 0 , 1 , 0 ) ;
1117- const quaternion = new THREE . Quaternion ( ) . setFromUnitVectors ( up , direction ) ;
1118- cone . setRotationFromQuaternion ( quaternion ) ;
1119-
1120- arrowsGroup . add ( cone ) ;
1099+ if ( state . animateFlow ) {
1100+ // Animate flow: create particles along the cycle path
1101+ // Use vertices array and close the loop
1102+ const cyclePath = [ ...cycle . vertices , cycle . vertices [ 0 ] ] ;
1103+ createFlowParticles ( cyclePath , vertices , scale , color , cycleIdx ) ;
1104+ } else {
1105+ // Static arrows on each edge
1106+ const directedEdges = cycle . directed_edges || [ ] ;
1107+ for ( const edge of directedEdges ) {
1108+ const [ from , to ] = edge ;
1109+ const fromPos = new THREE . Vector3 (
1110+ vertices [ from ] [ 0 ] * scale ,
1111+ vertices [ from ] [ 1 ] * scale ,
1112+ vertices [ from ] [ 2 ] * scale
1113+ ) ;
1114+ const toPos = new THREE . Vector3 (
1115+ vertices [ to ] [ 0 ] * scale ,
1116+ vertices [ to ] [ 1 ] * scale ,
1117+ vertices [ to ] [ 2 ] * scale
1118+ ) ;
1119+
1120+ // Arrow at midpoint pointing toward 'to'
1121+ const mid = new THREE . Vector3 ( ) . addVectors ( fromPos , toPos ) . multiplyScalar ( 0.5 ) ;
1122+ const direction = new THREE . Vector3 ( ) . subVectors ( toPos , fromPos ) . normalize ( ) ;
1123+
1124+ const coneGeom = new THREE . ConeGeometry ( 0.03 , 0.08 , 8 ) ;
1125+ const coneMat = new THREE . MeshBasicMaterial ( { color : color , transparent : true , opacity : 0.8 } ) ;
1126+ const cone = new THREE . Mesh ( coneGeom , coneMat ) ;
1127+
1128+ cone . position . copy ( mid ) ;
1129+ const up = new THREE . Vector3 ( 0 , 1 , 0 ) ;
1130+ const quaternion = new THREE . Quaternion ( ) . setFromUnitVectors ( up , direction ) ;
1131+ cone . setRotationFromQuaternion ( quaternion ) ;
1132+
1133+ arrowsGroup . add ( cone ) ;
1134+ }
11211135 }
11221136
11231137 // Mark start/end vertices
@@ -1309,6 +1323,19 @@ function updateEdgeColors() {
13091323 }
13101324 }
13111325 } ) ;
1326+ } else if ( isCycleMode && currentResult . cycles ) {
1327+ // Cycle decomposition mode
1328+ currentResult . cycles . forEach ( ( cycle , cycleIdx ) => {
1329+ const directedEdges = cycle . directed_edges || [ ] ;
1330+ for ( const edge of directedEdges ) {
1331+ const [ a , b ] = edge ;
1332+ const key = a < b ? `${ a } -${ b } ` : `${ b } -${ a } ` ;
1333+ if ( ! edgeUsage [ key ] ) edgeUsage [ key ] = [ ] ;
1334+ if ( ! edgeUsage [ key ] . includes ( cycleIdx ) ) {
1335+ edgeUsage [ key ] . push ( cycleIdx ) ;
1336+ }
1337+ }
1338+ } ) ;
13121339 }
13131340
13141341 // Apply colors to all edges
0 commit comments