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/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
- Ordinary **yapCAD** geometry is assumed to lie in the w=1 hyperplane, and
86
- when a vector lies in that plane it is usually interpreted as
87
- transformable coordinate, or point. Because we
88
- expect w=1 unless certain types of affine transforms have been
89
- applied, most **yapCAD** geometry operations do not look at the w
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 left" to "upper
131
- top right" of a figure, *e.g.* ``bbx =
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 other
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 VauleError('non-poly list passed to poly()')
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
- return [ point(minx,miny),point(maxx,maxy)]
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([1,1,0,1],bb[1])
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
- p2 = sub(bb[0],[1,1,0,1])
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
- return samplepoly(a,0.5)
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
- p2 = sub(bb[0],[1,1,0,1])
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) > 2:
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 i in range(1,len(g)):
2393
- zz = intersectSimplePolyXY(line(g[i-1],g[i]),
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.append(zz[0])
2397
- zz2.append(zz[1])
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
- raise NotImplementedError('unsampling geometry lists currently not supported')
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,inverse=True)
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
- flip=point(1,1,1)
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 ispoint(x):
2794
- return point(mul(x,flip))
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(mul(p,flip))
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!")