Skip to content

Commit 248a439

Browse files
Initial version of the constraints docs
1 parent 20eb996 commit 248a439

File tree

5 files changed

+426
-3
lines changed

5 files changed

+426
-3
lines changed

doc/_static/door_assy.png

39.4 KB
Loading

doc/_static/door_assy_freecad.png

38.8 KB
Loading

doc/assy.rst

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
.. _assy_tutorial:
2+
3+
***********************
4+
Assembly Tutorial
5+
***********************
6+
7+
Introduction
8+
============
9+
10+
The purpose of this section is to demonstrate how to use the assembly and constraints
11+
functionality to build a realistic model. It will be a enclosure door assembly made out of 20x20 v-slot profiles.
12+
13+
14+
Defining parameters
15+
===================
16+
17+
We want to start with defining the model parameters to allow for easy dimension changes later:
18+
19+
.. code-block:: python
20+
:linenos:
21+
22+
import cadquery as cq
23+
24+
# Parameters
25+
H = 400
26+
W = 200
27+
D = 350
28+
29+
PROFILE = cq.importers.importDXF("vslot-2020_1.dxf").wires()
30+
31+
SLOT_D = 5
32+
PANEL_T = 3
33+
34+
HANDLE_D = 20
35+
HANDLE_L = 50
36+
HANDLE_W = 4
37+
38+
It is interesting to note that the v-slot profile is imported from a DXF file.
39+
This way it is very easy to change to other aluminum extrusion type, e.g. Item or Bosch.
40+
Vendors usually provide DXF files.
41+
42+
Defining reusable components
43+
============================
44+
45+
Next we want to define functions generating the assembly components based on the specified parameters.
46+
47+
.. code-block:: python
48+
:linenos:
49+
50+
def make_vslot(l):
51+
52+
return PROFILE.toPending().extrude(l)
53+
54+
55+
def make_connector():
56+
57+
rv = (
58+
cq.Workplane()
59+
.box(20, 20, 20)
60+
.faces("<X")
61+
.workplane()
62+
.cboreHole(6, 15, 18)
63+
.faces("<Z")
64+
.workplane()
65+
.cboreHole(6, 15, 18)
66+
)
67+
68+
# tag mating faces
69+
rv.faces(">X").tag("X").end()
70+
rv.faces(">Z").tag("Z").end()
71+
72+
return rv
73+
74+
75+
def make_panel(w, h, t):
76+
77+
rv = (
78+
cq.Workplane("XZ")
79+
.rect(w, h)
80+
.extrude(t)
81+
.faces("<Y")
82+
.workplane()
83+
.pushPoints([(-w / 3, HANDLE_L / 2), (-w / 3, -HANDLE_L / 2)])
84+
.hole(3)
85+
)
86+
87+
# tag mating edges
88+
rv.faces(">Y").edges("%CIRCLE").edges(">Z").tag("hole1")
89+
rv.faces(">Y").edges("%CIRCLE").edges("<Z").tag("hole2")
90+
91+
return rv
92+
93+
94+
def make_handle(w, h, r):
95+
96+
pts = ((0, 0), (w, 0), (w, h), (0, h))
97+
98+
path = cq.Workplane().polyline(pts)
99+
100+
rv = (
101+
cq.Workplane("YZ")
102+
.rect(r, r)
103+
.sweep(path, transition="round")
104+
.tag("solid")
105+
.faces("<X")
106+
.workplane()
107+
.faces("<X", tag="solid")
108+
.hole(r / 1.5)
109+
)
110+
111+
# tag mating faces
112+
rv.faces("<X").faces(">Y").tag("mate1")
113+
rv.faces("<X").faces("<Y").tag("mate2")
114+
115+
return rv
116+
117+
Initial assembly
118+
================
119+
120+
Next we want to instantiate all the components and add them to the assembly.
121+
122+
.. code-block:: python
123+
:linenos:
124+
125+
# define the elements
126+
door = (
127+
cq.Assembly()
128+
.add(make_vslot(H), name="left")
129+
.add(make_vslot(H), name="right")
130+
.add(make_vslot(W), name="top")
131+
.add(make_vslot(W), name="bottom")
132+
.add(make_connector(), name="con_tl", color=cq.Color("black"))
133+
.add(make_connector(), name="con_tr", color=cq.Color("black"))
134+
.add(make_connector(), name="con_bl", color=cq.Color("black"))
135+
.add(make_connector(), name="con_br", color=cq.Color("black"))
136+
.add(
137+
make_panel(W + SLOT_D, H + SLOT_D, PANEL_T),
138+
name="panel",
139+
color=cq.Color(0, 0, 1, 0.2),
140+
)
141+
.add(
142+
make_handle(HANDLE_D, HANDLE_L, HANDLE_W),
143+
name="handle",
144+
color=cq.Color("yellow"),
145+
)
146+
)
147+
148+
Constraints definition
149+
======================
150+
151+
Then we want to define all the constraints
152+
153+
.. code-block:: python
154+
:linenos:
155+
156+
# define the elements
157+
door = (
158+
cq.Assembly()
159+
.add(make_vslot(H), name="left")
160+
.add(make_vslot(H), name="right")
161+
.add(make_vslot(W), name="top")
162+
.add(make_vslot(W), name="bottom")
163+
.add(make_connector(), name="con_tl", color=cq.Color("black"))
164+
.add(make_connector(), name="con_tr", color=cq.Color("black"))
165+
.add(make_connector(), name="con_bl", color=cq.Color("black"))
166+
.add(make_connector(), name="con_br", color=cq.Color("black"))
167+
.add(
168+
make_panel(W + SLOT_D, H + SLOT_D, PANEL_T),
169+
name="panel",
170+
color=cq.Color(0, 0, 1, 0.2),
171+
)
172+
.add(
173+
make_handle(HANDLE_D, HANDLE_L, HANDLE_W),
174+
name="handle",
175+
color=cq.Color("yellow"),
176+
)
177+
)
178+
179+
Final result
180+
============
181+
182+
Below is the complete code including the final solve step.
183+
184+
.. code-block:: python
185+
:linenos:
186+
187+
import cadquery as cq
188+
189+
# Parameters
190+
H = 400
191+
W = 200
192+
D = 350
193+
194+
PROFILE = cq.importers.importDXF("vslot-2020_1.dxf").wires()
195+
196+
SLOT_D = 5
197+
PANEL_T = 3
198+
199+
HANDLE_D = 20
200+
HANDLE_L = 50
201+
HANDLE_W = 4
202+
203+
204+
def make_vslot(l):
205+
206+
return PROFILE.toPending().extrude(l)
207+
208+
209+
def make_connector():
210+
211+
rv = (
212+
cq.Workplane()
213+
.box(20, 20, 20)
214+
.faces("<X")
215+
.workplane()
216+
.cboreHole(6, 15, 18)
217+
.faces("<Z")
218+
.workplane()
219+
.cboreHole(6, 15, 18)
220+
)
221+
222+
# tag mating faces
223+
rv.faces(">X").tag("X").end()
224+
rv.faces(">Z").tag("Z").end()
225+
226+
return rv
227+
228+
229+
def make_panel(w, h, t):
230+
231+
rv = (
232+
cq.Workplane("XZ")
233+
.rect(w, h)
234+
.extrude(t)
235+
.faces("<Y")
236+
.workplane()
237+
.pushPoints([(-w / 3, HANDLE_L / 2), (-w / 3, -HANDLE_L / 2)])
238+
.hole(3)
239+
)
240+
241+
# tag mating edges
242+
rv.faces(">Y").edges("%CIRCLE").edges(">Z").tag("hole1")
243+
rv.faces(">Y").edges("%CIRCLE").edges("<Z").tag("hole2")
244+
245+
return rv
246+
247+
248+
def make_handle(w, h, r):
249+
250+
pts = ((0, 0), (w, 0), (w, h), (0, h))
251+
252+
path = cq.Workplane().polyline(pts)
253+
254+
rv = (
255+
cq.Workplane("YZ")
256+
.rect(r, r)
257+
.sweep(path, transition="round")
258+
.tag("solid")
259+
.faces("<X")
260+
.workplane()
261+
.faces("<X", tag="solid")
262+
.hole(r / 1.5)
263+
)
264+
265+
# tag mating faces
266+
rv.faces("<X").faces(">Y").tag("mate1")
267+
rv.faces("<X").faces("<Y").tag("mate2")
268+
269+
return rv
270+
271+
272+
# define the elements
273+
door = (
274+
cq.Assembly()
275+
.add(make_vslot(H), name="left")
276+
.add(make_vslot(H), name="right")
277+
.add(make_vslot(W), name="top")
278+
.add(make_vslot(W), name="bottom")
279+
.add(make_connector(), name="con_tl", color=cq.Color("black"))
280+
.add(make_connector(), name="con_tr", color=cq.Color("black"))
281+
.add(make_connector(), name="con_bl", color=cq.Color("black"))
282+
.add(make_connector(), name="con_br", color=cq.Color("black"))
283+
.add(
284+
make_panel(W + SLOT_D, H + SLOT_D, PANEL_T),
285+
name="panel",
286+
color=cq.Color(0, 0, 1, 0.2),
287+
)
288+
.add(
289+
make_handle(HANDLE_D, HANDLE_L, HANDLE_W),
290+
name="handle",
291+
color=cq.Color("yellow"),
292+
)
293+
)
294+
295+
# define the constraints
296+
(
297+
door
298+
# left profile
299+
.constrain("left@faces@<Z", "con_bl?Z", "Plane")
300+
.constrain("left@faces@<X", "con_bl?X", "Axis")
301+
.constrain("left@faces@>Z", "con_tl?Z", "Plane")
302+
.constrain("left@faces@<X", "con_tl?X", "Axis")
303+
# top
304+
.constrain("top@faces@<Z", "con_tl?X", "Plane")
305+
.constrain("top@faces@<Y", "con_tl@faces@>Y", "Axis")
306+
# bottom
307+
.constrain("bottom@faces@<Y", "con_bl@faces@>Y", "Axis")
308+
.constrain("bottom@faces@>Z", "con_bl?X", "Plane")
309+
# right connectors
310+
.constrain("top@faces@>Z", "con_tr@faces@>X", "Plane")
311+
.constrain("bottom@faces@<Z", "con_br@faces@>X", "Plane")
312+
.constrain("left@faces@>Z", "con_tr?Z", "Axis")
313+
.constrain("left@faces@<Z", "con_br?Z", "Axis")
314+
# right profile
315+
.constrain("right@faces@>Z", "con_tr@faces@>Z", "Plane")
316+
.constrain("right@faces@<X", "left@faces@<X", "Axis")
317+
# panel
318+
.constrain("left@faces@>X[-4]", "panel@faces@<X", "Plane")
319+
.constrain("left@faces@>Z", "panel@faces@>Z", "Axis")
320+
# handle
321+
.constrain("panel?hole1", "handle?mate1", "Plane")
322+
.constrain("panel?hole2", "handle?mate2", "Point")
323+
)
324+
325+
# solve
326+
door.solve()
327+
328+
show_object(door,name='door')
329+
330+
This code generates the following assembly.
331+
332+
.. image:: _static/door_assy.png
333+
334+
335+
Data export
336+
===========
337+
338+
The resulting assembly can be exported as a STEP file or in a internal OCCT XML format.
339+
340+
341+
STEP can be loaded in all CAD tool, e.g. in FreeCAD and the XML be used in other applications using OCCT.
342+
343+
.. code-block:: python
344+
:linenos:
345+
346+
door.save('door.step')
347+
door.save('door.xml')
348+
349+
In the case of STEP colors are preserved but not transparency.
350+
351+
.. image:: _static/door_assy_freecad.png

doc/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Table Of Contents
3838
quickstart.rst
3939
designprinciples.rst
4040
primer.rst
41+
assy.rst
4142
fileformat.rst
4243
examples.rst
4344
apireference.rst

0 commit comments

Comments
 (0)