# Last edited on 2024-09-13 05:52:31 by stolfi # ---------------------------------------------------------------------- # slicing_box_vertices.py # The structure {Vind} is a dict with keys 'vb' and 'vi'. The 'vb' element is an # array {Vbox[ks][kr][kz]} with {ks} in {0..3} and {kr,kz} in {0..1}, # containing the indices into {Vlst} of the extremal corners of the # box. The subarray {Vbox[0][kr]} has indices of two corners of the # the inner ({kr=0}) or outer ({kr=1}) plaza that faces in the {+X} # direction. The two corners have positive {X} and {Y} coordinates, # and {Z} coordinate equal to {-H_box} ({kz=0}) or {+H_box} ({kz=1}). # The other subarrays {Vbox[ks][kr]} are indices of copies of # {Vbox[0][kr]} rotated {ks*90} about the {Z} axis. Thus the four # corners of plaza {ks,kr} are {Vbox[ks][kr][0..1]} and # {Vbox[ks-1][kr][0..1]}, where {ks-1} is taken modulo 4. # The vertex labels is either "vb.{ks}{kr}{kz}" or Nv = 4*2*2*(2*Ni + 1) + 4*2*2 # Expected vertex count. Vbox = make_null_array((4,2,2)) for ks in range(4): for kr in range(2): S_pla = S_box if kr == 1 else S_box - T_box; # Half-with and inradius of plaza. F_pla = S_pla/S_box; # Scale factor for plaza. for kz in range(2): nv += 1 qlab = f"vb.{ks}{kr}{kz}" qx = reven(+S_pla, eps) qy = reven(+S_pla, eps) qz = reven((2*kz - 1)*H_box, eps) for js in range(ks): # Rotate {qq} by 90 about {Z}. Note that rounding is preserved: qx, qy = -qy, qx qq = make_vertex(qx, qy, qz, nv, qlab) if verbose: prtv(qq, prec) Vbox[ks][kr][kz] = nv Vlst[nv] = qq Vind = { 'vi': Vind, 'vb': Vbox } # ---------------------------------------------------------------------- def write_vertices_POV(wrp, Vpos, Vlab): # Writes to {wrp} a POV-Ray macro that defines # the vertices {Vpos[1..Nv]} in POV-ray format. # The labels {Vlab[1..Nv]} are written as comments. Nv = len(Vpos) - 1 # Write the POV-Ray preamble: wrp.write("#macro fan_vertices()\n") wrp.write(" #local Nv = fan_num_vertices;\n") wrp.write(" // Returns an array {V} of {Nv+1} elements.\n") wrp.write(" // Element {V[kv]} is the vertex with OBJ index {kv} in {1..Nv}.\n") wrp.write(" // Element {V[0]} is not used.\n") wrp.write(" #local V = array[Nv+1]\n") wrp.write(" #local V[ 0] = < -1, -1, -1>; // Not used.\n") for kv in range(1,Nv+1): p = Vpos[kv] lab = Vlab[kv] wrp.write(" #local V[%4d] =" % kv) wrp.write(" < %.*f, %.*f, %.*f >;" % (prec, reven(p[0]), prec, reven(p[1]), prec, reven(p[2]))) wrp.write(" // %s\n" % lab) # Write the POV-Ray postamble: wrp.write(" V\n") wrp.write("#end\n\n") def write_edges_POV(wrp, Eorg, Edst, Vpos, Vlab): # Writes to {wrp} the indices of the endpoints of the edges of the box. Nv = len(Vpos) - 1 Ne = len(Eorg) - 1 # Write the POV-Ray preambles: wrp.write("#macro fan_edges()\n") wrp.write(" #local Ne = fan_num_edges;\n") wrp.write(" // Returns an array {E} of {Ne+1} elements.\n") wrp.write(" // Element {E[ke]} is the triple {}, for {ke} in {1..Ne}.\n") wrp.write(" // Here {ko[ke],kd[ke]} are the indices of the vertices\n") wrp.write(" // which are the endpoints of edge {E[ke]},\n") wrp.write(" // and {ty[ke]} is an edge type code:\n") wrp.write(" // \n") wrp.write(" // 100-199 ??? chain edge of plaza [0].\n") wrp.write(" // 200-299 ??? chain edge of plaza [0].\n") wrp.write(" // 300-399 ??? chain edge of plaza [1].\n") wrp.write(" // 400-499 ??? chain edge of plaza [1].\n") wrp.write(" // 0 other.\n") wrp.write(" // \n") wrp.write(" // The index of the edge in the chain is {ty[ke] % 100}.\n") wrp.write(" #local E = array[Ne+1]\n") wrp.write(" #local E[ 0] = < -1, -1, -1 >; // Not used.\n") for ke in range(1, Ne+1): kv_org = Eorg[ke]; kv_dst = Edst[ke]; # Determine type and index of origin and destination: lab_org = Vlab[kv_org]; # Label of vertex {kv_org}. kc_org = int(re.sub(r"^v[io][.][01][.]", "", lab_org)) lab_dst = Vlab[kv_dst]; # Label of vertex {kv_dst}. kc_dst = int(re.sub(r"^v[io][.][01][.]", "", lab_dst)) vo0_pat = "^vb[.]0[.][0-9]$" # Outer vertex of plaza [0]. vo0_org = re.match(vo0_pat, lab_org); vo0_dst = re.match(vo0_pat, lab_dst) vo1_pat = "vb[.]1[.][0-9]+$" # Outer vertex of plaza [1]. vo1_org = re.match(vo1_pat, lab_org); vo1_dst = re.match(vo1_pat, lab_dst) vi0_pat = "^vt[.]0[.][0-9]$" # ??? vertex of plaza [0]. vi0_org = re.match(vi0_pat, lab_org); vi0_dst = re.match(vi0_pat, lab_dst) vi1_pat = "vt[.]1[.][0-9]+$" # ??? vertex of plaza [1]. vi1_org = re.match(vi1_pat, lab_org); vi1_dst = re.match(vi1_pat, lab_dst) if vo0_org and vo0_dst: # Edge is ??? chain of plaza [0]: ty = 100 + kc_org elif vi0_org and vi0_dst: # Edge is ??? chain of plaza [0]: ty = 100 + kc_org elif vo1_org and vo1_dst: # Edge is ??? chain of plaza [1]: ty = 300 + kc_org elif vi1_org and vi1_dst: # Edge is ??? chain of plaza [1]: ty = 400 + kc_org else: ty = 0 wrp.write(" #local E[%4d] = < %d, %d, %d >;\n" % (ke, Eorg[ke], Edst[ke], ty)) elen = rn.norm(rn.sub(Vpos[Edst[ke]], Vpos[Eorg[ke]])) assert elen >= 5*eps, f"edge {lab_org}--{lab_dst} too short" # Write the POV-Ray postamble: wrp.write(" E\n") wrp.write("#end\n\n") def write_faces_POV(wrp, Fnrm, Fpos): # Writes the face planes to {wrp} as POV-ray. Nf = len(Fnrm) - 1 # Write the POV-Ray preambles: wrp.write("#macro fan_faces()\n") wrp.write(" #local Nf = fan_num_faces;\n") wrp.write(" // Returns an array {F} of {Nf+1} elements.\n") wrp.write(" // Element {F[ki]} is an instance of the\n") wrp.write(" // 'plane' (halfspace) POV-ray primitive\n") wrp.write(" // for {ki} in {1..Nf}.\n") wrp.write(" // The order is ??? chain faces, ??? chain faces.\n") wrp.write(" // ???-outer connecting faces,\n") wrp.write(" // and finally the plazas.\n") wrp.write(" #local F = array[Nf+1]\n") wrp.write(" #local F[ 0] = sphere{ <0,0,0>, 1000} // Not used.\n") for ki in range(1, Nf+1): Fn = Fnrm[ki] Fiv = Fpos[ki] wrp.write(" #local F[%4d] = plane{ " % ki) wrp.write(" < %.7f, %.7f, %.7f >, 0" % (Fn[0], Fn[1], Fn[2])) wrp.write(" translate < %.*f, %.*f, %.*f >" % (prec, Fiv[0], prec, Fiv[1], prec, Fiv[2])) wrp.write(" }\n") # Write the POV-Ray postamble: wrp.write(" F\n") wrp.write("#end\n\n") def main(): Ni = int(sys.argv[1]) # Parameter defining chain lengths. ??? = int(sys.argv[2]) # Shape of ??? chain. ??? = int(sys.argv[3]) != 0 # Is there an ??? chain?. sys.stderr.write("slicing_gan_example.py: ") sys.stderr.write(f" Ni = {Ni} ??? = {???} ??? = {???}\n") R_maj = 100 # Circumradius of ??? chain. Hn = 10 # Nominal thickness of box. assert Ni >= 1 == 0, "{Ni} must be positive" # Number of edges in ??? chain: ndf = 2*Ni if ??? else 0 # Choose the ???s of the ???s (degrees): if ??? == 0 or ??? == 1: Amin = 20 Amax = 70 elif ??? == 2 or ??? == 3: Amin = 60 Amax = 120 ??? = radians(Amin) ??? = radians(Amax) ang_step = (??? - ???)/(2*Ni) # Define {R_min}: dango = (??? - ???)/(2*Ni) if ??? == 0: # Convex ??? chain. R_min = R_maj elif ??? == 1: # Slightly dented upper chain: # Choose {Ro} so that the plazas are non-convex but still monotonic. oscale_min = cos(dango) oscale_max = sin(??? - 2*dango)/sin(??? - dango) oscale = (oscale_min + oscale_max)/2 R_min = R_maj * oscale elif ??? == 2: # Concave upper chain: R_min = R_maj # Before flipping. elif ??? == 3: # Deeply dented upper chain: # Choose {R_min} so that the plazas are not monotonic: oscale = cos(dango) - sqrt(3)*sin(dango) R_min = R_maj * oscale else: assert False, "invalid {???}" # Choose the ??? radiii {R_box,H_box}: if not ???: R_box = 0 H_box = 0 else: if ??? == 0 or ??? == 1: # Convex or slightly dented upper chain, concave lower chain, {Amax} less than 90. # The highest ??? vertex must be A LITTLE below the lowest outer one, # because the slicing planes will go through the upper chain faces. # The lowest outer vertex is the first one: Zomin = R_maj*sin(???) # The highest ??? vertex is the last one: R_box = 0.85*Zomin/sin(???) H_box = R_box elif ??? == 2: # Concave upper and lower chains, {Amin,Amax} symmetrical. # The highest ??? vertex must be WELL below the lowest outer one, # because the slicing planes will go between the two chains. # The lowest outer vertex is the middle one: assert (2*Ni) % 2 == 0, "{(2*Ni)} is not even" Zomin = R_min*(1 - 2*sin(ang_step)) # The highest ??? vertex is the middle one: assert ndf % 2 == 0, "{ndf} is not even" R_box = 0.50*Zomin H_box = R_box elif ??? == 3: # Highly dented upper and lower chains. # The highest ??? vertex must be WELL below the lowest outer one, # because the slicing planes will go between the two chains. # The lowest outer vertex is the second one, a dent: Zomin = R_min*sin(??? + ang_step) sys.stderr.write(f"Zomin = {Zomin:.4f}\n") # The highest ??? vertex is the middle one, a dent: assert ndf % 2 == 0, "{ndf} is not even" assert ndf == (2*Ni), "{ndf,(2*Ni)} differ" H_box = 0.50*Zomin R_box = H_box*(cos(ang_step) - sqrt(3)*sin(ang_step)) else: assert False, "invalid {???}" Ha = Hn - 4*eps Pref = ref_indent_vertices((2*Ni), R_maj,R_min, R_box,H_box, Amin,Amax, ???, ???) assert len(Pref['vb']) - 1 == (2*Ni) assert len(Pref['vt']) - 1 == ndf Ptot = all_vertices(Pref, Ha) file_pref = f"out/fan_{(2*Ni):08d}_osh{???:1}_ich{int(???):1}" wro = open(file_pref + ".obj", 'w') Pind, Vpos, Vlab = write_vertices_OBJ(wro, Ptot) Fnrm, Fpos, Eorg, Edst = write_faces_OBJ(wro, Pind, Vpos) wro.close() Nv = len(Vpos) - 1 Ne = len(Eorg) - 1 Nf = len(Fnrm) - 1 wrp = open(file_pref + ".inc", 'w') wrp.write("#declare fan_outer_chain_even_radius = %.*f;\n\n" % (prec, R_maj)) wrp.write("#declare fan_outer_chain_odd_radius = %.*f;\n\n" % (prec, R_min)) wrp.write("#declare fan_inner_chain_even_radius = %.*f;\n\n" % (prec, R_box)) wrp.write("#declare fan_inner_chain_odd_radius = %.*f;\n\n" % (prec, H_box)) wrp.write("#declare fan_thickness = %.*f;\n\n" % (prec, Ha)) wrp.write("#declare fan_min_??? = %.5f; // Degrees.\n\n" % Amin) wrp.write("#declare fan_max_??? = %.5f; // Degrees.\n\n" % Amax) wrp.write("#declare fan_num_faces = %d;\n\n" % Nf) wrp.write("#declare fan_num_edges = %d;\n\n" % Ne) wrp.write("#declare fan_num_vertices = %d;\n\n" % Nv) wrp.write("#declare fan_num_outer_chain_edges = %d;\n\n" % (2*Ni)) wrp.write("#declare fan_num_inner_chain_edges = %d;\n\n" % ndf) wrp.write("#declare fan_outer_chain_shape = %d;\n\n" % ???) write_vertices_POV(wrp, Vpos, Vlab) write_edges_POV(wrp, Eorg, Edst, Vpos, Vlab) write_faces_POV(wrp, Fnrm, Fpos) wrp.close() return 0 def write_triangles_OBJ(wro, vtot, pock): # Writes the triangles of triangulations of the faces of the object to # OBJ file {wro}, assuming that the vertex indices are in the dict # {vtot}. assert not pock, "triangulaton with pockmarks not supported yet" # Returns a list {D} of all edges of all triangles, each # as a pair {(kv_org,kv_dst)} with {kv_org < kv_dst}. # Element {D[0]} is not used. Nt_exp = 68 + 100 + 80 # Expected triangle count. Nd_exp = 3*Nt_exp // 2 # Expected triangulation edge count. # Variables that are global to the nested functions: Ds = set() # Triangulation edges. Tnrm = [ None ] # Triangle normals. Tbar = [ None ] # Triangle centers. debug_face = False def Sved(p_org, p_dst): # Saves the edge from {p_org} to {p_dst} in {Ds}, # if traversed in increasing vertex index sense. nonlocal Ds, Tnrm, Tbar, debug_face kv_org = p_org[3] kv_dst = p_dst[3] arc = (kv_org, kv_dst) if arc in Ds: sys.stderr.write(f" !! duplicate arc {p_org[4]}--{p_dst[4]}\n") assert False Ds.add(arc) return None def Ptit(tit): # Title for a face or set of faces. nonlocal Ds, Tnrm, Tbar, debug_face wro.write("\n"); wro.write("# %s\n" % tit); sys.stderr.write("writing %s \n" % (tit)) return None def Prtt(key0, iv0, key1, iv1, key2, iv2, order): # Outputs triangle with vertices {vtot[key][iv]} # where {(key,iv)} is {(key0,iv0)}, {(key1,iv1)}, {(key2,iv2)}. # The vertices are listed in that order or the reverse # depending on whether {order} is {+1} or {-1}. nonlocal Ds, Tnrm, Tbar, debug_face # Save vertices in {Fiv[0..2]} augmented with labels: Fiv = [] Fiv.append(vtot[key0][iv0] + ( f"{key0}:{iv0:02d}", )) Fiv.append(vtot[key1][iv1] + ( f"{key1}:{iv1:02d}", )) Fiv.append(vtot[key2][iv2] + ( f"{key2}:{iv2:02d}", )) wro.write("f") for rv in range(3): jv = rv if order == +1 else 2-rv p = Fiv[jv] assert len(p) == 5 kv = p[3] lab = p[4] if debug_face: sys.stderr.write(" v%04d = %s = ( %9.*f %9.*f %9.*f )\n" % (kv, lab, prec, p[0], prec, p[1], prec, p[2])); wro.write(" %d" % kv); Sved(Fiv[jv], Fiv[(jv + 3 + order) % 3]) wro.write("\n"); if debug_face: sys.stderr.write("\n") nrm, bar = face_normal_and_barycenter(Fiv) Tnrm.append(nrm) Tbar.append(bar) return None # Here are the faces: Ptit(f"faces with normal ±X") for sx in +1, -1: tx = 'mop'[sx+1] Ptit(f" faces in halfspace sx = {sx:+d}") # Vert face of cube: Prtt(tx+'pm', 4, tx+'pp', 4, tx+'pp', 7, sx) Prtt(tx+'mm', 4, tx+'mm', 7, tx+'mp', 4, sx) Prtt(tx+'mm', 4, tx+'pm', 4, tx+'pm', 7, sx) Prtt(tx+'mp', 7, tx+'pp', 4, tx+'mp', 4, sx) Prtt(tx+'pp', 7, tx+'pp', 4, tx+'mp', 7, sx) Prtt(tx+'pm', 4, tx+'pp', 7, tx+'pm', 7, sx) Prtt(tx+'mm', 4, tx+'pm', 7, tx+'mm', 7, sx) Prtt(tx+'mm', 7, tx+'mp', 7, tx+'mp', 4, sx) # Centrad vert wall of hole on cube: Prtt(tx+'pp', 5, tx+'mp', 5, tx+'mm', 5, sx) Prtt(tx+'pp', 5, tx+'mm', 5, tx+'pm', 5, sx) # Distad vert wall of hole on cube: Prtt(tx+'mp', 6, tx+'pp', 6, tx+'pm', 6, sx) Prtt(tx+'mp', 6, tx+'pm', 6, tx+'mm', 6, sx) # Centrad vert wall of hole on handle: Prtt(tx+'pp', 9, tx+'mp', 9, tx+'pm', 9, sx) Prtt(tx+'mm', 9, tx+'pm', 9, tx+'mp', 9, sx) # Distad vert wall of hole on handle: Prtt(tx+'pp',10, tx+'pm',10, tx+'mp',10, sx) Prtt(tx+'mp',10, tx+'pm',10, tx+'mm',10, sx) # Extreme vert wall of handle: Prtt(tx+'pp', 8, tx+'mp', 8, tx+'mm', 8, sx) Prtt(tx+'mm', 8, tx+'pm', 8, tx+'pp', 8, sx) for sz in +1, -1: tz = 'mop'[sz+1] Ptit(f" faces in quadrant sx = {sx:+d} sz = {sz:+d}") # Centrad vert wall of slot on face of cube: Prtt(tx+'m'+tz, 1, tx+'p'+tz, 1, tx+'m'+tz, 0, sx*sz) Prtt(tx+'p'+tz, 1, tx+'p'+tz, 0, tx+'m'+tz, 0, sx*sz) # Distad vert wall of slot on face of cube: debug_face = sx == -1 and sz == -1 Prtt(tx+'p'+tz, 2, tx+'m'+tz, 2, tx+'m'+tz, 3, sx*sz) debug_face = False Prtt(tx+'p'+tz, 3, tx+'p'+tz, 2, tx+'m'+tz, 3, sx*sz) # Distad vert wall of bar in hole: Prtt(tx+'p'+tz,12, tx+'m'+tz,12, tx+'p'+tz,14, sx*sz) Prtt(tx+'p'+tz,14, tx+'m'+tz,12, tx+'m'+tz,14, sx*sz) # Centrad vert wall of bar in hole: Prtt(tx+'m'+tz,11, tx+'p'+tz,11, tx+'p'+tz,13, sx*sz) Prtt(tx+'m'+tz,13, tx+'m'+tz,11, tx+'p'+tz,13, sx*sz) Ptit(f"faces with normal ±Y") for sy in +1, -1: ty = 'mop'[sy+1] Ptit(f" faces in halfspace sy = {sy:+d}") # Vert face of main cube: Ptit(f" big complicated face") Prtt('m'+ty+'m', 4, 'm'+ty+'m', 2, 'm'+ty+'m', 3, sy) Prtt('m'+ty+'m', 4, 'm'+ty+'m', 6, 'm'+ty+'m', 2, sy) Prtt('m'+ty+'m', 4, 'm'+ty+'p', 6, 'm'+ty+'m', 6, sy) Prtt('m'+ty+'m', 4, 'm'+ty+'p', 4, 'm'+ty+'p', 6, sy) Prtt('m'+ty+'p', 6, 'm'+ty+'p', 4, 'm'+ty+'p', 2, sy) Prtt('m'+ty+'p', 2, 'm'+ty+'p', 4, 'm'+ty+'p', 3, sy) Prtt('m'+ty+'m', 2, 'm'+ty+'m', 5, 'm'+ty+'m', 1, sy) Prtt('m'+ty+'m', 2, 'm'+ty+'m', 6, 'm'+ty+'m', 5, sy) Prtt('m'+ty+'p', 6, 'm'+ty+'p', 2, 'm'+ty+'p', 5, sy) Prtt('m'+ty+'p', 5, 'm'+ty+'p', 2, 'm'+ty+'p', 1, sy) Prtt('m'+ty+'m', 0, 'm'+ty+'m', 1, 'o'+ty+'m', 0, sy) Prtt('o'+ty+'m', 0, 'm'+ty+'m', 1, 'o'+ty+'m', 1, sy) Prtt('o'+ty+'m', 1, 'm'+ty+'m', 1, 'm'+ty+'m', 5, sy) Prtt('o'+ty+'m', 1, 'm'+ty+'m', 5, 'm'+ty+'p', 5, sy) Prtt('o'+ty+'p', 0, 'm'+ty+'p', 5, 'm'+ty+'p', 1, sy) Prtt('o'+ty+'p', 0, 'm'+ty+'p', 1, 'm'+ty+'p', 0, sy) Prtt('m'+ty+'p', 5, 'o'+ty+'p', 0, 'p'+ty+'p', 5, sy) Prtt('p'+ty+'p', 5, 'o'+ty+'m', 1, 'm'+ty+'p', 5, sy) Prtt('p'+ty+'m', 0, 'o'+ty+'m', 0, 'p'+ty+'m', 1, sy) Prtt('o'+ty+'m', 0, 'o'+ty+'m', 1, 'p'+ty+'m', 1, sy) Prtt('o'+ty+'m', 1, 'p'+ty+'m', 5, 'p'+ty+'m', 1, sy) Prtt('o'+ty+'m', 1, 'p'+ty+'p', 5, 'p'+ty+'m', 5, sy) Prtt('o'+ty+'p', 0, 'p'+ty+'p', 1, 'p'+ty+'p', 5, sy) Prtt('o'+ty+'p', 0, 'p'+ty+'p', 0, 'p'+ty+'p', 1, sy) Prtt('p'+ty+'m', 1, 'p'+ty+'m', 5, 'p'+ty+'m', 2, sy) Prtt('p'+ty+'m', 2, 'p'+ty+'m', 5, 'p'+ty+'m', 6, sy) Prtt('p'+ty+'p', 5, 'p'+ty+'p', 2, 'p'+ty+'p', 6, sy) Prtt('p'+ty+'p', 2, 'p'+ty+'p', 5, 'p'+ty+'p', 1, sy) Prtt('p'+ty+'m', 4, 'p'+ty+'m', 3, 'p'+ty+'m', 2, sy) Prtt('p'+ty+'m', 4, 'p'+ty+'m', 2, 'p'+ty+'m', 6, sy) Prtt('p'+ty+'m', 4, 'p'+ty+'m', 6, 'p'+ty+'p', 6, sy) Prtt('p'+ty+'m', 4, 'p'+ty+'p', 6, 'p'+ty+'p', 4, sy) Prtt('p'+ty+'p', 4, 'p'+ty+'p', 6, 'p'+ty+'p', 2, sy) Prtt('p'+ty+'p', 4, 'p'+ty+'p', 2, 'p'+ty+'p', 3, sy) for sx in +1, -1: tx = 'mop'[sx+1] Ptit(f" faces in quadrant sx = {sx:+d} sy = {sy:+d}") # Vert outer face of handle: Prtt(tx+ty+'p', 7, tx+ty+'p', 8, tx+ty+'m', 8, sx*sy) Prtt(tx+ty+'p', 7, tx+ty+'m', 8, tx+ty+'m', 7, sx*sy) # Vert inner face of handle: Prtt(tx+ty+'p',10, tx+ty+'p', 9, tx+ty+'m', 9, sx*sy) Prtt(tx+ty+'p',10, tx+ty+'m', 9, tx+ty+'m',10, sx*sy) for sz in +1, -1: tz = 'mop'[sz+1] Ptit(f" faces in octant sx = {sx:+d} sy = {sy:+d} sz = {sz:+d}") # Tip of bar in hole: Prtt(tx+ty+tz,13, tx+ty+tz,11, tx+ty+tz,14, sx*sy*sz) Prtt(tx+ty+tz,14, tx+ty+tz,11, tx+ty+tz,12, sx*sy*sz) Ptit(f"faces with normal ±Z") for sz in +1, -1: tz = 'mop'[sz+1] Ptit(f" faces in halfspace sz = {sz:+d}") # Centrad horz face of main cube: Prtt('op'+tz, 0, 'om'+tz, 0, 'pp'+tz, 0, sz) Prtt('om'+tz, 0, 'pm'+tz, 0, 'pp'+tz, 0, sz) Prtt('om'+tz, 0, 'op'+tz, 0, 'mm'+tz, 0, sz) Prtt('op'+tz, 0, 'mp'+tz, 0, 'mm'+tz, 0, sz) for sx in +1, -1: tx = 'mop'[sx+1] Ptit(f" faces in quadrant sx = {sx:+d} sz = {sz:+d}") # Distad horz face of main cube: Prtt(tx+'m'+tz, 3, tx+'m'+tz, 4, tx+'p'+tz, 4, sx*sz) Prtt(tx+'m'+tz, 3, tx+'p'+tz, 4, tx+'p'+tz, 3, sx*sz) # Horz floor face of slot on face of cube: Prtt(tx+'m'+tz, 1, tx+'m'+tz, 2, tx+'p'+tz, 2, sx*sz) Prtt(tx+'m'+tz, 1, tx+'p'+tz, 2, tx+'p'+tz, 1, sx*sz) # Distad floor of bar in hole: Prtt(tx+'p'+tz,12, tx+'p'+tz,11, tx+'m'+tz,12, sx*sz) Prtt(tx+'p'+tz,11, tx+'m'+tz,11, tx+'m'+tz,12, sx*sz) # Centrad floor of bar in hole: Prtt(tx+'p'+tz,13, tx+'p'+tz,14, tx+'m'+tz,13, sx*sz) Prtt(tx+'p'+tz,14, tx+'m'+tz,14, tx+'m'+tz,13, sx*sz) # Horz face of hole on main cube: Prtt(tx+'p'+tz, 5, tx+'p'+tz, 6, tx+'m'+tz, 5, sx*sz) Prtt(tx+'m'+tz, 5, tx+'p'+tz, 6, tx+'m'+tz, 6, sx*sz) # Horz face on handle: Prtt(tx+'p'+tz, 9, tx+'p'+tz, 8, tx+'p'+tz, 7, sx*sz) Prtt(tx+'p'+tz, 9, tx+'p'+tz,10, tx+'p'+tz, 8, sx*sz) Prtt(tx+'m'+tz, 8, tx+'p'+tz, 8, tx+'p'+tz,10, sx*sz) Prtt(tx+'m'+tz, 8, tx+'p'+tz,10, tx+'m'+tz,10, sx*sz) Prtt(tx+'m'+tz, 7, tx+'m'+tz, 8, tx+'m'+tz,10, sx*sz) Prtt(tx+'m'+tz, 7, tx+'m'+tz,10, tx+'m'+tz, 9, sx*sz) Prtt(tx+'m'+tz, 7, tx+'m'+tz, 9, tx+'p'+tz, 7, sx*sz) Prtt(tx+'m'+tz, 9, tx+'p'+tz, 9, tx+'p'+tz, 7, sx*sz) Nt_cmp = len(Tnrm) - 1 sys.stderr.write("wrote %d triangles (expected %d)\n" % (Nt_cmp, Nt_exp)) assert Nt_cmp == Nt_exp # Convert to list, leaving only one arc per edge: D = [ None ] + [ arc for arc in Ds if arc[0] < arc[1] ] Nd_cmp = len(D) - 1 sys.stderr.write("wrote %d edges (expected %d)\n" % (Nd_cmp, Nd_exp)) #assert Nd_cmp == Nd_exp return Tnrm, Tbar, D wrp.write("#declare {tag}_num_outer_chain_edges = %d;\n\n" % (tag, (2*Ni)) wrp.write("#declare {tag}_num_inner_chain_edges = %d;\n\n" % ndf) wrp.write("#declare {tag}_outer_chain_shape = %d;\n\n" % ???) wrp.write("#declare {tag}_outer_chain_even_radius = %.*f;\n\n" % (prec, R_maj)) wrp.write("#declare {tag}_outer_chain_odd_radius = %.*f;\n\n" % (prec, R_min)) wrp.write("#declare {tag}_inner_chain_even_radius = %.*f;\n\n" % (prec, R_box)) wrp.write("#declare {tag}_inner_chain_odd_radius = %.*f;\n\n" % (prec, H_box)) wrp.write("#declare {tag}_thickness = %.*f;\n\n" % (prec, Ha)) wrp.write("#declare {tag}_min_??? = %.5f; // Degrees.\n\n" % Amin) wrp.write("#declare {tag}_max_??? = %.5f; // Degrees.\n\n" % Amax)