1#  Copyright (c) 2020, Manfred Moitzi
2#  License: MIT License
3from typing import Sequence, Optional
4import abc
5from ezdxf.math import Vec3
6from .backend import Backend
7from .properties import Properties
8
9
10class AbstractLineRenderer:
11    """ The line rendering class should get all options from the backend, so a
12    change in the backend is also applied by the line renderer e.g. disable
13    lineweight or linetype rendering.
14    """
15
16    def __init__(self, backend: Backend):
17        self._pattern_cache = dict()
18        self._backend = backend
19
20    @abc.abstractmethod
21    def draw_line(self, start: Vec3, end: Vec3,
22                  properties: Properties, z: float):
23        ...
24
25    @abc.abstractmethod
26    def draw_path(self, path, properties: Properties, z: float):
27        ...
28
29    @property
30    def linetype_scaling(self) -> float:
31        return self._backend.linetype_scaling
32
33    @property
34    def lineweight_scaling(self) -> float:
35        return self._backend.lineweight_scaling
36
37    @property
38    def min_lineweight(self) -> float:
39        return self._backend.min_lineweight
40
41    @property
42    def min_dash_length(self) -> float:
43        return self._backend.min_dash_length
44
45    @property
46    def max_flattening_distance(self) -> float:
47        return self._backend.max_flattening_distance
48
49    @property
50    def measurement(self) -> float:
51        return self._backend.measurement
52
53    @property
54    def measurement_scale(self) -> float:
55        """ Returns internal linetype scaling factor. """
56        return 1.0
57
58    def pattern(self, properties: Properties) -> Sequence[float]:
59        """ Get pattern - implements pattern caching. """
60        scale = (self.measurement_scale * self.linetype_scaling *
61                 properties.linetype_scale)
62        key = (properties.linetype_name, scale)
63        pattern_ = self._pattern_cache.get(key)
64        if pattern_ is None:
65            pattern_ = self.create_pattern(properties, scale)
66            self._pattern_cache[key] = pattern_
67        return pattern_
68
69    def create_pattern(self, properties: Properties,
70                       scale: float) -> Sequence[float]:
71        """ Returns simplified linetype tuple: on_off_sequence """
72        # only matplotlib needs a different pattern definition
73        if len(properties.linetype_pattern) < 2:
74            # Do not return None -> None indicates: "not cached"
75            return tuple()
76        else:
77            min_dash_length = self.min_dash_length
78            pattern = [max(e * scale, min_dash_length) for e in
79                       properties.linetype_pattern]
80            if len(pattern) % 2:
81                pattern.pop()
82            return pattern
83
84    def lineweight(self, properties: Properties) -> float:
85        """ Set lineweight_scaling=0 to use a constant minimal lineweight. """
86        return max(
87            properties.lineweight * self.lineweight_scaling,
88            self.min_lineweight
89        )
90
91    def linetype(self, properties: Properties) -> Optional[Sequence[float]]:
92        """ Set linetype_scaling=0 to disable linetype rendering.
93
94        Returns ``None`` to disable linetype rendering.
95
96        """
97        if self.linetype_scaling:
98            return self.pattern(properties)
99        else:
100            return None
101