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

  ViewVC Help
Powered by ViewVC 1.1.26