/[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 1109 - (hide annotations)
Thu Apr 19 04:21:43 2007 UTC (12 years, 5 months ago) by btully
File MIME type: text/x-python
File size: 42749 byte(s)


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 btully 1109 if not p in out: out.append(p)
90 gross 1021 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