/[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 1045 - (hide annotations)
Tue Mar 20 01:30:58 2007 UTC (12 years, 7 months ago) by gross
File MIME type: text/x-python
File size: 42739 byte(s)
some modifications on the pycad implementation to make it easier to build
interfaces for other mesh generators. The script statement generation is now
done by the Design and not the Primitive classes.


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

  ViewVC Help
Powered by ViewVC 1.1.26