/[escript]/trunk/pycad/py_src/primitives.py
ViewVC logotype

Diff of /trunk/pycad/py_src/primitives.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.912  
changed lines
  Added in v.999

  ViewVC Help
Powered by ViewVC 1.1.26