/[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 6651 - (hide annotations)
Wed Feb 7 02:12:08 2018 UTC (20 months, 1 week ago) by jfenwick
File MIME type: text/x-python
File size: 68602 byte(s)
Make everyone sad by touching all the files

Copyright dates update

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

  ViewVC Help
Powered by ViewVC 1.1.26