CadQuery / cadquery

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

Center assemblies #1558

Open chaffra opened 2 months ago

chaffra commented 2 months ago

The code below allows me to stack 2 assemblies but I don't know how to center them, so that they have the same center in the XY plane.

rows = 5
cols = 5
pitch = 8

dx = cols*pitch
dy = rows*pitch
dz = 3*pitch
roic = cq.Workplane('XY').box(dx,dy,dz, centered=True)

r = pitch/2/2
h = (2*r)*2
bumps = cq.Workplane('XY').rarray(pitch,pitch,cols,rows, center=True).cylinder(h,r)

assy = cq.Assembly()
assy.add(roic, name='roic', color=cq.Color('pink'))
assy.add(bumps, name='bumps', color=cq.Color('gray'))
assy.constrain("roic@faces@>Z", "bumps@faces@<Z", "Plane")
assy.solve()
display(assy)
huskier commented 2 months ago

The "Plane" constraint is stronger than "Axis" plus "PointInPlane", and it can make the first part's center meet the second part's center as close as possible. In your case, there are 25 faces for bumps.faces("<Z"), and CQ's assembly takes the first one (-16.0, -16.0)[the one enclosed by the green circle] as the object of "bumps@faces@<Z". So the constraint assy.constrain("roic@faces@>Z", "bumps@faces@<Z", "Plane") makes the bottom face center of enclosed cylinder by the green circle and the top face center of the box coincidence. demo

Using the following constraints can do the job. assy.constrain("roic@faces@>Z", "bumps@faces@<Z", "Axis") assy.constrain("roic@faces@>Z", "bumps@faces@<Z", "PointInPlane") assy.constrain("roic@faces@<Y", "bumps@faces@>Y", "Axis") assy.constrain("roic@faces@>Z", "bumps", "Point", param=(h/2))

Or the following constraints can also do the job. assy.constrain("roic@faces@>Z", "bumps@faces@<Z", "Axis") assy.constrain("roic@faces@>Z", "bumps@faces@<Z", "PointInPlane") assy.constrain("roic", "FixedRotation", (0, 0, 0)) assy.constrain("bumps", "FixedRotation", (0, 0, 0)) assy.constrain("roic@faces@>Z", "bumps", "Point", param=(h/2))

lorenzncode commented 2 months ago

Change this: https://github.com/CadQuery/cadquery/blob/3451007f8eeb9d78e784ec8047ef69a5359ecb5e/cadquery/assembly.py#L271

to something like?:

        if len(res.vals()) > 1:
            if shapes:= _selectShapes(res.objects):
                center = Shape.CombinedCenter(shapes)
                res = getattr(res, query.selector_kind)(NearestToPointSelector(center.toTuple()))

        val = res.val()

Then the original example posted here works as expected.

Otherwise can select and tag single object:

bumps.faces("<Z").faces(cq.selectors.NearestToPointSelector((0, 0, 0))).tag("centerface")
#...
assy.constrain("roic", "Fixed")
assy.constrain("roic@faces@>Z", "bumps?centerface", "Plane")
chaffra commented 2 months ago

Thanks! Both solutions work. Maybe a deeper issue I was after is that you have to use assemblies to get colors. A simpler implementation would be like below but it's missing colors. It would be nice if the primitives could have colors when displaying. Should I file a feature request for that? Something like box(..., color=cq.Color('pink')) would be useful.

rows = 8
cols = 8
pitch = 8

dx = cols*pitch
dy = rows*pitch
dz = 3*pitch

#roic
assy = cq.Workplane('XY').center(0,0).box(dx,dy,dz, centered=True)

#bumps
r = pitch/2/2
h = (2*r)*2
assy = assy.faces('>Z').workplane().rarray(pitch,pitch,cols,rows).cylinder(h,r)
display(assy)
chaffra commented 2 months ago

Plus I think the aspect ratios are different between using assemblies and using the stack directly. The bumps look taller when using assemblies.

image image

adam-urbanczyk commented 2 months ago

No need to use constraints, or modify cq:

rows = 8
cols = 8
pitch = 8

dx = cols*pitch
dy = rows*pitch
dz = 3*pitch

#roic
base = cq.Workplane('XY').center(0,0).box(dx,dy,dz, centered=True)

#bumps
r = pitch/2/2
h = (2*r)*2
top = base.faces('>Z').workplane().rarray(pitch,pitch,cols,rows).circle(r).extrude(h/2,combine=False)

assy = cq.Assembly().add(base, color=cq.Color('pink')).add(top, color=cq.Color('gray'))
show_object(assy)
huskier commented 2 months ago

Plus I think the aspect ratios are different between using assemblies and using the stack directly. The bumps look taller when using assemblies.

@chaffra The difference is expected. When using assembly, there are two individual objects, and the bumps are on the top of the face of the roic box. When using the stack directly, I remember there is an implicit concept that the object is symmetrical with respect to the workplane except for some operations, such as extrude.

If you want to get the same results, the code should be written as following (NB: offset=h/2). assy.faces('>Z').workplane(offset=h/2).rarray(pitch,pitch,cols,rows).cylinder(h,r)