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 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 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 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 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 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 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 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 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 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 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 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 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 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_base_points(pgon, centres=0, twist=False): N = pgon.N D = pgon.D a = pgon.angle()/2 points = [Vec(0, 0, 1), Vec(0, 0, -1)] # poles A = a B = math.pi/2 * (1 - D/N) cos_lat = 1/(math.tan(A) * math.tan(B)) sin_lat = math.sqrt(1 - cos_lat**2) p_north = Vec(sin_lat, 0, cos_lat) p_south = Vec(sin_lat, 0, -cos_lat) for i in range(N): points.append(p_north.rot_z(i*2*a)) for i in range(N): points.append(p_south.rot_z(i*2*a+a)) if centres: cos_cent_lat = math.cos(B)/math.sin(A) sin_cent_lat = math.sqrt(1 - cos_cent_lat**2) cent_north = Vec(sin_cent_lat, 0, cos_cent_lat) cent_south = Vec(sin_cent_lat, 0, -cos_cent_lat) for i in range(N): points.append(cent_north.rot_z(i*2*a+a) * centres) for i in range(N): points.append(cent_south.rot_z(i*2*a) * centres) rhombi = [] for i in range(N): rhombi.append([2 + i, 0, 2 + ((i+1) % N), 2 + N + i]) for i in range(N): rhombi.append([2 + N + i, 1, 2 + N + ((i-1) % N), 2 + i]) if twist: hx = [0, 2, 2 + N, 1, 2 + N+N//2, 2 + N-N//2] axis = points[hx[0]] + points[hx[2]] + points[hx[4]] rot = Mat.rot_axis_ang(axis.unit(), 2*math.pi/3) for i in list(range(hx[5]+1, 2 + N)) + list(range(hx[4]+1, 2 + 2*N)): points[i] = rot * points[i] if centres: for r in list(range(N-N//2, N+1)) + list(range(2*N-N//2, 2*N)): points[2 + 2*N + r] = rot * points[2 + 2*N + r] for r in list(range(N-N//2, N+1)) + list(range(2*N-N//2, 2*N)): for i in range(4): for idx, p_idx in enumerate(hx): if p_idx == rhombi[r][i]: rhombi[r][i] = hx[(idx+2) % 6] break return points, rhombi
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_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 main(): """Entry point""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( 'number_sides0', help='number of sides of polygon 0 (default: 6) ' '(or may be a polygon fraction, e.g. 5/2)', type=anti_lib.read_polygon, nargs='?', default='6') parser.add_argument( 'number_sides1', help='number of sides of polygon 0 (default: 5) ' '(or may be a polygon fraction, e.g. 5/2)', type=anti_lib.read_polygon, nargs='?', default='5') parser.add_argument( '-A', '--angle_between_axes', help='angle between the two axes (default: 60 degs)', type=float, default=60.0) 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( '-x', '--x-axis-vert', help='offset of vertex of side polygon to align with x-axis, with 0' 'being the vertex attached to the axial polygon', type=int) parser.add_argument( '-o', '--outfile', help='output file name (default: standard output)', type=argparse.FileType('w'), default=sys.stdout) args = parser.parse_args() pgon0 = args.number_sides0 pgon1 = args.number_sides1 if args.angle[1] == 'e': # units: half edge central angle turn_angle = args.angle[0] * pgon0.angle()/2 elif args.angle[1] == 'x': # units: half edge central angle turn_angle = math.pi + args.angle[0] * pgon0.angle()/2 else: # units: degrees turn_angle = math.radians(args.angle[0]) try: (points, faces) = calc_polygons2( pgon0, pgon0.circumradius(), turn_angle, pgon1, args.ratio*pgon1.circumradius(), math.radians(args.angle_between_axes)) except Exception as e: parser.error(e.args[0]) if(args.x_axis_vert is not None): v = pgon0.N + args.x_axis_vert % pgon1.N P = points[v].copy() transl = Mat.transl(Vec(0, 0, -P[2])) # for i in range(len(points)): # points[i][2] -= P[2] rot = Mat.rot_xyz(0, 0, anti_lib.angle_around_axis( P, Vec(1, 0, 0), Vec(0, 0, 1))) points = [rot*transl*point for point in points] out = anti_lib.OffFile(args.outfile) out.print_all(points, faces)
def main(): """Entry point""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( 'polygon', help='number of sides of polygon (default: 7) ' '(or may be a polygon fraction, e.g. 5/2)', type=anti_lib.read_polygon, nargs='?', default='7') parser.add_argument( 'turn_angle', help='amount to turn polygon on axis0 in degrees ' '(default (0.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, nargs='?', default='0') parser.add_argument( '-n', '--number-faces', help='number of faces in output (default: all): ' '0 - none (frame only), ' '2 - cap and adjoining polygon' '4 - two caps and two adjoining connected polygons', type=int, choices=[0, 2, 4], default=-1) 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() pgon = args.polygon N = pgon.N D = pgon.D parts = pgon.parts # if N % 2 == 0: # parser.error('polygon: %sfraction numerator must be odd' % # ('reduced ' if parts > 1 else '')) if D % 2 == 0: print(os.path.basename(__file__)+': warning: ' 'polygon: %sfraction denominator should be odd, ' 'model will only connect correctly at certain twist angles' % ('reduced ' if parts > 1 else ''), file=sys.stderr) if abs(N/D) < 3/2: parser.error('polygon: the polygon fraction cannot be less than 3/2 ' '(base rhombic tiling is not constructible)') axis_angle = math.acos(1/math.tan( math.pi*D/N)/math.tan(math.pi*(N-D)/(2*N))) if args.turn_angle[1] == 'e': # units: half edge central angle turn_angle = args.turn_angle[0] * pgon.angle()/2 elif args.turn_angle[1] == 'x': # units: half edge central angle turn_angle = math.pi + args.turn_angle[0] * pgon.angle()/2 else: # units: degrees turn_angle = math.radians(args.turn_angle[0]) turn_angle_test_val = abs(math.fmod(abs(turn_angle), 2*math.pi) - math.pi) sign_flag = 1 - 2*(turn_angle_test_val > math.pi/2) try: (points, faces) = calc_polygons(pgon, turn_angle, axis_angle, sign_flag) except Exception as e: parser.error(e.args[0]) if args.number_faces < 0: num_twist_faces = (2*N + 2)*parts else: num_twist_faces = args.number_faces*parts num_twist_points = num_twist_faces * N frame_points, frame_faces = make_frame(args.frame, pgon, axis_angle, 10) if num_twist_points + len(frame_points) == 0: parser.error('no output specified, use -f with -n 0 to output a frame') if D % 2 == 0: mat = Mat.rot_axis_ang(Vec(0, 0, 1), math.pi/N) points = [mat * p for p in points] out = anti_lib.OffFile(args.outfile) out.print_header(num_twist_points+2*N*len(frame_points), num_twist_faces+2*N*len(frame_faces)) if args.number_faces == -1: out.print_verts(rot_reflect_pair(points[:N*parts], pgon, 0, True)) for i in range(N): out.print_verts(rot_reflect_pair(points[N*parts:], pgon, i)) elif args.number_faces == 2: out.print_verts(points) elif args.number_faces == 4: out.print_verts(rot_reflect_pair(points[:N*parts], pgon, 0, True),) out.print_verts(rot_reflect_pair(points[N*parts:], pgon, 0)) for i in range(N): out.print_verts(rot_reflect_pair(frame_points, pgon, i)) for i in range(num_twist_faces): out.print_face(faces[0], i*N, (i//parts) % 2) for i in range(N): cur_num_points = num_twist_points + 2*i*len(frame_points) for j in [0, len(frame_points)]: out.print_faces(frame_faces, cur_num_points+j, col=2)
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)
def main(): """Entry point""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('number_sides0', help='number of sides of polygon 0 (default: 6) ' '(or may be a polygon fraction, e.g. 5/2)', type=anti_lib.read_polygon, nargs='?', default='6') parser.add_argument('number_sides1', help='number of sides of polygon 0 (default: 5) ' '(or may be a polygon fraction, e.g. 5/2)', type=anti_lib.read_polygon, nargs='?', default='5') parser.add_argument('-A', '--angle_between_axes', help='angle between the two axes (default: 60 degs)', type=float, default=60.0) 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( '-x', '--x-axis-vert', help='offset of vertex of side polygon to align with x-axis, with 0' 'being the vertex attached to the axial polygon', type=int) parser.add_argument('-o', '--outfile', help='output file name (default: standard output)', type=argparse.FileType('w'), default=sys.stdout) args = parser.parse_args() pgon0 = args.number_sides0 pgon1 = args.number_sides1 if args.angle[1] == 'e': # units: half edge central angle turn_angle = args.angle[0] * pgon0.angle() / 2 elif args.angle[1] == 'x': # units: half edge central angle turn_angle = math.pi + args.angle[0] * pgon0.angle() / 2 else: # units: degrees turn_angle = math.radians(args.angle[0]) try: (points, faces) = calc_polygons2(pgon0, pgon0.circumradius(), turn_angle, pgon1, args.ratio * pgon1.circumradius(), math.radians(args.angle_between_axes)) except Exception as e: parser.error(e.args[0]) if (args.x_axis_vert is not None): v = pgon0.N + args.x_axis_vert % pgon1.N P = points[v].copy() transl = Mat.transl(Vec(0, 0, -P[2])) # for i in range(len(points)): # points[i][2] -= P[2] rot = Mat.rot_xyz( 0, 0, anti_lib.angle_around_axis(P, Vec(1, 0, 0), Vec(0, 0, 1))) points = [rot * transl * point for point in points] out = anti_lib.OffFile(args.outfile) out.print_all(points, faces)
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)
def main(): """Entry point""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('polygon', help='number of sides of polygon (default: 7) ' '(or may be a polygon fraction, e.g. 5/2)', type=anti_lib.read_polygon, nargs='?', default='7') parser.add_argument('turn_angle', help='amount to turn polygon on axis0 in degrees ' '(default (0.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, nargs='?', default='0') parser.add_argument('-n', '--number-faces', help='number of faces in output (default: all): ' '0 - none (frame only), ' '2 - cap and adjoining polygon' '4 - two caps and two adjoining connected polygons', type=int, choices=[0, 2, 4], default=-1) 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() pgon = args.polygon N = pgon.N D = pgon.D parts = pgon.parts # if N % 2 == 0: # parser.error('polygon: %sfraction numerator must be odd' % # ('reduced ' if parts > 1 else '')) if D % 2 == 0: print(os.path.basename(__file__) + ': warning: ' 'polygon: %sfraction denominator should be odd, ' 'model will only connect correctly at certain twist angles' % ('reduced ' if parts > 1 else ''), file=sys.stderr) if abs(N / D) < 3 / 2: parser.error('polygon: the polygon fraction cannot be less than 3/2 ' '(base rhombic tiling is not constructible)') axis_angle = math.acos(1 / math.tan(math.pi * D / N) / math.tan(math.pi * (N - D) / (2 * N))) if args.turn_angle[1] == 'e': # units: half edge central angle turn_angle = args.turn_angle[0] * pgon.angle() / 2 elif args.turn_angle[1] == 'x': # units: half edge central angle turn_angle = math.pi + args.turn_angle[0] * pgon.angle() / 2 else: # units: degrees turn_angle = math.radians(args.turn_angle[0]) turn_angle_test_val = abs( math.fmod(abs(turn_angle), 2 * math.pi) - math.pi) sign_flag = 1 - 2 * (turn_angle_test_val > math.pi / 2) try: (points, faces) = calc_polygons(pgon, turn_angle, axis_angle, sign_flag) except Exception as e: parser.error(e.args[0]) if args.number_faces < 0: num_twist_faces = (2 * N + 2) * parts else: num_twist_faces = args.number_faces * parts num_twist_points = num_twist_faces * N frame_points, frame_faces = make_frame(args.frame, pgon, axis_angle, 10) if num_twist_points + len(frame_points) == 0: parser.error('no output specified, use -f with -n 0 to output a frame') if D % 2 == 0: mat = Mat.rot_axis_ang(Vec(0, 0, 1), math.pi / N) points = [mat * p for p in points] out = anti_lib.OffFile(args.outfile) out.print_header(num_twist_points + 2 * N * len(frame_points), num_twist_faces + 2 * N * len(frame_faces)) if args.number_faces == -1: out.print_verts(rot_reflect_pair(points[:N * parts], pgon, 0, True)) for i in range(N): out.print_verts(rot_reflect_pair(points[N * parts:], pgon, i)) elif args.number_faces == 2: out.print_verts(points) elif args.number_faces == 4: out.print_verts(rot_reflect_pair(points[:N * parts], pgon, 0, True), ) out.print_verts(rot_reflect_pair(points[N * parts:], pgon, 0)) for i in range(N): out.print_verts(rot_reflect_pair(frame_points, pgon, i)) for i in range(num_twist_faces): out.print_face(faces[0], i * N, (i // parts) % 2) for i in range(N): cur_num_points = num_twist_points + 2 * i * len(frame_points) for j in [0, len(frame_points)]: out.print_faces(frame_faces, cur_num_points + j, col=2)