diff --git a/papers/coloring_nested_tire_graphs/experiments/draw_rainbow_orbit.py b/papers/coloring_nested_tire_graphs/experiments/draw_rainbow_orbit.py new file mode 100644 index 0000000..07879f2 --- /dev/null +++ b/papers/coloring_nested_tire_graphs/experiments/draw_rainbow_orbit.py @@ -0,0 +1,314 @@ +"""Visualise the 'rainbow orbit' intersection from +tire_fiber_step2.tex, Observation rainbow: + + k = 6, T_1 = (6, (0,3), SP) vs T_2 = (3, -, SR) + |S_1 ∩ S_2| = 6 + The 6 elements are exactly {(a, b, c, b, c, a) : {a,b,c} = {1,2,3}} + --- the S_3 orbit of the pattern abstractly written abcbca. + +Produces three figures: + + fig_rainbow_pattern.png: + The abstract pattern abcbca on the shared 6-cycle, showing the + three antipodal/positional symmetry classes (positions {0,5}, + {1,3}, {2,4}). + + fig_rainbow_orbit.png: + All six concrete colourings in the S_3 orbit, drawn as small + hexagons with positions colored. Highlights how each permutation + of {1,2,3} gives one element. + + fig_rainbow_setup.png: + The geometric setup: shared cycle gamma (the hexagon), T_1 + outside (with its antipodal chord), T_2 inside (the triangle), + spoke edges crossing gamma. Shows why the antipodal chord in T_1 + pins the pattern. +""" +import math +import os +import matplotlib.pyplot as plt +import matplotlib.patches as patches + + +# Color palette: a 3-color set used consistently across all figures. +COLORS = { + 1: '#1f77b4', # blue ("color 1") + 2: '#d62728', # red ("color 2") + 3: '#2ca02c', # green ("color 3") +} +NAMES = {1: '1', 2: '2', 3: '3'} + + +def hex_positions(n=6, radius=1.0, angle_offset=math.pi / 2): + """Return positions of n evenly-spaced points on a circle of given radius.""" + return [ + (radius * math.cos(angle_offset - 2 * math.pi * i / n), + radius * math.sin(angle_offset - 2 * math.pi * i / n)) + for i in range(n) + ] + + +def draw_hex_with_pattern(ax, pattern, *, radius=1.0, label_size=10, + dot_size=20, ring_color='#888888'): + """Draw a 6-cycle with edges in ring_color and vertices colored by pattern + (length-6 list of colors). pattern[i] is the color of position i. + """ + pos = hex_positions(6, radius=radius) + # cycle edges + for i in range(6): + x1, y1 = pos[i] + x2, y2 = pos[(i + 1) % 6] + ax.plot([x1, x2], [y1, y2], color=ring_color, linewidth=1.6, zorder=1) + # vertices + for i, c in enumerate(pattern): + x, y = pos[i] + ax.plot(x, y, 'o', color=COLORS[c], markersize=dot_size, zorder=2, + markeredgecolor='black', markeredgewidth=0.8) + # tiny label of position + lx, ly = 1.32 * x, 1.32 * y + ax.annotate(str(i), (lx, ly), color='#444', ha='center', va='center', + fontsize=label_size - 1, zorder=3) + + +# --------------------------------------------------------------------------- +# Figure 1: the abstract pattern abcbca on a hexagon. +# --------------------------------------------------------------------------- +def fig_pattern(outdir): + fig, ax = plt.subplots(figsize=(6.0, 5.4)) + pos = hex_positions(6, radius=1.0) + + # Draw cycle edges + for i in range(6): + x1, y1 = pos[i] + x2, y2 = pos[(i + 1) % 6] + ax.plot([x1, x2], [y1, y2], color='#888888', linewidth=2.0, zorder=1) + + classes = {0: 'a', 1: 'b', 2: 'c', 3: 'b', 4: 'c', 5: 'a'} + class_colors = { + 'a': '#bdbdbd', + 'b': '#ffe082', + 'c': '#a5d6a7', + } + + for i in range(6): + x, y = pos[i] + cls = classes[i] + ax.plot(x, y, 'o', color=class_colors[cls], markersize=34, zorder=2, + markeredgecolor='black', markeredgewidth=1.0) + ax.annotate(cls, (x, y), color='black', ha='center', va='center', + fontsize=15, fontweight='bold', zorder=3) + lx, ly = 1.34 * x, 1.34 * y + ax.annotate(f'$v_{i}$', (lx, ly), color='#333', ha='center', + va='center', fontsize=11, zorder=3) + + # Legend showing the position-class assignments + legend = ( + r"$\sigma = (a, b, c, b, c, a)$" + "\n" + + r"position classes:" + "\n" + + r" $a$: $\{v_0, v_5\}$" + "\n" + + r" $b$: $\{v_1, v_3\}$" + "\n" + + r" $c$: $\{v_2, v_4\}$" + ) + ax.text(1.85, 0.0, legend, fontsize=11, color='#222', + family='monospace', + verticalalignment='center', + bbox=dict(boxstyle='round,pad=0.5', facecolor='#f8f8f8', + edgecolor='#bbb')) + + ax.set_xlim(-1.6, 4.0) + ax.set_ylim(-1.7, 1.7) + ax.set_aspect('equal') + ax.axis('off') + ax.set_title(r"Rainbow pattern $(a,b,c,b,c,a)$ on the shared cycle $\gamma$" + + " (length $k=6$)", + fontsize=11) + + out = os.path.join(outdir, 'fig_rainbow_pattern.png') + plt.savefig(out, dpi=160, bbox_inches='tight') + plt.close() + print(f"wrote {out}") + + +# --------------------------------------------------------------------------- +# Figure 2: the 6 concrete colourings in the S_3 orbit. +# --------------------------------------------------------------------------- +def fig_orbit(outdir): + # The 6 permutations of (a, b, c) -> (1, 2, 3) instantiated into the + # pattern (a, b, c, b, c, a). + perms = [ + (1, 2, 3), + (1, 3, 2), + (2, 1, 3), + (2, 3, 1), + (3, 1, 2), + (3, 2, 1), + ] + + fig, axes = plt.subplots(2, 3, figsize=(10.5, 7.5)) + + for idx, (a, b, c) in enumerate(perms): + ax = axes[idx // 3, idx % 3] + pattern = [a, b, c, b, c, a] + draw_hex_with_pattern(ax, pattern, dot_size=24) + ax.set_xlim(-1.7, 1.7) + ax.set_ylim(-1.7, 1.7) + ax.set_aspect('equal') + ax.axis('off') + # title shows pattern explicitly + title = (f"$(a,b,c) = ({a},{b},{c})$\n" + + r"$\sigma = ($" + ",".join(str(x) for x in pattern) + r"$)$") + ax.set_title(title, fontsize=10) + + fig.suptitle(r"The 6-element rainbow orbit: $S_3$ acting on pattern " + + r"$(a,b,c,b,c,a)$, the entire $|S_1 \cap S_2| = 6$ in step-2" + + "\n" + + r"$T_1 = (m{=}6, \mathrm{chord}{=}(0,3), \mathrm{SP})$ vs. " + + r"$T_2 = (k{=}3, \mathrm{SR})$ on shared cycle of length $k{=}6$", + fontsize=11, y=1.00) + plt.tight_layout() + out = os.path.join(outdir, 'fig_rainbow_orbit.png') + plt.savefig(out, dpi=160, bbox_inches='tight') + plt.close() + print(f"wrote {out}") + + +# --------------------------------------------------------------------------- +# Figure 3: geometric setup --- T_1 (hexagon + antipodal chord) outside gamma, +# T_2 (triangle) inside gamma, the 6 spoke edges crossing gamma. +# --------------------------------------------------------------------------- +def fig_setup(outdir): + fig, ax = plt.subplots(figsize=(8.5, 7.5)) + + # Shared cycle gamma at radius R_gamma + R_gamma = 1.4 + gamma_pos = hex_positions(6, radius=R_gamma) + + # T_1 lives outside gamma. Its "B_in" is gamma; its "B_out" is a bigger + # hexagon at R_out, with an antipodal chord across T_1's B_in side. + # For visualization, draw T_1's annular region as an annulus around gamma. + R_out = 2.5 + R_in = 0.55 + T1_outer = hex_positions(6, radius=R_out) + + # Draw T_1 outer boundary (B_out for T_1) + for i in range(6): + x1, y1 = T1_outer[i] + x2, y2 = T1_outer[(i + 1) % 6] + ax.plot([x1, x2], [y1, y2], color='#444', linewidth=2.0, zorder=2) + + # Draw the annular region for T_1 (shaded) + outer_poly = patches.Polygon(T1_outer + gamma_pos[::-1], closed=True, + facecolor='#e3f2fd', edgecolor='none', + alpha=0.55, zorder=0) + ax.add_patch(outer_poly) + + # Draw spokes between gamma_pos[i] and T1_outer[i] (representing radial spokes + # in T_1's annular triangulation, schematically). + for i in range(6): + x1, y1 = gamma_pos[i] + x2, y2 = T1_outer[i] + ax.plot([x1, x2], [y1, y2], color='#a0a0a0', linewidth=0.9, + linestyle=':', zorder=2) + + # T_1's antipodal chord across its B_in (= gamma): + # this chord is in the O of T_1, conceptually living outside gamma in T_1's + # interior outerplanar O. For the diagram we represent it as a curved chord + # outside gamma connecting two opposite gamma vertices. + chord_p1 = gamma_pos[0] + chord_p2 = gamma_pos[3] + # Draw outside gamma using an arc above + chord_arc = patches.FancyArrowPatch(chord_p1, chord_p2, + connectionstyle='arc3,rad=0.55', + arrowstyle='-', color='#9c27b0', + linewidth=2.2, linestyle=(0, (3, 2)), + zorder=4) + ax.add_patch(chord_arc) + + # T_2 lives inside gamma. T_2's B_out = gamma (length 6), + # T_2's B_in = a triangle of length 3. + triangle_pos = hex_positions(3, radius=R_in, angle_offset=math.pi / 2) + for i in range(3): + x1, y1 = triangle_pos[i] + x2, y2 = triangle_pos[(i + 1) % 3] + ax.plot([x1, x2], [y1, y2], color='#444', linewidth=2.0, zorder=2) + + # Draw T_2's annular region (between gamma and triangle, shaded) + inner_poly = patches.Polygon(gamma_pos + triangle_pos[::-1], closed=True, + facecolor='#fff3e0', edgecolor='none', + alpha=0.55, zorder=0) + ax.add_patch(inner_poly) + + # Draw shared cycle gamma (on top of everything) + for i in range(6): + x1, y1 = gamma_pos[i] + x2, y2 = gamma_pos[(i + 1) % 6] + ax.plot([x1, x2], [y1, y2], color='#333', linewidth=3.0, zorder=3) + + # Place vertices of gamma colored by one of the rainbow patterns: (1,2,3,2,3,1) + pattern = [1, 2, 3, 2, 3, 1] + for i in range(6): + x, y = gamma_pos[i] + ax.plot(x, y, 'o', color=COLORS[pattern[i]], markersize=26, zorder=4, + markeredgecolor='black', markeredgewidth=1.0) + ax.annotate(f"$v_{i}$", (x * 1.0, y * 1.0), color='white', + ha='center', va='center', fontsize=10, fontweight='bold', + zorder=5) + + # Triangle vertices + for i, (x, y) in enumerate(triangle_pos): + ax.plot(x, y, 'o', color='#666', markersize=14, zorder=4, + markeredgecolor='black', markeredgewidth=0.6) + + # Annotate which regions are T_1 and T_2 (placed off to the side with arrows) + ax.annotate(r'$T_1$ annulus' + '\n' + + r'$m_1 = 6$,' + '\n' + + r'chord $(v_0, v_3) \in O_1$' + '\n' + + r'Steiner-poor', + xy=(R_out - 0.1, -0.5), xytext=(3.7, -1.4), + fontsize=10, color='#1565c0', + ha='left', va='center', + arrowprops=dict(arrowstyle='->', color='#1565c0', lw=1.0)) + ax.annotate(r'$T_2$ annulus' + '\n' + + r'$k_2 = 3$, no chord' + '\n' + + r'Steiner-rich', + xy=(R_in + 0.2, 0.0), xytext=(3.7, 0.8), + fontsize=10, color='#e65100', + ha='left', va='center', + arrowprops=dict(arrowstyle='->', color='#e65100', lw=1.0)) + ax.annotate(r'shared cycle $\gamma$' + '\n' + r'(length $k = 6$)', + xy=(R_gamma * 0.9, R_gamma * 0.4), xytext=(3.7, 2.1), + fontsize=10, color='#222', + ha='left', va='center', + arrowprops=dict(arrowstyle='->', color='#222', lw=1.0)) + ax.annotate(r'antipodal chord' + '\n' + r'$(v_0, v_3)$ in $O_1$', + xy=(-0.55, 1.7), xytext=(-3.2, 2.4), + fontsize=10, color='#9c27b0', + ha='left', va='center', + arrowprops=dict(arrowstyle='->', color='#9c27b0', lw=1.0)) + + ax.set_xlim(-3.6, 5.4) + ax.set_ylim(-2.8, 3.2) + ax.set_aspect('equal') + ax.axis('off') + ax.set_title(r"Geometric setup behind the rainbow orbit: $T_1$ outside $\gamma$," + + r" $T_2$ inside $\gamma$" + "\n" + + r"shown with the spoke configuration $\sigma = (1,2,3,2,3,1)$" + + r" from the orbit", + fontsize=11) + + out = os.path.join(outdir, 'fig_rainbow_setup.png') + plt.savefig(out, dpi=160, bbox_inches='tight') + plt.close() + print(f"wrote {out}") + + +def main(): + here = os.path.dirname(os.path.abspath(__file__)) + notes = os.path.normpath(os.path.join(here, '..', 'notes')) + fig_pattern(notes) + fig_orbit(notes) + fig_setup(notes) + + +if __name__ == '__main__': + main() diff --git a/papers/coloring_nested_tire_graphs/notes/fig_rainbow_orbit.png b/papers/coloring_nested_tire_graphs/notes/fig_rainbow_orbit.png new file mode 100644 index 0000000..cb6e108 Binary files /dev/null and b/papers/coloring_nested_tire_graphs/notes/fig_rainbow_orbit.png differ diff --git a/papers/coloring_nested_tire_graphs/notes/fig_rainbow_pattern.png b/papers/coloring_nested_tire_graphs/notes/fig_rainbow_pattern.png new file mode 100644 index 0000000..664165e Binary files /dev/null and b/papers/coloring_nested_tire_graphs/notes/fig_rainbow_pattern.png differ diff --git a/papers/coloring_nested_tire_graphs/notes/fig_rainbow_setup.png b/papers/coloring_nested_tire_graphs/notes/fig_rainbow_setup.png new file mode 100644 index 0000000..b6e5c3a Binary files /dev/null and b/papers/coloring_nested_tire_graphs/notes/fig_rainbow_setup.png differ