/[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 915 - (hide annotations)
Thu Dec 14 06:12:53 2006 UTC (16 years, 3 months ago) by gross
File MIME type: text/x-python
File size: 20919 byte(s)
more testing on pycad
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     global global_primitive_id_counter
34     global_primitive_id_counter=1
35    
36     resetGlobalPrimitiveIdCounter()
37    
38 gross 899 class Primitive(object):
39 gross 898 """
40     template for elementary geometrical object
41     """
42     def __init__(self):
43     """
44 gross 915
45 gross 898 """
46 gross 899 global global_primitive_id_counter
47     self.__ID=global_primitive_id_counter
48     global_primitive_id_counter+=1
49 gross 915
50 gross 899 def getID(self):
51     return self.__ID
52 gross 915
53 gross 899 def __repr__(self):
54     return "%s(%s)"%(self.__class__.__name__,self.getID())
55     def __cmp__(self,other):
56     return cmp(self.getID(),other.getID())
57 gross 912 def getPoints(self):
58     """
59     returns the C{set} of points used to construct the primitive
60     """
61     out=set()
62     for i in self.getHistory():
63     if isinstance(i,Point): out.add(i)
64     return out
65    
66     def setLocalScale(self,factor=1.):
67     """
68     sets the local refinement factor
69     """
70     for p in self.getPoints(): p.setLocalScale(factor)
71    
72 gross 899 def isPoint(self):
73 gross 912 """
74     returns C{True} is the primitive is a L{Point}
75     """
76 gross 899 return False
77     def isCurve(self):
78 gross 912 """
79     returns C{True} is the primitive is a L{Curve}
80     """
81 gross 899 return False
82     def isSurface(self):
83 gross 912 """
84     returns C{True} is the primitive is a L{Surface}
85     """
86 gross 899 return False
87     def isCurveLoop(self):
88 gross 912 """
89     returns C{True} is the primitive is a L{CurveLoop}
90     """
91 gross 899 return False
92     def isSurfaceLoop(self):
93 gross 912 """
94     returns C{True} is the primitive is a L{SurfaceLoop}
95     """
96 gross 899 return False
97 gross 912 def getHistory(self):
98     """
99     returns C{set} of primitive used to construct the primitive
100     """
101     return set()
102 gross 915 def copy(self):
103     """
104     returns a copy of the object
105     """
106     return Primitive()
107 gross 912
108 gross 915 def apply(self,transformation):
109     """
110     returns a new object by applying the transformation
111     """
112     raise NotImplementedError("apply is not implemented for this class %s."%self.__class__.__name__)
113    
114     def modifyBy(self,transformation):
115     """
116     modifies the object by applying a transformation
117     """
118     raise NotImplementedError("modifyBy not implemented for this class %s."%self.__class__.__name__)
119    
120     def __add__(self,other):
121     """
122     returns a new object shifted by other
123     """
124     return self.apply(Translation(numarray.array(other,_TYPE)))
125    
126     def __sub__(self,other):
127     """
128     returns a new object shifted by other
129     """
130     return self.apply(Translation(-numarray.array(other,_TYPE)))
131    
132     def __iadd__(self,other):
133     """
134     shifts the point by other
135     """
136     self.modifyBy(Translation(numarray.array(other,_TYPE)))
137     return self
138    
139     def __isub__(self,other):
140     """
141     shifts the point by -other
142     """
143     self.modifyBy(Translation(-numarray.array(other,_TYPE)))
144     return self
145    
146     def __imul__(self,other):
147     """
148     modifies object by applying L{Transformation} other. If other is not a L{Transformation} it will try convert it.
149     """
150     if isinstance(other,int) or isinstance(other,float):
151     trafo=Dilation(other)
152     elif isinstance(other,numarray.NumArray):
153     trafo=Translation(other)
154     elif isinstance(other,Transformation):
155     trafo=other
156     else:
157     raise TypeError, "cannot convert argument to Trnsformation class object."
158     self.modifyBy(trafo)
159     return self
160    
161     def __rmul__(self,other):
162     """
163     applies L{Transformation} other to object. If other is not a L{Transformation} it will try convert it.
164     """
165     if isinstance(other,int) or isinstance(other,float):
166     trafo=Dilation(other)
167     elif isinstance(other,numarray.NumArray):
168     trafo=Translation(other)
169     elif isinstance(other,Transformation):
170     trafo=other
171     else:
172     raise TypeError, "cannot convert argument to Trnsformation class object."
173     return self.apply(trafo)
174    
175 gross 899 def __neg__(self):
176     return ReversedPrimitive(self)
177 gross 915
178 gross 902 def getGmshCommand(self):
179     raise NotImplementedError("getGmshCommand is not implemented for this class %s."%self.__class__.__name__)
180 gross 898
181 gross 899 class Point(Primitive):
182 gross 898 """
183     a three dimensional point
184     """
185     def __init__(self,x=0.,y=0.,z=0.,local_scale=1.):
186     """
187 gross 912 creates a point with coorinates x,y,z with the local refinement factor local_scale
188 gross 898 """
189     super(Point, self).__init__()
190 gross 915 self.setCoordinates(numarray.array([x,y,z],_TYPE))
191 gross 899 self.setLocalScale(local_scale)
192 gross 915
193 gross 898 def setLocalScale(self,factor=1.):
194 gross 902 """
195 gross 912 sets the local refinement factor
196 gross 902 """
197 gross 912 if factor<=0.:
198     raise ValueError("scaling factor must be positive.")
199 gross 898 self.__local_scale=factor
200 gross 902 def getLocalScale(self):
201 gross 912 """
202     returns the local refinement factor
203     """
204 gross 902 return self.__local_scale
205 gross 912 def getCoordinates(self):
206     """
207     returns the coodinates of the point as L{numarray.NumArray} object
208     """
209     return self._x
210 gross 915 def setCoordinates(self,x):
211 gross 912 """
212     returns the coodinates of the point as L{numarray.NumArray} object
213     """
214 gross 915 if not isinstance(x, numarray.NumArray):
215     self._x=numarray.array(x,_TYPE)
216     else:
217     self._x=x
218    
219 gross 912 def getHistory(self):
220     """
221     returns C{set} of primitive used to construct the primitive
222     """
223     return set([self])
224    
225     def isColocated(self,point,tol=1.e-11):
226     """
227     returns True if L{Point} point is colocation (same coordinates)
228     that means if |self-point| <= tol * max(|self|,|point|)
229     """
230     if isinstance(point,Point):
231     point=point.getCoordinates()
232     c=self.getCoordinates()
233     d=c-point
234     return numarray.dot(d,d)<=tol**2*max(numarray.dot(c,c),numarray.dot(point,point))
235    
236 gross 902 def copy(self):
237 gross 915 """
238     returns a copy of the point
239     """
240 gross 902 c=self.getCoordinates()
241     return Point(c[0],c[1],c[2],local_scale=self.getLocalScale())
242 gross 915
243     def modifyBy(self,transformation):
244     """
245     modifies the coordinates by applying a transformation
246     """
247     self.setCoordinates(transformation(self.getCoordinates()))
248    
249     def apply(self,transformation):
250     """
251     returns a new L{Point} by applying the transformation
252     """
253     new_p=self.copy()
254     new_p.modifyBy(transformation)
255     return new_p
256    
257    
258     def getGmshCommand(self, local_scaling_factor=1.):
259 gross 899 c=self.getCoordinates()
260 gross 915 return "Point(%s) = {%s , %s, %s , %s };"%(self.getID(),c[0],c[1],c[2], self.getLocalScale()*local_scaling_factor)
261 gross 898
262 gross 899 class Curve(Primitive):
263 gross 898 """
264     a curve
265     """
266     def __init__(self,*args):
267     """
268     defines a curve form a set of control points
269     """
270     super(Curve, self).__init__()
271     l=len(args)
272     for i in range(l):
273 gross 899 if not args[i].isPoint():
274     raise TypeError("%s-th argument is not a Point object."%i)
275     self.__nodes=args
276 gross 898 def __len__(self):
277     return len(self.__nodes)
278 gross 899 def isCurve(self):
279     return True
280 gross 898 def getStart(self):
281     """
282     returns start point
283     """
284     return self.__nodes[0]
285    
286     def getEnd(self):
287     """
288     returns end point
289     """
290     return self.__nodes[-1]
291    
292     def getNodes(self):
293     """
294     returns a list of the nodes
295     """
296     return self.__nodes
297 gross 899 def getGmshCommand(self):
298     out=""
299     for i in self.getNodes():
300     if len(out)>0:
301     out+=", %s"%i.getID()
302     else:
303     out="%s"%i.getID()
304     return "Spline(%s) = {%s};"%(self.getID(),out)
305 gross 902 def getHistory(self):
306     out=set([self])
307     for i in self.getNodes(): out|=i.getHistory()
308     return out
309     def getPoints(self):
310     out=set()
311     for i in self.getNodes(): out|=i.getPoints()
312     return out
313 gross 898
314 gross 902
315 gross 898 class BezierCurve(Curve):
316     """
317     a Bezier curve
318     """
319     def __neg__(self):
320 gross 899 """
321     returns the line segment with swapped start and end points
322     """
323     return BezierCurve(self.getNodes()[::-1])
324     def __add__(self,other):
325     return BezierCurve([p+other for p in self.getNodes()])
326     def getGmshCommand(self):
327     out=""
328     for i in self.getNodes():
329     if len(out)>0:
330     out+=", %s"%i.getID()
331     else:
332     out="%s"%i.getID()
333     return "Bezier(%s) = {%s};"%(self.getID(),out)
334 gross 898
335     class BSplineCurve(Curve):
336     """
337     a BSpline curve. Control points may be repeated.
338     """
339     def __neg__(self):
340 gross 899 """
341     returns the line segment with swapped start and end points
342     """
343     return BSplineCurve(self.getNodes()[::-1])
344     def __add__(self,other):
345     return BSplineCurve([p+other for p in self.getNodes()])
346     def getGmshCommand(self):
347     out=""
348     for i in self.getNodes():
349     if len(out)>0:
350     out+=", %s"%i.getID()
351     else:
352     out="%s"%i.getID()
353     return "BSpline(%s) = {%s};"%(self.getID(),out)
354 gross 898
355     class Line(Curve):
356     """
357     a line is defined by two L{Point}s
358     """
359 gross 899 def __init__(self,start,end):
360     """
361     defines a curve form a set of control points
362     """
363     super(Line, self).__init__(start,end)
364 gross 898 def __neg__(self):
365 gross 899 return ReversedPrimitive(self)
366     def __add__(self,other):
367     return Line(self.getEnd()+other,self.getStart()+other)
368     def getGmshCommand(self):
369     return "Line(%s) = {%s, %s};"%(self.getID(),self.getStart().getID(),self.getEnd().getID())
370 gross 898
371     class Arc(Curve):
372     """
373     defines an arc
374     """
375     def __init__(self,center,start,end):
376     """
377     creates an arc by the start point, end point and center
378     """
379 gross 899 if center.isPoint():
380 gross 898 raise TypeError("center needs to be a Point object.")
381     super(Arc, self).__init__(start,end)
382     self.__center=center
383    
384     def getCenter(self):
385     """
386     returns center
387     """
388     return self.__center
389 gross 899 def __add__(self,other):
390     return Arc(self.getCenter()+other,self.getStart()+other,self.getEnd()+other)
391 gross 898
392 gross 899 def getHistory(self):
393     out=set([self])
394     out|=self.getCenter().getHistory()
395     for i in self.getNodes(): out|=i.getHistory()
396     return out
397 gross 902 def getPoints(self):
398     out=self.getCenter().getPoints()
399     for i in self.getNodes(): out|=i.getPoints()
400     return out
401 gross 899 def getGmshCommand(self):
402     return "Circle(%s) = {%s, %s, %s};"%(self.getID(),self.getStart().getID(),self.getCenter().getID(),self.getEnd().getID())
403    
404     class CurveLoop(Primitive):
405 gross 898 """
406     An oriented loop of curves.
407    
408     The loop must be closed and the L{Curves}s should be oriented consistently.
409     """
410     def __init__(self,*curves):
411     """
412     creates a polygon from a list of line curves. The curves must form a closed loop.
413     """
414     super(CurveLoop, self).__init__()
415 gross 899 self.__curves=[]
416     self.addCurve(*curves)
417     def addCurve(self,*curves):
418     for i in range(len(curves)):
419     if not curves[i].isCurve():
420     raise TypeError("%s-th argument is not a Curve object."%i)
421     self.__curves+=curves
422 gross 898
423 gross 899 def isCurveLoop(self):
424     return True
425 gross 898 def getCurves(self):
426     return self.__curves
427 gross 899 def __add__(self,other):
428     return CurveLoop(*tuple([c+other for c in self.getCurves()[::-1]]))
429 gross 898 def __len__(self):
430     return len(self.__curves)
431 gross 899 def getHistory(self):
432     out=set([self])
433     for i in self.getCurves(): out|=i.getHistory()
434     return out
435 gross 902 def getPoints(self):
436     out=set()
437     for i in self.getCurves(): out|=i.getPoints()
438     return out
439 gross 899 def getGmshCommand(self):
440     out=""
441     for i in self.getCurves():
442     if len(out)>0:
443     out+=", %s"%i.getID()
444     else:
445     out="%s"%i.getID()
446     return "Line Loop(%s) = {%s};"%(self.getID(),out)
447 gross 898
448 gross 899 class Surface(Primitive):
449 gross 898 """
450     a surface
451     """
452     def __init__(self,loop):
453     """
454     creates a surface with boundary loop
455    
456     @param loop: L{CurveLoop} defining the boundary of the surface
457     """
458     super(Surface, self).__init__()
459 gross 899 if not loop.isCurveLoop():
460 gross 898 raise TypeError("argument loop needs to be a CurveLoop object.")
461     self.__loop=loop
462 gross 899 def isSurface(self):
463     return True
464 gross 898 def getBoundaryLoop(self):
465     return self.__loop
466 gross 899 def __add__(self,other):
467     return Surface(self.getBoundaryLoop()+other)
468     def getHistory(self):
469     out=set([self]) | self.getBoundaryLoop().getHistory()
470     return out
471 gross 902 def getPoints(self):
472     return self.getBoundaryLoop().getPoints()
473 gross 899 def getGmshCommand(self):
474     return "Ruled Surface(%s) = {%s};"%(self.getID(),self.getBoundaryLoop().getID())
475    
476 gross 898 class PlaneSurface(Surface):
477     """
478     a plane surface with holes
479     """
480     def __init__(self,loop,holes=[]):
481     """
482     creates a plane surface.
483    
484     @param loop: L{CurveLoop} defining the boundary of the surface
485     @param holes: list of L{CurveLoop} defining holes in the surface.
486     @note: A CurveLoop defining a hole should not have any lines in common with the exterior CurveLoop.
487     A CurveLoop defining a hole should not have any lines in common with another CurveLoop defining a hole in the same surface.
488     """
489     super(PlaneSurface, self).__init__(loop)
490     for i in range(len(holes)):
491 gross 899 if not holes[i].inCurveLoop():
492 gross 898 raise TypeError("%i th hole needs to be a CurveLoop object.")
493     self.__holes=holes
494     def getHoles(self):
495     return self.__holes
496 gross 899 def __add__(self,other):
497     return PlaneSurface(self.getBoundaryLoop()+other, holes=[h+other for h in self.getHoles()])
498     def getHistory(self):
499     out=set([self]) | self.getBoundaryLoop().getHistory()
500     for i in self.getHoles(): out|=i.getHistory()
501     return out
502 gross 902 def getPoints(self):
503     out=self.getBoundaryLoop().getPoints()
504     for i in self.getHoles(): out|=i.getPoints()
505     return out
506 gross 899 def getGmshCommand(self):
507     out=""
508     for i in self.getHoles():
509     if len(out)>0:
510     out+=", %s"%i.getID()
511     else:
512     out="%s"%i.getID()
513     if len(out)>0:
514     return "Plane Surface(%s) = {%s, %s};"%(self.getID(),self.getBoundaryLoop().getID(), out)
515     else:
516     return "Plane Surface(%s) = {%s};"%(self.getID(),self.getBoundaryLoop().getID())
517 gross 898
518     class RuledSurface(Surface):
519     """
520     A ruled surface, i.e., a surface that can be interpolated using transfinite interpolation
521     """
522     def __init__(self,loop):
523     """
524     creates a ruled surface from a
525    
526     @param loop: L{CurveLoop} defining the boundary of the surface. There is a restriction of composed of either three or four L{Curve} objects.
527     """
528 gross 899 if not loop.isCurveLoop():
529 gross 898 raise TypeError("argument loop needs to be a CurveLoop object.")
530     if len(loop)<3:
531     raise TypeError("the loop must contain at least three Curves.")
532     super(RuledSurface, self).__init__(loop)
533 gross 899 def __add__(self,other):
534     return RuledSurface(self.getBoundaryLoop()+other)
535     def getGmshCommand(self):
536     return "Ruled Surface(%s) = {%s};"%(self.getID(),self.getBoundaryLoop().getID())
537 gross 898
538 gross 899 class SurfaceLoop(Primitive):
539 gross 898 """
540     a surface loop. It defines the shell of a volume.
541    
542     The loop must represent a closed shell, and the L{Surface}s should be oriented consistently.
543     """
544     def __init__(self,*surfaces):
545     """
546     creates a surface loop
547     """
548     super(SurfaceLoop, self).__init__()
549 gross 899 self.__surfaces=[]
550     self.addSurface(*surfaces)
551     def addSurface(self,*surfaces):
552     for i in range(len(surfaces)):
553     if not surfaces[i].isSurface():
554     raise TypeError("%s-th argument is not a Surface object."%i)
555     self.__surfaces+=surfaces
556 gross 898
557 gross 899 def isSurfaceLoop(self):
558     return True
559 gross 898 def getSurfaces(self):
560     return self.__surfaces
561 gross 899 def __add__(self,other):
562     return SurfaceLoop([c+other for c in self.getSurfaces])
563 gross 898 def __len__(self):
564     return len(self.__surfaces)
565 gross 899 def getHistory(self):
566     out=set([self])
567     for i in self.getSurfaces(): out|=i.getHistory()
568     return out
569 gross 902 def getPoints(self):
570     out=set()
571     for i in self.getSurfaces(): out|=i.getPoints()
572     return out
573 gross 899 def getGmshCommand(self):
574     out=""
575     for i in self.getSurfaces():
576     if len(out)>0:
577     out+=", %s"%i.getID()
578     else:
579     out="%s"%i.getID()
580     return "Surface Loop(%s) = {%s};"%(self.getID(),out)
581 gross 898
582 gross 899 class Volume(Primitive):
583 gross 898 """
584     a volume with holes.
585     """
586     def __init__(self,loop,holes=[]):
587     """
588     creates a volume
589    
590     @param loop: L{SurfaceLoop} defining the boundary of the surface
591     @param holes: list of L{SurfaceLoop} defining holes in the surface.
592     @note: A SurfaceLoop defining a hole should not have any surfaces in common with the exterior SurfaceLoop.
593     A SurfaceLoop defining a hole should not have any surfaces in common with another SurfaceLoop defining a hole in the same volume.
594     """
595     super(Volume, self).__init__()
596 gross 899 if not loop.isSurfaceLoop():
597 gross 898 raise TypeError("argument loop needs to be a SurfaceLoop object.")
598     for i in range(len(holes)):
599 gross 899 if not holes[i].isSurfaceLoop():
600 gross 898 raise TypeError("%i th hole needs to be a SurfaceLoop object.")
601     self.__loop=loop
602     self.__holes=holes
603     def getHoles(self):
604     return self.__holes
605     def getSurfaceLoop(self):
606     return self.__loop
607 gross 899 def __add__(self,other):
608     return Volume(self.getSurfaceLoop()+other, holes=[h+other for h in self.getHoles()])
609     def getHistory(self):
610     out=set([self]) | self.getSurfaceLoop().getHistory()
611     for i in self.getHoles(): out|=i.getHistory()
612     return out
613 gross 902 def getPoints(self):
614     out=self.getSurfaceLoop().getPoints()
615     for i in self.getHoles(): out|=i.Points()
616     return out
617 gross 899 def getGmshCommand(self):
618     out=""
619     for i in self.getHoles():
620     if len(out)>0:
621     out+=", %s"%i.getID()
622     else:
623     out="%s"%i.getID()
624     if len(out)>0:
625     return "Volume(%s) = {%s, %s};"%(self.getID(),self.getSurfaceLoop().getID(), out)
626     else:
627     return "Volume(%s) = {%s};"%(self.getID(),self.getSurfaceLoop().getID())
628 gross 898
629 gross 912 class ReversedPrimitive(object):
630     def __init__(self,prim):
631     self.__prim=prim
632     def __getattr__(self,name):
633     if name == "getID":
634     return self.getReverseID
635     else:
636     return getattr(self.__prim,name)
637     def getReverseID(self):
638     return -self.__prim.getID()
639    
640 gross 899 class PropertySet(Primitive):
641 gross 898 """
642 gross 899 defines a group L{Primitive} objects.
643 gross 898 """
644     def __init__(self,tag=None,*items):
645     super(PropertySet, self).__init__()
646     self.__items=items
647     self.__tag=tag
648 gross 899 def getHistory(self):
649     out=set([self, self.getBoundaryLoop().getHistory()])
650     for i in self.getHoles(): out|=i.getHistory()
651     return out
652    
653     class PrimitiveStack(object):
654     def __init__(self,*items):
655     self.__prims=set()
656     for i in items:
657     self.__prims|=i.getHistory()
658     self.__prims=list(self.__prims)
659     self.__prims.sort()
660    
661     def getGmshCommands(self):
662     out=""
663     for i in self.__prims:
664     out+=i.getGmshCommand()+"\n"
665     return out

  ViewVC Help
Powered by ViewVC 1.1.26