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