/[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 2700 - (show annotations)
Wed Sep 30 08:28:55 2009 UTC (10 years ago) by gross
File MIME type: text/x-python
File size: 58891 byte(s)
pycad test fixed.
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 ``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 ``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 ``-other``.
132 """
133 return self.apply(Translation(-numpy.array(other,_TYPE)))
134
135 def __iadd__(self,other):
136 """
137 Shifts the point inplace by ``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 ``-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 `Transformation` ``other``. If ``other``
152 is not a `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 `Transformation` ``other`` to object. If ``other`` is not a
168 `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 ``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 ``sub_dict``. If a substitute for
248 the object is given by ``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 ``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 ``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 ``sub_dict``. If a substitute for
318 the object is given by ``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 ``x``, ``y``, ``z`` with the local
353 refinement factor ``local_scale``. If ``x`` is a list or similar it needs to have
354 length less or equal 3. In this case ``y`` and ``z`` are overwritten by
355 ``x[1]`` and ``x[2]``.
356 """
357 PrimitiveBase.__init__(self)
358 Primitive.__init__(self)
359 try:
360 l=len(x)
361 if l>3:
362 raise ValueError,"x has a lanegth bigger than 3."
363 if l>1:
364 y=x[1]
365 else:
366 y=0.
367 if l>2:
368 z=x[2]
369 else:
370 z=0.
371 if l>0:
372 x=x[0]
373 else:
374 x=0.
375 except TypeError:
376 pass
377 a=numpy.array([x,y,z], _TYPE)
378 self.setCoordinates(a)
379 self.setLocalScale(local_scale)
380
381 def setLocalScale(self,factor=1.):
382 """
383 Sets the local refinement factor.
384 """
385 if factor<=0.:
386 raise ValueError("scaling factor must be positive.")
387 self.__local_scale=factor
388
389 def getLocalScale(self):
390 """
391 Returns the local refinement factor.
392 """
393 return self.__local_scale
394
395 def getCoordinates(self):
396 """
397 Returns the coodinates of the point as a ``numpy.ndarray`` object.
398 """
399 return self._x
400
401 def setCoordinates(self,x):
402 """
403 Sets the coodinates of the point from a ``numpy.ndarray`` object ``x``.
404 """
405 if not isinstance(x, numpy.ndarray):
406 self._x=numpy.array(x,_TYPE)
407 else:
408 self._x=x
409
410 def collectPrimitiveBases(self):
411 """
412 Returns primitives used to construct the primitive.
413 """
414 return [self]
415
416 def isColocated(self,primitive):
417 """
418 Returns True if the `Point` ``primitive`` is colocated (has the same
419 coordinates) with self. That is, if
420 *|self - primitive| <= tol * max(\|self\|,|primitive|)*.
421 """
422 if isinstance(primitive,Point):
423 primitive=primitive.getCoordinates()
424 c=self.getCoordinates()
425 d=c-primitive
426 if numpyImported:
427 return numpy.dot(d,d)<=getToleranceForColocation()**2*max(numpy.dot(c,c),numpy.dot(primitive,primitive))
428 else:
429 return numpy.dot(d,d)<=getToleranceForColocation()**2*max(numpy.dot(c,c),numpy.dot(primitive,primitive))
430 else:
431 return False
432
433 def substitute(self,sub_dict):
434 """
435 Returns a copy of self with substitutes for the primitives used to
436 construct it given by the dictionary ``sub_dict``. If a substitute for
437 the object is given by ``sub_dict`` the value is returned, otherwise a
438 new instance with substituted arguments is returned.
439 """
440 if not sub_dict.has_key(self):
441 c=self.getCoordinates()
442 sub_dict[self]=Point(c[0],c[1],c[2],local_scale=self.getLocalScale())
443 return sub_dict[self]
444
445 def modifyBy(self,transformation):
446 """
447 Modifies the coordinates by applying the given transformation.
448 """
449 self.setCoordinates(transformation(self.getCoordinates()))
450
451 def __neg__(self):
452 """
453 Returns a view of the object with reverse orientation. As a point has
454 no direction the object itself is returned.
455 """
456 return self
457
458 class Manifold1D(PrimitiveBase):
459 """
460 General one-dimensional manifold in 1D defined by a start and end point.
461 """
462 def __init__(self):
463 """
464 Initializes the one-dimensional manifold.
465 """
466 PrimitiveBase.__init__(self)
467 self.resetElementDistribution()
468
469 def getStartPoint(self):
470 """
471 Returns the start point.
472 """
473 raise NotImplementedError()
474
475 def getEndPoint(self):
476 """
477 Returns the end point.
478 """
479 raise NotImplementedError()
480
481 def getBoundary(self):
482 """
483 Returns a list of the zero-dimensional manifolds forming the boundary
484 of the curve.
485 """
486 return [ self.getStartPoint(), self.getEndPoint()]
487
488 def setElementDistribution(self,n,progression=1,createBump=False):
489 """
490 Defines the number of elements on the line. If set it overwrites the local length setting which would be applied.
491 The progression factor ``progression`` defines the change of element size between naighboured elements. If ``createBump`` is set
492 progression is applied towards the center of the line.
493
494 :param n: number of elements on the line
495 :type n: ``int``
496 :param progression: a positive progression factor
497 :type progression: positive ``float``
498 :param createBump: of elements on the line
499 :type createBump: ``bool``
500 """
501 if n<1:
502 raise ValueError,"number of elements must be positive."
503 if progression<=0:
504 raise ValueError,"progression factor must be positive."
505 self.__apply_elements=True
506 self.__n=n
507 self.__progression_factor=progression
508 self.__createBump=createBump
509
510 def resetElementDistribution(self):
511 """
512 removes the a previously set element distribution from the line.
513 """
514 self.__apply_elements=False
515
516 def getElementDistribution(self):
517 """
518 Returns the element distribution.
519
520 :return: the tuple of the number of elements, the progression factor and the bump flag. If no element distribution is set ``None`` is returned
521 :rtype: ``tuple``
522 """
523 if self.__apply_elements:
524 return (self.__n, self.__progression_factor, self.__createBump)
525 else:
526 return None
527
528 class CurveBase(Manifold1D):
529 """
530 Base class for curves. A Curve is defined by a set of control points.
531 """
532 def __init__(self):
533 """
534 Initializes the curve.
535 """
536 Manifold1D.__init__(self)
537
538 def __len__(self):
539 """
540 Returns the number of control points.
541 """
542 return len(self.getControlPoints())
543
544 def getStartPoint(self):
545 """
546 Returns the start point.
547 """
548 return self.getControlPoints()[0]
549
550 def getEndPoint(self):
551 """
552 Returns the end point.
553 """
554 return self.getControlPoints()[-1]
555
556 def getControlPoints(self):
557 """
558 Returns a list of the points.
559 """
560 raise NotImplementedError()
561
562 class Curve(CurveBase, Primitive):
563 """
564 A curve defined through a list of control points.
565 """
566 def __init__(self,*points):
567 """
568 Defines a curve from control points given by ``points``.
569 """
570 if len(points)==1:
571 points=points[0]
572 if not hasattr(points,'__iter__'): raise ValueError("Curve needs at least two points")
573 if len(points)<2:
574 raise ValueError("Curve needs at least two points")
575 i=0
576 for p in points:
577 i+=1
578 if not isinstance(p,Point): raise TypeError("%s-th argument is not a Point object."%i)
579 self.__points=points
580 CurveBase.__init__(self)
581 Primitive.__init__(self)
582
583 def getControlPoints(self):
584 """
585 Returns a list of the points.
586 """
587 return self.__points
588
589 def __neg__(self):
590 """
591 Returns a view onto the curve with reversed ordering.
592 """
593 return ReverseCurve(self)
594
595 def substitute(self,sub_dict):
596 """
597 Returns a copy of self with substitutes for the primitives used to
598 construct it given by the dictionary ``sub_dict``. If a substitute for
599 the object is given by ``sub_dict`` the value is returned, otherwise a
600 new instance with substituted arguments is returned.
601 """
602 if not sub_dict.has_key(self):
603 new_p=[]
604 for p in self.getControlPoints(): new_p.append(p.substitute(sub_dict))
605 sub_dict[self]=self.__class__(*tuple(new_p))
606 return sub_dict[self]
607
608 def collectPrimitiveBases(self):
609 """
610 Returns the primitives used to construct the curve.
611 """
612 out=[self]
613 for p in self.getControlPoints(): out+=p.collectPrimitiveBases()
614 return out
615
616 def isColocated(self,primitive):
617 """
618 Returns True if curves are at the same position.
619 """
620 if hasattr(primitive,"getUnderlyingPrimitive"):
621 if isinstance(primitive.getUnderlyingPrimitive(),self.__class__):
622 if len(primitive) == len(self):
623 cp0=self.getControlPoints()
624 cp1=primitive.getControlPoints()
625 match=True
626 for i in range(len(cp0)):
627 if not cp0[i].isColocated(cp1[i]):
628 match=False
629 break
630 if not match:
631 for i in range(len(cp0)):
632 if not cp0[i].isColocated(cp1[len(cp0)-1-i]):
633 return False
634 return True
635 return False
636
637 class ReverseCurve(CurveBase, ReversePrimitive):
638 """
639 A curve defined through a list of control points.
640 """
641 def __init__(self,curve):
642 """
643 Defines a curve from control points.
644 """
645 if not isinstance(curve, Curve):
646 raise TypeError("ReverseCurve needs to be an instance of Curve")
647 CurveBase.__init__(self)
648 ReversePrimitive.__init__(self,curve)
649
650 def getControlPoints(self):
651 """
652 Returns a list of the points.
653 """
654 out=[p for p in self.getUnderlyingPrimitive().getControlPoints()]
655 out.reverse()
656 return tuple(out)
657
658 class Spline(Curve):
659 """
660 A spline curve defined through a list of control points.
661 """
662 pass
663
664 class BezierCurve(Curve):
665 """
666 A Bezier curve.
667 """
668 pass
669
670 class BSpline(Curve):
671 """
672 A BSpline curve. Control points may be repeated.
673 """
674 pass
675
676 class Line(Curve):
677 """
678 A line is defined by two points.
679 """
680 def __init__(self,*points):
681 """
682 Defines a line with start and end point.
683 """
684 if len(points)!=2:
685 raise TypeError("Line needs two points")
686 Curve.__init__(self,*points)
687
688 class ArcBase(Manifold1D):
689 """
690 Base class for arcs.
691 """
692 def __init__(self):
693 """
694 Initializes the arc.
695 """
696 Manifold1D.__init__(self)
697
698 def collectPrimitiveBases(self):
699 """
700 Returns the primitives used to construct the Arc.
701 """
702 out=[self]
703 out+=self.getStartPoint().collectPrimitiveBases()
704 out+=self.getEndPoint().collectPrimitiveBases()
705 out+=self.getCenterPoint().collectPrimitiveBases()
706 return out
707
708 def getCenterPoint(self):
709 """
710 Returns the center.
711 """
712 raise NotImplementedError()
713
714 class Arc(ArcBase, Primitive):
715 """
716 Defines an arc which is strictly smaller than pi.
717 """
718 def __init__(self,center,start,end):
719 """
720 Creates an arc defined by the start point, end point and center.
721 """
722 if not isinstance(center,Point): raise TypeError("center needs to be a Point object.")
723 if not isinstance(end,Point): raise TypeError("end needs to be a Point object.")
724 if not isinstance(start,Point): raise TypeError("start needs to be a Point object.")
725 if center.isColocated(end): raise TypeError("center and start point are colocated.")
726 if center.isColocated(start): raise TypeError("center end end point are colocated.")
727 if start.isColocated(end): raise TypeError("start and end are colocated.")
728 # TODO: check length of circle.
729 ArcBase.__init__(self)
730 Primitive.__init__(self)
731 self.__center=center
732 self.__start=start
733 self.__end=end
734
735 def __neg__(self):
736 """
737 Returns a view onto the curve with reversed ordering.
738 """
739 return ReverseArc(self)
740
741 def getStartPoint(self):
742 """
743 Returns the start point.
744 """
745 return self.__start
746
747 def getEndPoint(self):
748 """
749 Returns the end point.
750 """
751 return self.__end
752
753 def getCenterPoint(self):
754 """
755 Returns the center point.
756 """
757 return self.__center
758
759 def substitute(self,sub_dict):
760 """
761 Returns a copy of self with substitutes for the primitives used to
762 construct it given by the dictionary ``sub_dict``. If a substitute for
763 the object is given by ``sub_dict`` the value is returned, otherwise a
764 new instance with substituted arguments is returned.
765 """
766 if not sub_dict.has_key(self):
767 sub_dict[self]=Arc(self.getCenterPoint().substitute(sub_dict),self.getStartPoint().substitute(sub_dict),self.getEndPoint().substitute(sub_dict))
768 return sub_dict[self]
769
770 def isColocated(self,primitive):
771 """
772 Returns True if curves are at the same position.
773 """
774 if hasattr(primitive,"getUnderlyingPrimitive"):
775 if isinstance(primitive.getUnderlyingPrimitive(),Arc):
776 return (self.getCenterPoint().isColocated(primitive.getCenterPoint())) and ( \
777 (self.getEndPoint().isColocated(primitive.getEndPoint()) and self.getStartPoint().isColocated(primitive.getStartPoint()) ) \
778 or (self.getEndPoint().isColocated(primitive.getStartPoint()) and self.getStartPoint().isColocated(primitive.getEndPoint()) ) )
779 return False
780
781 class ReverseArc(ArcBase, ReversePrimitive):
782 """
783 Defines an arc which is strictly smaller than pi.
784 """
785 def __init__(self,arc):
786 """
787 Creates an arc defined by the start point, end point and center.
788 """
789 if not isinstance(arc, Arc):
790 raise TypeError("ReverseCurve needs to be an instance of Arc")
791 ArcBase.__init__(self)
792 ReversePrimitive.__init__(self,arc)
793
794 def getStartPoint(self):
795 """
796 Returns the start point.
797 """
798 return self.getUnderlyingPrimitive().getEndPoint()
799
800 def getEndPoint(self):
801 """
802 Returns the end point.
803 """
804 return self.getUnderlyingPrimitive().getStartPoint()
805
806 def getCenterPoint(self):
807 """
808 Returns the center point.
809 """
810 return self.getUnderlyingPrimitive().getCenterPoint()
811
812 class EllipseBase(Manifold1D):
813 """
814 Base class for ellipses.
815 """
816 def __init__(self):
817 """
818 Initializes the ellipse.
819 """
820 Manifold1D.__init__(self)
821
822 def collectPrimitiveBases(self):
823 """
824 Returns the primitives used to construct the ellipse.
825 """
826 out=[self]
827 out+=self.getStartPoint().collectPrimitiveBases()
828 out+=self.getEndPoint().collectPrimitiveBases()
829 out+=self.getCenterPoint().collectPrimitiveBases()
830 out+=self.getPointOnMainAxis().collectPrimitiveBases()
831 return out
832
833 class Ellipse(EllipseBase, Primitive):
834 """
835 Defines an ellipse which is strictly smaller than pi.
836 """
837 def __init__(self,center,point_on_main_axis,start,end):
838 """
839 Creates an ellipse defined by the start point, end point, the center
840 and a point on the main axis.
841 """
842 if not isinstance(center,Point): raise TypeError("center needs to be a Point object.")
843 if not isinstance(end,Point): raise TypeError("end needs to be a Point object.")
844 if not isinstance(start,Point): raise TypeError("start needs to be a Point object.")
845 if not isinstance(point_on_main_axis,Point): raise TypeError("point on main axis needs to be a Point object.")
846 if center.isColocated(end): raise TypeError("center and start point are colocated.")
847 if center.isColocated(start): raise TypeError("center end end point are colocated.")
848 if center.isColocated(point_on_main_axis): raise TypeError("center and point on main axis are colocated.")
849 if start.isColocated(end): raise TypeError("start and end point are colocated.")
850 # TODO: check length of circle.
851 EllipseBase.__init__(self)
852 Primitive.__init__(self)
853 self.__center=center
854 self.__start=start
855 self.__end=end
856 self.__point_on_main_axis=point_on_main_axis
857
858 def __neg__(self):
859 """
860 Returns a view onto the curve with reversed ordering.
861 """
862 return ReverseEllipse(self)
863
864 def getStartPoint(self):
865 """
866 Returns the start point.
867 """
868 return self.__start
869
870 def getEndPoint(self):
871 """
872 Returns the end point.
873 """
874 return self.__end
875
876 def getCenterPoint(self):
877 """
878 Returns the center.
879 """
880 return self.__center
881
882 def getPointOnMainAxis(self):
883 """
884 Returns a point on the main axis.
885 """
886 return self.__point_on_main_axis
887
888 def substitute(self,sub_dict):
889 """
890 Returns a copy of self with substitutes for the primitives used to
891 construct it given by the dictionary ``sub_dict``. If a substitute for
892 the object is given by ``sub_dict`` the value is returned, otherwise a
893 new instance with substituted arguments is returned.
894 """
895 if not sub_dict.has_key(self):
896 sub_dict[self]=Ellipse(self.getCenterPoint().substitute(sub_dict),
897 self.getPointOnMainAxis().substitute(sub_dict),
898 self.getStartPoint().substitute(sub_dict),
899 self.getEndPoint().substitute(sub_dict))
900 return sub_dict[self]
901
902
903 def isColocated(self,primitive):
904 """
905 Returns True if curves are at the same position.
906 """
907 if hasattr(primitive,"getUnderlyingPrimitive"):
908 if isinstance(primitive.getUnderlyingPrimitive(),Ellipse):
909 self_c=self.getCenterPoint().getCoordinates()
910 p=self.getPointOnMainAxis().getCoordinates()-self_c
911 q=primitive.getPointOnMainAxis().getCoordinates()-self_c
912 # are p and q orthogonal or collinear?
913 len_p=math.sqrt(p[0]**2+p[1]**2+p[2]**2)
914 len_q=math.sqrt(q[0]**2+q[1]**2+q[2]**2)
915 p_q= abs(p[0]*q[0]+p[1]*q[1]+p[2]*q[2])
916 return ((p_q <= getToleranceForColocation() * len_q * p_q) or \
917 (abs(p_q - len_q * p_q) <= getToleranceForColocation())) and \
918 self.getCenterPoint().isColocated(primitive.getCenterPoint()) and \
919 ( \
920 (self.getEndPoint().isColocated(primitive.getEndPoint()) and \
921 self.getStartPoint().isColocated(primitive.getStartPoint()) ) \
922 or \
923 (self.getEndPoint().isColocated(primitive.getStartPoint()) and \
924 self.getStartPoint().isColocated(primitive.getEndPoint())) \
925 )
926 return False
927
928 class ReverseEllipse(EllipseBase, ReversePrimitive):
929 """
930 Defines an ellipse which is strictly smaller than pi.
931 """
932 def __init__(self,arc):
933 """
934 Creates an instance of a reverse view to an ellipse.
935 """
936 if not isinstance(arc, Ellipse):
937 raise TypeError("ReverseCurve needs to be an instance of Ellipse")
938 EllipseBase.__init__(self)
939 ReversePrimitive.__init__(self,arc)
940
941 def getStartPoint(self):
942 """
943 Returns the start point.
944 """
945 return self.getUnderlyingPrimitive().getEndPoint()
946
947 def getEndPoint(self):
948 """
949 Returns the end point.
950 """
951 return self.getUnderlyingPrimitive().getStartPoint()
952
953 def getCenterPoint(self):
954 """
955 Returns the center point.
956 """
957 return self.getUnderlyingPrimitive().getCenterPoint()
958
959 def getPointOnMainAxis(self):
960 """
961 Returns a point on the main axis.
962 """
963 return self.getUnderlyingPrimitive().getPointOnMainAxis()
964
965
966 class CurveLoop(Primitive, PrimitiveBase):
967 """
968 An oriented loop of one-dimensional manifolds (= curves and arcs).
969
970 The loop must be closed and the `Manifold1D` s should be oriented
971 consistently.
972 """
973 def __init__(self,*curves):
974 """
975 Creates a polygon from a list of line curves. The curves must form a
976 closed loop.
977 """
978 if len(curves)==1:
979 curves=curves[0]
980 if not hasattr(curves,'__iter__'): raise ValueError("CurveLoop needs at least two points")
981 if len(curves)<2:
982 raise ValueError("At least two curves have to be given.")
983 for i in range(len(curves)):
984 if not isinstance(curves[i],Manifold1D):
985 raise TypeError("%s-th argument is not a Manifold1D object."%i)
986 # for the curves a loop:
987 used=[ False for i in curves]
988 self.__curves=list(curves)
989 Primitive.__init__(self)
990 PrimitiveBase.__init__(self)
991
992 def getCurves(self):
993 """
994 Returns the curves defining the CurveLoop.
995 """
996 return self.__curves
997
998 def __neg__(self):
999 """
1000 Returns a view onto the curve with reversed ordering.
1001 """
1002 return ReverseCurveLoop(self)
1003
1004 def __len__(self):
1005 """
1006 Returns the number of curves in the CurveLoop.
1007 """
1008 return len(self.getCurves())
1009
1010 def collectPrimitiveBases(self):
1011 """
1012 Returns primitives used to construct the CurveLoop.
1013 """
1014 out=[self]
1015 for c in self.getCurves(): out+=c.collectPrimitiveBases()
1016 return out
1017
1018 def substitute(self,sub_dict):
1019 """
1020 Returns a copy of self with substitutes for the primitives used to
1021 construct it given by the dictionary ``sub_dict``. If a substitute for
1022 the object is given by ``sub_dict`` the value is returned, otherwise a
1023 new instance with substituted arguments is returned.
1024 """
1025 if not sub_dict.has_key(self):
1026 new_c=[]
1027 for c in self.getCurves(): new_c.append(c.substitute(sub_dict))
1028 sub_dict[self]=CurveLoop(*tuple(new_c))
1029 return sub_dict[self]
1030
1031 def isColocated(self,primitive):
1032 """
1033 Returns True if each curve is colocated with a curve in ``primitive``.
1034 """
1035 if hasattr(primitive,"getUnderlyingPrimitive"):
1036 if isinstance(primitive.getUnderlyingPrimitive(),CurveLoop):
1037 if len(primitive) == len(self):
1038 cp0=self.getCurves()
1039 cp1=primitive.getCurves()
1040 for c0 in cp0:
1041 colocated = False
1042 for c1 in cp1:
1043 colocated = colocated or c0.isColocated(c1)
1044 if not colocated: return False
1045 return True
1046 return False
1047
1048 class ReverseCurveLoop(ReversePrimitive, PrimitiveBase):
1049 """
1050 An oriented loop of one-dimensional manifolds (= curves and arcs).
1051
1052 The loop must be closed and the one-dimensional manifolds should be
1053 oriented consistently.
1054 """
1055 def __init__(self,curve_loop):
1056 """
1057 Creates a polygon from a list of line curves. The curves must form a
1058 closed loop.
1059 """
1060 if not isinstance(curve_loop, CurveLoop):
1061 raise TypeError("arguments need to be an instance of CurveLoop.")
1062 ReversePrimitive.__init__(self, curve_loop)
1063 PrimitiveBase.__init__(self)
1064
1065 def getCurves(self):
1066 """
1067 Returns the curves defining the CurveLoop.
1068 """
1069 return [ -c for c in self.getUnderlyingPrimitive().getCurves() ]
1070
1071 def __len__(self):
1072 return len(self.getUnderlyingPrimitive())
1073
1074 #=
1075 class Manifold2D(PrimitiveBase):
1076 """
1077 General two-dimensional manifold.
1078
1079 :ivar LEFT: left element orientation when meshing with transifinite meshing
1080 :ivar RIGHT: right element orientation when meshing with transifinite meshing
1081 :ivar ALTERNATE: alternate element orientation when meshing with transifinite meshing
1082 """
1083 LEFT="Left"
1084 RIGHT="Right"
1085 ALTERNATE="Alternate"
1086 def __init__(self):
1087 """
1088 Creates a two-dimensional manifold.
1089 """
1090 PrimitiveBase.__init__(self)
1091 self.setRecombination(None)
1092 self.resetTransfiniteMeshing()
1093
1094 def getBoundary(self):
1095 """
1096 Returns a list of the one-dimensional manifolds forming the boundary
1097 of the surface (including holes).
1098 """
1099 raise NotImplementedError()
1100
1101 def hasHole(self):
1102 """
1103 Returns True if a hole is present.
1104 """
1105 raise NotImplementedError()
1106
1107 def getPoints(self):
1108 """
1109 returns a list of points used to define the boundary
1110
1111 :return: list of points used to define the boundary
1112 :rtype: ``list`` of `Point` s
1113 """
1114 out=[]
1115 boundary=self.getBoundary()
1116 for l in boundary:
1117 for p in l.getBoundary():
1118 if not p in out: out.append(p)
1119 return out
1120
1121 def setRecombination(self, max_deviation=45*DEG):
1122 """
1123 Recombines triangular meshes on the surface into mixed triangular/quadrangular meshes.
1124 ``max_deviation`` specifies the maximum derivation of the largest angle in the quadrangle
1125 from the right angle. Use ``max_deviation``==``None`` to switch off recombination.
1126
1127 :param max_deviation: maximum derivation of the largest angle in the quadrangle from the right angle.
1128 :type max_deviation: ``float`` or ``None``.
1129 """
1130 if not max_deviation==None:
1131 if max_deviation<=0:
1132 raise ValueError, "max_deviation must be positive."
1133 if max_deviation/DEG>=90:
1134 raise ValueError, "max_deviation must be smaller than 90 DEG"
1135 self.__recombination_angle=max_deviation
1136
1137 def getRecombination(self):
1138 """
1139 returns max deviation from right angle in the recombination algorithm
1140
1141 :return: max deviation from right angle in the recombination algorithm. If recombination is switched off, ``None`` is returned.
1142 :rtype: ``float`` or ``None``
1143 """
1144 return self.__recombination_angle
1145
1146 def setTransfiniteMeshing(self,orientation="Left"):
1147 """
1148 applies 2D transfinite meshing to the surface.
1149
1150 :param orientation: sets the orientation of the triangles. It is only used if recombination is not used.
1151 :type orientation: `Manifold2D.LEFT`, `Manifold2D.RIGHT`, `Manifold2D.ALTERNATE`
1152 :note: Transfinite meshing can not be applied if holes are present.
1153 """
1154 if not orientation in [ Manifold2D.LEFT, Manifold2D.RIGHT, Manifold2D.ALTERNATE]:
1155 raise ValueError,"invalid orientation %s."%orientation
1156 if self.hasHole():
1157 raise ValueError,"transfinite meshing cannot be appled to surfaces with a hole."
1158 b=self.getBoundary()
1159 if len(b)>4 or len(b)<3:
1160 raise ValueError,"transfinite meshing permits 3 or 4 boundary lines only."
1161 for l in b:
1162 if l.getElementDistribution() == None: raise ValueError,"transfinite meshing requires element distribution on all boundary lines."
1163 start=b[0]
1164 opposite=None
1165 top=None
1166 bottom=None
1167 for l in b[1:]:
1168 if l.getEndPoint() == start.getStartPoint():
1169 bottom=l
1170 elif l.getStartPoint() == start.getEndPoint():
1171 top=l
1172 else:
1173 opposite=l
1174 if top==None or bottom == None:
1175 raise ValueError,"transfinite meshing cannot be applied to boundary is not closed. Most likely the orientation of some boundray segments is wrong."
1176 if opposite == None: # three sides only
1177 if not top.getElementDistribution() == bottom.getElementDistribution(): start, top, bottom= bottom, start, top
1178 if not top.getElementDistribution() == bottom.getElementDistribution():
1179 raise ValueError,"transfinite meshing requires oposite faces to be have the same element distribution."
1180 if not opposite == None:
1181 if not start.getElementDistribution() == opposite.getElementDistribution():
1182 raise ValueError,"transfinite meshing requires oposite faces to be have the same element distribution."
1183 if opposite == None:
1184 if bottom.getEndPoint == top.getStartPoint():
1185 raise ValueError,"cannot identify corner proints for transfinite meshing."
1186 else:
1187 points=[ bottom.getStartPoint(), bottom.getEndPoint(), top.getStartPoint() ]
1188 else:
1189 points=[ bottom.getStartPoint(), bottom.getEndPoint(), top.getStartPoint(), top.getEndPoint() ]
1190 self.__points=points
1191 self.__orientation=orientation
1192 self.__transfinitemeshing=True
1193
1194 def resetTransfiniteMeshing(self):
1195 """
1196 removes the transfinite meshing from the surface
1197 """
1198 self.__transfinitemeshing=False
1199
1200 def getTransfiniteMeshing(self):
1201 """
1202 returns the transfinite meshing setings. If transfinite meshing is not set, ``None`` is returned.
1203
1204 :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 ``None``. If no transfinite meshing is not set, ``None`` is returned.
1205 :rtype: ``tuple`` of a ``tuple`` of `Point` s (or ``None``) and the orientation which is one of the values `Manifold2D.LEFT` , `Manifold2D.RIGHT` , `Manifold2D.ALTERNATE`
1206 """
1207 if self.__transfinitemeshing:
1208 return (self.__points, self.__orientation)
1209 else:
1210 return None
1211
1212 class RuledSurface(Primitive, Manifold2D):
1213 """
1214 A ruled surface, i.e. a surface that can be interpolated using transfinite
1215 interpolation.
1216 """
1217 def __init__(self,loop):
1218 """
1219 Creates a ruled surface with boundary ``loop``.
1220
1221 :param loop: `CurveLoop` defining the boundary of the surface.
1222 """
1223 if not isinstance(loop.getUnderlyingPrimitive(),CurveLoop):
1224 raise TypeError("argument loop needs to be a CurveLoop object.")
1225 if len(loop)<2:
1226 raise ValueError("the loop must contain at least two Curves.")
1227 if len(loop)>4:
1228 raise ValueError("the loop must contain at most four Curves.")
1229 Primitive.__init__(self)
1230 Manifold2D.__init__(self)
1231 self.__loop=loop
1232
1233 def hasHole(self):
1234 """
1235 Returns True if a hole is present.
1236 """
1237 return False
1238
1239 def __neg__(self):
1240 """
1241 Returns a view onto the suface with reversed ordering.
1242 """
1243 return ReverseRuledSurface(self)
1244
1245 def getBoundaryLoop(self):
1246 """
1247 Returns the loop defining the outer boundary.
1248 """
1249 return self.__loop
1250
1251 def getBoundary(self):
1252 """
1253 Returns a list of the one-dimensional manifolds forming the boundary
1254 of the Surface (including holes).
1255 """
1256 return self.getBoundaryLoop().getCurves()
1257
1258 def substitute(self,sub_dict):
1259 """
1260 Returns a copy of self with substitutes for the primitives used to
1261 construct it given by the dictionary ``sub_dict``. If a substitute for
1262 the object is given by ``sub_dict`` the value is returned, otherwise a
1263 new instance with substituted arguments is returned.
1264 """
1265 if not sub_dict.has_key(self):
1266 sub_dict[self]=RuledSurface(self.getBoundaryLoop().substitute(sub_dict))
1267 return sub_dict[self]
1268
1269 def isColocated(self,primitive):
1270 """
1271 Returns True if each curve is colocated with a curve in ``primitive``.
1272 """
1273 if hasattr(primitive,"getUnderlyingPrimitive"):
1274 if isinstance(primitive.getUnderlyingPrimitive(),RuledSurface):
1275 return self.getBoundaryLoop().isColocated(primitive.getBoundaryLoop())
1276 return False
1277
1278 def collectPrimitiveBases(self):
1279 """
1280 Returns primitives used to construct the Surface.
1281 """
1282 return [self] + self.getBoundaryLoop().collectPrimitiveBases()
1283
1284 def createRuledSurface(*curves):
1285 """
1286 An easier way to create a `RuledSurface` from given curves.
1287 """
1288 return RuledSurface(CurveLoop(*curves))
1289
1290
1291 class ReverseRuledSurface(ReversePrimitive, Manifold2D):
1292 """
1293 Creates a view onto a `RuledSurface` but with reverse orientation.
1294 """
1295 def __init__(self,surface):
1296 """
1297 Creates a polygon from a list of line curves. The curves must form a
1298 closed loop.
1299 """
1300 if not isinstance(surface, RuledSurface):
1301 raise TypeError("arguments need to be an instance of CurveLoop.")
1302 ReversePrimitive.__init__(self, surface)
1303 Manifold2D.__init__(self)
1304
1305 def getBoundaryLoop(self):
1306 """
1307 Returns the CurveLoop defining the ReverseRuledSurface.
1308 """
1309 return -self.getUnderlyingPrimitive().getBoundaryLoop()
1310
1311 def getBoundary(self):
1312 """
1313 Returns a list of the one-dimensional manifolds forming the boundary
1314 of the Surface (including holes).
1315 """
1316 return self.getBoundaryLoop().getCurves()
1317
1318 def hasHole(self):
1319 """
1320 Returns True if a hole is present.
1321 """
1322 return False
1323
1324 #==============================
1325 class PlaneSurface(Primitive, Manifold2D):
1326 """
1327 A plane surface with holes.
1328 """
1329 def __init__(self,loop,holes=[]):
1330 """
1331 Creates a plane surface with holes.
1332
1333 :param loop: `CurveLoop` defining the boundary of the surface
1334 :param holes: list of `CurveLoop` s defining holes in the surface
1335 :note: A CurveLoop defining a hole should not have any lines in common
1336 with the exterior CurveLoop.
1337 :note: A CurveLoop defining a hole should not have any lines in common
1338 with another CurveLoop defining a hole in the same surface.
1339 """
1340 if not isinstance(loop.getUnderlyingPrimitive(),CurveLoop):
1341 raise TypeError("argument loop needs to be a CurveLoop object.")
1342 for i in range(len(holes)):
1343 if not isinstance(holes[i].getUnderlyingPrimitive(), CurveLoop):
1344 raise TypeError("%i-th hole needs to be a CurveLoop object.")
1345 #TODO: check if lines and holes are in a plane
1346 #TODO: are holes really holes?
1347 Primitive.__init__(self)
1348 Manifold2D.__init__(self)
1349 self.__loop=loop
1350 self.__holes=holes
1351
1352 def hasHole(self):
1353 """
1354 Returns True if a hole is present.
1355 """
1356 return len(self.getHoles())>0
1357
1358 def getHoles(self):
1359 """
1360 Returns the holes.
1361 """
1362 return self.__holes
1363
1364 def getBoundaryLoop(self):
1365 """
1366 Returns the loop defining the boundary.
1367 """
1368 return self.__loop
1369
1370 def substitute(self,sub_dict):
1371 """
1372 Returns a copy of self with substitutes for the primitives used to
1373 construct it given by the dictionary ``sub_dict``. If a substitute for
1374 the object is given by ``sub_dict`` the value is returned, otherwise a
1375 new instance with substituted arguments is returned.
1376 """
1377 if not sub_dict.has_key(self):
1378 sub_dict[self]=PlaneSurface(self.getBoundaryLoop().substitute(sub_dict),[ h.substitute(sub_dict) for h in self.getHoles()])
1379 return sub_dict[self]
1380
1381 def isColocated(self,primitive):
1382 """
1383 Returns True if each curve is colocated with a curve in ``primitive``.
1384 """
1385 if hasattr(primitive,"getUnderlyingPrimitive"):
1386 if isinstance(primitive.getUnderlyingPrimitive(),PlaneSurface):
1387 if self.getBoundaryLoop().isColocated(primitive.getBoundaryLoop()):
1388 hs0=self.getHoles()
1389 hs1=primitive.getHoles()
1390 if len(hs0) == len(hs1):
1391 for h0 in hs0:
1392 colocated = False
1393 for h1 in hs1:
1394 colocated = colocated or h0.isColocated(h1)
1395 if not colocated: return False
1396 return True
1397 return False
1398
1399 def collectPrimitiveBases(self):
1400 """
1401 Returns primitives used to construct the Surface.
1402 """
1403 out=[self] + self.getBoundaryLoop().collectPrimitiveBases()
1404 for i in self.getHoles(): out+=i.collectPrimitiveBases()
1405 return out
1406
1407 def __neg__(self):
1408 """
1409 Returns a view onto the curve with reversed ordering.
1410 """
1411 return ReversePlaneSurface(self)
1412
1413 def getBoundary(self):
1414 """
1415 Returns a list of the one-dimensional manifolds forming the boundary
1416 of the Surface (including holes).
1417 """
1418 out = []+ self.getBoundaryLoop().getCurves()
1419 for h in self.getHoles(): out+=h.getCurves()
1420 return out
1421
1422 class ReversePlaneSurface(ReversePrimitive, Manifold2D):
1423 """
1424 Creates a view onto a `PlaneSurface` but with reverse orientation.
1425 """
1426 def __init__(self,surface):
1427 """
1428 Creates a polygon from a `PlaneSurface`.
1429 """
1430 if not isinstance(surface, PlaneSurface):
1431 raise TypeError("arguments need to be an instance of PlaneSurface.")
1432 ReversePrimitive.__init__(self, surface)
1433 Manifold2D.__init__(self)
1434
1435 def getBoundaryLoop(self):
1436 """
1437 Returns the CurveLoop defining the ReversePlaneSurface.
1438 """
1439 return -self.getUnderlyingPrimitive().getBoundaryLoop()
1440
1441 def getHoles(self):
1442 """
1443 Returns the holes.
1444 """
1445 return [ -h for h in self.getUnderlyingPrimitive().getHoles() ]
1446
1447 def getBoundary(self):
1448 """
1449 Returns a list of the one-dimensional manifolds forming the boundary
1450 of the Surface (including holes).
1451 """
1452 out = [] + self.getBoundaryLoop().getCurves()
1453 for h in self.getHoles(): out+=h.getCurves()
1454 return out
1455
1456 def hasHole(self):
1457 """
1458 Returns True if a hole is present.
1459 """
1460 return len(self.getHoles())>0
1461
1462 #=========================================================================
1463 class SurfaceLoop(Primitive, PrimitiveBase):
1464 """
1465 A loop of 2D primitives which defines the shell of a volume.
1466
1467 The loop must represent a closed shell, and the primitives should be
1468 oriented consistently.
1469 """
1470 def __init__(self,*surfaces):
1471 """
1472 Creates a surface loop.
1473 """
1474 if len(surfaces)==1:
1475 surfaces=surfaces[0]
1476 if not hasattr(surfaces,'__iter__'): raise ValueError("SurfaceLoop needs at least two points")
1477 if len(surfaces)<2:
1478 raise ValueError("at least two surfaces have to be given.")
1479 for i in range(len(surfaces)):
1480 if not isinstance(surfaces[i].getUnderlyingPrimitive(),Manifold2D):
1481 raise TypeError("%s-th argument is not a Manifold2D object."%i)
1482 self.__surfaces=list(surfaces)
1483 Primitive.__init__(self)
1484 PrimitiveBase.__init__(self)
1485
1486 def __len__(self):
1487 """
1488 Returns the number of curves in the SurfaceLoop.
1489 """
1490 return len(self.__surfaces)
1491
1492 def __neg__(self):
1493 """
1494 Returns a view onto the curve with reversed ordering.
1495 """
1496 return ReverseSurfaceLoop(self)
1497
1498 def getSurfaces(self):
1499 """
1500 Returns the surfaces defining the SurfaceLoop.
1501 """
1502 return self.__surfaces
1503
1504 def collectPrimitiveBases(self):
1505 """
1506 Returns primitives used to construct the SurfaceLoop.
1507 """
1508 out=[self]
1509 for c in self.getSurfaces(): out+=c.collectPrimitiveBases()
1510 return out
1511
1512 def substitute(self,sub_dict):
1513 """
1514 Returns a copy of self with substitutes for the primitives used to
1515 construct it given by the dictionary ``sub_dict``. If a substitute for
1516 the object is given by ``sub_dict`` the value is returned, otherwise a
1517 new instance with substituted arguments is returned.
1518 """
1519 if not sub_dict.has_key(self):
1520 new_s=[]
1521 for s in self.getSurfaces(): new_s.append(s.substitute(sub_dict))
1522 sub_dict[self]=SurfaceLoop(*tuple(new_s))
1523 return sub_dict[self]
1524
1525 def isColocated(self,primitive):
1526 """
1527 Returns True if each surface is colocated with a curve in ``primitive``
1528 and vice versa.
1529 """
1530 if hasattr(primitive,"getUnderlyingPrimitive"):
1531 if isinstance(primitive.getUnderlyingPrimitive(),SurfaceLoop):
1532 if len(primitive) == len(self):
1533 sp0=self.getSurfaces()
1534 sp1=primitive.getSurfaces()
1535 for s0 in sp0:
1536 colocated = False
1537 for s1 in sp1:
1538 colocated = colocated or s0.isColocated(s1)
1539 if not colocated: return False
1540 return True
1541 return False
1542
1543 class ReverseSurfaceLoop(ReversePrimitive, PrimitiveBase):
1544 """
1545 A view of a SurfaceLoop with reverse orientation.
1546
1547 The loop must represent a closed shell and the primitives should be
1548 oriented consistently.
1549 """
1550 def __init__(self,surface_loop):
1551 """
1552 Creates a polygon from a list of line surfaces. The curves must form
1553 a closed loop.
1554 """
1555 if not isinstance(surface_loop, SurfaceLoop):
1556 raise TypeError("arguments need to be an instance of SurfaceLoop.")
1557 ReversePrimitive.__init__(self, surface_loop)
1558 PrimitiveBase.__init__(self)
1559
1560 def getSurfaces(self):
1561 """
1562 Returns the surfaces defining the SurfaceLoop.
1563 """
1564 return [ -s for s in self.getUnderlyingPrimitive().getSurfaces() ]
1565
1566 def __len__(self):
1567 return len(self.getUnderlyingPrimitive())
1568
1569 #==============================
1570 class Manifold3D(PrimitiveBase):
1571 """
1572 General three-dimensional manifold.
1573 """
1574 def __init__(self):
1575 """
1576 Creates a three-dimensional manifold.
1577 """
1578 PrimitiveBase.__init__(self)
1579
1580 def getBoundary(self):
1581 """
1582 Returns a list of the one-dimensional manifolds forming the boundary
1583 of the volume (including holes).
1584 """
1585 raise NotImplementedError()
1586
1587 class Volume(Manifold3D, Primitive):
1588 """
1589 A volume with holes.
1590 """
1591 def __init__(self,loop,holes=[]):
1592 """
1593 Creates a volume with holes.
1594
1595 :param loop: `SurfaceLoop` defining the boundary of the surface
1596 :param holes: list of `SurfaceLoop` defining holes in the surface
1597 :note: A SurfaceLoop defining a hole should not have any surfaces in
1598 common with the exterior SurfaceLoop.
1599 :note: A SurfaceLoop defining a hole should not have any surfaces in
1600 common with another SurfaceLoop defining a hole in the same
1601 volume.
1602 """
1603 if not isinstance(loop.getUnderlyingPrimitive(), SurfaceLoop):
1604 raise TypeError("argument loop needs to be a SurfaceLoop object.")
1605 for i in range(len(holes)):
1606 if not isinstance(holes[i].getUnderlyingPrimitive(), SurfaceLoop):
1607 raise TypeError("%i th hole needs to be a SurfaceLoop object.")
1608 Primitive.__init__(self)
1609 Manifold3D.__init__(self)
1610 self.__loop=loop
1611 self.__holes=holes
1612
1613 def getHoles(self):
1614 """
1615 Returns the holes in the volume.
1616 """
1617 return self.__holes
1618
1619 def getSurfaceLoop(self):
1620 """
1621 Returns the loop forming the surface.
1622 """
1623 return self.__loop
1624
1625 def substitute(self,sub_dict):
1626 """
1627 Returns a copy of self with substitutes for the primitives used to
1628 construct it given by the dictionary ``sub_dict``. If a substitute for
1629 the object is given by ``sub_dict`` the value is returned, otherwise a
1630 new instance with substituted arguments is returned.
1631 """
1632 if not sub_dict.has_key(self):
1633 sub_dict[self]=Volume(self.getSurfaceLoop().substitute(sub_dict),[ h.substitute(sub_dict) for h in self.getHoles()])
1634 return sub_dict[self]
1635
1636 def isColocated(self,primitive):
1637 """
1638 Returns True if each curve is colocated with a curve in ``primitive``.
1639 """
1640 if hasattr(primitive,"getUnderlyingPrimitive"):
1641 if isinstance(primitive.getUnderlyingPrimitive(),Volume):
1642 if self.getSurfaceLoop().isColocated(primitive.getSurfaceLoop()):
1643 hs0=self.getHoles()
1644 hs1=primitive.getHoles()
1645 if len(hs0) == len(hs1):
1646 for h0 in hs0:
1647 colocated = False
1648 for h1 in hs1:
1649 colocated = colocated or h0.isColocated(h1)
1650 if not colocated: return False
1651 return True
1652 return False
1653
1654 def collectPrimitiveBases(self):
1655 """
1656 Returns primitives used to construct the surface.
1657 """
1658 out=[self] + self.getSurfaceLoop().collectPrimitiveBases()
1659 for i in self.getHoles(): out+=i.collectPrimitiveBases()
1660 return out
1661
1662 def getBoundary(self):
1663 """
1664 Returns a list of the one-dimensional manifolds forming the boundary
1665 of the Surface (including holes).
1666 """
1667 out = []+ self.getSurfaceLoop().getSurfaces()
1668 for h in self.getHoles(): out+=h.getSurfaces()
1669 return out
1670
1671 class PropertySet(Primitive, PrimitiveBase):
1672 """
1673 Defines a group of `Primitive` s which can be accessed through a name.
1674 """
1675 def __init__(self,name,*items):
1676 Primitive.__init__(self)
1677 self.__dim=None
1678 self.clearItems()
1679 self.addItem(*items)
1680 self.setName(name)
1681
1682 def getDim(self):
1683 """
1684 Returns the dimensionality of the items.
1685 """
1686 if self.__dim == None:
1687 items=self.getItems()
1688 if len(items)>0:
1689 if isinstance(items[0] ,Manifold1D):
1690 self.__dim=1
1691 elif isinstance(items[0] ,Manifold2D):
1692 self.__dim=2
1693 elif isinstance(items[0] ,Manifold3D):
1694 self.__dim=3
1695 else:
1696 self.__dim=0
1697 return self.__dim
1698
1699 def __repr__(self):
1700 """
1701 Returns a string representation.
1702 """
1703 return "%s(%s)"%(self.getName(),self.getID())
1704
1705 def getManifoldClass(self):
1706 """
1707 Returns the manifold class expected from items.
1708 """
1709 d=self.getDim()
1710 if d == None:
1711 raise ValueError("undefined spatial diemnsion.")
1712 else:
1713 if d==0:
1714 return Point
1715 elif d==1:
1716 return Manifold1D
1717 elif d==2:
1718 return Manifold2D
1719 else:
1720 return Manifold3D
1721
1722 def getName(self):
1723 """
1724 Returns the name of the set.
1725 """
1726 return self.__name
1727
1728 def setName(self,name):
1729 """
1730 Sets the name.
1731 """
1732 self.__name=str(name)
1733
1734 def addItems(self,*items):
1735 """
1736 Adds items. An item my be any `Primitive` but no `PropertySet`.
1737 """
1738 self.addItem(*items)
1739
1740 def addItem(self,*items):
1741 """
1742 Adds items. An item my be any `Primitive` but no `PropertySet`.
1743 """
1744 for i in items:
1745 if not (isinstance(i, Manifold1D) or isinstance(i, Manifold2D) or isinstance(i, Manifold3D) ):
1746 raise TypeError, "Illegal argument type %s added to PropertySet."%(i.__class__)
1747 for i in items:
1748 if not i in self.__items:
1749 if len(self.__items)>0:
1750 m=self.getManifoldClass()
1751 if not isinstance(i, m):
1752 raise TypeError("argument %s is not a %s class object."%(i, m.__name__))
1753 self.__items.append(i)
1754
1755 def getNumItems(self):
1756 """
1757 Returns the number of items in the property set.
1758 """
1759 return len(self.__items)
1760
1761 def getItems(self):
1762 """
1763 Returns the list of items.
1764 """
1765 return self.__items
1766
1767 def clearItems(self):
1768 """
1769 Clears the list of items.
1770 """
1771 self.__items=[]
1772
1773 def collectPrimitiveBases(self):
1774 """
1775 Returns primitives used to construct the PropertySet.
1776 """
1777 out=[self]
1778 for i in self.getItems(): out+=i.collectPrimitiveBases()
1779 return out
1780
1781 def getTag(self):
1782 """
1783 Returns the tag used for this property set.
1784 """
1785 return self.getID()
1786

  ViewVC Help
Powered by ViewVC 1.1.26