shape.py
1 # SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-or-later OR CERN-OHL-S-2.0+ OR Apache-2.0 2 from typing import Optional, Callable 3 4 from ..technology import geometry as _geo 5 6 7 class ShapeDispatcher: 8 """Dispatch to class method based on type of `_Shape` subclasses. 9 10 This dispatcher follows the common way of working in the `dispatch` module. 11 Exception is the "geometry.MultiPartShape._Part", for this one can overload 12 the `MultiPartShape__Part` method. The default implementation with call the 13 dispatcher on the `part_shape` of the given object. Assume that dispatcher is 14 called with a `MultiPartShape._Part` with object `part` with `part.part_shape` 15 of type `Rect`. Then the default implement will call the `Rect` method with 16 `part.part_shape` as shape. 17 """ 18 def __init__(self): 19 self._parent_call: Optional[Callable] = None 20 21 def __call__(self, shape: _geo._Shape, *args, **kwargs): 22 # Reset _parent_call 23 self._parent_call = None 24 25 if isinstance(shape, _geo.MultiPartShape._Part): 26 classname = "MultiPartShape__Part" 27 else: 28 classname = shape.__class__.__name__.split(".")[-1] 29 return getattr(self, classname, self._pd_unhandled)(shape, *args, **kwargs) 30 31 def _pd_unhandled(self, shape: _geo._Shape, *args, **kwargs): 32 raise RuntimeError( 33 "Internal error: unhandled dispatcher for object of type " 34 f"{shape.__class__.__name__}" 35 ) 36 37 def _Shape(self, shape: _geo._Shape, *args, **kwargs): 38 """This for the base class and by default raises NotImplementedError 39 """ 40 raise NotImplementedError( 41 f"No dispatcher implemented for object of type {shape.__class__.__name__}" 42 ) 43 44 def _Rectangular(self, shape: _geo._Rectangular, *args, **kwargs): 45 return self._Shape(shape, *args, **kwargs) 46 47 def _PointsShape(self, 48 shape: _geo._PointsShape, *args, **kwargs, 49 ): 50 if self._parent_call is None: 51 call = self._Shape 52 else: 53 call = self._parent_call 54 self._parent_call = None 55 return call(shape, *args, **kwargs) 56 57 def Point(self, point: _geo.Point, *args, **kwargs): 58 self._parent_call = self._Rectangular 59 return self._PointsShape(point, *args, **kwargs) 60 61 def Line(self, line: _geo.Line, *args, **kwargs): 62 self._parent_call = self._Rectangular 63 return self._PointsShape(line, *args, **kwargs) 64 65 def Polygon(self, 66 polygon: _geo.Polygon, *args, **kwargs, 67 ): 68 # _parent_call is kept and used in self._PointsShape 69 return self._PointsShape(polygon, *args, **kwargs) 70 71 def Rect(self, rect: _geo.Rect, *args, **kwargs): 72 self._parent_call = self._Rectangular 73 return self.Polygon(rect, *args, **kwargs) 74 75 def MultiPath(self, mp: _geo.MultiPath, *args, **kwargs): 76 return self.Polygon(mp, *args, **kwargs) 77 78 def Ring(self, ring: _geo.Ring, *args, **kwargs): 79 return self.MultiPath(ring, *args, **kwargs) 80 81 def RectRing(self, ring: _geo.RectRing, *args, **kwargs): 82 return self._Shape(ring, *args, **kwargs) 83 84 def Label(self, label: _geo.Label, *args, **kwargs): 85 return self._Shape(label, *args, **kwargs) 86 87 def MultiPartShape(self, mps: _geo.MultiPartShape, *args, **kwargs): 88 return self.Polygon(mps, *args, **kwargs) 89 90 def MultiPartShape__Part(self, part: _geo.MultiPartShape._Part, *args, **kwargs): 91 return self(part._partshape, *args, **kwargs) 92 93 def MultiShape(self, ms: _geo.MultiShape, *args, **kwargs): 94 return self._Shape(ms, *args, **kwargs) 95 96 def RepeatedShape(self, rs: _geo.RepeatedShape, *args, **kwargs): 97 return self._Shape(rs, *args, **kwargs) 98 99 def ArrayShape(self, array: _geo.ArrayShape, *args, **kwargs): 100 return self.RepeatedShape(array, *args, **kwargs)