1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2015,2016 Thorsten Liebig (Thorsten.Liebig@gmx.de)
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License as published
7# by the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU Lesser General Public License for more details.
14#
15# You should have received a copy of the GNU Lesser General Public License
16# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17#
18
19"""CSXCAD.CSPrimitives
20Module for all Primitives
21
22Notes
23-----
24Usually it is not meant to create primitives manually, but instead
25use the ContinuousStructure object to create primives using the
26e.g. AddBox or AddCylinder methods.
27
28Examples
29--------
30Create a metal box:
31
32>>> pset  = ParameterObjects.ParameterSet()
33>>> metal = CSProperties.CSPropMetal(pset)
34>>> box   = CSPrimitives.CSPrimBox(pset, metal)
35"""
36
37import numpy as np
38import sys
39from libcpp.string cimport string
40from libcpp cimport bool
41
42cimport CSPrimitives
43from Utilities import CheckNyDir, GetMultiDirs
44cimport CSRectGrid
45
46cdef class CSPrimitives:
47    """
48    Virtual base class for all primives, cannot be created!
49
50    """
51    @staticmethod
52    def fromType(prim_type, pset, prop, no_init=False, **kw):
53        """ fromType(prim_type, pset, prop, no_init=False, **kw)
54
55        Create a primtive specified by the `prim_type`
56
57        :param prim_type: Primitive type
58        :param pset: ParameterSet to assign to the new primitive
59        :param prop: CSProperty to assign to the new primitive
60        :param no_init: do not create an actual C++ instance
61        """
62        prim = None
63        if prim_type == POINT:
64            prim = CSPrimPoint(pset, prop, no_init=no_init, **kw)
65        elif prim_type == POINT:
66            prim = CSPrimPoint(pset, prop, no_init=no_init, **kw)
67        elif prim_type == BOX:
68            prim = CSPrimBox(pset, prop, no_init=no_init, **kw)
69        elif prim_type == MULTIBOX:
70            raise Exception('Primitive type "MULTIBOX" not yet implemented!')
71        elif prim_type == SPHERE:
72            prim = CSPrimSphere(pset, prop, no_init=no_init, **kw)
73        elif prim_type == SPHERICALSHELL:
74            prim = CSPrimSphericalShell(pset, prop, no_init=no_init, **kw)
75        elif prim_type == CYLINDER:
76            prim = CSPrimCylinder(pset, prop, no_init=no_init, **kw)
77        elif prim_type == CYLINDRICALSHELL:
78            prim = CSPrimCylindricalShell(pset, prop, no_init=no_init, **kw)
79        elif prim_type == POLYGON:
80            prim = CSPrimPolygon(pset, prop, no_init=no_init, **kw)
81        elif prim_type == LINPOLY:
82            prim = CSPrimLinPoly(pset, prop, no_init=no_init, **kw)
83        elif prim_type == ROTPOLY:
84            prim = CSPrimRotPoly(pset, prop, no_init=no_init, **kw)
85        elif prim_type == POLYHEDRON:
86            prim = CSPrimPolyhedron(pset, prop, no_init=no_init, **kw)
87        elif prim_type == CURVE:
88            prim = CSPrimCurve(pset, prop, no_init=no_init, **kw)
89        elif prim_type == WIRE:
90            prim = CSPrimWire(pset, prop, no_init=no_init, **kw)
91        elif prim_type == USERDEFINED:
92            raise Exception('Primitive type "USERDEFINED" not yet implemented!')
93        elif prim_type == POLYHEDRONREADER:
94            prim = CSPrimPolyhedronReader(pset, prop, no_init=no_init, **kw)
95        return prim
96
97    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
98        self.__transform = None
99        self.__prop      = prop
100        if no_init:
101            self.thisptr = NULL
102            return
103
104        assert self.thisptr, 'Error, object cannot be created (protected)'
105        self.__prop = prop
106
107        if 'priority' in kw:
108            self.SetPriority(kw['priority'])
109            del kw['priority']
110        if 'no_init' in kw:
111            del kw['no_init']
112
113        assert len(kw)==0, 'Unknown keyword arguments: "{}"'.format(kw)
114
115    def GetID(self):
116        """
117        Get the ID for this primitive
118
119        :returns: int -- ID for this primitive
120        """
121        return self.thisptr.GetID()
122
123    def GetProperty(self):
124        """
125        Get the property for this primitive
126
127        :returns: CSProperties.CSProperties
128        """
129        return self.__GetProperty()
130
131    cdef __GetProperty(self):
132        cdef _CSProperties* _prop
133        cdef CSProperties prop
134        if self.__prop is None:
135            _prop = self.thisptr.GetProperty()
136            self.__prop  = CSProperties.fromType(_prop.GetType(), pset=None, no_init=True)
137            self.__prop.thisptr = _prop
138
139        return self.__prop
140
141    def GetType(self):
142        """
143        Get the type as int for this primitive
144
145        :returns: int -- Type for this primitive (e.g. 0 --> Point, 1 --> Box, ...)
146        """
147        return self.thisptr.GetType()
148
149    def GetTypeName(self):
150        """
151        Get the type as string (UTF-8) for this primitive
152
153        :returns: str -- Type name for this primitive ("Point", "Box", ...)
154        """
155        return self.thisptr.GetTypeName().decode('UTF-8')
156
157    def SetPriority(self, val):
158        """ SetPriority(val)
159
160        Set the priority for this primitive
161
162        :param val: int -- Higher priority values will override primitives with a lower priority
163        """
164        self.thisptr.SetPriority(val)
165
166    def GetPriority(self):
167        """
168        Get the priority for this primitive
169
170        :returns: int -- Priority for this primitive
171        """
172        return self.thisptr.GetPriority()
173
174    def GetBoundBox(self):
175        """
176        Get the bounding box for this primitive
177
178        :returns: (2,3) ndarray -- bounding box for this primitive
179        """
180        bb = np.zeros([2,3])
181        cdef double _bb[6]
182        self.thisptr.GetBoundBox(_bb)
183        for n in range(3):
184            bb[0,n] = _bb[2*n]
185            bb[1,n] = _bb[2*n+1]
186        return bb
187
188    def GetDimension(self):
189        """
190        Get the dimension of this primitive
191
192        :returns: int -- dimension 0..3
193        """
194        return self.thisptr.GetDimension()
195
196    def IsInside(self, coord, tol=0):
197        """ IsInside(coord, tol=0)
198
199        Check if a given coordinate is inside this primitive.
200
201        :param coord: (3,) array -- coordinate
202        :returns: bool
203        """
204        cdef double c_coord[3]
205        for n in range(3):
206            c_coord[n] = coord[n]
207
208        return self.thisptr.IsInside(c_coord, tol)
209
210    def GetPrimitiveUsed(self):
211        """
212        Get if this primitive has been used (used flag set)
213        """
214        return self.thisptr.GetPrimitiveUsed()
215
216    def SetPrimitiveUsed(self, val):
217        """ SetPrimitiveUsed(val)
218
219        Set used flag.
220        """
221        self.thisptr.SetPrimitiveUsed(val)
222
223    def __GetCSX(self):
224        if self.__prop is None:
225            return None
226        return self.__prop.__CSX
227
228    def GetTransform(self):
229        """ GetTransform()
230
231        Get the transformation class assigned to this primitive.
232        If this primitve does not have any, it will be created.
233
234        :return: CSTransform class
235
236        See Also
237        --------
238        CSXCAD.CSTransform.CSTransform
239        """
240        if self.__transform is None:
241            self.__transform         = CSTransform(no_init=True)
242            self.__transform.thisptr = self.thisptr.GetTransform()
243        return self.__transform
244
245    def AddTransform(self, transform, *args, no_init=False, **kw):
246        """ AddTransform(transform, *args, **kw)
247
248        Add a transformation to this primitive.
249
250        See Also
251        --------
252        CSXCAD.CSTransform.CSTransform.AddTransform
253        """
254        tr = self.GetTransform()
255        tr.AddTransform(transform, *args, **kw)
256
257    def HasTransform(self):
258        """
259        Check if this primitive has a transformation attached.
260        It will not create one if it does not.
261
262        :returns: bool
263        """
264        if self.__transform is None:
265            return False
266        return self.__transform.HasTransform()
267
268    def SetCoordinateSystem(self, cs_type):
269        """ SetCoordinateSystem(cs_type)
270
271        Set the coordinate system type (Cartesian or cylindrical) for this primitive.
272        If set to None, the mesh type of the assigned rect grid will be used.
273
274        :param cs_type: coordinate system (0 : Cartesian, 1 : Cylindrical) or None
275
276        See Also
277        --------
278        CSXCAD.CSRectGrid.CSRectGrid.SetMeshType
279
280        """
281        assert cs_type in [CSRectGrid.CARTESIAN, CSRectGrid.CYLINDRICAL, None], 'Unknown coordinate system: {}'.format(cs_type)
282        if cs_type is None:
283            cs_type = CSRectGrid.UNDEFINED_CS
284        self.thisptr.SetCoordinateSystem(cs_type)
285
286    def GetCoordinateSystem(self):
287        """ GetCoordinateSystem
288
289        :returns: coordinate system (0 : Cartesian, 1 : Cylindrical) or None
290        """
291        cs_type = self.thisptr.GetCoordinateSystem()
292        if cs_type == CSRectGrid.UNDEFINED_CS:
293            return None
294        return cs_type
295
296    def Update(self):
297        """ Trigger an internal update and report success and error message
298
299        :returns: bool, err_msg -- success and error message (empty on success)
300        """
301        cdef string s
302        succ = self.thisptr.Update(&s)
303        return succ, str(s)
304
305
306###############################################################################
307cdef class CSPrimPoint(CSPrimitives):
308    """ Point Primitive
309
310    A point is defined by a single 3D coordinate.
311
312    Parameters
313    ----------
314    coord : (vector)
315        Coordinate vector (3,) array
316
317    """
318    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
319        if no_init:
320            self.thisptr = NULL
321            return
322        if not self.thisptr:
323            self.thisptr   = new _CSPrimPoint(pset.thisptr, prop.thisptr)
324
325        if 'coord' in kw:
326            self.SetCoord(kw['coord'])
327            del kw['coord']
328        super(CSPrimPoint, self).__init__(pset, prop, *args, **kw)
329
330    def SetCoord(self, coord):
331        """ SetCoord(coord)
332
333        Set the coordinate for this point primitive
334
335        :param coord: list/array of float -- Set the point coordinate
336        """
337        ptr = <_CSPrimPoint*>self.thisptr
338        assert len(coord)==3, "CSPrimPoint:SetCoord: length of array needs to be 3"
339        for n in range(3):
340            ptr.SetCoord(n, coord[n])
341
342    def GetCoord(self):
343        """
344        Get the point coordinate for this primitive
345
346        :returns: (3,) ndarray -- point coordinate for this primitive
347        """
348        ptr = <_CSPrimPoint*>self.thisptr
349        coord = np.zeros(3)
350        for n in range(3):
351            coord[n] = ptr.GetCoord(n)
352        return coord
353
354###############################################################################
355cdef class CSPrimBox(CSPrimitives):
356    """ Box Primitive
357
358    A box is defined by two 3D coordinates. E.g. the lower-left (start)
359    and upper right (stop) point.
360    A box is a cube in Cartesian coordinates.
361    A box is a cylinder in cylindrical coordinates.
362
363    Parameters
364    ----------
365    start : (3,) array
366        Start point vector (3,) array
367    stop : (3,) array
368        Stop point vector (3,) array
369
370    """
371    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
372        if no_init:
373            self.thisptr = NULL
374            return
375        if not self.thisptr:
376            self.thisptr   = new _CSPrimBox(pset.thisptr, prop.thisptr)
377        if 'start' in kw:
378            self.SetStart(kw['start'])
379            del kw['start']
380        if 'stop' in kw:
381            self.SetStop(kw['stop'])
382            del kw['stop']
383        super(CSPrimBox, self).__init__(pset, prop, *args, **kw)
384
385    def SetStart(self, coord):
386        """ SetStart(coord)
387
388        Set the start coordinate for this box primitive.
389
390        :param coord: list/array of float -- Set the start point coordinate
391        """
392        ptr = <_CSPrimBox*>self.thisptr
393        assert len(coord)==3, "CSPrimBox:SetStart: length of array needs to be 3"
394        for n in range(3):
395            ptr.SetCoord(2*n, coord[n])
396
397    def SetStop(self, coord):
398        """ SetStop(coord)
399
400        Set the stop coordinate for this box primitive.
401
402        :param coord: list/array of float -- Set the stop point coordinate
403        """
404        ptr = <_CSPrimBox*>self.thisptr
405        assert len(coord)==3, "CSPrimBox:SetStop: length of array needs to be 3"
406        for n in range(3):
407            ptr.SetCoord(2*n+1, coord[n])
408
409    def GetStart(self):
410        """
411        Get the start coordinate for this primitive.
412
413        :returns: (3,) ndarray -- Start coordinate for this primitive
414        """
415        ptr = <_CSPrimBox*>self.thisptr
416        coord = np.zeros(3)
417        for n in range(3):
418            coord[n] = ptr.GetCoord(2*n)
419        return coord
420
421    def GetStop(self):
422        """
423        Get the stop coordinate for this primitive.
424
425        :returns: (3,) ndarray -- Stop coordinate for this primitive
426        """
427        ptr = <_CSPrimBox*>self.thisptr
428        coord = np.zeros(3)
429        for n in range(3):
430            coord[n] = ptr.GetCoord(2*n+1)
431        return coord
432
433###############################################################################
434cdef class CSPrimCylinder(CSPrimitives):
435    """ Cylinder Primitive
436
437    A cylinder is defined by its axis and a radius.
438    The axis is defined by two 3D coordinates (start/stop).
439
440    Parameters
441    ----------
442    start : (3,) array
443        Axis start point vector (3,) array
444    stop : (3,) array
445        Axis stop point vector (3,) array
446    radius : float
447        The cylinder radius
448
449    """
450    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
451        if no_init:
452            self.thisptr = NULL
453            return
454        if not self.thisptr:
455            self.thisptr   = new _CSPrimCylinder(pset.thisptr, prop.thisptr)
456        if 'start' in kw:
457            self.SetStart(kw['start'])
458            del kw['start']
459        if 'stop' in kw:
460            self.SetStop(kw['stop'])
461            del kw['stop']
462        if 'radius' in kw:
463            self.SetRadius(kw['radius'])
464            del kw['radius']
465        super(CSPrimCylinder, self).__init__(pset, prop, *args, **kw)
466
467    def SetStart(self, coord):
468        """ SetStart(coord)
469
470        Set the start coordinate for the cylinder axis.
471
472        :param coord: list/array of float -- Set the axis start point coordinate.
473        """
474        ptr = <_CSPrimCylinder*>self.thisptr
475        assert len(coord)==3, "CSPrimCylinder:SetStart: length of array needs to be 3"
476        for n in range(3):
477            ptr.SetCoord(2*n, coord[n])
478
479    def SetStop(self, coord):
480        """ SetStop(coord)
481
482        Set the stop coordinate for the cylinder axis.
483
484        :param coord: list/array of float -- Set the axis stop point coordinate.
485        """
486        ptr = <_CSPrimCylinder*>self.thisptr
487        assert len(coord)==3, "CSPrimCylinder:SetStop: length of array needs to be 3"
488        for n in range(3):
489            ptr.SetCoord(2*n+1, coord[n])
490
491    def GetStart(self):
492        """
493        Get the axis start coordinate.
494
495        :returns: (3,) ndarray -- Axis start coordinate.
496        """
497        ptr = <_CSPrimCylinder*>self.thisptr
498        coord = np.zeros(3)
499        for n in range(3):
500            coord[n] = ptr.GetCoord(2*n)
501        return coord
502
503    def GetStop(self):
504        """
505        Get the axis stop coordinate.
506
507        :returns: (3,) ndarray -- Axis stop coordinate.
508        """
509        ptr = <_CSPrimCylinder*>self.thisptr
510        coord = np.zeros(3)
511        for n in range(3):
512            coord[n] = ptr.GetCoord(2*n+1)
513        return coord
514
515    def SetRadius(self, val):
516        """ SetRadius(val)
517
518        Set the cylinder radius
519
520        :param val: float -- Set the cylinder radius
521        """
522        ptr = <_CSPrimCylinder*>self.thisptr
523        ptr.SetRadius(val)
524
525    def GetRadius(self):
526        """
527        Get the cylinder radius.
528
529        :returns: float -- Cylinder radius.
530        """
531        ptr = <_CSPrimCylinder*>self.thisptr
532        return ptr.GetRadius()
533
534###############################################################################
535cdef class CSPrimCylindricalShell(CSPrimCylinder):
536    """ Cylindrical Shell (hollow cylinder)
537
538    A cylindrical shell is defined like a cylinder with an additional shell width.
539
540    Parameters
541    ----------
542    shell_width : float
543        Width of the cylindrical shell
544
545    """
546    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
547        if no_init:
548            self.thisptr = NULL
549            return
550        if not self.thisptr:
551            self.thisptr = new _CSPrimCylindricalShell(pset.thisptr, prop.thisptr)
552        if 'shell_width' in kw:
553            self.SetShellWidth(kw['shell_width'])
554            del kw['shell_width']
555        super(CSPrimCylindricalShell, self).__init__(pset, prop, *args, **kw)
556
557    def SetShellWidth(self, val):
558        """ SetShellWidth(val)
559
560        Set the cylinder shell width.
561
562        :param val: float -- Set the cylinder shell width
563        """
564        ptr = <_CSPrimCylindricalShell*>self.thisptr
565        ptr.SetShellWidth(val)
566
567    def GetShellWidth(self):
568        """
569        Get the cylinder shell width.
570
571        :returns: float -- Cylinder shell width.
572        """
573        ptr = <_CSPrimCylindricalShell*>self.thisptr
574        return ptr.GetShellWidth()
575
576###############################################################################
577cdef class CSPrimSphere(CSPrimitives):
578    """ Sphere Primitive
579
580    A sphere is defined by its center and radius.
581    The center is defined by a 3D coordinate.
582
583    Parameters
584    ----------
585    center : (3,) array
586        Center point vector (3,) array
587    radius : float
588        The sphere radius
589
590    """
591    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
592        if no_init:
593            self.thisptr = NULL
594            return
595        if not self.thisptr:
596            self.thisptr   = new _CSPrimSphere(pset.thisptr, prop.thisptr)
597        if 'center' in kw:
598            self.SetCenter(kw['center'])
599            del kw['center']
600        if 'radius' in kw:
601            self.SetRadius(kw['radius'])
602            del kw['radius']
603        super(CSPrimSphere, self).__init__(pset, prop, *args, **kw)
604
605    def SetCenter(self, coord):
606        """ SetRadius(val)
607
608        Set the sphere center point.
609
610        :param coord: (3,) array -- Set the sphere center point.
611        """
612        ptr = <_CSPrimSphere*>self.thisptr
613        assert len(coord)==3, "CSPrimSphere:SetCenter: length of array needs to be 3"
614        ptr.SetCenter(coord[0], coord[1], coord[2])
615
616    def GetCenter(self):
617        """
618        Get the sphere center point.
619
620        :returns: (3,) ndarray -- Center coordinate.
621        """
622        ptr = <_CSPrimSphere*>self.thisptr
623        coord = np.zeros(3)
624        for n in range(3):
625            coord[n] = ptr.GetCoord(n)
626        return coord
627
628    def SetRadius(self, val):
629        """ SetRadius(val)
630
631        Set the sphere radius
632
633        :param val: float -- Set the sphere radius
634        """
635        ptr = <_CSPrimSphere*>self.thisptr
636        ptr.SetRadius(val)
637
638    def GetRadius(self):
639        """
640        Get the sphere radius.
641
642        :returns: float -- Sphere radius.
643        """
644        ptr = <_CSPrimSphere*>self.thisptr
645        return ptr.GetRadius()
646
647###############################################################################
648cdef class CSPrimSphericalShell(CSPrimSphere):
649    """ Spherical Shell (hollow sphere) Primitive Class
650
651    A spherical shell is defined like a sphere with an additional shell width.
652
653    Parameters
654    ----------
655    shell_width : float
656        Width of the spherical shell
657
658    """
659    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
660        if no_init:
661            self.thisptr = NULL
662            return
663        if not self.thisptr:
664            self.thisptr = new _CSPrimSphericalShell(pset.thisptr, prop.thisptr)
665        if 'shell_width' in kw:
666            self.SetShellWidth(kw['shell_width'])
667            del kw['shell_width']
668        super(CSPrimSphericalShell, self).__init__(pset, prop, *args, **kw)
669
670    def SetShellWidth(self, val):
671        """ SetShellWidth(val)
672
673        Set the sphere shell width.
674
675        :param val: float -- Set the sphere shell width
676        """
677        ptr = <_CSPrimSphericalShell*>self.thisptr
678        ptr.SetShellWidth(val)
679
680    def GetShellWidth(self):
681        """
682        Get the sphere shell width.
683
684        :returns: float -- sphere shell width.
685        """
686        ptr = <_CSPrimSphericalShell*>self.thisptr
687        return ptr.GetShellWidth()
688
689###############################################################################
690cdef class CSPrimPolygon(CSPrimitives):
691    """ Polygon Primitive
692
693    A polygon is a surface defined by a set of 2D points/coordinates.
694    To place the polygon in a 3D space, a normal direction (x/y/z) and an
695    elevation in this direction have to be specified.
696
697    Parameters
698    ----------
699    points : (N,2) array
700        Array of coordinates
701    norm_dir : float or str
702        Normal direction, either 0,1,2 or x/y/z respectively
703    elevation : float
704        Elevation in normal direciton
705
706    Examples
707    --------
708    Define a half circle as polygon
709
710    >>> polygon = CSPrimitives.CSPrimBox(pset, metal, norm_dir='z', elevation=1.0)
711    >>> ang     = np.linspace(0, np.pi, 21)
712    >>> polygon.SetCoords(5*np.cos(ang), 5*np.sin(ang))
713    """
714    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
715        if no_init:
716            self.thisptr = NULL
717            return
718        if not self.thisptr:
719            self.thisptr = new _CSPrimPolygon(pset.thisptr, prop.thisptr)
720        if 'points' in kw:
721            assert len(kw['points'])==2, 'points must be a list of length 2'
722            self.SetCoords(kw['points'][0], kw['points'][1])
723            del kw['points']
724        if 'norm_dir' in kw:
725            self.SetNormDir(kw['norm_dir'])
726            del kw['norm_dir']
727        if 'elevation' in kw:
728            self.SetElevation(kw['elevation'])
729            del kw['elevation']
730        super(CSPrimPolygon, self).__init__(pset, prop, *args, **kw)
731
732    def SetCoords(self, x0, x1):
733        """ SetCoords(x0, x1)
734
735        Set the coordinates for the polygon. This will clear all previous coordinates.
736
737        :param x0, x1: (N,), (N,) Two arrays for x0/x1 of the polygon coordinates.
738        """
739        assert len(x0)==len(x1), 'SetCoords: x0/x1 do not have the same length'
740        assert len(x0)>0, 'SetCoords: empty coordinates'
741
742        ptr = <_CSPrimPolygon*>self.thisptr
743        ptr.ClearCoords()
744        for n in range(len(x0)):
745            ptr.AddCoord(x0[n])
746            ptr.AddCoord(x1[n])
747
748    def GetCoords(self):
749        """
750        Get the coordinates for the polygon
751
752        :return x0, x1: (N,), (N,) Arrays for x0,x1 of the polygon coordinates
753        """
754        ptr = <_CSPrimPolygon*>self.thisptr
755        N = ptr.GetQtyCoords()
756        x0 = np.zeros(N)
757        x1 = np.zeros(N)
758        for n in range(N):
759            x0[n] = ptr.GetCoord(2*n)
760            x1[n] = ptr.GetCoord(2*n+1)
761        return x0, x1
762
763    def GetQtyCoords(self):
764        """
765        Get the number of coordinates for the polygon
766
767        :return val: int -- Number of polygon coordinates.
768        """
769        ptr = <_CSPrimPolygon*>self.thisptr
770        return ptr.GetQtyCoords()
771
772    def ClearCoords(self):
773        """
774        Remove all coordinates.
775        """
776        ptr = <_CSPrimPolygon*>self.thisptr
777        ptr.ClearCoords()
778
779    def SetNormDir(self, ny):
780        """ SetNormDir(ny)
781
782        Set the normal direction.
783
784        :param ny: int or string -- Normal direction, either 0/1/2 or 'x'/'y'/'z'
785        """
786        ptr = <_CSPrimPolygon*>self.thisptr
787        ptr.SetNormDir(CheckNyDir(ny))
788
789    def GetNormDir(self):
790        """
791        Get the normal direction.
792
793        :return ny: int -- Normal direction as 0, 1 or 2 meaning x,y or z
794        """
795        ptr = <_CSPrimPolygon*>self.thisptr
796        return ptr.GetNormDir()
797
798    def SetElevation(self, val):
799        """ SetElevation(val)
800
801        Set the elevation in normal direction.
802
803        :param val: float -- Elevation in normal direction.
804        """
805        ptr = <_CSPrimPolygon*>self.thisptr
806        ptr.SetElevation(val)
807
808    def GetElevation(self):
809        """
810        Get the elevation in normal direction.
811
812        :return val: float -- Get the elevation in normal direction.
813        """
814        ptr = <_CSPrimPolygon*>self.thisptr
815        return ptr.GetElevation()
816
817###############################################################################
818cdef class CSPrimLinPoly(CSPrimPolygon):
819    """ Linear Extruded Polygon
820
821    A linear extruded polygon is a polygon that is extruded in normal direction
822    for a certain length above the elevation.
823
824    Parameters
825    ----------
826    length : float
827        Extrusion length in normal direction
828
829    Examples
830    --------
831    Define a half circle with a height (in z direction) of 1
832
833    >>> linpoly = CSPrimitives.CSPrimLinPoly(pset, metal, norm_dir='z', elevation=0.5)
834    >>> ang     = np.linspace(0, np.pi, 21)
835    >>> linpoly.SetCoords(5*np.cos(ang), 5*np.sin(ang))
836    >>> linpoly.SetLength(1.0)
837    """
838    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
839        if no_init:
840            self.thisptr = NULL
841            return
842        if not self.thisptr:
843            self.thisptr = new _CSPrimLinPoly(pset.thisptr, prop.thisptr)
844        if 'length' in kw:
845            self.SetLength(kw['length'])
846            del kw['length']
847        super(CSPrimLinPoly, self).__init__(pset, prop, *args, **kw)
848
849    def SetLength(self, val):
850        """ SetLength(val)
851
852        Set the extrusion length in normal direction.
853
854        :param val: float -- Extrusion length in normal direction.
855        """
856        ptr = <_CSPrimLinPoly*>self.thisptr
857        ptr.SetLength(val)
858
859    def GetLength(self):
860        """
861        Get the extrusion length in normal direction.
862
863        :return val: float -- Get the extrusion length in normal direction.
864        """
865        ptr = <_CSPrimLinPoly*>self.thisptr
866        return ptr.GetLength()
867
868###############################################################################
869cdef class CSPrimRotPoly(CSPrimPolygon):
870    """ Rotation Extruded Polygon
871
872    A rotation extruded polygon is a polygon that is rotated around a Cartesian
873    axis with a given start and stop angle.
874
875    Parameters
876    ----------
877    rot_axis : float or str
878        Rotation axis direction, either 0,1,2 or x/y/z respectively.
879    angle : float, float
880        Start/Stop angle (rad) of rotation. Default is (0, 2*pi).
881
882    Examples
883    --------
884    Define a half circle on the xy-plane, rotated around the x axis
885
886    >>> rotpoly = CSPrimitives.CSPrimRotPoly(pset, metal, norm_dir='z')
887    >>> ang     = np.linspace(0, np.pi, 21)
888    >>> rotpoly.SetCoords(5*np.cos(ang), 5*np.sin(ang))
889    >>> rotpoly.SetRotAxisDir('x')
890    """
891    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
892        if no_init:
893            self.thisptr = NULL
894            return
895        if not self.thisptr:
896            self.thisptr = new _CSPrimRotPoly(pset.thisptr, prop.thisptr)
897        if 'rot_axis' in kw:
898            self.SetRotAxisDir(kw['rot_axis'])
899            del kw['rot_axis']
900        if 'angle' in kw:
901            assert len(kw['angle'])==2, 'angle must be a list/array of length 2'
902            self.SetAngle(kw['angle'][0],kw['angle'][1])
903            del kw['angle']
904        super(CSPrimRotPoly, self).__init__(pset, prop, *args, **kw)
905
906    def SetRotAxisDir(self, ny):
907        """ SetRotAxisDir(ny)
908
909        Set the rotation axis direction, either 0,1,2 or x/y/z respectively.
910
911        :param ny: int or str -- Rotation axis direction, either 0,1,2 or x/y/z respectively.
912        """
913        ptr = <_CSPrimRotPoly*>self.thisptr
914        ptr.SetRotAxisDir(CheckNyDir(ny))
915
916    def GetRotAxisDir(self):
917        """
918        Get the rotation axis direction
919
920        :returns ny: int -- Rotation axis direction as 0, 1 or 2 meaning x,y or z
921        """
922        ptr = <_CSPrimRotPoly*>self.thisptr
923        return ptr.GetRotAxisDir()
924
925    def SetAngle(self, a0, a1):
926        """ SetAngle(a0, a1)
927
928        Set the start/stop angle (rad) of rotation. Default is (0, 2*pi).
929
930        :param a0: float -- Start angle (rad) of rotation.
931        :param a1: float -- Stop angle (rad) of rotation.
932        """
933        ptr = <_CSPrimRotPoly*>self.thisptr
934        ptr.SetAngle(0, a0)
935        ptr.SetAngle(1, a1)
936
937    def GetAngle(self):
938        """
939        Get the start/stop angle (rad) of rotation.
940
941        :returns a0, a1: float, float -- Start/Stop angle (rad) of rotation.
942        """
943        ptr = <_CSPrimRotPoly*>self.thisptr
944        return ptr.GetAngle(0), ptr.GetAngle(1)
945
946###############################################################################
947cdef class CSPrimCurve(CSPrimitives):
948    """ Curve Primitive
949
950    A curve is a set of consequtive 3D coordinates.
951
952    Parameters
953    ----------
954    points : (3, N) array
955        Array of 3D coordinates.
956
957    Examples
958    --------
959
960    >>> x = np.array([0, 0, 1, 1]) + 1.5
961    >>> y = np.array([0, 1, 1, 0]) + 2.5
962    >>> z = np.array([0, 1, 3, 4])
963    >>> curve = CSPrimitives.CSPrimCurve(pset, metal, points=[x,y,z])
964    >>> curve.AddPoint([2, 0, 5])
965    """
966    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
967        if no_init:
968            self.thisptr = NULL
969            return
970        if not self.thisptr:
971            self.thisptr = new _CSPrimCurve(pset.thisptr, prop.thisptr)
972        if 'points' in kw:
973            assert len(kw['points'])==3, 'points list not of length 3'
974            self.SetPoints(kw['points'][0], kw['points'][1], kw['points'][2])
975            del kw['points']
976        super(CSPrimCurve, self).__init__(pset, prop, *args, **kw)
977
978    def AddPoint(self, point):
979        """ AddPoint(point)
980
981        Add a single point to the list.
982
983        :param point: (3,) array -- Add a single 3D point
984        """
985        assert len(point)==3, "CSPrimSphere:SetCenter: length of array needs to be 3"
986        ptr = <_CSPrimCurve*>self.thisptr
987        cdef double dp[3]
988        for n in range(3):
989            dp[n] = point[n]
990        ptr.AddPoint(dp)
991
992    def SetPoints(self, x, y, z):
993        """ SetPoints(x, y, z)
994
995        Set an array of 3D coordinates
996
997        :param x,y,z: each (N,) array -- Array of 3D point components
998        """
999        assert len(x)==len(y)==len(z), 'SetPoints: each component must be of equal length'
1000        assert len(x)>0, 'SetPoints: empty list!'
1001        ptr = <_CSPrimCurve*>self.thisptr
1002        ptr.ClearPoints()
1003        cdef double dp[3]
1004        for n in range(len(x)):
1005            dp[0] = x[n]
1006            dp[1] = y[n]
1007            dp[2] = z[n]
1008            ptr.AddPoint(dp)
1009
1010    def GetPoint(self, idx):
1011        """ GetPoint(idx)
1012
1013        Get a point with a given index.
1014
1015        :param idx: int -- Index of point requested.
1016        :return point: (3,) array -- Point coordinate at index `idx`
1017        """
1018        ptr = <_CSPrimCurve*>self.thisptr
1019        cdef double dp[3]
1020        assert ptr.GetPoint(idx, dp), 'GetPoint: invalid index'
1021        point = np.zeros(3)
1022        for n in range(3):
1023            point[n] = dp[n]
1024        return point
1025
1026    def ClearPoints(self):
1027        """
1028        Clear all points.
1029        """
1030        ptr = <_CSPrimCurve*>self.thisptr
1031        ptr.ClearPoints()
1032
1033    def GetNumberOfPoints(self):
1034        """
1035        Get the number of points.
1036
1037        :return num: int -- Get the number of points.
1038        """
1039        ptr = <_CSPrimCurve*>self.thisptr
1040        return ptr.GetNumberOfPoints()
1041
1042
1043###############################################################################
1044cdef class CSPrimWire(CSPrimCurve):
1045    """ Wire Primitive
1046
1047    A wire is a curve with a given radius.
1048
1049    Parameters
1050    ----------
1051    radius : float
1052        Wire radius
1053    """
1054    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
1055        if no_init:
1056            self.thisptr = NULL
1057            return
1058        if not self.thisptr:
1059            self.thisptr = new _CSPrimWire(pset.thisptr, prop.thisptr)
1060        if 'radius' in kw:
1061            self.SetWireRadius(kw['radius'])
1062            del kw['radius']
1063        super(CSPrimWire, self).__init__(pset, prop, *args, **kw)
1064
1065
1066    def SetWireRadius(self, val):
1067        """ SetWireRadius(val)
1068
1069        Set the wire radius
1070
1071        :param val: float -- Set the wire radius
1072        """
1073        ptr = <_CSPrimWire*>self.thisptr
1074        ptr.SetWireRadius(val)
1075
1076    def GetWireRadius(self):
1077        """
1078        Get the wire radius.
1079
1080        :returns: float -- Wire radius.
1081        """
1082        ptr = <_CSPrimWire*>self.thisptr
1083        return ptr.GetWireRadius()
1084
1085###############################################################################
1086cdef class CSPrimPolyhedron(CSPrimitives):
1087    """ Polyhedron Primitive
1088
1089    A polyhedron is a 3D solid with flat polygonal faces (currently only triangles).
1090
1091    """
1092    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
1093        if no_init:
1094            self.thisptr = NULL
1095            return
1096        if not self.thisptr:
1097            self.thisptr = new _CSPrimPolyhedron(pset.thisptr, prop.thisptr)
1098        super(CSPrimPolyhedron, self).__init__(pset, prop, *args, **kw)
1099
1100    def Reset(self):
1101        """
1102        Reset the polyhedron, that means removeing all faces.
1103        """
1104        ptr = <_CSPrimPolyhedron*>self.thisptr
1105        ptr.Reset()
1106
1107    def AddVertex(self, x, y, z):
1108        """ AddVertex(x, y, z)
1109
1110        Add a single 3D vertex.
1111
1112        :param x,y,z: float,float,float -- 3D vertex
1113        """
1114        ptr = <_CSPrimPolyhedron*>self.thisptr
1115        ptr.AddVertex(x, y, z)
1116
1117    def GetVertex(self, idx):
1118        """ GetVertex(idx)
1119
1120        Get the vertex with index `idx`.
1121
1122        :param idx: int -- Vertex index to return
1123        :returns point: (3,) array -- Vertex coordinate at index `idx`
1124        """
1125        ptr = <_CSPrimPolyhedron*>self.thisptr
1126        assert idx>=0 and idx<ptr.GetNumVertices(), "Error: invalid vertex index"
1127        cdef float* p
1128        p = ptr.GetVertex(idx)
1129        assert p!=NULL
1130        pyp = np.zeros(3)
1131        for n in range(3):
1132            pyp[n] = p[n]
1133        return pyp
1134
1135    def GetNumVertices(self):
1136        """
1137        Get the number of vertices.
1138
1139        :returns num: int -- Number of vertices
1140        """
1141        ptr = <_CSPrimPolyhedron*>self.thisptr
1142        return ptr.GetNumVertices()
1143
1144    def AddFace(self, verts):
1145        """ AddFace(verts)
1146
1147        Add a face with a given list of vertices.
1148        The vertices have to be added already.
1149        Currently only triangle faces are possible.
1150
1151        :params verts: (N,) array -- Face with N vericies (currently N=3!)
1152        """
1153        assert len(verts)==3, 'AddFace: currently only triangles allowed as faces'
1154        cdef int i_v[3]
1155        for n in range(3):
1156            i_v[n] = verts[n]
1157        ptr = <_CSPrimPolyhedron*>self.thisptr
1158        ptr.AddFace(len(verts), i_v)
1159
1160    def GetFace(self, idx):
1161        """ GetFace(idx)
1162
1163        Get the face vertex indicies for the given face index `idx`
1164
1165        :param idx: int -- Face index to return
1166        :returns: (N,) array -- Vertices array for face with index `idx`
1167        """
1168        ptr = <_CSPrimPolyhedron*>self.thisptr
1169        assert idx>=0 and idx<ptr.GetNumFaces(), "Error: invalid face index"
1170        cdef int *i_v
1171        cdef unsigned int numVert=0
1172        i_v = ptr.GetFace(idx, numVert)
1173        assert i_v!=NULL
1174        face = np.zeros(numVert, np.int)
1175        for n in range(numVert):
1176            face[n] = i_v[n]
1177        return face
1178
1179    def GetNumFaces(self):
1180        """
1181        Get the number of faces.
1182
1183        :return num: int -- number of faces
1184        """
1185        ptr = <_CSPrimPolyhedron*>self.thisptr
1186        return ptr.GetNumFaces()
1187
1188###############################################################################
1189cdef class CSPrimPolyhedronReader(CSPrimPolyhedron):
1190    """ Polyhedron Reader
1191
1192    This primives creates a polyhedron by reading a STL or PLY file.
1193
1194    Parameters
1195    ----------
1196    filename : str
1197        File name to read
1198    """
1199    def __init__(self, ParameterSet pset, CSProperties prop, *args, no_init=False, **kw):
1200        if no_init:
1201            self.thisptr = NULL
1202            return
1203        if not self.thisptr:
1204            self.thisptr = new _CSPrimPolyhedronReader(pset.thisptr, prop.thisptr)
1205        if 'filename' in kw:
1206            self.SetFilename(kw['filename'])
1207            del kw['filename']
1208        super(CSPrimPolyhedronReader, self).__init__(pset, prop, *args, **kw)
1209
1210    def SetFilename(self, fn):
1211        """ SetFilename(fn)
1212
1213        Set the file name to read. This will try set the propper file type as well.
1214
1215        :param fn: str -- File name to read
1216        """
1217        ptr = <_CSPrimPolyhedronReader*>self.thisptr
1218        if fn.endswith('.stl'):
1219            self.SetFileType(1)
1220        elif fn.endswith('.ply'):
1221            self.SetFileType(2)
1222        else:
1223            self.SetFileType(0)
1224        ptr.SetFilename(fn.encode('UTF-8'))
1225
1226    def GetFilename(self):
1227        """
1228        Get the file name.
1229
1230        :returns fn: str -- File name to read
1231        """
1232        ptr = <_CSPrimPolyhedronReader*>self.thisptr
1233        return ptr.GetFilename()
1234
1235    def SetFileType(self, t):
1236        """ SetFileType(t)
1237
1238        Set the file type. 1 --> STL-File, 2 --> PLY, 0 --> other/unknown
1239
1240        :param t: int -- File type (see above)
1241        """
1242        ptr = <_CSPrimPolyhedronReader*>self.thisptr
1243        ptr.SetFileType(t)
1244
1245    def GetFileType(self):
1246        """
1247        Get the file type. 1 --> STL-File, 2 --> PLY, 0 --> other/unknown
1248
1249        :return t: int -- File type (see above)
1250        """
1251        ptr = <_CSPrimPolyhedronReader*>self.thisptr
1252        return ptr.GetFileType()
1253
1254    def ReadFile(self):
1255        """
1256        Issue to read the file.
1257
1258        :return succ: bool -- True on successful read
1259        """
1260        ptr = <_CSPrimPolyhedronReader*>self.thisptr
1261        return ptr.ReadFile()
1262