Ejemplo n.º 1
0
    def _make_neg(what):
        """makes a negative shape to be cut out of the parent walls"""
        # this copies some logic in the eachpoint() function so that we can use each() which is safer
        base_plane = self.plane
        base = base_plane.location
        if isinstance(what, (cq.Vector, cq.Shape)):
            loc = base.inverse * cq.Location(base_plane, what.Center())
        elif isinstance(what, cq.Sketch):
            loc = base.inverse * cq.Location(base_plane, what._faces.Center())
        else:
            loc = what

        # fastener threaded holes
        # TODO: mark these holes as "M3-0.5 threaded" in the engineering drawing
        fhs = CQ().pushPoints(fhps).circle(fix_scr.tap_hole_diameters["Soft"] /
                                           2).extrude(-wall_depth +
                                                      pt_fix_wall_buffer)

        nwp = CQ().add(through_face)
        through = nwp.wires().toPending().extrude(-wall_depth)

        nwp2 = CQ().add(recess_face)
        recess = nwp2.wires().toPending().extrude(-part_thickness)

        neg = recess.union(through).union(fhs)

        return neg.findSolid().moved(base * loc)
Ejemplo n.º 2
0
    def _make_pt(what):
        """build a passthrough component"""
        # this copies some logic in the eachpoint() function so that we can use each() which is safer
        base_plane = self.plane
        base = base_plane.location
        if isinstance(what, (cq.Vector, cq.Shape)):
            loc = base.inverse * cq.Location(base_plane, what.Center())
        elif isinstance(what, cq.Sketch):
            loc = base.inverse * cq.Location(base_plane, what._faces.Center())
        else:
            loc = what

        hardware = cadquery.Assembly()
        passthrough = CQ().add(passthrough_face)
        passthrough = passthrough.wires().toPending().extrude(
            -part_thickness)  # extrude the bulk
        slotd = pcbt + 2 * min_gap
        passthrough = passthrough.workplane(
            centerOption="ProjectedOrigin").slot2D(
                length=board_width + slotd / 2, diameter=slotd,
                angle=0).cutThruAll()  # cut the pcb slot
        # TODO: retool some geometry because this cutout could possibly interfere with the oring gland for thick PCBs

        # cut the oring groove
        cq.Workplane.mk_groove = groovy.mk_groove
        oring_path = o_face.outerWire().translate((0, 0, -part_thickness))
        passthrough = passthrough.faces("<<Z").workplane(
            **u.copo).add(oring_path).toPending().mk_groove(ring_cs=oring_cs,
                                                            hardware=hardware)

        # cut the fastening screw holes
        passthrough = passthrough.faces(">Z").workplane(
            **u.copo,
            origin=(0, 0,
                    0)).pushPoints(fhps).clearanceHole(fix_scr,
                                                       fit="Close",
                                                       baseAssembly=hardware)

        # add the support towers
        in_post_length = wall_depth + board_inner_depth
        passthrough = passthrough.faces(">Z").workplane(
            **u.copo, origin=(0, 0, 0)).sketch().push(sbpts).rect(
                *support_block).finalize().extrude(-in_post_length)
        passthrough = passthrough.faces(">Z").workplane(
            **u.copo, origin=(0, 0, 0)).sketch().push(sbpts).rect(
                *support_block).finalize().extrude(board_outer_depth)
        # mount holes
        pcb_center_z = ((board_outer_depth) -
                        (wall_depth + board_inner_depth)) / 2
        passthrough = passthrough.faces("+Y").faces(">>Z").workplane(
            **u.copo, origin=(0, 0, pcb_center_z)).rarray(
                board_width - 2 * pt_pcb_mount_hole_offset[1],
                board_inner_depth + board_outer_depth + wall_depth -
                2 * pt_pcb_mount_hole_offset[0], 2,
                2).clearanceHole(pcb_scr, fit="Close", counterSunk=False)
        passthrough = passthrough.edges("<<Z or >>Z").edges("|Y").fillet(
            pcb_corner)
        passthrough = passthrough.edges(
            "<<Z[-1] or <<Z[-2] or <<Z[-3] or >>Z[-1] or >>Z[-2] or >>Z[-3]"
        ).chamfer(0.5)

        if hw_asy is not None:
            hw_asy.add(hardware, loc=base * loc)

        return passthrough.findSolid().moved(base * loc)
