yapCAD 0.2.5__py2.py3-none-any.whl → 0.3.1__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/__init__.py +8 -8
- yapcad/combine.py +83 -371
- yapcad/drawable.py +44 -3
- yapcad/ezdxf_drawable.py +1 -1
- yapcad/geom.py +214 -276
- yapcad/geom3d.py +975 -55
- yapcad/geom3d_util.py +541 -0
- yapcad/geom_util.py +817 -0
- yapcad/geometry.py +442 -50
- 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 +10 -5
- {yapCAD-0.2.5.dist-info → yapcad-0.3.1.dist-info}/METADATA +110 -39
- yapcad-0.3.1.dist-info/RECORD +27 -0
- {yapCAD-0.2.5.dist-info → yapcad-0.3.1.dist-info}/WHEEL +1 -1
- yapCAD-0.2.5.dist-info/RECORD +0 -17
- {yapCAD-0.2.5.dist-info → yapcad-0.3.1.dist-info/licenses}/AUTHORS.rst +0 -0
- {yapCAD-0.2.5.dist-info → yapcad-0.3.1.dist-info/licenses}/LICENSE +0 -0
- {yapCAD-0.2.5.dist-info → yapcad-0.3.1.dist-info/licenses}/LICENSE.txt +0 -0
- {yapCAD-0.2.5.dist-info → yapcad-0.3.1.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
|
@@ -1870,7 +1912,6 @@ def isinsidepolyXY(a,p):
|
|
1870
1912
|
|
1871
1913
|
"""
|
1872
1914
|
closed=False
|
1873
|
-
|
1874
1915
|
if len(a) > 2 and dist(a[0],a[-1]) < epsilon:
|
1875
1916
|
closed = True
|
1876
1917
|
|
@@ -1887,12 +1928,11 @@ def isinsidepolyXY(a,p):
|
|
1887
1928
|
if not isinsidebbox(bb,p):
|
1888
1929
|
return False
|
1889
1930
|
## inside the bounding box, do intersection testing
|
1890
|
-
p2 = add([
|
1931
|
+
p2 = add([0.01,0.01,0,1],bb[1])
|
1891
1932
|
if vclose(p2,p): ## did we randomly pick an outside point near the
|
1892
|
-
## test point?
|
1893
|
-
|
1933
|
+
## test point? If so, test point is outside bb
|
1934
|
+
return False
|
1894
1935
|
l = line(p,p2)
|
1895
|
-
|
1896
1936
|
pp = intersectSimplePolyXY(l,a)
|
1897
1937
|
if pp == False:
|
1898
1938
|
return False
|
@@ -2113,9 +2153,24 @@ def intersectSimplePolyXY(g,a,inside=True,params=False):
|
|
2113
2153
|
|
2114
2154
|
|
2115
2155
|
def polycenter(a):
|
2116
|
-
|
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)
|
2117
2169
|
|
2118
2170
|
def polylength(a):
|
2171
|
+
"""
|
2172
|
+
compute length of poly ``a``
|
2173
|
+
"""
|
2119
2174
|
if len(a) < 2:
|
2120
2175
|
return 0.0
|
2121
2176
|
l = 0.0
|
@@ -2129,6 +2184,9 @@ def polylength(a):
|
|
2129
2184
|
## or other geometry lists.
|
2130
2185
|
|
2131
2186
|
def isgeomlist(a):
|
2187
|
+
"""
|
2188
|
+
determine if argument ``a`` is a valid geometry list
|
2189
|
+
"""
|
2132
2190
|
if not isinstance(a,list):
|
2133
2191
|
return False
|
2134
2192
|
b = list(filter(lambda x: not (ispoint(x) or isline(x) \
|
@@ -2137,6 +2195,43 @@ def isgeomlist(a):
|
|
2137
2195
|
return not len(b) > 0
|
2138
2196
|
|
2139
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
|
+
|
2140
2235
|
def __geomlistlength(gl):
|
2141
2236
|
leng=0.0
|
2142
2237
|
lengths=[]
|
@@ -2171,6 +2266,46 @@ def samplegeomlist(gl,u):
|
|
2171
2266
|
uu = (dst-leng+lengths[-1])/lengths[-1]
|
2172
2267
|
return sample(gl[-1],uu)
|
2173
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
|
+
|
2174
2309
|
## function to reverse a geometry list (presumably contiguous) for
|
2175
2310
|
## sampling purposes
|
2176
2311
|
def reverseGeomList(gl):
|
@@ -2232,6 +2367,8 @@ def segmentgeomlist(gl,u1,u2,closed=False,reverse=False):
|
|
2232
2367
|
|
2233
2368
|
for i in range(len(gl)):
|
2234
2369
|
l=lengths[i]
|
2370
|
+
if l < epsilon:
|
2371
|
+
continue
|
2235
2372
|
if dst1 <= d+l:
|
2236
2373
|
if not STARTED:
|
2237
2374
|
uu = 1.0 - (d+l-dst1)/l
|
@@ -2285,10 +2422,10 @@ def isinsidegeomlistXY(a,p):
|
|
2285
2422
|
bb = geomlistbbox(a)
|
2286
2423
|
if not isinsidebbox(bb,p):
|
2287
2424
|
return False
|
2288
|
-
p2 = add([1,1,0,1],bb[1])
|
2425
|
+
p2 = add([0.1,0.1,0,1],bb[1])
|
2289
2426
|
if vclose(p2,p): ## did we randomly pick an outside point near the
|
2290
|
-
## test point?
|
2291
|
-
|
2427
|
+
## test point? If so, test point is outside bb
|
2428
|
+
return False
|
2292
2429
|
l = line(p,p2)
|
2293
2430
|
|
2294
2431
|
pp = intersectGeomListXY(l,a)
|
@@ -2317,7 +2454,7 @@ def isgeomlistXYPlanar(gl):
|
|
2317
2454
|
return False
|
2318
2455
|
return isXYPlanar(pp)
|
2319
2456
|
|
2320
|
-
## compute the intersection between geometric element g, and geometry
|
2457
|
+
## compute the intersection between coplanar geometric element g, and geometry
|
2321
2458
|
## list gl. NOTE: this function does not impose continuity
|
2322
2459
|
## requirements on the geometry list, and point elements are ignored
|
2323
2460
|
## for intersection testing.
|
@@ -2365,7 +2502,7 @@ def intersectGeomListXY(g,gl,inside=True,params=False):
|
|
2365
2502
|
raise ValueError('bad gtype in intersectGeomListXY, this should never happen')
|
2366
2503
|
|
2367
2504
|
dst = 0.0
|
2368
|
-
if len(gl) >
|
2505
|
+
if len(gl) > 1:
|
2369
2506
|
for i in range(len(gl)):
|
2370
2507
|
g2 = gl[i]
|
2371
2508
|
uu = []
|
@@ -2389,12 +2526,12 @@ def intersectGeomListXY(g,gl,inside=True,params=False):
|
|
2389
2526
|
elif gtype == 'poly' and gtype2 == 'poly':
|
2390
2527
|
zz1 = []
|
2391
2528
|
zz2 = []
|
2392
|
-
for
|
2393
|
-
zz = intersectSimplePolyXY(line(g[
|
2529
|
+
for j in range(1,len(g)):
|
2530
|
+
zz = intersectSimplePolyXY(line(g[j-1],g[j]),
|
2394
2531
|
g2, params=True)
|
2395
2532
|
if not isinstance(zz,bool):
|
2396
|
-
zz1
|
2397
|
-
zz2
|
2533
|
+
zz1 = zz1 + zz[0]
|
2534
|
+
zz2 = zz2 + zz[1]
|
2398
2535
|
if len(zz1) > 0:
|
2399
2536
|
uu = [ zz1,zz2 ]
|
2400
2537
|
elif gtype == 'simple' and gtype2 == 'glist':
|
@@ -2543,6 +2680,8 @@ def center(x):
|
|
2543
2680
|
pl = []
|
2544
2681
|
for g in x:
|
2545
2682
|
pl.append(center(g))
|
2683
|
+
if isclosedgeomlist(x):
|
2684
|
+
pl.append(pl[0])
|
2546
2685
|
return polycenter(pl)
|
2547
2686
|
else:
|
2548
2687
|
raise ValueError("inappropriate type for center(): ",format(x))
|
@@ -2604,7 +2743,7 @@ def unsample(x,p):
|
|
2604
2743
|
elif ispoly(x):
|
2605
2744
|
return unsamplepoly(x,p)
|
2606
2745
|
elif isgeomlist(x):
|
2607
|
-
|
2746
|
+
return unsamplegeomlist(x,p)
|
2608
2747
|
else:
|
2609
2748
|
raise ValueError("inappropriate type for unasample(): "+str(x))
|
2610
2749
|
|
@@ -2626,10 +2765,13 @@ def segment(x,u1,u2):
|
|
2626
2765
|
else:
|
2627
2766
|
raise ValueError("inappropriate figure type for segment(): "+str(x))
|
2628
2767
|
|
2629
|
-
|
2630
|
-
|
2631
|
-
|
2632
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
|
+
"""
|
2633
2775
|
if ispoint(x):
|
2634
2776
|
return isinsidepointXY(x,p)
|
2635
2777
|
elif isline(x):
|
@@ -2644,6 +2786,7 @@ def isinsideXY(x,p):
|
|
2644
2786
|
raise ValueError("bad thing passed to inside: {}".format(x))
|
2645
2787
|
|
2646
2788
|
def translate(x,delta):
|
2789
|
+
""" return a translated version of the figure"""
|
2647
2790
|
if ispoint(x):
|
2648
2791
|
return add(x,delta)
|
2649
2792
|
elif isline(x):
|
@@ -2666,6 +2809,7 @@ def translate(x,delta):
|
|
2666
2809
|
raise ValueError("don't know how to translate {}".format(x))
|
2667
2810
|
|
2668
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"""
|
2669
2813
|
if sy == False and sz == False:
|
2670
2814
|
sy = sz = sx
|
2671
2815
|
if vclose(point(sx,sy,sz),point(1.0,1.0,1.0)):
|
@@ -2708,6 +2852,8 @@ def scale(x,sx=1.0,sy=False,sz=False,cent=point(0,0),mat=False):
|
|
2708
2852
|
|
2709
2853
|
|
2710
2854
|
def transform(x,m):
|
2855
|
+
""" return a transformed version of the figure, as specified by the
|
2856
|
+
transformation matrix m"""
|
2711
2857
|
if not isinstance(m,xform.Matrix):
|
2712
2858
|
raise ValueError('bad transformation matrix passed to transform')
|
2713
2859
|
if ispoint(x):
|
@@ -2739,20 +2885,21 @@ def transform(x,m):
|
|
2739
2885
|
# recursive processing of geometry lists.
|
2740
2886
|
|
2741
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"""
|
2742
2889
|
if close(ang,0.0):
|
2743
2890
|
return deepcopy(x)
|
2744
2891
|
if not mat: # if matrix isn't pre-specified, calculate it
|
2745
2892
|
if vclose(cent,point(0,0,0)):
|
2746
2893
|
mat = xform.Rotation(axis,ang)
|
2747
2894
|
else:
|
2748
|
-
mat = xform.Translation(cent
|
2895
|
+
mat = xform.Translation(cent)
|
2749
2896
|
mat = mat.mul(xform.Rotation(axis,ang))
|
2750
|
-
mat = mat.mul(xform.Translation(cent))
|
2897
|
+
mat = mat.mul(xform.Translation(cent,inverse=True))
|
2751
2898
|
|
2752
2899
|
# arcs are wierd, since we will have to deal with a non-trivial
|
2753
2900
|
# change of basis function to handle the interpretation of "start"
|
2754
2901
|
# and "end" if the axis of rotation isn't the z axis.
|
2755
|
-
if ispoint(x):
|
2902
|
+
if ispoint(x) or isvect(x):
|
2756
2903
|
return mat.mul(x)
|
2757
2904
|
elif isline(x):
|
2758
2905
|
return line(mat.mul(x[0]),
|
@@ -2767,7 +2914,7 @@ def rotate(x,ang,cent=point(0,0),axis=point(0,0,1.0),mat=False):
|
|
2767
2914
|
c[1][2] += ang
|
2768
2915
|
return c
|
2769
2916
|
|
2770
|
-
elif isgeomlist(x):
|
2917
|
+
elif isgeomlist(x) or isdirectlist(x):
|
2771
2918
|
gl = []
|
2772
2919
|
for g in x:
|
2773
2920
|
gl.append(rotate(g,ang,cent,axis,mat))
|
@@ -2780,7 +2927,16 @@ def rotate(x,ang,cent=point(0,0),axis=point(0,0,1.0),mat=False):
|
|
2780
2927
|
## generalized geometry mirror function
|
2781
2928
|
|
2782
2929
|
def mirror(x,plane):
|
2783
|
-
|
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]
|
2784
2940
|
if plane == 'xz':
|
2785
2941
|
flip[1]= -1
|
2786
2942
|
elif plane == 'yz':
|
@@ -2790,8 +2946,8 @@ def mirror(x,plane):
|
|
2790
2946
|
else:
|
2791
2947
|
raise ValueError('bad reflection plane passed to mirror')
|
2792
2948
|
|
2793
|
-
if
|
2794
|
-
return
|
2949
|
+
if isvect(x):
|
2950
|
+
return mul4(x,flip)
|
2795
2951
|
elif isarc(x):
|
2796
2952
|
a2=arc(x)
|
2797
2953
|
a2[0] = mul(x[0],flip)
|
@@ -2808,10 +2964,10 @@ def mirror(x,plane):
|
|
2808
2964
|
a2[1][1]=start
|
2809
2965
|
a2[1][2]=end
|
2810
2966
|
return a2
|
2811
|
-
elif ispoly(x):
|
2967
|
+
elif ispoly(x) or isdirectlist(x):
|
2812
2968
|
ply = []
|
2813
2969
|
for p in x:
|
2814
|
-
ply.append(
|
2970
|
+
ply.append(mul4(p,flip))
|
2815
2971
|
return ply
|
2816
2972
|
elif isgeomlist(x):
|
2817
2973
|
r = []
|
@@ -2831,6 +2987,7 @@ def mirror(x,plane):
|
|
2831
2987
|
## if no intersections
|
2832
2988
|
|
2833
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"""
|
2834
2991
|
g1line = True
|
2835
2992
|
g2line = True
|
2836
2993
|
if isarc(g1):
|
@@ -2861,6 +3018,7 @@ def _intersectSimpleXY(g1,g2,inside=True,params=False):
|
|
2861
3018
|
## Value-safe simple wrapper for calculation of intersection of
|
2862
3019
|
## non-compound geometric elements
|
2863
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"""
|
2864
3022
|
if not (isline(g1) or isarc(g1)) \
|
2865
3023
|
or not (isline(g2) or isarc(g2)):
|
2866
3024
|
raise ValueError('bad geometry passed to intersectSimpleXY')
|
@@ -2872,6 +3030,7 @@ def intersectSimpleXY(g1,g2,inside=True,params=False):
|
|
2872
3030
|
|
2873
3031
|
## is the argument a "simple" (non-compound) geometry object
|
2874
3032
|
def issimple(g):
|
3033
|
+
""" determine if the argument is a simple (non-compound) figure"""
|
2875
3034
|
if ispoint(g) or isline(g) or isarc(g):
|
2876
3035
|
return True
|
2877
3036
|
else:
|
@@ -2882,6 +3041,16 @@ def issimple(g):
|
|
2882
3041
|
## intersection. Booo-ya.
|
2883
3042
|
|
2884
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
|
+
"""
|
2885
3054
|
if ispoint(g1) or ispoint(g2):
|
2886
3055
|
return False
|
2887
3056
|
elif issimple(g1):
|
@@ -2962,234 +3131,3 @@ def intersectXY(g1,g2,inside=True,params=False):
|
|
2962
3131
|
else:
|
2963
3132
|
raise ValueError('very bad thing, this should never happen')
|
2964
3133
|
|
2965
|
-
|
2966
|
-
|
2967
|
-
|
2968
|
-
|
2969
|
-
|
2970
|
-
|
2971
|
-
## UNIT TESTS
|
2972
|
-
## ========================================
|
2973
|
-
## check to see if we have been invoked on the command line
|
2974
|
-
## if so, run some tests
|
2975
|
-
|
2976
|
-
if __name__ == "__main__":
|
2977
|
-
print("------------------------------------------")
|
2978
|
-
print("yapCAD geometry tests")
|
2979
|
-
print("------------------------------------------")
|
2980
|
-
print("light-weight unit tests for geom.py module")
|
2981
|
-
a = point(5,0)
|
2982
|
-
b = point(0,5)
|
2983
|
-
c = point(-3,-3)
|
2984
|
-
d = point(1,1)
|
2985
|
-
e = point(10,10)
|
2986
|
-
# print("---> point creation and testing")
|
2987
|
-
# print("some points: a:" + vstr(a) + ", b:"
|
2988
|
-
# + vstr(b) + ", c:" + vstr(c) + ", d:" + vstr(d) + ", e:" + vstr(e))
|
2989
|
-
# print("ispoint(a): ",ispoint(a))
|
2990
|
-
# print("ispoint(point(a)): ",ispoint(point(a)))
|
2991
|
-
# print("ispoint([1,2]: ",ispoint([1,2]))
|
2992
|
-
# print("ispoint(vect(1,2)): ",ispoint(vect(1,2)))
|
2993
|
-
# print("ispoint(vect(1,2,3,4)): ",ispoint(vect(1,2,3,4)))
|
2994
|
-
# print("ispoint(vect(1,2,3,-1)): ",ispoint(vect(1,2,3,-1)))
|
2995
|
-
|
2996
|
-
print("---> basic vector operations tests")
|
2997
|
-
# print("mag a: " + str(mag(a)))
|
2998
|
-
# print("add(a,b): " + vstr(add(a,b)))
|
2999
|
-
# print("sub(a,b): " + vstr(sub(a,b)))
|
3000
|
-
# print("mag(sub(a,b)): " + str(mag(sub(a,b))))
|
3001
|
-
# print("mag(sub(a,b)) == sqrt(50): " + str(mag(sub(a,b))==sqrt(50.0)))
|
3002
|
-
|
3003
|
-
print("---> line creation and testing")
|
3004
|
-
l1 = [a,b]
|
3005
|
-
# print("l1 = [a,b] -- l1:",vstr(l1))
|
3006
|
-
# l11 = line(a,b)
|
3007
|
-
# print("l11 = line(a,b) -- l1:",vstr(l11))
|
3008
|
-
l2 = [c,d]
|
3009
|
-
# l22 = line(l2)
|
3010
|
-
# print("l2 = [c,d], l22 = line(l2) -- l22: ",vstr(l22))
|
3011
|
-
l3 = line(c,e)
|
3012
|
-
# print("l3 = line(c,e), isline(l3) : ",isline(l3))
|
3013
|
-
# print("a {}, isline(a): {}".format(vstr(a),isline(a)))
|
3014
|
-
|
3015
|
-
|
3016
|
-
print("---> vector and geometry copying tests")
|
3017
|
-
foo = [a,b,l3,l2,d]
|
3018
|
-
print("foo: ",vstr(foo))
|
3019
|
-
print("deepcopy(foo)",vstr(deepcopy(foo)))
|
3020
|
-
bar = [a,b,[1,2],l2,l3]
|
3021
|
-
print("bar: ",vstr(bar))
|
3022
|
-
print("expect False: deepcopy(bar)",vstr(deepcopy(bar)))
|
3023
|
-
|
3024
|
-
print("---> line-line intersection tests")
|
3025
|
-
# print("l1:" + vstr(l1) + ", l2:" + vstr(l2) +", l3:" + vstr(l3))
|
3026
|
-
|
3027
|
-
# int0 = lineLineIntersectXY(l1,l1)
|
3028
|
-
# int1 = lineLineIntersectXY(l1,l2,False)
|
3029
|
-
# int2 = lineLineIntersectXY(l1,l2,True)
|
3030
|
-
# int3 = lineLineIntersectXY(l1,l3,True)
|
3031
|
-
|
3032
|
-
# print("expect False: lineLineIntersectXY(l1,l1): " + vstr(int0))
|
3033
|
-
# print("expect [2.5, 2.5]: lineLineIntersectXY(l1,l2,False): " + vstr(int1))
|
3034
|
-
# print("expect False: lineLineIntersectXY(l1,l2,True): " + vstr(int2))
|
3035
|
-
# print("expect [2.5, 2.5]: lineLineIntersectXY(l1,l3,True): " + vstr(int3))
|
3036
|
-
|
3037
|
-
# print("linePointXY(l1,vect(0,0)): "
|
3038
|
-
# + vstr(linePointXY(l1,vect(0,0))))
|
3039
|
-
# print("linePointXYDist(l1,vect(0,0)) == sqrt(12.5): "
|
3040
|
-
# + vstr(abs(linePointXYDist(l1,vect(0,0))-sqrt(12.5))<epsilon))
|
3041
|
-
# print("linePointXY(l1,vect(0,10),False): "
|
3042
|
-
# + vstr(linePointXY(l1,vect(vect(0,10)),False)))
|
3043
|
-
# print("linePointXY(l1,vect(0,10),True): "
|
3044
|
-
# + vstr(linePointXY(l1,vect(0,10),True)))
|
3045
|
-
# print("linePointXY(l1,vect(10,0),False): "
|
3046
|
-
# + vstr(linePointXY(l1,vect(10,0),False)))
|
3047
|
-
# print("linePointXY(l1,vect(10,0),True): "
|
3048
|
-
# + vstr(linePointXY(l1,vect(10,0),True)))
|
3049
|
-
|
3050
|
-
print("---> arc creation and testing")
|
3051
|
-
# arc1=[vect(2.5,2.5),vect(2.5,90.0,270.0,-1)]
|
3052
|
-
# print("arc1=[vect(2.5,2.5),vect(2.5,90.0,270.0,-1)], arc1: ",vstr(arc1))
|
3053
|
-
# arc11=arc(vect(2.5,2.5),2.5,90.0,270.0)
|
3054
|
-
# print("arc11=arc(vect(2.5,2.5),2.5,90.0,270.0), arc11: ",vstr(arc11))
|
3055
|
-
# print("isarc(arc1): {} isarc(arc11): {}".format(isarc(arc1),isarc(arc11)))
|
3056
|
-
# arc12=arc(arc11)
|
3057
|
-
# print("arc12=arc(arc11), arc12: {}, isarc(arc12): {}".format(vstr(arc12),isarc(arc12)))
|
3058
|
-
# try:
|
3059
|
-
# print("try creating an arc with a negative radius, should raise ValueError")
|
3060
|
-
# print("arc(vect(0,0),-2): ",arc(vect(0,0),-2))
|
3061
|
-
# except ValueError as err:
|
3062
|
-
# print('got expected result:',err)
|
3063
|
-
# print("--> line-arc disambiguation")
|
3064
|
-
# print("l1: ",vstr(l1)," arc1: ",vstr(arc1))
|
3065
|
-
# print("isline(l1): {} isline(arc1): {}".format(isline(l1),isline(arc1)))
|
3066
|
-
# print("isarc(l1): {} isarc(arc1): {}".format(isarc(l1),isarc(arc1)))
|
3067
|
-
# print("---> arc-line intersection tests")
|
3068
|
-
arc1=[vect(2.5,2.5),vect(2.5,90.0,270.0)]
|
3069
|
-
print("arc1: {}".format(vstr(arc1)))
|
3070
|
-
print("l1: {}".format(vstr(l1)))
|
3071
|
-
l2[1]=vect(0,0)
|
3072
|
-
print("l2: {}".format(vstr(l2)))
|
3073
|
-
int4 = lineArcIntersectXY(l1,arc1,False)
|
3074
|
-
int5 = lineArcIntersectXY(l1,arc1,True)
|
3075
|
-
int6 = lineArcIntersectXY([vect(0,5),vect(5,5)],arc1,True)
|
3076
|
-
int7 = lineArcIntersectXY(l2,arc1,True)
|
3077
|
-
int8 = lineArcIntersectXY(l2,arc1,False)
|
3078
|
-
print("lineArcIntersectXY(l1,arc1,False): {}".format(vstr(int4)))
|
3079
|
-
print("lineArcIntersectXY(l1,arc1,True): {}".format(vstr(int5)))
|
3080
|
-
print("lineArcIntersectXY([vect(0,5),vect(5,5)],arc1,True): {}".format(vstr(int6)))
|
3081
|
-
print("lineArcIntersectXY(l2,arc1,False): {}".format(vstr(int7)))
|
3082
|
-
print("lineArcIntersectXY(l2,arc1,True): {}".format(vstr(int8)))
|
3083
|
-
|
3084
|
-
print("---> circle-circle tangent testing")
|
3085
|
-
circ1 = arc(point(5,5),5)
|
3086
|
-
circ2 = arc(point(-5,5),7.5)
|
3087
|
-
circ3 = arc(point(0,0),1)
|
3088
|
-
|
3089
|
-
tl1 = circleCircleTangentsXY(circ1,circ2)
|
3090
|
-
tl2 = circleCircleTangentsXY(circ2,circ1)
|
3091
|
-
tl3 = circleCircleTangentsXY(circ3,circ2)
|
3092
|
-
|
3093
|
-
print("circ1: ",vstr(circ1))
|
3094
|
-
print("circ2: ",vstr(circ2))
|
3095
|
-
print("circ3: ",vstr(circ3))
|
3096
|
-
|
3097
|
-
print("circleCircleTangentsXY(circ1,circ2) :", vstr(tl1))
|
3098
|
-
print("circleCircleTangentsXY(circ2,circ1) :", vstr(tl2))
|
3099
|
-
print("circleCircleTangentsXY(circ3,circ2) :", vstr(tl3))
|
3100
|
-
|
3101
|
-
print("---> arc-arc intersection tests")
|
3102
|
-
arc2=[vect(4.0,2.5),vect(2.5,90.0,270.0)]
|
3103
|
-
print("arc1: {}".format(vstr(arc1)))
|
3104
|
-
print("arc2: {}".format(vstr(arc2)))
|
3105
|
-
|
3106
|
-
int9 = arcArcIntersectXY(arc1,arc2,False)
|
3107
|
-
int10 = arcArcIntersectXY(arc1,arc2,True)
|
3108
|
-
int11 = arcArcIntersectXY(arc2,arc1,True)
|
3109
|
-
print("arcArcIntersectXY(arc1,arc2,False):",vstr(int9))
|
3110
|
-
print("arcArcIntersectXY(arc1,arc2,True):",vstr(int10))
|
3111
|
-
print("arcArcIntersectXY(arc2,arc1,True):",vstr(int11))
|
3112
|
-
|
3113
|
-
|
3114
|
-
print("---> do some planar point testing")
|
3115
|
-
# points in the x-y plane
|
3116
|
-
p1 = point(2.5,0)
|
3117
|
-
p2 = point(5,5)
|
3118
|
-
p3 = point(0,5)
|
3119
|
-
p4 = point(2.5,10)
|
3120
|
-
|
3121
|
-
# points in the x,z plane
|
3122
|
-
p5 = point(1,0,-5)
|
3123
|
-
p6 = point(10,0,10)
|
3124
|
-
|
3125
|
-
# points in the y-z plane
|
3126
|
-
p7 = point(0,2,-1)
|
3127
|
-
p8 = point(0,10,10)
|
3128
|
-
|
3129
|
-
print('expect True: isCardinalPlanar("xy",[p1,p2,p3,p4]) : {}'.format(
|
3130
|
-
isCardinalPlanar("xy",[p1,p2,p3,p4])))
|
3131
|
-
print('expect False: isCardinalPlanar("xz",[p1,p2,p3,p4]) : {}'.format(
|
3132
|
-
isCardinalPlanar("xz",[p1,p2,p3,p4])))
|
3133
|
-
print('expect False: isCardinalPlanar("yz",[p1,p2,p3,p4]) : {}'.format(
|
3134
|
-
isCardinalPlanar("yz",[p1,p2,p3,p4])))
|
3135
|
-
|
3136
|
-
print('expect True: isCardinalPlanar("xz",[p1,p5,p6]) : {}'.format(
|
3137
|
-
isCardinalPlanar("xz",[p1,p5,p6])))
|
3138
|
-
|
3139
|
-
print('expect True: isCardinalPlanar("yz",[p3,p7,p8]) : {}'.format(
|
3140
|
-
isCardinalPlanar("yz",[p3,p7,p8])))
|
3141
|
-
|
3142
|
-
try:
|
3143
|
-
print('deliberate bad plane specification, should raise ValueError')
|
3144
|
-
print('isCardinalPlanar("FOO!",[p1,p2,p3,p4]) : {}'.format(
|
3145
|
-
isCardinalPlanar("FOO!",[p1,p2,p3,p4])))
|
3146
|
-
except ValueError as err:
|
3147
|
-
print('got expected result:',err)
|
3148
|
-
|
3149
|
-
|
3150
|
-
print("---> convex polygon inside testing")
|
3151
|
-
tri1 = [p1,p2,p3]
|
3152
|
-
poly1 = [p1,p2,p4,p3]
|
3153
|
-
p = point(2.5,1.0)
|
3154
|
-
q = point(2.5,5.0)
|
3155
|
-
r = point(2.5,7.0)
|
3156
|
-
s= point(-10,-10)
|
3157
|
-
t= point(10,10)
|
3158
|
-
print ("p: {}, q: {}, r: {}, s: {}, t: {}".format(vstr(p), vstr(q),
|
3159
|
-
vstr(r), vstr(s),
|
3160
|
-
vstr(t)))
|
3161
|
-
print ("tri1: {}".format(vstr(tri1)))
|
3162
|
-
print ("ispoly(tri1): ",ispoly(tri1))
|
3163
|
-
print ("istriangle(tri1): ",istriangle(tri1))
|
3164
|
-
print("expect True: isInsideTriangleXY(p,tri1): {}"\
|
3165
|
-
.format(isInsideTriangleXY(p,tri1)))
|
3166
|
-
print("expect True: isInsideTriangleXY(q,tri1): {}"\
|
3167
|
-
.format(isInsideTriangleXY(q,tri1)))
|
3168
|
-
print("expect False: isInsideTriangleXY(r,tri1): {}"\
|
3169
|
-
.format(isInsideTriangleXY(r,tri1)))
|
3170
|
-
print("expect False: isInsideTriangleXY(s,tri1): {}"\
|
3171
|
-
.format(isInsideTriangleXY(s,tri1)))
|
3172
|
-
print("expect False: isInsideTriangleXY(t,tri1): {}"\
|
3173
|
-
.format(isInsideTriangleXY(t,tri1)))
|
3174
|
-
print("inside poly testing")
|
3175
|
-
print ("tri1: {}".format(vstr(tri1)))
|
3176
|
-
print ("poly1: {}".format(vstr(poly1)))
|
3177
|
-
print("expect True: isInsideConvexPolyXY(p,tri1): {}"\
|
3178
|
-
.format(isInsideConvexPolyXY(p,tri1)))
|
3179
|
-
print("expect True: isInsideConvexPolyXY(q,tri1): {}"\
|
3180
|
-
.format(isInsideConvexPolyXY(q,tri1)))
|
3181
|
-
print("expect False: isInsideConvexPolyXY(r,tri1): {}"\
|
3182
|
-
.format(isInsideConvexPolyXY(r,tri1)))
|
3183
|
-
|
3184
|
-
print("expect True: isInsideConvexPolyXY(p,poly1): {}"\
|
3185
|
-
.format(isInsideConvexPolyXY(p,poly1)))
|
3186
|
-
print("expect True: isInsideConvexPolyXY(q,poly1): {}"\
|
3187
|
-
.format(isInsideConvexPolyXY(q,poly1)))
|
3188
|
-
print("expect True: isInsideConvexPolyXY(r,poly1): {}"\
|
3189
|
-
.format(isInsideConvexPolyXY(r,poly1)))
|
3190
|
-
print("expect False: isInsideConvexPolyXY(s,tri1): {}"\
|
3191
|
-
.format(isInsideConvexPolyXY(s,tri1)))
|
3192
|
-
print("expect False: isInsideConvexPolyXY(t,tri1): {}"\
|
3193
|
-
.format(isInsideConvexPolyXY(t,tri1)))
|
3194
|
-
|
3195
|
-
print("done!")
|