/[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 1809 - (show annotations)
Thu Sep 25 06:43:44 2008 UTC (11 years ago) by ksteube
File MIME type: text/x-python
File size: 49469 byte(s)
Copyright updated in all python files

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

  ViewVC Help
Powered by ViewVC 1.1.26