Ejemplo n.º 3
0
def make_oringer(
    self: cq.Workplane,
    board_width: float = 84.12,
    board_inner_depth: float = 9.271,
    board_outer_depth: float = 9.271,
    part_thickness: float = 0,
    wall_depth: float = 0,
    screw="M3-0.5",
    pt_asy: cadquery.Assembly = None,
    pcb_asy: cadquery.Assembly = None,
    hw_asy: cadquery.Assembly = None,
) -> cq.Workplane:
    logger = logging.getLogger(__name__)
    if wall_depth == 0:  # if depth is not given do our best to find it
        wall_depth = u.find_length(self, along="normal", bb_method=False)
    if part_thickness == 0:  # if thickness is not given, use half the wall thickness
        part_thickness = wall_depth / 2

    pcbt = 1.6  # pcb thickness
    washert = 0.5  # washer thickness

    screw_nominal_d = 3
    screw_head_nominal_d = 6

    # header specific for making the pin0 holes, probably doesn't belong here, but it's too convenient...
    non_notch_side_chunk_width = 0.381  # is 0.15 in
    non_chunk_con_width = 8.89  # is 0.35 in
    pin0_offsetx = non_chunk_con_width / 2 + non_notch_side_chunk_width + 2.54 / 2
    pin0_offsety_25 = 2.54 * (25 - 1) / 2
    pin0_offsety_20 = 2.54 * (20 - 1) / 2
    pin0_holed = 1

    p0pts = [
    ]  # the pin pin 1 points for the two connectors (for checking the correctness of the PCB designs)
    p0pts.append((pin0_offsety_25, pin0_offsetx))
    p0pts.append((-pin0_offsety_20 + 2.5 * 2.54, -(wall_depth + pin0_offsetx)))

    block_width = 7
    block_height_nominal = 6
    support_block = (block_width, block_height_nominal - washert
                     )  # actual support block (leaves room for washer)

    pcb_corner = 2
    pt_pcb_mount_hole_offset = (4.445, block_width / 2)  # from corners

    pcb_scr_len = 12  # SHP-M3-12-V2-A2, round(block_height_nominal + pcbt + 4)
    pt_fix_scr_len = 10  # SHK-M3-10-V2-A2, round(wall_depth * 0.8)
    pt_fix_wall_buffer = 1  # amount of wall to leave behind the threaded screw hole
    fix_scr = CounterSunkScrew(size=screw,
                               fastener_type="iso14581",
                               length=pt_fix_scr_len)
    pcb_scr = PanHeadScrew(size=screw,
                           fastener_type="iso14583",
                           length=pcb_scr_len)
    # washer = CheeseHeadWasher(size=screw, fastener_type="iso7092")
    # nylock nut = HNN-M3-A2

    oring_cs = 1  # oring thickness

    min_radius = oring_cs * 3  # min inner bend radius
    min_wall = 0.8  # walls should not be mfg'd thinner than this

    min_gap = 0.25  # cutting tolerances prevent smaller gaps between things

    gland_width = groovy.get_gland_width(oring_cs)
    # effective_gland_width = (round(gland_width * 100) + 1) / 100  # rounded up to the nearest 0.01mm
    # logger.info(f"Using {effective_gland_width=}")

    # some important radii for construction to ensure we don't overbend the o-ring
    minr1 = min_radius - min_wall
    minr2 = min_radius + min_wall + gland_width

    # actual support block centers
    sbpts = []
    sbx = board_width / 2 - block_width / 2
    sby = ((-pcbt / 2 - washert) + (-pcbt / 2 - block_height_nominal)) / 2
    sbpts.append((sbx, sby))
    sbpts.append((-sbx, sby))

    in_off = minr1 * 2**-0.5 - min_gap * 2**0.5 / 2  # exact inward offset to get min_gap spacing with minr1 fillets

    ffo = minr1 * (1 - 2**-0.5) + min_gap * (2**0.5 / 2)
    co_tw = minr1 * 2 + min_gap  # width of the thin part of the cutout (so that the cutting tool can easily fit)
    max_slot_y = ffo + pcbt + block_height_nominal + ffo  # width at the slot at its max
    if co_tw > max_slot_y:
        co_tw = max_slot_y

    scy = -pcbt / 2 - block_height_nominal + in_off  # y coordinate for the inner, small radius circle
    scx = board_width / 2 - block_width + in_off  # small, inner circle x value

    tcy = pcbt / 2 - in_off  # top circles center y value
    tcx = board_width / 2 - in_off  # top circles center c values
    tcpts = []  # top circle points
    tcpts.append((tcx, tcy))
    tcpts.append((-tcx, tcy))

    ocpts1 = []  # outer circle points for positive x
    ocpts1.append((tcx, tcy))
    ocpts1.append((tcx, scy))

    ocpts2 = []  # outer circle points for negative x
    ocpts2.append((-tcx, tcy))
    ocpts2.append((-tcx, scy))

    bcpts1 = []  # bottom circle points for positive x
    bcpts1.append((tcx, scy))
    bcpts1.append((scx, scy))

    bcpts2 = []  # bottom circle points for negative x
    bcpts2.append((-tcx, scy))
    bcpts2.append((-scx, scy))

    icpts1 = []  # inner circle points for positive x
    icpts1.append((scx, scy))
    icpts1.append((scx, tcy))

    icpts2 = []  # inner circle points for negative x
    icpts2.append((-scx, scy))
    icpts2.append((-scx, tcy))

    swp = CQ().sketch()

    # need support block shapes to fill in gaps
    swp.push(sbpts).rect(*support_block)

    # the fillets at the bottom collide and should be unified with a circle, but the ones at the sides don't
    if (2 * minr1 > 2 * ffo + block_width) and (2 * minr1 < max_slot_y):
        scx = board_width / 2 - block_width / 2  # new center point for circles
        tcpts = []  # top circle points
        tcpts.append((scx, tcy))
        tcpts.append((-scx, tcy))

        ocpts1 = []  # outer circle points for positive x
        ocpts1.append((scx, tcy))
        ocpts1.append((scx, scy))

        ocpts2 = []  # outer circle points for negative x
        ocpts2.append((-scx, tcy))
        ocpts2.append((-scx, scy))

        # make the right circle hull
        swp.push(ocpts1).circle(
            minr1, mode="c",
            tag="c").reset().edges(tag="c").hull().clean().reset()

        # make the left circle hull
        swp.push(ocpts2).circle(
            minr1, mode="c",
            tag="d").reset().edges(tag="d").hull().clean().reset()

    # the fillets at the side collide and should be unified with a circle
    elif 2 * minr1 >= max_slot_y:
        cpts = []  # center points for new circles
        scy = (pcbt / 2 + (-pcbt / 2 - block_height_nominal)) / 2
        tcpts = []  # top circle points
        tcpts.append((tcx, scy))
        tcpts.append((-tcx, scy))

    # normal case, no fillets collide
    else:
        # make the right outer circle hull
        swp.push(ocpts1).circle(
            minr1, mode="c",
            tag="c").reset().edges(tag="c").hull().clean().reset()

        # make the left outer circle hull
        swp.push(ocpts2).circle(
            minr1, mode="c",
            tag="d").reset().edges(tag="d").hull().clean().reset()

        # make the right bottom circle hull
        swp.push(bcpts1).circle(
            minr1, mode="c",
            tag="e").reset().edges(tag="e").hull().clean().reset()

        # make the left bottom circle hull
        swp.push(bcpts2).circle(
            minr1, mode="c",
            tag="f").reset().edges(tag="f").hull().clean().reset()

    bcy = pcbt / 2 + ffo - (co_tw + minr2
                            )  # y coordinate for the large radius circle

    # make the top circle hull
    swp.push(tcpts).circle(
        minr1, mode="c",
        tag="g").reset().edges(tag="g").hull().clean().reset()

    # do all the big circle stuff only if the outer fillets haven't merged
    if not (2 * minr1 > max_slot_y):
        o = scy - bcy  # opposite triangle side length (along y)
        h = minr2 + minr1  # hypotenuse
        if o < 0:  # the circles have moved apart: big one above small one (and the trig breaks)
            a = h
        elif o > h:  # the circles have moved apart (and the trig breaks)
            a = h  # adjacent (along x)
        else:
            a = h * math.cos(math.asin(o / h))
            # a = o/math.tan(math.asin(o/h))  # adjacent (along x)
        bcx = scx - a  # big circle x
        # bcy = pcbt/2+ffo-(co_tw+minr2)
        bcpts = []
        bcpts.append((-bcx, bcy))
        bcpts.append((bcx, bcy))

        swp.push([(-scx, scy), (scx, scy)]).circle(minr1).clean().reset()

        if o < 0:  # the circles have moved apart: big one above small one
            scy = bcy
            if 2 * minr1 < 2 * ffo + block_width:  # the bottom fillets haven't merged
                # make the left inner circle hull
                swp.push(icpts1).circle(
                    minr1, mode="c",
                    tag="h").reset().edges(tag="h").hull().clean().reset()
                # make the right inner circle hull
                swp.push(icpts2).circle(
                    minr1, mode="c",
                    tag="i").reset().edges(tag="i").hull().clean().reset()

        swp.polygon([(-bcx, bcy), (bcx, bcy), (scx, scy), (scx, 0), (-scx, 0),
                     (-scx, scy), (-bcx, bcy)]).clean().reset()

        swp.push(bcpts).circle(
            minr2, mode="s").clean().reset()  # cut away the large circles

        swp.push([(0, bcy)]).rect(
            2 * bcx, minr2 * 2,
            mode="s").clean().reset()  # cut away the space between the circles

    through_face = swp.finalize().extrude(-1).faces(
        ">>Z").val()  # get just the face for the through cut

    swp = swp.wires().offset(min_wall + gland_width /
                             2).clean().reset()  # inner edge of ogland
    o_face = swp.finalize().extrude(-1).faces(
        ">>Z").val()  # get just the face for the oring path wire

    # passthrough face
    pfw = min_wall + gland_width + min_wall + ffo + board_width + ffo + min_wall + gland_width + min_wall  # passthrough face width
    pfha = min_wall + gland_width + min_wall + screw_nominal_d + (
        screw_head_nominal_d / 2 - screw_nominal_d /
        2) + min_wall  # passthrough face height above cutout top edge
    pfhb1 = co_tw + min_wall + gland_width + min_wall + screw_nominal_d + (
        screw_head_nominal_d / 2 - screw_nominal_d /
        2) + min_wall  # passthrough face height below cutout top edge
    pfhb2 = ffo + pcbt + block_height_nominal + ffo + min_wall + gland_width + min_wall  # passthrough face height below cutout top edge if limited by support block clearance
    if pfhb2 > pfhb1:  # if the part below the support blocks would be lower, use that to determine the face height
        pfhb = pfhb2
    else:
        pfhb = pfhb1
    pfha_pcb = pfha + pcbt / 2 + ffo  # passthrough face height above PCB middle
    pfh = pfha + pfhb  # passthrough face height
    pfx = 0
    pfy = -pfh / 2 + pfha_pcb
    pf_ctr = (pfx, pfy)  # passthrough face center
    pfdim = (pfw, pfh)  # passthrough face dims
    pf_fillets = 5  # fillets to corners of passthrough face
    pfwp = CQ().sketch()  # make passthrough face sketch workplane
    pfwp = pfwp.push([pf_ctr]).rect(*pfdim).reset()
    pfwp = pfwp.vertices().fillet(pf_fillets).clean().reset()
    # swp = swp.wires().offset(gland_width / 2 + min_wall).clean().reset()  # edge of passthrough part
    # + screw_nominal_d + (screw_head_nominal_d / 2 - screw_nominal_d / 2) + min_wall
    passthrough_face = pfwp.finalize().extrude(-1).faces(
        ">>Z").val()  # get just the face for the passthrough part

    pfwp = pfwp.wires().offset(min_gap).clean().reset()  # edge of recess_cut
    recess_face = pfwp.finalize().extrude(-1).faces(
        ">>Z").val()  # get just the face for the recess

    # fastening screw hole points
    fhps = []
    fhps.append(((board_width - 2 * block_width / 2) / 2,
                 pcbt / 2 + ffo + min_wall + gland_width + min_wall +
                 fix_scr.clearance_hole_diameters["Close"] / 2))
    fhps.append((-(board_width - 2 * block_width / 2) / 2,
                 pcbt / 2 + ffo + min_wall + gland_width + min_wall +
                 fix_scr.clearance_hole_diameters["Close"] / 2))
    fhps.append((bcx, pcbt / 2 + ffo - co_tw - min_wall - gland_width -
                 min_wall - fix_scr.clearance_hole_diameters["Close"] / 2))
    fhps.append((-bcx, pcbt / 2 + ffo - co_tw - min_wall - gland_width -
                 min_wall - fix_scr.clearance_hole_diameters["Close"] / 2))

    def _make_pcb(what):
        """build the actual passthrough PCB"""
        # this copies some logic in the eachpoint() function so that we can use each() which is safer
        base_plane = self.plane
        base = base_plane.location
        if isinstance(what, (cq.Vector, cq.Shape)):
            loc = base.inverse * cq.Location(base_plane, what.Center())
        elif isinstance(what, cq.Sketch):
            loc = base.inverse * cq.Location(base_plane, what._faces.Center())
        else:
            loc = what

        pcb = CQ().workplane(offset=-wall_depth - board_inner_depth)
        pcb = pcb.rect(board_width,
                       pcbt).extrude(until=board_inner_depth + wall_depth +
                                     board_outer_depth)

        pcb = pcb.edges("|Y").fillet(pcb_corner)

        # put in screws with holes
        hardware = cadquery.Assembly()
        pcb = pcb.faces(">Y").workplane(**u.cobb).rarray(
            board_width - 2 * block_width / 2, board_inner_depth +
            board_outer_depth + wall_depth - 2 * pt_pcb_mount_hole_offset[0],
            2, 2).clearanceHole(pcb_scr,
                                fit="Close",
                                counterSunk=False,
                                baseAssembly=hardware)
        if hw_asy is not None:
            hw_asy.add(hardware, loc=base * loc)

        # put in pin0 holes
        pcb = pcb.faces(">Y").workplane(**u.copo,
                                        origin=(0, 0,
                                                0)).pushPoints(p0pts).circle(
                                                    pin0_holed /
                                                    2).cutThruAll()

        return pcb.findSolid().moved(base * loc)

    def _make_pt(what):
        """build a passthrough component"""
        # this copies some logic in the eachpoint() function so that we can use each() which is safer
        base_plane = self.plane
        base = base_plane.location
        if isinstance(what, (cq.Vector, cq.Shape)):
            loc = base.inverse * cq.Location(base_plane, what.Center())
        elif isinstance(what, cq.Sketch):
            loc = base.inverse * cq.Location(base_plane, what._faces.Center())
        else:
            loc = what

        hardware = cadquery.Assembly()
        passthrough = CQ().add(passthrough_face)
        passthrough = passthrough.wires().toPending().extrude(
            -part_thickness)  # extrude the bulk
        slotd = pcbt + 2 * min_gap
        passthrough = passthrough.workplane(
            centerOption="ProjectedOrigin").slot2D(
                length=board_width + slotd / 2, diameter=slotd,
                angle=0).cutThruAll()  # cut the pcb slot
        # TODO: retool some geometry because this cutout could possibly interfere with the oring gland for thick PCBs

        # cut the oring groove
        cq.Workplane.mk_groove = groovy.mk_groove
        oring_path = o_face.outerWire().translate((0, 0, -part_thickness))
        passthrough = passthrough.faces("<<Z").workplane(
            **u.copo).add(oring_path).toPending().mk_groove(ring_cs=oring_cs,
                                                            hardware=hardware)

        # cut the fastening screw holes
        passthrough = passthrough.faces(">Z").workplane(
            **u.copo,
            origin=(0, 0,
                    0)).pushPoints(fhps).clearanceHole(fix_scr,
                                                       fit="Close",
                                                       baseAssembly=hardware)

        # add the support towers
        in_post_length = wall_depth + board_inner_depth
        passthrough = passthrough.faces(">Z").workplane(
            **u.copo, origin=(0, 0, 0)).sketch().push(sbpts).rect(
                *support_block).finalize().extrude(-in_post_length)
        passthrough = passthrough.faces(">Z").workplane(
            **u.copo, origin=(0, 0, 0)).sketch().push(sbpts).rect(
                *support_block).finalize().extrude(board_outer_depth)
        # mount holes
        pcb_center_z = ((board_outer_depth) -
                        (wall_depth + board_inner_depth)) / 2
        passthrough = passthrough.faces("+Y").faces(">>Z").workplane(
            **u.copo, origin=(0, 0, pcb_center_z)).rarray(
                board_width - 2 * pt_pcb_mount_hole_offset[1],
                board_inner_depth + board_outer_depth + wall_depth -
                2 * pt_pcb_mount_hole_offset[0], 2,
                2).clearanceHole(pcb_scr, fit="Close", counterSunk=False)
        passthrough = passthrough.edges("<<Z or >>Z").edges("|Y").fillet(
            pcb_corner)
        passthrough = passthrough.edges(
            "<<Z[-1] or <<Z[-2] or <<Z[-3] or >>Z[-1] or >>Z[-2] or >>Z[-3]"
        ).chamfer(0.5)

        if hw_asy is not None:
            hw_asy.add(hardware, loc=base * loc)

        return passthrough.findSolid().moved(base * loc)

    def _make_neg(what):
        """makes a negative shape to be cut out of the parent walls"""
        # this copies some logic in the eachpoint() function so that we can use each() which is safer
        base_plane = self.plane
        base = base_plane.location
        if isinstance(what, (cq.Vector, cq.Shape)):
            loc = base.inverse * cq.Location(base_plane, what.Center())
        elif isinstance(what, cq.Sketch):
            loc = base.inverse * cq.Location(base_plane, what._faces.Center())
        else:
            loc = what

        # fastener threaded holes
        # TODO: mark these holes as "M3-0.5 threaded" in the engineering drawing
        fhs = CQ().pushPoints(fhps).circle(fix_scr.tap_hole_diameters["Soft"] /
                                           2).extrude(-wall_depth +
                                                      pt_fix_wall_buffer)

        nwp = CQ().add(through_face)
        through = nwp.wires().toPending().extrude(-wall_depth)

        nwp2 = CQ().add(recess_face)
        recess = nwp2.wires().toPending().extrude(-part_thickness)

        neg = recess.union(through).union(fhs)

        return neg.findSolid().moved(base * loc)

    rslt = self.each(_make_neg,
                     useLocalCoordinates=False,
                     combine="cut",
                     clean=True)

    # pass out the passthrough geometry
    if pt_asy is not None:
        passthroughs = self.each(_make_pt,
                                 useLocalCoordinates=False,
                                 combine=False).vals()
        for i, passthrough in enumerate(passthroughs):
            pt_asy.add(passthrough.Solids()[0], name=f"passthrough {i}")

    # pass out the pcb geometry
    if pcb_asy is not None:
        # pcbs = self.eachpoint(lambda loc: _make_pcb().moved(loc), useLocalCoordinates=True, combine=False).vals()
        pcbs = self.each(_make_pcb, useLocalCoordinates=False,
                         combine=False).vals()
        for i, pcb in enumerate(pcbs):
            pcb_asy.add(pcb.Solids()[0], name=f"pcb {i}")

    return rslt