From d4c0fa766edcf6bde69e779ce0288f3c97db6a9a Mon Sep 17 00:00:00 2001 From: Sam Holt Date: Mon, 28 Aug 2023 18:35:16 +0200 Subject: [PATCH 01/64] Region --- discretisedfield/plotting/pyvista_region.py | 88 +++++++++++++++++++++ discretisedfield/region.py | 5 ++ 2 files changed, 93 insertions(+) create mode 100644 discretisedfield/plotting/pyvista_region.py diff --git a/discretisedfield/plotting/pyvista_region.py b/discretisedfield/plotting/pyvista_region.py new file mode 100644 index 000000000..9e7bd6616 --- /dev/null +++ b/discretisedfield/plotting/pyvista_region.py @@ -0,0 +1,88 @@ +import pyvista as pv +import ubermagutil.units as uu + +import discretisedfield.plotting.util as plot_util + + +class PyVistaRegion: + def __init__(self, region): + if region.ndim != 3: + raise RuntimeError("Only 3d regions can be plotted.") + self.region = region + + def __call__( + self, *, plot=None, color=plot_util.cp_hex[0], multiplier=None, **kwargs + ): + """``pyvista`` plot. + + If ``plot`` is not passed, a new `pyvista` plotter object is created + automatically. The colour of the region can be specified using + ``color`` argument. + + For details about ``multiplier``, please refer to + ``discretisedfield.Region.mpl``. + + Parameters + ---------- + plot : pyvista.Plotter, optional + + Plot to which the plot is added. Defaults to ``None`` - plot is + created internally. + + color : tuple, optional + + Colour of the region. Defaults to the default color palette. + + multiplier : numbers.Real, optional + + Axes multiplier. Defaults to ``None``. + + Examples + -------- + 1. Visualising the region using ``pyvista``. + + >>> import discretisedfield as df + ... + >>> p1 = (-50e-9, -50e-9, 0) + >>> p2 = (50e-9, 50e-9, 10e-9) + >>> region = df.Region(p1=p1, p2=p2) + >>> region.pyvista() + + """ + if self.region.ndim != 3: + raise RuntimeError("Only 3-dimensional regions can be plotted.") + + if plot is None: + plotter = pv.Plotter() + else: + plotter = plot + + multiplier = self._setup_multiplier(multiplier) + + rescaled_region = self.region.scale(1 / multiplier, reference_point=(0, 0, 0)) + + bounds = tuple( + val + for pair in zip(rescaled_region.pmin, rescaled_region.pmax) + for val in pair + ) + + # Create a box (cube) mesh using pyvista + box = pv.Box(bounds) + + # Add the box to the plotter + plotter.add_mesh(box, color=color, **kwargs) + # plot.show_bounds(axes_ranges=bounds) + label = self._axis_labels(multiplier) + plotter.show_grid(xtitle=label[0], ytitle=label[1], ztitle=label[2]) + if plot is None: + plotter.show() + + def _setup_multiplier(self, multiplier): + return self.region.multiplier if multiplier is None else multiplier + + def _axis_labels(self, multiplier): + return [ + rf"{dim} ({uu.rsi_prefixes[multiplier]}{unit})" + for dim, unit in zip(self.region.dims, self.region.units) + ] diff --git a/discretisedfield/region.py b/discretisedfield/region.py index 05a2a2ab6..d7ddf27ad 100644 --- a/discretisedfield/region.py +++ b/discretisedfield/region.py @@ -1196,6 +1196,11 @@ def k3d(self): """ return dfp.K3dRegion(self) + @property + def pyvista(self): + r"""``pyvista`` plot.""" + return dfp.PyVistaRegion(self) + def to_dict(self): """Convert region object to dict. From aa887cd7c50481e0e364327ff4ba852fa2c1f7f0 Mon Sep 17 00:00:00 2001 From: Sam Holt Date: Mon, 28 Aug 2023 18:50:07 +0200 Subject: [PATCH 02/64] Mesh --- dev/pyvista.ipynb | 271 ++++++++++++++++++++++ discretisedfield/mesh.py | 5 + discretisedfield/plotting/__init__.py | 2 + discretisedfield/plotting/pyvista_mesh.py | 96 ++++++++ 4 files changed, 374 insertions(+) create mode 100644 dev/pyvista.ipynb create mode 100644 discretisedfield/plotting/pyvista_mesh.py diff --git a/dev/pyvista.ipynb b/dev/pyvista.ipynb new file mode 100644 index 000000000..9e18e8880 --- /dev/null +++ b/dev/pyvista.ipynb @@ -0,0 +1,271 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import discretisedfield as df # df is here chosen to be an alias for discretisedfield\n", + "\n", + "p1 = (0, 0, 0)\n", + "p2 = (100e-9, 50e-9, 20e-9)\n", + "\n", + "region = df.Region(p1=p1, p2=p2)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import pyvista as pv\n", + "\n", + "pv.set_jupyter_backend(\"trame\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "cc1c8a2b9bfe4743957ed50077fceeae", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Widget(value=\"