/[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 927 - (show annotations)
Fri Jan 12 06:32:08 2007 UTC (12 years, 5 months ago) by gross
File MIME type: text/x-python
File size: 28832 byte(s)
surfaces implemented by no testing yet
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 class Primitive(object):
56 """
57 template for elementary geometrical object
58 """
59 def __init__(self):
60 """
61
62 """
63 global global_primitive_id_counter
64 self.__ID=global_primitive_id_counter
65 global_primitive_id_counter+=1
66
67 def getID(self):
68 return self.__ID
69
70 def __repr__(self):
71 return "%s(%s)"%(self.__class__.__name__,self.getID())
72
73 def __cmp__(self,other):
74 return cmp(self.getID(),other.getID())
75
76 def getConstructionPoints(self):
77 """
78 returns the points used to construct the primitive
79 """
80 out=set()
81 for i in self.getPrimitives():
82 if isinstance(i,Point): out.add(i)
83 return list(out)
84
85 def getPrimitives(self):
86 """
87 returns primitives used to construct the primitive
88 """
89 return []
90
91 def copy(self):
92 """
93 returns a deep copy of the object
94 """
95 return self.substitute({})
96
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
105 def __add__(self,other):
106 """
107 returns a new object shifted by other
108 """
109 return self.apply(Translation(numarray.array(other,_TYPE)))
110
111 def __sub__(self,other):
112 """
113 returns a new object shifted by other
114 """
115 return self.apply(Translation(-numarray.array(other,_TYPE)))
116
117 def __iadd__(self,other):
118 """
119 shifts the point by other
120 """
121 self.modifyBy(Translation(numarray.array(other,_TYPE)))
122 return self
123
124 def __isub__(self,other):
125 """
126 shifts the point by -other
127 """
128 self.modifyBy(Translation(-numarray.array(other,_TYPE)))
129 return self
130
131 def __imul__(self,other):
132 """
133 modifies object by applying L{Transformation} other. If other is not a L{Transformation} it will try convert it.
134 """
135 if isinstance(other,int) or isinstance(other,float):
136 trafo=Dilation(other)
137 elif isinstance(other,numarray.NumArray):
138 trafo=Translation(other)
139 elif isinstance(other,Transformation):
140 trafo=other
141 else:
142 raise TypeError, "cannot convert argument to Trnsformation class object."
143 self.modifyBy(trafo)
144 return self
145
146 def __rmul__(self,other):
147 """
148 applies L{Transformation} other to object. If other is not a L{Transformation} it will try convert it.
149 """
150 if isinstance(other,int) or isinstance(other,float):
151 trafo=Dilation(other)
152 elif isinstance(other,numarray.NumArray):
153 trafo=Translation(other)
154 elif isinstance(other,Transformation):
155 trafo=other
156 else:
157 raise TypeError, "cannot convert argument to Transformation class object."
158 return self.apply(trafo)
159
160 def __neg__(self):
161 return ReversedPrimitive(self)
162
163 def setLocalScale(self,factor=1.):
164 """
165 sets the local refinement factor
166 """
167 for p in self.getConstructionPoints(): p.setLocalScale(factor)
168
169 def getGmshCommand(self, local_scaling_factor=1.):
170 """
171 returns the Gmsh command(s) to create the primitive
172 """
173 raise NotImplementedError("getGmshCommand is not implemented for this class %s."%self.__class__.__name__)
174
175 def apply(self,transformation):
176 """
177 returns a new L{Point} by applying the transformation
178 """
179 out=self.copy()
180 out.modifyBy(transformation)
181 return out
182
183 def isColocated(self,primitive):
184 """
185 returns True is the two primitives are located at the smae position
186 """
187 raise NotImplementedError("isColocated is not implemented for this class %s."%self.__class__.__name__)
188
189 def substitute(self,sub_dict):
190 """
191 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
192 If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
193 with substituted arguments is returned.
194 """
195 if not sub_dict.has_key(self):
196 sub_dict[self]=self.__class__()
197 return sub_dict[self]
198
199 class Point(Primitive):
200 """
201 a three dimensional point
202 """
203 def __init__(self,x=0.,y=0.,z=0.,local_scale=1.):
204 """
205 creates a point with coorinates x,y,z with the local refinement factor local_scale
206 """
207 super(Point, self).__init__()
208 self.setCoordinates(numarray.array([x,y,z],_TYPE))
209 self.setLocalScale(local_scale)
210
211 def setLocalScale(self,factor=1.):
212 """
213 sets the local refinement factor
214 """
215 if factor<=0.:
216 raise ValueError("scaling factor must be positive.")
217 self.__local_scale=factor
218 def getLocalScale(self):
219 """
220 returns the local refinement factor
221 """
222 return self.__local_scale
223 def getCoordinates(self):
224 """
225 returns the coodinates of the point as L{numarray.NumArray} object
226 """
227 return self._x
228 def setCoordinates(self,x):
229 """
230 returns the coodinates of the point as L{numarray.NumArray} object
231 """
232 if not isinstance(x, numarray.NumArray):
233 self._x=numarray.array(x,_TYPE)
234 else:
235 self._x=x
236
237 def getPrimitives(self):
238 """
239 returns primitives used to construct the primitive
240 """
241 return [self]
242
243 def isColocated(self,primitive):
244 """
245 returns True if L{Point} primitive is colocation (same coordinates)
246 that means if |self-primitive| <= tol * max(|self|,|primitive|)
247 """
248 if isinstance(primitive,Point):
249 primitive=primitive.getCoordinates()
250 c=self.getCoordinates()
251 d=c-primitive
252 return numarray.dot(d,d)<=getToleranceForColocation()**2*max(numarray.dot(c,c),numarray.dot(primitive,primitive))
253 else:
254 return False
255
256 def substitute(self,sub_dict):
257 """
258 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
259 If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
260 with substituted arguments is returned.
261 """
262 if not sub_dict.has_key(self):
263 c=self.getCoordinates()
264 sub_dict[self]=Point(c[0],c[1],c[2],local_scale=self.getLocalScale())
265 return sub_dict[self]
266
267 def modifyBy(self,transformation):
268 """
269 modifies the coordinates by applying a transformation
270 """
271 self.setCoordinates(transformation(self.getCoordinates()))
272
273
274 def getGmshCommand(self, local_scaling_factor=1.):
275 """
276 returns the Gmsh command(s) to create the primitive
277 """
278 c=self.getCoordinates()
279 return "Point(%s) = {%s , %s, %s , %s };"%(self.getID(),c[0],c[1],c[2], self.getLocalScale()*local_scaling_factor)
280
281 class Primitive1D(Primitive):
282 """
283 general one-dimensional primitive
284 """
285 def __init__(self,*args):
286 """
287 create a one-dimensional primitive
288 """
289 super(Primitive1D, self).__init__()
290
291 class Curve(Primitive1D):
292 """
293 a curve defined through a list of control points.
294 """
295 def __init__(self,*points):
296 """
297 defines a curve form control points
298 """
299 if len(points)<2:
300 raise TypeError("Curve needs at least two points")
301 super(Curve, self).__init__()
302 i=0
303 for p in points:
304 i+=1
305 if not isinstance(p,Point): raise TypeError("%s-th argument is not a Point object."%i)
306 self.__points=points
307
308 def __len__(self):
309 """
310 returns the number of control points
311 """
312 return len(self.__points)
313
314 def getStartPoint(self):
315 """
316 returns start point
317 """
318 return self.__points[0]
319
320 def getEndPoint(self):
321 """
322 returns end point
323 """
324 return self.__points[-1]
325
326 def getControlPoints(self):
327 """
328 returns a list of the points
329 """
330 return self.__points
331
332 def getPrimitives(self):
333 """
334 returns primitives used to construct the Curve
335 """
336 out=set()
337 for p in self.getControlPoints(): out|=set(p.getPrimitives())
338 out.add(self)
339 return list(out)
340
341 def substitute(self,sub_dict):
342 """
343 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
344 If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
345 with substituted arguments is returned.
346 """
347 if not sub_dict.has_key(self):
348 new_p=[]
349 for p in self.getControlPoints(): new_p.append(p.substitute(sub_dict))
350 sub_dict[self]=self.__class__(*tuple(new_p))
351 return sub_dict[self]
352
353 def isColocated(self,primitive):
354 """
355 returns True curves are on the same position
356 """
357 if isinstance(primitive,self.__class__):
358 if len(primitive) == len(self):
359 cp0=self.getControlPoints()
360 cp1=primitive.getControlPoints()
361 match=True
362 for i in range(len(cp0)):
363 if not cp0[i].isColocated(cp1[i]):
364 match=False
365 break
366 if not match:
367 for i in range(len(cp0)):
368 if not cp0[i].isColocated(cp1[len(cp0)-1-i]):
369 return False
370 return True
371 else:
372 return False
373 else:
374 return False
375
376 class Spline(Curve):
377 """
378 a spline curve defined through a list of control points.
379 """
380 def getGmshCommand(self):
381 """
382 returns the Gmsh command(s) to create the Curve
383 """
384 out=""
385 for i in self.getControlPoints():
386 if len(out)>0:
387 out+=", %s"%i.getID()
388 else:
389 out="%s"%i.getID()
390 return "Spline(%s) = {%s};"%(self.getID(),out)
391
392
393 class BezierCurve(Curve):
394 """
395 a Bezier curve
396 """
397 def getGmshCommand(self):
398 """
399 returns the Gmsh command(s) to create the Curve
400 """
401 out=""
402 for i in self.getControlPoints():
403 if len(out)>0:
404 out+=", %s"%i.getID()
405 else:
406 out="%s"%i.getID()
407 return "Bezier(%s) = {%s};"%(self.getID(),out)
408
409 class BSpline(Curve):
410 """
411 a BSpline curve. Control points may be repeated.
412 """
413 def getGmshCommand(self):
414 """
415 returns the Gmsh command(s) to create the Curve
416 """
417 out=""
418 for i in self.getControlPoints():
419 if len(out)>0:
420 out+=", %s"%i.getID()
421 else:
422 out="%s"%i.getID()
423 return "BSpline(%s) = {%s};"%(self.getID(),out)
424
425 class Line(Curve):
426 """
427 a line is defined by two L{Point}s
428 """
429 def __init__(self,*points):
430 """
431 defines a line with start and end point
432 """
433 if len(points)!=2:
434 raise TypeError("Line needs two points")
435 super(Line, self).__init__(*points)
436 def getGmshCommand(self):
437 """
438 returns the Gmsh command(s) to create the Curve
439 """
440 return "Line(%s) = {%s, %s};"%(self.getID(),self.getStartPoint().getID(),self.getEndPoint().getID())
441
442
443 class Arc(Primitive1D):
444 """
445 defines an arc which is strictly, smaller than Pi
446 """
447 def __init__(self,center,start,end):
448 """
449 creates an arc by the start point, end point and center
450 """
451 if not isinstance(center,Point): raise TypeError("center needs to be a Point object.")
452 if not isinstance(end,Point): raise TypeError("end needs to be a Point object.")
453 if not isinstance(start,Point): raise TypeError("start needs to be a Point object.")
454 # TODO: check length of circle.
455 super(Arc, self).__init__()
456 self.__center=center
457 self.__start=start
458 self.__end=end
459
460 def getStartPoint(self):
461 """
462 returns start point
463 """
464 return self.__start
465
466 def getEndPoint(self):
467 """
468 returns end point
469 """
470 return self.__end
471
472 def getCenterPoint(self):
473 """
474 returns center
475 """
476 return self.__center
477
478 def getPrimitives(self):
479 """
480 returns the primitives used to construct the Curve
481 """
482 out=set()
483 out|=set(self.getStartPoint().getPrimitives())
484 out|=set(self.getEndPoint().getPrimitives())
485 out|=set(self.getCenterPoint().getPrimitives())
486 out.add(self)
487 return list(out)
488
489 def getGmshCommand(self):
490 """
491 returns the Gmsh command(s) to create the primitive
492 """
493 return "Circle(%s) = {%s, %s, %s};"%(self.getID(),self.getStartPoint().getID(),self.getCenterPoint().getID(),self.getEndPoint().getID())
494
495 def substitute(self,sub_dict):
496 """
497 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
498 If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
499 with substituted arguments is returned.
500 """
501 if not sub_dict.has_key(self):
502 sub_dict[self]=Arc(self.getCenterPoint().substitute(sub_dict),self.getStartPoint().substitute(sub_dict),self.getEndPoint().substitute(sub_dict))
503 return sub_dict[self]
504
505 def isColocated(self,primitive):
506 """
507 returns True curves are on the same position
508 """
509 if isinstance(primitive,Arc):
510 return (self.getCenterPoint().isColocated(primitive.getCenterPoint())) and ( \
511 (self.getEndPoint().isColocated(primitive.getEndPoint()) and self.getStartPoint().isColocated(primitive.getStartPoint()) ) \
512 or (self.getEndPoint().isColocated(primitive.getStartPoint()) and self.getStartPoint().isColocated(primitive.getEndPoint()) ) )
513 else:
514 return False
515
516 class Primitive2D(Primitive):
517 """
518 general two-dimensional primitive
519 """
520 def __init__(self,*args):
521 """
522 create a two-dimensional primitive
523 """
524 super(Primitive2D, self).__init__()
525
526 class CurveLoop(Primitive2D):
527 """
528 An oriented loop of curves.
529
530 The loop must be closed and the L{Curves}s should be oriented consistently.
531 """
532 def __init__(self,*curves):
533 """
534 creates a polygon from a list of line curves. The curves must form a closed loop.
535 """
536 super(CurveLoop, self).__init__()
537 if len(curves)<2:
538 raise TypeError("at least two curves have to be given.")
539 for i in range(len(curves)):
540 if not isinstance(curves[i],Primitive1D):
541 raise TypeError("%s-th argument is not a Primitive1D object."%i)
542 # for the curves a loop:
543 used=[ False for i in curves]
544 self.__curves=[curves[0]]
545 used[0]=True
546 while not min(used):
547 found=False
548 for i in xrange(len(curves)):
549 if not used[i]:
550 if self.__curves[-1].getEndPoint() == curves[i].getStartPoint():
551 self.__curves.append(curves[i])
552 used[i]=True
553 found=True
554 break
555 if not found:
556 raise ValueError("loop is not closed.")
557 if not self.__curves[0].getStartPoint() == self.__curves[-1].getEndPoint():
558 raise ValueError("loop is not closed.")
559
560 def getCurves(self):
561 """
562 returns the curves defining the CurveLoop
563 """
564 return self.__curves
565
566 def __len__(self):
567 """
568 return the number of curves in the CurveLoop
569 """
570 return len(self.__curves)
571
572 def getPrimitives(self):
573 """
574 returns primitives used to construct the CurveLoop
575 """
576 out=set()
577 for c in self.getCurves(): out|=set(c.getPrimitives())
578 out.add(self)
579 return list(out)
580
581 def substitute(self,sub_dict):
582 """
583 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
584 If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
585 with substituted arguments is returned.
586 """
587 if not sub_dict.has_key(self):
588 new_c=[]
589 for c in self.getCurves(): new_c.append(c.substitute(sub_dict))
590 sub_dict[self]=CurveLoop(*tuple(new_c))
591 return sub_dict[self]
592
593
594 def isColocated(self,primitive):
595 """
596 returns True if each curve is collocted with a curve in primitive
597 """
598 if isinstance(primitive,CurveLoop):
599 if len(primitive) == len(self):
600 cp0=self.getCurves()
601 cp1=primitive.getCurves()
602 for c0 in cp0:
603 collocated = False
604 for c1 in cp1:
605 collocated = collocated or c0.isColocated(c1)
606 if not collocated: return False
607 return True
608 else:
609 return False
610 else:
611 return False
612
613 def getGmshCommand(self):
614 out=""
615 for i in self.getCurves():
616 if len(out)>0:
617 out+=", %s"%i.getID()
618 else:
619 out="%s"%i.getID()
620 return "Line Loop(%s) = {%s};"%(self.getID(),out)
621
622 class Surface(Primitive2D):
623 """
624 a surface
625 """
626 pass
627
628 class RuledSurface(Surface):
629 """
630 A ruled surface, i.e., a surface that can be interpolated using transfinite interpolation
631 """
632 def __init__(self,loop):
633 """
634 creates a ruled surface with boundary loop
635
636 @param loop: L{CurveLoop} defining the boundary of the surface.
637 """
638 super(RuledSurface, self).__init__()
639 if not isinstance(CurveLoop):
640 raise TypeError("argument loop needs to be a CurveLoop object.")
641 if len(loop)<2:
642 raise TypeError("the loop must contain at least two Curves.")
643 if len(loop)>4:
644 raise TypeError("the loop must contain at least three Curves.")
645
646 self.__loop=loop
647
648 def getBoundaryLoop(self):
649 """
650 returns the loop defining the boundary
651 """
652 return self.__loop
653
654 def getPrimitives(self):
655 out=set([self]) | self.getBoundaryLoop().getPrimitives()
656
657 def getGmshCommand(self):
658 return "Ruled Surface(%s) = {%s};"%(self.getID(),self.getBoundaryLoop().getID())
659
660 def getPrimitives(self):
661 """
662 returns primitives used to construct the CurveLoop
663 """
664 out=list(set([self]) | self.getBoundaryLoop().getPrimitives())
665
666 def substitute(self,sub_dict):
667 """
668 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
669 If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
670 with substituted arguments is returned.
671 """
672 if not sub_dict.has_key(self):
673 sub_dict[self]=CurveLoop(self.getBoundaryLoop().substitute(sub_dict))
674 return sub_dict[self]
675
676 def isColocated(self,primitive):
677 """
678 returns True if each curve is collocted with a curve in primitive
679 """
680 if isinstance(primitive,RuledSurface):
681 return self.getBoundaryLoop().colocated(primitive.getBoundaryLoop())
682 else:
683 return False
684
685 def createRuledSurface(*curves):
686 """
687 an easier way to create a L{RuledSurface} from given curves.
688 """
689 return RuledSurface(CurveLoop(*curves))
690
691 class PlaneSurface(Surface):
692 """
693 a plane surface with holes
694 """
695 def __init__(self,loop,holes=[]):
696 """
697 creates a plane surface with a hole
698
699 @param loop: L{CurveLoop} defining the boundary of the surface
700 @param holes: list of L{CurveLoop} defining holes in the surface.
701 @note: A CurveLoop defining a hole should not have any lines in common with the exterior CurveLoop.
702 A CurveLoop defining a hole should not have any lines in common with another CurveLoop defining a hole in the same surface.
703 """
704 super(PlaneSurface, self).__init__()
705 if not isinstance(loop,CurveLoop):
706 raise TypeError("argument loop needs to be a CurveLoop object.")
707 for i in range(len(holes)):
708 if not holes[i].inCurveLoop():
709 raise TypeError("%i th hole needs to be a CurveLoop object.")
710 #TODO: check if lines are in a plane
711 self.__holes=holes
712 def getHoles(self):
713 """
714 returns the holes
715 """
716 return self.__holes
717 def getBoundaryLoop(self):
718 """
719 returns the loop defining the boundary
720 """
721 return self.__loop
722 def getPrimitives(self):
723 out=set([self]) | self.getBoundaryLoop().getPrimitives()
724 for i in self.getHoles(): out|=i.getPrimitives()
725 return out
726
727 def getGmshCommand(self):
728 out=""
729 for i in self.getHoles():
730 if len(out)>0:
731 out+=", %s"%i.getID()
732 else:
733 out="%s"%i.getID()
734 if len(out)>0:
735 return "Plane Surface(%s) = {%s, %s};"%(self.getID(),self.getBoundaryLoop().getID(), out)
736 else:
737 return "Plane Surface(%s) = {%s};"%(self.getID(),self.getBoundaryLoop().getID())
738
739 def substitute(self,sub_dict):
740 """
741 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
742 If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
743 with substituted arguments is returned.
744 """
745 if not sub_dict.has_key(self):
746 sub_dict[self]=CurveLoop(self.getBoundaryLoop().substitute(sub_dict),[ h.substitute(sub_dict) for h in self.getHoles()])
747 return sub_dict[self]
748
749 def isColocated(self,primitive):
750 """
751 returns True if each curve is collocted with a curve in primitive
752 """
753 if isinstance(primitive,PlaneSurface):
754 if self.getBoundaryLoop().colocated(primitive.getBoundaryLoop()):
755 my_h=self.getHoles()
756 h=primitive.getHoles()
757 else:
758 return False
759 else:
760 return False
761
762
763 #=================================================================================================================================
764 class SurfaceLoop(Primitive):
765 """
766 a surface loop. It defines the shell of a volume.
767
768 The loop must represent a closed shell, and the L{Surface}s should be oriented consistently.
769 """
770 def __init__(self,*surfaces):
771 """
772 creates a surface loop
773 """
774 super(SurfaceLoop, self).__init__()
775 self.__surfaces=[]
776 self.addSurface(*surfaces)
777 def addSurface(self,*surfaces):
778 for i in range(len(surfaces)):
779 if not surfaces[i].isSurface():
780 raise TypeError("%s-th argument is not a Surface object."%i)
781 self.__surfaces+=surfaces
782
783 def isSurfaceLoop(self):
784 return True
785 def getSurfaces(self):
786 return self.__surfaces
787 def __add__(self,other):
788 return SurfaceLoop([c+other for c in self.getSurfaces])
789 def __len__(self):
790 return len(self.__surfaces)
791 def getPrimitives(self):
792 out=set([self])
793 for i in self.getSurfaces(): out|=i.getPrimitives()
794 return out
795 def getConstructionPoints(self):
796 out=set()
797 for i in self.getSurfaces(): out|=i.getConstructionPoints()
798 return out
799 def getGmshCommand(self):
800 out=""
801 for i in self.getSurfaces():
802 if len(out)>0:
803 out+=", %s"%i.getID()
804 else:
805 out="%s"%i.getID()
806 return "Surface Loop(%s) = {%s};"%(self.getID(),out)
807
808 class Volume(Primitive):
809 """
810 a volume with holes.
811 """
812 def __init__(self,loop,holes=[]):
813 """
814 creates a volume
815
816 @param loop: L{SurfaceLoop} defining the boundary of the surface
817 @param holes: list of L{SurfaceLoop} defining holes in the surface.
818 @note: A SurfaceLoop defining a hole should not have any surfaces in common with the exterior SurfaceLoop.
819 A SurfaceLoop defining a hole should not have any surfaces in common with another SurfaceLoop defining a hole in the same volume.
820 """
821 super(Volume, self).__init__()
822 if not loop.isSurfaceLoop():
823 raise TypeError("argument loop needs to be a SurfaceLoop object.")
824 for i in range(len(holes)):
825 if not holes[i].isSurfaceLoop():
826 raise TypeError("%i th hole needs to be a SurfaceLoop object.")
827 self.__loop=loop
828 self.__holes=holes
829 def getHoles(self):
830 return self.__holes
831 def getSurfaceLoop(self):
832 return self.__loop
833 def __add__(self,other):
834 return Volume(self.getSurfaceLoop()+other, holes=[h+other for h in self.getHoles()])
835 def getPrimitives(self):
836 out=set([self]) | self.getSurfaceLoop().getPrimitives()
837 for i in self.getHoles(): out|=i.getPrimitives()
838 return out
839 def getConstructionPoints(self):
840 out=self.getSurfaceLoop().getConstructionPoints()
841 for i in self.getHoles(): out|=i.Points()
842 return out
843 def getGmshCommand(self):
844 out=""
845 for i in self.getHoles():
846 if len(out)>0:
847 out+=", %s"%i.getID()
848 else:
849 out="%s"%i.getID()
850 if len(out)>0:
851 return "Volume(%s) = {%s, %s};"%(self.getID(),self.getSurfaceLoop().getID(), out)
852 else:
853 return "Volume(%s) = {%s};"%(self.getID(),self.getSurfaceLoop().getID())
854
855 class ReversedPrimitive(object):
856 def __init__(self,prim):
857 self.__prim=prim
858 def __getattr__(self,name):
859 if name == "getID":
860 return self.getReverseID
861 else:
862 return getattr(self.__prim,name)
863 def getReverseID(self):
864 return -self.__prim.getID()
865
866 class PropertySet(Primitive):
867 """
868 defines a group L{Primitive} objects.
869 """
870 def __init__(self,tag=None,*items):
871 super(PropertySet, self).__init__()
872 self.__items=items
873 self.__tag=tag
874 def getPrimitives(self):
875 out=set([self, self.getBoundaryLoop().getPrimitives()])
876 for i in self.getHoles(): out|=i.getPrimitives()
877 return out
878
879 class PrimitiveStack(object):
880 def __init__(self,*items):
881 self.__prims=set()
882 for i in items:
883 self.__prims|=i.getPrimitives()
884 self.__prims=list(self.__prims)
885 self.__prims.sort()
886
887 def getGmshCommands(self):
888 out=""
889 for i in self.__prims:
890 out+=i.getGmshCommand()+"\n"
891 return out

  ViewVC Help
Powered by ViewVC 1.1.26