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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 915 - (show annotations)
Thu Dec 14 06:12:53 2006 UTC (13 years, 8 months ago) by gross
File MIME type: text/x-python
File size: 20919 byte(s)
more testing on pycad
1 # $Id:$
2
3 """
4 Geometrical Primitives
5
6 the concept is inspired by gmsh and very much focused on the fact that
7 the classes are used to wrk with gmsh.
8
9 @var __author__: name of author
10 @var __copyright__: copyrights
11 @var __license__: licence agreement
12 @var __url__: url entry point on documentation
13 @var __version__: version
14 @var __date__: date of the version
15 """
16
17
18 __author__="Lutz Gross, l.gross@uq.edu.au"
19 __copyright__=""" Copyright (c) 2006 by ACcESS MNRF
20 http://www.access.edu.au
21 Primary Business: Queensland, Australia"""
22 __license__="""Licensed under the Open Software License version 3.0
23 http://www.opensource.org/licenses/osl-3.0.php"""
24 __url__="http://www.iservo.edu.au/esys/escript"
25 __version__="$Revision:$"
26 __date__="$Date:$"
27
28 import numarray
29 from transformations import _TYPE, Translation, Dilation, Transformation
30
31
32 def resetGlobalPrimitiveIdCounter():
33 global global_primitive_id_counter
34 global_primitive_id_counter=1
35
36 resetGlobalPrimitiveIdCounter()
37
38 class Primitive(object):
39 """
40 template for elementary geometrical object
41 """
42 def __init__(self):
43 """
44
45 """
46 global global_primitive_id_counter
47 self.__ID=global_primitive_id_counter
48 global_primitive_id_counter+=1
49
50 def getID(self):
51 return self.__ID
52
53 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 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 def isPoint(self):
73 """
74 returns C{True} is the primitive is a L{Point}
75 """
76 return False
77 def isCurve(self):
78 """
79 returns C{True} is the primitive is a L{Curve}
80 """
81 return False
82 def isSurface(self):
83 """
84 returns C{True} is the primitive is a L{Surface}
85 """
86 return False
87 def isCurveLoop(self):
88 """
89 returns C{True} is the primitive is a L{CurveLoop}
90 """
91 return False
92 def isSurfaceLoop(self):
93 """
94 returns C{True} is the primitive is a L{SurfaceLoop}
95 """
96 return False
97 def getHistory(self):
98 """
99 returns C{set} of primitive used to construct the primitive
100 """
101 return set()
102 def copy(self):
103 """
104 returns a copy of the object
105 """
106 return Primitive()
107
108 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 def __neg__(self):
176 return ReversedPrimitive(self)
177
178 def getGmshCommand(self):
179 raise NotImplementedError("getGmshCommand is not implemented for this class %s."%self.__class__.__name__)
180
181 class Point(Primitive):
182 """
183 a three dimensional point
184 """
185 def __init__(self,x=0.,y=0.,z=0.,local_scale=1.):
186 """
187 creates a point with coorinates x,y,z with the local refinement factor local_scale
188 """
189 super(Point, self).__init__()
190 self.setCoordinates(numarray.array([x,y,z],_TYPE))
191 self.setLocalScale(local_scale)
192
193 def setLocalScale(self,factor=1.):
194 """
195 sets the local refinement factor
196 """
197 if factor<=0.:
198 raise ValueError("scaling factor must be positive.")
199 self.__local_scale=factor
200 def getLocalScale(self):
201 """
202 returns the local refinement factor
203 """
204 return self.__local_scale
205 def getCoordinates(self):
206 """
207 returns the coodinates of the point as L{numarray.NumArray} object
208 """
209 return self._x
210 def setCoordinates(self,x):
211 """
212 returns the coodinates of the point as L{numarray.NumArray} object
213 """
214 if not isinstance(x, numarray.NumArray):
215 self._x=numarray.array(x,_TYPE)
216 else:
217 self._x=x
218
219 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 def copy(self):
237 """
238 returns a copy of the point
239 """
240 c=self.getCoordinates()
241 return Point(c[0],c[1],c[2],local_scale=self.getLocalScale())
242
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 c=self.getCoordinates()
260 return "Point(%s) = {%s , %s, %s , %s };"%(self.getID(),c[0],c[1],c[2], self.getLocalScale()*local_scaling_factor)
261
262 class Curve(Primitive):
263 """
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 if not args[i].isPoint():
274 raise TypeError("%s-th argument is not a Point object."%i)
275 self.__nodes=args
276 def __len__(self):
277 return len(self.__nodes)
278 def isCurve(self):
279 return True
280 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 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 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
314
315 class BezierCurve(Curve):
316 """
317 a Bezier curve
318 """
319 def __neg__(self):
320 """
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
335 class BSplineCurve(Curve):
336 """
337 a BSpline curve. Control points may be repeated.
338 """
339 def __neg__(self):
340 """
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
355 class Line(Curve):
356 """
357 a line is defined by two L{Point}s
358 """
359 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 def __neg__(self):
365 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
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 if center.isPoint():
380 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 def __add__(self,other):
390 return Arc(self.getCenter()+other,self.getStart()+other,self.getEnd()+other)
391
392 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 def getPoints(self):
398 out=self.getCenter().getPoints()
399 for i in self.getNodes(): out|=i.getPoints()
400 return out
401 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 """
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 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
423 def isCurveLoop(self):
424 return True
425 def getCurves(self):
426 return self.__curves
427 def __add__(self,other):
428 return CurveLoop(*tuple([c+other for c in self.getCurves()[::-1]]))
429 def __len__(self):
430 return len(self.__curves)
431 def getHistory(self):
432 out=set([self])
433 for i in self.getCurves(): out|=i.getHistory()
434 return out
435 def getPoints(self):
436 out=set()
437 for i in self.getCurves(): out|=i.getPoints()
438 return out
439 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
448 class Surface(Primitive):
449 """
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 if not loop.isCurveLoop():
460 raise TypeError("argument loop needs to be a CurveLoop object.")
461 self.__loop=loop
462 def isSurface(self):
463 return True
464 def getBoundaryLoop(self):
465 return self.__loop
466 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 def getPoints(self):
472 return self.getBoundaryLoop().getPoints()
473 def getGmshCommand(self):
474 return "Ruled Surface(%s) = {%s};"%(self.getID(),self.getBoundaryLoop().getID())
475
476 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 if not holes[i].inCurveLoop():
492 raise TypeError("%i th hole needs to be a CurveLoop object.")
493 self.__holes=holes
494 def getHoles(self):
495 return self.__holes
496 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 def getPoints(self):
503 out=self.getBoundaryLoop().getPoints()
504 for i in self.getHoles(): out|=i.getPoints()
505 return out
506 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
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 if not loop.isCurveLoop():
529 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 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
538 class SurfaceLoop(Primitive):
539 """
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 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
557 def isSurfaceLoop(self):
558 return True
559 def getSurfaces(self):
560 return self.__surfaces
561 def __add__(self,other):
562 return SurfaceLoop([c+other for c in self.getSurfaces])
563 def __len__(self):
564 return len(self.__surfaces)
565 def getHistory(self):
566 out=set([self])
567 for i in self.getSurfaces(): out|=i.getHistory()
568 return out
569 def getPoints(self):
570 out=set()
571 for i in self.getSurfaces(): out|=i.getPoints()
572 return out
573 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
582 class Volume(Primitive):
583 """
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 if not loop.isSurfaceLoop():
597 raise TypeError("argument loop needs to be a SurfaceLoop object.")
598 for i in range(len(holes)):
599 if not holes[i].isSurfaceLoop():
600 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 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 def getPoints(self):
614 out=self.getSurfaceLoop().getPoints()
615 for i in self.getHoles(): out|=i.Points()
616 return out
617 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
629 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 class PropertySet(Primitive):
641 """
642 defines a group L{Primitive} objects.
643 """
644 def __init__(self,tag=None,*items):
645 super(PropertySet, self).__init__()
646 self.__items=items
647 self.__tag=tag
648 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