def calc_temcor_side(pgon, pyramid_ht, base_ht, freq): freq += 1 inrad = pgon.inradius() axis = Vec(-inrad, 0, pyramid_ht) # axis to rotate plane around A = Vec(0, 0, base_ht + pyramid_ht) # apex B = Vec(inrad, 0.5, base_ht) # base polygon vertex n0 = Vec.cross(A, B).unit() edge_ang = anti_lib.angle_around_axis(Vec(B[0], 0, B[2]), B, axis) ang_inc = edge_ang/freq points = [] faces = [] for i in range(freq): n_inc = Mat.rot_axis_ang(axis, i*ang_inc) * Vec(0, 1, 0) edge_v = Vec.cross(n_inc, n0).unit() last_idx = i*(i-1)//2 new_idx = i*(i+1)//2 for j in range(i + 1): v = Mat.rot_axis_ang(axis, -2*j*ang_inc) * edge_v points.append(v) if new_idx and j < i: faces.append([new_idx+j, new_idx+j+1, last_idx+j]) if j < i-1: faces.append([new_idx+j+1, last_idx+j+1, last_idx+j]) return (points, faces)
def j89_get_principal_verts(pgon, ang, flags): bad = flags.strip('ABC') if bad: raise ValueError('Unrecognised form flag(s) \'' + bad + '\'') ridge_down = 'A' in flags ridge2_down = 'B' in flags belt_back = 'C' in flags pgon.N *= 2 # p_ang2 = pgon2.angle() A, B, B2, C = get_pstt_cap_verts(pgon, ang, ridge_down) pgon.N //= 2 p_ang = pgon.angle() / 2 A2_rad = pgon.circumradius(edge) # circumradius of top polygon sq_mid = (B + B2) / 2 sq_mid_rad = Vec(sq_mid[0], sq_mid[1], 0).mag() tri_ht2 = 3 / 4 - (A2_rad - sq_mid_rad)**2 if tri_ht2 < -epsilon: raise ValueError( 'Not possible to calculate upper equilateral triangle') A2_ht = (1 - 2 * belt_back) * sqrt(tri_ht2) + B[2] A2 = Vec(A2_rad, 0, A2_ht).rot_z(p_ang / 2) A2_minus1 = A2.rot_z(-2 * p_ang) B2_minus1 = B2.rot_z(-2 * p_ang) C2 = get_three_line_vert(A2_minus1, A2, B2_minus1, B, ridge2_down) return [pt.rot_z(p_ang / 2) for pt in [A, B, B2, C, A2, C2]]
def j88_get_principal_verts(pgon, ang, flags): bad = flags.strip('AB') if bad: raise ValueError('Unrecognised form flag(s) \'' + bad + '\'') ridge = 'A' not in flags belt_back = 'B' in flags A, B, B2, C = get_pstts_cap_verts(pgon, ang, ridge) p_ang = pgon.angle() / 2 sq_mid = (B + B2.rot_z(2 * p_ang)) / 2 sq_mid_rad = Vec(sq_mid[0], sq_mid[1], 0).mag() A_rad = A[0] tri_ht2 = 3 * edge / 4 - (A_rad - sq_mid_rad)**2 if tri_ht2 < -epsilon: raise ValueError( 'Not possible to calculate upper equilateral triangle') A2_ht = (1 - 2 * belt_back) * sqrt(tri_ht2) + B[2] A2 = Vec(A_rad, 0, A2_ht).rot_z(p_ang) mid_B_B1 = Vec(B[0], 0, B[2]) ax = C - mid_B_B1 rot = Mat.rot_axis_ang(ax, math.pi) pt = A - mid_B_B1 C2 = rot * pt + mid_B_B1 return A, B, B2, C, A2, C2
def make_equ_antiherm(pgon, dih_ang): """Make a hermaphrodite with equilateral triangles""" N = pgon.N a = pgon.angle()/2 R = pgon.circumradius() tri_alt = sqrt(3)/2 tri_ht = tri_alt * sin(dih_ang) R2 = pgon.inradius() + tri_alt * cos(dih_ang) points = [Vec(0, 0, 0)]*(2*N+1) for i in range(N): points[i] = Vec(R*cos(2*i*a), R*sin(2*i*a), 0) points[i+N] = Vec(R2*cos(2*i*a+a), R2*sin(2*i*a+a), tri_ht) A = sqrt(points[0][0]**2 + points[0][1]**2) mid = anti_lib.centroid([points[N], points[2*N-1]]) B = sqrt(mid[0]**2 + mid[1]**2) z_diff = mid[2] - points[0][2] points[2*N][2] = A * z_diff / (A - B) faces = [] faces.append([i for i in range(N)]) # bottom for i in range(N): faces.append([i, (i + 1) % N, N + i]) faces.append([N + i, 2*N, N + (i + 1) % N, (i + 1) % N]) return points, faces
def calculate_belt_points(pgon, model_type): ang = pgon.angle() / 2 tan_a = math.sin(ang) / (math.cos(ang) + phi) # half of unit edge P = Vec(1 / (2 * tan_a), -1 / 2, 0) tan_b = math.sin(ang) / (math.cos(ang) + 1 / phi) # half of phi edge Q = Vec(phi / (2 * tan_b), -phi / 2, 0) bar_ht = math.cos(math.pi / 10) # planar height of pentagon "bar" diff_r = P[0] - Q[0] try: ht0 = math.sqrt(bar_ht**2 - diff_r**2) / 2 except: raise ValueError('model is not constructible') P[2] = -ht0 Q[2] = ht0 pent_ht = math.sqrt(5 + 2 * math.sqrt(5)) / 2 # planar height of pentagon x_cap = P[0] - pent_ht * (diff_r / bar_ht) z_cap = -ht0 + pent_ht * ((2 * ht0) / bar_ht) R = Vec(x_cap, 0, z_cap) S = Vec(x_cap * math.cos(ang), x_cap * math.sin(ang), -z_cap) cap_inrad = x_cap * math.cos(ang) apex_ht = 0 if model_type == 'a': try: apex_ht = z_cap + cap_inrad * (z_cap + P[2]) / (P[0] - cap_inrad) except: raise ValueError('could not calculate apex height') A = Vec(0, 0, apex_ht) return [P, Q, R, S, A]
def j89_get_principal_verts(pgon, ang, flags): bad = flags.strip('ABC') if bad: raise ValueError('Unrecognised form flag(s) \''+bad+'\'') ridge_down = 'A' in flags ridge2_down = 'B' in flags belt_back = 'C' in flags pgon.N *= 2 # p_ang2 = pgon2.angle() A, B, B2, C = get_pstt_cap_verts(pgon, ang, ridge_down) pgon.N //= 2 p_ang = pgon.angle()/2 A2_rad = pgon.circumradius(edge) # circumradius of top polygon sq_mid = (B + B2) / 2 sq_mid_rad = Vec(sq_mid[0], sq_mid[1], 0).mag() tri_ht2 = 3/4 - (A2_rad - sq_mid_rad)**2 if tri_ht2 < -epsilon: raise ValueError( 'Not possible to calculate upper equilateral triangle') A2_ht = (1 - 2*belt_back)*sqrt(tri_ht2) + B[2] A2 = Vec(A2_rad, 0, A2_ht).rot_z(p_ang/2) A2_minus1 = A2.rot_z(-2*p_ang) B2_minus1 = B2.rot_z(-2*p_ang) C2 = get_three_line_vert(A2_minus1, A2, B2_minus1, B, ridge2_down) return [pt.rot_z(p_ang/2) for pt in [A, B, B2, C, A2, C2]]
def get_default_angle(pgon, radius_top, radius_bottom, height): a = pgon.angle() / 2 P1 = Vec(radius_top, 0, height) Q1 = Vec(radius_bottom * cos(a), radius_bottom * sin(a), 0.0) Q2 = Vec(radius_bottom * cos(-a), radius_bottom * sin(-a), 0.0) v0 = (Q1 - P1).unit() v1 = (Q2 - P1).unit() cos_a = anti_lib.safe_for_trig(Vec.dot(v0, v1)) ang = acos(cos_a) return pi - ang
def read_coords(): points = [] while 1: line = sys.stdin.readline() if line == '\n': continue if line == "": break m = re.search('^ *([^ ,]+) *,? *([^ ,]+) *,? *([^ ,\n]+) *$', line) if not m: sys.stderr.write( 'error: did not find x, y and z values in following ' 'line (1):\n') sys.stderr.write(line) sys.exit(1) else: try: points.append( Vec(*[float(m.group(i)) for i in range(1, 3 + 1)])) except: sys.stderr.write( 'error: did not find x, y and z values in following ' 'linei (2):\n') sys.stderr.write(line) sys.exit(1) return points
def main(): """Entry point""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('num_balls_on_ring', help='Number of balls on each ring', type=int, nargs='?', default=10) parser.add_argument('-o', '--outfile', help='output file name (default: standard output)', type=argparse.FileType('w'), default=sys.stdout) args = parser.parse_args() ring_centres = read_coords() if not len(ring_centres): parser.error('no coordinates in input') R = ring_centres[0].mag() dist = find_minimum_separation(ring_centres) a = math.asin(dist / (2 * R)) ball_points, ball_ang = make_ring(R, args.num_balls_on_ring, a) print('ball radius = %.14f' % (2 * R * math.sin(ball_ang / 2)), file=sys.stderr) out = anti_lib.OffFile(args.outfile) out.print_header(len(ring_centres) * len(ball_points), 0) for cent in ring_centres: mat = Mat.rot_from_to(Vec(0, 0, 1), cent) out.print_verts([mat * p for p in ball_points])
def touching_spheres(R, p0, r0, p1, r1, p2, r2): # Add R to the radii, the intersection points of the three sheres are # the centres of the touching ball(s) r0 += R r1 += R r2 += R # find centre of circle nd readius where first two spheres intersect pc1, rc1 = sphere_intersection(p0, r0, p1, r1) if pc1 is None: return None, None # the second circle is defined by th plane of first circle intersecting # the third sphere # find centre of second circle p2_pc1 = pc1 - p2 p0_p1 = p1 - p0 unit_p0_p1 = p0_p1.unit() len_p2_pc2 = Vec.dot(p2_pc1, unit_p0_p1) # is circle outside of third sphere if not (r2 > len_p2_pc2 > -r2): return None, None p2_pc2 = unit_p0_p1 * len_p2_pc2 pc2 = p2 + p2_pc2 rc2 = math.sqrt(r2**2 - len_p2_pc2**2) # find pt - intersection of plane of sphere centres and line joining # touching spheres, and R distance pt to a touching sphere centre. pt, R = sphere_intersection(pc1, rc1, pc2, rc2) if pt is None: return None, None # make a vector length R perpendicular to the plane of the sphere centres R_norm = Vec.cross(p1 - p0, p2 - p0) len_R_norm = R_norm.mag() if not len_R_norm: return None, None R_norm = R_norm * R/len_R_norm return pt + R_norm, pt - R_norm
def get_pstt_cap_verts(pgon, ang, ridge_down): p_ang = pgon.angle()/2 rad = pgon.circumradius(edge) # Polygon vertex A = Vec(rad, 0, 0) # First and last square vertices B = Vec(rad + edge*cos(ang)*cos(p_ang), edge*cos(ang)*sin(p_ang), edge*sin(ang)) B2 = Vec(B[0], -B[1], B[2]).rot_z(2*p_ang) A_minus1 = A.rot_z(-2*p_ang) B2_minus1 = B2.rot_z(-4*p_ang) C = get_three_line_vert(A, A_minus1, B, B2_minus1, ridge_down) return A, B, B2, C
def get_default_angle(pgon, radius_top, radius_bottom, height): a = pgon.angle()/2 P1 = Vec(radius_top, 0, height) Q1 = Vec(radius_bottom * cos(a), radius_bottom * sin(a), 0.0) Q2 = Vec(radius_bottom * cos(-a), radius_bottom * sin(-a), 0.0) v0 = (Q1-P1).unit() v1 = (Q2-P1).unit() cos_a = anti_lib.safe_for_trig(Vec.dot(v0, v1)) ang = acos(cos_a) return pi-ang
def make_frame(frame_elems, axis_angle, rad, num_segs): points = [] faces = [] if frame_elems: v0 = Vec(0, 0, -1) v1 = Vec(math.sin(axis_angle), 0, -math.cos(axis_angle)) if 'r' in frame_elems: ps, fs = make_arc(v0, v1, num_segs, 0) points += ps faces += fs if 'a' in frame_elems: num_pts = len(points) points += [v0, -v0, v1, -v1] faces += [[num_pts + 0, num_pts + 1], [num_pts + 2, num_pts + 3]] points = [rad * p for p in points] faces += [[i] for i in range(len(points))] return points, faces
def calc_polygons2(pgon0, r0, ang, pgon1, r1, angle_between_axes): # rotate initial vertex of polygon0 by ang. Common # point lies on a line through vertex in the direction # of axis0 (z-axis). Common vertex lies on a cylinder # radius r1 with axis on axis1. # rotate model so axis1 is on z-axis, to simplify the # calculation of the intersection rot = Mat.rot_axis_ang(Vec(0, 1, 0), angle_between_axes) # initial vertex turned on axis V = Vec(r0, 0, 0).rot_z(ang) # vertex in rotated model Q = rot * V # direction of line in rotated model u = rot * Vec(0, 0, 1) # equation of line is P = Q + tu for components x y z and parameter t # equation of cylinder at z=0 is x^2 + y^2 = r1^2 a = u[0]**2 + u[1]**2 b = 2 * (Q[0] * u[0] + Q[1] * u[1]) c = Q[0]**2 + Q[1]**2 - r1**2 disc = b**2 - 4 * a * c if disc < -epsilon: raise Exception("model is not geometrically constructible") elif disc < 0: disc = 0 t = (-b - math.sqrt(disc)) / (2 * a) # negative gives most distant point P = V + Vec(0, 0, t) # the common point points = [] points += [P.rot_z(i * pgon0.angle()) for i in range(pgon0.N)] faces = [[i for i in range(pgon0.N)]] Q = rot * P rot_inv = Mat.rot_axis_ang(Vec(0, 1, 0), -angle_between_axes) points += [rot_inv * Q.rot_z(i * pgon1.angle()) for i in range(pgon1.N)] faces += [[i + pgon0.N for i in range(pgon1.N)]] return points, faces
def bifrustum_model(pgon, arg_type): """Construct bifrustum model with golden trapziums""" N = pgon.N ang = pgon.angle() / 2 R0 = phi / (2 * sin(ang)) R1 = 1 / (2 * sin(ang)) ht = sqrt(phi**2 - (R0 - R1)**2) points = [] points += [Vec(R0, 0, ht).rot_z(2 * i * ang) for i in range(N)] points += [Vec(R1, 0, 0).rot_z(2 * i * ang) for i in range(N)] points += [Vec(R0, 0, -ht).rot_z(2 * i * ang) for i in range(N)] faces = [] faces.append([i for i in range(N)]) faces.append([i + 2 * N for i in range(N)]) for i in range(N): faces.append([i, (i + 1) % N, N + (i + 1) % N, N + i]) faces.append([N + i, N + (i + 1) % N, 2 * N + (i + 1) % N, 2 * N + i]) return points, faces
def calc_polygons(pgon0, pgon1, ang, ratio, angle_between_axes): # rotate initial vertex of polygon0 by ang. Common # point lies on a line through vertex in the direction # of axis0 (z-axis). Common vertex lies on a cylinder # radius r1 with axis on axis1. # rotate model so axis1 is on z-axis, to simplify the # calculation of the intersection rot = Mat.rot_axis_ang(Vec(0, 1, 0), angle_between_axes) # initial vertex turned on axis V = Vec(pgon0.circumradius(), 0, 0).rot_z(ang) # vertex in rotated model Q = rot * V # direction of line in rotated model u = rot * Vec(0, 0, 1) # equation of line is P = Q + tu for components x y z and parameter t # equation of cylinder at z=0 is x^2 + y^2 = r1^2 a = u[0]**2 + u[1]**2 b = 2 * (Q[0] * u[0] + Q[1] * u[1]) c = Q[0]**2 + Q[1]**2 - (pgon1.circumradius() * ratio)**2 disc = b**2 - 4 * a * c if disc < -epsilon: raise Exception("model is not geometrically constructible") elif disc < 0: disc = 0 t = (-b - math.sqrt(disc)) / (2 * a) P = V + Vec(0, 0, t) # the common point points = pgon0.get_points(P) faces = pgon0.get_faces() Q = rot * P rot_inv = Mat.rot_axis_ang(Vec(0, 1, 0), -angle_between_axes) points += [rot_inv * p for p in pgon1.get_points(Q)] faces += pgon1.get_faces(pgon0.N * pgon0.parts) return points, faces
def get_three_line_vert(A0, A1, B0, B1, ridge_down): A_edge = A1 - A0 B_edge = B0 - A0 cos_gamma = Vec.dot(A_edge.unit(), B_edge.unit()) if abs(cos_gamma) > 1: cos_gamma /= abs(cos_gamma) gamma = acos(cos_gamma) d = (edge / 2) * tan(gamma / 2) h2 = 3 * edge / 4 - d**2 if h2 < -epsilon: raise ValueError( 'Not possible to calculate a three-line-fill triangle') h = (1 - 2 * ridge_down) * sqrt(h2) A_mid = (A0 + A1) / 2 B_mid = (B0 + B1) / 2 mid_dir = (B_mid - A_mid).unit() norm = Vec.cross(B_edge, A_edge).unit() P_from_A_mid = mid_dir * d + norm * h return A_mid + P_from_A_mid
def make_arc(v0, v1, num_segs, start_idx): axis = Vec.cross(v0, v1).unit() ang = anti_lib.angle_around_axis(v0, v1, axis) points = [v0] faces = [] mat = Mat.rot_axis_ang(axis, ang/num_segs) for i in range(num_segs): # accumulated error points.append(mat * points[-1]) faces.append([start_idx + i, start_idx + i+1]) return points, faces
def get_three_line_vert(A0, A1, B0, B1, ridge_down): A_edge = A1 - A0 B_edge = B0 - A0 cos_gamma = Vec.dot(A_edge.unit(), B_edge.unit()) if abs(cos_gamma) > 1: cos_gamma /= abs(cos_gamma) gamma = acos(cos_gamma) d = (edge/2)*tan(gamma/2) h2 = 3*edge/4 - d**2 if h2 < -epsilon: raise ValueError( 'Not possible to calculate a three-line-fill triangle') h = (1 - 2*ridge_down) * sqrt(h2) A_mid = (A0 + A1) / 2 B_mid = (B0 + B1) / 2 mid_dir = (B_mid - A_mid).unit() norm = Vec.cross(B_edge, A_edge).unit() P_from_A_mid = mid_dir*d + norm*h return A_mid + P_from_A_mid
def antiprism_model(pgon, arg_type): """Construct antiprism model with golden trapziums""" N = pgon.N ang = pgon.angle() / 2 P = tri_antiprism_pt(pgon, gold_trap_diag, 1, phi) Q = Vec(P[0], -P[1], -P[2]) points = [] for i in range(N): points.append(P.rot_z(2 * i * ang)) for i in range(N): points.append(Q.rot_z(2 * i * ang)) R = points[N - 1] + (P - Q) * phi for i in range(N): points.append(R.rot_z(2 * i * ang)) S = Vec(R[0], -R[1], -R[2]) for i in range(N): points.append(S.rot_z(2 * i * ang)) faces = [] faces.append([2 * N + i for i in range(N)]) faces.append([3 * N + i for i in range(N)]) for i in range(N): faces.append([i, 2 * N + i, 2 * N + ((i + 1) % N)]) faces.append([N + i, 3 * N + i, 3 * N + ((i - 1) % N)]) faces.append( [i, 2 * N + ((i + 1) % N), ((i + 1) % N), N + ((i + 1) % N)]) faces.append([i, N + ((i + 1) % N), 3 * N + i, N + i]) return points, faces
def make_frame(frame_elems, pgon, axis_angle, num_segs): points = [] faces = [] if frame_elems: v0 = Vec(0, 0, 1) v1 = Vec(-math.sin(axis_angle), 0, math.cos(axis_angle)) v2 = v1.rot_z(pgon.angle()/2) v2[2] *= -1 if 'r' in frame_elems: ps, fs = make_arc(v0, v1, num_segs, 0) points += ps faces += fs ps, fs = make_arc(v1, v2, num_segs, num_segs+1) points += ps faces += fs if 'a' in frame_elems: faces += [[len(points)+i, len(points)+i+1] for i in range(0, 6, 2)] points += [v0, -v0, v1, -v1, v2, -v2] rad = calc_polygons(pgon, 0, axis_angle, -1)[0][0].mag() points = [rad * p for p in points] faces += [[i] for i in range(len(points))] return points, faces
def tri_antiprism_pt(pgon, a, b, c): """Return construct point for antiprism model""" s = (a + b + c) / 2 alt = 2 * math.sqrt(s * (s - a) * (s - b) * (s - c)) / a # planar height of A ang = pgon.angle() / 2 R = pgon.circumradius(a) # polygon circumradius (side a) r = pgon.inradius(a) # polygon inradius (side a) a0 = math.sqrt(b**2 - alt**2) # from C to perpendicular from A y = a0 - a / 2 x = math.sqrt(R**2 - y**2) alt_proj_x = x - r # projection of alt onto x dir z = math.sqrt(alt**2 - alt_proj_x**2) / 2 ang_to_A = math.atan2(y, x) # adjust point for dih axis ang_off = (ang_to_A - ang) / 2 return Vec(x, y, z).rot_z(-ang_off)
def make_ring(R, N, a): b = ring_ball_ang(N, a) P = Vec(R*math.sin(a-b), 0, R*math.cos(a-b)) return [P.rot_z(2*math.pi*i/N) for i in range(N)], b
def main(): """Entry point""" epilog = ''' notes: Depends on anti_lib.py. Use poly_kscope to repeat the model. examples: Icosahedral model twister.py I[5,3] | poly_kscope -s I| antiview Twisted icosahedral model with hexagons twister.py I[5,3] 1 2 -a 0.5e | poly_kscope -s I| antiview Dihedral model with frame twister.py D6[6,2] 1 2 -f ra | poly_kscope -s D6 | antiview ''' parser = argparse.ArgumentParser(formatter_class=anti_lib.DefFormatter, description=__doc__, epilog=epilog) parser.add_argument( 'symmetry_axes', help=']Axes given in the form: sym_type[axis1,axis2]id_no\n' ' sym_type: rotational symmetry group, can be T, O, I,\n' ' or can be D followed by an integer (e.g D5)\n' ' axis1,axis2: rotational order of each of the two axes\n' ' id_no (default: 1): integer to select between\n' ' non-equivalent pairs of axes having the same\n' ' symmetry group and rotational orders\n' 'e.g. T[2,3], I[5,2]2, D7[7,2], D11[2,2]4\n' 'Axis pairs are from the following\n' ' T: [3, 3], [3, 2], [2, 2]\n' ' O: [4, 4], [4, 3], [4, 2]x2, [3, 3], [3, 2]x2,\n' ' [2, 2]x2\n' ' I: [5, 5], [5, 3]x2, [5, 2]x3, [3, 3]x2, [3, 2]x4,\n' ' [2, 2]x4\n' ' Dn: [n 2], [2,2]x(n/2 rounded down)\n', type=read_axes, nargs='?', default='O[4,3]') parser.add_argument( 'multiplier1', help='integer or fractional multiplier for axis 1 ' '(default: 1). If the axis is N/D and the ' 'multiplier is n/d the polygon used will be N*n/d', type=read_axis_multiplier, nargs='?', default="1") parser.add_argument( 'multiplier2', help='integer or fractional multiplier for axis 2 ' '(default: 1). If the axis is N/D and the ' 'multiplier is n/d the polygon used will be N*n/d', type=read_axis_multiplier, nargs='?', default="1") parser.add_argument( '-r', '--ratio', help='ratio of edge lengths (default: 1.0)', type=float, default=1.0) parser.add_argument( '-a', '--angle', help='amount to turn polygon on axis0 in degrees ' '(default: 0), or a value followed by \'e\', ' 'where 1.0e is half the central angle of an edge, ' 'which produces an edge-connected model (negative ' 'values may have to be specified as, e.g. -a=-1.0e), ' 'or a value followed by x, which is like e but with ' 'a half turn offset', type=read_turn_angle, default='0') parser.add_argument( '-f', '--offset', help='amount to offset the first polygon to avoid ' 'coplanarity with the second polygon, for example ' '0.0001 (default: 0.0)', type=float, default=0.0) parser.add_argument( '-F', '--frame', help='include frame elements in output, any from: ' 'r - rhombic tiling edges, ' 'a - rotation axes (default: no elements)', type=frame_type, default='') parser.add_argument( '-o', '--outfile', help='output file name (default: standard output)', type=argparse.FileType('w'), default=sys.stdout) args = parser.parse_args() axis_pair = args.symmetry_axes pgons = [] for i, m in enumerate([args.multiplier1, args.multiplier2]): try: pgons.append(anti_lib.Polygon( axis_pair['nfolds'][i]*m.N, m.D)) except Exception as e: parser.error('multiplier%d: ' % (i) + e.args[0]) axes = axis_pair['axes'] if args.angle[1] == 'e': # units: half edge central angle turn_angle = args.angle[0] * pgons[0].angle()/2 elif args.angle[1] == 'x': # units: half edge central angle turn_angle = math.pi + args.angle[0] * pgons[0].angle()/2 else: # units: degrees turn_angle = math.radians(args.angle[0]) sin_angle_between_axes = Vec.cross(axes[0], axes[1]).mag() if abs(sin_angle_between_axes) > 1: sin_angle_between_axes = -1 if sin_angle_between_axes < 0 else 1 angle_between_axes = math.asin(sin_angle_between_axes) if(Vec.dot(axes[0], axes[1]) > 0): axes[1] *= -1 try: (points, faces) = calc_polygons( pgons[0], pgons[1], turn_angle, args.ratio, angle_between_axes) except Exception as e: parser.error(e.args[0]) if args.offset: for i in range(len(faces[0])): points[i][2] += args.offset frame_rad = calc_polygons(pgons[0], pgons[1], 0, args.ratio, angle_between_axes)[0][0].mag() frame_points, frame_faces = make_frame(args.frame, angle_between_axes, frame_rad, 10) rot = Mat.rot_from_to2(Vec(0, 0, 1), Vec(1, 0, 0), axes[0], axes[1]) all_points = [rot * point for point in points+frame_points] out = anti_lib.OffFile(args.outfile) out.print_header(len(all_points), len(faces)+len(frame_faces)) out.print_verts(all_points) for i in range(pgons[0].parts+pgons[1].parts): out.print_face(faces[i], 0, int(i < pgons[0].parts)) out.print_faces(frame_faces, len(points), 2)