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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1123 - (show annotations)
Thu May 3 04:13:52 2007 UTC (12 years, 5 months ago) by gross
File MIME type: text/x-python
File size: 43319 byte(s)
PropertySets cann now be created wirout an intial 
set of items.


1 # $Id:$
2
3 """
4 Geometrical Primitives
5
6 the concept is inspired by gmsh and very much focused on the fact that
7 the classes are used to wrk with gmsh.
8
9 @var __author__: name of author
10 @var __copyright__: copyrights
11 @var __license__: licence agreement
12 @var __url__: url entry point on documentation
13 @var __version__: version
14 @var __date__: date of the version
15 """
16
17
18 __author__="Lutz Gross, l.gross@uq.edu.au"
19 __copyright__=""" Copyright (c) 2006 by ACcESS MNRF
20 http://www.access.edu.au
21 Primary Business: Queensland, Australia"""
22 __license__="""Licensed under the Open Software License version 3.0
23 http://www.opensource.org/licenses/osl-3.0.php"""
24 __url__="http://www.iservo.edu.au/esys/escript"
25 __version__="$Revision:$"
26 __date__="$Date:$"
27
28 import numarray
29 from transformations import _TYPE, Translation, Dilation, Transformation
30
31
32 def resetGlobalPrimitiveIdCounter():
33 """
34 initializes the global primitive ID counter
35 """
36 global global_primitive_id_counter
37 global_primitive_id_counter=1
38
39 def setToleranceForColocation(tol=1.e-11):
40 """
41 set the global tolerance for colocation checks to tol
42 """
43 global global_tolerance_for_colocation
44 global_tolerance_for_colocation=tol
45
46 def getToleranceForColocation():
47 """
48 returns the global tolerance for colocation checks
49 """
50 return global_tolerance_for_colocation
51
52 resetGlobalPrimitiveIdCounter()
53 setToleranceForColocation()
54
55
56 class PrimitiveBase(object):
57 """
58 template for a set of primitives
59 """
60 def __init__(self):
61 """
62 initializes PrimitiveBase instance object with id
63 """
64 pass
65
66 def __cmp__(self,other):
67 """
68 compares object with other by comparing the absolute value of the ID
69 """
70 if isinstance(other, PrimitiveBase):
71 return cmp(self.getID(),other.getID())
72 else:
73 return False
74 def getConstructionPoints(self):
75 """
76 returns the points used to construct the primitive
77 """
78 out=[]
79 for i in self.getPrimitives():
80 if isinstance(i,Point): out.append(i)
81 return out
82
83 def getPrimitives(self):
84 """
85 returns a list of primitives used to construct the primitive with no double entries
86 """
87 out=[]
88 for p in self.collectPrimitiveBases():
89 if not p in out: out.append(p)
90 return out
91
92 def copy(self):
93 """
94 returns a deep copy of the object
95 """
96 return self.substitute({})
97
98 def modifyBy(self,transformation):
99 """
100 modifies the coordinates by applying a transformation
101 """
102 for p in self.getConstructionPoints(): p.modifyBy(transformation)
103
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 raise TypeError, "cannot convert argument to Transformation class object."
157 return self.apply(trafo)
158
159
160 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 returns a new object by applying the transformation
169 """
170 out=self.copy()
171 out.modifyBy(transformation)
172 return out
173
174 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 """
194 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 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
212 def __neg__(self):
213 """
214 returns a view onto the curve with reversed ordering
215
216 @note: this class is overwritten by subclass
217 """
218 raise NotImplementedError("__neg__ is not implemented.")
219
220 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
226 @note: this class is overwritten by subclass
227 """
228 raise NotImplementedError("substitute is not implemented.")
229
230 def collectPrimitiveBases(self):
231 """
232 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 """
236 raise NotImplementedError("collectPrimitiveBases is not implemented.")
237
238 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 """
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 if not isinstance(primitive, Primitive):
256 raise ValueError("argument needs to be a Primitive class object.")
257 self.__primitive=primitive
258
259 def getID(self):
260 """
261 returns the primitive ID
262 """
263 return self.__primitive.getID()
264
265 def getUnderlyingPrimitive(self):
266 """
267 returns the underlying primitive
268 """
269 return self.__primitive
270
271 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 def __repr__(self):
278 return "-%s(%s)"%(self.__primitive.__class__.__name__,self.getID())
279
280 def getDirectedID(self):
281 """
282 returns the primitive ID where a negative signs means that the reversed ordring is used.
283 """
284 return -self.__primitive.getID()
285
286 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 """
318 a three dimensional point
319 """
320 def __init__(self,x=0.,y=0.,z=0.,local_scale=1.):
321 """
322 creates a point with coorinates x,y,z with the local refinement factor local_scale
323 """
324 PrimitiveBase.__init__(self)
325 Primitive.__init__(self)
326 self.setCoordinates(numarray.array([x,y,z],_TYPE))
327 self.setLocalScale(local_scale)
328
329 def setLocalScale(self,factor=1.):
330 """
331 sets the local refinement factor
332 """
333 if factor<=0.:
334 raise ValueError("scaling factor must be positive.")
335 self.__local_scale=factor
336
337 def getLocalScale(self):
338 """
339 returns the local refinement factor
340 """
341 return self.__local_scale
342 def getCoordinates(self):
343 """
344 returns the coodinates of the point as L{numarray.NumArray} object
345 """
346 return self._x
347 def setCoordinates(self,x):
348 """
349 returns the coodinates of the point as L{numarray.NumArray} object
350 """
351 if not isinstance(x, numarray.NumArray):
352 self._x=numarray.array(x,_TYPE)
353 else:
354 self._x=x
355
356 def collectPrimitiveBases(self):
357 """
358 returns primitives used to construct the primitive
359 """
360 return [self]
361
362 def isColocated(self,primitive):
363 """
364 returns True if L{Point} primitive is colocation (same coordinates)
365 that means if |self-primitive| <= tol * max(|self|,|primitive|)
366 """
367 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
375 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
386 def modifyBy(self,transformation):
387 """
388 modifies the coordinates by applying a transformation
389 """
390 self.setCoordinates(transformation(self.getCoordinates()))
391
392
393 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 """
401 general one-dimensional minifold in 3D defined by a start and end point.
402 """
403 def __init__(self):
404 """
405 create a one-dimensional manifold
406 """
407 PrimitiveBase.__init__(self)
408
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 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
426 class CurveBase(Manifold1D):
427 """
428 A Curve is defined by a set of control points
429 """
430 def __init__(self):
431 """
432 create curve
433 """
434 Manifold1D.__init__(self)
435
436 def __len__(self):
437 """
438 returns the number of control points
439 """
440 return len(self.getControlPoints())
441
442 def getStartPoint(self):
443 """
444 returns start point
445 """
446 return self.getControlPoints()[0]
447
448 def getEndPoint(self):
449 """
450 returns end point
451 """
452 return self.getControlPoints()[-1]
453
454 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 """
466 defines a curve form control points
467 """
468 if len(points)<2:
469 raise ValueError("Curve needs at least two points")
470 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
478 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 def substitute(self,sub_dict):
491 """
492 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 """
496 if not sub_dict.has_key(self):
497 new_p=[]
498 for p in self.getControlPoints(): new_p.append(p.substitute(sub_dict))
499 sub_dict[self]=self.__class__(*tuple(new_p))
500 return sub_dict[self]
501
502 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 def isColocated(self,primitive):
511 """
512 returns True curves are on the same position
513 """
514 if hasattr(primitive,"getUnderlyingPrimitive"):
515 if isinstance(primitive.getUnderlyingPrimitive(),self.__class__):
516 if len(primitive) == len(self):
517 cp0=self.getControlPoints()
518 cp1=primitive.getControlPoints()
519 match=True
520 for i in range(len(cp0)):
521 if not cp0[i].isColocated(cp1[i]):
522 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 return True
529 return False
530
531 class ReverseCurve(CurveBase, ReversePrimitive):
532 """
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 if not isinstance(curve, Curve):
540 raise TypeError("ReverseCurve needs to be an instance of Curve")
541 CurveBase.__init__(self)
542 ReversePrimitive.__init__(self,curve)
543
544 def getControlPoints(self):
545 """
546 returns a list of the points
547 """
548 out=[p for p in self.getUnderlyingPrimitive().getControlPoints()]
549 out.reverse()
550 return out
551
552 class Spline(Curve):
553 """
554 a spline curve defined through a list of control points.
555 """
556 pass
557
558 class BezierCurve(Curve):
559 """
560 a Bezier curve
561 """
562 pass
563
564 class BSpline(Curve):
565 """
566 a BSpline curve. Control points may be repeated.
567 """
568 pass
569
570 class Line(Curve):
571 """
572 a line is defined by two pointDirecteds
573 """
574 def __init__(self,*points):
575 """
576 defines a line with start and end point
577 """
578 if len(points)!=2:
579 raise TypeError("Line needs two points")
580 Curve.__init__(self,*points)
581
582 class ArcBase(Manifold1D):
583 def __init__(self):
584 """
585 create curve
586 """
587 Manifold1D.__init__(self)
588 def collectPrimitiveBases(self):
589 """
590 returns the primitives used to construct the Curve
591 """
592 out=[self]
593 out+=self.getStartPoint().collectPrimitiveBases()
594 out+=self.getEndPoint().collectPrimitiveBases()
595 out+=self.getCenterPoint().collectPrimitiveBases()
596 return out
597
598
599 def getCenterPoint(self):
600 """
601 returns center
602 """
603 raise NotImplementedError()
604
605 class Arc(ArcBase, Primitive):
606 """
607 defines an arc which is strictly, smaller than Pi
608 """
609 def __init__(self,center,start,end):
610 """
611 creates an arc by the start point, end point and center
612 """
613 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 ArcBase.__init__(self)
618 Primitive.__init__(self)
619 self.__center=center
620 self.__start=start
621 self.__end=end
622 def __neg__(self):
623 """
624 returns a view onto the curve with reversed ordering
625 """
626 return ReverseArc(self)
627
628 def getStartPoint(self):
629 """
630 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 returns center
643 """
644 return self.__center
645
646 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 if hasattr(primitive,"getUnderlyingPrimitive"):
662 if isinstance(primitive.getUnderlyingPrimitive(),Arc):
663 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 return False
667
668 class ReverseArc(ArcBase, ReversePrimitive):
669 """
670 defines an arc which is strictly, smaller than Pi
671 """
672 def __init__(self,arc):
673 """
674 creates an arc by the start point, end point and center
675 """
676 if not isinstance(arc, Arc):
677 raise TypeError("ReverseCurve needs to be an instance of Arc")
678 ArcBase.__init__(self)
679 ReversePrimitive.__init__(self,arc)
680
681 def getStartPoint(self):
682 """
683 returns start point
684 """
685 return self.getUnderlyingPrimitive().getEndPoint()
686
687 def getEndPoint(self):
688 """
689 returns end point
690 """
691 return self.getUnderlyingPrimitive().getStartPoint()
692
693 def getCenterPoint(self):
694 """
695 returns center
696 """
697 return self.getUnderlyingPrimitive().getCenterPoint()
698
699 class CurveLoop(Primitive, PrimitiveBase):
700 """
701 An oriented loop of one-dimensional manifolds (= curves and arcs)
702
703 The loop must be closed and the L{Manifold1D}s should be oriented consistently.
704 """
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 if len(curves)<2:
710 raise ValueError("at least two curves have to be given.")
711 for i in range(len(curves)):
712 if not isinstance(curves[i],Manifold1D):
713 raise TypeError("%s-th argument is not a Manifold1D object."%i)
714 # for the curves a loop:
715 used=[ False for i in curves]
716 self.__curves=list(curves)
717 Primitive.__init__(self)
718 PrimitiveBase.__init__(self)
719
720 def getCurves(self):
721 """
722 returns the curves defining the CurveLoop
723 """
724 return self.__curves
725
726 def __neg__(self):
727 """
728 returns a view onto the curve with reversed ordering
729 """
730 return ReverseCurveLoop(self)
731
732 def __len__(self):
733 """
734 return the number of curves in the CurveLoop
735 """
736 return len(self.getCurves())
737
738
739 def collectPrimitiveBases(self):
740 """
741 returns primitives used to construct the CurveLoop
742 """
743 out=[self]
744 for c in self.getCurves(): out+=c.collectPrimitiveBases()
745 return out
746
747 def substitute(self,sub_dict):
748 """
749 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 """
753 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
759 def isColocated(self,primitive):
760 """
761 returns True if each curve is collocted with a curve in primitive
762 """
763 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
776 class ReverseCurveLoop(ReversePrimitive, PrimitiveBase):
777 """
778 An oriented loop of one-dimensional manifolds (= curves and arcs)
779
780 The loop must be closed and the one-dimensional manifolds should be oriented consistently.
781 """
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 raise TypeError("arguments need to be an instance of CurveLoop.")
788 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 #=
801 class Manifold2D(PrimitiveBase):
802 """
803 general two-dimensional manifold
804 """
805 def __init__(self):
806 """
807 create a two-dimensional manifold
808 """
809 PrimitiveBase.__init__(self)
810
811 def getBoundary(self):
812 """
813 returns a list of the one-dimensional manifolds forming the boundary of the Surface (including holes)
814 """
815 raise NotImplementedError()
816
817 class RuledSurface(Primitive, Manifold2D):
818 """
819 A ruled surface, i.e., a surface that can be interpolated using transfinite interpolation
820 """
821 def __init__(self,loop):
822 """
823 creates a ruled surface with boundary loop
824
825 @param loop: L{CurveLoop} defining the boundary of the surface.
826 """
827 if not isinstance(loop.getUnderlyingPrimitive(),CurveLoop):
828 raise TypeError("argument loop needs to be a CurveLoop object.")
829 if len(loop)<2:
830 raise ValueError("the loop must contain at least two Curves.")
831 if len(loop)>4:
832 raise ValueError("the loop must contain at least three Curves.")
833 Primitive.__init__(self)
834 Manifold2D.__init__(self)
835 self.__loop=loop
836
837 def __neg__(self):
838 """
839 returns a view onto the suface with reversed ordering
840 """
841 return ReverseRuledSurface(self)
842
843 def getBoundaryLoop(self):
844 """
845 returns the loop defining the outer boundary
846 """
847 return self.__loop
848
849 def getBoundary(self):
850 """
851 returns a list of the one-dimensional manifolds forming the boundary of the Surface (including holes)
852 """
853 return self.getBoundaryLoop().getCurves()
854
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 sub_dict[self]=RuledSurface(self.getBoundaryLoop().substitute(sub_dict))
863 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 if hasattr(primitive,"getUnderlyingPrimitive"):
870 if isinstance(primitive.getUnderlyingPrimitive(),RuledSurface):
871 return self.getBoundaryLoop().isColocated(primitive.getBoundaryLoop())
872 return False
873
874 def collectPrimitiveBases(self):
875 """
876 returns primitives used to construct the Surface
877 """
878 return [self] + self.getBoundaryLoop().collectPrimitiveBases()
879
880 def createRuledSurface(*curves):
881 """
882 an easier way to create a L{RuledSurface} from given curves.
883 """
884 return RuledSurface(CurveLoop(*curves))
885
886
887 class ReverseRuledSurface(ReversePrimitive, Manifold2D):
888 """
889 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 raise TypeError("arguments need to be an instance of CurveLoop.")
897 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 a plane surface with holes
915 """
916 def __init__(self,loop,holes=[]):
917 """
918 creates a plane surface with a hole
919
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 if not isinstance(loop.getUnderlyingPrimitive(),CurveLoop):
926 raise TypeError("argument loop needs to be a CurveLoop object.")
927 for l in loop.getCurves():
928 if not isinstance(l.getUnderlyingPrimitive(),Line):
929 raise TypeError("loop may be formed by Lines only.")
930 for i in range(len(holes)):
931 if not isinstance(holes[i].getUnderlyingPrimitive(), CurveLoop):
932 raise TypeError("%i-th hole needs to be a CurveLoop object.")
933 for l in holes[i].getCurves():
934 if not isinstance(l.getUnderlyingPrimitive(),Line):
935 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 Primitive.__init__(self)
939 Manifold2D.__init__(self)
940 self.__loop=loop
941 self.__holes=holes
942 def getHoles(self):
943 """
944 returns the holes
945 """
946 return self.__holes
947
948 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 sub_dict[self]=PlaneSurface(self.getBoundaryLoop().substitute(sub_dict),[ h.substitute(sub_dict) for h in self.getHoles()])
962 return sub_dict[self]
963
964 def isColocated(self,primitive):
965 """
966 returns True if each curve is collocted with a curve in primitive
967 """
968 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
1001 class ReversePlaneSurface(ReversePrimitive, Manifold2D):
1002 """
1003 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 raise TypeError("arguments need to be an instance of PlaneSurface.")
1011 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 a loop of 2D primitives. It defines the shell of a volume.
1039
1040 The loop must represent a closed shell, and the primitives should be oriented consistently.
1041 """
1042 def __init__(self,*surfaces):
1043 """
1044 creates a surface loop
1045 """
1046 if len(surfaces)<2:
1047 raise ValueError("at least two surfaces have to be given.")
1048 for i in range(len(surfaces)):
1049 if not isinstance(surfaces[i].getUnderlyingPrimitive(),Manifold2D):
1050 raise TypeError("%s-th argument is not a Manifold2D object."%i)
1051 self.__surfaces=list(surfaces)
1052 Primitive.__init__(self)
1053 PrimitiveBase.__init__(self)
1054 def __len__(self):
1055 """
1056 return the number of curves in the SurfaceLoop
1057 """
1058 return len(self.__surfaces)
1059
1060 def __neg__(self):
1061 """
1062 returns a view onto the curve with reversed ordering
1063 """
1064 return ReverseSurfaceLoop(self)
1065
1066 def getSurfaces(self):
1067 """
1068 returns the surfaces defining the SurfaceLoop
1069 """
1070 return self.__surfaces
1071
1072 def collectPrimitiveBases(self):
1073 """
1074 returns primitives used to construct the SurfaceLoop
1075 """
1076 out=[self]
1077 for c in self.getSurfaces(): out+=c.collectPrimitiveBases()
1078 return out
1079
1080 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 for s in self.getSurfaces(): new_s.append(s.substitute(sub_dict))
1089 sub_dict[self]=SurfaceLoop(*tuple(new_s))
1090 return sub_dict[self]
1091
1092 def isColocated(self,primitive):
1093 """
1094 returns True if each surface is collocted with a curve in primitive and vice versa.
1095 """
1096 if hasattr(primitive,"getUnderlyingPrimitive"):
1097 if isinstance(primitive.getUnderlyingPrimitive(),SurfaceLoop):
1098 if len(primitive) == len(self):
1099 sp0=self.getSurfaces()
1100 sp1=primitive.getSurfaces()
1101 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
1109 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 raise TypeError("arguments need to be an instance of SurfaceLoop.")
1124 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
1136 #==============================
1137 class Manifold3D(PrimitiveBase):
1138 """
1139 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 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 if not isinstance(loop.getUnderlyingPrimitive(), SurfaceLoop):
1167 raise TypeError("argument loop needs to be a SurfaceLoop object.")
1168 for i in range(len(holes)):
1169 if not isinstance(holes[i].getUnderlyingPrimitive(), SurfaceLoop):
1170 raise TypeError("%i th hole needs to be a SurfaceLoop object.")
1171 Primitive.__init__(self)
1172 Manifold3D.__init__(self)
1173 self.__loop=loop
1174 self.__holes=holes
1175 def getHoles(self):
1176 """
1177 returns the hole in the volume
1178 """
1179 return self.__holes
1180 def getSurfaceLoop(self):
1181 """
1182 returns the loop forming the surface
1183 """
1184 return self.__loop
1185
1186 def substitute(self,sub_dict):
1187 """
1188 returns a copy of self with substitutes for the primitives used to construct it given by the dictionary C{sub_dict}.
1189 If a substitute for the object is given by C{sub_dict} the value is returned, otherwise a new instance
1190 with substituted arguments is returned.
1191 """
1192 if not sub_dict.has_key(self):
1193 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 class PropertySet(Primitive, PrimitiveBase):
1229 """
1230 defines a group of L{Primitive} which can be accessed through a name
1231 """
1232 def __init__(self,name,*items):
1233 Primitive.__init__(self)
1234 self.__dim=None
1235 self.clearItems()
1236 self.addItem(*items)
1237 self.setName(name)
1238
1239 def getDim(self):
1240 """
1241 returns the dimension of the items
1242 """
1243 if self.__dim == None:
1244 items=self.getItems()
1245 if len(items)>0:
1246 if isinstance(items[0] ,Manifold1D):
1247 self.__dim=1
1248 elif isinstance(items[0] ,Manifold2D):
1249 self.__dim=2
1250 elif isinstance(items[0] ,Manifold3D):
1251 self.__dim=3
1252 else:
1253 self.__dim=0
1254 return self.__dim
1255 def __repr__(self):
1256 """
1257 returns a string representation
1258 """
1259 return "%s(%s)"%(self.getName(),self.getID())
1260 def getManifoldClass(self):
1261 """
1262 returns the manifold class expected from items
1263 """
1264 d=self.getDim()
1265 if d == None:
1266 raise ValueError("undefined spatial diemnsion.")
1267 else:
1268 if d==0:
1269 return Point
1270 elif d==1:
1271 return Manifold1D
1272 elif d==2:
1273 return Manifold2D
1274 else:
1275 return Manifold3D
1276
1277 def getName(self):
1278 """
1279 returns the name of the set
1280 """
1281 return self.__name
1282 def setName(self,name):
1283 """
1284 sets the name.
1285 """
1286 self.__name=str(name)
1287
1288 def addItems(self,*items):
1289 """
1290 adds items. An item my be any L{Primitive} but no L{PropertySet}
1291 """
1292 self.addItem(*items)
1293
1294 def addItem(self,*items):
1295 """
1296 adds items. An item my be any L{Primitive} but no L{PropertySet}
1297 """
1298 for i in items:
1299 if not i in self.__items:
1300 if len(self.__items)>0:
1301 m=self.getManifoldClass()
1302 if not isinstance(i, m):
1303 raise TypeError("argument %s is not a %s class object."%(i, m.__name__))
1304 self.__items.append(i)
1305 def getNumItems(self):
1306 """
1307 returns the number of items in the property set
1308 """
1309 return len(self.__items)
1310
1311 def getItems(self):
1312 """
1313 returns the list of items
1314 """
1315 return self.__items
1316
1317 def clearItems(self):
1318 """
1319 clears the list of items
1320 """
1321 self.__items=[]
1322 def collectPrimitiveBases(self):
1323 """
1324 returns primitives used to construct the PropertySet
1325 """
1326 out=[self]
1327 for i in self.getItems(): out+=i.collectPrimitiveBases()
1328 return out
1329
1330 def getTag(self):
1331 """
1332 returns the tag used for this property set
1333 """
1334 return self.getID()

  ViewVC Help
Powered by ViewVC 1.1.26