def create_ommatidum(outer_sphere_radius, inner_sphere_radius, ommatidum_radius): """Create an hexagonal based pyramid.""" # Outer shell outer_shell = [tuple(np.round(sph2cart(ommatidum_radius, az, 0), 2)) for az in np.arange(0, 359, 60)] outer_points = [[0, 0, 0]] + [[outer_sphere_radius, x, y] for x, y, _ in outer_shell] # Inner shell inner_shell = [tuple(np.round( sph2cart(ommatidum_radius - thickness, az, 0), 2)) for az in np.arange(0, 359, 60)] inner_points = [[0, 0, 0]] + [[outer_sphere_radius, x, y] for x, y, _ in inner_shell] # Define Faces faces = [ [0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 6], [0, 6, 1], [1, 2, 3, 4, 5, 6]] # Create ommatidum ommatidum = solid.difference()( solid.hull()(solid.polyhedron(outer_points, faces)), solid.hull()(solid.polyhedron(inner_points, faces)), solid.sphere(inner_sphere_radius) ) return ommatidum
def create_model(data, rows, columns): hexes = to_base(int.from_bytes(data, 'big'), 720) assert len(hexes) == rows * columns sts = [] for row in range(rows): for col in range(columns): order = permute.permute([1, 2, 3, 4, 5, 6], hexes[row * 9 + col]) heights = [1 + 1.5 * i / len(order) for i in order] st = stump(heights) xdiff = 1.5 * col xdiff *= 1.2 ydiff = (-3**0.5) * row - (3**0.5 / 2) * col ydiff *= 1.2 st = solid.translate([xdiff, ydiff, 0])(st) sts.append(st) base = solid.translate([-3.4, -3.2, 0])(solid.rotate([0, 0, -30])( solid.scale([22, 6.5, 0.3])(solid.cube(1)))) x, y = 0.5, (3**0.5 / 2) / 2 marker = solid.translate([-1.5, -2.5, 0])( solid.polyhedron( points=[ # bottom (-x, -y, 0), (x, -y, 0), (0, y, 0), # top (-x, -y, 1), (x, -y, 1), (0, y, 1) ], faces=[ # bottom (0, 1, 2), # top (5, 4, 3), # sides (0, 3, 4, 1), (1, 4, 5, 2), (2, 5, 3, 0) ])) return solid.union()([base, solid.union()(sts), marker])
def extrude_along_path( shape_pts:Points, path_pts:Points, scale_factors:Sequence[float]=None) -> OpenSCADObject: # Extrude the convex curve defined by shape_pts along path_pts. # -- For predictable results, shape_pts must be planar, convex, and lie # in the XY plane centered around the origin. # # -- len(scale_factors) should equal len(path_pts). If not present, scale # will be assumed to be 1.0 for each point in path_pts # -- Future additions might include corner styles (sharp, flattened, round) # or a twist factor polyhedron_pts:Points= [] facet_indices:List[Tuple[int, int, int]] = [] if not scale_factors: scale_factors = [1.0] * len(path_pts) # Make sure we've got Euclid Point3's for all elements shape_pts = euclidify(shape_pts, Point3) path_pts = euclidify(path_pts, Point3) src_up = Vector3(*UP_VEC) for which_loop in range(len(path_pts)): path_pt = path_pts[which_loop] scale = scale_factors[which_loop] # calculate the tangent to the curve at this point if which_loop > 0 and which_loop < len(path_pts) - 1: prev_pt = path_pts[which_loop - 1] next_pt = path_pts[which_loop + 1] v_prev = path_pt - prev_pt v_next = next_pt - path_pt tangent = v_prev + v_next elif which_loop == 0: tangent = path_pts[which_loop + 1] - path_pt elif which_loop == len(path_pts) - 1: tangent = path_pt - path_pts[which_loop - 1] # Scale points this_loop:Point3 = [] if scale != 1.0: this_loop = [(scale * sh) for sh in shape_pts] # Convert this_loop back to points; scaling changes them to Vectors this_loop = [Point3(v.x, v.y, v.z) for v in this_loop] else: this_loop = shape_pts[:] # type: ignore # Rotate & translate this_loop = transform_to_point(this_loop, dest_point=path_pt, dest_normal=tangent, src_up=src_up) # Add the transformed points to our final list polyhedron_pts += this_loop # And calculate the facet indices shape_pt_count = len(shape_pts) segment_start = which_loop * shape_pt_count segment_end = segment_start + shape_pt_count - 1 if which_loop < len(path_pts) - 1: for i in range(segment_start, segment_end): facet_indices.append( (i, i + shape_pt_count, i + 1) ) facet_indices.append( (i + 1, i + shape_pt_count, i + shape_pt_count + 1) ) facet_indices.append( (segment_start, segment_end, segment_end + shape_pt_count) ) facet_indices.append( (segment_start, segment_end + shape_pt_count, segment_start + shape_pt_count) ) # Cap the start of the polyhedron for i in range(1, shape_pt_count - 1): facet_indices.append((0, i, i + 1)) # And the end (could be rolled into the earlier loop) # FIXME: concave cross-sections will cause this end-capping algorithm # to fail end_cap_base = len(polyhedron_pts) - shape_pt_count for i in range(end_cap_base + 1, len(polyhedron_pts) - 1): facet_indices.append( (end_cap_base, i + 1, i) ) return polyhedron(points=euc_to_arr(polyhedron_pts), faces=facet_indices) # type: ignore
def as_scad(self): return solid.polyhedron(points=self.vertices, faces=self.triangles)
def __init__(self, r, u=1): super().__init__() key_pitch = 19.0 # width between keys on standard board vertical_offset = 5.5 # vertical height from plate mount when mounted on switch bottom_width = 18.0 + key_pitch * (u-1) bottom_length = 18.0 top_width = 12.5 + key_pitch * (u-1) top_length = 14.5 top_curve_depth = 0.85 # all keys have same angle from vertical for front face # measured using depth and height of number key # units radians front_tiltback_angle = np.arctan(0.5/11.5) # keys measured from ducky one2 key caps # R1 is number row, counting down. # measured to the ridge on the edges, not the valley of the curved cutout if r == 1: # number row and F row top_front_height = 11.5 top_back_height = 11.05 elif r == 2: # Q row top_front_height = 9.1 top_back_height = 9.35 elif r == 3: # A row top_front_height = 8.0 top_back_height = 9.35 elif r == 4: # Z row and Ctrl row top_front_height = 8.05 top_back_height = 10.25 else: # other rows can be implemented later, such as R5 raise Exception('OEM Keycap R value out of range:' + str(r)) top_offset_front = top_front_height * np.tan(front_tiltback_angle) top_face_angle = utils.rad2deg(np.arcsin((top_front_height - top_back_height) / top_length)) bottom_corners = [[-bottom_width/2, 0, 0], # front left [ bottom_width/2, 0, 0], # front right [ bottom_width/2, -bottom_length, 0], # back right [-bottom_width/2, -bottom_length, 0]] # back left top_corners = [[-top_width/2, 0, 0], # front left [ top_width/2, 0, 0], # front right [ top_width/2, -top_length, 0], #back right [-top_width/2, -top_length, 0]] # back left top_corners = utils.rotate_points(top_corners, [top_face_angle, 0, 0]) top_corners = utils.translate_points(top_corners, [0, -top_offset_front, top_front_height]) corners = top_corners + bottom_corners # faces must be numbered clockwise when looking at exterior key_faces = [[2, 3, 0, 1], # top [1, 0, 4, 5], # front [5, 4, 7, 6], # bottom [2, 1, 5, 6], # right [3, 2, 6, 7], # back [0, 3, 7, 4]] # left key_cap = sl.polyhedron(corners, key_faces) top_curve_radius = (top_curve_depth**2 + (top_width/2)**2)/(2 * top_curve_depth) curve_cut = sl.cylinder(top_curve_radius, bottom_length*2, center=True, segments=100) curve_cut = sl.rotate([90+top_face_angle, 0, 0])(curve_cut) curve_cut = sl.translate([0,-top_offset_front,top_curve_radius+top_front_height-top_curve_depth])(curve_cut) key_cap -= curve_cut key_cap = sl.translate([0, bottom_length/2, 0])(key_cap) corners = utils.translate_points(corners, (0, bottom_length/2, 0)) key_cap = sl.translate([0, 0, vertical_offset])(key_cap) corners = utils.translate_points(corners, (0, 0, vertical_offset)) key_cap = sl.color([50 / 255, 175 / 255, 255 / 255, 1])(key_cap) # setting the % modifier makes it render visually in openscad, but not when exporting to stl self._solid = key_cap self._anchors = CuboidAnchorCollection(corners)
def sphere(): """ Creates a solid geodesic sphere """ (vs, es, fs) = geo.sphere(v=3) return solid.polyhedron(vs, fs)
def extrude_along_path( shape_pts:Points, path_pts:Points, scales:Sequence[Union[Vector2, float, Tuple2]] = None, rotations: Sequence[float] = None, transforms: Sequence[Point3Transform] = None, connect_ends = False, cap_ends = True) -> OpenSCADObject: ''' Extrude the curve defined by shape_pts along path_pts. -- For predictable results, shape_pts must be planar, convex, and lie in the XY plane centered around the origin. *Some* nonconvexity (e.g, star shapes) and nonplanarity will generally work fine -- len(scales) should equal len(path_pts). No-op if not supplied Each entry may be a single number for uniform scaling, or a pair of numbers (or Point2) for differential X/Y scaling If not supplied, no scaling will occur. -- len(rotations) should equal 1 or len(path_pts). No-op if not supplied. Each point in shape_pts will be rotated by rotations[i] degrees at each point in path_pts. Or, if only one rotation is supplied, the shape will be rotated smoothly over rotations[0] degrees in the course of the extrusion -- len(transforms) should be 1 or be equal to len(path_pts). No-op if not supplied. Each entry should be have the signature: def transform_func(p:Point3, path_norm:float, loop_norm:float): Point3 where path_norm is in [0,1] and expresses progress through the extrusion and loop_norm is in [0,1] and express progress through a single loop of the extrusion -- if connect_ends is True, the first and last loops of the extrusion will be joined, which is useful for toroidal geometries. Overrides cap_ends -- if cap_ends is True, each point in the first and last loops of the extrusion will be connected to the centroid of that loop. For planar, convex shapes, this works nicely. If shape is less planar or convex, some self-intersection may happen. Not applied if connect_ends is True ''' polyhedron_pts:Points= [] facet_indices:List[Tuple[int, int, int]] = [] # Make sure we've got Euclid Point3's for all elements shape_pts = euclidify(shape_pts, Point3) path_pts = euclidify(path_pts, Point3) src_up = Vector3(0, 0, 1) shape_pt_count = len(shape_pts) tangent_path_points: List[Point3] = [] if connect_ends: tangent_path_points = [path_pts[-1]] + path_pts + [path_pts[0]] else: first = Point3(*(path_pts[0] - (path_pts[1] - path_pts[0]))) last = Point3(*(path_pts[-1] - (path_pts[-2] - path_pts[-1]))) tangent_path_points = [first] + path_pts + [last] tangents = [tangent_path_points[i+2] - tangent_path_points[i] for i in range(len(path_pts))] for which_loop in range(len(path_pts)): # path_normal is 0 at the first path_pts and 1 at the last path_normal = which_loop/ (len(path_pts) - 1) path_pt = path_pts[which_loop] tangent = tangents[which_loop] scale = scales[which_loop] if scales else 1 rotate_degrees = None if rotations: rotate_degrees = rotations[which_loop] if len(rotations) > 1 else rotations[0] * path_normal transform_func = None if transforms: transform_func = transforms[which_loop] if len(transforms) > 1 else transforms[0] this_loop = shape_pts[:] this_loop = _scale_loop(this_loop, scale) this_loop = _rotate_loop(this_loop, rotate_degrees) this_loop = _transform_loop(this_loop, transform_func, path_normal) this_loop = transform_to_point(this_loop, dest_point=path_pt, dest_normal=tangent, src_up=src_up) loop_start_index = which_loop * shape_pt_count if (which_loop < len(path_pts) - 1): loop_facets = _loop_facet_indices(loop_start_index, shape_pt_count) facet_indices += loop_facets # Add the transformed points & facets to our final list polyhedron_pts += this_loop if connect_ends: next_loop_start_index = len(polyhedron_pts) - shape_pt_count loop_facets = _loop_facet_indices(0, shape_pt_count, next_loop_start_index) facet_indices += loop_facets elif cap_ends: # endcaps at start & end of extrusion # NOTE: this block adds points & indices to the polyhedron, so it's # very sensitive to the order this is happening in start_cap_index = len(polyhedron_pts) end_cap_index = start_cap_index + 1 last_loop_start_index = len(polyhedron_pts) - shape_pt_count start_loop_pts = polyhedron_pts[:shape_pt_count] end_loop_pts = polyhedron_pts[last_loop_start_index:] start_loop_indices = list(range(0, shape_pt_count)) end_loop_indices = list(range(last_loop_start_index, last_loop_start_index + shape_pt_count)) start_centroid, start_facet_indices = _end_cap(start_cap_index, start_loop_pts, start_loop_indices) end_centroid, end_facet_indices = _end_cap(end_cap_index, end_loop_pts, end_loop_indices) polyhedron_pts += [start_centroid, end_centroid] facet_indices += start_facet_indices facet_indices += end_facet_indices return polyhedron(points=euc_to_arr(polyhedron_pts), faces=facet_indices) # type: ignore