예제 #1
0
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
예제 #2
0
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])
예제 #3
0
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
예제 #4
0
 def as_scad(self):
     return solid.polyhedron(points=self.vertices, faces=self.triangles)
예제 #5
0
    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)
예제 #6
0
def sphere():
    """ Creates a solid geodesic sphere """
    (vs, es, fs) = geo.sphere(v=3)
    return solid.polyhedron(vs, fs)
예제 #7
0
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