Skip to content

Commit e990a41

Browse files
authored
Merge pull request #20 from fedorkotov/apply_to_faces
applyToEachFace(..) - simplified each(..) for faces
2 parents 3b4a0f1 + 86b301f commit e990a41

File tree

11 files changed

+708
-10
lines changed

11 files changed

+708
-10
lines changed
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
# Apply To Each Face plugin
2+
3+
This plugin simplifies using
4+
`Workplane.each(..)` on faces.
5+
To use each you have to select workplane coordinate
6+
system for each face before building your geometry.
7+
`Workplane.applyToEachFace()` function provided by
8+
this plugin separates tasks of choosing face coordinate
9+
system and actually building new geometry and provides
10+
a few built in ways of choosing coordinate system that
11+
are good enough in many cases.
12+
13+
14+
## Installation
15+
16+
```bash
17+
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=apply_to_each_face&subdirectory=plugins/apply_to_each_face"
18+
```
19+
You can also clone the repository of the plugin and
20+
run in the repository the following command:
21+
```bash
22+
python setup.py install
23+
```
24+
25+
## Dependencies
26+
27+
This plugin has no dependencies other than the cadquery library.
28+
29+
## Usage
30+
31+
To use this plugin after it has been installed,
32+
just import it and use `applyToEachFace(..)`
33+
method of `Workplane`. To use this plugin in `CQ-editor`
34+
you have to
35+
- either (preferably) untick `Edit -> Preferences -> Debugger -> Reload CQ`
36+
option
37+
- or reload `apply_to_each_face` module on each run
38+
like so
39+
```python
40+
if "apply_to_each_face" in sys.modules:
41+
del sys.modules["apply_to_each_face"]
42+
```
43+
44+
If none of the above is done second and subsequent runs
45+
of a script using `apply_to_each_face` in CQ-Editor will fail.
46+
47+
`applyToEachFace(..)` method has two arguments both of which are callbacks.
48+
49+
1. `f_workplane_selector(face)` callback accepts a face and returns
50+
CadQuery Workplane instance that is passed to the next callback.
51+
Two variants of this callback are provided both of which
52+
choose workplane center of origin at face center (`Face.Center()`) and
53+
face normal at face center as Z axis. They have different
54+
methods of X axis selection
55+
1. `XAxisInPlane` (recommended in most cases) - a callable that chooses
56+
x axis that is simultaneously perpendicular to face normal at center
57+
and belongs to one of user-provided planes (specified by their
58+
normal vectors). Plane normal vectors are checked in the order
59+
they are provided and the first one that is not collinear with
60+
face normal is used.
61+
The plugin provides the following plane lists
62+
- `WORLD_AXIS_PLANES_XY_ZX_YZ`
63+
- `WORLD_AXIS_PLANES_XY_YZ_ZX`
64+
- `WORLD_AXIS_PLANES_YZ_XY_ZX`
65+
- `WORLD_AXIS_PLANES_YZ_ZX_XY`
66+
- `WORLD_AXIS_PLANES_ZX_XY_YZ`
67+
- `WORLD_AXIS_PLANES_ZX_YZ_XY`
68+
2. `XAxisClosestTo` - a callable that chooses x axis
69+
perpendicular to face normal at face center as
70+
close as possible to one of user-specified unit
71+
vectors (usually world coordinate system axis unit
72+
vectors).
73+
Vectors are checked in the order they are provided
74+
and the first one that is not collinear with face
75+
normal is used.
76+
The plugin provides the following vector lists
77+
- `WORLD_AXIS_UNIT_VECTORS_XYZ`
78+
- `WORLD_AXIS_UNIT_VECTORS_XZY`
79+
- `WORLD_AXIS_UNIT_VECTORS_YXZ`
80+
- `WORLD_AXIS_UNIT_VECTORS_YZX`
81+
- `WORLD_AXIS_UNIT_VECTORS_ZXY`
82+
- `WORLD_AXIS_UNIT_VECTORS_ZYX`
83+
2. `f_draw(wp_face, face)` callback creates geometry at each face using
84+
`face` itself and workplane `wp_face` provided by `f_workplane_selector`
85+
86+
User-provided vectors for both `XAxisInPlane` and `XAxisClosestTo` do not
87+
have to be linearly independent but their span (linear hull) should be all
88+
3D vector space for these face coordinate system selectors to work on
89+
arbitrary faces. In some cases this requirement can be relaxed.
90+
91+
## Examples
92+
93+
### Example 1
94+
```python
95+
import sys
96+
import cadquery as cq
97+
98+
from apply_to_each_face import (
99+
XAxisInPlane,
100+
WORLD_AXIS_PLANES_XY_ZX_YZ,
101+
)
102+
103+
104+
def main_body():
105+
return (
106+
cq.Workplane("XY")
107+
.box(10.0, 10.0, 10.0, centered=(True, True, True))
108+
.union(
109+
cq.Workplane("XY")
110+
.move(15, 0)
111+
.box(
112+
10.0,
113+
10.0,
114+
10.0,
115+
centered=(True, True, True),
116+
)
117+
)
118+
)
119+
120+
121+
result = main_body().union(
122+
main_body()
123+
.faces()
124+
.applyToEachFace(
125+
XAxisInPlane(WORLD_AXIS_PLANES_XY_ZX_YZ),
126+
lambda wp, face: wp.circle(4).extrude(1),
127+
)
128+
)
129+
```
130+
![example 1](images/ex1.png "Example 1")
131+
132+
133+
### Example 2
134+
```python
135+
import sys
136+
import cadquery as cq
137+
138+
from apply_to_each_face import (
139+
XAxisInPlane,
140+
WORLD_AXIS_PLANES_XY_YZ_ZX,
141+
)
142+
143+
144+
def main_body():
145+
return cq.Workplane("XY").polygon(5, 10.0).extrude(5)
146+
147+
148+
result = main_body().union(
149+
main_body()
150+
.faces("#Z")
151+
.applyToEachFace(
152+
XAxisInPlane(WORLD_AXIS_PLANES_XY_YZ_ZX),
153+
lambda wp, face: wp.rect(1, 2).extrude(3),
154+
)
155+
)
156+
```
157+
![example 2](images/ex2.png "Example 2")
158+
159+
### Example 3
160+
161+
```python
162+
import sys
163+
import cadquery as cq
164+
165+
from apply_to_each_face import (
166+
XAxisInPlane,
167+
WORLD_AXIS_PLANES_XY_ZX_YZ,
168+
)
169+
170+
171+
def main_body():
172+
return (
173+
cq.Workplane("XY")
174+
.polygon(6, 10.0)
175+
.extrude(3, taper=45)
176+
)
177+
178+
179+
result = (
180+
main_body()
181+
.faces("<Z")
182+
.shell(-0.5)
183+
.cut(
184+
main_body()
185+
.faces("not <Z")
186+
.applyToEachFace(
187+
XAxisInPlane(WORLD_AXIS_PLANES_XY_ZX_YZ),
188+
lambda wp, face: wp.add(face)
189+
.wires()
190+
.toPending()
191+
.offset2D(-0.8)
192+
.extrude(-2),
193+
)
194+
)
195+
)
196+
```
197+
![example 3](images/ex3.png "Example 3")
198+
199+
### Example 4
200+
201+
```python
202+
import sys
203+
import cadquery as cq
204+
205+
from apply_to_each_face import (
206+
XAxisInPlane,
207+
WORLD_AXIS_PLANES_XY_ZX_YZ,
208+
XAxisClosestTo,
209+
WORLD_AXIS_UNIT_VECTORS_XYZ,
210+
)
211+
212+
213+
def main_body():
214+
return (
215+
cq.Workplane("XY")
216+
.polygon(6, 10.0)
217+
.extrude(3, taper=45)
218+
)
219+
220+
221+
result_x_axis_in_plane = main_body().union(
222+
main_body()
223+
.faces("#Z")
224+
.applyToEachFace(
225+
XAxisInPlane(WORLD_AXIS_PLANES_XY_ZX_YZ),
226+
lambda wp, face: wp.rect(2, 1).extrude(2),
227+
)
228+
)
229+
230+
result_x_axis_closest_to = (
231+
main_body()
232+
.union(
233+
main_body()
234+
.faces("#Z")
235+
.applyToEachFace(
236+
XAxisClosestTo(WORLD_AXIS_UNIT_VECTORS_XYZ),
237+
lambda wp, face: wp.rect(2, 1).extrude(2),
238+
)
239+
)
240+
.translate(cq.Vector(8, 8, 0))
241+
)
242+
```
243+
244+
![example 4](images/ex4.png "Example 4")
245+
246+
### Example 5
247+
248+
```python
249+
import sys
250+
import cadquery as cq
251+
252+
from apply_to_each_face import (
253+
XAxisInPlane,
254+
WORLD_AXIS_PLANES_XY_ZX_YZ,
255+
XAxisClosestTo,
256+
WORLD_AXIS_UNIT_VECTORS_ZXY,
257+
)
258+
259+
260+
def main_body():
261+
return (
262+
cq.Workplane("XY")
263+
.rect(10.0, 10.0)
264+
.extrude(5, both=True)
265+
)
266+
267+
268+
result = (
269+
main_body()
270+
.union(
271+
main_body()
272+
.faces()
273+
.applyToEachFace(
274+
XAxisClosestTo(WORLD_AXIS_UNIT_VECTORS_ZXY),
275+
lambda wp, face: wp.add(face)
276+
.wires()
277+
.toPending()
278+
.twistExtrude(10, 45),
279+
)
280+
)
281+
.edges()
282+
.fillet(3)
283+
)
284+
```
285+
286+
![example 5](images/ex5.png "Example 5")
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from .apply_to_each_face import (
2+
applyToEachFace,
3+
XAxisInPlane,
4+
XAxisClosestTo,
5+
WORLD_AXIS_UNIT_VECTORS_XYZ,
6+
WORLD_AXIS_UNIT_VECTORS_XZY,
7+
WORLD_AXIS_UNIT_VECTORS_YXZ,
8+
WORLD_AXIS_UNIT_VECTORS_YZX,
9+
WORLD_AXIS_UNIT_VECTORS_ZXY,
10+
WORLD_AXIS_UNIT_VECTORS_ZYX,
11+
WORLD_AXIS_PLANES_XY_ZX_YZ,
12+
WORLD_AXIS_PLANES_XY_YZ_ZX,
13+
WORLD_AXIS_PLANES_YZ_XY_ZX,
14+
WORLD_AXIS_PLANES_YZ_ZX_XY,
15+
WORLD_AXIS_PLANES_ZX_XY_YZ,
16+
WORLD_AXIS_PLANES_ZX_YZ_XY,
17+
)

0 commit comments

Comments
 (0)