/[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 1045 - (show annotations)
Tue Mar 20 01:30:58 2007 UTC (13 years, 4 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 # $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 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 returns the dimension of the items
1269 """
1270 return self.__dim
1271 def getName(self):
1272 """
1273 returns the name of the set
1274 """
1275 return self.__name
1276 def setName(self,name):
1277 """
1278 sets the name.
1279 """
1280 self.__name=str(name)
1281
1282 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 def collectPrimitiveBases(self):
1304 """
1305 returns primitives used to construct the PropertySet
1306 """
1307 out=[self]
1308 for i in self.getItems(): out+=i.collectPrimitiveBases()
1309 return out
1310
1311 def getTag(self):
1312 """
1313 returns the tag used for this property set
1314 """
1315 return self.getID()

  ViewVC Help
Powered by ViewVC 1.1.26