CadQuery / cadquery

A python parametric CAD scripting framework based on OCCT
https://cadquery.readthedocs.io
Other
3.26k stars 295 forks source link

Try to improve the readability of the enclosure door assembly example code in the assembly tutorial section #1546

Closed huskier closed 8 months ago

huskier commented 8 months ago

It is quite complicated to understand the constraints of the enclosure door assembly example code in the assembly tutorial section. I've tried an approach to improve the readability of the example code.

To assemble the parts, I've firstly made them, and then put them in relative rough positions. In this layout state, I've set the constraints. In this way, it is much better to understand the constraints, at least for me. Actually, it's the GUI's manner (such as Solidworks, UG, and Creo etc.) to assemble models.

Here is my source code. I could do a PR if people think it is useful.

import cadquery as cq
from cadquery.vis import show

# Parameters
H = 400
W = 200
D = 350

PROFILE = cq.importers.importDXF("vslot-2020_1.dxf").wires()

SLOT_D = 6
PANEL_T = 3

HANDLE_D = 20
HANDLE_L = 50
HANDLE_W = 4

def make_vslot(l):
    return PROFILE.toPending().extrude(l)

def make_connector():
    rv = (
        cq.Workplane()
        .box(20, 20, 20)
        .faces("<X")
        .workplane()
        .cboreHole(6, 15, 18)
        .faces("<Z")
        .workplane(centerOption="CenterOfMass")
        .cboreHole(6, 15, 18)
    )

    # tag mating faces
    rv.faces(">X").tag("X").end()
    rv.faces(">Z").tag("Z").end()

    return rv

def make_panel(w, h, t, cutout):
    rv = (
        cq.Workplane("XZ")
        .rect(w, h)
        .extrude(t)
        .faces(">Y")
        .vertices()
        .rect(2 * cutout, 2 * cutout)
        .cutThruAll()
        .faces("<Y")
        .workplane()
        .pushPoints([(-w / 3, HANDLE_L / 2), (-w / 3, -HANDLE_L / 2)])
        .hole(3)
    )

    # tag mating edges
    rv.faces(">Y").edges("%CIRCLE").edges(">Z").tag("hole1")
    rv.faces(">Y").edges("%CIRCLE").edges("<Z").tag("hole2")

    return rv

def make_handle(w, h, r):
    pts = ((0, 0), (w, 0), (w, h), (0, h))

    path = cq.Workplane().polyline(pts)

    rv = (
        cq.Workplane("YZ")
        .rect(r, r)
        .sweep(path, transition="round")
        .tag("solid")
        .faces("<X")
        .workplane()
        .faces("<X", tag="solid")
        .hole(r / 1.5)
    )

    # tag mating faces
    rv.faces("<X").faces(">Y").tag("mate1")
    rv.faces("<X").faces("<Y").tag("mate2")

    return rv

con_tl = make_connector().rotate((0,0,0),(0,1,0),90).translate((0, 20,H+30))
con_tr = make_connector().rotate((0,0,0),(0,1,0),180).translate((W+60,0,H+30))
con_bl = make_connector().translate((0,0,-30))
con_br = make_connector().rotate((0,0,0),(0,1,0),-90).translate((W+60,0,-30))

left = make_vslot(H)
right = make_vslot(H).translate((W+60,0,0))
top = make_vslot(W).rotate((0,0,0),(0,1,0),90).translate((30,0,H+30))
bottom = make_vslot(W).rotate((0,0,0),(0,1,0),90).translate((30,0, -30))

panel = make_panel(W + 2 * SLOT_D, H + 2 * SLOT_D, PANEL_T, SLOT_D)
handle = make_handle(HANDLE_D, HANDLE_L, HANDLE_W) #.rotate((0,0,0),(1,0,0),90).rotate((0,0,0),(0,0,1),-90).translate((W/2,0,H/2))

door_asm = cq.Assembly()
door_asm.add(con_tl, color=cq.Color(1, 0, 0), name="con_tl")
door_asm.add(con_tr, color=cq.Color(1, 0, 0), name="con_tr")
door_asm.add(con_bl, color=cq.Color(1, 0, 0), name="con_bl")
door_asm.add(con_br, color=cq.Color(1, 0, 0), name="con_br")

door_asm.add(left, name="left")
door_asm.add(right, name="right")
door_asm.add(top, name="top")
door_asm.add(bottom, name="bottom")

door_asm.add(panel, color=cq.Color(0, 0, 1, 0.2), name="panel")
door_asm.add(handle, color=cq.Color("yellow"), name="handle")

# define the constraints
(
    door_asm
    # left profile
    .constrain("left@faces@<Z", "con_bl@faces@>Z", "Plane")
    .constrain("left@faces@<X", "con_bl@faces@>X", "Axis")
    .constrain("left@faces@>Z", "con_tl@faces@<Z", "Plane")
    .constrain("left@faces@<X", "con_tl@faces@>X", "Axis")
    # top
    .constrain("top@faces@<X", "con_tl@faces@>X", "Plane")
    .constrain("top@faces@>Z", "con_tl@faces@<Z", "Axis")
    # bottom
    .constrain("bottom@faces@<Y", "con_bl@faces@>Y", "Axis")
    .constrain("bottom@faces@<X", "con_bl@faces@>X", "Plane")
    # right connectors
    .constrain("top@faces@>X", "con_tr@faces@<X", "Plane")
    .constrain("bottom@faces@>X", "con_br@faces@<X", "Plane")
    .constrain("left@faces@>Y", "con_tr@faces@<Y", "Axis")
    .constrain("left@faces@>Y", "con_br@faces@<Y", "Axis")
    # right profile
    .constrain("right@faces@>Z", "con_tr@faces@<Z", "Plane")
    .constrain("right@faces@<X", "left@faces@>X", "Axis")
    # panel
    .constrain("left@faces@>X[-4]", "panel@faces@<X", "Plane")
    .constrain("left@faces@>Z", "panel@faces@>Z", "Axis")
    # handle
    .constrain("panel?hole1", "handle?mate1", "Plane")
    .constrain("panel?hole2", "handle?mate2", "Point")
)

# solve
door_asm.solve()
show(door_asm)
#door_asm.save('door.step')

If I've done a rotation or a translation to a part, the tagged mating assistants are not useful since the coordination system has changed.

At present, I do not know how to improve the handle part code.

adam-urbanczyk commented 8 months ago

I miss the point of this and find it confusing. Selection is done in the original coordinate system of the parts.

huskier commented 8 months ago

@adam-urbanczyk Hi, Adam, This issue is related to #1538. It is originated from the understanding of the following constraint.

# top
.constrain("top@faces@<Z", "con_tl?X", "Plane")

Through making a pre-arranged initial parts layout, I think the constraints become more straightforward.

# top
.constrain("top@faces@<X", "con_tl@faces@>X", "Plane")
adam-urbanczyk commented 8 months ago

That is exactly the same constraint. What is exactly more readable here? Also, you are not using tags, which is one of the things that the example is trying to demonstrate.

I seriously don't understand what was your question in #1538. It reads as if you did not get how assy constraints work, even though it is quite exactly specified in the docs.

huskier commented 8 months ago

OK, Let's forget this issue and the issue of #1538.