Skip to content

Commit ad5bdec

Browse files
authored
Merge pull request #1018 from lorenzncode/sketch-distribute
Sketch distribute fix (#1017)
2 parents ee90f2c + 7ff8e61 commit ad5bdec

File tree

3 files changed

+152
-60
lines changed

3 files changed

+152
-60
lines changed

cadquery/sketch.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
Modes = Literal["a", "s", "i", "c"] # add, subtract, intersect, construct
3939
Point = Union[Vector, Tuple[Real, Real]]
40+
TOL = 1e-6
4041

4142
T = TypeVar("T", bound="Sketch")
4243
SketchVal = Union[Shape, Location]
@@ -346,7 +347,7 @@ def parray(self: T, r: Real, a1: Real, da: Real, n: int, rotate: bool = True) ->
346347

347348
locs = []
348349

349-
if abs(remainder(da, 360)) < 1e-6:
350+
if abs(remainder(da, 360)) < TOL:
350351
angle = da / n
351352
else:
352353
angle = da / (n - 1) if n > 1 else a1
@@ -386,16 +387,36 @@ def distribute(
386387
Distribute locations along selected edges or wires.
387388
"""
388389

390+
if n < 1:
391+
raise ValueError(f"At least 1 element required, requested {n}")
392+
389393
if not self._selection:
390394
raise ValueError("Nothing selected to distribute over")
391395

392-
params = [start + i * (stop - start) / n for i in range(n + 1)]
396+
if 1 - abs(stop - start) < TOL:
397+
trimmed = False
398+
else:
399+
trimmed = True
400+
401+
# closed edge or wire parameters
402+
params_closed = [start + i * (stop - start) / n for i in range(n)]
403+
404+
# open or trimmed edge or wire parameters
405+
params_open = [
406+
start + i * (stop - start) / (n - 1) if n - 1 > 0 else start
407+
for i in range(n)
408+
]
393409

394410
locs = []
395411
for el in self._selection:
396412
if isinstance(el, (Wire, Edge)):
413+
if el.IsClosed() and not trimmed:
414+
params = params_closed
415+
else:
416+
params = params_open
417+
397418
if rotate:
398-
locs.extend(el.locations(params, planar=True))
419+
locs.extend(el.locations(params, planar=True,))
399420
else:
400421
locs.extend(Location(v) for v in el.positions(params))
401422
else:

environment.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ channels:
66
dependencies:
77
- python>=3.6
88
- ipython
9-
- ocp=7.5.1
9+
- ocp=7.5.3
1010
- pyparsing>=2.1.9
1111
- sphinx=4.4.0
1212
- sphinx_rtd_theme
1313
- black=19.10b0
14+
- click=8.0.4
1415
- mypy
1516
- codecov
1617
- pytest

tests/test_sketch.py

Lines changed: 126 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -83,113 +83,183 @@ def test_modes():
8383

8484
def test_distribute():
8585

86-
s1 = Sketch().rarray(2, 2, 3, 3).rect(1, 1)
87-
88-
assert s1._faces.Area() == approx(9)
89-
assert len(s1._faces.Faces()) == 9
90-
91-
s2 = Sketch().parray(2, 0, 90, 3).rect(1, 1)
92-
93-
assert s2._faces.Area() == approx(3)
94-
assert len(s2._faces.Faces()) == 3
86+
with raises(ValueError):
87+
Sketch().rect(2, 2).faces().distribute(5)
9588

9689
with raises(ValueError):
97-
Sketch().rarray(2, 2, 3, 0).rect(1, 1)
90+
Sketch().rect(2, 2).distribute(5)
9891

9992
with raises(ValueError):
100-
Sketch().parray(2, 0, 90, 0).rect(1, 1)
93+
Sketch().circle(1).wires().distribute(0, 0, 1)
10194

102-
s3 = Sketch().circle(4, mode="c", tag="c").edges(tag="c").distribute(3).rect(1, 1)
95+
s1 = Sketch().circle(4, mode="c", tag="c").edges(tag="c").distribute(3)
10396

104-
assert s2._faces.Area() == approx(3)
105-
assert len(s3._faces.Faces()) == 3
106-
assert len(s3.reset().vertices("<X")._selection) == 2
97+
assert len(s1._selection) == approx(3)
98+
99+
s1.rect(1, 1)
107100

108-
for f in s3._faces.Faces():
101+
assert s1._faces.Area() == approx(3)
102+
assert len(s1._faces.Faces()) == 3
103+
assert len(s1.reset().vertices("<X")._selection) == 2
104+
105+
for f in s1._faces.Faces():
109106
assert f.Center().Length == approx(4)
110107

111-
s4 = (
108+
s2 = (
112109
Sketch()
113110
.circle(4, mode="c", tag="c")
114111
.edges(tag="c")
115112
.distribute(3, rotate=False)
116113
.rect(1, 1)
117114
)
118115

119-
assert s4._faces.Area() == approx(3)
120-
assert len(s4._faces.Faces()) == 3
121-
assert len(s4.reset().vertices("<X")._selection) == 4
116+
assert s2._faces.Area() == approx(3)
117+
assert len(s2._faces.Faces()) == 3
118+
assert len(s2.reset().vertices("<X")._selection) == 4
122119

123-
for f in s4._faces.Faces():
120+
for f in s2._faces.Faces():
124121
assert f.Center().Length == approx(4)
125122

126-
with raises(ValueError):
127-
Sketch().rect(2, 2).faces().distribute(5)
123+
s3 = (
124+
Sketch().circle(4, mode="c", tag="c").edges(tag="c").distribute(3, 0.625, 0.875)
125+
)
126+
127+
assert len(s3._selection) == approx(3)
128+
129+
s3.rect(1, 0.5).reset().vertices("<X")
130+
131+
assert s3._selection[0].toTuple() == approx(
132+
(-3.358757210636101, -3.005203820042827, 0.0)
133+
)
134+
135+
s3.reset().vertices(">X")
136+
137+
assert s3._selection[0].toTuple() == approx(
138+
(3.358757210636101, -3.005203820042827, 0.0)
139+
)
140+
141+
s4 = Sketch().arc((0, 0), 4, 180, 180).edges().distribute(3, 0.25, 0.75)
142+
143+
assert len(s4._selection) == approx(3)
144+
145+
s4.rect(1, 0.5).reset().faces("<X").vertices("<X")
146+
147+
assert s4._selection[0].toTuple() == approx(
148+
(-3.358757210636101, -3.005203820042827, 0.0)
149+
)
150+
151+
s4.reset().faces(">X").vertices(">X")
152+
153+
assert s4._selection[0].toTuple() == approx(
154+
(3.358757210636101, -3.005203820042827, 0.0)
155+
)
156+
157+
s5 = (
158+
Sketch()
159+
.arc((0, 2), 4, 0, 90)
160+
.arc((0, -2), 4, 0, -90)
161+
.edges()
162+
.distribute(4, 0, 1)
163+
.circle(0.5)
164+
)
165+
166+
assert len(s5._selection) == approx(8)
167+
168+
s5.reset().faces(">X").faces(">Y")
169+
170+
assert s5._selection[0].Center().toTuple() == approx((4.0, 2.0, 0.0))
171+
172+
s5.reset().faces(">X").faces("<Y")
173+
174+
assert s5._selection[0].Center().toTuple() == approx((4.0, -2.0, 0.0))
175+
176+
s5.reset().faces(">Y")
177+
178+
assert s5._selection[0].Center().toTuple() == approx((0.0, 6.0, 0.0))
179+
180+
181+
def test_rarray():
128182

129183
with raises(ValueError):
130-
Sketch().rect(2, 2).distribute(5)
184+
Sketch().rarray(2, 2, 3, 0).rect(1, 1)
131185

132-
s5 = Sketch().push([(0, 0), (1, 1)]).rarray(2, 2, 3, 3).rect(0.5, 0.5)
186+
s1 = Sketch().rarray(2, 2, 3, 3).rect(1, 1)
133187

134-
assert s5._faces.Area() == approx(18 * 0.25)
135-
assert len(s5._faces.Faces()) == 18
136-
assert s5.reset().vertices(">(1,1,0)")._selection[0].toTuple() == approx(
188+
assert s1._faces.Area() == approx(9)
189+
assert len(s1._faces.Faces()) == 9
190+
191+
s2 = Sketch().push([(0, 0), (1, 1)]).rarray(2, 2, 3, 3).rect(0.5, 0.5)
192+
193+
assert s2._faces.Area() == approx(18 * 0.25)
194+
assert len(s2._faces.Faces()) == 18
195+
assert s2.reset().vertices(">(1,1,0)")._selection[0].toTuple() == approx(
137196
(3.25, 3.25, 0)
138197
)
139198

140-
s6 = Sketch().push([(0, 0), (1, 1)]).parray(2, 0, 90, 3).rect(0.5, 0.5)
141199

142-
assert s6._faces.Area() == approx(6 * 0.25)
143-
assert len(s6._faces.Faces()) == 6
200+
def test_parray():
144201

145-
s7 = Sketch().parray(2, 0, 90, 3, False).rect(0.5, 0.5).reset().vertices(">(1,1,0)")
202+
with raises(ValueError):
203+
Sketch().parray(2, 0, 90, 0).rect(1, 1)
146204

147-
assert len(s7._selection) == 1
148-
assert s7._selection[0].toTuple() == approx(
205+
s1 = Sketch().parray(2, 0, 90, 3).rect(1, 1)
206+
207+
assert s1._faces.Area() == approx(3)
208+
assert len(s1._faces.Faces()) == 3
209+
210+
s2 = Sketch().push([(0, 0), (1, 1)]).parray(2, 0, 90, 3).rect(0.5, 0.5)
211+
212+
assert s2._faces.Area() == approx(6 * 0.25)
213+
assert len(s2._faces.Faces()) == 6
214+
215+
s3 = Sketch().parray(2, 0, 90, 3, False).rect(0.5, 0.5).reset().vertices(">(1,1,0)")
216+
217+
assert len(s3._selection) == 1
218+
assert s3._selection[0].toTuple() == approx(
149219
(1.6642135623730951, 1.664213562373095, 0.0)
150220
)
151221

152-
s8 = Sketch().push([(0, 0), (0, 1)]).parray(2, 0, 90, 3).rect(0.5, 0.5)
153-
s8.reset().faces(">(0,1,0)")
222+
s4 = Sketch().push([(0, 0), (0, 1)]).parray(2, 0, 90, 3).rect(0.5, 0.5)
223+
s4.reset().faces(">(0,1,0)")
154224

155-
assert s8._selection[0].Center().Length == approx(3)
225+
assert s4._selection[0].Center().Length == approx(3)
156226

157-
s9 = Sketch().push([(0, 1)], tag="loc")
227+
s5 = Sketch().push([(0, 1)], tag="loc")
158228

159-
assert len(s9._tags["loc"]) == 1
229+
assert len(s5._tags["loc"]) == 1
160230

161-
s10 = Sketch().push([(-4, 1), (0, 0), (4, -1)]).parray(2, 10, 50, 3).rect(1.0, 0.5)
162-
s10.reset().vertices(">(-1,0,0)")
231+
s6 = Sketch().push([(-4, 1), (0, 0), (4, -1)]).parray(2, 10, 50, 3).rect(1.0, 0.5)
232+
s6.reset().vertices(">(-1,0,0)")
163233

164-
assert s10._selection[0].toTuple() == approx(
234+
assert s6._selection[0].toTuple() == approx(
165235
(-3.46650635094611, 2.424038105676658, 0.0)
166236
)
167237

168-
s10.reset().vertices(">(1,0,0)")
238+
s6.reset().vertices(">(1,0,0)")
169239

170-
assert s10._selection[0].toTuple() == approx(
240+
assert s6._selection[0].toTuple() == approx(
171241
(6.505431426947252, -0.8120814940857262, 0.0)
172242
)
173243

174-
s11 = Sketch().parray(1, 135, 0, 1).circle(0.1)
175-
s11.reset().faces()
244+
s7 = Sketch().parray(1, 135, 0, 1).circle(0.1)
245+
s7.reset().faces()
176246

177-
assert len(s11._selection) == 1
178-
assert s11._selection[0].Center().toTuple() == approx(
247+
assert len(s7._selection) == 1
248+
assert s7._selection[0].Center().toTuple() == approx(
179249
(-0.7071067811865475, 0.7071067811865476, 0.0)
180250
)
181251

182-
s12 = Sketch().parray(4, 20, 360, 6).rect(1.0, 0.5)
252+
s8 = Sketch().parray(4, 20, 360, 6).rect(1.0, 0.5)
183253

184-
assert len(s12._faces.Faces()) == 6
254+
assert len(s8._faces.Faces()) == 6
185255

186-
s12.reset().vertices(">(0,-1,0)")
256+
s8.reset().vertices(">(0,-1,0)")
187257

188-
assert s12._selection[0].toTuple() == approx(
258+
assert s8._selection[0].toTuple() == approx(
189259
(-0.5352148612481344, -4.475046932971669, 0.0)
190260
)
191261

192-
s13 = (
262+
s9 = (
193263
Sketch()
194264
.push([(-4, 1)])
195265
.circle(0.1)
@@ -199,11 +269,11 @@ def test_distribute():
199269
.rect(1.0, 0.5, 40, "a", "rects")
200270
)
201271

202-
assert len(s13._faces.Faces()) == 4
272+
assert len(s9._faces.Faces()) == 4
203273

204-
s13.reset().vertices(">(-1,0,0)", tag="rects")
274+
s9.reset().vertices(">(-1,0,0)", tag="rects")
205275

206-
assert s13._selection[0].toTuple() == approx(
276+
assert s9._selection[0].toTuple() == approx(
207277
(-3.3330260270865173, 3.1810426396582487, 0.0)
208278
)
209279

0 commit comments

Comments
 (0)