|
5 | 5 | import sys |
6 | 6 | import warnings |
7 | 7 | import numpy as np |
8 | | -import matplotlib.pyplot as plt |
9 | | -from matplotlib.patches import FancyArrowPatch |
10 | | -from matplotlib.path import Path |
11 | | -from matplotlib.transforms import Affine2D, ScaledTranslation |
12 | | -from mpl_toolkits.mplot3d import proj3d |
13 | 8 | import copy |
14 | 9 | import heapq |
15 | 10 | import importlib |
|
20 | 15 | from typing import IO, Any, Callable, ClassVar, overload |
21 | 16 | from numpy.typing import ArrayLike, NDArray |
22 | 17 |
|
23 | | -from spatialmath.base.graphics import axes_logic |
| 18 | +# matplotlib and spatialmath are imported by the methods that plot, and by |
| 19 | +# pgraph._plot, so that importing pgraph does not load them. |
24 | 20 |
|
25 | 21 |
|
26 | 22 | def _L1(v: NDArray) -> float: |
@@ -159,58 +155,6 @@ def _renamed( |
159 | 155 | return old |
160 | 156 |
|
161 | 157 |
|
162 | | -def _loop_path(angle: float, radius: float, markersize: float) -> Path: |
163 | | - """ |
164 | | - The path of a loop at a vertex, in points from the vertex (private function) |
165 | | -
|
166 | | - A circle of ``radius`` whose centre is that far from the vertex, in the |
167 | | - direction ``angle`` degrees, less the part inside the vertex's marker. It |
168 | | - is counter-clockwise, so a directed loop has its head where it returns. |
169 | | - """ |
170 | | - m = markersize / 2 |
171 | | - phi = np.degrees(np.arccos(np.clip(1 - m * m / (2 * radius * radius), -1, 1))) |
172 | | - arc = Path.arc(angle + 180 + phi, angle + 540 - phi) |
173 | | - centre = radius * np.array([np.cos(np.radians(angle)), np.sin(np.radians(angle))]) |
174 | | - return Path(np.asarray(arc.vertices) * radius + centre, arc.codes) |
175 | | - |
176 | | - |
177 | | -class _Arrow3D(FancyArrowPatch): |
178 | | - """An edge between two points of a 3D plot, which follows a rotation""" |
179 | | - |
180 | | - def __init__(self, p1: ArrayLike, p2: ArrayLike, **kwargs: Any): |
181 | | - super().__init__((0, 0), (0, 0), **kwargs) |
182 | | - self._p1, self._p2 = np.asarray(p1, float), np.asarray(p2, float) |
183 | | - |
184 | | - def do_3d_projection(self, renderer: Any = None) -> float: |
185 | | - ax: Any = self.axes |
186 | | - xs, ys, zs = proj3d.proj_transform( |
187 | | - [self._p1[0], self._p2[0]], |
188 | | - [self._p1[1], self._p2[1]], |
189 | | - [self._p1[2], self._p2[2]], |
190 | | - ax.M, |
191 | | - ) |
192 | | - self.set_positions((xs[0], ys[0]), (xs[1], ys[1])) |
193 | | - return float(np.min(zs)) |
194 | | - |
195 | | - |
196 | | -class _Loop3D(FancyArrowPatch): |
197 | | - """A loop at a vertex of a 3D plot, which follows a rotation""" |
198 | | - |
199 | | - def __init__(self, anchor: ArrayLike, path: Path, **kwargs: Any): |
200 | | - super().__init__(path=path, **kwargs) |
201 | | - self._anchor = np.asarray(anchor, float) |
202 | | - |
203 | | - def do_3d_projection(self, renderer: Any = None) -> float: |
204 | | - ax: Any = self.axes |
205 | | - x, y, z = proj3d.proj_transform(*self._anchor, ax.M) |
206 | | - self.set_transform( |
207 | | - Affine2D().scale(1 / 72) |
208 | | - + ax.figure.dpi_scale_trans |
209 | | - + ScaledTranslation(x, y, ax.transData) |
210 | | - ) |
211 | | - return float(z) |
212 | | - |
213 | | - |
214 | 158 | class Graph: |
215 | 159 | """ |
216 | 160 | Base class for all graphs |
@@ -2423,6 +2367,9 @@ def _edge_artist( |
2423 | 2367 | :param kwargs: options of the patch: color, linewidth, arrowstyle, ... |
2424 | 2368 | :return: the artist, to be added to ``ax`` |
2425 | 2369 | """ |
| 2370 | + from matplotlib.patches import FancyArrowPatch |
| 2371 | + from matplotlib.transforms import Affine2D, ScaledTranslation |
| 2372 | + from ._plot import _Arrow3D, _Loop3D, _loop_path |
2426 | 2373 | v1, v2 = edge.v1, edge.v2 |
2427 | 2374 | assert v1 is not None and v2 is not None |
2428 | 2375 | shrink = markersize / 2 + 1 |
@@ -2580,6 +2527,9 @@ def plot( |
2580 | 2527 |
|
2581 | 2528 | :seealso: :meth:`highlight_path` :meth:`highlight_edge` |
2582 | 2529 | """ |
| 2530 | + import matplotlib.pyplot as plt |
| 2531 | + from matplotlib.patches import FancyArrowPatch |
| 2532 | + from spatialmath.base.graphics import axes_logic |
2583 | 2533 | # the options that have been renamed |
2584 | 2534 | vertex_opt = _renamed("plot", "vopt", vopt, "vertex_opt", vertex_opt) |
2585 | 2535 | edge_opt = _renamed("plot", "eopt", eopt, "edge_opt", edge_opt) |
@@ -2753,6 +2703,7 @@ def highlight_path( |
2753 | 2703 |
|
2754 | 2704 | :seealso: :meth:`highlight_vertex` :meth:`highlight_edge` |
2755 | 2705 | """ |
| 2706 | + import matplotlib.pyplot as plt |
2756 | 2707 | for i in range(len(path)): |
2757 | 2708 | if i < len(path) - 1: |
2758 | 2709 | e = path[i].edgeto(path[i + 1]) |
@@ -2811,6 +2762,7 @@ def highlight_edge( |
2811 | 2762 |
|
2812 | 2763 | :seealso: :meth:`plot` :meth:`highlight_vertex` :meth:`highlight_path` |
2813 | 2764 | """ |
| 2765 | + import matplotlib.pyplot as plt |
2814 | 2766 | if edge.v1 is None or edge.v2 is None: |
2815 | 2767 | raise ValueError("edge does not connect two vertices") |
2816 | 2768 | # over the edge as plot() draws it, so that it follows an arc |
@@ -2879,6 +2831,7 @@ def highlight_vertex( |
2879 | 2831 | >>> g.highlight_vertex(g[2]) |
2880 | 2832 |
|
2881 | 2833 | """ |
| 2834 | + import matplotlib.pyplot as plt |
2882 | 2835 | if isinstance(vertex, Iterable): |
2883 | 2836 | for n in vertex: |
2884 | 2837 | if isinstance(n, str): |
|
0 commit comments