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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 928 - (hide annotations)
Tue Jan 16 08:36:03 2007 UTC (12 years, 8 months ago) by gross
File MIME type: text/x-python
File size: 37673 byte(s)
more tests but there is a problem with reversing directions.
1 gross 898 # $Id:$
2    
3     """
4 gross 899 Geometrical Primitives
5 gross 898
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 gross 899 import numarray
29 gross 915 from transformations import _TYPE, Translation, Dilation, Transformation
30 gross 899
31    
32 gross 915 def resetGlobalPrimitiveIdCounter():
33 gross 917 """
34     initializes the global primitive ID counter
35     """
36 gross 915 global global_primitive_id_counter
37     global_primitive_id_counter=1
38    
39 gross 916 def setToleranceForColocation(tol=1.e-11):
40 gross 917 """
41     set the global tolerance for colocation checks to tol
42     """
43 gross 916 global global_tolerance_for_colocation
44     global_tolerance_for_colocation=tol
45    
46     def getToleranceForColocation():
47 gross 917 """
48     returns the global tolerance for colocation checks
49     """
50 gross 916 return global_tolerance_for_colocation
51    
52 gross 915 resetGlobalPrimitiveIdCounter()
53 gross 916 setToleranceForColocation()
54 gross 915
55 gross 928
56     class PrimitiveBase(object):
57 gross 898 """
58     template for elementary geometrical object
59     """
60 gross 928 def __init__(self,id):
61 gross 898 """
62 gross 928 initializes PrimitiveBase instance object with id
63 gross 898 """
64 gross 928 self.__ID=id
65 gross 915
66 gross 899 def getID(self):
67 gross 928 """
68     returns the primitive ID
69     """
70 gross 899 return self.__ID
71 gross 915
72 gross 899 def __repr__(self):
73 gross 928 return "%s(%s)"%(self.getClass().__name__,self.getID())
74 gross 923
75 gross 899 def __cmp__(self,other):
76 gross 928 """
77     compares object with other by comparing the absolute value of the ID
78     """
79     if isinstance(self, PrimitiveBase):
80     return cmp(abs(self.getID()),abs(other.getID()))
81     else:
82     return False
83 gross 916
84     def getConstructionPoints(self):
85 gross 912 """
86 gross 916 returns the points used to construct the primitive
87 gross 912 """
88     out=set()
89 gross 916 for i in self.getPrimitives():
90 gross 912 if isinstance(i,Point): out.add(i)
91 gross 916 return list(out)
92 gross 912
93 gross 916 def getPrimitives(self):
94 gross 912 """
95 gross 928 returns a list of primitives used to construct the primitive with no double entries
96 gross 912 """
97 gross 928 out=set()
98     for p in self.collectPrimitives():
99     if isintance(p,ReversePrimitive):
100     out.add(-p)
101     elif isintance(p,Primitive):
102     out.add(p)
103     return list(set(self.collectPrimitives()))
104 gross 912
105 gross 915 def copy(self):
106     """
107 gross 916 returns a deep copy of the object
108 gross 915 """
109 gross 925 return self.substitute({})
110 gross 912
111 gross 915 def modifyBy(self,transformation):
112 gross 916 """
113     modifies the coordinates by applying a transformation
114     """
115     for p in self.getConstructionPoints(): p.modifyBy(transformation)
116 gross 915
117     def __add__(self,other):
118     """
119     returns a new object shifted by other
120     """
121     return self.apply(Translation(numarray.array(other,_TYPE)))
122    
123     def __sub__(self,other):
124     """
125     returns a new object shifted by other
126     """
127     return self.apply(Translation(-numarray.array(other,_TYPE)))
128    
129     def __iadd__(self,other):
130     """
131     shifts the point by other
132     """
133     self.modifyBy(Translation(numarray.array(other,_TYPE)))
134     return self
135    
136     def __isub__(self,other):
137     """
138     shifts the point by -other
139     """
140     self.modifyBy(Translation(-numarray.array(other,_TYPE)))
141     return self
142    
143     def __imul__(self,other):
144     """
145     modifies object by applying L{Transformation} other. 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 Trnsformation class object."
155     self.modifyBy(trafo)
156     return self
157    
158     def __rmul__(self,other):
159     """
160     applies L{Transformation} other to object. If other is not a L{Transformation} it will try convert it.
161     """
162     if isinstance(other,int) or isinstance(other,float):
163     trafo=Dilation(other)
164     elif isinstance(other,numarray.NumArray):
165     trafo=Translation(other)
166     elif isinstance(other,Transformation):
167     trafo=other
168     else:
169 gross 916 raise TypeError, "cannot convert argument to Transformation class object."
170 gross 915 return self.apply(trafo)
171    
172    
173 gross 916 def setLocalScale(self,factor=1.):
174     """
175     sets the local refinement factor
176     """
177     for p in self.getConstructionPoints(): p.setLocalScale(factor)
178    
179 gross 928 def __neg__(self):
180 gross 916 """
181 gross 928 returns a view of the object with reverse orientiention
182 gross 916 """
183 gross 928 if isinstance(self,ReversePrimitive):
184     return self.getUnderlyingPrimitive()
185     else:
186     return ReversedPrimitive(self)
187 gross 898
188 gross 916 def apply(self,transformation):
189     """
190 gross 928 returns a new object by applying the transformation
191 gross 916 """
192 gross 925 out=self.copy()
193     out.modifyBy(transformation)
194     return out
195 gross 916
196 gross 928 def collectPrimitives(self):
197     """
198     returns a list of primitives used to construct the primitive. It may contain primitives twice
199    
200     @note: this class is overwritten by subclass
201     """
202     return [self]
203    
204     def getGmshCommand(self, local_scaling_factor=1.):
205     """
206     returns the Gmsh command(s) to create the primitive
207    
208     @note: this class is overwritten by subclass
209     """
210     raise NotImplementedError("getGmshCommand is not implemented for this class %s."%self.__class__.__name__)
211    
212 gross 916 def isColocated(self,primitive):
213     """
214     returns True is the two primitives are located at the smae position
215 gross 928
216     @note: this class is overwritten by subclass
217 gross 916 """
218     raise NotImplementedError("isColocated is not implemented for this class %s."%self.__class__.__name__)
219    
220 gross 925 def substitute(self,sub_dict):
221     """
222     returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
223     If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
224     with substituted arguments is returned.
225 gross 928
226     @note: this class is overwritten by subclass
227 gross 925 """
228     if not sub_dict.has_key(self):
229 gross 928 sub_dict[self]=self.getClass()
230 gross 925 return sub_dict[self]
231    
232 gross 928 class Primitive(PrimitiveBase):
233     """
234     A general primitive
235     """
236     def __init__(self):
237     """
238     instantiate a primitve
239     """
240     global global_primitive_id_counter
241     self.__ID=global_primitive_id_counter
242     PrimitiveBase.__init__(self,global_primitive_id_counter)
243    
244     def getClass(self):
245     """
246     returns the class of the object
247     """
248     return self.__class__
249    
250     class ReversePrimitive(PrimitiveBase):
251     """
252     A view onto a primitive creating an reverse orientation
253     """
254     def __init__(self,primitive):
255     """
256     instantiate a view onto primitve
257     """
258     PrimitiveBase.__init__(self,-primitive.getID())
259     self.__primitive=primitive
260    
261     def getUnderlyingPrimitive(self):
262     """
263     returns the underlying primitive
264     """
265     return self.__primitive
266    
267     def getClass(self):
268     """
269     returns the class of the underlying object
270     """
271     return self.getUnderlyingPrimitive().__class__()
272    
273 gross 899 class Point(Primitive):
274 gross 898 """
275     a three dimensional point
276     """
277     def __init__(self,x=0.,y=0.,z=0.,local_scale=1.):
278     """
279 gross 912 creates a point with coorinates x,y,z with the local refinement factor local_scale
280 gross 898 """
281 gross 928 Primitive.__init__(self)
282 gross 915 self.setCoordinates(numarray.array([x,y,z],_TYPE))
283 gross 899 self.setLocalScale(local_scale)
284 gross 915
285 gross 898 def setLocalScale(self,factor=1.):
286 gross 902 """
287 gross 912 sets the local refinement factor
288 gross 902 """
289 gross 912 if factor<=0.:
290     raise ValueError("scaling factor must be positive.")
291 gross 898 self.__local_scale=factor
292 gross 902 def getLocalScale(self):
293 gross 912 """
294     returns the local refinement factor
295     """
296 gross 902 return self.__local_scale
297 gross 912 def getCoordinates(self):
298     """
299     returns the coodinates of the point as L{numarray.NumArray} object
300     """
301     return self._x
302 gross 915 def setCoordinates(self,x):
303 gross 912 """
304     returns the coodinates of the point as L{numarray.NumArray} object
305     """
306 gross 915 if not isinstance(x, numarray.NumArray):
307     self._x=numarray.array(x,_TYPE)
308     else:
309     self._x=x
310    
311 gross 928 def collectPrimitives(self):
312 gross 912 """
313 gross 916 returns primitives used to construct the primitive
314 gross 912 """
315 gross 916 return [self]
316 gross 912
317 gross 916 def isColocated(self,primitive):
318 gross 912 """
319 gross 916 returns True if L{Point} primitive is colocation (same coordinates)
320     that means if |self-primitive| <= tol * max(|self|,|primitive|)
321 gross 912 """
322 gross 916 if isinstance(primitive,Point):
323     primitive=primitive.getCoordinates()
324     c=self.getCoordinates()
325     d=c-primitive
326     return numarray.dot(d,d)<=getToleranceForColocation()**2*max(numarray.dot(c,c),numarray.dot(primitive,primitive))
327     else:
328     return False
329 gross 912
330 gross 925 def substitute(self,sub_dict):
331     """
332     returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
333     If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
334     with substituted arguments is returned.
335     """
336     if not sub_dict.has_key(self):
337     c=self.getCoordinates()
338     sub_dict[self]=Point(c[0],c[1],c[2],local_scale=self.getLocalScale())
339     return sub_dict[self]
340 gross 915
341     def modifyBy(self,transformation):
342     """
343     modifies the coordinates by applying a transformation
344     """
345     self.setCoordinates(transformation(self.getCoordinates()))
346    
347    
348     def getGmshCommand(self, local_scaling_factor=1.):
349 gross 916 """
350     returns the Gmsh command(s) to create the primitive
351     """
352 gross 899 c=self.getCoordinates()
353 gross 915 return "Point(%s) = {%s , %s, %s , %s };"%(self.getID(),c[0],c[1],c[2], self.getLocalScale()*local_scaling_factor)
354 gross 898
355 gross 928 def __neg__(self):
356     """
357     returns a view of the object with reverse orientiention. As a point has no direction the object itself is returned.
358     """
359     return self
360    
361     class Manifold1D(PrimitiveBase):
362 gross 916 """
363 gross 928 general one-dimensional minifold in 3D defined by a start and end point.
364 gross 916 """
365 gross 928 def __init__(self):
366     """
367     create a one-dimensional manifold
368     """
369     PrimitiveBass.__init__(self)
370    
371     def getStartPoint(self):
372     """
373     returns start point
374     """
375     raise NotImplementedError()
376    
377     def getEndPoint(self):
378     """
379     returns end point
380     """
381     raise NotImplementedError()
382    
383     class CurveBase(Manifold1D):
384     def __init__(self):
385 gross 916 """
386 gross 928 create a one-dimensional primitive
387 gross 916 """
388 gross 928 Manifold1D.__init__(self)
389 gross 916
390     def __len__(self):
391     """
392     returns the number of control points
393     """
394 gross 928 return len(self.getControlPoints())
395 gross 916
396     def getStartPoint(self):
397 gross 898 """
398     returns start point
399     """
400 gross 928 return self.getControlPoints[0]
401 gross 898
402 gross 916 def getEndPoint(self):
403 gross 898 """
404     returns end point
405     """
406 gross 928 return self.getControlPoints[-1]
407 gross 898
408 gross 928 def collectPrimitives(self):
409 gross 916 """
410     returns primitives used to construct the Curve
411     """
412 gross 928 out=[self]
413     for p in self.getControlPoints(): out+=p.collectPrimitives()
414     return out
415 gross 916
416 gross 925 def substitute(self,sub_dict):
417 gross 916 """
418 gross 925 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
419     If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
420     with substituted arguments is returned.
421 gross 916 """
422 gross 925 if not sub_dict.has_key(self):
423     new_p=[]
424     for p in self.getControlPoints(): new_p.append(p.substitute(sub_dict))
425 gross 928 sub_dict[self]=self.getGeneratingClass()(*tuple(new_p))
426 gross 925 return sub_dict[self]
427 gross 916
428     def isColocated(self,primitive):
429     """
430     returns True curves are on the same position
431     """
432     if isinstance(primitive,self.__class__):
433     if len(primitive) == len(self):
434     cp0=self.getControlPoints()
435     cp1=primitive.getControlPoints()
436 gross 919 match=True
437 gross 916 for i in range(len(cp0)):
438     if not cp0[i].isColocated(cp1[i]):
439 gross 919 match=False
440     break
441     if not match:
442     for i in range(len(cp0)):
443     if not cp0[i].isColocated(cp1[len(cp0)-1-i]):
444     return False
445 gross 916 return True
446     else:
447     return False
448     else:
449     return False
450    
451 gross 928
452     class Curve(CurveBase):
453     """
454     a curve defined through a list of control points.
455     """
456     def __init__(self,*points):
457     """
458     defines a curve form control points
459     """
460     CurveBase.__init__(self)
461     if len(points)<2:
462     raise TypeError("Curve needs at least two points")
463     super(Curve, self).__init__()
464     i=0
465     for p in points:
466     i+=1
467     if not isinstance(p,Point): raise TypeError("%s-th argument is not a Point object."%i)
468     self.__points=points
469    
470     def __neg__(self):
471     """
472     returns a view onto the curce with reversed ordering
473     """
474     return ReverseCurve(self)
475     def getControlPoints(self):
476     """
477     returns a list of the points
478     """
479     return self.__points
480    
481     def getGeneratingClass(self):
482     return self.__class__
483    
484     class ReverseCurve(CurveBase):
485     """
486     a curve defined through a list of control points.
487     """
488     def __init__(self,curve):
489     """
490     defines a curve form control points
491     """
492     CurveBase.__init__(self)
493     if not isinstance(curve, CurveBase):
494     raise TypeError("curve needs to be an instance of Curve")
495     self.__curve=curve
496    
497     def __neg__(self):
498     """
499     returns a view onto the curce with reversed ordering
500     """
501     return self.__curve
502     def getControlPoints(self):
503     """
504     returns a list of the points
505     """
506     out=[p for p in self.__curve.getControlPoints()]
507     out.reverse()
508     return out
509     def getGeneratingClass(self):
510     return self.__curve.getGeneratingClass()
511    
512 gross 916 class Spline(Curve):
513     """
514     a spline curve defined through a list of control points.
515     """
516     def getGmshCommand(self):
517     """
518     returns the Gmsh command(s) to create the Curve
519     """
520 gross 899 out=""
521 gross 916 for i in self.getControlPoints():
522 gross 899 if len(out)>0:
523     out+=", %s"%i.getID()
524     else:
525     out="%s"%i.getID()
526     return "Spline(%s) = {%s};"%(self.getID(),out)
527 gross 916
528 gross 898
529     class BezierCurve(Curve):
530     """
531     a Bezier curve
532     """
533 gross 899 def getGmshCommand(self):
534 gross 916 """
535     returns the Gmsh command(s) to create the Curve
536     """
537 gross 899 out=""
538 gross 916 for i in self.getControlPoints():
539 gross 899 if len(out)>0:
540     out+=", %s"%i.getID()
541     else:
542     out="%s"%i.getID()
543     return "Bezier(%s) = {%s};"%(self.getID(),out)
544 gross 898
545 gross 916 class BSpline(Curve):
546 gross 898 """
547     a BSpline curve. Control points may be repeated.
548     """
549 gross 899 def getGmshCommand(self):
550 gross 916 """
551     returns the Gmsh command(s) to create the Curve
552     """
553 gross 899 out=""
554 gross 916 for i in self.getControlPoints():
555 gross 899 if len(out)>0:
556     out+=", %s"%i.getID()
557     else:
558     out="%s"%i.getID()
559     return "BSpline(%s) = {%s};"%(self.getID(),out)
560 gross 898
561     class Line(Curve):
562     """
563     a line is defined by two L{Point}s
564     """
565 gross 916 def __init__(self,*points):
566 gross 899 """
567 gross 916 defines a line with start and end point
568 gross 899 """
569 gross 916 if len(points)!=2:
570     raise TypeError("Line needs two points")
571     super(Line, self).__init__(*points)
572 gross 899 def getGmshCommand(self):
573 gross 916 """
574     returns the Gmsh command(s) to create the Curve
575     """
576     return "Line(%s) = {%s, %s};"%(self.getID(),self.getStartPoint().getID(),self.getEndPoint().getID())
577 gross 898
578 gross 916
579 gross 928 class ArcBase(Manifold1D):
580     def collectPrimitives(self):
581     """
582     returns the primitives used to construct the Curve
583     """
584     out=[self]
585     out+=self.getStartPoint().collectPrimitives()
586     out+=self.getEndPoint().collectPrimitives()
587     out+=self.getCenterPoint().collectPrimitives()
588     return out
589    
590     def getGmshCommand(self):
591     """
592     returns the Gmsh command(s) to create the primitive
593     """
594     return "Circle(%s) = {%s, %s, %s};"%(self.getID(),self.getStartPoint().getID(),self.getCenterPoint().getID(),self.getEndPoint().getID())
595    
596     def substitute(self,sub_dict):
597     """
598     returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
599     If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
600     with substituted arguments is returned.
601     """
602     if not sub_dict.has_key(self):
603     sub_dict[self]=Arc(self.getCenterPoint().substitute(sub_dict),self.getStartPoint().substitute(sub_dict),self.getEndPoint().substitute(sub_dict))
604     return sub_dict[self]
605    
606     def isColocated(self,primitive):
607     """
608     returns True curves are on the same position
609     """
610     if isinstance(primitive,Arc):
611     return (self.getCenterPoint().isColocated(primitive.getCenterPoint())) and ( \
612     (self.getEndPoint().isColocated(primitive.getEndPoint()) and self.getStartPoint().isColocated(primitive.getStartPoint()) ) \
613     or (self.getEndPoint().isColocated(primitive.getStartPoint()) and self.getStartPoint().isColocated(primitive.getEndPoint()) ) )
614     else:
615     return False
616    
617     class Arc(ArcBase):
618 gross 898 """
619 gross 917 defines an arc which is strictly, smaller than Pi
620 gross 898 """
621     def __init__(self,center,start,end):
622     """
623     creates an arc by the start point, end point and center
624     """
625 gross 917 if not isinstance(center,Point): raise TypeError("center needs to be a Point object.")
626     if not isinstance(end,Point): raise TypeError("end needs to be a Point object.")
627     if not isinstance(start,Point): raise TypeError("start needs to be a Point object.")
628     # TODO: check length of circle.
629 gross 928 ArcBase.__init__(self)
630 gross 898 self.__center=center
631 gross 916 self.__start=start
632     self.__end=end
633 gross 928 def __neg__(self):
634     return ReverseArc(self)
635 gross 898
636 gross 916 def getStartPoint(self):
637 gross 898 """
638 gross 916 returns start point
639     """
640     return self.__start
641    
642     def getEndPoint(self):
643     """
644     returns end point
645     """
646     return self.__end
647    
648     def getCenterPoint(self):
649     """
650 gross 898 returns center
651     """
652     return self.__center
653    
654 gross 928 class ReverseArc(_ArcBase):
655     """
656     defines an arc which is strictly, smaller than Pi
657     """
658     def __init__(self,arc):
659 gross 916 """
660 gross 928 creates an arc by the start point, end point and center
661 gross 916 """
662 gross 928 if not isinstance(arc,Arc): raise TypeError("arc needs to be an instance of Arc.")
663     ArcBase.__init__(self)
664     self.__arc=arc
665 gross 916
666 gross 928 def getStartPoint(self):
667 gross 916 """
668 gross 928 returns start point
669 gross 916 """
670 gross 928 return self.__arc.getEndPoint()
671 gross 899
672 gross 928 def getEndPoint(self):
673     """
674     returns end point
675     """
676     return self.__arc.getStartPoint()
677 gross 916
678 gross 928 def getCenterPoint(self):
679 gross 916 """
680 gross 928 returns center
681 gross 916 """
682 gross 928 return self.__arc.getCenterPoint()
683 gross 916
684 gross 928 def __neg__(self):
685     return self.__arc
686 gross 923
687 gross 928 #====================================
688     class CurveLoop(Primitive):
689 gross 898 """
690 gross 928 An oriented loop of 1D primitives (= curves and arcs)
691 gross 898
692 gross 928 The loop must be closed and the L{Manifold1D}s should be oriented consistently.
693 gross 898 """
694     def __init__(self,*curves):
695     """
696     creates a polygon from a list of line curves. The curves must form a closed loop.
697     """
698     super(CurveLoop, self).__init__()
699 gross 923 if len(curves)<2:
700     raise TypeError("at least two curves have to be given.")
701 gross 899 for i in range(len(curves)):
702 gross 928 if not isinstance(curves[i],Manifold1D):
703     raise TypeError("%s-th argument is not a Manifold1D object."%i)
704 gross 923 # for the curves a loop:
705     used=[ False for i in curves]
706     self.__curves=[curves[0]]
707     used[0]=True
708     while not min(used):
709     found=False
710     for i in xrange(len(curves)):
711     if not used[i]:
712     if self.__curves[-1].getEndPoint() == curves[i].getStartPoint():
713     self.__curves.append(curves[i])
714     used[i]=True
715     found=True
716     break
717     if not found:
718     raise ValueError("loop is not closed.")
719     if not self.__curves[0].getStartPoint() == self.__curves[-1].getEndPoint():
720     raise ValueError("loop is not closed.")
721 gross 898
722     def getCurves(self):
723 gross 919 """
724     returns the curves defining the CurveLoop
725     """
726 gross 898 return self.__curves
727 gross 925
728 gross 898 def __len__(self):
729 gross 919 """
730     return the number of curves in the CurveLoop
731     """
732 gross 928 return len(self.getCurves())
733 gross 919
734 gross 928 def collectPrimitives(self):
735 gross 919 """
736     returns primitives used to construct the CurveLoop
737     """
738 gross 928 out=[self]
739     for c in self.getCurves(): out+=c.collectPrimitives()
740     return out
741 gross 919
742 gross 925 def substitute(self,sub_dict):
743 gross 919 """
744 gross 925 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
745     If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
746     with substituted arguments is returned.
747 gross 919 """
748 gross 925 if not sub_dict.has_key(self):
749     new_c=[]
750     for c in self.getCurves(): new_c.append(c.substitute(sub_dict))
751     sub_dict[self]=CurveLoop(*tuple(new_c))
752     return sub_dict[self]
753 gross 919
754     def isColocated(self,primitive):
755     """
756     returns True if each curve is collocted with a curve in primitive
757     """
758     if isinstance(primitive,CurveLoop):
759     if len(primitive) == len(self):
760     cp0=self.getCurves()
761     cp1=primitive.getCurves()
762     for c0 in cp0:
763     collocated = False
764 gross 925 for c1 in cp1:
765     collocated = collocated or c0.isColocated(c1)
766 gross 919 if not collocated: return False
767     return True
768     else:
769     return False
770     else:
771     return False
772    
773 gross 899 def getGmshCommand(self):
774 gross 928 """
775     returns the Gmsh command(s) to create the primitive
776     """
777 gross 899 out=""
778     for i in self.getCurves():
779     if len(out)>0:
780     out+=", %s"%i.getID()
781     else:
782     out="%s"%i.getID()
783     return "Line Loop(%s) = {%s};"%(self.getID(),out)
784 gross 898
785 gross 928 class Primitive2D(Primitive):
786 gross 898 """
787 gross 928 general two-dimensional primitive
788 gross 898 """
789 gross 928 def __init__(self):
790     """
791     create a two-dimensional primitive
792     """
793     super(Primitive2D, self).__init__()
794 gross 927
795 gross 928 def getBoundary(self):
796     """
797     returns a list of the 1D primitives forming the boundary of the Surface (including holes)
798     """
799     out=[]
800     for i in self.getPrimitives():
801     if isinstance(i, Manifold1D): out.append(i)
802     return out
803    
804     def getBoundary(self):
805     """
806     returns a list of the 1D primitives forming the boundary of the Surface (including holes)
807     """
808     out=[]
809     for i in self.getPrimitives():
810     if isinstance(i, Manifold1D): out.append(i)
811     return out
812    
813     def getBoundaryLoop(self):
814     """
815     returns the loop defining the outer boundary
816     """
817     raise NotImplementedError("getBoundaryLoop is not implemented for this class %s."%self.__class__.__name__)
818    
819     def getHoles(self):
820     """
821     returns the holes
822     """
823     raise NotImplementedError("getHoles is not implemented for this class %s."%self.__class__.__name__)
824    
825     def collectPrimitives(self):
826     """
827     returns primitives used to construct the Surface
828     """
829     out=[self] + self.getBoundaryLoop().collectPrimitives()
830     for i in self.getHoles(): out+=i.collectPrimitives()
831     return out
832    
833     class RuledSurface(Primitive2D):
834 gross 927 """
835     A ruled surface, i.e., a surface that can be interpolated using transfinite interpolation
836     """
837 gross 898 def __init__(self,loop):
838     """
839 gross 927 creates a ruled surface with boundary loop
840 gross 898
841 gross 927 @param loop: L{CurveLoop} defining the boundary of the surface.
842 gross 898 """
843 gross 927 super(RuledSurface, self).__init__()
844 gross 928 if not isinstance(loop,CurveLoop):
845 gross 898 raise TypeError("argument loop needs to be a CurveLoop object.")
846 gross 927 if len(loop)<2:
847     raise TypeError("the loop must contain at least two Curves.")
848     if len(loop)>4:
849     raise TypeError("the loop must contain at least three Curves.")
850    
851 gross 898 self.__loop=loop
852 gross 927
853 gross 898 def getBoundaryLoop(self):
854 gross 927 """
855 gross 928 returns the loop defining the outer boundary
856 gross 927 """
857     return self.__loop
858    
859 gross 928 def getHoles(self):
860     """
861     returns the holes
862     """
863     return []
864 gross 927
865 gross 899 def getGmshCommand(self):
866 gross 928 """
867     returns the Gmsh command(s) to create the primitive
868     """
869 gross 899 return "Ruled Surface(%s) = {%s};"%(self.getID(),self.getBoundaryLoop().getID())
870    
871 gross 927 def substitute(self,sub_dict):
872     """
873     returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
874     If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
875     with substituted arguments is returned.
876     """
877     if not sub_dict.has_key(self):
878 gross 928 sub_dict[self]=RuledSurface(self.getBoundaryLoop().substitute(sub_dict))
879 gross 927 return sub_dict[self]
880    
881     def isColocated(self,primitive):
882     """
883     returns True if each curve is collocted with a curve in primitive
884     """
885     if isinstance(primitive,RuledSurface):
886 gross 928 return self.getBoundaryLoop().isColocated(primitive.getBoundaryLoop())
887 gross 927 else:
888     return False
889    
890     def createRuledSurface(*curves):
891     """
892     an easier way to create a L{RuledSurface} from given curves.
893     """
894     return RuledSurface(CurveLoop(*curves))
895    
896 gross 928 class PlaneSurface(Primitive2D):
897 gross 898 """
898     a plane surface with holes
899     """
900     def __init__(self,loop,holes=[]):
901     """
902 gross 927 creates a plane surface with a hole
903 gross 898
904     @param loop: L{CurveLoop} defining the boundary of the surface
905     @param holes: list of L{CurveLoop} defining holes in the surface.
906     @note: A CurveLoop defining a hole should not have any lines in common with the exterior CurveLoop.
907     A CurveLoop defining a hole should not have any lines in common with another CurveLoop defining a hole in the same surface.
908     """
909 gross 927 super(PlaneSurface, self).__init__()
910     if not isinstance(loop,CurveLoop):
911     raise TypeError("argument loop needs to be a CurveLoop object.")
912 gross 928 for l in loop.getCurves():
913     if not isinstance(l,Line):
914     raise TypeError("loop may be formed by Lines only.")
915 gross 898 for i in range(len(holes)):
916 gross 928 if not isinstance(holes[i], CurveLoop):
917     raise TypeError("%i-th hole needs to be a CurveLoop object.")
918     for l in holes[i].getCurves():
919     if not isinstance(l,Line):
920     raise TypeError("holes may be formed by Lines only.")
921     #TODO: check if lines and holes are in a plane
922     #TODO: are holes really holes?
923     self.__loop=loop
924 gross 898 self.__holes=holes
925     def getHoles(self):
926 gross 927 """
927     returns the holes
928     """
929 gross 898 return self.__holes
930 gross 927 def getBoundaryLoop(self):
931     """
932     returns the loop defining the boundary
933     """
934     return self.__loop
935    
936 gross 899 def getGmshCommand(self):
937 gross 928 """
938     returns the Gmsh command(s) to create the primitive
939     """
940 gross 899 out=""
941     for i in self.getHoles():
942     if len(out)>0:
943     out+=", %s"%i.getID()
944     else:
945     out="%s"%i.getID()
946     if len(out)>0:
947     return "Plane Surface(%s) = {%s, %s};"%(self.getID(),self.getBoundaryLoop().getID(), out)
948     else:
949     return "Plane Surface(%s) = {%s};"%(self.getID(),self.getBoundaryLoop().getID())
950 gross 898
951 gross 927 def substitute(self,sub_dict):
952     """
953     returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
954     If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
955     with substituted arguments is returned.
956     """
957     if not sub_dict.has_key(self):
958 gross 928 sub_dict[self]=PlaneSurface(self.getBoundaryLoop().substitute(sub_dict),[ h.substitute(sub_dict) for h in self.getHoles()])
959 gross 927 return sub_dict[self]
960 gross 898
961 gross 927 def isColocated(self,primitive):
962 gross 898 """
963 gross 927 returns True if each curve is collocted with a curve in primitive
964     """
965     if isinstance(primitive,PlaneSurface):
966 gross 928 if self.getBoundaryLoop().isColocated(primitive.getBoundaryLoop()):
967     hs0=self.getHoles()
968     hs1=primitive.getHoles()
969     if len(hs0) == len(hs1):
970     for h0 in hs0:
971     collocated = False
972     for h1 in hs1:
973     collocated = collocated or h0.isColocated(h1)
974     if not collocated: return False
975     return True
976     return False
977 gross 927 else:
978     return False
979     else:
980     return False
981 gross 898
982 gross 899 class SurfaceLoop(Primitive):
983 gross 898 """
984 gross 928 a loop of 2D primitives. It defines the shell of a volume.
985 gross 898
986 gross 928 The loop must represent a closed shell, and the primitives should be oriented consistently.
987 gross 898 """
988     def __init__(self,*surfaces):
989     """
990     creates a surface loop
991     """
992     super(SurfaceLoop, self).__init__()
993 gross 928 if len(surfaces)<2:
994     raise TypeError("at least two surfaces have to be given.")
995 gross 899 for i in range(len(surfaces)):
996 gross 928 if not isinstance(surfaces[i],Primitive2D):
997     raise TypeError("%s-th argument is not a Primitive2D object."%i)
998     # for the curves a loop:
999     used=[ True for s in surfaces]
1000     self.__surfaces=[surfaces[0]]
1001     edges=[ e in surfaces[0].getBoundary() ]
1002     used_edges=[ False in surfaces[0].getBoundary() ]
1003     while min(used):
1004     found=False
1005     for i in xrange(len(surfaces)):
1006     if not used[i]:
1007     i_boundary=surfaces[i].getBoundary()
1008     for ib in xrange(i_boundary):
1009     if i_boundary[ib] in edges:
1010     if used_edges[edges.index(i_boundary[ib])]:
1011     raise TypeError("boundary segment %s is shared by more than one surface."%str(i_boundary[ib]))
1012     used_edges[edges.index(i_boundary[ib])]=True
1013     self.__surfaces.append(surfaces[i])
1014     for b in i_boundary:
1015     if not b in edges:
1016     edges.append(b)
1017     used_edges.append(False)
1018     found=True
1019     used[i]=True
1020     break
1021     if found: break
1022     if not found:
1023     raise ValueError("loop is not closed.")
1024     if min(used_edges):
1025     raise ValueError("loop is not closed. Surface is missing.")
1026     def __len__(self):
1027     """
1028     return the number of curves in the SurfaceLoop
1029     """
1030     return len(self.__surfaces)
1031 gross 898
1032     def getSurfaces(self):
1033 gross 928 """
1034     returns the surfaces defining the SurfaceLoop
1035     """
1036     return self.__curves
1037    
1038     def collectPrimitives(self):
1039     """
1040     returns primitives used to construct the SurfaceLoop
1041     """
1042     out=[self]
1043     for c in self.getSurfaces(): out+=c.collectPrimitives()
1044     return out
1045 gross 899 def getGmshCommand(self):
1046 gross 928 """
1047     returns the Gmsh command(s) to create the primitive
1048     """
1049 gross 899 out=""
1050     for i in self.getSurfaces():
1051     if len(out)>0:
1052     out+=", %s"%i.getID()
1053     else:
1054     out="%s"%i.getID()
1055     return "Surface Loop(%s) = {%s};"%(self.getID(),out)
1056 gross 928 def substitute(self,sub_dict):
1057     """
1058     returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
1059     If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
1060     with substituted arguments is returned.
1061     """
1062     if not sub_dict.has_key(self):
1063     new_s=[]
1064     for s in self.getCurves(): new_s.append(s.substitute(sub_dict))
1065     sub_dict[self]=SurfaceLoop(*tuple(new_s))
1066     return sub_dict[self]
1067 gross 898
1068 gross 928 def isColocated(self,primitive):
1069     """
1070     returns True if each surface is collocted with a curve in primitive and vice versa.
1071     """
1072     if isinstance(primitive,SurfaceLoop):
1073     if len(primitive) == len(self):
1074     sp0=self.getSurfaces()
1075     sp1=primitive.getCurves()
1076     for s0 in sp0:
1077     collocated = False
1078     for s1 in sp1:
1079     collocated = collocated or s0.isColocated(s1)
1080     if not collocated: return False
1081     return True
1082     else:
1083     return False
1084     else:
1085     return False
1086    
1087     #==========================
1088 gross 899 class Volume(Primitive):
1089 gross 898 """
1090     a volume with holes.
1091     """
1092     def __init__(self,loop,holes=[]):
1093     """
1094     creates a volume
1095    
1096     @param loop: L{SurfaceLoop} defining the boundary of the surface
1097     @param holes: list of L{SurfaceLoop} defining holes in the surface.
1098     @note: A SurfaceLoop defining a hole should not have any surfaces in common with the exterior SurfaceLoop.
1099     A SurfaceLoop defining a hole should not have any surfaces in common with another SurfaceLoop defining a hole in the same volume.
1100     """
1101     super(Volume, self).__init__()
1102 gross 899 if not loop.isSurfaceLoop():
1103 gross 898 raise TypeError("argument loop needs to be a SurfaceLoop object.")
1104     for i in range(len(holes)):
1105 gross 899 if not holes[i].isSurfaceLoop():
1106 gross 898 raise TypeError("%i th hole needs to be a SurfaceLoop object.")
1107     self.__loop=loop
1108     self.__holes=holes
1109     def getHoles(self):
1110     return self.__holes
1111     def getSurfaceLoop(self):
1112     return self.__loop
1113 gross 899 def __add__(self,other):
1114     return Volume(self.getSurfaceLoop()+other, holes=[h+other for h in self.getHoles()])
1115 gross 928 def collectPrimitives(self):
1116     out=[self] + self.getSurfaceLoop().collectPrimitives()
1117     for i in self.getHoles(): out+=i.collectPrimitives()
1118 gross 899 return out
1119 gross 916 def getConstructionPoints(self):
1120     out=self.getSurfaceLoop().getConstructionPoints()
1121 gross 902 for i in self.getHoles(): out|=i.Points()
1122     return out
1123 gross 899 def getGmshCommand(self):
1124 gross 928 """
1125     returns the Gmsh command(s) to create the primitive
1126     """
1127 gross 899 out=""
1128     for i in self.getHoles():
1129     if len(out)>0:
1130     out+=", %s"%i.getID()
1131     else:
1132     out="%s"%i.getID()
1133     if len(out)>0:
1134     return "Volume(%s) = {%s, %s};"%(self.getID(),self.getSurfaceLoop().getID(), out)
1135     else:
1136     return "Volume(%s) = {%s};"%(self.getID(),self.getSurfaceLoop().getID())
1137 gross 898
1138 gross 912 class ReversedPrimitive(object):
1139     def __init__(self,prim):
1140     self.__prim=prim
1141     def __getattr__(self,name):
1142     if name == "getID":
1143     return self.getReverseID
1144     else:
1145     return getattr(self.__prim,name)
1146     def getReverseID(self):
1147     return -self.__prim.getID()
1148    
1149 gross 899 class PropertySet(Primitive):
1150 gross 898 """
1151 gross 899 defines a group L{Primitive} objects.
1152 gross 898 """
1153     def __init__(self,tag=None,*items):
1154     super(PropertySet, self).__init__()
1155     self.__items=items
1156     self.__tag=tag
1157 gross 928 def collectPrimitives(self):
1158     out=[self]+self.getBoundaryLoop().collectPrimitives()
1159     for i in self.getHoles(): out+=i.collectPrimitives()
1160 gross 899 return out
1161    
1162     class PrimitiveStack(object):
1163     def __init__(self,*items):
1164     self.__prims=set()
1165     for i in items:
1166 gross 916 self.__prims|=i.getPrimitives()
1167 gross 899 self.__prims=list(self.__prims)
1168     self.__prims.sort()
1169    
1170     def getGmshCommands(self):
1171     out=""
1172     for i in self.__prims:
1173     out+=i.getGmshCommand()+"\n"
1174     return out

  ViewVC Help
Powered by ViewVC 1.1.26