1# -*- coding: utf-8 -*-
2#
3#   Copyright © 2010 Eugeniy Meshcheryakov <eugen@debian.org>
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 by
7#   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:mod:`gdsii.elements` -- interface to GDSII elements
19====================================================
20
21This module contains definitions for classes representing
22various GDSII elements. Mapping between GDSII elements and
23classes is given in the following table:
24
25   +-------------------+-------------------+
26   | GDSII Record      | Class             |
27   +===================+===================+
28   | :const:`AREF`     | :class:`ARef`     |
29   +-------------------+-------------------+
30   | :const:`BOUNDARY` | :class:`Boundary` |
31   +-------------------+-------------------+
32   | :const:`BOX`      | :class:`Box`      |
33   +-------------------+-------------------+
34   | :const:`NODE`     | :class:`Node`     |
35   +-------------------+-------------------+
36   | :const:`PATH`     | :class:`Path`     |
37   +-------------------+-------------------+
38   | :const:`SREF`     | :class:`SRef`     |
39   +-------------------+-------------------+
40   | :const:`TEXT`     | :class:`Text`     |
41   +-------------------+-------------------+
42
43This module implements the following GDS syntax:
44    .. productionlist::
45        element: `aref` |
46               : `boundary` |
47               : `box` |
48               : `node` |
49               : `path` |
50               : `sref` |
51               : `text`
52Additional definitions:
53    .. productionlist::
54        properties: `property`*
55        property: PROPATTR
56                : PROPVALUE
57        strans: STRANS
58              : [MAG]
59              : [ANGLE]
60
61.. moduleauthor:: Eugeniy Meshcheryakov <eugen@debian.org>
62"""
63from __future__ import absolute_import
64from . import exceptions, record, tags, _records
65
66__all__ = (
67    'Boundary',
68    'Path',
69    'SRef',
70    'ARef',
71    'Text',
72    'Node',
73    'Box'
74)
75
76_ELFLAGS = _records.OptionalWholeRecord('elflags', tags.ELFLAGS)
77_PLEX = _records.SimpleOptionalRecord('plex', tags.PLEX)
78_LAYER = _records.SimpleRecord('layer', tags.LAYER)
79_DATATYPE = _records.SimpleRecord('data_type', tags.DATATYPE)
80_PATHTYPE = _records.SimpleOptionalRecord('path_type', tags.PATHTYPE)
81_WIDTH = _records.SimpleOptionalRecord('width', tags.WIDTH)
82_BGNEXTN = _records.SimpleOptionalRecord('bgn_extn', tags.BGNEXTN)
83_ENDEXTN = _records.SimpleOptionalRecord('end_extn', tags.ENDEXTN)
84_XY = _records.XYRecord('xy', tags.XY)
85_SNAME = _records.StringRecord('struct_name', tags.SNAME)
86_STRANS = _records.STransRecord('strans', tags.STRANS)
87_COLROW = _records.ColRowRecord('cols', 'rows')
88_TEXTTYPE = _records.SimpleRecord('text_type', tags.TEXTTYPE)
89_PRESENTATION = _records.OptionalWholeRecord('presentation', tags.PRESENTATION)
90_STRING = _records.StringRecord('string', tags.STRING)
91_NODETYPE = _records.SimpleRecord('node_type', tags.NODETYPE)
92_BOXTYPE = _records.SimpleRecord('box_type', tags.BOXTYPE)
93_PROPERTIES = _records.PropertiesRecord('properties')
94
95class _Base(object):
96    """Base class for all GDSII elements."""
97
98    # dummy descriptors to silence pyckecker, should be set in derived classes
99    _gds_tag = None
100    _gds_objs = None
101    __slots__ = ()
102
103    def __init__(self):
104        """Initialize the element."""
105        self._init_optional()
106
107    def _init_optional(self):
108        """Initialize optional attributes to None."""
109        raise NotImplementedError
110
111    @classmethod
112    def _load(cls, gen):
113        """
114        Load an element from file using given generator `gen`.
115
116        :param gen: :class:`pygdsii.record.Record` generator
117        :returns: new element of class defined by `gen`
118        """
119        element_class = cls._tag_to_class_map[gen.current.tag]
120        if not element_class:
121            raise exceptions.FormatError('unexpected element tag')
122        # do not call __init__() during reading from file
123        # __init__() should require some arguments
124        new_element = element_class._read_element(gen)
125        return new_element
126
127    @classmethod
128    def _read_element(cls, gen):
129        """Read element using `gen` generator."""
130        self = cls.__new__(cls)
131        self._init_optional()
132        gen.read_next()
133        for obj in self._gds_objs:
134            obj.read(self, gen)
135        gen.current.check_tag(tags.ENDEL)
136        gen.read_next()
137        return self
138
139    def _save(self, stream):
140        record.Record(self._gds_tag).save(stream)
141        for obj in self._gds_objs:
142            obj.save(self, stream)
143        record.Record(tags.ENDEL).save(stream)
144
145class Boundary(_Base):
146    """
147    Class for :const:`BOUNDARY` GDSII element.
148
149    GDS syntax:
150        .. productionlist::
151            boundary: BOUNDARY
152                     : [ELFLAGS]
153                     : [PLEX]
154                     : LAYER
155                     : DATATYPE
156                     : XY
157                     : [`properties`]
158                     : ENDEL
159    """
160    _gds_tag = tags.BOUNDARY
161    _gds_objs = (_ELFLAGS, _PLEX, _LAYER, _DATATYPE, _XY, _PROPERTIES)
162    __slots__ = ('layer', 'data_type', 'xy', 'elflags', 'plex', 'properties')
163
164    def __init__(self, layer, data_type, xy):
165        _Base.__init__(self)
166        self.layer = layer
167        self.data_type = data_type
168        self.xy = xy
169
170    def _init_optional(self):
171        self.elflags = None
172        self.plex = None
173        self.properties = None
174
175class Path(_Base):
176    """
177    Class for :const:`PATH` GDSII element.
178
179    GDS syntax:
180        .. productionlist::
181            path: PATH
182                : [ELFLAGS]
183                : [PLEX]
184                : LAYER
185                : DATATYPE
186                : [PATHTYPE]
187                : [WIDTH]
188                : [BGNEXTN]
189                : [ENDEXTN]
190                : XY
191                : [`properties`]
192                : ENDEL
193    """
194    _gds_tag = tags.PATH
195    _gds_objs = (_ELFLAGS, _PLEX, _LAYER, _DATATYPE, _PATHTYPE, _WIDTH,
196            _BGNEXTN, _ENDEXTN, _XY, _PROPERTIES)
197    __slots__ = ('layer', 'data_type', 'xy', 'elflags', 'plex', 'path_type',
198            'width', 'bgn_extn', 'end_extn', 'properties')
199
200    def __init__(self, layer, data_type, xy):
201        _Base.__init__(self)
202        self.layer = layer
203        self.data_type = data_type
204        self.xy = xy
205
206    def _init_optional(self):
207        self.elflags = None
208        self.plex = None
209        self.path_type = None
210        self.width = None
211        self.bgn_extn = None
212        self.end_extn = None
213        self.properties = None
214
215class SRef(_Base):
216    """
217    Class for :const:`SREF` GDSII element.
218
219    GDS syntax:
220        .. productionlist::
221            sref: SREF
222                : [ELFLAGS]
223                : [PLEX]
224                : SNAME
225                : [`strans`]
226                : XY
227                : [`properties`]
228                : ENDEL
229    """
230    _gds_tag = tags.SREF
231    _gds_objs = (_ELFLAGS, _PLEX, _SNAME, _STRANS, _XY, _PROPERTIES)
232    __slots__ = ('struct_name', 'xy', 'elflags', 'strans', 'mag', 'angle',
233            'properties')
234
235    def __init__(self, struct_name, xy):
236        _Base.__init__(self)
237        self.struct_name = struct_name
238        self.xy = xy
239
240    def _init_optional(self):
241        self.elflags = None
242        self.strans = None
243        self.mag = None
244        self.angle = None
245        self.properties = None
246
247class ARef(_Base):
248    """
249    Class for :const:`AREF` GDSII element.
250
251    GDS syntax:
252        .. productionlist::
253            aref: AREF
254                : [ELFLAGS]
255                : [PLEX]
256                : SNAME
257                : [`strans`]
258                : COLROW
259                : XY
260                : [`properties`]
261                : ENDEL
262    """
263    _gds_tag = tags.AREF
264    _gds_objs = (_ELFLAGS, _PLEX, _SNAME, _STRANS, _COLROW, _XY, _PROPERTIES)
265    __slots__ = ('struct_name', 'cols', 'rows', 'xy', 'elflags', 'plex',
266            'strans', 'mag', 'angle', 'properties')
267
268    def __init__(self, struct_name, cols, rows, xy):
269        _Base.__init__(self)
270        self.struct_name = struct_name
271        self.cols = cols
272        self.rows = rows
273        self.xy = xy
274
275    def _init_optional(self):
276        self.elflags = None
277        self.plex = None
278        self.strans = None
279        self.mag = None
280        self.angle = None
281        self.properties = None
282
283class Text(_Base):
284    """
285    Class for :const:`TEXT` GDSII element.
286
287    GDS syntax:
288        .. productionlist::
289            text: TEXT
290                : [ELFLAGS]
291                : [PLEX]
292                : LAYER
293                : TEXTTYPE
294                : [PRESENTATION]
295                : [PATHTYPE]
296                : [WIDTH]
297                : [`strans`]
298                : XY
299                : STRING
300                : [`properties`]
301                : ENDEL
302    """
303    _gds_tag = tags.TEXT
304    _gds_objs = (_ELFLAGS, _PLEX, _LAYER, _TEXTTYPE, _PRESENTATION, _PATHTYPE,
305            _WIDTH, _STRANS, _XY, _STRING, _PROPERTIES)
306    __slots__ = ('layer', 'text_type', 'xy', 'string', 'elflags', 'plex',
307            'presentation', 'path_type', 'width', 'strans', 'mag', 'angle',
308            'properties')
309
310    def __init__(self, layer, text_type, xy, string):
311        _Base.__init__(self)
312        self.layer = layer
313        self.text_type = text_type
314        self.xy = xy
315        self.string = string
316
317    def _init_optional(self):
318        self.elflags = None
319        self.plex = None
320        self.presentation = None
321        self.path_type = None
322        self.width = None
323        self.strans = None
324        self.mag = None
325        self.angle = None
326        self.properties = None
327
328class Node(_Base):
329    """
330    Class for :const:`NODE` GDSII element.
331
332    GDS syntax:
333        .. productionlist::
334            node: NODE
335                : [ELFLAGS]
336                : [PLEX]
337                : LAYER
338                : NODETYPE
339                : XY
340                : [`properties`]
341                : ENDEL
342    """
343    _gds_tag = tags.NODE
344    _gds_objs = (_ELFLAGS, _PLEX, _LAYER, _NODETYPE, _XY, _PROPERTIES)
345    __slots__ = ('layer', 'node_type', 'xy', 'elflags', 'plex', 'properties')
346
347    def __init__(self, layer, node_type, xy):
348        _Base.__init__(self)
349        self.layer = layer
350        self.node_type = node_type
351        self.xy = xy
352
353    def _init_optional(self):
354        self.elflags = None
355        self.plex = None
356        self.properties = None
357
358class Box(_Base):
359    """
360    Class for :const:`BOX` GDSII element.
361
362    GDS syntax:
363        .. productionlist::
364            box: BOX
365               : [ELFLAGS]
366               : [PLEX]
367               : LAYER
368               : BOXTYPE
369               : XY
370               : [`properties`]
371               : ENDEL
372    """
373    _gds_tag = tags.BOX
374    _gds_objs = (_ELFLAGS, _PLEX, _LAYER, _BOXTYPE, _XY, _PROPERTIES)
375    __slots__ = ('layer', 'box_type', 'xy', 'elflags', 'plex', 'properties')
376
377    def __init__(self, layer, box_type, xy):
378        _Base.__init__(self)
379        self.layer = layer
380        self.box_type = box_type
381        self.xy = xy
382
383    def _init_optional(self):
384        self.elflags = None
385        self.plex = None
386        self.properties = None
387
388_all_elements = (Boundary, Path, SRef, ARef, Text, Node, Box)
389
390_Base._tag_to_class_map = (lambda: dict(((cls._gds_tag, cls) for cls in _all_elements)))()
391