// Last edited on 2024-05-30 17:31:23 by stolfi #include "nut_collect_quines.inc" #include "nut_collect_walk_edges.inc" #macro nut_skeleton(V, E, Nr, kr_start_o, kr_start_i, subfig) // The edge and vertex skeleton of the nut. Parameters: // {V} Array of vertex coordinates {V[1..Nv]}. // {E} Array of edges {E[1..Ne]}. // {Nr} Number of outer or inner quines (rotations) in model. // {kr_start_o} Rotation index of first outer quine to highlight ({e^*}). // {kr_start_i} Rotation index of first inner quine to highlight ({e^*}). // {subfig} Which sub-figure we are generating. // Assumes each entry {E[ke]} is a triple of integers {} // where {kv_org,kv_dst} are indices into {V}, and {ty} is a edge type code. #debug "!! Generating skeleton edges and vertices ...\n" #local Nv = dimension_size(V, 1) - 1; #local Ne = dimension_size(E, 1) - 1; // {Vmaxrad[kv]} is the max radius of spheres painted at vertex {kv}: #local Vmaxrad = array[Nv + 1]; #for (kv, 0, Nv) #local Vmaxrad[kv] = 0; #end #local tx_skel = nut_tx_skel_plain #local tx_active = nut_tx_skel_active #local tx_cross = nut_tx_skel_cross #local tx_walk = nut_tx_skel_walk #local vrad_skel = 0.35; #local erad_skel = vrad_skel; #local erad_cross = 1.00; #local erad_active = 0.80; #local erad_walk = 0.80; #local skel = union{ #if (subfig != 8) #debug "!! Plotting all edges and vertices with plain style ...\n" nut_skeleton_full(E, V, vrad_skel, erad_skel, tx_skel, Vmaxrad) #end #if ((subfig >= 0) & (subfig <= 7)) // Plot the active set {A} as needed: #if ((subfig >= 0) & (subfig <= 4)) #debug "!! Plotting outer quines as active ...\n" nut_skeleton_quines(E, V, Nr, 0, Nr, +1, erad_active, tx_active, Vmaxrad) #end #if ((subfig >= 0) & (subfig <= 7)) #debug "!! Plotting inner quines as active ...\n" nut_skeleton_quines(E, V, Nr, 0, Nr, -1, erad_active, tx_active, Vmaxrad) #end #end #if ((subfig >= 1) & (subfig <= 4)) #debug "!! Highlighting outer traversed quines and face borders ...\n" // Plots {nr_plot_q} quines, {nr_plot_w} wall walks // (-1 if not even one, {Nr} if all): #if (subfig <= 3) #local nr_plot_q = subfig; #local nr_plot_w = subfig-1; #else #local nr_plot_q = Nr; #local nr_plot_w = Nr; #end nut_skeleton_quines(E, V, Nr, kr_start_o, nr_plot_q, +1, erad_cross, tx_cross, Vmaxrad) nut_skeleton_walk(E, V, Nr, kr_start_o, nr_plot_w, +1, erad_walk, tx_walk, Vmaxrad) #end #if ((subfig >= 5) & (subfig <= 7)) #debug "!! Highlighting inner traversed quines and face borders ...\n" // {nr_plot_q} quines {nr_plot_w} walks // (-1 if not even one, {Nr} if all). #if (subfig <= 6) #local nr_plot_q = subfig - 4; #local nr_plot_w = subfig - 5; #else #local nr_plot_q = Nr; #local nr_plot_w = Nr; #end nut_skeleton_quines(E, V, Nr, kr_start_i, nr_plot_q, -1, erad_cross, tx_cross, Vmaxrad) nut_skeleton_walk(E, V, Nr, kr_start_i, nr_plot_w, -1, erad_walk, tx_walk, Vmaxrad) #end // To avoid stupid POV-Ray CSG warning: object{ nothing scale 1.7 } object{ nothing scale 1.8 } } skel #end #macro nut_skeleton_full(E, V, vrad, erad, tx, Vmaxrad) // Generates all edges and vertices of the skeleton, with given style. // Assumes that there are {Ne} edges {E[1..Ne]}. #local Ne = dimension_size(E, 1) - 1; #for (ke, 1, Ne) nut_skeleton_edge_verts(E, V, ke, erad, vrad, tx, Vmaxrad) #end #end #macro nut_skeleton_quines(E, V, Nr, kr_start, nr_plot, dir, erad, tx, Vmaxrad) // Generates the first {nr_plot} quines of the skeleton, with highlighted quine style. // Consider outer quines if {dir = +1}, inner (hole) quines if {dir = -1}. // Also generates the quine endpoints. // Assumes that there are {Nr} quines total of the selected type, whose indices into {E} // are {Ke[0..Nr-1]}. The enumeration of the quines plotted starts with {E[Ke[kr_start]]} // and proceeds in the direction {dir}, wrapping around modulo {Nr}. // Collect edge indices of quines {Ke[0..Nr-1]}, in rotation order: #local inner = (dir = -1); #local Ke = nut_collect_quines(E, inner) #if (Nr != dimension_size(Ke,1)) kaboom("quine {Ke} count is not {Nr}") #end #if (nr_plot > Nr) kaboom("{nr_plot > Nr}") #end #local vrad = erad; #for (dr, 0, nr_plot - 1) #local kr = mod(Nr+kr_start+dir*dr, Nr); // #debug concat("!! hiquines_plot: dir = ", str(dir,0,0), " kr = ", str(kr,0,0), "\n") nut_skeleton_edge_verts(E, V, Ke[kr], erad, vrad, tx, Vmaxrad) #end #end #macro nut_skeleton_walk(E, V, Nr, kr_start, nr_plot, dir, erad, tx, Vmaxrad) // Generates the walk edges for the first {nr_plot} walls, with highlighted walk edge style. // Consider outer walls if {dir = +1}, inner (hole) walls if {dir = -1}. // Also generates the walk edge endpoints. // Assumes that there are {Nw*Nr} walk edges in total of the selected type, whose indices into {E} // are {Ke[0..Nr-1][0..Nw-1]}. The wall enumeration starts with // wall {kr_start} and proceeds in the direction {dir}, wrapping around // modulo {Nr}. // Collect all walk edges, grouped by rotation index #local inner = (dir = -1); #local Ke = nut_collect_walk_edges(E, inner) #if (Nr != dimension_size(Ke,1)) kaboom("walk path count in {Ke} is not {Nr}") #end #local Nw = dimension_size(Ke,2); #if (nr_plot > Nr) kaboom("{nr_plot > Nr}") #end #local vrad = erad; #for (dr, 0, nr_plot - 1) #local kr = kr_start + dir*dr; #if (dir < 0) #local kr = kr - 1; #end #local kr = mod(Nr + kr, Nr); // #debug concat("!! hiwalks_plot: dir = ", str(dir,0,0), " kr = ", str(kr,0,0), "\n") #for (kw,0,Nw-1) nut_skeleton_edge_verts(E, V, Ke[kr][kw], erad, vrad, tx, Vmaxrad) #end #end #end #macro nut_skeleton_edge_verts(E, V, ke, erad, vrad, tx, Vmaxrad) #local kvo = E[ke].x; // Origin vertex index. #local kvd = E[ke].y; // Destination vertex index. #local Ty = E[ke].z; // Edge type. #local Vo = V[kvo]; // Origin vertex. #local Vd = V[kvd]; // Destination vertex. #if (erad > 0) cylinder{ Vo, Vd, erad texture{ tx } } #end #if (vrad > 0) #if (vrad > Vmaxrad[kvo]) sphere{ Vo, vrad texture{ tx } } #local Vmaxrad[kvo] = vrad; #end #if (vrad > Vmaxrad[kvd]) sphere{ Vd, vrad texture{ tx } } #local Vmaxrad[kvd] = vrad; #end #end #end