/[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 6939 - (hide annotations)
Mon Jan 20 03:37:18 2020 UTC (3 years, 2 months ago) by uqaeller
File MIME type: text/x-python
File size: 68672 byte(s)
Updated the copyright header.


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

  ViewVC Help
Powered by ViewVC 1.1.26