diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/branching_medial_tire_decomposition/random_c5_n30_medial_tire_decomposition_1.md b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/branching_medial_tire_decomposition/random_c5_n30_medial_tire_decomposition_1.md index fd3b522..a270968 100644 --- a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/branching_medial_tire_decomposition/random_c5_n30_medial_tire_decomposition_1.md +++ b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/branching_medial_tire_decomposition/random_c5_n30_medial_tire_decomposition_1.md @@ -10,16 +10,9 @@ - tire-tree nodes: 4 - tire-tree edges: 3 -| node | depth | faces | annular | up | singleton down | bite apexes | full medial tires | +| node | depth | faces | annular cycles | annular | up | singleton down | bite apexes | |--:|--:|--:|--:|--:|--:|--:|--:| -| T0 | 2 | 23 | 23 | 10 | 13 | 0 | 1 | -| T0.0 | | | | 10 | 13 | - | `UDDUDUUDDUDUDUDUDDDUUDD` | -| T1 | 1 | 15 | 15 | 5 | 10 | 0 | 1 | -| T1.0 | | | | 5 | 10 | - | `UDDDUDDUDUDDDUD` | -| T2 | 3 | 14 | 14 | 11 | 0 | 0 | 4 | -| T2.0 | | | | 3 | 0 | - | `UUU` | -| T2.1 | | | | 3 | 0 | - | `UUU` | -| T2.2 | | | | 5 | 0 | - | `UUUUU` | -| T2.3 | | | | 3 | 0 | - | `UUU` | -| T3 | 3 | 5 | 5 | 5 | 0 | 0 | 1 | -| T3.0 | | | | 5 | 0 | - | `UUUUU` | +| T0 | 2 | 23 | 1 | 23 | 10 | 13 | 0 | +| T1 | 1 | 15 | 1 | 15 | 5 | 10 | 0 | +| T2 | 3 | 14 | 4 | 14 | 11 | 0 | 0 | +| T3 | 3 | 5 | 1 | 5 | 5 | 0 | 0 | diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/branching_medial_tire_decomposition/random_c5_n30_medial_tire_decomposition_1.pdf b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/branching_medial_tire_decomposition/random_c5_n30_medial_tire_decomposition_1.pdf index c2893ef..a1c5a0a 100644 Binary files a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/branching_medial_tire_decomposition/random_c5_n30_medial_tire_decomposition_1.pdf and b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/branching_medial_tire_decomposition/random_c5_n30_medial_tire_decomposition_1.pdf differ diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/branching_medial_tire_decomposition/random_c5_n30_medial_tire_decomposition_1.png b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/branching_medial_tire_decomposition/random_c5_n30_medial_tire_decomposition_1.png index 4fb8276..f24be12 100644 Binary files a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/branching_medial_tire_decomposition/random_c5_n30_medial_tire_decomposition_1.png and b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/branching_medial_tire_decomposition/random_c5_n30_medial_tire_decomposition_1.png differ diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/draw_random_medial_tire_decompositions.py b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/draw_random_medial_tire_decompositions.py index d48cc60..9491c30 100644 --- a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/draw_random_medial_tire_decompositions.py +++ b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/draw_random_medial_tire_decompositions.py @@ -2,9 +2,8 @@ The source graphs come from ``plantri -c5`` in graph6 format. For each sampled 30-vertex triangulation, this script chooses a random source vertex, builds the -BFS depth-component tire tree, recognizes every full medial tire graph in the -decomposition, and draws both the tire tree and the realized full medial tire -graphs. +BFS depth-component tire tree, and draws both the tire tree and the medial +tread model for each depth component. """ from __future__ import annotations @@ -36,8 +35,11 @@ import networkx as nx if str(PAPER_DIR) not in sys.path: sys.path.insert(0, str(PAPER_DIR)) -from lib.medial_tire_decomposition import ekey, medial_tire_facemodel, recognise -from lib.full_medial_tire_generator import FullMedialTireGraph +from lib.medial_tire_decomposition import ( + annular_cycle_components, + ekey, + medial_tire_facemodel, +) @dataclass(frozen=True) @@ -49,7 +51,8 @@ class TreadNode: up: frozenset down: frozenset bites: frozenset - tires: tuple[tuple[FullMedialTireGraph, dict], ...] + medial: nx.Graph + annular_cycles: tuple[tuple, ...] @dataclass(frozen=True) @@ -228,8 +231,8 @@ def build_tire_tree(g: nx.Graph, source: int, augment: bool = True): if tread is None or len(tread["up"]) < 3: continue mt = medial_tire_facemodel(tread["tread_faces"]) - tires = tuple(recognise(mt, tread)) - if not tires: + annular_cycles = tuple(annular_cycle_components(mt, tread["annular"])) + if not annular_cycles: continue node = TreadNode( idx=len(nodes), @@ -239,7 +242,8 @@ def build_tire_tree(g: nx.Graph, source: int, augment: bool = True): up=frozenset(tread["up"]), down=frozenset(tread["down"]), bites=frozenset(tread["bites"]), - tires=tires, + medial=mt, + annular_cycles=annular_cycles, ) comp_to_node[comp_idx] = node.idx nodes.append(node) @@ -343,7 +347,7 @@ def draw_tire_tree(ax, nodes: list[TreadNode], tree_edges): ax.text( x, y, - f"T{node.idx}\nd={node.depth}\n{len(node.tires)} tire(s)", + f"T{node.idx}\nd={node.depth}\n{len(node.annular_cycles)} cycle(s)", ha="center", va="center", fontsize=8, @@ -373,58 +377,113 @@ def edge_midpoint_angle(i: int, n: int) -> float: return math.pi / 2 - 2 * math.pi * (i + 0.5) / n -def draw_full_medial_tire(ax, graph: FullMedialTireGraph, title: str): - n = graph.n - ann = [vertex_xy(k, n, 1.0) for k in range(n)] - matched = graph.bite_edges - cyc_x = [p[0] for p in ann] + [ann[0][0]] - cyc_y = [p[1] for p in ann] + [ann[0][1]] - ax.plot(cyc_x, cyc_y, color="black", lw=1.3, zorder=2) - for i, tooth in enumerate(graph.tooth_word): - if tooth == "U": - r, color = 1.42, "#2563eb" - elif i not in matched: - r, color = 0.58, "#dc2626" - else: - continue - ang = edge_midpoint_angle(i, n) - apex = (r * math.cos(ang), r * math.sin(ang)) - for corner in (ann[i], ann[(i + 1) % n]): - ax.plot([apex[0], corner[0]], [apex[1], corner[1]], color="#9ca3af", lw=0.5) - ax.scatter([apex[0]], [apex[1]], s=12, color=color, zorder=3) - for i, j in sorted(graph.bites): - corners = [ann[i], ann[(i + 1) % n], ann[j], ann[(j + 1) % n]] - apex = (0.82 * sum(p[0] for p in corners) / 4, 0.82 * sum(p[1] for p in corners) / 4) +def draw_tread_model(ax, node: TreadNode): + cycle_count = len(node.annular_cycles) + offsets = [3.25 * (i - (cycle_count - 1) / 2) for i in range(cycle_count)] + apex_positions: dict[tuple, tuple[float, float]] = {} + apex_corners: dict[tuple, list[tuple[float, float]]] = defaultdict(list) + ann_positions: dict[tuple, tuple[float, float]] = {} + + for cycle_idx, order in enumerate(node.annular_cycles): + n = len(order) + dx = offsets[cycle_idx] + ann = { + vertex: (dx + x, y) + for vertex, (x, y) in zip(order, [vertex_xy(k, n, 1.0) for k in range(n)]) + } + ann_positions.update(ann) + + cyc_x = [ann[v][0] for v in order] + [ann[order[0]][0]] + cyc_y = [ann[v][1] for v in order] + [ann[order[0]][1]] + ax.plot(cyc_x, cyc_y, color="black", lw=1.3, zorder=2) + + for i, a in enumerate(order): + b = order[(i + 1) % n] + apexes = [ + w for w in set(node.medial.neighbors(a)) & set(node.medial.neighbors(b)) + if w not in node.annular + ] + for apex in apexes: + apex_corners[apex].extend([ann[a], ann[b]]) + if apex in apex_positions: + continue + angle = edge_midpoint_angle(i, n) + if apex in node.up: + radius = 1.42 + else: + radius = 0.58 + apex_positions[apex] = ( + dx + radius * math.cos(angle), + radius * math.sin(angle), + ) + + for apex, corners in apex_corners.items(): + if apex in node.bites and corners: + cx = sum(p[0] for p in corners) / len(corners) + cy = sum(p[1] for p in corners) / len(corners) + center_x = sum(offsets) / len(offsets) if offsets else 0.0 + apex_positions[apex] = ( + center_x + 0.82 * (cx - center_x), + 0.82 * cy, + ) + pos = apex_positions[apex] for corner in corners: - ax.plot([apex[0], corner[0]], [apex[1], corner[1]], color="#9ca3af", lw=0.5) - ax.scatter([apex[0]], [apex[1]], s=22, color="#7f1d1d", edgecolors="black", lw=0.4) - ax.scatter([p[0] for p in ann], [p[1] for p in ann], s=9, color="black", zorder=4) - bites = ",".join(f"{i}{j}" for i, j in sorted(graph.bites)) or "-" - ax.set_title(f"{title}\n{graph.tooth_word} b:{bites}", fontsize=5.8, pad=1.5) - ax.set_xlim(-1.6, 1.6) - ax.set_ylim(-1.6, 1.6) + ax.plot([pos[0], corner[0]], [pos[1], corner[1]], color="#9ca3af", lw=0.5) + + for apex, pos in apex_positions.items(): + if apex in node.up: + color, size, edgecolor = "#2563eb", 13, "none" + elif apex in node.bites: + color, size, edgecolor = "#7f1d1d", 24, "black" + else: + color, size, edgecolor = "#dc2626", 13, "none" + ax.scatter( + [pos[0]], + [pos[1]], + s=size, + color=color, + edgecolors=edgecolor, + linewidths=0.4, + zorder=3, + ) + + if ann_positions: + ax.scatter( + [p[0] for p in ann_positions.values()], + [p[1] for p in ann_positions.values()], + s=9, + color="black", + zorder=4, + ) + + singleton_down = set(node.down) - set(node.bites) + ax.set_title( + f"T{node.idx} d={node.depth}: {len(node.annular_cycles)} annular cycle(s)\n" + f"ann={len(node.annular)} up={len(node.up)} down={len(singleton_down)} " + f"bite={len(node.bites)}", + fontsize=6.4, + pad=1.5, + ) + pad = 1.7 + ax.set_xlim(min(offsets, default=0.0) - pad, max(offsets, default=0.0) + pad) + ax.set_ylim(-1.65, 1.65) ax.set_aspect("equal") ax.axis("off") def draw_medial_tire_grid(fig, outer_spec, nodes): - tires = [] - for node in nodes: - for comp_idx, (graph, _bij) in enumerate(node.tires): - tires.append((node.idx, comp_idx, graph)) - if not tires: + if not nodes: ax = fig.add_subplot(outer_spec) - ax.text(0.5, 0.5, "No full medial tire graphs recognized", ha="center") + ax.text(0.5, 0.5, "No medial treads extracted", ha="center") ax.axis("off") return - cols = min(5, max(1, math.ceil(math.sqrt(len(tires))))) - rows = math.ceil(len(tires) / cols) + cols = min(3, max(1, math.ceil(math.sqrt(len(nodes))))) + rows = math.ceil(len(nodes) / cols) sub = outer_spec.subgridspec(rows, cols, wspace=0.08, hspace=0.35) for i in range(rows * cols): ax = fig.add_subplot(sub[i // cols, i % cols]) - if i < len(tires): - node_idx, comp_idx, graph = tires[i] - draw_full_medial_tire(ax, graph, f"T{node_idx}.{comp_idx} n={graph.n}") + if i < len(nodes): + draw_tread_model(ax, nodes[i]) else: ax.axis("off") @@ -452,22 +511,16 @@ def write_index( f"- tire-tree nodes: {len(nodes)}", f"- tire-tree edges: {len(tree_edges)}", "", - "| node | depth | faces | annular | up | singleton down | bite apexes | full medial tires |", + "| node | depth | faces | annular cycles | annular | up | singleton down | bite apexes |", "|--:|--:|--:|--:|--:|--:|--:|--:|", ] for node in nodes: singleton_down = set(node.down) - set(node.bites) lines.append( f"| T{node.idx} | {node.depth} | {len(node.face_indices)} | " - f"{len(node.annular)} | {len(node.up)} | {len(singleton_down)} | " - f"{len(node.bites)} | {len(node.tires)} |" + f"{len(node.annular_cycles)} | {len(node.annular)} | {len(node.up)} | " + f"{len(singleton_down)} | {len(node.bites)} |" ) - for comp_idx, (tire, _bij) in enumerate(node.tires): - bites = ",".join(f"({i},{j})" for i, j in sorted(tire.bites)) or "-" - lines.append( - f"| T{node.idx}.{comp_idx} | | | | {len(tire.up_edges)} | " - f"{len(tire.singleton_down_edges)} | {bites} | `{tire.tooth_word}` |" - ) path.write_text("\n".join(lines) + "\n") @@ -517,7 +570,7 @@ def draw_case(out_dir: Path, graph_idx: int, g: nx.Graph, source: int, augment: nodes, tree_edges, ) - return png, pdf, len(nodes), sum(len(node.tires) for node in nodes) + return png, pdf, len(nodes), sum(len(node.annular_cycles) for node in nodes) def run(args: argparse.Namespace): @@ -535,12 +588,12 @@ def run(args: argparse.Namespace): sources = [rng.choice(list(graph.nodes())) for graph in graphs] for i, (graph, source) in enumerate(zip(graphs, sources), start=1): - png, pdf, node_count, tire_count = draw_case( + png, pdf, node_count, annular_cycle_count = draw_case( out_dir, i, graph, source, augment=not args.no_augment_same_level_faces ) print( f"case {i}: source={source}, connectivity={nx.node_connectivity(graph)}, " - f"tire nodes={node_count}, full medial tires={tire_count}" + f"tire nodes={node_count}, annular cycles={annular_cycle_count}" ) print(f" wrote {png}") print(f" wrote {pdf}") diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_1.md b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_1.md index 2898e67..f9ce39d 100644 --- a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_1.md +++ b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_1.md @@ -10,13 +10,8 @@ - tire-tree nodes: 3 - tire-tree edges: 2 -| node | depth | faces | annular | up | singleton down | bite apexes | full medial tires | +| node | depth | faces | annular cycles | annular | up | singleton down | bite apexes | |--:|--:|--:|--:|--:|--:|--:|--:| -| T0 | 1 | 16 | 16 | 6 | 10 | 0 | 1 | -| T0.0 | | | | 6 | 10 | - | `DUDUDUDDUDDDUDDU` | -| T1 | 2 | 20 | 20 | 10 | 10 | 0 | 1 | -| T1.0 | | | | 10 | 10 | - | `UUDUUDUDDUDUDUDUUDDD` | -| T2 | 3 | 16 | 16 | 12 | 0 | 1 | 3 | -| T2.0 | | | | 6 | 0 | (1,5) | `UDUUUDUU` | -| T2.1 | | | | 3 | 0 | - | `UUU` | -| T2.2 | | | | 5 | 0 | - | `UUUUU` | +| T0 | 1 | 16 | 1 | 16 | 6 | 10 | 0 | +| T1 | 2 | 20 | 1 | 20 | 10 | 10 | 0 | +| T2 | 3 | 16 | 3 | 16 | 12 | 0 | 1 | diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_1.pdf b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_1.pdf index 5ba9d88..c6bac3c 100644 Binary files a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_1.pdf and b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_1.pdf differ diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_1.png b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_1.png index 121503e..985942d 100644 Binary files a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_1.png and b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_1.png differ diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_2.md b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_2.md index 36869e7..7d3d54f 100644 --- a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_2.md +++ b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_2.md @@ -10,14 +10,9 @@ - tire-tree nodes: 4 - tire-tree edges: 3 -| node | depth | faces | annular | up | singleton down | bite apexes | full medial tires | +| node | depth | faces | annular cycles | annular | up | singleton down | bite apexes | |--:|--:|--:|--:|--:|--:|--:|--:| -| T0 | 1 | 16 | 16 | 7 | 9 | 0 | 1 | -| T0.0 | | | | 7 | 9 | - | `DUDUDUDUDUDDUDUD` | -| T1 | 2 | 17 | 17 | 9 | 8 | 0 | 1 | -| T1.0 | | | | 9 | 8 | - | `UUUDDUDUDUDUDDUUD` | -| T2 | 3 | 14 | 14 | 8 | 4 | 1 | 1 | -| T2.0 | | | | 8 | 4 | (1,5) | `DDUUUDDUUDUDUU` | -| T3 | 4 | 6 | 6 | 5 | 0 | 0 | 2 | -| T3.0 | | | | 3 | 0 | - | `UUU` | -| T3.1 | | | | 3 | 0 | - | `UUU` | +| T0 | 1 | 16 | 1 | 16 | 7 | 9 | 0 | +| T1 | 2 | 17 | 1 | 17 | 9 | 8 | 0 | +| T2 | 3 | 14 | 1 | 14 | 8 | 4 | 1 | +| T3 | 4 | 6 | 2 | 6 | 5 | 0 | 0 | diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_2.pdf b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_2.pdf index 7f03ba2..6f5b5a8 100644 Binary files a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_2.pdf and b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_2.pdf differ diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_2.png b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_2.png index d56d6f8..e02abac 100644 Binary files a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_2.png and b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/random_medial_tire_decompositions/random_c5_n30_medial_tire_decomposition_2.png differ