Example #1
0
def sa_cap(Usize=1):
    # MODIFIED TO NOT HAVE THE ROTATION.  NEEDS ROTATION DURING ASSEMBLY
    sa_length = 18.25

    bw2 = Usize * sa_length / 2
    bl2 = sa_length / 2
    m = 0
    pw2 = 6 * Usize + 1
    pl2 = 6

    if Usize == 1:
        m = 17 / 2

    k1 = sl.polygon([[bw2, bl2], [bw2, -bl2], [-bw2, -bl2], [-bw2, bl2]])
    k1 = sl.linear_extrude(height=0.1, twist=0, convexity=0, center=True)(k1)
    k1 = sl.translate([0, 0, 0.05])(k1)
    k2 = sl.polygon([[pw2, pl2], [pw2, -pl2], [-pw2, -pl2], [-pw2, pl2]])
    k2 = sl.linear_extrude(height=0.1, twist=0, convexity=0, center=True)(k2)
    k2 = sl.translate([0, 0, 12.0])(k2)
    if m > 0:
        m1 = sl.polygon([[m, m], [m, -m], [-m, -m], [-m, m]])
        m1 = sl.linear_extrude(height=0.1, twist=0, convexity=0, center=True)(m1)
        m1 = sl.translate([0, 0, 6.0])(m1)
        key_cap = sl.hull()(k1, k2, m1)
    else:
        key_cap = sl.hull()(k1, k2)

    # key_cap = sl.translate([0, 0, 5 + plate_thickness])(key_cap)
    key_cap = sl.color([220 / 255, 163 / 255, 163 / 255, 1])(key_cap)

    return key_cap
Example #2
0
def snap_shaft():
    """Return snap shaft and hole geometry in a list of PolyMeshes."""

    s_pts = solid.polygon(
        points=[
            [0.0, 0.0], [4.95, 0.0], [4.95, 3.0], [2.45, 3.0], [1.95, 3.5],
            [1.95, 5.675], [2.45, 6.175], [2.45, 7.425], [1.95, 7.925],
            [0.75, 7.925], [0.75, 3.5], [0.0, 3.0], [0.0, 0.0]
        ]
    )

    sh_gen = (
        solid.rotate_extrude(convexity=10)(s_pts)
        - solid.translate([0, 0, 8.5])(solid.cube([1.5, 10, 10], center=True))
        - solid.translate([0, 0, 3.75])(
            solid.rotate([90, 0, 0])(
                solid.cylinder(r=0.75, h=10, center=True)
            )
        )
    )

    h_pts = solid.polygon(
        points=[
            [2.5, 0], [2, 0.5], [2.0, 2.675], [2.5, 3.175], [
                4.95, 3.175], [4.95, 0], [2.5, 0]
        ]
    )

    # added hole so it will subtract geometry from arm as necessary
    h_gen = solid.translate([0, 0, 3])(
        solid.rotate_extrude(convexity=10)(h_pts))

    return [PolyMesh(generator=sh_gen), PolyMesh(generator=h_gen)]
Example #3
0
def makeRegister(board, jigFrameSize, jigThickness, pcbThickness, outerBorder,
                 innerBorder, tolerance, topSide):
    bBox = findBoardBoundingBox(board)
    centerpoint = rectCenter(bBox)

    top = jigThickness - fromMm(0.15)
    pcbBottom = jigThickness - pcbThickness

    outerPolygon, holes = createOuterPolygon(board, jigFrameSize, outerBorder)
    outerRing = outerPolygon.exterior.coords
    if topSide:
        outerRing = mirrorX(outerRing, centerpoint[0])
    body = solid.linear_extrude(height=top,
                                convexity=10)(solid.polygon(outerRing))

    innerRing = createOffsetPolygon(board, -innerBorder).exterior.coords
    if topSide:
        innerRing = mirrorX(innerRing, centerpoint[0])
    innerCutout = solid.utils.down(jigThickness)(solid.linear_extrude(
        height=3 * jigThickness, convexity=10)(solid.polygon(innerRing)))
    registerRing = createOffsetPolygon(board, tolerance).exterior.coords
    if topSide:
        registerRing = mirrorX(registerRing, centerpoint[0])
    registerCutout = solid.utils.up(jigThickness - pcbThickness)(
        solid.linear_extrude(height=jigThickness,
                             convexity=10)(solid.polygon(registerRing)))

    register = body - innerCutout - registerCutout
    for hole in holes:
        register = register - solid.translate([hole[0], hole[1], top])(
            m2countersink())
    return solid.scale(toMm(1))(solid.translate(
        [-centerpoint[0], -centerpoint[1], 0])(register))
Example #4
0
def process(outline_file,
            solderpaste_file,
            stencil_thickness=0.2,
            include_ledge=True,
            ledge_height=1.2,
            ledge_gap=0.0,
            increase_hole_size_by=0.0):

    outline_shape = create_outline_shape(outline_file)
    cutout_polygon = create_cutouts(
        solderpaste_file, increase_hole_size_by=increase_hole_size_by)

    if ledge_gap:
        # Add a gap between the ledge and the stencil
        outline_shape = offset_shape(outline_shape, ledge_gap)
    outline_polygon = polygon(outline_shape)

    stencil = linear_extrude(height=stencil_thickness)(outline_polygon -
                                                       cutout_polygon)

    if include_ledge:
        ledge_shape = offset_shape(outline_shape, 1.2)
        ledge_polygon = polygon(ledge_shape) - outline_polygon

        # Cut the ledge in half by taking the bounding box of the outline, cutting it in half
        # and removing the resulting shape from the ledge shape
        # We always leave the longer side of the ledge intact so we don't end up with a tiny ledge.
        cutter = bounding_box(ledge_shape)
        height = abs(cutter[1][1] - cutter[0][1])
        width = abs(cutter[0][0] - cutter[3][0])

        if width > height:
            cutter[1][1] -= height / 2
            cutter[2][1] -= height / 2
        else:
            cutter[2][0] -= width / 2
            cutter[3][0] -= width / 2

        ledge_polygon = ledge_polygon - polygon(cutter)

        ledge = utils.down(ledge_height - stencil_thickness)(
            linear_extrude(height=ledge_height)(ledge_polygon))
        stencil = ledge + stencil

    # Rotate the stencil to make it printable
    stencil = rotate(a=180, v=[1, 0, 0])(stencil)

    return scad_render(stencil)
