Skip to content

Commit 4acd4d5

Browse files
committed
fixes for viewer
1 parent 75cefe9 commit 4acd4d5

File tree

4 files changed

+3898
-2347
lines changed

4 files changed

+3898
-2347
lines changed

Solver/equal_cycle_decomp.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -658,10 +658,10 @@ def compute_cycle_weights(
658658
w, residuals, rank, s = np.linalg.lstsq(A, target, rcond=None)
659659
w = np.clip(w, 0, None) # Non-negative weights
660660

661-
# Normalize weights so they sum to 1 (relative weights)
662-
w_sum = w.sum()
663-
if w_sum > 0:
664-
w_normalized = w / w_sum
661+
# Normalize weights so max is 1.0 (like geodesic covers)
662+
w_max = w.max()
663+
if w_max > 0:
664+
w_normalized = w / w_max
665665
else:
666666
w_normalized = w
667667

webviewer/app.js

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)