/[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 2700 - (hide annotations)
Wed Sep 30 08:28:55 2009 UTC (11 years, 6 months ago) by gross
File MIME type: text/x-python
File size: 58891 byte(s)
pycad test fixed.
1 ksteube 1809
2     ########################################################
3 ksteube 1312 #
4 jfenwick 2548 # Copyright (c) 2003-2009 by University of Queensland
5 ksteube 1809 # Earth Systems Science Computational Center (ESSCC)
6     # http://www.uq.edu.au/esscc
7 ksteube 1312 #
8 ksteube 1809 # Primary Business: Queensland, Australia
9     # Licensed under the Open Software License version 3.0
10     # http://www.opensource.org/licenses/osl-3.0.php
11 ksteube 1312 #
12 ksteube 1809 ########################################################
13 gross 898
14 jfenwick 2549 __copyright__="""Copyright (c) 2003-2009 by University of Queensland
15 ksteube 1809 Earth Systems Science Computational Center (ESSCC)
16     http://www.uq.edu.au/esscc
17     Primary Business: Queensland, Australia"""
18     __license__="""Licensed under the Open Software License version 3.0
19     http://www.opensource.org/licenses/osl-3.0.php"""
20 jfenwick 2344 __url__="https://launchpad.net/escript-finley"
21 ksteube 1809
22 gross 898 """
23 gross 899 Geometrical Primitives
24 gross 898
25     the concept is inspired by gmsh and very much focused on the fact that
26     the classes are used to wrk with gmsh.
27    
28 jfenwick 2625 :var __author__: name of author
29     :var __copyright__: copyrights
30     :var __license__: licence agreement
31     :var __url__: url entry point on documentation
32     :var __version__: version
33     :var __date__: date of the version
34 gross 898 """
35    
36     __author__="Lutz Gross, l.gross@uq.edu.au"
37    
38 artak 1727 try:
39     import numpy
40     numpyImported=True
41     except:
42 caltinay 2180 numpyImported=False
43 artak 1727
44 jfenwick 2455 import numpy
45 gross 2429 from transformations import _TYPE, Translation, Dilation, Transformation, DEG
46 gross 2377 import math
47 gross 899
48    
49 gross 915 def resetGlobalPrimitiveIdCounter():
50 gross 917 """
51 caltinay 2180 Initializes the global primitive ID counter.
52 gross 917 """
53 gross 915 global global_primitive_id_counter
54     global_primitive_id_counter=1
55    
56 gross 916 def setToleranceForColocation(tol=1.e-11):
57 gross 917 """
58 jfenwick 2625 Sets the global tolerance for colocation checks to ``tol``.
59 gross 917 """
60 gross 916 global global_tolerance_for_colocation
61     global_tolerance_for_colocation=tol
62    
63     def getToleranceForColocation():
64 gross 917 """
65 caltinay 2180 Returns the global tolerance for colocation checks.
66 gross 917 """
67 gross 916 return global_tolerance_for_colocation
68    
69 gross 915 resetGlobalPrimitiveIdCounter()
70 gross 916 setToleranceForColocation()
71 gross 915
72 gross 928
73     class PrimitiveBase(object):
74 gross 898 """
75 caltinay 2180 Template for a set of primitives.
76 gross 898 """
77 caltinay 2180 def __init__(self):
78 gross 898 """
79 caltinay 2180 Initializes the PrimitiveBase instance object.
80     """
81 gross 929 pass
82 gross 915
83 gross 899 def __cmp__(self,other):
84 gross 928 """
85 caltinay 2180 Compares object with other by comparing the absolute value of the ID.
86 gross 928 """
87 gross 929 if isinstance(other, PrimitiveBase):
88     return cmp(self.getID(),other.getID())
89 gross 928 else:
90 gross 2429 return -1
91 caltinay 2180
92 gross 916 def getConstructionPoints(self):
93 gross 912 """
94 caltinay 2180 Returns the points used to construct the primitive.
95 gross 912 """
96 gross 1021 out=[]
97 caltinay 2180 for i in self.getPrimitives():
98 gross 1021 if isinstance(i,Point): out.append(i)
99     return out
100 gross 912
101 gross 916 def getPrimitives(self):
102 gross 912 """
103 caltinay 2180 Returns a list of primitives used to construct the primitive with no
104     double entries.
105 gross 912 """
106 gross 1021 out=[]
107     for p in self.collectPrimitiveBases():
108 btully 1109 if not p in out: out.append(p)
109 gross 1021 return out
110 gross 912
111 gross 915 def copy(self):
112     """
113 caltinay 2180 Returns a deep copy of the object.
114 gross 915 """
115 gross 925 return self.substitute({})
116 gross 912
117 gross 915 def modifyBy(self,transformation):
118 gross 916 """
119 caltinay 2180 Modifies the coordinates by applying a transformation.
120 gross 916 """
121     for p in self.getConstructionPoints(): p.modifyBy(transformation)
122 gross 915
123     def __add__(self,other):
124     """
125 jfenwick 2625 Returns a new object shifted by ``other``.
126 gross 915 """
127 jfenwick 2455 return self.apply(Translation(numpy.array(other,_TYPE)))
128 gross 915
129     def __sub__(self,other):
130     """
131 jfenwick 2625 Returns a new object shifted by ``-other``.
132 gross 915 """
133 jfenwick 2455 return self.apply(Translation(-numpy.array(other,_TYPE)))
134 gross 915
135     def __iadd__(self,other):
136     """
137 jfenwick 2625 Shifts the point inplace by ``other``.
138 gross 915 """
139 jfenwick 2455 self.modifyBy(Translation(numpy.array(other,_TYPE)))
140 gross 915 return self
141    
142     def __isub__(self,other):
143     """
144 jfenwick 2625 Shifts the point inplace by ``-other``.
145 gross 915 """
146 jfenwick 2455 self.modifyBy(Translation(-numpy.array(other,_TYPE)))
147 gross 915 return self
148    
149     def __imul__(self,other):
150     """
151 jfenwick 2625 Modifies object by applying `Transformation` ``other``. If ``other``
152     is not a `Transformation` it is first tried to be converted.
153 gross 915 """
154     if isinstance(other,int) or isinstance(other,float):
155     trafo=Dilation(other)
156 jfenwick 2455 elif isinstance(other,numpy.ndarray):
157 gross 915 trafo=Translation(other)
158     elif isinstance(other,Transformation):
159     trafo=other
160     else:
161 caltinay 2180 raise TypeError, "cannot convert argument to a Transformation class object."
162 gross 915 self.modifyBy(trafo)
163     return self
164    
165     def __rmul__(self,other):
166     """
167 jfenwick 2625 Applies `Transformation` ``other`` to object. If ``other`` is not a
168     `Transformation` it is first tried to be converted.
169 gross 915 """
170     if isinstance(other,int) or isinstance(other,float):
171     trafo=Dilation(other)
172 jfenwick 2455 elif isinstance(other,numpy.ndarray):
173 gross 915 trafo=Translation(other)
174     elif isinstance(other,Transformation):
175     trafo=other
176     else:
177 gross 916 raise TypeError, "cannot convert argument to Transformation class object."
178 gross 915 return self.apply(trafo)
179    
180    
181 gross 916 def setLocalScale(self,factor=1.):
182     """
183 caltinay 2180 Sets the local refinement factor.
184 gross 916 """
185     for p in self.getConstructionPoints(): p.setLocalScale(factor)
186    
187     def apply(self,transformation):
188     """
189 caltinay 2180 Returns a new object by applying the transformation.
190 gross 916 """
191 gross 925 out=self.copy()
192     out.modifyBy(transformation)
193     return out
194 gross 916
195 gross 929 class Primitive(object):
196     """
197 caltinay 2180 Class that represents a general primitive.
198 gross 929 """
199 caltinay 2180 def __init__(self):
200 gross 929 """
201 caltinay 2180 Initializes the Primitive instance object with a unique ID.
202     """
203 gross 929 global global_primitive_id_counter
204     self.__ID=global_primitive_id_counter
205     global_primitive_id_counter+=1
206    
207     def getID(self):
208     """
209 caltinay 2180 Returns the primitive ID.
210 gross 929 """
211     return self.__ID
212    
213     def getDirectedID(self):
214 gross 928 """
215 caltinay 2180 Returns the primitive ID where a negative sign means that reversed
216     ordering is used.
217 gross 929 """
218     return self.getID()
219    
220     def __repr__(self):
221 caltinay 2180 return "%s(%s)"%(self.__class__.__name__,self.getID())
222 gross 929
223     def getUnderlyingPrimitive(self):
224     """
225 caltinay 2180 Returns the underlying primitive.
226 gross 929 """
227     return self
228 caltinay 2180
229 gross 930 def hasSameOrientation(self,other):
230     """
231 jfenwick 2625 Returns True if ``other`` is the same primitive and has the same
232 caltinay 2180 orientation, False otherwise.
233 gross 930 """
234     return self == other and isinstance(other,Primitive)
235 gross 929
236     def __neg__(self):
237     """
238 caltinay 2180 Returns a view onto the curve with reversed ordering.
239 gross 929
240 jfenwick 2625 :note: This method is overwritten by subclasses.
241 gross 928 """
242 gross 929 raise NotImplementedError("__neg__ is not implemented.")
243 gross 928
244 gross 925 def substitute(self,sub_dict):
245     """
246 caltinay 2180 Returns a copy of self with substitutes for the primitives used to
247 jfenwick 2625 construct it given by the dictionary ``sub_dict``. If a substitute for
248     the object is given by ``sub_dict`` the value is returned, otherwise a
249 caltinay 2180 new instance with substituted arguments is returned.
250 gross 928
251 jfenwick 2625 :note: This method is overwritten by subclasses.
252 gross 925 """
253 gross 929 raise NotImplementedError("substitute is not implemented.")
254 gross 925
255 gross 929 def collectPrimitiveBases(self):
256 gross 928 """
257 caltinay 2180 Returns a list of primitives used to construct the primitive. It may
258     contain primitives twice.
259    
260 jfenwick 2625 :note: This method is overwritten by subclasses.
261 gross 928 """
262 gross 929 raise NotImplementedError("collectPrimitiveBases is not implemented.")
263 gross 928
264 gross 929 def isColocated(self,primitive):
265     """
266 caltinay 2180 Rreturns True if the two primitives are located at the same position.
267 gross 929
268 jfenwick 2625 :note: This method is overwritten by subclasses.
269 gross 929 """
270     raise NotImplementedError("isColocated is not implemented.")
271    
272    
273     class ReversePrimitive(object):
274 gross 928 """
275 caltinay 2180 A view onto a primitive creating a reverse orientation.
276 gross 928 """
277 caltinay 2180 def __init__(self,primitive):
278 gross 928 """
279 jfenwick 2625 Instantiates a view onto ``primitive``.
280 caltinay 2180 """
281 gross 929 if not isinstance(primitive, Primitive):
282     raise ValueError("argument needs to be a Primitive class object.")
283 gross 928 self.__primitive=primitive
284    
285 gross 929 def getID(self):
286     """
287 caltinay 2180 Returns the primitive ID.
288 gross 929 """
289     return self.__primitive.getID()
290    
291 gross 928 def getUnderlyingPrimitive(self):
292     """
293 caltinay 2180 Returns the underlying primitive.
294 gross 928 """
295     return self.__primitive
296    
297 gross 930 def hasSameOrientation(self,other):
298     """
299 jfenwick 2625 Returns True if ``other`` is the same primitive and has the same
300 caltinay 2180 orientation as self.
301 gross 930 """
302     return self == other and isinstance(other,ReversePrimitive)
303    
304 gross 929 def __repr__(self):
305     return "-%s(%s)"%(self.__primitive.__class__.__name__,self.getID())
306    
307     def getDirectedID(self):
308 gross 928 """
309 caltinay 2180 Returns the primitive ID where a negative signs means that reversed
310     ordering is used.
311 gross 928 """
312 gross 929 return -self.__primitive.getID()
313 gross 928
314 gross 929 def substitute(self,sub_dict):
315     """
316 caltinay 2180 Returns a copy of self with substitutes for the primitives used to
317 jfenwick 2625 construct it given by the dictionary ``sub_dict``. If a substitute for
318     the object is given by ``sub_dict`` the value is returned, otherwise a
319 caltinay 2180 new instance with substituted arguments is returned.
320 gross 929 """
321     if not sub_dict.has_key(self):
322     sub_dict[self]=-self.getUnderlyingPrimitive().substitute(sub_dict)
323     return sub_dict[self]
324 caltinay 2180
325 gross 929 def __neg__(self):
326     """
327 caltinay 2180 Returns a view onto the curve with reversed ordering.
328 gross 929 """
329     return self.__primitive
330    
331     def collectPrimitiveBases(self):
332     """
333 caltinay 2180 Returns a list of primitives used to construct the primitive. It may
334     contain primitives twice.
335 gross 929 """
336     return self.__primitive.collectPrimitiveBases()
337    
338     def isColocated(self,primitive):
339     """
340 caltinay 2180 Returns True if the two primitives are located at the same position.
341 gross 929
342 jfenwick 2625 :note: This method is overwritten by subclasses.
343 gross 929 """
344     return self.__primitive.isColocated(primitive)
345    
346     class Point(Primitive, PrimitiveBase):
347 gross 898 """
348 caltinay 2180 A three-dimensional point.
349 gross 898 """
350 caltinay 2180 def __init__(self,x=0.,y=0.,z=0.,local_scale=1.):
351 gross 898 """
352 jfenwick 2625 Creates a point with coordinates ``x``, ``y``, ``z`` with the local
353     refinement factor ``local_scale``. If ``x`` is a list or similar it needs to have
354     length less or equal 3. In this case ``y`` and ``z`` are overwritten by
355     ``x[1]`` and ``x[2]``.
356 caltinay 2180 """
357 gross 929 PrimitiveBase.__init__(self)
358 gross 928 Primitive.__init__(self)
359 gross 2620 try:
360     l=len(x)
361     if l>3:
362     raise ValueError,"x has a lanegth bigger than 3."
363     if l>1:
364     y=x[1]
365     else:
366     y=0.
367     if l>2:
368     z=x[2]
369     else:
370     z=0.
371     if l>0:
372     x=x[0]
373     else:
374     x=0.
375     except TypeError:
376     pass
377     a=numpy.array([x,y,z], _TYPE)
378     self.setCoordinates(a)
379 gross 899 self.setLocalScale(local_scale)
380 gross 915
381 gross 898 def setLocalScale(self,factor=1.):
382 gross 902 """
383 caltinay 2180 Sets the local refinement factor.
384 gross 902 """
385 gross 912 if factor<=0.:
386     raise ValueError("scaling factor must be positive.")
387 gross 898 self.__local_scale=factor
388 gross 929
389 gross 902 def getLocalScale(self):
390 gross 912 """
391 caltinay 2180 Returns the local refinement factor.
392 gross 912 """
393 gross 902 return self.__local_scale
394 caltinay 2180
395 gross 912 def getCoordinates(self):
396     """
397 jfenwick 2625 Returns the coodinates of the point as a ``numpy.ndarray`` object.
398 gross 912 """
399     return self._x
400 caltinay 2180
401 gross 915 def setCoordinates(self,x):
402 gross 912 """
403 jfenwick 2625 Sets the coodinates of the point from a ``numpy.ndarray`` object ``x``.
404 gross 912 """
405 jfenwick 2455 if not isinstance(x, numpy.ndarray):
406     self._x=numpy.array(x,_TYPE)
407 gross 915 else:
408     self._x=x
409    
410 gross 929 def collectPrimitiveBases(self):
411 gross 912 """
412 caltinay 2180 Returns primitives used to construct the primitive.
413 gross 912 """
414 gross 916 return [self]
415 caltinay 2180
416 gross 916 def isColocated(self,primitive):
417 gross 912 """
418 jfenwick 2625 Returns True if the `Point` ``primitive`` is colocated (has the same
419 caltinay 2180 coordinates) with self. That is, if
420 jfenwick 2625 *|self - primitive| <= tol * max(\|self\|,|primitive|)*.
421 gross 912 """
422 gross 916 if isinstance(primitive,Point):
423     primitive=primitive.getCoordinates()
424     c=self.getCoordinates()
425     d=c-primitive
426 artak 1727 if numpyImported:
427     return numpy.dot(d,d)<=getToleranceForColocation()**2*max(numpy.dot(c,c),numpy.dot(primitive,primitive))
428     else:
429 jfenwick 2455 return numpy.dot(d,d)<=getToleranceForColocation()**2*max(numpy.dot(c,c),numpy.dot(primitive,primitive))
430 gross 916 else:
431     return False
432 gross 912
433 gross 925 def substitute(self,sub_dict):
434     """
435 caltinay 2180 Returns a copy of self with substitutes for the primitives used to
436 jfenwick 2625 construct it given by the dictionary ``sub_dict``. If a substitute for
437     the object is given by ``sub_dict`` the value is returned, otherwise a
438 caltinay 2180 new instance with substituted arguments is returned.
439 gross 925 """
440     if not sub_dict.has_key(self):
441     c=self.getCoordinates()
442     sub_dict[self]=Point(c[0],c[1],c[2],local_scale=self.getLocalScale())
443     return sub_dict[self]
444 gross 915
445     def modifyBy(self,transformation):
446     """
447 caltinay 2180 Modifies the coordinates by applying the given transformation.
448 gross 915 """
449     self.setCoordinates(transformation(self.getCoordinates()))
450    
451 gross 928 def __neg__(self):
452     """
453 caltinay 2180 Returns a view of the object with reverse orientation. As a point has
454     no direction the object itself is returned.
455 gross 928 """
456     return self
457 caltinay 2180
458 gross 928 class Manifold1D(PrimitiveBase):
459 gross 916 """
460 caltinay 2180 General one-dimensional manifold in 1D defined by a start and end point.
461 gross 916 """
462 gross 928 def __init__(self):
463     """
464 caltinay 2180 Initializes the one-dimensional manifold.
465 gross 928 """
466 gross 929 PrimitiveBase.__init__(self)
467 gross 2429 self.resetElementDistribution()
468 gross 928
469     def getStartPoint(self):
470     """
471 caltinay 2180 Returns the start point.
472 gross 928 """
473     raise NotImplementedError()
474    
475     def getEndPoint(self):
476     """
477 caltinay 2180 Returns the end point.
478 gross 928 """
479     raise NotImplementedError()
480 caltinay 2180
481 gross 931 def getBoundary(self):
482     """
483 caltinay 2180 Returns a list of the zero-dimensional manifolds forming the boundary
484     of the curve.
485 gross 931 """
486     return [ self.getStartPoint(), self.getEndPoint()]
487 gross 928
488 gross 2429 def setElementDistribution(self,n,progression=1,createBump=False):
489     """
490     Defines the number of elements on the line. If set it overwrites the local length setting which would be applied.
491 jfenwick 2625 The progression factor ``progression`` defines the change of element size between naighboured elements. If ``createBump`` is set
492 gross 2429 progression is applied towards the center of the line.
493    
494 jfenwick 2625 :param n: number of elements on the line
495     :type n: ``int``
496     :param progression: a positive progression factor
497     :type progression: positive ``float``
498     :param createBump: of elements on the line
499     :type createBump: ``bool``
500 gross 2429 """
501     if n<1:
502     raise ValueError,"number of elements must be positive."
503     if progression<=0:
504     raise ValueError,"progression factor must be positive."
505     self.__apply_elements=True
506     self.__n=n
507     self.__progression_factor=progression
508     self.__createBump=createBump
509    
510     def resetElementDistribution(self):
511     """
512     removes the a previously set element distribution from the line.
513     """
514     self.__apply_elements=False
515    
516     def getElementDistribution(self):
517     """
518     Returns the element distribution.
519    
520 jfenwick 2625 :return: the tuple of the number of elements, the progression factor and the bump flag. If no element distribution is set ``None`` is returned
521     :rtype: ``tuple``
522 gross 2429 """
523     if self.__apply_elements:
524     return (self.__n, self.__progression_factor, self.__createBump)
525     else:
526     return None
527    
528 gross 928 class CurveBase(Manifold1D):
529 gross 929 """
530 caltinay 2180 Base class for curves. A Curve is defined by a set of control points.
531 gross 929 """
532 gross 928 def __init__(self):
533 caltinay 2180 """
534     Initializes the curve.
535     """
536     Manifold1D.__init__(self)
537 gross 916
538     def __len__(self):
539 caltinay 2180 """
540     Returns the number of control points.
541     """
542     return len(self.getControlPoints())
543 gross 916
544     def getStartPoint(self):
545 caltinay 2180 """
546     Returns the start point.
547     """
548     return self.getControlPoints()[0]
549 gross 898
550 gross 916 def getEndPoint(self):
551 caltinay 2180 """
552     Returns the end point.
553     """
554     return self.getControlPoints()[-1]
555 gross 898
556 gross 929 def getControlPoints(self):
557 caltinay 2180 """
558     Returns a list of the points.
559     """
560     raise NotImplementedError()
561 gross 929
562     class Curve(CurveBase, Primitive):
563     """
564 caltinay 2180 A curve defined through a list of control points.
565 gross 929 """
566     def __init__(self,*points):
567 gross 916 """
568 jfenwick 2625 Defines a curve from control points given by ``points``.
569 gross 916 """
570 gross 2627 if len(points)==1:
571     points=points[0]
572     if not hasattr(points,'__iter__'): raise ValueError("Curve needs at least two points")
573 gross 929 if len(points)<2:
574 gross 931 raise ValueError("Curve needs at least two points")
575 gross 929 i=0
576     for p in points:
577     i+=1
578     if not isinstance(p,Point): raise TypeError("%s-th argument is not a Point object."%i)
579     self.__points=points
580     CurveBase.__init__(self)
581     Primitive.__init__(self)
582 gross 916
583 gross 929 def getControlPoints(self):
584 caltinay 2180 """
585     Returns a list of the points.
586     """
587     return self.__points
588    
589 gross 929 def __neg__(self):
590 caltinay 2180 """
591     Returns a view onto the curve with reversed ordering.
592     """
593     return ReverseCurve(self)
594 gross 929
595 gross 925 def substitute(self,sub_dict):
596 gross 916 """
597 caltinay 2180 Returns a copy of self with substitutes for the primitives used to
598 jfenwick 2625 construct it given by the dictionary ``sub_dict``. If a substitute for
599     the object is given by ``sub_dict`` the value is returned, otherwise a
600 caltinay 2180 new instance with substituted arguments is returned.
601 gross 916 """
602 gross 925 if not sub_dict.has_key(self):
603     new_p=[]
604     for p in self.getControlPoints(): new_p.append(p.substitute(sub_dict))
605 gross 929 sub_dict[self]=self.__class__(*tuple(new_p))
606 gross 925 return sub_dict[self]
607 gross 916
608 gross 929 def collectPrimitiveBases(self):
609     """
610 caltinay 2180 Returns the primitives used to construct the curve.
611 gross 929 """
612     out=[self]
613     for p in self.getControlPoints(): out+=p.collectPrimitiveBases()
614     return out
615    
616 gross 916 def isColocated(self,primitive):
617     """
618 caltinay 2180 Returns True if curves are at the same position.
619 gross 916 """
620 caltinay 2180 if hasattr(primitive,"getUnderlyingPrimitive"):
621     if isinstance(primitive.getUnderlyingPrimitive(),self.__class__):
622 gross 930 if len(primitive) == len(self):
623 gross 916 cp0=self.getControlPoints()
624     cp1=primitive.getControlPoints()
625 gross 919 match=True
626 gross 916 for i in range(len(cp0)):
627     if not cp0[i].isColocated(cp1[i]):
628 gross 919 match=False
629     break
630     if not match:
631     for i in range(len(cp0)):
632     if not cp0[i].isColocated(cp1[len(cp0)-1-i]):
633     return False
634 gross 916 return True
635 gross 930 return False
636 gross 916
637 gross 929 class ReverseCurve(CurveBase, ReversePrimitive):
638 gross 928 """
639 caltinay 2180 A curve defined through a list of control points.
640 gross 928 """
641     def __init__(self,curve):
642     """
643 caltinay 2180 Defines a curve from control points.
644 gross 928 """
645 gross 929 if not isinstance(curve, Curve):
646     raise TypeError("ReverseCurve needs to be an instance of Curve")
647 gross 928 CurveBase.__init__(self)
648 gross 929 ReversePrimitive.__init__(self,curve)
649 gross 928
650     def getControlPoints(self):
651     """
652 caltinay 2180 Returns a list of the points.
653 gross 928 """
654 gross 929 out=[p for p in self.getUnderlyingPrimitive().getControlPoints()]
655 gross 928 out.reverse()
656 gross 2598 return tuple(out)
657 gross 928
658 gross 916 class Spline(Curve):
659     """
660 caltinay 2180 A spline curve defined through a list of control points.
661 gross 916 """
662 gross 1045 pass
663 gross 898
664     class BezierCurve(Curve):
665     """
666 caltinay 2180 A Bezier curve.
667 gross 898 """
668 gross 1045 pass
669 gross 898
670 gross 916 class BSpline(Curve):
671 gross 898 """
672 caltinay 2180 A BSpline curve. Control points may be repeated.
673 gross 898 """
674 gross 1045 pass
675 gross 898
676     class Line(Curve):
677     """
678 caltinay 2180 A line is defined by two points.
679 gross 898 """
680 gross 916 def __init__(self,*points):
681 gross 899 """
682 caltinay 2180 Defines a line with start and end point.
683 gross 899 """
684 gross 916 if len(points)!=2:
685     raise TypeError("Line needs two points")
686 gross 929 Curve.__init__(self,*points)
687 gross 898
688 gross 928 class ArcBase(Manifold1D):
689 caltinay 2180 """
690     Base class for arcs.
691     """
692 gross 929 def __init__(self):
693     """
694 caltinay 2180 Initializes the arc.
695 gross 929 """
696     Manifold1D.__init__(self)
697 caltinay 2180
698 gross 929 def collectPrimitiveBases(self):
699 gross 928 """
700 caltinay 2180 Returns the primitives used to construct the Arc.
701 gross 928 """
702     out=[self]
703 gross 929 out+=self.getStartPoint().collectPrimitiveBases()
704     out+=self.getEndPoint().collectPrimitiveBases()
705     out+=self.getCenterPoint().collectPrimitiveBases()
706 gross 928 return out
707    
708 gross 929 def getCenterPoint(self):
709     """
710 caltinay 2180 Returns the center.
711 gross 929 """
712     raise NotImplementedError()
713 gross 928
714 gross 929 class Arc(ArcBase, Primitive):
715 gross 898 """
716 jfenwick 2625 Defines an arc which is strictly smaller than pi.
717 gross 898 """
718     def __init__(self,center,start,end):
719     """
720 caltinay 2180 Creates an arc defined by the start point, end point and center.
721 gross 898 """
722 gross 917 if not isinstance(center,Point): raise TypeError("center needs to be a Point object.")
723     if not isinstance(end,Point): raise TypeError("end needs to be a Point object.")
724     if not isinstance(start,Point): raise TypeError("start needs to be a Point object.")
725 ksteube 1312 if center.isColocated(end): raise TypeError("center and start point are colocated.")
726     if center.isColocated(start): raise TypeError("center end end point are colocated.")
727     if start.isColocated(end): raise TypeError("start and end are colocated.")
728 gross 917 # TODO: check length of circle.
729 gross 928 ArcBase.__init__(self)
730 gross 929 Primitive.__init__(self)
731 gross 898 self.__center=center
732 gross 916 self.__start=start
733     self.__end=end
734 caltinay 2180
735 gross 928 def __neg__(self):
736 caltinay 2180 """
737     Returns a view onto the curve with reversed ordering.
738     """
739     return ReverseArc(self)
740 gross 898
741 gross 916 def getStartPoint(self):
742 gross 898 """
743 caltinay 2180 Returns the start point.
744 gross 916 """
745     return self.__start
746    
747     def getEndPoint(self):
748     """
749 caltinay 2180 Returns the end point.
750 gross 916 """
751     return self.__end
752    
753     def getCenterPoint(self):
754     """
755 caltinay 2180 Returns the center point.
756 gross 898 """
757     return self.__center
758    
759 gross 929 def substitute(self,sub_dict):
760     """
761 caltinay 2180 Returns a copy of self with substitutes for the primitives used to
762 jfenwick 2625 construct it given by the dictionary ``sub_dict``. If a substitute for
763     the object is given by ``sub_dict`` the value is returned, otherwise a
764 caltinay 2180 new instance with substituted arguments is returned.
765 gross 929 """
766     if not sub_dict.has_key(self):
767     sub_dict[self]=Arc(self.getCenterPoint().substitute(sub_dict),self.getStartPoint().substitute(sub_dict),self.getEndPoint().substitute(sub_dict))
768     return sub_dict[self]
769    
770     def isColocated(self,primitive):
771     """
772 caltinay 2180 Returns True if curves are at the same position.
773 gross 929 """
774 caltinay 2180 if hasattr(primitive,"getUnderlyingPrimitive"):
775 gross 930 if isinstance(primitive.getUnderlyingPrimitive(),Arc):
776 gross 929 return (self.getCenterPoint().isColocated(primitive.getCenterPoint())) and ( \
777     (self.getEndPoint().isColocated(primitive.getEndPoint()) and self.getStartPoint().isColocated(primitive.getStartPoint()) ) \
778     or (self.getEndPoint().isColocated(primitive.getStartPoint()) and self.getStartPoint().isColocated(primitive.getEndPoint()) ) )
779 gross 930 return False
780 gross 929
781     class ReverseArc(ArcBase, ReversePrimitive):
782 gross 928 """
783 jfenwick 2625 Defines an arc which is strictly smaller than pi.
784 gross 928 """
785     def __init__(self,arc):
786 gross 916 """
787 caltinay 2180 Creates an arc defined by the start point, end point and center.
788 gross 916 """
789 gross 929 if not isinstance(arc, Arc):
790     raise TypeError("ReverseCurve needs to be an instance of Arc")
791 gross 928 ArcBase.__init__(self)
792 gross 929 ReversePrimitive.__init__(self,arc)
793 gross 916
794 gross 928 def getStartPoint(self):
795 gross 916 """
796 caltinay 2180 Returns the start point.
797 gross 916 """
798 gross 929 return self.getUnderlyingPrimitive().getEndPoint()
799 gross 899
800 gross 928 def getEndPoint(self):
801     """
802 caltinay 2180 Returns the end point.
803 gross 928 """
804 gross 929 return self.getUnderlyingPrimitive().getStartPoint()
805 gross 916
806 gross 928 def getCenterPoint(self):
807 gross 916 """
808 caltinay 2180 Returns the center point.
809 gross 916 """
810 gross 929 return self.getUnderlyingPrimitive().getCenterPoint()
811 gross 916
812 ksteube 1312 class EllipseBase(Manifold1D):
813 caltinay 2180 """
814     Base class for ellipses.
815     """
816 ksteube 1312 def __init__(self):
817 caltinay 2180 """
818     Initializes the ellipse.
819     """
820     Manifold1D.__init__(self)
821    
822 ksteube 1312 def collectPrimitiveBases(self):
823     """
824 caltinay 2180 Returns the primitives used to construct the ellipse.
825 ksteube 1312 """
826     out=[self]
827     out+=self.getStartPoint().collectPrimitiveBases()
828     out+=self.getEndPoint().collectPrimitiveBases()
829     out+=self.getCenterPoint().collectPrimitiveBases()
830     out+=self.getPointOnMainAxis().collectPrimitiveBases()
831     return out
832    
833     class Ellipse(EllipseBase, Primitive):
834     """
835 jfenwick 2625 Defines an ellipse which is strictly smaller than pi.
836 ksteube 1312 """
837     def __init__(self,center,point_on_main_axis,start,end):
838     """
839 caltinay 2180 Creates an ellipse defined by the start point, end point, the center
840     and a point on the main axis.
841 ksteube 1312 """
842     if not isinstance(center,Point): raise TypeError("center needs to be a Point object.")
843     if not isinstance(end,Point): raise TypeError("end needs to be a Point object.")
844     if not isinstance(start,Point): raise TypeError("start needs to be a Point object.")
845     if not isinstance(point_on_main_axis,Point): raise TypeError("point on main axis needs to be a Point object.")
846     if center.isColocated(end): raise TypeError("center and start point are colocated.")
847     if center.isColocated(start): raise TypeError("center end end point are colocated.")
848     if center.isColocated(point_on_main_axis): raise TypeError("center and point on main axis are colocated.")
849     if start.isColocated(end): raise TypeError("start and end point are colocated.")
850     # TODO: check length of circle.
851     EllipseBase.__init__(self)
852     Primitive.__init__(self)
853     self.__center=center
854     self.__start=start
855     self.__end=end
856     self.__point_on_main_axis=point_on_main_axis
857    
858     def __neg__(self):
859 caltinay 2180 """
860     Returns a view onto the curve with reversed ordering.
861     """
862     return ReverseEllipse(self)
863 ksteube 1312
864     def getStartPoint(self):
865     """
866 caltinay 2180 Returns the start point.
867 ksteube 1312 """
868     return self.__start
869    
870     def getEndPoint(self):
871     """
872 caltinay 2180 Returns the end point.
873 ksteube 1312 """
874     return self.__end
875    
876     def getCenterPoint(self):
877     """
878 caltinay 2180 Returns the center.
879 ksteube 1312 """
880     return self.__center
881    
882     def getPointOnMainAxis(self):
883     """
884 caltinay 2180 Returns a point on the main axis.
885 ksteube 1312 """
886     return self.__point_on_main_axis
887    
888     def substitute(self,sub_dict):
889     """
890 caltinay 2180 Returns a copy of self with substitutes for the primitives used to
891 jfenwick 2625 construct it given by the dictionary ``sub_dict``. If a substitute for
892     the object is given by ``sub_dict`` the value is returned, otherwise a
893 caltinay 2180 new instance with substituted arguments is returned.
894 ksteube 1312 """
895     if not sub_dict.has_key(self):
896     sub_dict[self]=Ellipse(self.getCenterPoint().substitute(sub_dict),
897     self.getPointOnMainAxis().substitute(sub_dict),
898     self.getStartPoint().substitute(sub_dict),
899     self.getEndPoint().substitute(sub_dict))
900     return sub_dict[self]
901    
902    
903     def isColocated(self,primitive):
904     """
905 caltinay 2180 Returns True if curves are at the same position.
906 ksteube 1312 """
907 caltinay 2180 if hasattr(primitive,"getUnderlyingPrimitive"):
908 ksteube 1312 if isinstance(primitive.getUnderlyingPrimitive(),Ellipse):
909     self_c=self.getCenterPoint().getCoordinates()
910     p=self.getPointOnMainAxis().getCoordinates()-self_c
911     q=primitive.getPointOnMainAxis().getCoordinates()-self_c
912     # are p and q orthogonal or collinear?
913 gross 2377 len_p=math.sqrt(p[0]**2+p[1]**2+p[2]**2)
914     len_q=math.sqrt(q[0]**2+q[1]**2+q[2]**2)
915 ksteube 1312 p_q= abs(p[0]*q[0]+p[1]*q[1]+p[2]*q[2])
916     return ((p_q <= getToleranceForColocation() * len_q * p_q) or \
917 caltinay 2180 (abs(p_q - len_q * p_q) <= getToleranceForColocation())) and \
918 ksteube 1312 self.getCenterPoint().isColocated(primitive.getCenterPoint()) and \
919 caltinay 2180 ( \
920     (self.getEndPoint().isColocated(primitive.getEndPoint()) and \
921     self.getStartPoint().isColocated(primitive.getStartPoint()) ) \
922     or \
923     (self.getEndPoint().isColocated(primitive.getStartPoint()) and \
924     self.getStartPoint().isColocated(primitive.getEndPoint())) \
925 ksteube 1312 )
926     return False
927    
928     class ReverseEllipse(EllipseBase, ReversePrimitive):
929     """
930 jfenwick 2625 Defines an ellipse which is strictly smaller than pi.
931 ksteube 1312 """
932     def __init__(self,arc):
933     """
934 caltinay 2180 Creates an instance of a reverse view to an ellipse.
935 ksteube 1312 """
936     if not isinstance(arc, Ellipse):
937     raise TypeError("ReverseCurve needs to be an instance of Ellipse")
938     EllipseBase.__init__(self)
939     ReversePrimitive.__init__(self,arc)
940    
941     def getStartPoint(self):
942     """
943 caltinay 2180 Returns the start point.
944 ksteube 1312 """
945     return self.getUnderlyingPrimitive().getEndPoint()
946    
947     def getEndPoint(self):
948     """
949 caltinay 2180 Returns the end point.
950 ksteube 1312 """
951     return self.getUnderlyingPrimitive().getStartPoint()
952    
953     def getCenterPoint(self):
954     """
955 caltinay 2180 Returns the center point.
956 ksteube 1312 """
957     return self.getUnderlyingPrimitive().getCenterPoint()
958    
959     def getPointOnMainAxis(self):
960     """
961 caltinay 2180 Returns a point on the main axis.
962 ksteube 1312 """
963     return self.getUnderlyingPrimitive().getPointOnMainAxis()
964    
965    
966 gross 929 class CurveLoop(Primitive, PrimitiveBase):
967 gross 898 """
968 caltinay 2180 An oriented loop of one-dimensional manifolds (= curves and arcs).
969 gross 898
970 jfenwick 2625 The loop must be closed and the `Manifold1D` s should be oriented
971 caltinay 2180 consistently.
972 gross 898 """
973     def __init__(self,*curves):
974     """
975 caltinay 2180 Creates a polygon from a list of line curves. The curves must form a
976     closed loop.
977 gross 898 """
978 gross 2627 if len(curves)==1:
979     curves=curves[0]
980     if not hasattr(curves,'__iter__'): raise ValueError("CurveLoop needs at least two points")
981 gross 923 if len(curves)<2:
982 gross 2627 raise ValueError("At least two curves have to be given.")
983 gross 899 for i in range(len(curves)):
984 gross 928 if not isinstance(curves[i],Manifold1D):
985     raise TypeError("%s-th argument is not a Manifold1D object."%i)
986 gross 923 # for the curves a loop:
987     used=[ False for i in curves]
988 gross 932 self.__curves=list(curves)
989 gross 930 Primitive.__init__(self)
990     PrimitiveBase.__init__(self)
991 gross 898
992     def getCurves(self):
993 gross 919 """
994 caltinay 2180 Returns the curves defining the CurveLoop.
995 gross 919 """
996 gross 898 return self.__curves
997 gross 925
998 gross 929 def __neg__(self):
999     """
1000 caltinay 2180 Returns a view onto the curve with reversed ordering.
1001 gross 929 """
1002     return ReverseCurveLoop(self)
1003    
1004 gross 898 def __len__(self):
1005 gross 919 """
1006 caltinay 2180 Returns the number of curves in the CurveLoop.
1007 gross 919 """
1008 gross 928 return len(self.getCurves())
1009 gross 919
1010 gross 929 def collectPrimitiveBases(self):
1011 gross 919 """
1012 caltinay 2180 Returns primitives used to construct the CurveLoop.
1013 gross 919 """
1014 gross 928 out=[self]
1015 gross 929 for c in self.getCurves(): out+=c.collectPrimitiveBases()
1016 gross 928 return out
1017 gross 919
1018 gross 925 def substitute(self,sub_dict):
1019 gross 919 """
1020 caltinay 2180 Returns a copy of self with substitutes for the primitives used to
1021 jfenwick 2625 construct it given by the dictionary ``sub_dict``. If a substitute for
1022     the object is given by ``sub_dict`` the value is returned, otherwise a
1023 caltinay 2180 new instance with substituted arguments is returned.
1024 gross 919 """
1025 gross 925 if not sub_dict.has_key(self):
1026     new_c=[]
1027     for c in self.getCurves(): new_c.append(c.substitute(sub_dict))
1028     sub_dict[self]=CurveLoop(*tuple(new_c))
1029     return sub_dict[self]
1030 gross 919
1031     def isColocated(self,primitive):
1032     """
1033 jfenwick 2625 Returns True if each curve is colocated with a curve in ``primitive``.
1034 gross 919 """
1035 caltinay 2180 if hasattr(primitive,"getUnderlyingPrimitive"):
1036 gross 930 if isinstance(primitive.getUnderlyingPrimitive(),CurveLoop):
1037     if len(primitive) == len(self):
1038     cp0=self.getCurves()
1039     cp1=primitive.getCurves()
1040 caltinay 2180 for c0 in cp0:
1041 ksteube 1312 colocated = False
1042 artak 1727 for c1 in cp1:
1043 ksteube 1312 colocated = colocated or c0.isColocated(c1)
1044     if not colocated: return False
1045 gross 930 return True
1046     return False
1047 gross 919
1048 gross 929 class ReverseCurveLoop(ReversePrimitive, PrimitiveBase):
1049 gross 898 """
1050 caltinay 2180 An oriented loop of one-dimensional manifolds (= curves and arcs).
1051 gross 929
1052 caltinay 2180 The loop must be closed and the one-dimensional manifolds should be
1053     oriented consistently.
1054 gross 929 """
1055     def __init__(self,curve_loop):
1056     """
1057 caltinay 2180 Creates a polygon from a list of line curves. The curves must form a
1058     closed loop.
1059 gross 929 """
1060     if not isinstance(curve_loop, CurveLoop):
1061 gross 931 raise TypeError("arguments need to be an instance of CurveLoop.")
1062 gross 929 ReversePrimitive.__init__(self, curve_loop)
1063     PrimitiveBase.__init__(self)
1064    
1065     def getCurves(self):
1066     """
1067 caltinay 2180 Returns the curves defining the CurveLoop.
1068 gross 929 """
1069     return [ -c for c in self.getUnderlyingPrimitive().getCurves() ]
1070    
1071     def __len__(self):
1072     return len(self.getUnderlyingPrimitive())
1073    
1074 gross 930 #=
1075     class Manifold2D(PrimitiveBase):
1076 gross 929 """
1077 caltinay 2180 General two-dimensional manifold.
1078 gross 2429
1079 jfenwick 2625 :ivar LEFT: left element orientation when meshing with transifinite meshing
1080     :ivar RIGHT: right element orientation when meshing with transifinite meshing
1081     :ivar ALTERNATE: alternate element orientation when meshing with transifinite meshing
1082 gross 898 """
1083 gross 2429 LEFT="Left"
1084     RIGHT="Right"
1085     ALTERNATE="Alternate"
1086 gross 928 def __init__(self):
1087     """
1088 caltinay 2180 Creates a two-dimensional manifold.
1089 gross 928 """
1090 gross 930 PrimitiveBase.__init__(self)
1091 gross 2429 self.setRecombination(None)
1092     self.resetTransfiniteMeshing()
1093 gross 928
1094 gross 930 def getBoundary(self):
1095 gross 928 """
1096 caltinay 2180 Returns a list of the one-dimensional manifolds forming the boundary
1097     of the surface (including holes).
1098 gross 928 """
1099 gross 930 raise NotImplementedError()
1100 gross 928
1101 gross 2429 def hasHole(self):
1102     """
1103     Returns True if a hole is present.
1104     """
1105     raise NotImplementedError()
1106    
1107     def getPoints(self):
1108     """
1109     returns a list of points used to define the boundary
1110    
1111 jfenwick 2625 :return: list of points used to define the boundary
1112     :rtype: ``list`` of `Point` s
1113 gross 2429 """
1114     out=[]
1115     boundary=self.getBoundary()
1116     for l in boundary:
1117     for p in l.getBoundary():
1118     if not p in out: out.append(p)
1119     return out
1120    
1121     def setRecombination(self, max_deviation=45*DEG):
1122     """
1123     Recombines triangular meshes on the surface into mixed triangular/quadrangular meshes.
1124 jfenwick 2625 ``max_deviation`` specifies the maximum derivation of the largest angle in the quadrangle
1125     from the right angle. Use ``max_deviation``==``None`` to switch off recombination.
1126 gross 2429
1127 jfenwick 2625 :param max_deviation: maximum derivation of the largest angle in the quadrangle from the right angle.
1128     :type max_deviation: ``float`` or ``None``.
1129 gross 2429 """
1130     if not max_deviation==None:
1131     if max_deviation<=0:
1132     raise ValueError, "max_deviation must be positive."
1133     if max_deviation/DEG>=90:
1134     raise ValueError, "max_deviation must be smaller than 90 DEG"
1135     self.__recombination_angle=max_deviation
1136    
1137     def getRecombination(self):
1138     """
1139     returns max deviation from right angle in the recombination algorithm
1140    
1141 jfenwick 2625 :return: max deviation from right angle in the recombination algorithm. If recombination is switched off, ``None`` is returned.
1142     :rtype: ``float`` or ``None``
1143 gross 2429 """
1144     return self.__recombination_angle
1145    
1146     def setTransfiniteMeshing(self,orientation="Left"):
1147     """
1148     applies 2D transfinite meshing to the surface.
1149    
1150 jfenwick 2625 :param orientation: sets the orientation of the triangles. It is only used if recombination is not used.
1151     :type orientation: `Manifold2D.LEFT`, `Manifold2D.RIGHT`, `Manifold2D.ALTERNATE`
1152     :note: Transfinite meshing can not be applied if holes are present.
1153 gross 2429 """
1154     if not orientation in [ Manifold2D.LEFT, Manifold2D.RIGHT, Manifold2D.ALTERNATE]:
1155     raise ValueError,"invalid orientation %s."%orientation
1156     if self.hasHole():
1157     raise ValueError,"transfinite meshing cannot be appled to surfaces with a hole."
1158     b=self.getBoundary()
1159     if len(b)>4 or len(b)<3:
1160     raise ValueError,"transfinite meshing permits 3 or 4 boundary lines only."
1161     for l in b:
1162     if l.getElementDistribution() == None: raise ValueError,"transfinite meshing requires element distribution on all boundary lines."
1163     start=b[0]
1164     opposite=None
1165     top=None
1166     bottom=None
1167     for l in b[1:]:
1168     if l.getEndPoint() == start.getStartPoint():
1169     bottom=l
1170     elif l.getStartPoint() == start.getEndPoint():
1171     top=l
1172     else:
1173     opposite=l
1174     if top==None or bottom == None:
1175     raise ValueError,"transfinite meshing cannot be applied to boundary is not closed. Most likely the orientation of some boundray segments is wrong."
1176     if opposite == None: # three sides only
1177     if not top.getElementDistribution() == bottom.getElementDistribution(): start, top, bottom= bottom, start, top
1178     if not top.getElementDistribution() == bottom.getElementDistribution():
1179     raise ValueError,"transfinite meshing requires oposite faces to be have the same element distribution."
1180     if not opposite == None:
1181     if not start.getElementDistribution() == opposite.getElementDistribution():
1182     raise ValueError,"transfinite meshing requires oposite faces to be have the same element distribution."
1183     if opposite == None:
1184     if bottom.getEndPoint == top.getStartPoint():
1185     raise ValueError,"cannot identify corner proints for transfinite meshing."
1186     else:
1187     points=[ bottom.getStartPoint(), bottom.getEndPoint(), top.getStartPoint() ]
1188     else:
1189     points=[ bottom.getStartPoint(), bottom.getEndPoint(), top.getStartPoint(), top.getEndPoint() ]
1190     self.__points=points
1191     self.__orientation=orientation
1192     self.__transfinitemeshing=True
1193    
1194     def resetTransfiniteMeshing(self):
1195     """
1196     removes the transfinite meshing from the surface
1197     """
1198     self.__transfinitemeshing=False
1199    
1200     def getTransfiniteMeshing(self):
1201     """
1202 jfenwick 2625 returns the transfinite meshing setings. If transfinite meshing is not set, ``None`` is returned.
1203 gross 2429
1204 jfenwick 2625 :return: a tuple of the tuple of points used to define the transfinite meshing and the orientation. If no points are set the points tuple is returned as ``None``. If no transfinite meshing is not set, ``None`` is returned.
1205     :rtype: ``tuple`` of a ``tuple`` of `Point` s (or ``None``) and the orientation which is one of the values `Manifold2D.LEFT` , `Manifold2D.RIGHT` , `Manifold2D.ALTERNATE`
1206 gross 2429 """
1207     if self.__transfinitemeshing:
1208     return (self.__points, self.__orientation)
1209     else:
1210     return None
1211    
1212 gross 930 class RuledSurface(Primitive, Manifold2D):
1213 gross 927 """
1214 caltinay 2180 A ruled surface, i.e. a surface that can be interpolated using transfinite
1215     interpolation.
1216 gross 927 """
1217 gross 898 def __init__(self,loop):
1218     """
1219 jfenwick 2625 Creates a ruled surface with boundary ``loop``.
1220 gross 898
1221 jfenwick 2625 :param loop: `CurveLoop` defining the boundary of the surface.
1222 gross 898 """
1223 gross 930 if not isinstance(loop.getUnderlyingPrimitive(),CurveLoop):
1224 gross 898 raise TypeError("argument loop needs to be a CurveLoop object.")
1225 gross 927 if len(loop)<2:
1226 gross 931 raise ValueError("the loop must contain at least two Curves.")
1227 gross 927 if len(loop)>4:
1228 caltinay 2180 raise ValueError("the loop must contain at most four Curves.")
1229 gross 930 Primitive.__init__(self)
1230     Manifold2D.__init__(self)
1231 gross 898 self.__loop=loop
1232 gross 927
1233 gross 2429 def hasHole(self):
1234     """
1235     Returns True if a hole is present.
1236     """
1237     return False
1238    
1239 gross 930 def __neg__(self):
1240 caltinay 2180 """
1241     Returns a view onto the suface with reversed ordering.
1242     """
1243     return ReverseRuledSurface(self)
1244 gross 930
1245 gross 898 def getBoundaryLoop(self):
1246 gross 927 """
1247 caltinay 2180 Returns the loop defining the outer boundary.
1248 gross 927 """
1249     return self.__loop
1250    
1251 gross 930 def getBoundary(self):
1252 gross 928 """
1253 caltinay 2180 Returns a list of the one-dimensional manifolds forming the boundary
1254     of the Surface (including holes).
1255 gross 928 """
1256 gross 930 return self.getBoundaryLoop().getCurves()
1257 gross 927
1258     def substitute(self,sub_dict):
1259     """
1260 caltinay 2180 Returns a copy of self with substitutes for the primitives used to
1261 jfenwick 2625 construct it given by the dictionary ``sub_dict``. If a substitute for
1262     the object is given by ``sub_dict`` the value is returned, otherwise a
1263 caltinay 2180 new instance with substituted arguments is returned.
1264 gross 927 """
1265     if not sub_dict.has_key(self):
1266 gross 928 sub_dict[self]=RuledSurface(self.getBoundaryLoop().substitute(sub_dict))
1267 gross 927 return sub_dict[self]
1268    
1269     def isColocated(self,primitive):
1270     """
1271 jfenwick 2625 Returns True if each curve is colocated with a curve in ``primitive``.
1272 gross 927 """
1273 caltinay 2180 if hasattr(primitive,"getUnderlyingPrimitive"):
1274 gross 930 if isinstance(primitive.getUnderlyingPrimitive(),RuledSurface):
1275     return self.getBoundaryLoop().isColocated(primitive.getBoundaryLoop())
1276     return False
1277 gross 927
1278 gross 930 def collectPrimitiveBases(self):
1279     """
1280 caltinay 2180 Returns primitives used to construct the Surface.
1281 gross 930 """
1282     return [self] + self.getBoundaryLoop().collectPrimitiveBases()
1283    
1284 gross 927 def createRuledSurface(*curves):
1285     """
1286 jfenwick 2625 An easier way to create a `RuledSurface` from given curves.
1287 gross 927 """
1288     return RuledSurface(CurveLoop(*curves))
1289    
1290 gross 930
1291     class ReverseRuledSurface(ReversePrimitive, Manifold2D):
1292 gross 898 """
1293 jfenwick 2625 Creates a view onto a `RuledSurface` but with reverse orientation.
1294 gross 930 """
1295     def __init__(self,surface):
1296     """
1297 caltinay 2180 Creates a polygon from a list of line curves. The curves must form a
1298     closed loop.
1299 gross 930 """
1300     if not isinstance(surface, RuledSurface):
1301 gross 931 raise TypeError("arguments need to be an instance of CurveLoop.")
1302 gross 930 ReversePrimitive.__init__(self, surface)
1303     Manifold2D.__init__(self)
1304    
1305     def getBoundaryLoop(self):
1306     """
1307 caltinay 2180 Returns the CurveLoop defining the ReverseRuledSurface.
1308 gross 930 """
1309     return -self.getUnderlyingPrimitive().getBoundaryLoop()
1310    
1311     def getBoundary(self):
1312 caltinay 2180 """
1313     Returns a list of the one-dimensional manifolds forming the boundary
1314     of the Surface (including holes).
1315     """
1316     return self.getBoundaryLoop().getCurves()
1317    
1318 gross 2429 def hasHole(self):
1319     """
1320     Returns True if a hole is present.
1321     """
1322     return False
1323    
1324 gross 930 #==============================
1325     class PlaneSurface(Primitive, Manifold2D):
1326     """
1327 caltinay 2180 A plane surface with holes.
1328 gross 898 """
1329     def __init__(self,loop,holes=[]):
1330     """
1331 caltinay 2180 Creates a plane surface with holes.
1332 gross 898
1333 jfenwick 2625 :param loop: `CurveLoop` defining the boundary of the surface
1334     :param holes: list of `CurveLoop` s defining holes in the surface
1335     :note: A CurveLoop defining a hole should not have any lines in common
1336 caltinay 2180 with the exterior CurveLoop.
1337 jfenwick 2625 :note: A CurveLoop defining a hole should not have any lines in common
1338 caltinay 2180 with another CurveLoop defining a hole in the same surface.
1339 gross 898 """
1340 gross 930 if not isinstance(loop.getUnderlyingPrimitive(),CurveLoop):
1341 gross 927 raise TypeError("argument loop needs to be a CurveLoop object.")
1342 gross 898 for i in range(len(holes)):
1343 gross 930 if not isinstance(holes[i].getUnderlyingPrimitive(), CurveLoop):
1344 gross 928 raise TypeError("%i-th hole needs to be a CurveLoop object.")
1345     #TODO: check if lines and holes are in a plane
1346     #TODO: are holes really holes?
1347 gross 930 Primitive.__init__(self)
1348     Manifold2D.__init__(self)
1349 gross 928 self.__loop=loop
1350 gross 898 self.__holes=holes
1351 caltinay 2180
1352 gross 2429 def hasHole(self):
1353     """
1354     Returns True if a hole is present.
1355     """
1356     return len(self.getHoles())>0
1357    
1358 gross 898 def getHoles(self):
1359 gross 927 """
1360 caltinay 2180 Returns the holes.
1361 gross 927 """
1362 gross 898 return self.__holes
1363 gross 930
1364 gross 927 def getBoundaryLoop(self):
1365     """
1366 caltinay 2180 Returns the loop defining the boundary.
1367 gross 927 """
1368     return self.__loop
1369    
1370     def substitute(self,sub_dict):
1371     """
1372 caltinay 2180 Returns a copy of self with substitutes for the primitives used to
1373 jfenwick 2625 construct it given by the dictionary ``sub_dict``. If a substitute for
1374     the object is given by ``sub_dict`` the value is returned, otherwise a
1375 caltinay 2180 new instance with substituted arguments is returned.
1376 gross 927 """
1377     if not sub_dict.has_key(self):
1378 gross 928 sub_dict[self]=PlaneSurface(self.getBoundaryLoop().substitute(sub_dict),[ h.substitute(sub_dict) for h in self.getHoles()])
1379 gross 927 return sub_dict[self]
1380 gross 898
1381 gross 927 def isColocated(self,primitive):
1382 gross 898 """
1383 jfenwick 2625 Returns True if each curve is colocated with a curve in ``primitive``.
1384 gross 927 """
1385 caltinay 2180 if hasattr(primitive,"getUnderlyingPrimitive"):
1386 gross 930 if isinstance(primitive.getUnderlyingPrimitive(),PlaneSurface):
1387     if self.getBoundaryLoop().isColocated(primitive.getBoundaryLoop()):
1388     hs0=self.getHoles()
1389     hs1=primitive.getHoles()
1390     if len(hs0) == len(hs1):
1391     for h0 in hs0:
1392 ksteube 1312 colocated = False
1393 caltinay 2180 for h1 in hs1:
1394 ksteube 1312 colocated = colocated or h0.isColocated(h1)
1395     if not colocated: return False
1396 gross 930 return True
1397     return False
1398 caltinay 2180
1399 gross 930 def collectPrimitiveBases(self):
1400     """
1401 caltinay 2180 Returns primitives used to construct the Surface.
1402 gross 930 """
1403     out=[self] + self.getBoundaryLoop().collectPrimitiveBases()
1404     for i in self.getHoles(): out+=i.collectPrimitiveBases()
1405     return out
1406 caltinay 2180
1407 gross 930 def __neg__(self):
1408 caltinay 2180 """
1409     Returns a view onto the curve with reversed ordering.
1410     """
1411     return ReversePlaneSurface(self)
1412    
1413 gross 930 def getBoundary(self):
1414     """
1415 caltinay 2180 Returns a list of the one-dimensional manifolds forming the boundary
1416     of the Surface (including holes).
1417 gross 930 """
1418     out = []+ self.getBoundaryLoop().getCurves()
1419     for h in self.getHoles(): out+=h.getCurves()
1420     return out
1421 gross 898
1422 gross 930 class ReversePlaneSurface(ReversePrimitive, Manifold2D):
1423 gross 898 """
1424 jfenwick 2625 Creates a view onto a `PlaneSurface` but with reverse orientation.
1425 gross 930 """
1426     def __init__(self,surface):
1427     """
1428 jfenwick 2625 Creates a polygon from a `PlaneSurface`.
1429 gross 930 """
1430     if not isinstance(surface, PlaneSurface):
1431 gross 931 raise TypeError("arguments need to be an instance of PlaneSurface.")
1432 gross 930 ReversePrimitive.__init__(self, surface)
1433     Manifold2D.__init__(self)
1434    
1435     def getBoundaryLoop(self):
1436     """
1437 caltinay 2180 Returns the CurveLoop defining the ReversePlaneSurface.
1438 gross 930 """
1439     return -self.getUnderlyingPrimitive().getBoundaryLoop()
1440    
1441     def getHoles(self):
1442     """
1443 caltinay 2180 Returns the holes.
1444 gross 930 """
1445     return [ -h for h in self.getUnderlyingPrimitive().getHoles() ]
1446    
1447     def getBoundary(self):
1448     """
1449 caltinay 2180 Returns a list of the one-dimensional manifolds forming the boundary
1450     of the Surface (including holes).
1451 gross 930 """
1452     out = [] + self.getBoundaryLoop().getCurves()
1453     for h in self.getHoles(): out+=h.getCurves()
1454     return out
1455    
1456 gross 2429 def hasHole(self):
1457     """
1458     Returns True if a hole is present.
1459     """
1460     return len(self.getHoles())>0
1461 gross 930
1462     #=========================================================================
1463     class SurfaceLoop(Primitive, PrimitiveBase):
1464     """
1465 caltinay 2180 A loop of 2D primitives which defines the shell of a volume.
1466 gross 898
1467 caltinay 2180 The loop must represent a closed shell, and the primitives should be
1468     oriented consistently.
1469 gross 898 """
1470     def __init__(self,*surfaces):
1471     """
1472 caltinay 2180 Creates a surface loop.
1473 gross 898 """
1474 gross 2627 if len(surfaces)==1:
1475     surfaces=surfaces[0]
1476     if not hasattr(surfaces,'__iter__'): raise ValueError("SurfaceLoop needs at least two points")
1477 gross 928 if len(surfaces)<2:
1478 gross 931 raise ValueError("at least two surfaces have to be given.")
1479 gross 899 for i in range(len(surfaces)):
1480 gross 930 if not isinstance(surfaces[i].getUnderlyingPrimitive(),Manifold2D):
1481     raise TypeError("%s-th argument is not a Manifold2D object."%i)
1482 gross 932 self.__surfaces=list(surfaces)
1483 gross 930 Primitive.__init__(self)
1484     PrimitiveBase.__init__(self)
1485 caltinay 2180
1486 gross 928 def __len__(self):
1487     """
1488 caltinay 2180 Returns the number of curves in the SurfaceLoop.
1489 gross 928 """
1490     return len(self.__surfaces)
1491 gross 898
1492 gross 930 def __neg__(self):
1493     """
1494 caltinay 2180 Returns a view onto the curve with reversed ordering.
1495 gross 930 """
1496     return ReverseSurfaceLoop(self)
1497    
1498 gross 898 def getSurfaces(self):
1499 gross 928 """
1500 caltinay 2180 Returns the surfaces defining the SurfaceLoop.
1501 gross 928 """
1502 gross 930 return self.__surfaces
1503 gross 928
1504 gross 929 def collectPrimitiveBases(self):
1505 gross 928 """
1506 caltinay 2180 Returns primitives used to construct the SurfaceLoop.
1507 gross 928 """
1508     out=[self]
1509 gross 929 for c in self.getSurfaces(): out+=c.collectPrimitiveBases()
1510 gross 928 return out
1511 gross 930
1512 gross 928 def substitute(self,sub_dict):
1513     """
1514 caltinay 2180 Returns a copy of self with substitutes for the primitives used to
1515 jfenwick 2625 construct it given by the dictionary ``sub_dict``. If a substitute for
1516     the object is given by ``sub_dict`` the value is returned, otherwise a
1517 caltinay 2180 new instance with substituted arguments is returned.
1518 gross 928 """
1519     if not sub_dict.has_key(self):
1520     new_s=[]
1521 gross 931 for s in self.getSurfaces(): new_s.append(s.substitute(sub_dict))
1522 gross 928 sub_dict[self]=SurfaceLoop(*tuple(new_s))
1523     return sub_dict[self]
1524 gross 898
1525 gross 928 def isColocated(self,primitive):
1526     """
1527 jfenwick 2625 Returns True if each surface is colocated with a curve in ``primitive``
1528 caltinay 2180 and vice versa.
1529 gross 928 """
1530 caltinay 2180 if hasattr(primitive,"getUnderlyingPrimitive"):
1531 gross 930 if isinstance(primitive.getUnderlyingPrimitive(),SurfaceLoop):
1532     if len(primitive) == len(self):
1533     sp0=self.getSurfaces()
1534 gross 931 sp1=primitive.getSurfaces()
1535 caltinay 2180 for s0 in sp0:
1536 ksteube 1312 colocated = False
1537 artak 1727 for s1 in sp1:
1538 ksteube 1312 colocated = colocated or s0.isColocated(s1)
1539     if not colocated: return False
1540 gross 930 return True
1541     return False
1542 gross 928
1543 gross 930 class ReverseSurfaceLoop(ReversePrimitive, PrimitiveBase):
1544     """
1545 caltinay 2180 A view of a SurfaceLoop with reverse orientation.
1546 gross 930
1547 caltinay 2180 The loop must represent a closed shell and the primitives should be
1548     oriented consistently.
1549 gross 930 """
1550     def __init__(self,surface_loop):
1551     """
1552 caltinay 2180 Creates a polygon from a list of line surfaces. The curves must form
1553     a closed loop.
1554 gross 930 """
1555     if not isinstance(surface_loop, SurfaceLoop):
1556 gross 931 raise TypeError("arguments need to be an instance of SurfaceLoop.")
1557 gross 930 ReversePrimitive.__init__(self, surface_loop)
1558     PrimitiveBase.__init__(self)
1559    
1560     def getSurfaces(self):
1561     """
1562 caltinay 2180 Returns the surfaces defining the SurfaceLoop.
1563 gross 930 """
1564     return [ -s for s in self.getUnderlyingPrimitive().getSurfaces() ]
1565    
1566     def __len__(self):
1567     return len(self.getUnderlyingPrimitive())
1568 gross 931
1569     #==============================
1570     class Manifold3D(PrimitiveBase):
1571 gross 898 """
1572 caltinay 2180 General three-dimensional manifold.
1573 gross 931 """
1574     def __init__(self):
1575     """
1576 caltinay 2180 Creates a three-dimensional manifold.
1577 gross 931 """
1578     PrimitiveBase.__init__(self)
1579    
1580     def getBoundary(self):
1581     """
1582 caltinay 2180 Returns a list of the one-dimensional manifolds forming the boundary
1583     of the volume (including holes).
1584 gross 931 """
1585     raise NotImplementedError()
1586    
1587     class Volume(Manifold3D, Primitive):
1588     """
1589 caltinay 2180 A volume with holes.
1590 gross 898 """
1591     def __init__(self,loop,holes=[]):
1592     """
1593 caltinay 2180 Creates a volume with holes.
1594 gross 898
1595 jfenwick 2625 :param loop: `SurfaceLoop` defining the boundary of the surface
1596     :param holes: list of `SurfaceLoop` defining holes in the surface
1597     :note: A SurfaceLoop defining a hole should not have any surfaces in
1598 caltinay 2180 common with the exterior SurfaceLoop.
1599 jfenwick 2625 :note: A SurfaceLoop defining a hole should not have any surfaces in
1600 caltinay 2180 common with another SurfaceLoop defining a hole in the same
1601     volume.
1602 gross 898 """
1603 gross 931 if not isinstance(loop.getUnderlyingPrimitive(), SurfaceLoop):
1604 gross 898 raise TypeError("argument loop needs to be a SurfaceLoop object.")
1605     for i in range(len(holes)):
1606 gross 931 if not isinstance(holes[i].getUnderlyingPrimitive(), SurfaceLoop):
1607 gross 898 raise TypeError("%i th hole needs to be a SurfaceLoop object.")
1608 gross 931 Primitive.__init__(self)
1609     Manifold3D.__init__(self)
1610 gross 898 self.__loop=loop
1611     self.__holes=holes
1612 caltinay 2180
1613 gross 898 def getHoles(self):
1614 gross 931 """
1615 caltinay 2180 Returns the holes in the volume.
1616 gross 931 """
1617 gross 898 return self.__holes
1618 caltinay 2180
1619 gross 898 def getSurfaceLoop(self):
1620 gross 931 """
1621 caltinay 2180 Returns the loop forming the surface.
1622 gross 931 """
1623 gross 898 return self.__loop
1624 gross 931
1625     def substitute(self,sub_dict):
1626     """
1627 caltinay 2180 Returns a copy of self with substitutes for the primitives used to
1628 jfenwick 2625 construct it given by the dictionary ``sub_dict``. If a substitute for
1629     the object is given by ``sub_dict`` the value is returned, otherwise a
1630 caltinay 2180 new instance with substituted arguments is returned.
1631 gross 931 """
1632     if not sub_dict.has_key(self):
1633     sub_dict[self]=Volume(self.getSurfaceLoop().substitute(sub_dict),[ h.substitute(sub_dict) for h in self.getHoles()])
1634     return sub_dict[self]
1635    
1636     def isColocated(self,primitive):
1637     """
1638 jfenwick 2625 Returns True if each curve is colocated with a curve in ``primitive``.
1639 gross 931 """
1640 caltinay 2180 if hasattr(primitive,"getUnderlyingPrimitive"):
1641 gross 931 if isinstance(primitive.getUnderlyingPrimitive(),Volume):
1642     if self.getSurfaceLoop().isColocated(primitive.getSurfaceLoop()):
1643     hs0=self.getHoles()
1644     hs1=primitive.getHoles()
1645     if len(hs0) == len(hs1):
1646     for h0 in hs0:
1647 ksteube 1312 colocated = False
1648 caltinay 2180 for h1 in hs1:
1649 ksteube 1312 colocated = colocated or h0.isColocated(h1)
1650     if not colocated: return False
1651 gross 931 return True
1652     return False
1653 caltinay 2180
1654 gross 931 def collectPrimitiveBases(self):
1655     """
1656 caltinay 2180 Returns primitives used to construct the surface.
1657 gross 931 """
1658     out=[self] + self.getSurfaceLoop().collectPrimitiveBases()
1659     for i in self.getHoles(): out+=i.collectPrimitiveBases()
1660     return out
1661 caltinay 2180
1662 gross 931 def getBoundary(self):
1663     """
1664 caltinay 2180 Returns a list of the one-dimensional manifolds forming the boundary
1665     of the Surface (including holes).
1666 gross 931 """
1667     out = []+ self.getSurfaceLoop().getSurfaces()
1668     for h in self.getHoles(): out+=h.getSurfaces()
1669     return out
1670    
1671 gross 944 class PropertySet(Primitive, PrimitiveBase):
1672 gross 898 """
1673 jfenwick 2625 Defines a group of `Primitive` s which can be accessed through a name.
1674 gross 898 """
1675 gross 944 def __init__(self,name,*items):
1676     Primitive.__init__(self)
1677 gross 1123 self.__dim=None
1678 gross 944 self.clearItems()
1679     self.addItem(*items)
1680     self.setName(name)
1681 gross 1123
1682     def getDim(self):
1683     """
1684 caltinay 2180 Returns the dimensionality of the items.
1685     """
1686 gross 1123 if self.__dim == None:
1687     items=self.getItems()
1688     if len(items)>0:
1689 caltinay 2180 if isinstance(items[0] ,Manifold1D):
1690 gross 1123 self.__dim=1
1691 caltinay 2180 elif isinstance(items[0] ,Manifold2D):
1692 gross 1123 self.__dim=2
1693 caltinay 2180 elif isinstance(items[0] ,Manifold3D):
1694 gross 1123 self.__dim=3
1695     else:
1696     self.__dim=0
1697     return self.__dim
1698 caltinay 2180
1699 gross 944 def __repr__(self):
1700     """
1701 caltinay 2180 Returns a string representation.
1702 gross 944 """
1703     return "%s(%s)"%(self.getName(),self.getID())
1704 caltinay 2180
1705 gross 944 def getManifoldClass(self):
1706     """
1707 caltinay 2180 Returns the manifold class expected from items.
1708 gross 944 """
1709     d=self.getDim()
1710 gross 1123 if d == None:
1711     raise ValueError("undefined spatial diemnsion.")
1712 gross 944 else:
1713 gross 1123 if d==0:
1714     return Point
1715     elif d==1:
1716     return Manifold1D
1717     elif d==2:
1718     return Manifold2D
1719     else:
1720     return Manifold3D
1721    
1722 gross 944 def getName(self):
1723     """
1724 caltinay 2180 Returns the name of the set.
1725 gross 944 """
1726     return self.__name
1727 caltinay 2180
1728 gross 999 def setName(self,name):
1729 gross 944 """
1730 caltinay 2180 Sets the name.
1731 gross 944 """
1732     self.__name=str(name)
1733 gross 1123
1734     def addItems(self,*items):
1735     """
1736 jfenwick 2625 Adds items. An item my be any `Primitive` but no `PropertySet`.
1737 gross 1123 """
1738     self.addItem(*items)
1739    
1740 caltinay 2180 def addItem(self,*items):
1741 gross 944 """
1742 jfenwick 2625 Adds items. An item my be any `Primitive` but no `PropertySet`.
1743 gross 944 """
1744 caltinay 2180 for i in items:
1745 gross 2700 if not (isinstance(i, Manifold1D) or isinstance(i, Manifold2D) or isinstance(i, Manifold3D) ):
1746     raise TypeError, "Illegal argument type %s added to PropertySet."%(i.__class__)
1747     for i in items:
1748 caltinay 2180 if not i in self.__items:
1749 gross 1123 if len(self.__items)>0:
1750     m=self.getManifoldClass()
1751     if not isinstance(i, m):
1752     raise TypeError("argument %s is not a %s class object."%(i, m.__name__))
1753 gross 944 self.__items.append(i)
1754 caltinay 2180
1755 gross 1123 def getNumItems(self):
1756     """
1757 caltinay 2180 Returns the number of items in the property set.
1758     """
1759 gross 1123 return len(self.__items)
1760    
1761 gross 944 def getItems(self):
1762     """
1763 caltinay 2180 Returns the list of items.
1764 gross 944 """
1765     return self.__items
1766    
1767     def clearItems(self):
1768     """
1769 caltinay 2180 Clears the list of items.
1770 gross 944 """
1771     self.__items=[]
1772 caltinay 2180
1773 gross 929 def collectPrimitiveBases(self):
1774 gross 944 """
1775 caltinay 2180 Returns primitives used to construct the PropertySet.
1776 gross 944 """
1777 caltinay 2180 out=[self]
1778 gross 944 for i in self.getItems(): out+=i.collectPrimitiveBases()
1779 gross 899 return out
1780 gross 944
1781     def getTag(self):
1782 caltinay 2180 """
1783     Returns the tag used for this property set.
1784     """
1785     return self.getID()
1786    

  ViewVC Help
Powered by ViewVC 1.1.26