Example #5
0
def create_cutouts(solder_paste, increase_hole_size_by=0.0):
    solder_paste.to_metric()

    cutout_shapes = []
    cutout_lines = []

    apertures = {}
    current_aperture = None
    current_x = None
    current_y = None
    for statement in solder_paste.statements:
        if statement.type == 'PARAM' and statement.param == 'AD':  # define aperture
            apertures[statement.d] = {
                'shape': statement.shape,
                'modifiers': statement.modifiers
            }
        elif statement.type == 'APERTURE':
            current_aperture = statement.d
        elif statement.type == 'COORD' and statement.op == 'D3':  # flash object coordinates
            if not current_aperture:
                raise Exception("No aperture set on flash object coordinates!")

            aperture = apertures[current_aperture]
            current_x = statement.x if statement.x is not None else current_x
            current_y = statement.y if statement.y is not None else current_y
            if aperture['shape'] == 'C':  # circle
                cutout_shapes.append(
                    primitive_to_shape(
                        primitives.Circle(diameter=aperture['modifiers'][0][0],
                                          position=[current_x, current_y])))
            elif aperture['shape'] == 'R':  # rectangle
                width, height = aperture['modifiers'][0]
                cutout_shapes.append(
                    primitive_to_shape(
                        primitives.Rectangle(
                            position=[current_x, current_y],
                            width=width,
                            height=height,
                        )))
            else:
                raise NotImplementedError(
                    "Only circular and rectangular flash objects are supported!"
                )

    for p in solder_paste.primitives:
        shape = primitive_to_shape(p)
        if len(shape) > 2:
            cutout_shapes.append(shape)
        else:
            cutout_lines.append(shape)

    # If the cutouts contain lines we try to first join them together into shapes
    cutout_shapes += lines_to_shapes(cutout_lines)
    polygons = []
    for shape in cutout_shapes:
        if increase_hole_size_by and len(shape) > 2:
            shape = offset_shape(shape, increase_hole_size_by)
        polygons.append(polygon([[x, y] for x, y in shape]))

    return union()(*polygons)
Example #6
0
def triangle90(a, b, height=1, axis="z", center=True):
    """A quick 90 degree triangle with sidelengths a,b, extruded to height"""
    p = solid.polygon([
        [
            0,
            0,
        ],
        [a, 0],
        [a, b],
    ]).e(height)
    if center:
        c = ((0 + a + a) / 3, (0 + 0 + b) / 3)
        if center is True or "z" in center:
            p = p.down(height / 2)
        if center is True or "x" in center:
            p = p.left(c[0])
        if center is True or "y" in center:
            p = p.back(c[1])
    if axis == "x":
        p = p.rotate(90, 0, 0)
    elif axis == "y":
        p = p.rotate(
            0,
            90,
            0,
        )
    elif axis == "z":
        pass
    else:
        raise ValueError("invalid axis")
    return p
Example #7
0
def dovetail(width, length, depth, ratio):
    return linear_extrude(depth)(polygon((
        (0, 0),
        (width, 0),
        (width - length * ratio, length),
        (length * ratio, length),
    )))
Example #8
0
def rounded_rectangle(size: XY, corner_radius, segments):
    if isinstance(size, (int, float)):
        size = XY(size, size)
    if isinstance(corner_radius, (int, float)):
        corner_radius = (corner_radius, corner_radius, corner_radius,
                         corner_radius)

    corner_points = [
        XY(size.x - corner_radius[0], size.y - corner_radius[0]),
        XY(corner_radius[1], size.y - corner_radius[1]),
        XY(corner_radius[2], corner_radius[2]),
        XY(size.x - corner_radius[3], corner_radius[3]),
    ]

    theta = 0
    dtheta = 90
    theta_step = dtheta / (segments / 4)
    points = []
    # Go counter clockwise just in case we need to calculate a normal
    for cp, r in zip(corner_points, corner_radius):
        t = theta
        while t < theta + dtheta + theta_step:
            points.append(
                XY(cp.x + r * cos(radians(t)), cp.y + r * sin(radians(t))))
            t += theta_step
        theta += dtheta
    points.append(points[0])  # Close the polygon
    return polygon(points)
    def write_to_scad(self, filename = 'test.scad'):
        """Uses solidpython to export the animation (shapes and their associated transformations) to
        an OpenSCAD file, ready to be rendered as an STL.

        Inputs:
        filename: The filename to export to.
        """

        if type(self.final_shapes) == NoneType:
            self.render_shapes()

        shapes_to_export = []

        for shape in self.final_shapes:
            solid_shapes = []

            for i in range(len(shape)):
                #For each shape (which is stored as a list of points)...
                solid_shape = shape[i].T.tolist()
                #Represent it as a polygon...
                solid_shapes.append(sp.polygon(solid_shape))
                #Extrude that polygon up .21mm...
                solid_shapes[i] = sp.linear_extrude(.21)(solid_shapes[i])
                #Then translate that extrusion up .2mm...
                solid_shapes[i] = up(i*.2)(solid_shapes[i])
            shapes_to_export.append(solid_shapes)

        #Then union ALL of the extrudes of EVERY shape
        final_export = union()(shapes_to_export)
        scad_render_to_file(final_export,filename)
Example #10
0
def path_2d_polygon(points:Sequence[Point23], width:float=1, closed:bool=False) -> polygon:
    '''
    Return an OpenSCAD `polygon()` in an area `width` units wide around `points`
    '''
    path_points = path_2d(points, width, closed)
    paths = [list(range(len(path_points)))]
    if closed:
        paths = [list(range(len(points))), list(range(len(points), len(path_points)))]
    return polygon(path_points, paths=paths)
Example #11
0
def conic_section(theta):
	line = solid.polygon(points = [[0,0],[50,50],[49.9,50],[0,.1]])
	cone = solid.rotate_extrude( convexity = 20)(line)
	
	plane = solid.translate([0,0,5])(solid.cube([50,50,.1],center = True))
	plane = solid.rotate([0,theta,0])(plane)
	
	section = solid.rotate([0,-1*theta, 0])(solid.intersection()(cone, plane))
	return section
