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 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 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_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 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 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""" 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)