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

  ViewVC Help
Powered by ViewVC 1.1.26