def process(outline_file, solderpaste_file, stencil_thickness=0.2, include_ledge=True,
            ledge_height=1.2, ledge_gap=0.0, increase_hole_size_by=0.0):

    outline_shape = create_outline_shape(outline_file)
    cutout_polygon = create_cutouts(solderpaste_file, increase_hole_size_by=increase_hole_size_by)

    if ledge_gap:
        # Add a gap between the ledge and the stencil
        outline_shape = offset_shape(outline_shape, ledge_gap)
    outline_polygon = polygon(outline_shape)

    stencil = linear_extrude(height=stencil_thickness)(outline_polygon - cutout_polygon)

    if include_ledge:
        ledge_shape = offset_shape(outline_shape, 1.2)
        ledge_polygon = polygon(ledge_shape) - outline_polygon

        # Cut the ledge in half by taking the bounding box of the outline, cutting it in half
        # and removing the resulting shape from the ledge shape
        # We always leave the longer side of the ledge intact so we don't end up with a tiny ledge.
        cutter = bounding_box(ledge_shape)
        height = abs(cutter[1][1] - cutter[0][1])
        width = abs(cutter[0][0] - cutter[3][0])

        if width > height:
            cutter[1][1] -= height/2
            cutter[2][1] -= height/2
        else:
            cutter[2][0] -= width/2
            cutter[3][0] -= width/2

        ledge_polygon = ledge_polygon - polygon(cutter)

        ledge = utils.down(
            ledge_height - stencil_thickness
        )(
            linear_extrude(height=ledge_height)(ledge_polygon)
        )
        stencil = ledge + stencil

    # Rotate the stencil to make it printable
    stencil = rotate(a=180, v=[1, 0, 0])(stencil)

    return scad_render(stencil)
Example #13
0
        def panel_shape(panel_tri):
            edge_midpoint = 0.5 * (panel_tri[0] + panel_tri[1])
            rotation_vector = panel_tri[1] - panel_tri[0]

            transform = lambda x: solid.translate(
                edge_midpoint)(solid.rotate(a=panel_angle, v=rotation_vector)
                               (solid.translate(-edge_midpoint)(x)))

            return transform(
                solid.linear_extrude(0.01)(solid.polygon(panel_tri * 0.9)))
Example #14
0
def make_stick_figures(shell, stick_figures, radius, breadth):
    """Adds stick figure inscription onto the shell."""
    # Create inscribed shell
    i_shell = solid.difference()
    i_shell.add(shell)
    # Add individual polygons to the inscribed shell
    for cname in sorted(stick_figures.keys()):
        for a1, e1, a2, e2 in stick_figures[cname]:
            i_shell.add(
                solid.linear_extrude(height=radius)(solid.polygon(
                    make_sticks(a1, e1, a2, e2, radius, breadth))))
    return i_shell
Example #15
0
def printedStencil(outlineDxf, holesDxf, extraHoles, thickness, frameHeight, frameWidth,
                   frameClearance, enlargeHoles, front):
    zScale = -1 if front else 1
    xRotate = 180 if front else 0
    substrate = solid.scale([1, 1, zScale])(printedStencilSubstrate(outlineDxf,
        thickness, frameHeight, frameWidth, frameClearance))
    holesOffset = solid.utils.up(0) if enlargeHoles == 0 else solid.offset(delta=enlargeHoles)
    holes = solid.linear_extrude(height=4*thickness, center=True)(
        holesOffset(solid.import_dxf(holesDxf)))
    substrate -= holes
    for h in extraHoles:
        substrate -= solid.scale([toMm(1), -toMm(1), 1])(
                solid.linear_extrude(height=4*thickness, center=True)(
                    solid.polygon(h.exterior.coords)))
    return solid.rotate(a=xRotate, v=[1, 0, 0])(substrate)
Example #16
0
def bezier_polygon(controls: FourPoints,
                   subdivisions: int = DEFAULT_SUBDIVISIONS,
                   extrude_height: float = DEFAULT_EXTRUDE_HEIGHT,
                   show_controls: bool = False,
                   center: bool = True) -> OpenSCADObject:
    points = bezier_points(controls, subdivisions)
    shape = polygon(points)
    if extrude_height != 0:
        shape = linear_extrude(extrude_height, center=center)(shape)

    if show_controls:
        control_objs = control_points(controls,
                                      extrude_height=extrude_height,
                                      center=center)
        shape += control_objs

    return shape
Example #17
0
def create_cutouts(solder_paste, increase_hole_size_by=0.0):
    solder_paste.to_metric()

    cutout_faces = []

    for p in solder_paste.primitives:
        cutout_faces += primitive_to_faces(p)

    cutout_shapes = combine_faces_into_shapes(cutout_faces)

    polygons = []
    for shape in cutout_shapes:
        if increase_hole_size_by and len(shape) > 2:
            shape = offset_shape(shape, increase_hole_size_by)
        polygons.append(polygon([[x, y] for x, y in shape]))

    return union()(*polygons)
def hold_down(od, wing_thickness, alpha, height, width, angle):
    
    half_thickness=to_mm(wing_thickness/2);
    r=to_mm(od/2);
    depth = r * sqrt(1-alpha*alpha);
    h=to_mm(height);
    w=to_mm(width);
    hprime = h*w/(w-(1-alpha)*r);
    basic_wedge = linear_extrude(h, center=True)(polygon([[0,half_thickness], [0,depth], [w, half_thickness]]))

    basic_cylinder = rotate([angle, 0,0])(cylinder(h=h*1.5, r=r,center=True))
    cone_envelope = translate([alpha*r, half_thickness,-h*0.5])(
                    rotate([0,90,0])(
                        scale([hprime/(depth-half_thickness),1,1])(
                            cylinder(r1=depth-half_thickness, r2=0, h=w))))
    clipping_cube = cube([10000,r*1.2, 10000],center=True);

    holder = (translate([alpha*r, 0, 0])(basic_wedge)- basic_cylinder)* cone_envelope *clipping_cube
    return holder
