/[escript]/trunk/pycad/py_src/primitives.py
ViewVC logotype

Contents of /trunk/pycad/py_src/primitives.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2377 - (show annotations)
Wed Apr 8 07:57:55 2009 UTC (10 years ago) by gross
File MIME type: text/x-python
File size: 49898 byte(s)
a namespace  problem in pycad fixed.
1
2 ########################################################
3 #
4 # Copyright (c) 2003-2008 by University of Queensland
5 # Earth Systems Science Computational Center (ESSCC)
6 # http://www.uq.edu.au/esscc
7 #
8 # Primary Business: Queensland, Australia
9 # Licensed under the Open Software License version 3.0
10 # http://www.opensource.org/licenses/osl-3.0.php
11 #
12 ########################################################
13
14 __copyright__="""Copyright (c) 2003-2008 by University of Queensland
15 Earth Systems Science Computational Center (ESSCC)
16 http://www.uq.edu.au/esscc
17 Primary Business: Queensland, Australia"""
18 __license__="""Licensed under the Open Software License version 3.0
19 http://www.opensource.org/licenses/osl-3.0.php"""
20 __url__="https://launchpad.net/escript-finley"
21
22 """
23 Geometrical Primitives
24
25 the concept is inspired by gmsh and very much focused on the fact that
26 the classes are used to wrk with gmsh.
27
28 @var __author__: name of author
29 @var __copyright__: copyrights
30 @var __license__: licence agreement
31 @var __url__: url entry point on documentation
32 @var __version__: version
33 @var __date__: date of the version
34 """
35
36 __author__="Lutz Gross, l.gross@uq.edu.au"
37
38 try:
39 import numpy
40 numpyImported=True
41 except:
42 numpyImported=False
43
44 import numarray
45 from transformations import _TYPE, Translation, Dilation, Transformation
46 import math
47
48
49 def resetGlobalPrimitiveIdCounter():
50 """
51 Initializes the global primitive ID counter.
52 """
53 global global_primitive_id_counter
54 global_primitive_id_counter=1
55
56 def setToleranceForColocation(tol=1.e-11):
57 """
58 Sets the global tolerance for colocation checks to C{tol}.
59 """
60 global global_tolerance_for_colocation
61 global_tolerance_for_colocation=tol
62
63 def getToleranceForColocation():
64 """
65 Returns the global tolerance for colocation checks.
66 """
67 return global_tolerance_for_colocation
68
69 resetGlobalPrimitiveIdCounter()
70 setToleranceForColocation()
71
72
73 class PrimitiveBase(object):
74 """
75 Template for a set of primitives.
76 """
77 def __init__(self):
78 """
79 Initializes the PrimitiveBase instance object.
80 """
81 pass
82
83 def __cmp__(self,other):
84 """
85 Compares object with other by comparing the absolute value of the ID.
86 """
87 if isinstance(other, PrimitiveBase):
88 return cmp(self.getID(),other.getID())
89 else:
90 return False
91
92 def getConstructionPoints(self):
93 """
94 Returns the points used to construct the primitive.
95 """
96 out=[]
97 for i in self.getPrimitives():
98 if isinstance(i,Point): out.append(i)
99 return out
100
101 def getPrimitives(self):
102 """
103 Returns a list of primitives used to construct the primitive with no
104 double entries.
105 """
106 out=[]
107 for p in self.collectPrimitiveBases():
108 if not p in out: out.append(p)
109 return out
110
111 def copy(self):
112 """
113 Returns a deep copy of the object.
114 """
115 return self.substitute({})
116
117 def modifyBy(self,transformation):
118 """
119 Modifies the coordinates by applying a transformation.
120 """
121 for p in self.getConstructionPoints(): p.modifyBy(transformation)
122
123 def __add__(self,other):
124 """
125 Returns a new object shifted by C{other}.
126 """
127 return self.apply(Translation(numarray.array(other,_TYPE)))
128
129 def __sub__(self,other):
130 """
131 Returns a new object shifted by C{-other}.
132 """
133 return self.apply(Translation(-numarray.array(other,_TYPE)))
134
135 def __iadd__(self,other):
136 """
137 Shifts the point inplace by C{other}.
138 """
139 self.modifyBy(Translation(numarray.array(other,_TYPE)))
140 return self
141
142 def __isub__(self,other):
143 """
144 Shifts the point inplace by C{-other}.
145 """
146 self.modifyBy(Translation(-numarray.array(other,_TYPE)))
147 return self
148
149 def __imul__(self,other):
150 """
151 Modifies object by applying L{Transformation} C{other}. If C{other}
152 is not a L{Transformation} it is first tried to be converted.
153 """
154 if isinstance(other,int) or isinstance(other,float):
155 trafo=Dilation(other)
156 elif isinstance(other,numarray.NumArray):
157 trafo=Translation(other)
158 elif isinstance(other,Transformation):
159 trafo=other
160 else:
161 raise TypeError, "cannot convert argument to a Transformation class object."
162 self.modifyBy(trafo)
163 return self
164
165 def __rmul__(self,other):
166 """
167 Applies L{Transformation} C{other} to object. If C{other} is not a
168 L{Transformation} it is first tried to be converted.
169 """
170 if isinstance(other,int) or isinstance(other,float):
171 trafo=Dilation(other)
172 elif isinstance(other,numarray.NumArray):
173 trafo=Translation(other)
174 elif isinstance(other,Transformation):
175 trafo=other
176 else:
177 raise TypeError, "cannot convert argument to Transformation class object."
178 return self.apply(trafo)
179
180
181 def setLocalScale(self,factor=1.):
182 """
183 Sets the local refinement factor.
184 """
185 for p in self.getConstructionPoints(): p.setLocalScale(factor)
186
187 def apply(self,transformation):
188 """
189 Returns a new object by applying the transformation.
190 """
191 out=self.copy()
192 out.modifyBy(transformation)
193 return out
194
195 class Primitive(object):
196 """
197 Class that represents a general primitive.
198 """
199 def __init__(self):
200 """
201 Initializes the Primitive instance object with a unique ID.
202 """
203 global global_primitive_id_counter
204 self.__ID=global_primitive_id_counter
205 global_primitive_id_counter+=1
206
207 def getID(self):
208 """
209 Returns the primitive ID.
210 """
211 return self.__ID
212
213 def getDirectedID(self):
214 """
215 Returns the primitive ID where a negative sign means that reversed
216 ordering is used.
217 """
218 return self.getID()
219
220 def __repr__(self):
221 return "%s(%s)"%(self.__class__.__name__,self.getID())
222
223 def getUnderlyingPrimitive(self):
224 """
225 Returns the underlying primitive.
226 """
227 return self
228
229 def hasSameOrientation(self,other):
230 """
231 Returns True if C{other} is the same primitive and has the same
232 orientation, False otherwise.
233 """
234 return self == other and isinstance(other,Primitive)
235
236 def __neg__(self):
237 """
238 Returns a view onto the curve with reversed ordering.
239
240 @note: This method is overwritten by subclasses.
241 """
242 raise NotImplementedError("__neg__ is not implemented.")
243
244 def substitute(self,sub_dict):
245 """
246 Returns a copy of self with substitutes for the primitives used to
247 construct it given by the dictionary C{sub_dict}. If a substitute for
248 the object is given by C{sub_dict} the value is returned, otherwise a
249 new instance with substituted arguments is returned.
250
251 @note: This method is overwritten by subclasses.
252 """
253 raise NotImplementedError("substitute is not implemented.")
254
255 def collectPrimitiveBases(self):
256 """
257 Returns a list of primitives used to construct the primitive. It may
258 contain primitives twice.
259
260 @note: This method is overwritten by subclasses.
261 """
262 raise NotImplementedError("collectPrimitiveBases is not implemented.")
263
264 def isColocated(self,primitive):
265 """
266 Rreturns True if the two primitives are located at the same position.
267
268 @note: This method is overwritten by subclasses.
269 """
270 raise NotImplementedError("isColocated is not implemented.")
271
272
273 class ReversePrimitive(object):
274 """
275 A view onto a primitive creating a reverse orientation.
276 """
277 def __init__(self,primitive):
278 """
279 Instantiates a view onto C{primitive}.
280 """
281 if not isinstance(primitive, Primitive):
282 raise ValueError("argument needs to be a Primitive class object.")
283 self.__primitive=primitive
284
285 def getID(self):
286 """
287 Returns the primitive ID.
288 """
289 return self.__primitive.getID()
290
291 def getUnderlyingPrimitive(self):
292 """
293 Returns the underlying primitive.
294 """
295 return self.__primitive
296
297 def hasSameOrientation(self,other):
298 """
299 Returns True if C{other} is the same primitive and has the same
300 orientation as self.
301 """
302 return self == other and isinstance(other,ReversePrimitive)
303
304 def __repr__(self):
305 return "-%s(%s)"%(self.__primitive.__class__.__name__,self.getID())
306
307 def getDirectedID(self):
308 """
309 Returns the primitive ID where a negative signs means that reversed
310 ordering is used.
311 """
312 return -self.__primitive.getID()
313
314 def substitute(self,sub_dict):
315 """
316 Returns a copy of self with substitutes for the primitives used to
317 construct it given by the dictionary C{sub_dict}. If a substitute for
318 the object is given by C{sub_dict} the value is returned, otherwise a
319 new instance with substituted arguments is returned.
320 """
321 if not sub_dict.has_key(self):
322 sub_dict[self]=-self.getUnderlyingPrimitive().substitute(sub_dict)
323 return sub_dict[self]
324
325 def __neg__(self):
326 """
327 Returns a view onto the curve with reversed ordering.
328 """
329 return self.__primitive
330
331 def collectPrimitiveBases(self):
332 """
333 Returns a list of primitives used to construct the primitive. It may
334 contain primitives twice.
335 """
336 return self.__primitive.collectPrimitiveBases()
337
338 def isColocated(self,primitive):
339 """
340 Returns True if the two primitives are located at the same position.
341
342 @note: This method is overwritten by subclasses.
343 """
344 return self.__primitive.isColocated(primitive)
345
346 class Point(Primitive, PrimitiveBase):
347 """
348 A three-dimensional point.
349 """
350 def __init__(self,x=0.,y=0.,z=0.,local_scale=1.):
351 """
352 Creates a point with coordinates C{x}, C{y}, C{z} with the local
353 refinement factor C{local_scale}.
354 """
355 PrimitiveBase.__init__(self)
356 Primitive.__init__(self)
357 self.setCoordinates(numarray.array([x,y,z],_TYPE))
358 self.setLocalScale(local_scale)
359
360 def setLocalScale(self,factor=1.):
361 """
362 Sets the local refinement factor.
363 """
364 if factor<=0.:
365 raise ValueError("scaling factor must be positive.")
366 self.__local_scale=factor
367
368 def getLocalScale(self):
369 """
370 Returns the local refinement factor.
371 """
372 return self.__local_scale
373
374 def getCoordinates(self):
375 """
376 Returns the coodinates of the point as a C{numarray.NumArray} object.
377 """
378 return self._x
379
380 def setCoordinates(self,x):
381 """
382 Sets the coodinates of the point from a C{numarray.NumArray} object C{x}.
383 """
384 if not isinstance(x, numarray.NumArray):
385 self._x=numarray.array(x,_TYPE)
386 else:
387 self._x=x
388
389 def collectPrimitiveBases(self):
390 """
391 Returns primitives used to construct the primitive.
392 """
393 return [self]
394
395 def isColocated(self,primitive):
396 """
397 Returns True if the L{Point} C{primitive} is colocated (has the same
398 coordinates) with self. That is, if
399 M{|self-primitive| <= tol * max(|self|,|primitive|)}.
400 """
401 if isinstance(primitive,Point):
402 primitive=primitive.getCoordinates()
403 c=self.getCoordinates()
404 d=c-primitive
405 if numpyImported:
406 return numpy.dot(d,d)<=getToleranceForColocation()**2*max(numpy.dot(c,c),numpy.dot(primitive,primitive))
407 else:
408 return numarray.dot(d,d)<=getToleranceForColocation()**2*max(numarray.dot(c,c),numarray.dot(primitive,primitive))
409 else:
410 return False
411
412 def substitute(self,sub_dict):
413 """
414 Returns a copy of self with substitutes for the primitives used to
415 construct it given by the dictionary C{sub_dict}. If a substitute for
416 the object is given by C{sub_dict} the value is returned, otherwise a
417 new instance with substituted arguments is returned.
418 """
419 if not sub_dict.has_key(self):
420 c=self.getCoordinates()
421 sub_dict[self]=Point(c[0],c[1],c[2],local_scale=self.getLocalScale())
422 return sub_dict[self]
423
424 def modifyBy(self,transformation):
425 """
426 Modifies the coordinates by applying the given transformation.
427 """
428 self.setCoordinates(transformation(self.getCoordinates()))
429
430 def __neg__(self):
431 """
432 Returns a view of the object with reverse orientation. As a point has
433 no direction the object itself is returned.
434 """
435 return self
436
437 class Manifold1D(PrimitiveBase):
438 """
439 General one-dimensional manifold in 1D defined by a start and end point.
440 """
441 def __init__(self):
442 """
443 Initializes the one-dimensional manifold.
444 """
445 PrimitiveBase.__init__(self)
446
447 def getStartPoint(self):
448 """
449 Returns the start point.
450 """
451 raise NotImplementedError()
452
453 def getEndPoint(self):
454 """
455 Returns the end point.
456 """
457 raise NotImplementedError()
458
459 def getBoundary(self):
460 """
461 Returns a list of the zero-dimensional manifolds forming the boundary
462 of the curve.
463 """
464 return [ self.getStartPoint(), self.getEndPoint()]
465
466 class CurveBase(Manifold1D):
467 """
468 Base class for curves. A Curve is defined by a set of control points.
469 """
470 def __init__(self):
471 """
472 Initializes the curve.
473 """
474 Manifold1D.__init__(self)
475
476 def __len__(self):
477 """
478 Returns the number of control points.
479 """
480 return len(self.getControlPoints())
481
482 def getStartPoint(self):
483 """
484 Returns the start point.
485 """
486 return self.getControlPoints()[0]
487
488 def getEndPoint(self):
489 """
490 Returns the end point.
491 """
492 return self.getControlPoints()[-1]
493
494 def getControlPoints(self):
495 """
496 Returns a list of the points.
497 """
498 raise NotImplementedError()
499
500 class Curve(CurveBase, Primitive):
501 """
502 A curve defined through a list of control points.
503 """
504 def __init__(self,*points):
505 """
506 Defines a curve from control points given by C{points}.
507 """
508 if len(points)<2:
509 raise ValueError("Curve needs at least two points")
510 i=0
511 for p in points:
512 i+=1
513 if not isinstance(p,Point): raise TypeError("%s-th argument is not a Point object."%i)
514 self.__points=points
515 CurveBase.__init__(self)
516 Primitive.__init__(self)
517
518 def getControlPoints(self):
519 """
520 Returns a list of the points.
521 """
522 return self.__points
523
524 def __neg__(self):
525 """
526 Returns a view onto the curve with reversed ordering.
527 """
528 return ReverseCurve(self)
529
530 def substitute(self,sub_dict):
531 """
532 Returns a copy of self with substitutes for the primitives used to
533 construct it given by the dictionary C{sub_dict}. If a substitute for
534 the object is given by C{sub_dict} the value is returned, otherwise a
535 new instance with substituted arguments is returned.
536 """
537 if not sub_dict.has_key(self):
538 new_p=[]
539 for p in self.getControlPoints(): new_p.append(p.substitute(sub_dict))
540 sub_dict[self]=self.__class__(*tuple(new_p))
541 return sub_dict[self]
542
543 def collectPrimitiveBases(self):
544 """
545 Returns the primitives used to construct the curve.
546 """
547 out=[self]
548 for p in self.getControlPoints(): out+=p.collectPrimitiveBases()
549 return out
550
551 def isColocated(self,primitive):
552 """
553 Returns True if curves are at the same position.
554 """
555 if hasattr(primitive,"getUnderlyingPrimitive"):
556 if isinstance(primitive.getUnderlyingPrimitive(),self.__class__):
557 if len(primitive) == len(self):
558 cp0=self.getControlPoints()
559 cp1=primitive.getControlPoints()
560 match=True
561 for i in range(len(cp0)):
562 if not cp0[i].isColocated(cp1[i]):
563 match=False
564 break
565 if not match:
566 for i in range(len(cp0)):
567 if not cp0[i].isColocated(cp1[len(cp0)-1-i]):
568 return False
569 return True
570 return False
571
572 class ReverseCurve(CurveBase, ReversePrimitive):
573 """
574 A curve defined through a list of control points.
575 """
576 def __init__(self,curve):
577 """
578 Defines a curve from control points.
579 """
580 if not isinstance(curve, Curve):
581 raise TypeError("ReverseCurve needs to be an instance of Curve")
582 CurveBase.__init__(self)
583 ReversePrimitive.__init__(self,curve)
584
585 def getControlPoints(self):
586 """
587 Returns a list of the points.
588 """
589 out=[p for p in self.getUnderlyingPrimitive().getControlPoints()]
590 out.reverse()
591 return out
592
593 class Spline(Curve):
594 """
595 A spline curve defined through a list of control points.
596 """
597 pass
598
599 class BezierCurve(Curve):
600 """
601 A Bezier curve.
602 """
603 pass
604
605 class BSpline(Curve):
606 """
607 A BSpline curve. Control points may be repeated.
608 """
609 pass
610
611 class Line(Curve):
612 """
613 A line is defined by two points.
614 """
615 def __init__(self,*points):
616 """
617 Defines a line with start and end point.
618 """
619 if len(points)!=2:
620 raise TypeError("Line needs two points")
621 Curve.__init__(self,*points)
622
623 class ArcBase(Manifold1D):
624 """
625 Base class for arcs.
626 """
627 def __init__(self):
628 """
629 Initializes the arc.
630 """
631 Manifold1D.__init__(self)
632
633 def collectPrimitiveBases(self):
634 """
635 Returns the primitives used to construct the Arc.
636 """
637 out=[self]
638 out+=self.getStartPoint().collectPrimitiveBases()
639 out+=self.getEndPoint().collectPrimitiveBases()
640 out+=self.getCenterPoint().collectPrimitiveBases()
641 return out
642
643 def getCenterPoint(self):
644 """
645 Returns the center.
646 """
647 raise NotImplementedError()
648
649 class Arc(ArcBase, Primitive):
650 """
651 Defines an arc which is strictly smaller than S{pi}.
652 """
653 def __init__(self,center,start,end):
654 """
655 Creates an arc defined by the start point, end point and center.
656 """
657 if not isinstance(center,Point): raise TypeError("center needs to be a Point object.")
658 if not isinstance(end,Point): raise TypeError("end needs to be a Point object.")
659 if not isinstance(start,Point): raise TypeError("start needs to be a Point object.")
660 if center.isColocated(end): raise TypeError("center and start point are colocated.")
661 if center.isColocated(start): raise TypeError("center end end point are colocated.")
662 if start.isColocated(end): raise TypeError("start and end are colocated.")
663 # TODO: check length of circle.
664 ArcBase.__init__(self)
665 Primitive.__init__(self)
666 self.__center=center
667 self.__start=start
668 self.__end=end
669
670 def __neg__(self):
671 """
672 Returns a view onto the curve with reversed ordering.
673 """
674 return ReverseArc(self)
675
676 def getStartPoint(self):
677 """
678 Returns the start point.
679 """
680 return self.__start
681
682 def getEndPoint(self):
683 """
684 Returns the end point.
685 """
686 return self.__end
687
688 def getCenterPoint(self):
689 """
690 Returns the center point.
691 """
692 return self.__center
693
694 def substitute(self,sub_dict):
695 """
696 Returns a copy of self with substitutes for the primitives used to
697 construct it given by the dictionary C{sub_dict}. If a substitute for
698 the object is given by C{sub_dict} the value is returned, otherwise a
699 new instance with substituted arguments is returned.
700 """
701 if not sub_dict.has_key(self):
702 sub_dict[self]=Arc(self.getCenterPoint().substitute(sub_dict),self.getStartPoint().substitute(sub_dict),self.getEndPoint().substitute(sub_dict))
703 return sub_dict[self]
704
705 def isColocated(self,primitive):
706 """
707 Returns True if curves are at the same position.
708 """
709 if hasattr(primitive,"getUnderlyingPrimitive"):
710 if isinstance(primitive.getUnderlyingPrimitive(),Arc):
711 return (self.getCenterPoint().isColocated(primitive.getCenterPoint())) and ( \
712 (self.getEndPoint().isColocated(primitive.getEndPoint()) and self.getStartPoint().isColocated(primitive.getStartPoint()) ) \
713 or (self.getEndPoint().isColocated(primitive.getStartPoint()) and self.getStartPoint().isColocated(primitive.getEndPoint()) ) )
714 return False
715
716 class ReverseArc(ArcBase, ReversePrimitive):
717 """
718 Defines an arc which is strictly smaller than S{pi}.
719 """
720 def __init__(self,arc):
721 """
722 Creates an arc defined by the start point, end point and center.
723 """
724 if not isinstance(arc, Arc):
725 raise TypeError("ReverseCurve needs to be an instance of Arc")
726 ArcBase.__init__(self)
727 ReversePrimitive.__init__(self,arc)
728
729 def getStartPoint(self):
730 """
731 Returns the start point.
732 """
733 return self.getUnderlyingPrimitive().getEndPoint()
734
735 def getEndPoint(self):
736 """
737 Returns the end point.
738 """
739 return self.getUnderlyingPrimitive().getStartPoint()
740
741 def getCenterPoint(self):
742 """
743 Returns the center point.
744 """
745 return self.getUnderlyingPrimitive().getCenterPoint()
746
747 class EllipseBase(Manifold1D):
748 """
749 Base class for ellipses.
750 """
751 def __init__(self):
752 """
753 Initializes the ellipse.
754 """
755 Manifold1D.__init__(self)
756
757 def collectPrimitiveBases(self):
758 """
759 Returns the primitives used to construct the ellipse.
760 """
761 out=[self]
762 out+=self.getStartPoint().collectPrimitiveBases()
763 out+=self.getEndPoint().collectPrimitiveBases()
764 out+=self.getCenterPoint().collectPrimitiveBases()
765 out+=self.getPointOnMainAxis().collectPrimitiveBases()
766 return out
767
768 class Ellipse(EllipseBase, Primitive):
769 """
770 Defines an ellipse which is strictly smaller than S{pi}.
771 """
772 def __init__(self,center,point_on_main_axis,start,end):
773 """
774 Creates an ellipse defined by the start point, end point, the center
775 and a point on the main axis.
776 """
777 if not isinstance(center,Point): raise TypeError("center needs to be a Point object.")
778 if not isinstance(end,Point): raise TypeError("end needs to be a Point object.")
779 if not isinstance(start,Point): raise TypeError("start needs to be a Point object.")
780 if not isinstance(point_on_main_axis,Point): raise TypeError("point on main axis needs to be a Point object.")
781 if center.isColocated(end): raise TypeError("center and start point are colocated.")
782 if center.isColocated(start): raise TypeError("center end end point are colocated.")
783 if center.isColocated(point_on_main_axis): raise TypeError("center and point on main axis are colocated.")
784 if start.isColocated(end): raise TypeError("start and end point are colocated.")
785 # TODO: check length of circle.
786 EllipseBase.__init__(self)
787 Primitive.__init__(self)
788 self.__center=center
789 self.__start=start
790 self.__end=end
791 self.__point_on_main_axis=point_on_main_axis
792
793 def __neg__(self):
794 """
795 Returns a view onto the curve with reversed ordering.
796 """
797 return ReverseEllipse(self)
798
799 def getStartPoint(self):
800 """
801 Returns the start point.
802 """
803 return self.__start
804
805 def getEndPoint(self):
806 """
807 Returns the end point.
808 """
809 return self.__end
810
811 def getCenterPoint(self):
812 """
813 Returns the center.
814 """
815 return self.__center
816
817 def getPointOnMainAxis(self):
818 """
819 Returns a point on the main axis.
820 """
821 return self.__point_on_main_axis
822
823 def substitute(self,sub_dict):
824 """
825 Returns a copy of self with substitutes for the primitives used to
826 construct it given by the dictionary C{sub_dict}. If a substitute for
827 the object is given by C{sub_dict} the value is returned, otherwise a
828 new instance with substituted arguments is returned.
829 """
830 if not sub_dict.has_key(self):
831 sub_dict[self]=Ellipse(self.getCenterPoint().substitute(sub_dict),
832 self.getPointOnMainAxis().substitute(sub_dict),
833 self.getStartPoint().substitute(sub_dict),
834 self.getEndPoint().substitute(sub_dict))
835 return sub_dict[self]
836
837
838 def isColocated(self,primitive):
839 """
840 Returns True if curves are at the same position.
841 """
842 if hasattr(primitive,"getUnderlyingPrimitive"):
843 if isinstance(primitive.getUnderlyingPrimitive(),Ellipse):
844 self_c=self.getCenterPoint().getCoordinates()
845 p=self.getPointOnMainAxis().getCoordinates()-self_c
846 q=primitive.getPointOnMainAxis().getCoordinates()-self_c
847 # are p and q orthogonal or collinear?
848 len_p=math.sqrt(p[0]**2+p[1]**2+p[2]**2)
849 len_q=math.sqrt(q[0]**2+q[1]**2+q[2]**2)
850 p_q= abs(p[0]*q[0]+p[1]*q[1]+p[2]*q[2])
851 return ((p_q <= getToleranceForColocation() * len_q * p_q) or \
852 (abs(p_q - len_q * p_q) <= getToleranceForColocation())) and \
853 self.getCenterPoint().isColocated(primitive.getCenterPoint()) and \
854 ( \
855 (self.getEndPoint().isColocated(primitive.getEndPoint()) and \
856 self.getStartPoint().isColocated(primitive.getStartPoint()) ) \
857 or \
858 (self.getEndPoint().isColocated(primitive.getStartPoint()) and \
859 self.getStartPoint().isColocated(primitive.getEndPoint())) \
860 )
861 return False
862
863 class ReverseEllipse(EllipseBase, ReversePrimitive):
864 """
865 Defines an ellipse which is strictly smaller than S{pi}.
866 """
867 def __init__(self,arc):
868 """
869 Creates an instance of a reverse view to an ellipse.
870 """
871 if not isinstance(arc, Ellipse):
872 raise TypeError("ReverseCurve needs to be an instance of Ellipse")
873 EllipseBase.__init__(self)
874 ReversePrimitive.__init__(self,arc)
875
876 def getStartPoint(self):
877 """
878 Returns the start point.
879 """
880 return self.getUnderlyingPrimitive().getEndPoint()
881
882 def getEndPoint(self):
883 """
884 Returns the end point.
885 """
886 return self.getUnderlyingPrimitive().getStartPoint()
887
888 def getCenterPoint(self):
889 """
890 Returns the center point.
891 """
892 return self.getUnderlyingPrimitive().getCenterPoint()
893
894 def getPointOnMainAxis(self):
895 """
896 Returns a point on the main axis.
897 """
898 return self.getUnderlyingPrimitive().getPointOnMainAxis()
899
900
901 class CurveLoop(Primitive, PrimitiveBase):
902 """
903 An oriented loop of one-dimensional manifolds (= curves and arcs).
904
905 The loop must be closed and the L{Manifold1D}s should be oriented
906 consistently.
907 """
908 def __init__(self,*curves):
909 """
910 Creates a polygon from a list of line curves. The curves must form a
911 closed loop.
912 """
913 if len(curves)<2:
914 raise ValueError("at least two curves have to be given.")
915 for i in range(len(curves)):
916 if not isinstance(curves[i],Manifold1D):
917 raise TypeError("%s-th argument is not a Manifold1D object."%i)
918 # for the curves a loop:
919 used=[ False for i in curves]
920 self.__curves=list(curves)
921 Primitive.__init__(self)
922 PrimitiveBase.__init__(self)
923
924 def getCurves(self):
925 """
926 Returns the curves defining the CurveLoop.
927 """
928 return self.__curves
929
930 def __neg__(self):
931 """
932 Returns a view onto the curve with reversed ordering.
933 """
934 return ReverseCurveLoop(self)
935
936 def __len__(self):
937 """
938 Returns the number of curves in the CurveLoop.
939 """
940 return len(self.getCurves())
941
942 def collectPrimitiveBases(self):
943 """
944 Returns primitives used to construct the CurveLoop.
945 """
946 out=[self]
947 for c in self.getCurves(): out+=c.collectPrimitiveBases()
948 return out
949
950 def substitute(self,sub_dict):
951 """
952 Returns a copy of self with substitutes for the primitives used to
953 construct it given by the dictionary C{sub_dict}. If a substitute for
954 the object is given by C{sub_dict} the value is returned, otherwise a
955 new instance with substituted arguments is returned.
956 """
957 if not sub_dict.has_key(self):
958 new_c=[]
959 for c in self.getCurves(): new_c.append(c.substitute(sub_dict))
960 sub_dict[self]=CurveLoop(*tuple(new_c))
961 return sub_dict[self]
962
963 def isColocated(self,primitive):
964 """
965 Returns True if each curve is colocated with a curve in C{primitive}.
966 """
967 if hasattr(primitive,"getUnderlyingPrimitive"):
968 if isinstance(primitive.getUnderlyingPrimitive(),CurveLoop):
969 if len(primitive) == len(self):
970 cp0=self.getCurves()
971 cp1=primitive.getCurves()
972 for c0 in cp0:
973 colocated = False
974 for c1 in cp1:
975 colocated = colocated or c0.isColocated(c1)
976 if not colocated: return False
977 return True
978 return False
979
980 class ReverseCurveLoop(ReversePrimitive, PrimitiveBase):
981 """
982 An oriented loop of one-dimensional manifolds (= curves and arcs).
983
984 The loop must be closed and the one-dimensional manifolds should be
985 oriented consistently.
986 """
987 def __init__(self,curve_loop):
988 """
989 Creates a polygon from a list of line curves. The curves must form a
990 closed loop.
991 """
992 if not isinstance(curve_loop, CurveLoop):
993 raise TypeError("arguments need to be an instance of CurveLoop.")
994 ReversePrimitive.__init__(self, curve_loop)
995 PrimitiveBase.__init__(self)
996
997 def getCurves(self):
998 """
999 Returns the curves defining the CurveLoop.
1000 """
1001 return [ -c for c in self.getUnderlyingPrimitive().getCurves() ]
1002
1003 def __len__(self):
1004 return len(self.getUnderlyingPrimitive())
1005
1006 #=
1007 class Manifold2D(PrimitiveBase):
1008 """
1009 General two-dimensional manifold.
1010 """
1011 def __init__(self):
1012 """
1013 Creates a two-dimensional manifold.
1014 """
1015 PrimitiveBase.__init__(self)
1016
1017 def getBoundary(self):
1018 """
1019 Returns a list of the one-dimensional manifolds forming the boundary
1020 of the surface (including holes).
1021 """
1022 raise NotImplementedError()
1023
1024 class RuledSurface(Primitive, Manifold2D):
1025 """
1026 A ruled surface, i.e. a surface that can be interpolated using transfinite
1027 interpolation.
1028 """
1029 def __init__(self,loop):
1030 """
1031 Creates a ruled surface with boundary C{loop}.
1032
1033 @param loop: L{CurveLoop} defining the boundary of the surface.
1034 """
1035 if not isinstance(loop.getUnderlyingPrimitive(),CurveLoop):
1036 raise TypeError("argument loop needs to be a CurveLoop object.")
1037 if len(loop)<2:
1038 raise ValueError("the loop must contain at least two Curves.")
1039 if len(loop)>4:
1040 raise ValueError("the loop must contain at most four Curves.")
1041 Primitive.__init__(self)
1042 Manifold2D.__init__(self)
1043 self.__loop=loop
1044
1045 def __neg__(self):
1046 """
1047 Returns a view onto the suface with reversed ordering.
1048 """
1049 return ReverseRuledSurface(self)
1050
1051 def getBoundaryLoop(self):
1052 """
1053 Returns the loop defining the outer boundary.
1054 """
1055 return self.__loop
1056
1057 def getBoundary(self):
1058 """
1059 Returns a list of the one-dimensional manifolds forming the boundary
1060 of the Surface (including holes).
1061 """
1062 return self.getBoundaryLoop().getCurves()
1063
1064 def substitute(self,sub_dict):
1065 """
1066 Returns a copy of self with substitutes for the primitives used to
1067 construct it given by the dictionary C{sub_dict}. If a substitute for
1068 the object is given by C{sub_dict} the value is returned, otherwise a
1069 new instance with substituted arguments is returned.
1070 """
1071 if not sub_dict.has_key(self):
1072 sub_dict[self]=RuledSurface(self.getBoundaryLoop().substitute(sub_dict))
1073 return sub_dict[self]
1074
1075 def isColocated(self,primitive):
1076 """
1077 Returns True if each curve is colocated with a curve in C{primitive}.
1078 """
1079 if hasattr(primitive,"getUnderlyingPrimitive"):
1080 if isinstance(primitive.getUnderlyingPrimitive(),RuledSurface):
1081 return self.getBoundaryLoop().isColocated(primitive.getBoundaryLoop())
1082 return False
1083
1084 def collectPrimitiveBases(self):
1085 """
1086 Returns primitives used to construct the Surface.
1087 """
1088 return [self] + self.getBoundaryLoop().collectPrimitiveBases()
1089
1090 def createRuledSurface(*curves):
1091 """
1092 An easier way to create a L{RuledSurface} from given curves.
1093 """
1094 return RuledSurface(CurveLoop(*curves))
1095
1096
1097 class ReverseRuledSurface(ReversePrimitive, Manifold2D):
1098 """
1099 Creates a view onto a L{RuledSurface} but with reverse orientation.
1100 """
1101 def __init__(self,surface):
1102 """
1103 Creates a polygon from a list of line curves. The curves must form a
1104 closed loop.
1105 """
1106 if not isinstance(surface, RuledSurface):
1107 raise TypeError("arguments need to be an instance of CurveLoop.")
1108 ReversePrimitive.__init__(self, surface)
1109 Manifold2D.__init__(self)
1110
1111 def getBoundaryLoop(self):
1112 """
1113 Returns the CurveLoop defining the ReverseRuledSurface.
1114 """
1115 return -self.getUnderlyingPrimitive().getBoundaryLoop()
1116
1117 def getBoundary(self):
1118 """
1119 Returns a list of the one-dimensional manifolds forming the boundary
1120 of the Surface (including holes).
1121 """
1122 return self.getBoundaryLoop().getCurves()
1123
1124 #==============================
1125 class PlaneSurface(Primitive, Manifold2D):
1126 """
1127 A plane surface with holes.
1128 """
1129 def __init__(self,loop,holes=[]):
1130 """
1131 Creates a plane surface with holes.
1132
1133 @param loop: L{CurveLoop} defining the boundary of the surface
1134 @param holes: list of L{CurveLoop}s defining holes in the surface
1135 @note: A CurveLoop defining a hole should not have any lines in common
1136 with the exterior CurveLoop.
1137 @note: A CurveLoop defining a hole should not have any lines in common
1138 with another CurveLoop defining a hole in the same surface.
1139 """
1140 if not isinstance(loop.getUnderlyingPrimitive(),CurveLoop):
1141 raise TypeError("argument loop needs to be a CurveLoop object.")
1142 for i in range(len(holes)):
1143 if not isinstance(holes[i].getUnderlyingPrimitive(), CurveLoop):
1144 raise TypeError("%i-th hole needs to be a CurveLoop object.")
1145 #TODO: check if lines and holes are in a plane
1146 #TODO: are holes really holes?
1147 Primitive.__init__(self)
1148 Manifold2D.__init__(self)
1149 self.__loop=loop
1150 self.__holes=holes
1151
1152 def getHoles(self):
1153 """
1154 Returns the holes.
1155 """
1156 return self.__holes
1157
1158 def getBoundaryLoop(self):
1159 """
1160 Returns the loop defining the boundary.
1161 """
1162 return self.__loop
1163
1164 def substitute(self,sub_dict):
1165 """
1166 Returns a copy of self with substitutes for the primitives used to
1167 construct it given by the dictionary C{sub_dict}. If a substitute for
1168 the object is given by C{sub_dict} the value is returned, otherwise a
1169 new instance with substituted arguments is returned.
1170 """
1171 if not sub_dict.has_key(self):
1172 sub_dict[self]=PlaneSurface(self.getBoundaryLoop().substitute(sub_dict),[ h.substitute(sub_dict) for h in self.getHoles()])
1173 return sub_dict[self]
1174
1175 def isColocated(self,primitive):
1176 """
1177 Returns True if each curve is colocated with a curve in C{primitive}.
1178 """
1179 if hasattr(primitive,"getUnderlyingPrimitive"):
1180 if isinstance(primitive.getUnderlyingPrimitive(),PlaneSurface):
1181 if self.getBoundaryLoop().isColocated(primitive.getBoundaryLoop()):
1182 hs0=self.getHoles()
1183 hs1=primitive.getHoles()
1184 if len(hs0) == len(hs1):
1185 for h0 in hs0:
1186 colocated = False
1187 for h1 in hs1:
1188 colocated = colocated or h0.isColocated(h1)
1189 if not colocated: return False
1190 return True
1191 return False
1192
1193 def collectPrimitiveBases(self):
1194 """
1195 Returns primitives used to construct the Surface.
1196 """
1197 out=[self] + self.getBoundaryLoop().collectPrimitiveBases()
1198 for i in self.getHoles(): out+=i.collectPrimitiveBases()
1199 return out
1200
1201 def __neg__(self):
1202 """
1203 Returns a view onto the curve with reversed ordering.
1204 """
1205 return ReversePlaneSurface(self)
1206
1207 def getBoundary(self):
1208 """
1209 Returns a list of the one-dimensional manifolds forming the boundary
1210 of the Surface (including holes).
1211 """
1212 out = []+ self.getBoundaryLoop().getCurves()
1213 for h in self.getHoles(): out+=h.getCurves()
1214 return out
1215
1216 class ReversePlaneSurface(ReversePrimitive, Manifold2D):
1217 """
1218 Creates a view onto a L{PlaneSurface} but with reverse orientation.
1219 """
1220 def __init__(self,surface):
1221 """
1222 Creates a polygon from a L{PlaneSurface}.
1223 """
1224 if not isinstance(surface, PlaneSurface):
1225 raise TypeError("arguments need to be an instance of PlaneSurface.")
1226 ReversePrimitive.__init__(self, surface)
1227 Manifold2D.__init__(self)
1228
1229 def getBoundaryLoop(self):
1230 """
1231 Returns the CurveLoop defining the ReversePlaneSurface.
1232 """
1233 return -self.getUnderlyingPrimitive().getBoundaryLoop()
1234
1235 def getHoles(self):
1236 """
1237 Returns the holes.
1238 """
1239 return [ -h for h in self.getUnderlyingPrimitive().getHoles() ]
1240
1241 def getBoundary(self):
1242 """
1243 Returns a list of the one-dimensional manifolds forming the boundary
1244 of the Surface (including holes).
1245 """
1246 out = [] + self.getBoundaryLoop().getCurves()
1247 for h in self.getHoles(): out+=h.getCurves()
1248 return out
1249
1250
1251 #=========================================================================
1252 class SurfaceLoop(Primitive, PrimitiveBase):
1253 """
1254 A loop of 2D primitives which defines the shell of a volume.
1255
1256 The loop must represent a closed shell, and the primitives should be
1257 oriented consistently.
1258 """
1259 def __init__(self,*surfaces):
1260 """
1261 Creates a surface loop.
1262 """
1263 if len(surfaces)<2:
1264 raise ValueError("at least two surfaces have to be given.")
1265 for i in range(len(surfaces)):
1266 if not isinstance(surfaces[i].getUnderlyingPrimitive(),Manifold2D):
1267 raise TypeError("%s-th argument is not a Manifold2D object."%i)
1268 self.__surfaces=list(surfaces)
1269 Primitive.__init__(self)
1270 PrimitiveBase.__init__(self)
1271
1272 def __len__(self):
1273 """
1274 Returns the number of curves in the SurfaceLoop.
1275 """
1276 return len(self.__surfaces)
1277
1278 def __neg__(self):
1279 """
1280 Returns a view onto the curve with reversed ordering.
1281 """
1282 return ReverseSurfaceLoop(self)
1283
1284 def getSurfaces(self):
1285 """
1286 Returns the surfaces defining the SurfaceLoop.
1287 """
1288 return self.__surfaces
1289
1290 def collectPrimitiveBases(self):
1291 """
1292 Returns primitives used to construct the SurfaceLoop.
1293 """
1294 out=[self]
1295 for c in self.getSurfaces(): out+=c.collectPrimitiveBases()
1296 return out
1297
1298 def substitute(self,sub_dict):
1299 """
1300 Returns a copy of self with substitutes for the primitives used to
1301 construct it given by the dictionary C{sub_dict}. If a substitute for
1302 the object is given by C{sub_dict} the value is returned, otherwise a
1303 new instance with substituted arguments is returned.
1304 """
1305 if not sub_dict.has_key(self):
1306 new_s=[]
1307 for s in self.getSurfaces(): new_s.append(s.substitute(sub_dict))
1308 sub_dict[self]=SurfaceLoop(*tuple(new_s))
1309 return sub_dict[self]
1310
1311 def isColocated(self,primitive):
1312 """
1313 Returns True if each surface is colocated with a curve in C{primitive}
1314 and vice versa.
1315 """
1316 if hasattr(primitive,"getUnderlyingPrimitive"):
1317 if isinstance(primitive.getUnderlyingPrimitive(),SurfaceLoop):
1318 if len(primitive) == len(self):
1319 sp0=self.getSurfaces()
1320 sp1=primitive.getSurfaces()
1321 for s0 in sp0:
1322 colocated = False
1323 for s1 in sp1:
1324 colocated = colocated or s0.isColocated(s1)
1325 if not colocated: return False
1326 return True
1327 return False
1328
1329 class ReverseSurfaceLoop(ReversePrimitive, PrimitiveBase):
1330 """
1331 A view of a SurfaceLoop with reverse orientation.
1332
1333 The loop must represent a closed shell and the primitives should be
1334 oriented consistently.
1335 """
1336 def __init__(self,surface_loop):
1337 """
1338 Creates a polygon from a list of line surfaces. The curves must form
1339 a closed loop.
1340 """
1341 if not isinstance(surface_loop, SurfaceLoop):
1342 raise TypeError("arguments need to be an instance of SurfaceLoop.")
1343 ReversePrimitive.__init__(self, surface_loop)
1344 PrimitiveBase.__init__(self)
1345
1346 def getSurfaces(self):
1347 """
1348 Returns the surfaces defining the SurfaceLoop.
1349 """
1350 return [ -s for s in self.getUnderlyingPrimitive().getSurfaces() ]
1351
1352 def __len__(self):
1353 return len(self.getUnderlyingPrimitive())
1354
1355 #==============================
1356 class Manifold3D(PrimitiveBase):
1357 """
1358 General three-dimensional manifold.
1359 """
1360 def __init__(self):
1361 """
1362 Creates a three-dimensional manifold.
1363 """
1364 PrimitiveBase.__init__(self)
1365
1366 def getBoundary(self):
1367 """
1368 Returns a list of the one-dimensional manifolds forming the boundary
1369 of the volume (including holes).
1370 """
1371 raise NotImplementedError()
1372
1373 class Volume(Manifold3D, Primitive):
1374 """
1375 A volume with holes.
1376 """
1377 def __init__(self,loop,holes=[]):
1378 """
1379 Creates a volume with holes.
1380
1381 @param loop: L{SurfaceLoop} defining the boundary of the surface
1382 @param holes: list of L{SurfaceLoop} defining holes in the surface
1383 @note: A SurfaceLoop defining a hole should not have any surfaces in
1384 common with the exterior SurfaceLoop.
1385 @note: A SurfaceLoop defining a hole should not have any surfaces in
1386 common with another SurfaceLoop defining a hole in the same
1387 volume.
1388 """
1389 if not isinstance(loop.getUnderlyingPrimitive(), SurfaceLoop):
1390 raise TypeError("argument loop needs to be a SurfaceLoop object.")
1391 for i in range(len(holes)):
1392 if not isinstance(holes[i].getUnderlyingPrimitive(), SurfaceLoop):
1393 raise TypeError("%i th hole needs to be a SurfaceLoop object.")
1394 Primitive.__init__(self)
1395 Manifold3D.__init__(self)
1396 self.__loop=loop
1397 self.__holes=holes
1398
1399 def getHoles(self):
1400 """
1401 Returns the holes in the volume.
1402 """
1403 return self.__holes
1404
1405 def getSurfaceLoop(self):
1406 """
1407 Returns the loop forming the surface.
1408 """
1409 return self.__loop
1410
1411 def substitute(self,sub_dict):
1412 """
1413 Returns a copy of self with substitutes for the primitives used to
1414 construct it given by the dictionary C{sub_dict}. If a substitute for
1415 the object is given by C{sub_dict} the value is returned, otherwise a
1416 new instance with substituted arguments is returned.
1417 """
1418 if not sub_dict.has_key(self):
1419 sub_dict[self]=Volume(self.getSurfaceLoop().substitute(sub_dict),[ h.substitute(sub_dict) for h in self.getHoles()])
1420 return sub_dict[self]
1421
1422 def isColocated(self,primitive):
1423 """
1424 Returns True if each curve is colocated with a curve in C{primitive}.
1425 """
1426 if hasattr(primitive,"getUnderlyingPrimitive"):
1427 if isinstance(primitive.getUnderlyingPrimitive(),Volume):
1428 if self.getSurfaceLoop().isColocated(primitive.getSurfaceLoop()):
1429 hs0=self.getHoles()
1430 hs1=primitive.getHoles()
1431 if len(hs0) == len(hs1):
1432 for h0 in hs0:
1433 colocated = False
1434 for h1 in hs1:
1435 colocated = colocated or h0.isColocated(h1)
1436 if not colocated: return False
1437 return True
1438 return False
1439
1440 def collectPrimitiveBases(self):
1441 """
1442 Returns primitives used to construct the surface.
1443 """
1444 out=[self] + self.getSurfaceLoop().collectPrimitiveBases()
1445 for i in self.getHoles(): out+=i.collectPrimitiveBases()
1446 return out
1447
1448 def getBoundary(self):
1449 """
1450 Returns a list of the one-dimensional manifolds forming the boundary
1451 of the Surface (including holes).
1452 """
1453 out = []+ self.getSurfaceLoop().getSurfaces()
1454 for h in self.getHoles(): out+=h.getSurfaces()
1455 return out
1456
1457 class PropertySet(Primitive, PrimitiveBase):
1458 """
1459 Defines a group of L{Primitive}s which can be accessed through a name.
1460 """
1461 def __init__(self,name,*items):
1462 Primitive.__init__(self)
1463 self.__dim=None
1464 self.clearItems()
1465 self.addItem(*items)
1466 self.setName(name)
1467
1468 def getDim(self):
1469 """
1470 Returns the dimensionality of the items.
1471 """
1472 if self.__dim == None:
1473 items=self.getItems()
1474 if len(items)>0:
1475 if isinstance(items[0] ,Manifold1D):
1476 self.__dim=1
1477 elif isinstance(items[0] ,Manifold2D):
1478 self.__dim=2
1479 elif isinstance(items[0] ,Manifold3D):
1480 self.__dim=3
1481 else:
1482 self.__dim=0
1483 return self.__dim
1484
1485 def __repr__(self):
1486 """
1487 Returns a string representation.
1488 """
1489 return "%s(%s)"%(self.getName(),self.getID())
1490
1491 def getManifoldClass(self):
1492 """
1493 Returns the manifold class expected from items.
1494 """
1495 d=self.getDim()
1496 if d == None:
1497 raise ValueError("undefined spatial diemnsion.")
1498 else:
1499 if d==0:
1500 return Point
1501 elif d==1:
1502 return Manifold1D
1503 elif d==2:
1504 return Manifold2D
1505 else:
1506 return Manifold3D
1507
1508 def getName(self):
1509 """
1510 Returns the name of the set.
1511 """
1512 return self.__name
1513
1514 def setName(self,name):
1515 """
1516 Sets the name.
1517 """
1518 self.__name=str(name)
1519
1520 def addItems(self,*items):
1521 """
1522 Adds items. An item my be any L{Primitive} but no L{PropertySet}.
1523 """
1524 self.addItem(*items)
1525
1526 def addItem(self,*items):
1527 """
1528 Adds items. An item my be any L{Primitive} but no L{PropertySet}.
1529 """
1530 for i in items:
1531 if not i in self.__items:
1532 if len(self.__items)>0:
1533 m=self.getManifoldClass()
1534 if not isinstance(i, m):
1535 raise TypeError("argument %s is not a %s class object."%(i, m.__name__))
1536 self.__items.append(i)
1537
1538 def getNumItems(self):
1539 """
1540 Returns the number of items in the property set.
1541 """
1542 return len(self.__items)
1543
1544 def getItems(self):
1545 """
1546 Returns the list of items.
1547 """
1548 return self.__items
1549
1550 def clearItems(self):
1551 """
1552 Clears the list of items.
1553 """
1554 self.__items=[]
1555
1556 def collectPrimitiveBases(self):
1557 """
1558 Returns primitives used to construct the PropertySet.
1559 """
1560 out=[self]
1561 for i in self.getItems(): out+=i.collectPrimitiveBases()
1562 return out
1563
1564 def getTag(self):
1565 """
1566 Returns the tag used for this property set.
1567 """
1568 return self.getID()
1569

  ViewVC Help
Powered by ViewVC 1.1.26