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 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 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 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_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 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 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 rot_reflect_pair(points, pgon, d, rev=False): mat0 = Mat.rot_axis_ang(Vec(0, 0, 1), (d + 0.5) * pgon.angle()) pts = [mat0 * Vec(p[0], p[1], -p[2]) for p in points] mat1 = Mat.rot_axis_ang(Vec(0, 0, 1), d * pgon.angle()) if rev: return [mat1 * p for p in points] + pts else: return pts + [mat1 * p for p in points]
def get_tetrahedron(verts, faces): """Return an tetrahedron""" X = 1 / math.sqrt(3) verts.extend( [Vec(-X, X, -X), Vec(-X, -X, X), Vec(X, X, X), Vec(X, -X, -X)]) faces.extend([(0, 1, 2), (0, 3, 1), (0, 2, 3), (2, 1, 3)])
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 get_principal_verts(p_ang, ang, cross_flag): """Calculate example vertex coordinates""" edge = 1 # polygon edge length rad = edge/(2*sin(p_ang)) # circumradius of base polygon belt_irad = edge*cos(ang)/(2*tan(p_ang/2)) # inradius of belt polygon tri_ht = edge * sqrt(3)/2 # Cut polygon cylinder (radius rad) by plane normal to a belt edge # and the through its centre point # Distance to this ellipse should be tri_ht S2 = sin(ang)**2 if abs(S2) < EPSILON: x = (1-2*cross_flag)*rad val = tri_ht**2 - (belt_irad - x)**2 if val < -EPSILON: raise ValueError('triangle cannot reach base polygon ' '(2nd coordinate)') elif val < 0.0: val = 0.0 y = sqrt(val) else: a = (1-S2) b = 2*belt_irad*S2 c = S2*tri_ht**2 - rad**2 - S2*belt_irad**2 disc = b**2 - 4*a*c if disc < - EPSILON: raise ValueError('triangle cannot reach base polygon ' '(1st coordinate)') elif disc < 0.0: disc = 0.0 if not a: raise ValueError('triangle cannot reach base polygon ' '(vertical edges)') x = (-b + (1-2*cross_flag)*sqrt(disc))/(2*a) val = (rad**2 - x**2)/S2 if val < -EPSILON: raise ValueError('triangle cannot reach base polygon ' '(2nd coordinate)') elif val < 0.0: val = 0.0 y = sqrt(val) return (Vec(x, y*sin(ang), y*cos(ang)), # base Vec(belt_irad, 0.5*edge*cos(ang), 0.5*edge*sin(ang))) # belt
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 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 calc_points(args): points = [] number_turns = args.number_turns if not number_turns: number_turns = 1e-12 if args.distance_between_points: rad = 2 * args.distance_between_points else: # half distance between turns on a rad 1 sphere rad = 2 * math.sqrt(1 - math.cos(math.pi / (number_turns - 1))) a0 = 0 cur_point = Vec(0.0, 1.0, 0.0) points.append(cur_point) delt = math.atan(rad / 2) / 10 a1 = a0 + .0999999 * delt # still within sphere while a1 < math.pi: if (cur_point - angle_to_point(a1, number_turns)).mag() > rad: a0 = psearch(a1 - delt, a1, a0, rad, number_turns) cur_point = angle_to_point(a0, number_turns) points.append(cur_point) a1 = a0 a1 += delt return points
def angle_to_point(a, number_turns): a2 = 2 * a * number_turns # angle turned around y axis r = math.sin(a) # distance from y axis y = math.cos(a) x = r * math.sin(a2) z = r * math.cos(a2) return Vec(x, y, z)
def make_verts(lat_type, width, container_is_cube): """Generate coordinates""" verts = [] if container_is_cube: coords_start = 0 else: # sphere coords_start = -width coords_end = width coord_test = eval(lat_type + "_coord_test") for x in range(coords_start, coords_end + 1): for y in range(coords_start, coords_end + 1): for z in range(coords_start, coords_end + 1): if container_is_cube or dist2_inside(Vec(x, y, z), width): if coord_test(x, y, z): verts.append(Vec(x, y, z)) return verts
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 calc_points(args): points = [] vert_ang_inc = math.pi/(args.number_circles-1) ball_dia = math.sqrt(2 - 2*math.cos(vert_ang_inc)) num_circles = args.number_circles horz_stagger = 0.0 circles = [] for circle in range(num_circles): if args.points_list: num_balls = args.points_list[circle] if num_balls == -1: num_balls = args.default_number_points else: num_balls = args.default_number_points vert_ang = circle*vert_ang_inc if num_balls == -1: rad = math.sin(vert_ang) try: num_balls = int(math.floor(2*math.pi / math.acos((2*rad*rad-ball_dia*ball_dia) / (2*rad*rad)) * args.aspect_ratio)) except: num_balls = 1 horz_ang_inc = 2*math.pi/num_balls if num_balls > 0 else 0 circles.append([vert_ang, num_balls, horz_ang_inc, 0.0]) if args.stagger: for circle in range(num_circles//2, -1, -1): if circle == num_circles//2: circles[circle][3] = circles[circle][2]/4 circles[circle+1][3] = -circles[circle][2]/4 else: for circ in [[circle, circle+1], [num_circles-circle-1, num_circles-circle-2]]: horz_stagger = circles[circ[1]][3] if horz_stagger > 0: horz_stagger -= circles[circ[1]][2]/2 else: horz_stagger += circles[circ[1]][2]/2 circles[circ[0]][3] = horz_stagger for circle in range(args.exclude_poles, num_circles-args.exclude_poles): vert_ang = circles[circle][0] num_balls = circles[circle][1] horz_ang_inc = circles[circle][2] horz_stagger = circles[circle][3] rad = math.sin(vert_ang) for n in range(num_balls): horz_ang = n*horz_ang_inc+horz_stagger points.append(Vec(rad*math.cos(horz_ang), rad*math.sin(horz_ang), math.cos(vert_ang))) return points
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 make_hexagonal_tiling(freq): grads = freq + 1 points = [[0, 0, 0]] faces = [] for i in range(6): rot = Mat.rot_axis_ang(Vec(0, 0, 1), i * math.pi / 3) for j in range(1, grads): points.append(rot * Vec(j, 0, 0)) for t in range(6): p1 = ((t + 1) % 6) * freq + 1 p2 = t * freq + 1 vec = points[p1] - points[p2] for i in range(1, freq): for j in range(0, i): points.append(points[t * freq + 1 + i] + vec * (j + 1)) faces += get_tri_faces(t, freq) return points, faces, points[freq].mag()
def make_sq_x_tiling(freq): points = [] faces = [] grads = 2 * freq + 1 for i in range(grads): for j in range(grads): points.append(Vec(i - freq, j - freq, 0)) for i in range(grads - 1): for j in range(grads - 1): cent_idx = len(points) points.append(Vec(i - freq + 0.5, j - freq + 0.5, 0)) face = [ j + i * grads, j + 1 + i * grads, j + 1 + (i + 1) * grads, j + (i + 1) * grads ] for v in range(4): faces.append([face[v], face[(v + 1) % 4], cent_idx]) return points, faces, points[0].mag()
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(pgon, ang, angle_between_axes, sign_flag=1): # rotate initial vertex of first polygon by ang. Common # point lies on a line through vertex in the direction # of first axis (z-axis). Common point lies on a cylinder # with the circumradius for radius and second axis for axis. # 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) R = pgon.circumradius() V = Vec(R, 0, 0).rot_z(ang) # initial vertex turned on axis Q = rot * V # vertex in rotated model u = rot * Vec(0, 0, 1) # direction of line in rotated model # 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 - R**2 disc = b**2 - 4 * a * c if disc < -epsilon: raise Exception("model is not geometrically constructible") elif disc < 0: disc = 0 # The sign flag, which changes for the range 90 to 270 degrees, allows # the model to reverse, otherwise the model breaks apart in this range. t = (-b + sign_flag * math.sqrt(disc)) / (2 * a) P = V + Vec(0, 0, t) # the common point points = pgon.get_points(P) faces = pgon.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 pgon.get_points(Q)] faces += pgon.get_faces(pgon.N * pgon.parts) 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 parallel_project(points, R, rad): new_points = [] for point in points: x = point[0] * rad / R y = point[1] * rad / R try: z = math.sqrt(1 - (x**2 + y**2)) except: z = 0 new_points.append(Vec(x, y, -z)) return new_points
def make_jitterbug(pgon, ang, cross, right_fill, left_fill, delete_main_faces): """Make the jitterbug model""" p_ang = pgon.angle()/2 A, B = get_principal_verts(p_ang, ang, cross) A2 = Vec(A[0], -A[1], -A[2]).rot_z(-p_ang) A = Vec(A2[0], A2[1], -A2[2]).rot_z(p_ang) B2 = Vec(B[0], -B[1], -B[2]) N = pgon.N points = [] for pt in [A, A2, B2, B]: points += [pt.rot_z(i*2*p_ang) for i in range(N)] faces = [] if not delete_main_faces: faces.append([i for i in range(N)]) # top faces.append([2*N-1 - i for i in range(N)]) # bottom # side triangles for i in range(N): faces.append([2*N+i, 3*N+i, (i+N) % N]) faces.append([3*N+i, 2*N+(i+1) % N, N + (i+1+N) % N]) # fill holes # Top hole: i, (i+1) % N, 2*N+(i+1) % N, 3*N+i # Bottom hole: N+i, N+(i-1+N) % N, 2*N+(i-1+N) % N, 3*N+(i-1) % N if right_fill: for i in range(N): faces.append([(i+1) % N, i, 3*N+i]) faces.append([3*N+i, (i+1) % N, 2*N+(i+1) % N]) faces.append([N+(i-1+N) % N, N+i, 2*N+(i-1+N) % N]) faces.append([2*N+(i-1+N) % N, N+i, 3*N+(i-1) % N]) if left_fill: for i in range(N): faces.append([i, (i+1) % N, 2*N+(i+1) % N]) faces.append([2*N+(i+1) % N, 3*N+i, i]) faces.append([N+i, N+(i-1+N) % N, 3*N+(i-1) % N]) faces.append([3*N+(i-1) % N, 2*N+(i-1+N) % N, N+(i-1+N) % N]) return points, faces
def make_sq_tiling(freq): points = [] faces = [] grads = 2 * freq + 1 for i in range(grads): for j in range(grads): points.append(Vec(i - freq, j - freq, 0)) if i < grads - 1 and j < grads - 1: faces.append([ j + i * grads, j + 1 + i * grads, j + 1 + (i + 1) * grads, j + (i + 1) * grads ]) return points, faces, points[0].mag()
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 project_onto_sphere(points, ht, proj_ht): new_points = [] for point in points: a = point[0] b = point[1] d = ht - proj_ht if abs(d) < epsilon: z = 1 x = 0 y = 0 else: z = (a**2 + b**2 - d**2) / (a**2 + b**2 + d**2) x = a * (z - 1) / d y = b * (z - 1) / d new_points.append(Vec(x, y, -z)) return new_points