engeom 0.2.4__cp38-abi3-win_amd64.whl → 0.2.6__cp38-abi3-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- engeom/_plot/__init__.py +1 -0
- engeom/_plot/common.py +17 -0
- engeom/{matplotlib.py → _plot/matplotlib.py} +88 -45
- engeom/_plot/pyvista.py +256 -0
- engeom/airfoil/__init__.py +4 -0
- engeom/airfoil.pyi +202 -51
- engeom/engeom.pyd +0 -0
- engeom/engeom.pyi +35 -2
- engeom/geom2/__init__.py +5 -0
- engeom/geom2.pyi +456 -80
- engeom/geom3/__init__.py +5 -0
- engeom/geom3.pyi +530 -148
- engeom/metrology/__init__.py +4 -0
- engeom/metrology.pyi +85 -12
- engeom/plot.py +26 -0
- {engeom-0.2.4.dist-info → engeom-0.2.6.dist-info}/METADATA +7 -1
- engeom-0.2.6.dist-info/RECORD +21 -0
- engeom/pyvista.py +0 -178
- engeom-0.2.4.dist-info/RECORD +0 -18
- {engeom-0.2.4.dist-info → engeom-0.2.6.dist-info}/WHEEL +0 -0
engeom/_plot/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
from .common import LabelPlace
|
engeom/_plot/common.py
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
|
3
|
+
class LabelPlace(Enum):
|
4
|
+
"""
|
5
|
+
Represents the different locations where a label can be placed between its anchor points.
|
6
|
+
"""
|
7
|
+
|
8
|
+
Outside = 1
|
9
|
+
""" The label is placed outside the anchor points, on the side of the second point in the measurement. """
|
10
|
+
|
11
|
+
Inside = 2
|
12
|
+
""" The label is placed between the two anchor points. """
|
13
|
+
|
14
|
+
OutsideRev = 3
|
15
|
+
""" The label is placed outside the two anchor points, on the side of the first point in the measurement. """
|
16
|
+
|
17
|
+
|
@@ -1,19 +1,11 @@
|
|
1
1
|
from typing import List, Iterable, Tuple, Union
|
2
|
-
from enum import Enum
|
3
|
-
import matplotlib.lines
|
4
2
|
import numpy
|
5
|
-
from .
|
6
|
-
from .
|
3
|
+
from .common import LabelPlace
|
4
|
+
from engeom.geom2 import Curve2, Circle2, Aabb2, Point2, Vector2, SurfacePoint2
|
5
|
+
from engeom.metrology import Distance2
|
7
6
|
|
8
7
|
PlotCoords = Union[Point2, Vector2, Iterable[float]]
|
9
8
|
|
10
|
-
|
11
|
-
class LabelPlace(Enum):
|
12
|
-
Outside = 1
|
13
|
-
Inside = 2
|
14
|
-
OutsideRev = 3
|
15
|
-
|
16
|
-
|
17
9
|
try:
|
18
10
|
from matplotlib.pyplot import Axes, Circle
|
19
11
|
from matplotlib.colors import ListedColormap
|
@@ -22,6 +14,12 @@ except ImportError:
|
|
22
14
|
else:
|
23
15
|
|
24
16
|
class GomColorMap(ListedColormap):
|
17
|
+
"""
|
18
|
+
A color map similar to the 8 discrete colors in the GOM/Zeiss Inspect software.
|
19
|
+
|
20
|
+
You can use this to instantiate a color map, or you can use the `GOM_CMAP` object directly.
|
21
|
+
"""
|
22
|
+
|
25
23
|
def __init__(self):
|
26
24
|
colors = numpy.array(
|
27
25
|
[
|
@@ -42,7 +40,13 @@ else:
|
|
42
40
|
self.set_under("magenta")
|
43
41
|
self.set_over("darkred")
|
44
42
|
|
43
|
+
|
45
44
|
GOM_CMAP = GomColorMap()
|
45
|
+
"""
|
46
|
+
A color map similar to the 8 discrete colors in the GOM/Zeiss Inspect software, already instantiated and
|
47
|
+
available in the module.
|
48
|
+
"""
|
49
|
+
|
46
50
|
|
47
51
|
def set_aspect_fill(ax: Axes):
|
48
52
|
"""
|
@@ -77,8 +81,30 @@ else:
|
|
77
81
|
x_mid = (x0 + x1) / 2
|
78
82
|
ax.set_xlim(x_mid - x_range / 2, x_mid + x_range / 2)
|
79
83
|
|
80
|
-
|
84
|
+
|
85
|
+
class MatplotlibAxesHelper:
|
86
|
+
"""
|
87
|
+
A helper class for working with Matplotlib. It wraps around a Matplotlib `Axes` object and provides direct
|
88
|
+
methods for plotting some `engeom` entities. It also enforces the aspect ratio to be 1:1 and expands the
|
89
|
+
subplot to fill its available space.
|
90
|
+
|
91
|
+
!!! example
|
92
|
+
```python
|
93
|
+
from matplotlib.pyplot import figure
|
94
|
+
fig = figure()
|
95
|
+
ax = fig.subplots()
|
96
|
+
helper = MatplotlibAxesHelper(ax)
|
97
|
+
```
|
98
|
+
"""
|
99
|
+
|
81
100
|
def __init__(self, ax: Axes, skip_aspect=False, hide_axes=False):
|
101
|
+
"""
|
102
|
+
Initialize the helper with a Matplotlib `Axes` object.
|
103
|
+
:param ax: The Matplotlib `Axes` object to wrap around.
|
104
|
+
:param skip_aspect: Set this to true to skip enforcing the aspect ratio to be 1:1.
|
105
|
+
:param hide_axes: Set this to true to hide the axes.
|
106
|
+
"""
|
107
|
+
|
82
108
|
self.ax = ax
|
83
109
|
if not skip_aspect:
|
84
110
|
ax.set_aspect("equal", adjustable="datalim")
|
@@ -90,7 +116,6 @@ else:
|
|
90
116
|
"""
|
91
117
|
Set the bounds of a Matplotlib Axes object.
|
92
118
|
:param box: an Aabb2 object
|
93
|
-
:return: None
|
94
119
|
"""
|
95
120
|
self.ax.set_xlim(box.min.x, box.max.x)
|
96
121
|
self.ax.set_ylim(box.min.y, box.max.y)
|
@@ -100,7 +125,6 @@ else:
|
|
100
125
|
Plot a circle on a Matplotlib Axes object.
|
101
126
|
:param circle: a Circle2 object
|
102
127
|
:param kwargs: keyword arguments to pass to the plot function
|
103
|
-
:return: None
|
104
128
|
"""
|
105
129
|
from matplotlib.pyplot import Circle
|
106
130
|
|
@@ -117,24 +141,45 @@ else:
|
|
117
141
|
Plot a curve on a Matplotlib Axes object.
|
118
142
|
:param curve: a Curve2 object
|
119
143
|
:param kwargs: keyword arguments to pass to the plot function
|
120
|
-
:return: None
|
121
144
|
"""
|
122
145
|
self.ax.plot(curve.points[:, 0], curve.points[:, 1], **kwargs)
|
123
146
|
|
124
|
-
def
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
147
|
+
def distance(
|
148
|
+
self,
|
149
|
+
distance: Distance2,
|
150
|
+
side_shift: float = 0,
|
151
|
+
template: str = "{value:.3f}",
|
152
|
+
fontsize: int = 10,
|
153
|
+
label_place: LabelPlace = LabelPlace.Outside,
|
154
|
+
label_offset: float | None = None,
|
155
|
+
fontname: str | None = None,
|
156
|
+
scale_value: float = 1.0,
|
133
157
|
):
|
158
|
+
"""
|
159
|
+
Plot a `Distance2` object on a Matplotlib Axes, drawing the leader lines and adding a text label with the
|
160
|
+
distance value.
|
161
|
+
:param distance: The `Distance2` object to plot.
|
162
|
+
:param side_shift: Shift the ends of the leader lines by this amount of data units. The direction of the
|
163
|
+
shift is orthogonal to the distance direction, with positive values shifting to the right.
|
164
|
+
:param template: The format string to use for the distance label. The default is "{value:.3f}".
|
165
|
+
:param fontsize: The font size to use for the label.
|
166
|
+
:param label_place: The placement of the label.
|
167
|
+
:param label_offset: The distance offset to use for the label. Will have different meanings depending on
|
168
|
+
the `label_place` parameter.
|
169
|
+
:param fontname: The name of the font to use for the label.
|
170
|
+
:param scale_value: A scaling factor to apply to the value before displaying it in the label. Use this to
|
171
|
+
convert between different units of measurement without having to modify the actual value or the coordinate
|
172
|
+
system.
|
173
|
+
"""
|
134
174
|
pad_scale = self._font_height(12) * 1.5
|
135
|
-
|
136
|
-
|
137
|
-
|
175
|
+
|
176
|
+
# The offset_dir is the direction from `a` to `b` projected so that it's parallel to the measurement
|
177
|
+
# direction.
|
178
|
+
offset_dir = distance.direction if distance.value >= 0 else -distance.direction
|
179
|
+
center = SurfacePoint2(*distance.center.point, *offset_dir)
|
180
|
+
center = center.shift_orthogonal(side_shift)
|
181
|
+
leader_a = center.projection(distance.a)
|
182
|
+
leader_b = center.projection(distance.b)
|
138
183
|
|
139
184
|
if label_place == LabelPlace.Inside:
|
140
185
|
label_offset = label_offset or 0.0
|
@@ -143,29 +188,26 @@ else:
|
|
143
188
|
self.arrow(label_coords, leader_b)
|
144
189
|
elif label_place == LabelPlace.Outside:
|
145
190
|
label_offset = label_offset or pad_scale * 3
|
146
|
-
label_coords = leader_b +
|
147
|
-
self.arrow(leader_a -
|
191
|
+
label_coords = leader_b + offset_dir * label_offset
|
192
|
+
self.arrow(leader_a - offset_dir * pad_scale, leader_a)
|
148
193
|
self.arrow(label_coords, leader_b)
|
149
194
|
elif label_place == LabelPlace.OutsideRev:
|
150
195
|
label_offset = label_offset or pad_scale * 3
|
151
|
-
label_coords = leader_a -
|
152
|
-
self.arrow(leader_b +
|
196
|
+
label_coords = leader_a - offset_dir * label_offset
|
197
|
+
self.arrow(leader_b + offset_dir * pad_scale, leader_b)
|
153
198
|
self.arrow(label_coords, leader_a)
|
154
199
|
|
155
200
|
# Do we need sideways leaders?
|
156
|
-
self._line_if_needed(pad_scale,
|
157
|
-
self._line_if_needed(pad_scale,
|
201
|
+
self._line_if_needed(pad_scale, distance.a, leader_a)
|
202
|
+
self._line_if_needed(pad_scale, distance.b, leader_b)
|
158
203
|
|
159
204
|
kwargs = {"ha": "center", "va": "center", "fontsize": fontsize}
|
160
205
|
if fontname is not None:
|
161
206
|
kwargs["fontname"] = fontname
|
162
207
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
bbox=dict(boxstyle="round,pad=0.3", ec="black", fc="white"),
|
167
|
-
**kwargs,
|
168
|
-
)
|
208
|
+
value = distance.value * scale_value
|
209
|
+
box_style = dict(boxstyle="round,pad=0.3", ec="black", fc="white")
|
210
|
+
self.annotate_text_only(template.format(value=value), label_coords, bbox=box_style, **kwargs)
|
169
211
|
|
170
212
|
def _line_if_needed(self, pad: float, actual: Point2, leader_end: Point2):
|
171
213
|
half_pad = pad * 0.5
|
@@ -182,7 +224,7 @@ else:
|
|
182
224
|
:param text: the text to annotate
|
183
225
|
:param pos: the position of the annotation
|
184
226
|
:param kwargs: keyword arguments to pass to the annotate function
|
185
|
-
:return:
|
227
|
+
:return: the annotation object
|
186
228
|
"""
|
187
229
|
return self.ax.annotate(text, xy=_tuplefy(pos), **kwargs)
|
188
230
|
|
@@ -191,10 +233,10 @@ else:
|
|
191
233
|
Plot an arrow on a Matplotlib Axes object.
|
192
234
|
:param start: the start point of the arrow
|
193
235
|
:param end: the end point of the arrow
|
194
|
-
:param
|
195
|
-
:return:
|
236
|
+
:param arrow: the style of arrow to use
|
237
|
+
:return: the annotation object
|
196
238
|
"""
|
197
|
-
self.ax.annotate(
|
239
|
+
return self.ax.annotate(
|
198
240
|
"",
|
199
241
|
xy=_tuplefy(end),
|
200
242
|
xytext=_tuplefy(start),
|
@@ -202,7 +244,7 @@ else:
|
|
202
244
|
)
|
203
245
|
|
204
246
|
def _font_height(self, font_size: int) -> float:
|
205
|
-
|
247
|
+
# Get the height of a font in data units
|
206
248
|
fig_dpi = self.ax.figure.dpi
|
207
249
|
font_height_inches = font_size * 1.0 / 72.0
|
208
250
|
font_height_px = font_height_inches * fig_dpi
|
@@ -211,7 +253,7 @@ else:
|
|
211
253
|
return font_height_px / px_per_data
|
212
254
|
|
213
255
|
def _get_scale(self) -> float:
|
214
|
-
|
256
|
+
# Get the scale of the plot in data units per pixel.
|
215
257
|
x0, x1 = self.ax.get_xlim()
|
216
258
|
y0, y1 = self.ax.get_ylim()
|
217
259
|
|
@@ -224,6 +266,7 @@ else:
|
|
224
266
|
|
225
267
|
return min(x_scale, y_scale)
|
226
268
|
|
269
|
+
|
227
270
|
def _tuplefy(item: PlotCoords) -> Tuple[float, float]:
|
228
271
|
if isinstance(item, (Point2, Vector2)):
|
229
272
|
return item.x, item.y
|
engeom/_plot/pyvista.py
ADDED
@@ -0,0 +1,256 @@
|
|
1
|
+
"""
|
2
|
+
This module contains helper functions for working with PyVista.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from __future__ import annotations
|
6
|
+
|
7
|
+
from typing import List, Any, Dict, Union, Iterable, Tuple
|
8
|
+
|
9
|
+
import numpy
|
10
|
+
|
11
|
+
from engeom.geom3 import Mesh, Curve3, Vector3, Point3, Iso3, SurfacePoint3
|
12
|
+
from engeom.metrology import Distance3
|
13
|
+
from .common import LabelPlace
|
14
|
+
|
15
|
+
PlotCoords = Union[Point3, Vector3, Iterable[float]]
|
16
|
+
|
17
|
+
try:
|
18
|
+
import pyvista
|
19
|
+
except ImportError:
|
20
|
+
pass
|
21
|
+
else:
|
22
|
+
class PyvistaPlotterHelper:
|
23
|
+
"""
|
24
|
+
A helper class for working with PyVista. It wraps around a PyVista `Plotter` object and provides direct methods
|
25
|
+
for plotting some `engeom` entities.
|
26
|
+
|
27
|
+
!!! example
|
28
|
+
```python
|
29
|
+
import pyvista
|
30
|
+
plotter = pyvista.Plotter()
|
31
|
+
helper = PyvistaPlotterHelper(plotter)
|
32
|
+
```
|
33
|
+
"""
|
34
|
+
|
35
|
+
def __init__(self, plotter: pyvista.Plotter):
|
36
|
+
"""
|
37
|
+
Initialize the helper with a PyVista `Plotter` object.
|
38
|
+
|
39
|
+
:param plotter: The PyVista `Plotter` object to wrap around.
|
40
|
+
"""
|
41
|
+
self.plotter = plotter
|
42
|
+
|
43
|
+
def add_curves(
|
44
|
+
self,
|
45
|
+
*curves: Curve3,
|
46
|
+
color: pyvista.ColorLike = "w",
|
47
|
+
width: float = 3.0,
|
48
|
+
label: str | None = None,
|
49
|
+
name: str | None = None,
|
50
|
+
) -> pyvista.vtkActor:
|
51
|
+
"""
|
52
|
+
Add one or more curves to be plotted.
|
53
|
+
:param curves: The curves to add.
|
54
|
+
:param color: The color to use for the curve(s).
|
55
|
+
:param width: The line width to use for the curve(s).
|
56
|
+
:param label: The label to use for the curve(s) if a legend is present.
|
57
|
+
:param name: The name to use for the actor in the scene.
|
58
|
+
:return: The PyVista actor that was added to the plotter.
|
59
|
+
"""
|
60
|
+
curve_vertices = []
|
61
|
+
for curve in curves:
|
62
|
+
b = curve.points[1:-1]
|
63
|
+
c = numpy.zeros((len(curve.points) + len(b), 3), dtype=curve.points.dtype)
|
64
|
+
c[0::2, :] = curve.points[0:-1]
|
65
|
+
c[1:-1:2, :] = b
|
66
|
+
c[-1] = curve.points[-1]
|
67
|
+
curve_vertices.append(c)
|
68
|
+
|
69
|
+
vertices = numpy.concatenate(curve_vertices, axis=0)
|
70
|
+
return self.plotter.add_lines(
|
71
|
+
vertices,
|
72
|
+
color=color,
|
73
|
+
width=width,
|
74
|
+
label=label,
|
75
|
+
name=name,
|
76
|
+
)
|
77
|
+
|
78
|
+
def add_mesh(self, mesh: Mesh, **kwargs) -> pyvista.vtkActor:
|
79
|
+
"""
|
80
|
+
Add an `engeom` mesh to be plotted. Additional keyword arguments will be passed directly to the PyVista
|
81
|
+
`Plotter.add_mesh` method, allowing for customization of the mesh appearance.
|
82
|
+
|
83
|
+
:param mesh: The mesh object to add to the plotter scene
|
84
|
+
:return: The PyVista actor that was added to the plotter.
|
85
|
+
"""
|
86
|
+
if "cmap" in kwargs:
|
87
|
+
cmap_extremes = _cmap_extremes(kwargs["cmap"])
|
88
|
+
kwargs.update(cmap_extremes)
|
89
|
+
|
90
|
+
prefix = numpy.ones((mesh.faces.shape[0], 1), dtype=mesh.faces.dtype)
|
91
|
+
faces = numpy.hstack((prefix * 3, mesh.faces))
|
92
|
+
data = pyvista.PolyData(mesh.vertices, faces)
|
93
|
+
return self.plotter.add_mesh(data, **kwargs)
|
94
|
+
|
95
|
+
def distance(
|
96
|
+
self,
|
97
|
+
distance: Distance3,
|
98
|
+
template: str = "{value:.3f}",
|
99
|
+
label_place: LabelPlace = LabelPlace.Outside,
|
100
|
+
label_offset: float | None = None,
|
101
|
+
font_size: int = 12,
|
102
|
+
scale_value: float = 1.0,
|
103
|
+
font_family=None,
|
104
|
+
):
|
105
|
+
"""
|
106
|
+
Add a distance entity to the plotter.
|
107
|
+
:param distance: The distance entity to add.
|
108
|
+
:param template: A format string to use for the label. The `value` key will be replaced with the actual
|
109
|
+
value read from the measurement.
|
110
|
+
:param label_place: The placement of the label relative to the distance entity's anchor points.
|
111
|
+
:param label_offset: The distance offset to use for the label. Will have different meanings depending on
|
112
|
+
the `label_place` parameter.
|
113
|
+
:param font_size: The size of the text to use for the label.
|
114
|
+
:param scale_value: A scaling factor to apply to the value before displaying it in the label. Use this to
|
115
|
+
convert between different units of measurement without having to modify the actual value or the coordinate
|
116
|
+
system.
|
117
|
+
:param font_family: The font family to use for the label.
|
118
|
+
"""
|
119
|
+
label_offset = label_offset or max(abs(distance.value), 1.0) * 3
|
120
|
+
|
121
|
+
# The offset_dir is the direction from `a` to `b` projected so that it's parallel to the measurement
|
122
|
+
# direction.
|
123
|
+
offset_dir = distance.direction if distance.value >= 0 else -distance.direction
|
124
|
+
|
125
|
+
# Rather than arrows, we'll use spheres to indicate the anchor points at the end of the leader lines
|
126
|
+
spheres = [distance.a, distance.b]
|
127
|
+
builder = LineBuilder()
|
128
|
+
|
129
|
+
if label_place == LabelPlace.Inside:
|
130
|
+
c = SurfacePoint3(*distance.center.point, *offset_dir)
|
131
|
+
label_coords = c.at_distance(label_offset)
|
132
|
+
|
133
|
+
builder.add(distance.a)
|
134
|
+
builder.add(distance.a - offset_dir * label_offset * 0.25)
|
135
|
+
builder.skip()
|
136
|
+
|
137
|
+
builder.add(distance.b)
|
138
|
+
builder.add(distance.b + offset_dir * label_offset * 0.25)
|
139
|
+
|
140
|
+
elif label_place == LabelPlace.Outside:
|
141
|
+
label_coords = distance.b + offset_dir * label_offset
|
142
|
+
|
143
|
+
builder.add(distance.a)
|
144
|
+
builder.add(distance.a - offset_dir * label_offset * 0.25)
|
145
|
+
builder.skip()
|
146
|
+
|
147
|
+
builder.add(distance.b)
|
148
|
+
builder.add(label_coords)
|
149
|
+
|
150
|
+
elif label_place == LabelPlace.OutsideRev:
|
151
|
+
label_coords = distance.a - offset_dir * label_offset
|
152
|
+
|
153
|
+
builder.add(distance.b)
|
154
|
+
builder.add(distance.b + offset_dir * label_offset * 0.25)
|
155
|
+
builder.skip()
|
156
|
+
|
157
|
+
builder.add(distance.a)
|
158
|
+
builder.add(label_coords)
|
159
|
+
|
160
|
+
points = numpy.array([_tuplefy(p) for p in spheres], dtype=numpy.float64)
|
161
|
+
self.plotter.add_points(points, color="black", point_size=4, render_points_as_spheres=True)
|
162
|
+
|
163
|
+
lines = builder.build()
|
164
|
+
self.plotter.add_lines(lines, color="black", width=1.5)
|
165
|
+
|
166
|
+
value = distance.value * scale_value
|
167
|
+
self.plotter.add_point_labels(
|
168
|
+
[_tuplefy(label_coords)],
|
169
|
+
[template.format(value=value)],
|
170
|
+
show_points=False,
|
171
|
+
background_color="white",
|
172
|
+
font_family=font_family,
|
173
|
+
# justification_vertical="center",
|
174
|
+
# justification_horizontal="center",
|
175
|
+
font_size=font_size,
|
176
|
+
bold=False,
|
177
|
+
)
|
178
|
+
|
179
|
+
def coordinate_frame(self, iso: Iso3, size: float = 1.0):
|
180
|
+
"""
|
181
|
+
Add a coordinate frame to the plotter. This will appear as three lines, with X in red, Y in green,
|
182
|
+
and Z in blue. The length of each line is determined by the `size` parameter.
|
183
|
+
:param iso: The isometry to use as the origin and orientation of the coordinate frame.
|
184
|
+
:param size: The length of each line in the coordinate frame.
|
185
|
+
"""
|
186
|
+
points = numpy.array([[0, 0, 0], [size, 0, 0], [0, size, 0], [0, 0, size]], dtype=numpy.float64)
|
187
|
+
points = iso.transform_points(points)
|
188
|
+
|
189
|
+
self.plotter.add_lines(points[[0, 1]], color="red", width=5.0)
|
190
|
+
self.plotter.add_lines(points[[0, 2]], color="green", width=5.0)
|
191
|
+
self.plotter.add_lines(points[[0, 3]], color="blue", width=5.0)
|
192
|
+
|
193
|
+
def label(self, point: PlotCoords, text: str, **kwargs):
|
194
|
+
"""
|
195
|
+
Add a text label to the plotter.
|
196
|
+
:param point: The position of the label in 3D space.
|
197
|
+
:param text: The text to display in the label.
|
198
|
+
:param kwargs: Additional keyword arguments to pass to the `pyvista.Label` constructor.
|
199
|
+
"""
|
200
|
+
label = pyvista.Label(text=text, position=_tuplefy(point), **kwargs)
|
201
|
+
self.plotter.add_actor(label)
|
202
|
+
|
203
|
+
def arrow(self, start: PlotCoords, direction: PlotCoords,
|
204
|
+
tip_length: float = 0.25,
|
205
|
+
tip_radius: float = 0.1,
|
206
|
+
shaft_radius: float = 0.05,
|
207
|
+
**kwargs):
|
208
|
+
pd = pyvista.Arrow(_tuplefy(start), _tuplefy(direction), tip_length=tip_length, tip_radius=tip_radius,
|
209
|
+
shaft_radius=shaft_radius)
|
210
|
+
self.plotter.add_mesh(pd, **kwargs, color="black")
|
211
|
+
|
212
|
+
|
213
|
+
def _cmap_extremes(item: Any) -> Dict[str, pyvista.ColorLike]:
|
214
|
+
working = {}
|
215
|
+
try:
|
216
|
+
from matplotlib.colors import Colormap
|
217
|
+
except ImportError:
|
218
|
+
return working
|
219
|
+
else:
|
220
|
+
if isinstance(item, Colormap):
|
221
|
+
over = getattr(item, "_rgba_over", None)
|
222
|
+
under = getattr(item, "_rgba_under", None)
|
223
|
+
if over is not None:
|
224
|
+
working["above_color"] = over
|
225
|
+
if under is not None:
|
226
|
+
working["below_color"] = under
|
227
|
+
return working
|
228
|
+
|
229
|
+
|
230
|
+
class LineBuilder:
|
231
|
+
def __init__(self):
|
232
|
+
self.vertices = []
|
233
|
+
self._skip = 1
|
234
|
+
|
235
|
+
def add(self, points: PlotCoords):
|
236
|
+
if self.vertices:
|
237
|
+
if self._skip > 0:
|
238
|
+
self._skip -= 1
|
239
|
+
else:
|
240
|
+
self.vertices.append(self.vertices[-1])
|
241
|
+
|
242
|
+
self.vertices.append(_tuplefy(points))
|
243
|
+
|
244
|
+
def skip(self):
|
245
|
+
self._skip = 2
|
246
|
+
|
247
|
+
def build(self) -> numpy.ndarray:
|
248
|
+
return numpy.array(self.vertices, dtype=numpy.float64)
|
249
|
+
|
250
|
+
|
251
|
+
def _tuplefy(item: PlotCoords) -> Tuple[float, float, float]:
|
252
|
+
if isinstance(item, (Point3, Vector3)):
|
253
|
+
return item.x, item.y, item.z
|
254
|
+
else:
|
255
|
+
x, y, z, *_ = item
|
256
|
+
return x, y, z
|