tzurshubi commited on
Commit
afdfed5
·
verified ·
1 Parent(s): 54fabdd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -44
app.py CHANGED
@@ -459,7 +459,13 @@ def make_figure(d: int,
459
 
460
  switch_vertices = set()
461
  if switch_active and sw_s is not None and sw_e is not None and path:
462
- switch_vertices = set(path[sw_s:sw_e + 1])
 
 
 
 
 
 
463
 
464
 
465
  # ---------- neighbors of path vertices ----------
@@ -586,22 +592,29 @@ def make_figure(d: int,
586
  )
587
 
588
  # ---------- highlight switch-dims selection edges ----------
589
- if switch_active and sw_s is not None and sw_e is not None and sw_e > sw_s:
590
- for i in range(sw_s, sw_e):
591
- a, b = path[i], path[i + 1]
592
- x1, y1 = pos[a]
593
- x2, y2 = pos[b]
594
- edge_traces.append(
595
- go.Scatter(
596
- x=[x1, x2],
597
- y=[y1, y2],
598
- mode="lines",
599
- line=dict(width=max(2, edge_w * 3), color="#DC2626"),
600
- opacity=1.0,
601
- hoverinfo="skip",
602
- name="switch dims selection",
 
 
 
 
 
 
 
603
  )
604
- )
605
 
606
 
607
  # ---------- nodes ----------
@@ -1057,57 +1070,82 @@ def update_path(clickData,
1057
  # If we're in switch-dims mode, vertex clicks define exactly 3 consecutive vertices
1058
  if switchsel.get("active") and isinstance(cd, (int, float)):
1059
  vid = int(cd)
1060
- idx = index_in_path(path, vid)
1061
- if idx is None:
 
 
 
 
 
 
 
 
 
 
 
1062
  return path, subsel, switchsel
1063
-
1064
  s = switchsel.get("start_idx")
1065
  e = switchsel.get("end_idx")
1066
-
1067
  # first vertex
1068
  if s is None:
1069
  return path, subsel, {"active": True, "start_idx": idx, "end_idx": idx}
1070
-
1071
  if e is None:
1072
  e = s
1073
-
1074
- # must extend consecutively forward: idx == e+1
1075
- if idx == e + 1 and (idx - s) <= 2:
1076
- new_sel = {"active": True, "start_idx": s, "end_idx": idx}
1077
-
 
 
 
 
 
1078
  # if we have 3 vertices selected: apply switch
1079
- if idx == s + 2:
1080
- x = path[s]
1081
- y = path[s + 1]
1082
- z = path[s + 2]
1083
-
1084
  a = edge_dimension(x, y)
1085
  b = edge_dimension(y, z)
1086
  if a is None or b is None:
1087
- # not a valid 2-edge segment, exit mode
1088
  return path, subsel, {"active": False, "start_idx": None, "end_idx": None}
1089
-
1090
  # new middle vertex to traverse b then a
1091
  y2 = x ^ (1 << b)
1092
-
1093
- # optional safety: don't create duplicates elsewhere in the path
1094
- if y2 in set(path) and y2 not in {x, y, z}:
1095
  return path, subsel, {"active": False, "start_idx": None, "end_idx": None}
1096
-
1097
- new_path = path[:]
1098
- new_path[s + 1] = y2
 
 
 
 
 
 
1099
  return new_path, subsel, {"active": False, "start_idx": None, "end_idx": None}
1100
-
1101
- return path, subsel, new_sel
1102
-
1103
  # allow shrinking by clicking current end
1104
  if idx == e:
1105
- new_e = e - 1 if e > s else s
 
 
 
1106
  return path, subsel, {"active": True, "start_idx": s, "end_idx": new_e}
1107
-
1108
  # otherwise ignore
1109
  return path, subsel, switchsel
1110
 
 
1111
  # If we're in subpath selection mode, vertex clicks define (start_idx, end_idx)
1112
  if subsel.get("active") and isinstance(cd, (int, float)):
1113
  vid = int(cd)
 
459
 
460
  switch_vertices = set()
461
  if switch_active and sw_s is not None and sw_e is not None and path:
462
+ is_closed = (len(path) >= 2 and path[0] == path[-1])
463
+ cycle = path[:-1] if is_closed else path[:]
464
+ n = len(cycle)
465
+ if n > 0:
466
+ dist = (sw_e - sw_s) % n
467
+ switch_vertices = {cycle[(sw_s + i) % n] for i in range(dist + 1)}
468
+
469
 
470
 
471
  # ---------- neighbors of path vertices ----------
 
592
  )
593
 
594
  # ---------- highlight switch-dims selection edges ----------
595
+ if switch_active and sw_s is not None and sw_e is not None and path:
596
+ is_closed = (len(path) >= 2 and path[0] == path[-1])
597
+ cycle = path[:-1] if is_closed else path[:]
598
+ n = len(cycle)
599
+ if n > 0:
600
+ dist = (sw_e - sw_s) % n
601
+ for k in range(dist):
602
+ a = cycle[(sw_s + k) % n]
603
+ b = cycle[(sw_s + k + 1) % n]
604
+ x1, y1 = pos[a]
605
+ x2, y2 = pos[b]
606
+ edge_traces.append(
607
+ go.Scatter(
608
+ x=[x1, x2],
609
+ y=[y1, y2],
610
+ mode="lines",
611
+ line=dict(width=max(2, edge_w * 3), color="#DC2626"),
612
+ opacity=1.0,
613
+ hoverinfo="skip",
614
+ name="switch dims selection",
615
+ )
616
  )
617
+
618
 
619
 
620
  # ---------- nodes ----------
 
1070
  # If we're in switch-dims mode, vertex clicks define exactly 3 consecutive vertices
1071
  if switchsel.get("active") and isinstance(cd, (int, float)):
1072
  vid = int(cd)
1073
+
1074
+ # Work on a cycle if path is explicitly closed
1075
+ is_closed = (len(path) >= 2 and path[0] == path[-1])
1076
+ cycle = path[:-1] if is_closed else path[:]
1077
+ n = len(cycle)
1078
+
1079
+ if n < 3:
1080
+ return path, subsel, {"active": False, "start_idx": None, "end_idx": None}
1081
+
1082
+ # In a proper cycle representation, vertices are unique
1083
+ try:
1084
+ idx = cycle.index(vid)
1085
+ except ValueError:
1086
  return path, subsel, switchsel
1087
+
1088
  s = switchsel.get("start_idx")
1089
  e = switchsel.get("end_idx")
1090
+
1091
  # first vertex
1092
  if s is None:
1093
  return path, subsel, {"active": True, "start_idx": idx, "end_idx": idx}
1094
+
1095
  if e is None:
1096
  e = s
1097
+
1098
+ # distance forward on the cycle (0,1,2,...)
1099
+ dist = (e - s) % n
1100
+
1101
+ # must extend consecutively forward (with wrap)
1102
+ expected = (e + 1) % n
1103
+ if idx == expected and dist < 2:
1104
+ new_e = idx
1105
+ new_dist = (new_e - s) % n
1106
+
1107
  # if we have 3 vertices selected: apply switch
1108
+ if new_dist == 2:
1109
+ x = cycle[s]
1110
+ y = cycle[(s + 1) % n]
1111
+ z = cycle[(s + 2) % n]
1112
+
1113
  a = edge_dimension(x, y)
1114
  b = edge_dimension(y, z)
1115
  if a is None or b is None:
 
1116
  return path, subsel, {"active": False, "start_idx": None, "end_idx": None}
1117
+
1118
  # new middle vertex to traverse b then a
1119
  y2 = x ^ (1 << b)
1120
+
1121
+ # verify it actually reaches z by traversing a next
1122
+ if edge_dimension(y2, z) != a:
1123
  return path, subsel, {"active": False, "start_idx": None, "end_idx": None}
1124
+
1125
+ # avoid creating duplicates in the cycle (allow x,z)
1126
+ if y2 in set(cycle) and y2 not in {x, z}:
1127
+ return path, subsel, {"active": False, "start_idx": None, "end_idx": None}
1128
+
1129
+ cycle2 = cycle[:]
1130
+ cycle2[(s + 1) % n] = y2
1131
+
1132
+ new_path = cycle2 + [cycle2[0]] if is_closed else cycle2
1133
  return new_path, subsel, {"active": False, "start_idx": None, "end_idx": None}
1134
+
1135
+ return path, subsel, {"active": True, "start_idx": s, "end_idx": new_e}
1136
+
1137
  # allow shrinking by clicking current end
1138
  if idx == e:
1139
+ if e != s:
1140
+ new_e = (e - 1) % n
1141
+ else:
1142
+ new_e = s
1143
  return path, subsel, {"active": True, "start_idx": s, "end_idx": new_e}
1144
+
1145
  # otherwise ignore
1146
  return path, subsel, switchsel
1147
 
1148
+
1149
  # If we're in subpath selection mode, vertex clicks define (start_idx, end_idx)
1150
  if subsel.get("active") and isinstance(cd, (int, float)):
1151
  vid = int(cd)