/[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 932 - (show annotations)
Fri Jan 19 09:27:15 2007 UTC (12 years, 7 months ago) by gross
File MIME type: text/x-python
File size: 45274 byte(s)
Design added. The loops tests are switched off to allow faults.
1 # $Id:$
2
3 """
4 Geometrical Primitives
5
6 the concept is inspired by gmsh and very much focused on the fact that
7 the classes are used to wrk with gmsh.
8
9 @var __author__: name of author
10 @var __copyright__: copyrights
11 @var __license__: licence agreement
12 @var __url__: url entry point on documentation
13 @var __version__: version
14 @var __date__: date of the version
15 """
16
17
18 __author__="Lutz Gross, l.gross@uq.edu.au"
19 __copyright__=""" Copyright (c) 2006 by ACcESS MNRF
20 http://www.access.edu.au
21 Primary Business: Queensland, Australia"""
22 __license__="""Licensed under the Open Software License version 3.0
23 http://www.opensource.org/licenses/osl-3.0.php"""
24 __url__="http://www.iservo.edu.au/esys/escript"
25 __version__="$Revision:$"
26 __date__="$Date:$"
27
28 import numarray
29 from transformations import _TYPE, Translation, Dilation, Transformation
30
31
32 def resetGlobalPrimitiveIdCounter():
33 """
34 initializes the global primitive ID counter
35 """
36 global global_primitive_id_counter
37 global_primitive_id_counter=1
38
39 def setToleranceForColocation(tol=1.e-11):
40 """
41 set the global tolerance for colocation checks to tol
42 """
43 global global_tolerance_for_colocation
44 global_tolerance_for_colocation=tol
45
46 def getToleranceForColocation():
47 """
48 returns the global tolerance for colocation checks
49 """
50 return global_tolerance_for_colocation
51
52 resetGlobalPrimitiveIdCounter()
53 setToleranceForColocation()
54
55
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):
173 """
174 A general primitive
175 """
176 def __init__(self):
177 """
178 initializes PrimitiveBase instance object with id
179 """
180 global global_primitive_id_counter
181 self.__ID=global_primitive_id_counter
182 global_primitive_id_counter+=1
183
184 def getID(self):
185 """
186 returns the primitive ID
187 """
188 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):
197 return "%s(%s)"%(self.__class__.__name__,self.getID())
198
199 def getUnderlyingPrimitive(self):
200 """
201 returns the underlying primitive
202 """
203 return self
204 def hasSameOrientation(self,other):
205 """
206 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 __neg__(self):
211 """
212 returns a view onto the curve with reversed ordering
213
214 @note: this class is overwritten by subclass
215 """
216 raise NotImplementedError("__neg__ is not implemented.")
217
218 def getGmshCommand(self, local_scaling_factor=1.):
219 """
220 returns the Gmsh command(s) to create the primitive
221
222 @note: this class is overwritten by subclass
223 """
224 raise NotImplementedError("getGmshCommand is not implemented.")
225
226 def substitute(self,sub_dict):
227 """
228 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 raise NotImplementedError("substitute is not implemented.")
235
236 def collectPrimitiveBases(self):
237 """
238 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 raise NotImplementedError("collectPrimitiveBases is not implemented.")
243
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 the underlying primitive
274 """
275 return self.__primitive
276
277 def hasSameOrientation(self,other):
278 """
279 returns True if other is the same primitive and has the same orientation
280 """
281 return self == other and isinstance(other,ReversePrimitive)
282
283 def __repr__(self):
284 return "-%s(%s)"%(self.__primitive.__class__.__name__,self.getID())
285
286 def getDirectedID(self):
287 """
288 returns the primitive ID where a negative signs means that the reversed ordring is used.
289 """
290 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):
303 """
304 returns a view onto the curve with reversed ordering
305 """
306 return self.__primitive
307
308 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
331 """
332 def __init__(self,x=0.,y=0.,z=0.,local_scale=1.):
333 """
334 creates a point with coorinates x,y,z with the local refinement factor local_scale
335 """
336 PrimitiveBase.__init__(self)
337 Primitive.__init__(self)
338 self.setCoordinates(numarray.array([x,y,z],_TYPE))
339 self.setLocalScale(local_scale)
340
341 def setLocalScale(self,factor=1.):
342 """
343 sets the local refinement factor
344 """
345 if factor<=0.:
346 raise ValueError("scaling factor must be positive.")
347 self.__local_scale=factor
348
349 def getLocalScale(self):
350 """
351 returns the local refinement factor
352 """
353 return self.__local_scale
354 def getCoordinates(self):
355 """
356 returns the coodinates of the point as L{numarray.NumArray} object
357 """
358 return self._x
359 def setCoordinates(self,x):
360 """
361 returns the coodinates of the point as L{numarray.NumArray} object
362 """
363 if not isinstance(x, numarray.NumArray):
364 self._x=numarray.array(x,_TYPE)
365 else:
366 self._x=x
367
368 def collectPrimitiveBases(self):
369 """
370 returns primitives used to construct the primitive
371 """
372 return [self]
373
374 def isColocated(self,primitive):
375 """
376 returns True if L{Point} primitive is colocation (same coordinates)
377 that means if |self-primitive| <= tol * max(|self|,|primitive|)
378 """
379 if isinstance(primitive,Point):
380 primitive=primitive.getCoordinates()
381 c=self.getCoordinates()
382 d=c-primitive
383 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 """
389 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
390 If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
391 with substituted arguments is returned.
392 """
393 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()
410 return "Point(%s) = {%s , %s, %s , %s };"%(self.getID(),c[0],c[1],c[2], self.getLocalScale()*local_scaling_factor)
411
412 def __neg__(self):
413 """
414 returns a view of the object with reverse orientiention. As a point has no direction the object itself is returned.
415 """
416 return self
417
418 class Manifold1D(PrimitiveBase):
419 """
420 general one-dimensional minifold in 3D defined by a start and end point.
421 """
422 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 def getEndPoint(self):
435 """
436 returns end point
437 """
438 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 create curve
452 """
453 Manifold1D.__init__(self)
454
455 def __len__(self):
456 """
457 returns the number of control points
458 """
459 return len(self.getControlPoints())
460
461 def getStartPoint(self):
462 """
463 returns start point
464 """
465 return self.getControlPoints()[0]
466
467 def getEndPoint(self):
468 """
469 returns end point
470 """
471 return self.getControlPoints()[-1]
472
473 def getControlPoints(self):
474 """
475 returns a list of the points
476 """
477 raise NotImplementedError()
478
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=""
580 for i in self.getControlPoints():
581 if len(out)>0:
582 out+=", %s"%i.getDirectedID()
583 else:
584 out="%s"%i.getDirectedID()
585 return "Spline(%s) = {%s};"%(self.getID(),out)
586
587
588 class BezierCurve(Curve):
589 """
590 a Bezier curve
591 """
592 def getGmshCommand(self,scaling_factor=1.):
593 """
594 returns the Gmsh command(s) to create the Curve
595 """
596 out=""
597 for i in self.getControlPoints():
598 if len(out)>0:
599 out+=", %s"%i.getDirectedID()
600 else:
601 out="%s"%i.getDirectedID()
602 return "Bezier(%s) = {%s};"%(self.getID(),out)
603
604 class BSpline(Curve):
605 """
606 a BSpline curve. Control points may be repeated.
607 """
608 def getGmshCommand(self,scaling_factor=1.):
609 """
610 returns the Gmsh command(s) to create the Curve
611 """
612 out=""
613 for i in self.getControlPoints():
614 if len(out)>0:
615 out+=", %s"%i.getDirectedID()
616 else:
617 out="%s"%i.getDirectedID()
618 return "BSpline(%s) = {%s};"%(self.getID(),out)
619
620 class Line(Curve):
621 """
622 a line is defined by two pointDirecteds
623 """
624 def __init__(self,*points):
625 """
626 defines a line with start and end point
627 """
628 if len(points)!=2:
629 raise TypeError("Line needs two points")
630 Curve.__init__(self,*points)
631 def getGmshCommand(self,scaling_factor=1.):
632 """
633 returns the Gmsh command(s) to create the Curve
634 """
635 return "Line(%s) = {%s, %s};"%(self.getID(),self.getStartPoint().getDirectedID(),self.getEndPoint().getDirectedID())
636
637
638 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 which is strictly, smaller than Pi
664 """
665 def __init__(self,center,start,end):
666 """
667 creates an arc by the start point, end point and center
668 """
669 if not isinstance(center,Point): raise TypeError("center needs to be a Point object.")
670 if not isinstance(end,Point): raise TypeError("end needs to be a Point object.")
671 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
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 getCenterPoint(self):
697 """
698 returns center
699 """
700 return self.__center
701
702 def substitute(self,sub_dict):
703 """
704 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
705 If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
706 with substituted arguments is returned.
707 """
708 if not sub_dict.has_key(self):
709 sub_dict[self]=Arc(self.getCenterPoint().substitute(sub_dict),self.getStartPoint().substitute(sub_dict),self.getEndPoint().substitute(sub_dict))
710 return sub_dict[self]
711
712 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 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 one-dimensional manifolds (= curves and arcs)
763
764 The loop must be closed and the L{Manifold1D}s should be oriented consistently.
765 """
766 def __init__(self,*curves):
767 """
768 creates a polygon from a list of line curves. The curves must form a closed loop.
769 """
770 if len(curves)<2:
771 raise ValueError("at least two curves have to be given.")
772 for i in range(len(curves)):
773 if not isinstance(curves[i],Manifold1D):
774 raise TypeError("%s-th argument is not a Manifold1D object."%i)
775 # 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
781 def getCurves(self):
782 """
783 returns the curves defining the CurveLoop
784 """
785 return self.__curves
786
787 def __neg__(self):
788 """
789 returns a view onto the curve with reversed ordering
790 """
791 return ReverseCurveLoop(self)
792
793 def __len__(self):
794 """
795 return the number of curves in the CurveLoop
796 """
797 return len(self.getCurves())
798
799
800 def collectPrimitiveBases(self):
801 """
802 returns primitives used to construct the CurveLoop
803 """
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=""
842 for i in self.getCurves():
843 if len(out)>0:
844 out+=", %s"%i.getDirectedID()
845 else:
846 out="%s"%i.getDirectedID()
847 return "Line Loop(%s) = {%s};"%(self.getID(),out)
848
849 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 ruled surface, i.e., a surface that can be interpolated using transfinite interpolation
893 """
894 def __init__(self,loop):
895 """
896 creates a ruled surface with boundary loop
897
898 @param loop: L{CurveLoop} defining the boundary of the surface.
899 """
900 if not isinstance(loop.getUnderlyingPrimitive(),CurveLoop):
901 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
909
910 def __neg__(self):
911 """
912 returns a view onto the suface with reversed ordering
913 """
914 return ReverseRuledSurface(self)
915
916 def getBoundaryLoop(self):
917 """
918 returns the loop defining the outer boundary
919 """
920 return self.__loop
921
922 def getBoundary(self):
923 """
924 returns a list of the one-dimensional manifolds forming the boundary of the Surface (including holes)
925 """
926 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 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
994 """
995 def __init__(self,loop,holes=[]):
996 """
997 creates a plane surface with a hole
998
999 @param loop: L{CurveLoop} defining the boundary of the surface
1000 @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.
1002 A CurveLoop defining a hole should not have any lines in common with another CurveLoop defining a hole in the same surface.
1003 """
1004 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)):
1010 if not isinstance(holes[i].getUnderlyingPrimitive(), CurveLoop):
1011 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
1021 def getHoles(self):
1022 """
1023 returns the holes
1024 """
1025 return self.__holes
1026
1027 def getBoundaryLoop(self):
1028 """
1029 returns the loop defining the boundary
1030 """
1031 return self.__loop
1032
1033 def getGmshCommand(self,scaling_factor=1.):
1034 """
1035 returns the Gmsh command(s) to create the primitive
1036 """
1037 out=""
1038 for i in self.getHoles():
1039 if len(out)>0:
1040 out+=", %s"%i.getDirectedID()
1041 else:
1042 out="%s"%i.getDirectedID()
1043 if len(out)>0:
1044 return "Plane Surface(%s) = {%s, %s};"%(self.getID(),self.getBoundaryLoop().getDirectedID(), out)
1045 else:
1046 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 ReversePlaneSurface(ReversePrimitive, Manifold2D):
1096 """
1097 creates a view onto a L{PlaneSurface} but with the reverse orientation
1098 """
1099 def __init__(self,surface):
1100 """
1101 creates a polygon from a list of line curves. The curves must form a closed loop.
1102 """
1103 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 def getBoundaryLoop(self):
1109 """
1110 returns the CurveLoop defining the RuledSurface
1111 """
1112 return -self.getUnderlyingPrimitive().getBoundaryLoop()
1113
1114 def getHoles(self):
1115 """
1116 returns a list of the one-dimensional manifolds forming the boundary of the Surface (including holes)
1117 """
1118 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
1129 #=========================================================================
1130 class SurfaceLoop(Primitive, PrimitiveBase):
1131 """
1132 a loop of 2D primitives. It defines the shell of a volume.
1133
1134 The loop must represent a closed shell, and the primitives should be oriented consistently.
1135 """
1136 def __init__(self,*surfaces):
1137 """
1138 creates a surface loop
1139 """
1140 if len(surfaces)<2:
1141 raise ValueError("at least two surfaces have to be given.")
1142 for i in range(len(surfaces)):
1143 if not isinstance(surfaces[i].getUnderlyingPrimitive(),Manifold2D):
1144 raise TypeError("%s-th argument is not a Manifold2D object."%i)
1145 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
1160 def getSurfaces(self):
1161 """
1162 returns the surfaces defining the SurfaceLoop
1163 """
1164 return self.__surfaces
1165
1166 def collectPrimitiveBases(self):
1167 """
1168 returns primitives used to construct the SurfaceLoop
1169 """
1170 out=[self]
1171 for c in self.getSurfaces(): out+=c.collectPrimitiveBases()
1172 return out
1173
1174 def getGmshCommand(self,scaling_factor=1.):
1175 """
1176 returns the Gmsh command(s) to create the primitive
1177 """
1178 out=""
1179 for i in self.getSurfaces():
1180 if len(out)>0:
1181 out+=", %s"%i.getDirectedID()
1182 else:
1183 out="%s"%i.getDirectedID()
1184 return "Surface Loop(%s) = {%s};"%(self.getID(),out)
1185
1186 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.
1262 """
1263 def __init__(self,loop,holes=[]):
1264 """
1265 creates a volume
1266
1267 @param loop: L{SurfaceLoop} defining the boundary of the surface
1268 @param holes: list of L{SurfaceLoop} defining holes in the surface.
1269 @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.
1271 """
1272 if not isinstance(loop.getUnderlyingPrimitive(), SurfaceLoop):
1273 raise TypeError("argument loop needs to be a SurfaceLoop object.")
1274 for i in range(len(holes)):
1275 if not isinstance(holes[i].getUnderlyingPrimitive(), SurfaceLoop):
1276 raise TypeError("%i th hole needs to be a SurfaceLoop object.")
1277 Primitive.__init__(self)
1278 Manifold3D.__init__(self)
1279 self.__loop=loop
1280 self.__holes=holes
1281 def getHoles(self):
1282 """
1283 returns the hole in the volume
1284 """
1285 return self.__holes
1286 def getSurfaceLoop(self):
1287 """
1288 returns the loop forming the surface
1289 """
1290 return self.__loop
1291
1292 def getGmshCommand(self,scaling_factor=1.):
1293 """
1294 returns the Gmsh command(s) to create the primitive
1295 """
1296 out=""
1297 for i in self.getHoles():
1298 if len(out)>0:
1299 out+=", %s"%i.getDirectedID()
1300 else:
1301 out="%s"%i.getDirectedID()
1302 if len(out)>0:
1303 return "Volume(%s) = {%s, %s};"%(self.getID(),self.getSurfaceLoop().getDirectedID(), out)
1304 else:
1305 return "Volume(%s) = {%s};"%(self.getID(),self.getSurfaceLoop().getDirectedID())
1306
1307 def substitute(self,sub_dict):
1308 """
1309 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
1310 If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
1311 with substituted arguments is returned.
1312 """
1313 if not sub_dict.has_key(self):
1314 sub_dict[self]=Volume(self.getSurfaceLoop().substitute(sub_dict),[ h.substitute(sub_dict) for h in self.getHoles()])
1315 return sub_dict[self]
1316
1317 def isColocated(self,primitive):
1318 """
1319 returns True if each curve is collocted with a curve in primitive
1320 """
1321 if hasattr(primitive,"getUnderlyingPrimitive"):
1322 if isinstance(primitive.getUnderlyingPrimitive(),Volume):
1323 if self.getSurfaceLoop().isColocated(primitive.getSurfaceLoop()):
1324 hs0=self.getHoles()
1325 hs1=primitive.getHoles()
1326 if len(hs0) == len(hs1):
1327 for h0 in hs0:
1328 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
1348
1349 class PropertySet(PrimitiveBase):
1350 """
1351 defines a group L{PrimitiveBase} objects.
1352 """
1353 def __init__(self,tag=None,*items):
1354 super(PropertySet, self).__init__()
1355 self.__items=items
1356 self.__tag=tag
1357 def collectPrimitiveBases(self):
1358 out=[self]+self.getBoundaryLoop().collectPrimitiveBases()
1359 for i in self.getHoles(): out+=i.collectPrimitiveBases()
1360 return out

  ViewVC Help
Powered by ViewVC 1.1.26