Example #19
0
    def test_with_solidpython(self):
        import solid

        sys = System()

        a = sys.add_param(10)
        b = sys.add_param(3)
        c = sys.add_param(17)
        d = sys.add_param(23)

        #NOTE We should use Point2d, but I don't want to
        #     create a workplane just for that.
        p1 = Point3d(Param(7), Param(2), Param(0), sys)

        poly = solid.polygon([[a,b],[c,d], [0,0], p1])

        self.assertEqual(
            solid.scad_render(poly),
            "\n\npolygon(paths = [[0, 1, 2, 3]], points = [[10.0000000000, 3.0000000000], [17.0000000000, 23.0000000000], [0, 0], [7.0000000000, 2.0000000000, 0.0000000000]]);")
Example #20
0
    def test_with_solidpython(self):
        import solid

        sys = System()

        a = sys.add_param(10)
        b = sys.add_param(3)
        c = sys.add_param(17)
        d = sys.add_param(23)

        #NOTE We should use Point2d, but I don't want to
        #     create a workplane just for that.
        p1 = Point3d(Param(7), Param(2), Param(0), sys)

        poly = solid.polygon([[a,b],[c,d], [0,0], p1])

        self.assertEqual(
            solid.scad_render(poly),
            "\n\npolygon(paths = [[0, 1, 2, 3]], points = [[10.0000000000, 3.0000000000], [17.0000000000, 23.0000000000], [0, 0], [7.0000000000, 2.0000000000, 0.0000000000]]);")
Example #21
0
def sector(radius=20, angles=(45, 135), segments=24):
    r = radius / math.cos(180 * degree_to_radians / segments)
    step = int(-360 / segments)

    points = [[0, 0]]
    for a in range(int(angles[0]), int(angles[1] - 360), step):
        points.append([
            r * math.cos(a * degree_to_radians),
            r * math.sin(a * degree_to_radians)
        ])
    for a in range(int(angles[0]), int(angles[1] - 360), step):
        points.append([
            r * math.cos(angles[1] * degree_to_radians),
            r * math.sin(angles[1] * degree_to_radians),
        ])

    return solid.difference()(
        solid.circle(radius, segments=segments),
        solid.polygon(points),
    )
Example #22
0
def create_cutouts(solder_paste, increase_hole_size_by=0.0):
    solder_paste.to_metric()

    cutout_shapes = []
    cutout_lines = []

    for p in solder_paste.primitives:
        shape = primitive_to_shape(p)
        if len(shape) > 2:
            cutout_shapes.append(shape)
        else:
            cutout_lines.append(shape)

    # If the cutouts contain lines we try to first join them together into shapes
    cutout_shapes += lines_to_shapes(cutout_lines)
    polygons = []
    for shape in cutout_shapes:
        if increase_hole_size_by and len(shape) > 2:
            shape = offset_shape(shape, increase_hole_size_by)
        polygons.append(polygon([[x, y] for x, y in shape]))

    return union()(*polygons)
def create_cutouts(solder_paste, increase_hole_size_by=0.0):
    solder_paste.to_metric()

    cutout_shapes = []
    cutout_lines = []

    for p in solder_paste.primitives:
        shape = primitive_to_shape(p)
        if len(shape) > 2:
            cutout_shapes.append(shape)
        else:
            cutout_lines.append(shape)

    # If the cutouts contain lines we try to first join them together into shapes
    cutout_shapes += lines_to_shapes(cutout_lines)
    polygons = []
    for shape in cutout_shapes:
        if increase_hole_size_by and len(shape) > 2:
            shape = offset_shape(shape, increase_hole_size_by)
        polygons.append(polygon([[x, y] for x, y in shape]))

    return union()(*polygons)
    def __init__(self, length, thickness=None, inner=None, outer=None, points=5, **kwargs):
        """

        :param length:
        :param thickness:
        :param inner:
        :param outer:
        :param points:
        :param kwargs:
        Hollow doesn't work quite right yet.
        """
        super(MorphedNoseCone, self).__init__(length, thickness, **kwargs)
        outer = to_mm(outer, self.outer_diameter / 4.)
        inner = to_mm(inner, self.outer_diameter / 8.)
        out = []
        for i in range(0, points):
            alpha = i * 2. * pi / points
            p = polygon([[0, 0], [inner * cos(alpha - pi / points), inner * sin(alpha - pi / points)],
                         [outer * cos(alpha), outer * sin(alpha)],
                         [inner * cos(alpha + pi / points), inner * sin(alpha + pi / points)]])
            out.append(
                MorphedNoseCone(length, p, inner_diameter=self.inner_diameter, outer_diameter=self.outer_diameter,
                                thickness=thickness).cone)
        self.cone = union()(*out)
Example #25
0
def catmull_rom_polygon(points: Sequence[Point23],
                        subdivisions: int = DEFAULT_SUBDIVISIONS,
                        extrude_height: float = DEFAULT_EXTRUDE_HEIGHT,
                        show_controls: bool = False,
                        center: bool = True) -> OpenSCADObject:
    """
    Return a closed OpenSCAD polygon object through all of `points`, 
    extruded to `extrude_height`. If `show_controls` is True, return red 
    cylinders at each of the specified control points; this makes it easier to
    move determine which points should move to get a desired shape.

    NOTE: if `extrude_height` is 0, this function returns a 2D `polygon()`
    object, which OpenSCAD can only combine with other 2D objects 
    (e.g. `square`, `circle`, but not `cube` or `cylinder`). If `extrude_height`
    is nonzero, the object returned will be 3D and only combine with 3D objects.
    """
    catmull_points = catmull_rom_points(points, subdivisions, close_loop=True)
    shape = polygon(catmull_points)
    if extrude_height > 0:
        shape = linear_extrude(height=extrude_height, center=center)(shape)

    if show_controls:
        shape += control_points(points, extrude_height, center)
    return shape
