yapCAD 0.3.0__py2.py3-none-any.whl → 0.5.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- yapcad/combine.py +82 -376
- yapcad/drawable.py +44 -3
- yapcad/ezdxf_drawable.py +1 -1
- yapcad/geom.py +204 -264
- yapcad/geom3d.py +975 -55
- yapcad/geom3d_util.py +541 -0
- yapcad/geom_util.py +817 -0
- yapcad/geometry.py +441 -49
- yapcad/geometry_checks.py +112 -0
- yapcad/geometry_utils.py +115 -0
- yapcad/io/__init__.py +5 -0
- yapcad/io/stl.py +83 -0
- yapcad/mesh.py +46 -0
- yapcad/metadata.py +109 -0
- yapcad/octtree.py +627 -0
- yapcad/poly.py +153 -299
- yapcad/pyglet_drawable.py +597 -61
- yapcad/triangulator.py +103 -0
- yapcad/xform.py +0 -1
- {yapcad-0.3.0.dist-info → yapcad-0.5.0.dist-info}/METADATA +94 -38
- yapcad-0.5.0.dist-info/RECORD +27 -0
- yapcad-0.3.0.dist-info/RECORD +0 -17
- {yapcad-0.3.0.dist-info → yapcad-0.5.0.dist-info}/WHEEL +0 -0
- {yapcad-0.3.0.dist-info → yapcad-0.5.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {yapcad-0.3.0.dist-info → yapcad-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {yapcad-0.3.0.dist-info → yapcad-0.5.0.dist-info}/licenses/LICENSE.txt +0 -0
- {yapcad-0.3.0.dist-info → yapcad-0.5.0.dist-info}/top_level.txt +0 -0
yapcad/geom.py
CHANGED
@@ -73,21 +73,30 @@ coordinates, and was popular with computer graphicists in the
|
|
73
73
|
In this interpretation, the w coordinate is a normalization
|
74
74
|
coordinate, and is either 1.0 or all coordinates are assumed to be
|
75
75
|
scaled by 1/w. The inclusion of the w coordinate allows the general
|
76
|
-
use of transformation matrices for affine transforms.
|
76
|
+
use of transformation matrices for affine transforms.
|
77
|
+
|
78
|
+
The exception to the above statement is direction vectors, such as the
|
79
|
+
vector ``[1,0,0,0]`` that represents the direction of the x axis.
|
80
|
+
Direction vectors live in the ``w=0.0`` plane and are subject to
|
81
|
+
rotation and scaling, but not translation.
|
77
82
|
|
78
83
|
There is a yapcad.geom convenience function, ``vect()``, that will make
|
79
84
|
a vector out of just about any plausible set of arguments. Unspecified
|
80
85
|
w values are set to 1, and unspecified z values are set to 0.
|
81
86
|
|
87
|
+
=========================================
|
88
|
+
yapcad.geom simple (non-compound) figures
|
89
|
+
=========================================
|
90
|
+
|
82
91
|
points
|
83
92
|
======
|
84
93
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
expect w=1 unless certain types of
|
89
|
-
applied, most **yapCAD** geometry
|
90
|
-
coordinate, and operate as though w=1.
|
94
|
+
Points are the simplest of the ``yapcad.geom`` figures. Ordinary
|
95
|
+
**yapCAD** geometry is assumed to lie in the w=1 hyperplane, and when
|
96
|
+
a vector lies in that plane it is usually interpreted as transformable
|
97
|
+
coordinate, or point. Because we expect w=1 unless certain types of
|
98
|
+
affine transforms have been applied, most **yapCAD** geometry
|
99
|
+
operations do not look at the w coordinate, and operate as though w=1.
|
91
100
|
|
92
101
|
Any vector that lies in the w>0 half-space is a valid coordinate, or
|
93
102
|
point. And in **yapCAD**, ``[x,y,z,w]`` corresponds to the same 3D
|
@@ -127,11 +136,11 @@ operations. (see below)
|
|
127
136
|
----------------
|
128
137
|
|
129
138
|
One special type of ``yapcad.geom`` line is a 3D bounding box. A
|
130
|
-
bounding box is a line that spans the "lower bottom
|
131
|
-
top
|
139
|
+
bounding box is a line that spans the "left lower bottom" to "right
|
140
|
+
upper top" of a figure, *e.g.* ``bbx =
|
132
141
|
[[xmin,ymin,zmin,1],[xmax,ymax,zmax,1]]``. Bounding boxes are used
|
133
|
-
internally to speed up inside testing and for a variety of
|
134
|
-
purposes.
|
142
|
+
internally to speed up inside/outside testing and for a variety of
|
143
|
+
other purposes.
|
135
144
|
|
136
145
|
arcs
|
137
146
|
====
|
@@ -189,6 +198,10 @@ sampling order will have the beneficial property of having the same
|
|
189
198
|
Arcs support all standard ``yapcad.geom`` computational geometry
|
190
199
|
operations. (see below)
|
191
200
|
|
201
|
+
=========================================
|
202
|
+
yapcad.geom compound figures
|
203
|
+
=========================================
|
204
|
+
|
192
205
|
polylines/polygons
|
193
206
|
==================
|
194
207
|
|
@@ -241,9 +254,9 @@ lists composed of closed elements.
|
|
241
254
|
Geometry lists are paramaterized a bit like polylines, except that
|
242
255
|
there is no guarantee of continuity.
|
243
256
|
|
244
|
-
|
245
|
-
COMPUTATIONAL GEOMETRY
|
246
|
-
|
257
|
+
=================================
|
258
|
+
COMPUTATIONAL GEOMETRY OPERATIONS
|
259
|
+
=================================
|
247
260
|
|
248
261
|
The primary purpose of the ``yapcad.geom`` module is to support
|
249
262
|
computational geometry operations on two-dimensional figures in
|
@@ -461,6 +474,15 @@ def isvect(x):
|
|
461
474
|
check to see if argument is a proper vector for our purposes
|
462
475
|
"""
|
463
476
|
return isinstance(x,list) and len(x) == 4 and isgoodnum(x[0]) and isgoodnum(x[1]) and isgoodnum(x[2]) and isgoodnum(x[3])
|
477
|
+
|
478
|
+
## check to see if an argument is a proper direction vector, with w=0
|
479
|
+
def isdirect(x):
|
480
|
+
return isinstance(x,list) and len(x) == 4 and isgoodnum(x[0]) and isgoodnum(x[1]) and isgoodnum(x[2]) and close(x[3],0.0)
|
481
|
+
|
482
|
+
## check to see if argument is a list of direction vectors
|
483
|
+
def isdirectlist(xx):
|
484
|
+
return len(list(filter(lambda x: not isdirect(x),xx))) == 0
|
485
|
+
|
464
486
|
|
465
487
|
## R^3 -> R^3 functions: ignore w component
|
466
488
|
## ------------------------------------------------
|
@@ -515,6 +537,12 @@ def scale4(a,c):
|
|
515
537
|
""" 4 vector ``a`` times scalar ``c``"""
|
516
538
|
return [a[0]*c,a[1]*c,a[2]*c,a[3]*c]
|
517
539
|
|
540
|
+
## component-wise 4vect multiplication
|
541
|
+
def mul4(a,b):
|
542
|
+
""" component-wise 3 vector multiplication"""
|
543
|
+
return [a[0]*b[0],a[1]*b[1],a[2]*b[2],a[3]*b[3]]
|
544
|
+
|
545
|
+
|
518
546
|
## Homogenize, or project back to the w=1 plane by scaling all values
|
519
547
|
## by w
|
520
548
|
def homo(a):
|
@@ -548,6 +576,13 @@ def isinsidebbox(bbox,p):
|
|
548
576
|
p[1] >= bbox[0][1] and p[1] <= bbox[1][1] and\
|
549
577
|
p[2] >= bbox[0][2] and p[2] <= bbox[1][2]
|
550
578
|
|
579
|
+
# does point p lie inside 2D bounding box bbox
|
580
|
+
def isinsidebbox2D(bbox,p):
|
581
|
+
""" does point ``p`` lie inside 2D bounding box ``bbox``?"""
|
582
|
+
return ( p[0] >= bbox[0][0] and p[0] <= bbox[1][0] and
|
583
|
+
p[1] >= bbox[0][1] and p[1] <= bbox[1][1] )
|
584
|
+
|
585
|
+
|
551
586
|
# utility function to determine if a list of points lies in the specified
|
552
587
|
# cardinal plane, one of XY, YZ, XZ
|
553
588
|
def isCardinalPlanar(plane="xy",points=[]):
|
@@ -1800,7 +1835,7 @@ def poly(*args):
|
|
1800
1835
|
if ispoly(args[0]):
|
1801
1836
|
return deepcopy(args[0])
|
1802
1837
|
else:
|
1803
|
-
raise
|
1838
|
+
raise ValueError(f'non-poly list passed to poly(): {args}')
|
1804
1839
|
# args is of length 3 or greater. Check to see if args are points
|
1805
1840
|
a = list(args)
|
1806
1841
|
b = list(filter(lambda x: not ispoint(x),a))
|
@@ -1838,7 +1873,7 @@ def polycenter(a):
|
|
1838
1873
|
|
1839
1874
|
## we heart poly bboxes
|
1840
1875
|
def polybbox(a):
|
1841
|
-
"""Compute the bounding box of polyline/polygon ``a``"""
|
1876
|
+
"""Compute the 3D bounding box of polyline/polygon ``a``"""
|
1842
1877
|
if len(a) == 0:
|
1843
1878
|
return False
|
1844
1879
|
elif len(a) == 1:
|
@@ -1846,9 +1881,11 @@ def polybbox(a):
|
|
1846
1881
|
else:
|
1847
1882
|
minx = maxx = a[0][0]
|
1848
1883
|
miny = maxy = a[0][1]
|
1884
|
+
minz = maxz = a[0][2]
|
1849
1885
|
for i in range(1,len(a)):
|
1850
1886
|
x=a[i][0]
|
1851
1887
|
y=a[i][1]
|
1888
|
+
z=a[i][2]
|
1852
1889
|
if x < minx:
|
1853
1890
|
minx =x
|
1854
1891
|
elif x > maxx:
|
@@ -1857,7 +1894,12 @@ def polybbox(a):
|
|
1857
1894
|
miny = y
|
1858
1895
|
elif y > maxy:
|
1859
1896
|
maxy = y
|
1860
|
-
|
1897
|
+
if z < minz:
|
1898
|
+
minz = z
|
1899
|
+
elif z > maxz:
|
1900
|
+
maxz = z
|
1901
|
+
|
1902
|
+
return [ point(minx,miny,minz),point(maxx,maxy,maxz)]
|
1861
1903
|
|
1862
1904
|
## only valid for closed polylines. Count the intersections for a
|
1863
1905
|
## line drawn from point to test to a point outside the bounding
|
@@ -2111,9 +2153,24 @@ def intersectSimplePolyXY(g,a,inside=True,params=False):
|
|
2111
2153
|
|
2112
2154
|
|
2113
2155
|
def polycenter(a):
|
2114
|
-
|
2156
|
+
"""Compute center of poly ``a``. If ``a`` is closed, return
|
2157
|
+
barycentric center of figure. If ``a`` is not closed, return
|
2158
|
+
sampling midpoint.
|
2159
|
+
|
2160
|
+
"""
|
2161
|
+
if ispolygon(a):
|
2162
|
+
avg = a[0]
|
2163
|
+
l = len(a)-1
|
2164
|
+
for i in range(1,l):
|
2165
|
+
avg = add(a[i],avg)
|
2166
|
+
return scale3(avg,1.0/l)
|
2167
|
+
else:
|
2168
|
+
return sample(a,0.5)
|
2115
2169
|
|
2116
2170
|
def polylength(a):
|
2171
|
+
"""
|
2172
|
+
compute length of poly ``a``
|
2173
|
+
"""
|
2117
2174
|
if len(a) < 2:
|
2118
2175
|
return 0.0
|
2119
2176
|
l = 0.0
|
@@ -2127,6 +2184,9 @@ def polylength(a):
|
|
2127
2184
|
## or other geometry lists.
|
2128
2185
|
|
2129
2186
|
def isgeomlist(a):
|
2187
|
+
"""
|
2188
|
+
determine if argument ``a`` is a valid geometry list
|
2189
|
+
"""
|
2130
2190
|
if not isinstance(a,list):
|
2131
2191
|
return False
|
2132
2192
|
b = list(filter(lambda x: not (ispoint(x) or isline(x) \
|
@@ -2135,6 +2195,43 @@ def isgeomlist(a):
|
|
2135
2195
|
return not len(b) > 0
|
2136
2196
|
|
2137
2197
|
|
2198
|
+
def iscontinuousgeomlist(a):
|
2199
|
+
"""
|
2200
|
+
deterine if geometry list ``a`` posesses C0 continuity over sampling
|
2201
|
+
interval [0.0,1.0].
|
2202
|
+
"""
|
2203
|
+
l = len(a)
|
2204
|
+
if l < 2:
|
2205
|
+
return True
|
2206
|
+
for i in range(1,l):
|
2207
|
+
x1 = sample(a[i-1],1.0)
|
2208
|
+
x2 = sample(a[i],0.0)
|
2209
|
+
if not vclose(x1,x2):
|
2210
|
+
#print(f"l: {l}, a: {vstr(a)}")
|
2211
|
+
#print(f"i: {i} dist(x1,x2): {dist(x1,x2)}")
|
2212
|
+
return False
|
2213
|
+
return True
|
2214
|
+
|
2215
|
+
def isclosedgeomlist(a):
|
2216
|
+
"""Determine if geometry list ``a`` is closed, which is to say
|
2217
|
+
determine if the geometry list is continuous and the distance
|
2218
|
+
between ``sample(a,0.0)`` and ``sample(a,1.0)`` is less than
|
2219
|
+
epsilon. *NOTE:* This test will not detemrine if this geometry
|
2220
|
+
list representes a simple closed figure as there could be any
|
2221
|
+
number of self intersections.
|
2222
|
+
|
2223
|
+
*NOTE:* The special case of ``a==[]`` will cause
|
2224
|
+
``isclosedgeomlist(a)`` to return ``True``, so that set operations
|
2225
|
+
such as intersection or difference operations which return an
|
2226
|
+
empty result will still be considered valid closed geometry for
|
2227
|
+
further operations.
|
2228
|
+
|
2229
|
+
"""
|
2230
|
+
return (a == [] or (
|
2231
|
+
iscontinuousgeomlist(a) and
|
2232
|
+
vclose(sample(a,0.0),sample(a,1.0))))
|
2233
|
+
|
2234
|
+
|
2138
2235
|
def __geomlistlength(gl):
|
2139
2236
|
leng=0.0
|
2140
2237
|
lengths=[]
|
@@ -2169,6 +2266,46 @@ def samplegeomlist(gl,u):
|
|
2169
2266
|
uu = (dst-leng+lengths[-1])/lengths[-1]
|
2170
2267
|
return sample(gl[-1],uu)
|
2171
2268
|
|
2269
|
+
|
2270
|
+
def unsamplegeomlist(gl,p):
|
2271
|
+
"""
|
2272
|
+
anlogous to the unsampleline() and unsamplearc() functions, given a
|
2273
|
+
point on a poly, return the corresponding sample parameter, or
|
2274
|
+
False if the point is more than epsilon away from any poly line
|
2275
|
+
segment
|
2276
|
+
"""
|
2277
|
+
if not isgeomlist(g):
|
2278
|
+
raise ValueError('non geometry list passed to unsamplegeomlist')
|
2279
|
+
|
2280
|
+
lengths,leng = __geomlistlength(gl)
|
2281
|
+
if len(gl) == 1:
|
2282
|
+
return unsample(gl[0],p)
|
2283
|
+
else:
|
2284
|
+
uu1 = unsample(gl[0],p)
|
2285
|
+
if (not isinstance(uu1,bool)
|
2286
|
+
and uu1 < 1.0 and uu1 >= 0.0):
|
2287
|
+
return uu1*lengths[0]/leng
|
2288
|
+
dst = lengths[0]
|
2289
|
+
if len(gl) > 2:
|
2290
|
+
for i in range(1,len(gl)-1):
|
2291
|
+
if lengths[i] <= epsilon:
|
2292
|
+
continue
|
2293
|
+
uu = unsample(gl[i],p)
|
2294
|
+
if (not isinstance(uu,bool)
|
2295
|
+
and uu >= 0.0 and uu < 1.0):
|
2296
|
+
return (uu*lengths[i]+dst)/length
|
2297
|
+
else:
|
2298
|
+
dst = dst+lengths[i]
|
2299
|
+
uu2 = unsample(gl[-1],p)
|
2300
|
+
|
2301
|
+
if (not isinstance(uu2,bool) and
|
2302
|
+
uu2 >= 0.0 and uu2 <= 1.0):
|
2303
|
+
return (uu2*lengths[-1]+dst)/length
|
2304
|
+
else:
|
2305
|
+
return False
|
2306
|
+
|
2307
|
+
|
2308
|
+
|
2172
2309
|
## function to reverse a geometry list (presumably contiguous) for
|
2173
2310
|
## sampling purposes
|
2174
2311
|
def reverseGeomList(gl):
|
@@ -2230,6 +2367,8 @@ def segmentgeomlist(gl,u1,u2,closed=False,reverse=False):
|
|
2230
2367
|
|
2231
2368
|
for i in range(len(gl)):
|
2232
2369
|
l=lengths[i]
|
2370
|
+
if l < epsilon:
|
2371
|
+
continue
|
2233
2372
|
if dst1 <= d+l:
|
2234
2373
|
if not STARTED:
|
2235
2374
|
uu = 1.0 - (d+l-dst1)/l
|
@@ -2315,7 +2454,7 @@ def isgeomlistXYPlanar(gl):
|
|
2315
2454
|
return False
|
2316
2455
|
return isXYPlanar(pp)
|
2317
2456
|
|
2318
|
-
## compute the intersection between geometric element g, and geometry
|
2457
|
+
## compute the intersection between coplanar geometric element g, and geometry
|
2319
2458
|
## list gl. NOTE: this function does not impose continuity
|
2320
2459
|
## requirements on the geometry list, and point elements are ignored
|
2321
2460
|
## for intersection testing.
|
@@ -2541,7 +2680,9 @@ def center(x):
|
|
2541
2680
|
pl = []
|
2542
2681
|
for g in x:
|
2543
2682
|
pl.append(center(g))
|
2544
|
-
|
2683
|
+
if isclosedgeomlist(x):
|
2684
|
+
pl.append(pl[0])
|
2685
|
+
return polycenter(pl)
|
2545
2686
|
else:
|
2546
2687
|
raise ValueError("inappropriate type for center(): ",format(x))
|
2547
2688
|
|
@@ -2602,7 +2743,7 @@ def unsample(x,p):
|
|
2602
2743
|
elif ispoly(x):
|
2603
2744
|
return unsamplepoly(x,p)
|
2604
2745
|
elif isgeomlist(x):
|
2605
|
-
|
2746
|
+
return unsamplegeomlist(x,p)
|
2606
2747
|
else:
|
2607
2748
|
raise ValueError("inappropriate type for unasample(): "+str(x))
|
2608
2749
|
|
@@ -2624,10 +2765,13 @@ def segment(x,u1,u2):
|
|
2624
2765
|
else:
|
2625
2766
|
raise ValueError("inappropriate figure type for segment(): "+str(x))
|
2626
2767
|
|
2627
|
-
|
2628
|
-
|
2629
|
-
|
2630
2768
|
def isinsideXY(x,p):
|
2769
|
+
"""for an XY-coplanar point and figure, determine if the point lies
|
2770
|
+
inside the figure. In the case of non-closed figures, such as
|
2771
|
+
lines, determine if the point lies within epsilon of one of the
|
2772
|
+
lines of the figure.
|
2773
|
+
|
2774
|
+
"""
|
2631
2775
|
if ispoint(x):
|
2632
2776
|
return isinsidepointXY(x,p)
|
2633
2777
|
elif isline(x):
|
@@ -2642,6 +2786,7 @@ def isinsideXY(x,p):
|
|
2642
2786
|
raise ValueError("bad thing passed to inside: {}".format(x))
|
2643
2787
|
|
2644
2788
|
def translate(x,delta):
|
2789
|
+
""" return a translated version of the figure"""
|
2645
2790
|
if ispoint(x):
|
2646
2791
|
return add(x,delta)
|
2647
2792
|
elif isline(x):
|
@@ -2664,6 +2809,7 @@ def translate(x,delta):
|
|
2664
2809
|
raise ValueError("don't know how to translate {}".format(x))
|
2665
2810
|
|
2666
2811
|
def scale(x,sx=1.0,sy=False,sz=False,cent=point(0,0),mat=False):
|
2812
|
+
""" return a scaled version of the figure"""
|
2667
2813
|
if sy == False and sz == False:
|
2668
2814
|
sy = sz = sx
|
2669
2815
|
if vclose(point(sx,sy,sz),point(1.0,1.0,1.0)):
|
@@ -2706,6 +2852,8 @@ def scale(x,sx=1.0,sy=False,sz=False,cent=point(0,0),mat=False):
|
|
2706
2852
|
|
2707
2853
|
|
2708
2854
|
def transform(x,m):
|
2855
|
+
""" return a transformed version of the figure, as specified by the
|
2856
|
+
transformation matrix m"""
|
2709
2857
|
if not isinstance(m,xform.Matrix):
|
2710
2858
|
raise ValueError('bad transformation matrix passed to transform')
|
2711
2859
|
if ispoint(x):
|
@@ -2737,20 +2885,21 @@ def transform(x,m):
|
|
2737
2885
|
# recursive processing of geometry lists.
|
2738
2886
|
|
2739
2887
|
def rotate(x,ang,cent=point(0,0),axis=point(0,0,1.0),mat=False):
|
2888
|
+
""" return a rotated version of the figure"""
|
2740
2889
|
if close(ang,0.0):
|
2741
2890
|
return deepcopy(x)
|
2742
2891
|
if not mat: # if matrix isn't pre-specified, calculate it
|
2743
2892
|
if vclose(cent,point(0,0,0)):
|
2744
2893
|
mat = xform.Rotation(axis,ang)
|
2745
2894
|
else:
|
2746
|
-
mat = xform.Translation(cent
|
2895
|
+
mat = xform.Translation(cent)
|
2747
2896
|
mat = mat.mul(xform.Rotation(axis,ang))
|
2748
|
-
mat = mat.mul(xform.Translation(cent))
|
2897
|
+
mat = mat.mul(xform.Translation(cent,inverse=True))
|
2749
2898
|
|
2750
2899
|
# arcs are wierd, since we will have to deal with a non-trivial
|
2751
2900
|
# change of basis function to handle the interpretation of "start"
|
2752
2901
|
# and "end" if the axis of rotation isn't the z axis.
|
2753
|
-
if ispoint(x):
|
2902
|
+
if ispoint(x) or isvect(x):
|
2754
2903
|
return mat.mul(x)
|
2755
2904
|
elif isline(x):
|
2756
2905
|
return line(mat.mul(x[0]),
|
@@ -2765,7 +2914,7 @@ def rotate(x,ang,cent=point(0,0),axis=point(0,0,1.0),mat=False):
|
|
2765
2914
|
c[1][2] += ang
|
2766
2915
|
return c
|
2767
2916
|
|
2768
|
-
elif isgeomlist(x):
|
2917
|
+
elif isgeomlist(x) or isdirectlist(x):
|
2769
2918
|
gl = []
|
2770
2919
|
for g in x:
|
2771
2920
|
gl.append(rotate(g,ang,cent,axis,mat))
|
@@ -2778,7 +2927,16 @@ def rotate(x,ang,cent=point(0,0),axis=point(0,0,1.0),mat=False):
|
|
2778
2927
|
## generalized geometry mirror function
|
2779
2928
|
|
2780
2929
|
def mirror(x,plane):
|
2781
|
-
|
2930
|
+
"""
|
2931
|
+
return a mirrored version of a figure. Currently, the following
|
2932
|
+
values of "plane" are allowed: 'xz', 'yz', xy'. Generalized
|
2933
|
+
arbitrary reflection plane specification will be added in the
|
2934
|
+
future.
|
2935
|
+
|
2936
|
+
NOTE: this operation will reverse the sign of the area of ``x`` if
|
2937
|
+
x is a closed polyline or geometry list
|
2938
|
+
"""
|
2939
|
+
flip=[1,1,1,1]
|
2782
2940
|
if plane == 'xz':
|
2783
2941
|
flip[1]= -1
|
2784
2942
|
elif plane == 'yz':
|
@@ -2788,8 +2946,8 @@ def mirror(x,plane):
|
|
2788
2946
|
else:
|
2789
2947
|
raise ValueError('bad reflection plane passed to mirror')
|
2790
2948
|
|
2791
|
-
if
|
2792
|
-
return
|
2949
|
+
if isvect(x):
|
2950
|
+
return mul4(x,flip)
|
2793
2951
|
elif isarc(x):
|
2794
2952
|
a2=arc(x)
|
2795
2953
|
a2[0] = mul(x[0],flip)
|
@@ -2806,10 +2964,10 @@ def mirror(x,plane):
|
|
2806
2964
|
a2[1][1]=start
|
2807
2965
|
a2[1][2]=end
|
2808
2966
|
return a2
|
2809
|
-
elif ispoly(x):
|
2967
|
+
elif ispoly(x) or isdirectlist(x):
|
2810
2968
|
ply = []
|
2811
2969
|
for p in x:
|
2812
|
-
ply.append(
|
2970
|
+
ply.append(mul4(p,flip))
|
2813
2971
|
return ply
|
2814
2972
|
elif isgeomlist(x):
|
2815
2973
|
r = []
|
@@ -2829,6 +2987,7 @@ def mirror(x,plane):
|
|
2829
2987
|
## if no intersections
|
2830
2988
|
|
2831
2989
|
def _intersectSimpleXY(g1,g2,inside=True,params=False):
|
2990
|
+
"""non-value-safe function that determines the intersection of two XY-coplanar non-compound geometric figures"""
|
2832
2991
|
g1line = True
|
2833
2992
|
g2line = True
|
2834
2993
|
if isarc(g1):
|
@@ -2859,6 +3018,7 @@ def _intersectSimpleXY(g1,g2,inside=True,params=False):
|
|
2859
3018
|
## Value-safe simple wrapper for calculation of intersection of
|
2860
3019
|
## non-compound geometric elements
|
2861
3020
|
def intersectSimpleXY(g1,g2,inside=True,params=False):
|
3021
|
+
"""value-safe function that determines the intersection of two XY-coplanar non-compound geometric figures"""
|
2862
3022
|
if not (isline(g1) or isarc(g1)) \
|
2863
3023
|
or not (isline(g2) or isarc(g2)):
|
2864
3024
|
raise ValueError('bad geometry passed to intersectSimpleXY')
|
@@ -2870,6 +3030,7 @@ def intersectSimpleXY(g1,g2,inside=True,params=False):
|
|
2870
3030
|
|
2871
3031
|
## is the argument a "simple" (non-compound) geometry object
|
2872
3032
|
def issimple(g):
|
3033
|
+
""" determine if the argument is a simple (non-compound) figure"""
|
2873
3034
|
if ispoint(g) or isline(g) or isarc(g):
|
2874
3035
|
return True
|
2875
3036
|
else:
|
@@ -2880,6 +3041,16 @@ def issimple(g):
|
|
2880
3041
|
## intersection. Booo-ya.
|
2881
3042
|
|
2882
3043
|
def intersectXY(g1,g2,inside=True,params=False):
|
3044
|
+
"""
|
3045
|
+
given two XY-coplanar figures, calculate the intersection of these
|
3046
|
+
two figures, and return a list of intersection points, or False if
|
3047
|
+
none. If ``inside == True``, only return intersections that are
|
3048
|
+
within the ``0 <= u <= 1.0`` interval for both figures. If
|
3049
|
+
``params == True``, instead of returning a list of points, return
|
3050
|
+
two lists corresponding to the sampling parameter value of the
|
3051
|
+
intersections corresponding to each figure.
|
3052
|
+
|
3053
|
+
"""
|
2883
3054
|
if ispoint(g1) or ispoint(g2):
|
2884
3055
|
return False
|
2885
3056
|
elif issimple(g1):
|
@@ -2960,234 +3131,3 @@ def intersectXY(g1,g2,inside=True,params=False):
|
|
2960
3131
|
else:
|
2961
3132
|
raise ValueError('very bad thing, this should never happen')
|
2962
3133
|
|
2963
|
-
|
2964
|
-
|
2965
|
-
|
2966
|
-
|
2967
|
-
|
2968
|
-
|
2969
|
-
## UNIT TESTS
|
2970
|
-
## ========================================
|
2971
|
-
## check to see if we have been invoked on the command line
|
2972
|
-
## if so, run some tests
|
2973
|
-
|
2974
|
-
if __name__ == "__main__":
|
2975
|
-
print("------------------------------------------")
|
2976
|
-
print("yapCAD geometry tests")
|
2977
|
-
print("------------------------------------------")
|
2978
|
-
print("light-weight unit tests for geom.py module")
|
2979
|
-
a = point(5,0)
|
2980
|
-
b = point(0,5)
|
2981
|
-
c = point(-3,-3)
|
2982
|
-
d = point(1,1)
|
2983
|
-
e = point(10,10)
|
2984
|
-
# print("---> point creation and testing")
|
2985
|
-
# print("some points: a:" + vstr(a) + ", b:"
|
2986
|
-
# + vstr(b) + ", c:" + vstr(c) + ", d:" + vstr(d) + ", e:" + vstr(e))
|
2987
|
-
# print("ispoint(a): ",ispoint(a))
|
2988
|
-
# print("ispoint(point(a)): ",ispoint(point(a)))
|
2989
|
-
# print("ispoint([1,2]: ",ispoint([1,2]))
|
2990
|
-
# print("ispoint(vect(1,2)): ",ispoint(vect(1,2)))
|
2991
|
-
# print("ispoint(vect(1,2,3,4)): ",ispoint(vect(1,2,3,4)))
|
2992
|
-
# print("ispoint(vect(1,2,3,-1)): ",ispoint(vect(1,2,3,-1)))
|
2993
|
-
|
2994
|
-
print("---> basic vector operations tests")
|
2995
|
-
# print("mag a: " + str(mag(a)))
|
2996
|
-
# print("add(a,b): " + vstr(add(a,b)))
|
2997
|
-
# print("sub(a,b): " + vstr(sub(a,b)))
|
2998
|
-
# print("mag(sub(a,b)): " + str(mag(sub(a,b))))
|
2999
|
-
# print("mag(sub(a,b)) == sqrt(50): " + str(mag(sub(a,b))==sqrt(50.0)))
|
3000
|
-
|
3001
|
-
print("---> line creation and testing")
|
3002
|
-
l1 = [a,b]
|
3003
|
-
# print("l1 = [a,b] -- l1:",vstr(l1))
|
3004
|
-
# l11 = line(a,b)
|
3005
|
-
# print("l11 = line(a,b) -- l1:",vstr(l11))
|
3006
|
-
l2 = [c,d]
|
3007
|
-
# l22 = line(l2)
|
3008
|
-
# print("l2 = [c,d], l22 = line(l2) -- l22: ",vstr(l22))
|
3009
|
-
l3 = line(c,e)
|
3010
|
-
# print("l3 = line(c,e), isline(l3) : ",isline(l3))
|
3011
|
-
# print("a {}, isline(a): {}".format(vstr(a),isline(a)))
|
3012
|
-
|
3013
|
-
|
3014
|
-
print("---> vector and geometry copying tests")
|
3015
|
-
foo = [a,b,l3,l2,d]
|
3016
|
-
print("foo: ",vstr(foo))
|
3017
|
-
print("deepcopy(foo)",vstr(deepcopy(foo)))
|
3018
|
-
bar = [a,b,[1,2],l2,l3]
|
3019
|
-
print("bar: ",vstr(bar))
|
3020
|
-
print("expect False: deepcopy(bar)",vstr(deepcopy(bar)))
|
3021
|
-
|
3022
|
-
print("---> line-line intersection tests")
|
3023
|
-
# print("l1:" + vstr(l1) + ", l2:" + vstr(l2) +", l3:" + vstr(l3))
|
3024
|
-
|
3025
|
-
# int0 = lineLineIntersectXY(l1,l1)
|
3026
|
-
# int1 = lineLineIntersectXY(l1,l2,False)
|
3027
|
-
# int2 = lineLineIntersectXY(l1,l2,True)
|
3028
|
-
# int3 = lineLineIntersectXY(l1,l3,True)
|
3029
|
-
|
3030
|
-
# print("expect False: lineLineIntersectXY(l1,l1): " + vstr(int0))
|
3031
|
-
# print("expect [2.5, 2.5]: lineLineIntersectXY(l1,l2,False): " + vstr(int1))
|
3032
|
-
# print("expect False: lineLineIntersectXY(l1,l2,True): " + vstr(int2))
|
3033
|
-
# print("expect [2.5, 2.5]: lineLineIntersectXY(l1,l3,True): " + vstr(int3))
|
3034
|
-
|
3035
|
-
# print("linePointXY(l1,vect(0,0)): "
|
3036
|
-
# + vstr(linePointXY(l1,vect(0,0))))
|
3037
|
-
# print("linePointXYDist(l1,vect(0,0)) == sqrt(12.5): "
|
3038
|
-
# + vstr(abs(linePointXYDist(l1,vect(0,0))-sqrt(12.5))<epsilon))
|
3039
|
-
# print("linePointXY(l1,vect(0,10),False): "
|
3040
|
-
# + vstr(linePointXY(l1,vect(vect(0,10)),False)))
|
3041
|
-
# print("linePointXY(l1,vect(0,10),True): "
|
3042
|
-
# + vstr(linePointXY(l1,vect(0,10),True)))
|
3043
|
-
# print("linePointXY(l1,vect(10,0),False): "
|
3044
|
-
# + vstr(linePointXY(l1,vect(10,0),False)))
|
3045
|
-
# print("linePointXY(l1,vect(10,0),True): "
|
3046
|
-
# + vstr(linePointXY(l1,vect(10,0),True)))
|
3047
|
-
|
3048
|
-
print("---> arc creation and testing")
|
3049
|
-
# arc1=[vect(2.5,2.5),vect(2.5,90.0,270.0,-1)]
|
3050
|
-
# print("arc1=[vect(2.5,2.5),vect(2.5,90.0,270.0,-1)], arc1: ",vstr(arc1))
|
3051
|
-
# arc11=arc(vect(2.5,2.5),2.5,90.0,270.0)
|
3052
|
-
# print("arc11=arc(vect(2.5,2.5),2.5,90.0,270.0), arc11: ",vstr(arc11))
|
3053
|
-
# print("isarc(arc1): {} isarc(arc11): {}".format(isarc(arc1),isarc(arc11)))
|
3054
|
-
# arc12=arc(arc11)
|
3055
|
-
# print("arc12=arc(arc11), arc12: {}, isarc(arc12): {}".format(vstr(arc12),isarc(arc12)))
|
3056
|
-
# try:
|
3057
|
-
# print("try creating an arc with a negative radius, should raise ValueError")
|
3058
|
-
# print("arc(vect(0,0),-2): ",arc(vect(0,0),-2))
|
3059
|
-
# except ValueError as err:
|
3060
|
-
# print('got expected result:',err)
|
3061
|
-
# print("--> line-arc disambiguation")
|
3062
|
-
# print("l1: ",vstr(l1)," arc1: ",vstr(arc1))
|
3063
|
-
# print("isline(l1): {} isline(arc1): {}".format(isline(l1),isline(arc1)))
|
3064
|
-
# print("isarc(l1): {} isarc(arc1): {}".format(isarc(l1),isarc(arc1)))
|
3065
|
-
# print("---> arc-line intersection tests")
|
3066
|
-
arc1=[vect(2.5,2.5),vect(2.5,90.0,270.0)]
|
3067
|
-
print("arc1: {}".format(vstr(arc1)))
|
3068
|
-
print("l1: {}".format(vstr(l1)))
|
3069
|
-
l2[1]=vect(0,0)
|
3070
|
-
print("l2: {}".format(vstr(l2)))
|
3071
|
-
int4 = lineArcIntersectXY(l1,arc1,False)
|
3072
|
-
int5 = lineArcIntersectXY(l1,arc1,True)
|
3073
|
-
int6 = lineArcIntersectXY([vect(0,5),vect(5,5)],arc1,True)
|
3074
|
-
int7 = lineArcIntersectXY(l2,arc1,True)
|
3075
|
-
int8 = lineArcIntersectXY(l2,arc1,False)
|
3076
|
-
print("lineArcIntersectXY(l1,arc1,False): {}".format(vstr(int4)))
|
3077
|
-
print("lineArcIntersectXY(l1,arc1,True): {}".format(vstr(int5)))
|
3078
|
-
print("lineArcIntersectXY([vect(0,5),vect(5,5)],arc1,True): {}".format(vstr(int6)))
|
3079
|
-
print("lineArcIntersectXY(l2,arc1,False): {}".format(vstr(int7)))
|
3080
|
-
print("lineArcIntersectXY(l2,arc1,True): {}".format(vstr(int8)))
|
3081
|
-
|
3082
|
-
print("---> circle-circle tangent testing")
|
3083
|
-
circ1 = arc(point(5,5),5)
|
3084
|
-
circ2 = arc(point(-5,5),7.5)
|
3085
|
-
circ3 = arc(point(0,0),1)
|
3086
|
-
|
3087
|
-
tl1 = circleCircleTangentsXY(circ1,circ2)
|
3088
|
-
tl2 = circleCircleTangentsXY(circ2,circ1)
|
3089
|
-
tl3 = circleCircleTangentsXY(circ3,circ2)
|
3090
|
-
|
3091
|
-
print("circ1: ",vstr(circ1))
|
3092
|
-
print("circ2: ",vstr(circ2))
|
3093
|
-
print("circ3: ",vstr(circ3))
|
3094
|
-
|
3095
|
-
print("circleCircleTangentsXY(circ1,circ2) :", vstr(tl1))
|
3096
|
-
print("circleCircleTangentsXY(circ2,circ1) :", vstr(tl2))
|
3097
|
-
print("circleCircleTangentsXY(circ3,circ2) :", vstr(tl3))
|
3098
|
-
|
3099
|
-
print("---> arc-arc intersection tests")
|
3100
|
-
arc2=[vect(4.0,2.5),vect(2.5,90.0,270.0)]
|
3101
|
-
print("arc1: {}".format(vstr(arc1)))
|
3102
|
-
print("arc2: {}".format(vstr(arc2)))
|
3103
|
-
|
3104
|
-
int9 = arcArcIntersectXY(arc1,arc2,False)
|
3105
|
-
int10 = arcArcIntersectXY(arc1,arc2,True)
|
3106
|
-
int11 = arcArcIntersectXY(arc2,arc1,True)
|
3107
|
-
print("arcArcIntersectXY(arc1,arc2,False):",vstr(int9))
|
3108
|
-
print("arcArcIntersectXY(arc1,arc2,True):",vstr(int10))
|
3109
|
-
print("arcArcIntersectXY(arc2,arc1,True):",vstr(int11))
|
3110
|
-
|
3111
|
-
|
3112
|
-
print("---> do some planar point testing")
|
3113
|
-
# points in the x-y plane
|
3114
|
-
p1 = point(2.5,0)
|
3115
|
-
p2 = point(5,5)
|
3116
|
-
p3 = point(0,5)
|
3117
|
-
p4 = point(2.5,10)
|
3118
|
-
|
3119
|
-
# points in the x,z plane
|
3120
|
-
p5 = point(1,0,-5)
|
3121
|
-
p6 = point(10,0,10)
|
3122
|
-
|
3123
|
-
# points in the y-z plane
|
3124
|
-
p7 = point(0,2,-1)
|
3125
|
-
p8 = point(0,10,10)
|
3126
|
-
|
3127
|
-
print('expect True: isCardinalPlanar("xy",[p1,p2,p3,p4]) : {}'.format(
|
3128
|
-
isCardinalPlanar("xy",[p1,p2,p3,p4])))
|
3129
|
-
print('expect False: isCardinalPlanar("xz",[p1,p2,p3,p4]) : {}'.format(
|
3130
|
-
isCardinalPlanar("xz",[p1,p2,p3,p4])))
|
3131
|
-
print('expect False: isCardinalPlanar("yz",[p1,p2,p3,p4]) : {}'.format(
|
3132
|
-
isCardinalPlanar("yz",[p1,p2,p3,p4])))
|
3133
|
-
|
3134
|
-
print('expect True: isCardinalPlanar("xz",[p1,p5,p6]) : {}'.format(
|
3135
|
-
isCardinalPlanar("xz",[p1,p5,p6])))
|
3136
|
-
|
3137
|
-
print('expect True: isCardinalPlanar("yz",[p3,p7,p8]) : {}'.format(
|
3138
|
-
isCardinalPlanar("yz",[p3,p7,p8])))
|
3139
|
-
|
3140
|
-
try:
|
3141
|
-
print('deliberate bad plane specification, should raise ValueError')
|
3142
|
-
print('isCardinalPlanar("FOO!",[p1,p2,p3,p4]) : {}'.format(
|
3143
|
-
isCardinalPlanar("FOO!",[p1,p2,p3,p4])))
|
3144
|
-
except ValueError as err:
|
3145
|
-
print('got expected result:',err)
|
3146
|
-
|
3147
|
-
|
3148
|
-
print("---> convex polygon inside testing")
|
3149
|
-
tri1 = [p1,p2,p3]
|
3150
|
-
poly1 = [p1,p2,p4,p3]
|
3151
|
-
p = point(2.5,1.0)
|
3152
|
-
q = point(2.5,5.0)
|
3153
|
-
r = point(2.5,7.0)
|
3154
|
-
s= point(-10,-10)
|
3155
|
-
t= point(10,10)
|
3156
|
-
print ("p: {}, q: {}, r: {}, s: {}, t: {}".format(vstr(p), vstr(q),
|
3157
|
-
vstr(r), vstr(s),
|
3158
|
-
vstr(t)))
|
3159
|
-
print ("tri1: {}".format(vstr(tri1)))
|
3160
|
-
print ("ispoly(tri1): ",ispoly(tri1))
|
3161
|
-
print ("istriangle(tri1): ",istriangle(tri1))
|
3162
|
-
print("expect True: isInsideTriangleXY(p,tri1): {}"\
|
3163
|
-
.format(isInsideTriangleXY(p,tri1)))
|
3164
|
-
print("expect True: isInsideTriangleXY(q,tri1): {}"\
|
3165
|
-
.format(isInsideTriangleXY(q,tri1)))
|
3166
|
-
print("expect False: isInsideTriangleXY(r,tri1): {}"\
|
3167
|
-
.format(isInsideTriangleXY(r,tri1)))
|
3168
|
-
print("expect False: isInsideTriangleXY(s,tri1): {}"\
|
3169
|
-
.format(isInsideTriangleXY(s,tri1)))
|
3170
|
-
print("expect False: isInsideTriangleXY(t,tri1): {}"\
|
3171
|
-
.format(isInsideTriangleXY(t,tri1)))
|
3172
|
-
print("inside poly testing")
|
3173
|
-
print ("tri1: {}".format(vstr(tri1)))
|
3174
|
-
print ("poly1: {}".format(vstr(poly1)))
|
3175
|
-
print("expect True: isInsideConvexPolyXY(p,tri1): {}"\
|
3176
|
-
.format(isInsideConvexPolyXY(p,tri1)))
|
3177
|
-
print("expect True: isInsideConvexPolyXY(q,tri1): {}"\
|
3178
|
-
.format(isInsideConvexPolyXY(q,tri1)))
|
3179
|
-
print("expect False: isInsideConvexPolyXY(r,tri1): {}"\
|
3180
|
-
.format(isInsideConvexPolyXY(r,tri1)))
|
3181
|
-
|
3182
|
-
print("expect True: isInsideConvexPolyXY(p,poly1): {}"\
|
3183
|
-
.format(isInsideConvexPolyXY(p,poly1)))
|
3184
|
-
print("expect True: isInsideConvexPolyXY(q,poly1): {}"\
|
3185
|
-
.format(isInsideConvexPolyXY(q,poly1)))
|
3186
|
-
print("expect True: isInsideConvexPolyXY(r,poly1): {}"\
|
3187
|
-
.format(isInsideConvexPolyXY(r,poly1)))
|
3188
|
-
print("expect False: isInsideConvexPolyXY(s,tri1): {}"\
|
3189
|
-
.format(isInsideConvexPolyXY(s,tri1)))
|
3190
|
-
print("expect False: isInsideConvexPolyXY(t,tri1): {}"\
|
3191
|
-
.format(isInsideConvexPolyXY(t,tri1)))
|
3192
|
-
|
3193
|
-
print("done!")
|