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

  ViewVC Help
Powered by ViewVC 1.1.26