Example #26
0
        How much to offset from the origin (set to the wall thickness)
    '''
    xpts = np.linspace(0, length, nsteps)
    term1 = (1 - xpts / length)**(2 / 3.)
    profile = 0.9 * length * term1 * np.sqrt(1 - term1)
    # For the profile to "stand up" swap the axes
    coords = list(zip(profile, xpts + offset))
    # Add origin point as the last point to close the profile
    coords.append((0., offset))
    return coords


# Read the outer profile for the shell
#shell = sol.polygon(read_csv(PROF_OUTER))
# Generate the outer profile for the shell
shell = sol.polygon(egg(LENGTH))
# Rotational extrusion to make the solid.
# rotate_extrude takes the profile on the XY plane and uses it as the
# profile in the XZ plane for revolution about the Z-axis
shell = sol.rotate_extrude(convexity=10)(shell)

# Variables...
# The following variables need to be defined in the HEADER string
# for OpenSCAD to have access to them in the generated program:
# coupler_shoulder_len, coupler_shoulder_diam, coupler_fitting_len
# coupler_fitting_diam, coupler_extra, delta, wall_th
OUTFILE = 'Customizable_hollow_egg_carrier.scad'
coupler_fitting_diam = 'coupler_fitting_diam'
coupler_shoulder_h = 'coupler_shoulder_len + coupler_extra'
coupler_shoulder_diam = 'coupler_shoulder_diam'
coupler_shoulder_shift = '-coupler_shoulder_len'
Example #27
0
def triangle_prism(a, b, c, thickness):
    return linear_extrude(thickness)(polygon((a, b, c, a)))
Example #28
0
def spurGear(nT=12, gmodule=3, holeDiam=6.35, gthick=4, pressAngle=28):
    '''Return CSG of a cylindrical spur gear having center-hole
    diameter=holeDiam, thickness=gthick, module=gmodule, pressure
    angle=pressAngle, #teeth=nT.  In more detail:

    module: A metric gear's module (mm) is its reference diameter (its
    pitch diameter) divided by its tooth count.  For example, a
    module-3 gear with 30 teeth is 90 mm across.  "Circular pitch",
    equal to pi*module, is tooth-to-tooth circumferential distance at
    pitch diameter.  "Diametral pitch" is the number of tooth
    intervals per inch at pitch diameter.

    Pressure angle (degrees) is profile angle at pitch diameter; also
    equals angle between normal to tooth surface and angle of force
    when gear contact point is at the pitch diameter.

    Coefficient of profile shift: [Not in this version; may add in
    future] Use 0 if #teeth is large; as #teeth gets smaller, use a
    larger shift to avoid tooth undercutting.  See Table 4, p. T-40 in
    SDP-SI 8050T034.pdf

    '''
    cyl = cylinder(h=gthick*1.1, d=holeDiam, center=True)
    nTeeth  = float(nT)
    gmodule = float(gmodule)
    pitchDiam = gmodule * nTeeth
    baseDiam = pitchDiam * cos(rad(pressAngle))
    rootDiam = pitchDiam - 2.5 * gmodule
    tipDiam  = pitchDiam + 2 * gmodule
    #addendum, dedendum  = gmodule, gmodule*1.25
    rp, rb, rr, rt = pitchDiam/2., baseDiam/2., rootDiam/2., tipDiam/2.
    rm = max(rb, rr)
    #print ('rp {:8.2f}   rb {:8.2f}   rr {:8.2f}  rt {:8.2f}'.format(rp, rb, rr, rt)) 

    def round3(v):  return int(1000*v+0.5)/1000.0
    def rotated(pl, ra):
        '''Return point list pl, rotated by ra radians, rounded
        to a few decimal places for compactness.  '''
        s, c = sin(ra), cos(ra)
        return [[round3(x*c-y*s), round3(x*s+y*c)] for x,y in pl]

    def invo(r): # Compute involute at radius r, return its (x,y)
        ia = sqrt((r/rb)**2 - 1) # Radians for involute to reach radius r
        ix = rb*(cos(ia) + ia*sin(ia))
        iy = rb*(sin(ia) - ia*cos(ia)) # x,y coords of point on involute 
        return (ix,-iy)

    # Make set of points for outline of one tooth along +y axis
    # Compute point at pitch radius, for use as alignment angle  
    x, y = invo(rp)           # (x,y) when involute crosses pitch circle
    alan = atan2(-y, x)        # angle from origin to x,y
    tang = pi/nTeeth          # angle subtended by one tooth or one gap at rp
    htan = tang/2             # half-tooth angle: 1/4 of pitch angle
    pang = 2*tang             # angle subtended by one tooth + one gap
    nradii = 6
    rstep = (rt-rm)/float(nradii)
    points = [invo(rm + j*rstep) for j in range(nradii)]
    
    # Rotate half-tooth for proper tooth thickness at pitch circle
    points = rotated(points, alan+htan)
    #print ('Half-tooth points after adjust: ',points)
    # Curve-rounding in gap.  Note, pang = adel+2*aeps = 2*tang
    x, y = points[0]; aeps = atan2(y,x); adel = pang-2*aeps
    g = rr*adel; u = g/20   # adel = angle in gap, g = gap width at rr
    if g>0:                 # Add 3 points like on an arc
        curve = [[rr,0],[rr+u,-u*6],[rr+u*3,-u*8]]
        # Skip first point if it's behind the curve
        points = rotated(curve,tang) + points[0 if x>rr+u*3 else 1:]
    #print ('Half-tooth points after curver: ',points)
    # Mirror tooth top side to bottom (except for center pt of gap)
    bepo = points + [[x,-y] for x,y in reversed(points[1:])]
    repo = list(reversed(bepo))   # draw up not down
    polyli = []
    for i in range(nT):
        polyli +=  rotated(repo, i*pang)
    return linear_extrude(gthick, True)(polygon(polyli)) - cyl
Example #29
0
def process_gerber(
    outline_file,
    solderpaste_file,
    stencil_thickness=0.2,
    include_ledge=True,
    ledge_height=1.2,
    ledge_gap=0.0,
    increase_hole_size_by=0.0,
    simplify_regions=False,
    flip_stencil=False,
):
    """Convert gerber outline and solderpaste files to an scad file."""
    outline_shape = create_outline_shape(outline_file)
    cutout_polygon = create_cutouts(
        solderpaste_file,
        increase_hole_size_by=increase_hole_size_by,
        simplify_regions=simplify_regions,
    )

    # debugging!
    # return scad_render(linear_extrude(height=stencil_thickness)(polygon(outline_shape)))

    if ledge_gap:
        # Add a gap between the ledge and the stencil
        outline_shape = offset_shape(outline_shape, ledge_gap)
    outline_polygon = polygon([v.as_tuple() for v in outline_shape])

    if flip_stencil:
        mirror_normal = (-1, 0, 0)
        outline_bounds = geometry.bounding_box(outline_shape)
        outline_polygon = translate((outline_bounds[2][0], 0, 0))(
            mirror(mirror_normal)(outline_polygon)
        )
        cutout_polygon = translate((outline_bounds[2][0], 0, 0))(
            mirror(mirror_normal)(cutout_polygon)
        )

    stencil = linear_extrude(height=stencil_thickness)(outline_polygon - cutout_polygon)

    if include_ledge:
        ledge_shape = offset_shape(outline_shape, 1.2)
        ledge_polygon = polygon([v.as_tuple() for v in ledge_shape]) - outline_polygon

        # Cut the ledge in half by taking the bounding box of the outline, cutting it in half
        # and removing the resulting shape from the ledge shape
        # We always leave the longer side of the ledge intact so we don't end up with a tiny ledge.
        cutter = geometry.bounding_box(ledge_shape)
        height = abs(cutter[1][1] - cutter[0][1])
        width = abs(cutter[0][0] - cutter[3][0])

        if width > height:
            cutter[1].y -= height / 2
            cutter[2].y -= height / 2
        else:
            cutter[2].x -= width / 2
            cutter[3].x -= width / 2

        ledge_polygon = ledge_polygon - polygon([v.as_tuple() for v in cutter])

        ledge = utils.down(ledge_height - stencil_thickness)(
            linear_extrude(height=ledge_height)(ledge_polygon)
        )
        stencil = ledge + stencil

    # Rotate the stencil to make it printable
    stencil = rotate(a=180, v=(1, 0, 0))(stencil)

    # for debugging, output just the cutout polygon (extruded)
    # return scad_render(linear_extrude(height=stencil_thickness)(cutout_polygon))

    return scad_render(stencil)
Example #30
0
def create_cutouts(solder_paste, increase_hole_size_by=0.0, simplify_regions=False):
    solder_paste.to_metric()

    cutout_shapes: List[List[V]] = []
    cutout_lines: List[List[V]] = []

    apertures = {}
    # Aperture macros are saved as a list of shapes
    aperture_macros = {}
    current_aperture = None
    current_x = 0
    current_y = 0
    for statement in solder_paste.statements:
        if statement.type == "PARAM":
            if statement.param == "AD":
                # define aperture
                apertures[statement.d] = {
                    "shape": statement.shape,
                    "modifiers": statement.modifiers,
                }
            elif statement.param == "AM":
                # Aperture macro
                aperture_macros[statement.name] = []
                for primitive in statement.primitives:
                    aperture_macros[statement.name].append(
                        primitive_to_shape(primitive)
                    )

        elif statement.type == "APERTURE":
            current_aperture = statement.d
        elif statement.type == "COORD" and statement.op in ["D02", "D2"]:
            # Move coordinates
            if statement.x is not None:
                current_x = statement.x
            if statement.y is not None:
                current_y = statement.y
        elif statement.type == "COORD" and statement.op in [
            "D03",
            "D3",
        ]:  # flash object coordinates
            if not current_aperture:
                raise Exception("No aperture set on flash object coordinates!")

            aperture = apertures[current_aperture]
            current_x = statement.x if statement.x is not None else current_x
            current_y = statement.y if statement.y is not None else current_y
            if aperture["shape"] == "C":  # circle
                cutout_shapes.append(
                    primitive_to_shape(
                        primitives.Circle(
                            diameter=aperture["modifiers"][0][0],
                            position=[current_x, current_y],
                        )
                    )
                )
            elif aperture["shape"] == "R":  # rectangle
                width, height = aperture["modifiers"][0]
                print(f"Rect X: {current_x} Y: {current_y}")
                cutout_shapes.append(
                    primitive_to_shape(
                        primitives.Rectangle(
                            position=[current_x, current_y],
                            width=width,
                            height=height,
                        )
                    )
                )
            elif aperture["shape"] == "O":  # obround
                width, height = aperture["modifiers"][0]
                print(f"Obround at {current_x},{current_y}")
                obround = primitives.Obround((0, 0), width, height)
                shape = primitive_to_shape(obround)
                positioned_shape = [
                    V(p[0] + current_x, p[1] + current_y) for p in shape
                ]
                cutout_shapes.append(positioned_shape)
            elif aperture["shape"] in aperture_macros:  # Aperture macro shape
                for macro_shape in aperture_macros[aperture["shape"]]:
                    # Offset all points in the macro and add the resulting shape
                    shape = [V(p[0] + current_x, p[1] + current_y) for p in macro_shape]
                    cutout_shapes.append(shape)
            else:
                raise NotImplementedError(
                    f"Unsupported flash aperture {aperture['shape']}"
                )
        else:
            pass

    for p in solder_paste.primitives:
        if type(p) == primitives.AMGroup:
            print(f"Ignoring AMGroup {p}")
            continue
        shape = primitive_to_shape(p, simplify_regions=simplify_regions)
        if len(shape) > 2:
            cutout_shapes.append(shape)
        else:
            cutout_lines.append(shape)

    # If the cutouts contain lines we try to first join them together into shapes
    cutout_shapes += lines_to_shapes(cutout_lines)
    polygons = []
    for shape in cutout_shapes:
        if increase_hole_size_by and len(shape) > 2:
            shape = offset_shape(shape, increase_hole_size_by)
        polygons.append(polygon([(x, y) for x, y in shape]))

    return union()(*polygons)
Example #31
0
def path_render3D(self, pconfig, border=False):
	global _delta, PRECISION, SCALEUP
	self.rotations_to_3D()
	config={}
	config=self.overwrite(config,pconfig)
      	inherited = self.get_config()
#               if('transformations' in config):
      	config=self.overwrite(config, inherited)
	if border==False and 'zoffset' in pconfig:
                zoffset= pconfig['zoffset']
	elif 'zoffset' in config and  config['zoffset']:
		zoffset= config['zoffset']
	else:
		zoffset = 0
	if 'thickness' not in config:
		config['thickness']=pconfig['thickness']
	if config['z0'] is None or config['z0'] is False:
		z0=0
	else:
		z0=config['z0']
	
	if  border==False:
                        z0 += 1

	if (config['z1'] is False or config['z1'] is None) and config['z0'] is not None and config['thickness'] is not None:
        	if  border==False:
                 	z1 = - config['thickness']- 20
           	else:
                   	z1 = - config['thickness']
	else:
		z1= config['z1']
	z0 *=config['zdir']
	z1*=config['zdir']
	#	z0 = - config['thickness'] - z0
	#	z1 = - config['thickness'] - z1
# try to avoid faces and points touching by offsetting them slightly
	z0+=_delta
	z1-=_delta
	_delta+=0.00001
	outline = []
	points = self.polygonise(RESOLUTION)
#	extrude_path = [ Point3(0,0,zoffset + float(z0)), Point3(0,0, zoffset + float(z1)) ]
	for p in points:
		outline.append( [round(p[0],PRECISION)*SCALEUP, round(p[1],PRECISION)*SCALEUP ])
	outline.append([round(points[0][0],PRECISION)*SCALEUP, round(points[0][1],PRECISION)*SCALEUP])
#		outline.append( Point3(p[0], p[1], p[2] ))
#	outline.append( Point3(points[0][0], points[0][1], points[0][2] ))
	h = round(abs(z1-z0),PRECISION)*SCALEUP
	bottom = round((min(z1,z0)+zoffset),PRECISION) *SCALEUP
#	extruded = extrude_along_path(shape_pts=outline, path_pts=extrude_path)
	
	if self.extrude_scale is not None:
                scale = self.extrude_scale
		if self.extrude_centre is None:
			self.extrude_centre = V(0,0)
		centre = (PSharp(V(0,0)).point_transform(config['transformations']).pos+self.extrude_centre)
		centre = [centre[0], centre[1]]
		uncentre = [-centre[0], -centre[1]]	
		extruded = solid.translate([0,0,bottom])(
				solid.translate(centre)(
					solid.linear_extrude(height=h, center=False, scale = scale)(
						solid.translate(uncentre)(solid.polygon(points=outline)))))
        else:
                scale = 1
	        extruded = solid.translate([0,0,bottom])(solid.linear_extrude(height=h, center=False)(solid.polygon(points=outline)))
	#extruded = translate([0,0,bottom])(linear_extrude(height=h, center=False)(solid.polygon(points=outline)))
#	if 'isback' in config and config['isback'] and border==False:
#		extruded = mirror([1,0,0])(extruded )
	if 'colour' in config and config['colour']:
		extruded = solid.color(self.scad_colour(config['colour']))(extruded)
	if hasattr(self, 'transform') and self.transform is not None and self.transform is not False and 'matrix3D' in self.transform:
		if type(self.transform['matrix3D'][0]) is list:
			extruded=solid.translate([-self.transform['rotate3D'][1][0], - self.transform['rotate3D'][1][1]])(extruded)
			extruded=solid.multmatrix(m=self.transform['matrix3D'][0])(extruded)
			extruded=solid.translate([self.transform['rotate3D'][1][0],  self.transform['rotate3D'][1][1]])(extruded)
		else:
			extruded=solid.multmatrix(m=self.transform['matrix3D'])(extruded)
	if hasattr(self, 'transform') and self.transform is not None and self.transform is not False and 'rotate3D' in self.transform:
		if type(self.transform['rotate3D'][0]) is list:
			print [-self.transform['rotate3D'][1][0], - self.transform['rotate3D'][1][1], - self.transform['rotate3D'][1][2]- zoffset]
			extruded=solid.translate([-self.transform['rotate3D'][1][0], - self.transform['rotate3D'][1][1], - self.transform['rotate3D'][1][2]- zoffset])(extruded)
			extruded=solid.rotate([self.transform['rotate3D'][0][0], self.transform['rotate3D'][0][1],self.transform['rotate3D'][0][2] ])(extruded)
			extruded=solid.translate([self.transform['rotate3D'][1][0], self.transform['rotate3D'][1][1], self.transform['rotate3D'][1][1] + zoffset])(extruded)
		else:
			extruded=solid.rotate([self.transform['rotate3D'][0], self.transform['rotate3D'][1],self.transform['rotate3D'][2] ])(extruded)
	if hasattr(self, 'transform') and self.transform is not None and self.transform is not False and 'translate3D' in self.transform:
		extruded=solid.translate([self.transform['translate3D'][0], self.transform['translate3D'][1],self.transform['translate3D'][2] ])(extruded)
	return [extruded]
Example #32
0
def path_render3D(self, pconfig, border=False):
        global _delta, PRECISION, SCALEUP
        self.rotations_to_3D()
        config={}
        config=self.overwrite(config,pconfig)
        inherited = self.get_config()
#               if('transformations' in config):
        config=self.overwrite(config, inherited)
        if border==False and 'zoffset' in pconfig:
                zoffset= pconfig['zoffset']
        elif 'zoffset' in config and  config['zoffset']:
                zoffset= config['zoffset']
        else:
                zoffset = 0
        if 'thickness' not in config:
                config['thickness']=pconfig['thickness']
        if config['z0'] is None or config['z0'] is False:
                z0=0
        else:
                z0=config['z0']
        
        if  border==False:
                        z0 += config['thickness'] +1

        if (config['z1'] is False or config['z1'] is None) and config['z0'] is not None and config['thickness'] is not None:
                if  border==False:
                        z1 = - config['thickness']- 20
                else:
                        z1 = - config['thickness']
        else:
                z1= config['z1']
        z0 *=config['zdir']
        z1*=config['zdir']
        #	z0 = - config['thickness'] - z0
        #	z1 = - config['thickness'] - z1
# try to avoid faces and points touching by offsetting them slightly
        z0+=_delta
        z1-=_delta
        _delta+=0.00001
        outline = []
        self.reset_points()
        points = self.polygonise(RESOLUTION)
#	extrude_path = [ Point3(0,0,zoffset + float(z0)), Point3(0,0, zoffset + float(z1)) ]
        for p in points:
                outline.append( [round(p[0],PRECISION)*SCALEUP, round(p[1],PRECISION)*SCALEUP ])
        outline.append([round(points[0][0],PRECISION)*SCALEUP, round(points[0][1],PRECISION)*SCALEUP])
#		outline.append( Point3(p[0], p[1], p[2] ))
#	outline.append( Point3(points[0][0], points[0][1], points[0][2] ))
        h = round(abs(z1-z0),PRECISION)*SCALEUP
        bottom = round((min(z1,z0)+zoffset),PRECISION) *SCALEUP
# dodgy but working atm
	if not border and 'isback' in config and config['isback']:
		pass
		h = 2*h
#	extruded = extrude_along_path(shape_pts=outline, path_pts=extrude_path)
        
        if self.extrude_scale is not None:
                scale = self.extrude_scale
                if self.extrude_centre is None:
                        self.extrude_centre = V(0,0)
                centre = (PSharp(V(0,0)).point_transform(config['transformations']).pos+self.extrude_centre)
                centre = [centre[0], centre[1]]
                uncentre = [-centre[0], -centre[1]]	
                extruded = solid.translate([0,0,bottom])(
                                solid.translate(centre)(
                                        solid.linear_extrude(height=h, center=False, scale = scale)(
                                                solid.translate(uncentre)(solid.polygon(points=outline)))))
        else:
                scale = 1
                extruded = solid.translate([0,0,bottom])(solid.linear_extrude(height=h, center=False)(solid.polygon(points=outline)))
        #extruded = translate([0,0,bottom])(linear_extrude(height=h, center=False)(solid.polygon(points=outline)))
#	if not border and 'isback' in config and config['isback'] and border==False:
#		extruded = solid.mirror([1,0,0])(extruded )
        if 'colour' in config and config['colour']:
                extruded = solid.color(self.scad_colour(config['colour']))(extruded)
        p=self
        p.rotations_to_3D()
        while(p and type(p) is not Plane and not ( hasattr(p,'renderable') and p.renderable())):# and (c==0 or not p.renderable() )):
                if hasattr(p, 'transform') and p.transform is not None and p.transform is not False and 'matrix3D' in p.transform:
                                if type(p.transform['matrix3D'][0]) is list or type(p.transform['matrix3D'][0]) is Vec:
                                        extruded=solid.translate([-p.transform['matrix3D'][0][0], -p.transform['matrix3D'][0][1],-p.transform['matrix3D'][0][2]])(extruded)
                                        extruded=solid.multmatrix(m=p.transform['matrix3D'][1])(extruded)
                                        extruded=solid.translate([p.transform['matrix3D'][0][0], p.transform['matrix3D'][0][1],p.transform['matrix3D'][0][2]])(extruded)
                                else:
                                        extruded=solid.multmatrix(m=p.transform['matrix3D'])(extruded)

                if hasattr(p, 'transform') and p.transform is not None and p.transform is not False and 'rotate3D' in p.transform:
                                if type(p.transform['rotate3D'][0]) is list or type(p.transform['rotate3D'][0]) is Vec:
                                        if p.transform['rotate3D'][0]!=[0,0,0]:
                                                extruded=solid.translate([-p.transform['rotate3D'][0][0], -p.transform['rotate3D'][0][1],-p.transform['rotate3D'][0][2]])(extruded)
                                        extruded=solid.rotate([p.transform['rotate3D'][1][0], p.transform['rotate3D'][1][1],p.transform['rotate3D'][1][2] ])(extruded)
                                        if p.transform['rotate3D'][0]!=[0,0,0]:
                                                extruded=solid.translate([p.transform['rotate3D'][0][0], p.transform['rotate3D'][0][1],p.transform['rotate3D'][0][2]])(extruded)
                                else:
                                        extruded=solid.rotate([p.transform['rotate3D'][0], p.transform['rotate3D'][1],p.transform['rotate3D'][2] ])(extruded)
                if hasattr(p, 'transform') and p.transform is not None and p.transform is not False and 'translate3D' in p.transform:
                        extruded=solid.translate([p.transform['translate3D'][0], p.transform['translate3D'][1],p.transform['translate3D'][2] ])(extruded)
                p=p.parent
#		if (hasattr(p,'renderable') and p.renderable and p.obType == "Part"):
#			if(p.border is not False and p.border is not None):
#	if hasattr(self, 'transform') and self.transform is not None and self.transform is not False and 'matrix3D' in self.transform:
#		if type(self.transform['matrix3D'][0]) is list:
#			extruded=solid.translate([-self.transform['rotate3D'][0][0], - self.transform['rotate3D'][0][1]])(extruded)
#			extruded=solid.multmatrix(m=self.transform['matrix3D'][0])(extruded)
#			extruded=solid.translate([self.transform['rotate3D'][0][0],  self.transform['rotate3D'][0][1]])(extruded)
#		else:
#			extruded=solid.multmatrix(m=self.transform['matrix3D'])(extruded)
#	if hasattr(self, 'transform') and self.transform is not None and self.transform is not False and 'rotate3D' in self.transform:
#		if type(self.transform['rotate3D'][0]) is list or type(self.transform['rotate3D'][0]) is Vec:
#			extruded=solid.translate([-self.transform['rotate3D'][1][0], - self.transform['rotate3D'][1][1], - self.transform['rotate3D'][1][2]- zoffset])(extruded)
#			extruded=solid.rotate([self.transform['rotate3D'][0][0], self.transform['rotate3D'][0][1],self.transform['rotate3D'][0][2] ])(extruded)
#			extruded=solid.translate([self.transform['rotate3D'][1][0], self.transform['rotate3D'][1][1], self.transform['rotate3D'][1][1] + zoffset])(extruded)
#		else:
#			extruded=solid.rotate([self.transform['rotate3D'][0], self.transform['rotate3D'][1],self.transform['rotate3D'][2] ])(extruded)
#	if hasattr(self, 'transform') and self.transform is not None and self.transform is not False and 'translate3D' in self.transform:
#		extruded=solid.translate([self.transform['translate3D'][0], self.transform['translate3D'][1],self.transform['translate3D'][2] ])(extruded)
        return [extruded]