Add walk-distance labelling to source-dual cut
Label each dual face by its distance, within the source-dual cut, from the first entry's cap down tooth. Regenerate the seed 1 and seed 2 full walk figures and metadata with the new labelling. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,13 +5,24 @@
|
|||||||
- deep-embedded edges: 84
|
- deep-embedded edges: 84
|
||||||
- graph seed: 59
|
- graph seed: 59
|
||||||
- deep-embedded minimum degree: 3
|
- deep-embedded minimum degree: 3
|
||||||
|
- chosen face: (8, 9, 19)
|
||||||
- chosen source cap vertex: 24
|
- chosen source cap vertex: 24
|
||||||
|
- root entry tooth: e2 (apex medial vertex = level-1 edge (19, 8))
|
||||||
- recognised treads: 11
|
- recognised treads: 11
|
||||||
- skipped treads: [(0, 'only 0 up teeth')]
|
- skipped treads: [((0, 0), 'only 0 up teeth')]
|
||||||
- removed source-dual edges: 29
|
- removed source-dual edges: 29
|
||||||
- annular/cap cuts: 12
|
- annular/cap cuts: 12
|
||||||
- up-apex cuts: 17
|
- up-apex cuts: 17
|
||||||
|
|
||||||
|
- **source-dual cut is a tree: True** (56 dual faces, 55 edges, 1 component(s), acyclic=True)
|
||||||
|
|
||||||
|
## Walk-distance labelling of the source-dual cut
|
||||||
|
|
||||||
|
Each dual face (vertex of the source-dual cut) is labelled by its distance, within the cut, from the **cap down tooth of the first entry**: the triangular face `(8, 24, 19)` = `{source 24, edge (19, 8)}` (dual node 34).
|
||||||
|
|
||||||
|
- maximum distance: 21
|
||||||
|
- distance histogram (faces by distance): `{0: 1, 1: 2, 2: 2, 3: 2, 4: 2, 5: 2, 6: 3, 7: 3, 8: 4, 9: 4, 10: 4, 11: 4, 12: 3, 13: 3, 14: 2, 15: 2, 16: 1, 17: 2, 18: 3, 19: 4, 20: 2, 21: 1}`
|
||||||
|
|
||||||
- dual cut figure: `full_medial_tire_cut_walk_1_dual.png`
|
- dual cut figure: `full_medial_tire_cut_walk_1_dual.png`
|
||||||
- tire cut grid: `full_medial_tire_cut_walk_1_tires.png`
|
- tire cut grid: `full_medial_tire_cut_walk_1_tires.png`
|
||||||
- combined PDF: `full_medial_tire_cut_walk_1.pdf`
|
- combined PDF: `full_medial_tire_cut_walk_1.pdf`
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 310 KiB |
@@ -7,7 +7,7 @@
|
|||||||
- deep-embedded minimum degree: 3
|
- deep-embedded minimum degree: 3
|
||||||
- chosen face: (4, 12, 11)
|
- chosen face: (4, 12, 11)
|
||||||
- chosen source cap vertex: 20
|
- chosen source cap vertex: 20
|
||||||
- root entry tooth: e3
|
- root entry tooth: e3 (apex medial vertex = level-1 edge (11, 4))
|
||||||
- recognised treads: 3
|
- recognised treads: 3
|
||||||
- skipped treads: [((0, 0), 'only 0 up teeth')]
|
- skipped treads: [((0, 0), 'only 0 up teeth')]
|
||||||
- removed source-dual edges: 20
|
- removed source-dual edges: 20
|
||||||
@@ -16,6 +16,13 @@
|
|||||||
|
|
||||||
- **source-dual cut is a tree: True** (38 dual faces, 37 edges, 1 component(s), acyclic=True)
|
- **source-dual cut is a tree: True** (38 dual faces, 37 edges, 1 component(s), acyclic=True)
|
||||||
|
|
||||||
|
## Walk-distance labelling of the source-dual cut
|
||||||
|
|
||||||
|
Each dual face (vertex of the source-dual cut) is labelled by its distance, within the cut, from the **cap down tooth of the first entry**: the triangular face `(4, 20, 11)` = `{source 20, edge (11, 4)}` (dual node 15).
|
||||||
|
|
||||||
|
- maximum distance: 17
|
||||||
|
- distance histogram (faces by distance): `{0: 1, 1: 2, 2: 2, 3: 2, 4: 2, 5: 3, 6: 3, 7: 3, 8: 3, 9: 3, 10: 4, 11: 3, 12: 2, 13: 1, 14: 1, 15: 1, 16: 1, 17: 1}`
|
||||||
|
|
||||||
- dual cut figure: `full_medial_tire_cut_walk_2_dual.png`
|
- dual cut figure: `full_medial_tire_cut_walk_2_dual.png`
|
||||||
- tire cut grid: `full_medial_tire_cut_walk_2_tires.png`
|
- tire cut grid: `full_medial_tire_cut_walk_2_tires.png`
|
||||||
- combined PDF: `full_medial_tire_cut_walk_2.pdf`
|
- combined PDF: `full_medial_tire_cut_walk_2.pdf`
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 205 KiB |
@@ -221,6 +221,44 @@ def dual_face_missing(G, removed):
|
|||||||
for v in G.nodes()}
|
for v in G.nodes()}
|
||||||
|
|
||||||
|
|
||||||
|
def dual_cut_graph(result):
|
||||||
|
"""The source-dual cut itself: the dual graph with every removed (cut) dual
|
||||||
|
edge deleted. This is the graph the walk distances are read on."""
|
||||||
|
D = result["dual"].copy()
|
||||||
|
removed = result["removed_dual_edges"]
|
||||||
|
D.remove_edges_from([(u, v) for u, v, d in D.edges(data=True)
|
||||||
|
if d["primal"] in removed])
|
||||||
|
return D
|
||||||
|
|
||||||
|
|
||||||
|
def entry_down_tooth_face(result):
|
||||||
|
"""Index of the cap down tooth of the first entry: the triangular face
|
||||||
|
``{source, x, y}`` where ``(x, y)`` is the first entry medial vertex (the
|
||||||
|
root tread's entry up-tooth apex, a level-1 edge that is a down tooth of the
|
||||||
|
source cap). This dual node roots the walk-distance labelling. Returns
|
||||||
|
None if no such face exists."""
|
||||||
|
em = result.get("entry_medial_vertex")
|
||||||
|
if em is None:
|
||||||
|
return None
|
||||||
|
target = frozenset((result["source"], em[0], em[1]))
|
||||||
|
for fi, f in enumerate(result["faces"]):
|
||||||
|
if frozenset(f) == target:
|
||||||
|
return fi
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def dual_cut_distances(result):
|
||||||
|
"""Distance of every dual face from the first entry's cap down tooth,
|
||||||
|
measured in the source-dual cut.
|
||||||
|
|
||||||
|
Returns ``(dist, root)`` where ``dist`` maps each reachable dual face to its
|
||||||
|
distance and ``root`` is the rooting dual face (or None if absent)."""
|
||||||
|
root = entry_down_tooth_face(result)
|
||||||
|
if root is None:
|
||||||
|
return {}, None
|
||||||
|
return nx.single_source_shortest_path_length(dual_cut_graph(result), root), root
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# The four chained entry points.
|
# The four chained entry points.
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
@@ -420,6 +458,7 @@ def _draw_dual_cut_ax(ax, result):
|
|||||||
missing = result["dual_face_missing"]
|
missing = result["dual_face_missing"]
|
||||||
source = result["source"]
|
source = result["source"]
|
||||||
entry_medial = result.get("entry_medial_vertex")
|
entry_medial = result.get("entry_medial_vertex")
|
||||||
|
dist, root = dual_cut_distances(result)
|
||||||
pos_v = _radial_source_layout(G, source, result["levels"])
|
pos_v = _radial_source_layout(G, source, result["levels"])
|
||||||
|
|
||||||
def centroid(fi):
|
def centroid(fi):
|
||||||
@@ -454,9 +493,20 @@ def _draw_dual_cut_ax(ax, result):
|
|||||||
color="0.80" if cut else "0.25",
|
color="0.80" if cut else "0.25",
|
||||||
lw=1.0 if cut else 1.3,
|
lw=1.0 if cut else 1.3,
|
||||||
linestyle=(0, (2, 2)) if cut else "solid", zorder=1)
|
linestyle=(0, (2, 2)) if cut else "solid", zorder=1)
|
||||||
|
# dual nodes labelled by walk distance from the first entry's cap down
|
||||||
|
# tooth; the rooting dual face is gold.
|
||||||
for fi in dual.nodes():
|
for fi in dual.nodes():
|
||||||
x, y = pos[fi]
|
x, y = pos[fi]
|
||||||
ax.plot(x, y, "o", ms=4, color="#3a6ea5", zorder=3)
|
d = dist.get(fi)
|
||||||
|
if fi == root:
|
||||||
|
ax.plot(x, y, "o", ms=12, mfc="#ffd24d", mec="#b8860b",
|
||||||
|
mew=1.3, zorder=3)
|
||||||
|
else:
|
||||||
|
ax.plot(x, y, "o", ms=10, mfc="white", mec="#3a6ea5",
|
||||||
|
mew=0.8, zorder=3)
|
||||||
|
ax.text(x, y, str(d) if d is not None else "∞",
|
||||||
|
color="#13335c", fontsize=5.5, fontweight="bold",
|
||||||
|
ha="center", va="center", zorder=4)
|
||||||
# label each source-graph vertex by its id; the cap source is flagged.
|
# label each source-graph vertex by its id; the cap source is flagged.
|
||||||
for v in G.nodes():
|
for v in G.nodes():
|
||||||
x, y = pos_v[v]
|
x, y = pos_v[v]
|
||||||
@@ -475,12 +525,15 @@ def _draw_dual_cut_ax(ax, result):
|
|||||||
ha="left", va="bottom", zorder=7,
|
ha="left", va="bottom", zorder=7,
|
||||||
bbox=dict(boxstyle="circle,pad=0.05", fc="white",
|
bbox=dict(boxstyle="circle,pad=0.05", fc="white",
|
||||||
ec="#b03030", lw=0.6))
|
ec="#b03030", lw=0.6))
|
||||||
|
root_face = result["faces"][root] if root is not None else None
|
||||||
|
max_dist = max(dist.values()) if dist else 0
|
||||||
ax.set_title(f"source-dual cut (cap source {source}, entry "
|
ax.set_title(f"source-dual cut (cap source {source}, entry "
|
||||||
f"e{result['entry_edge']} = medial vtx {entry_medial}); "
|
f"e{result['entry_edge']} = medial vtx {entry_medial}); "
|
||||||
f"gray = edges missing after cuts\n"
|
f"gray = edges missing after cuts\n"
|
||||||
f"green star = first entry medial vertex; red numbers = "
|
f"blue dual-node numbers = distance from gold root "
|
||||||
f"#missing dual edges around each dual face; "
|
f"(cap down tooth {root_face} of first entry; max {max_dist}); "
|
||||||
f"max {result['max_missing']}", fontsize=9)
|
f"green star = first entry; red = #missing dual edges per face",
|
||||||
|
fontsize=9)
|
||||||
ax.set_aspect("equal")
|
ax.set_aspect("equal")
|
||||||
ax.axis("off")
|
ax.axis("off")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user