/[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 1312 - (show annotations)
Mon Sep 24 06:18:44 2007 UTC (12 years, 1 month ago) by ksteube
File MIME type: text/x-python
File size: 49275 byte(s)
The MPI branch is hereby closed. All future work should be in trunk.

Previously in revision 1295 I merged the latest changes to trunk into trunk-mpi-branch.
In this revision I copied all files from trunk-mpi-branch over the corresponding
trunk files. I did not use 'svn merge', it was a copy.

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

  ViewVC Help
Powered by ViewVC 1.1.26