Skip to content
Snippets Groups Projects

Resolve "(LearnerND) add iso-surface plot feature"

Merged Jorn Hoofwijk requested to merge 112-learnernd-add-iso-surface-plot-feature into master
Compare and Show latest version
9 files
+ 148
87
Compare changes
  • Side-by-side
  • Inline
Files
9
@@ -10,20 +10,11 @@ import scipy.spatial
from .base_learner import BaseLearner
from ..notebook_integration import ensure_holoviews
from ..notebook_integration import ensure_holoviews, ensure_plotly
from .triangulation import (Triangulation, point_in_simplex,
circumsphere, simplex_volume_in_embedding)
from ..utils import restore, cache_latest
try:
import plotly.graph_objs
import plotly.figure_factory
import plotly.offline
with_plotly = True
except ModuleNotFoundError:
with_plotly = False
def volume(simplex, ys=None):
# Notice the parameter ys is there so you can use this volume method as
@@ -586,11 +577,6 @@ class LearnerND(BaseLearner):
else:
raise ValueError("Only 1 or 2-dimensional plots can be generated.")
def get_range(self):
r_min = min(self.data[v] for v in self.tri.vertices)
r_max = max(self.data[v] for v in self.tri.vertices)
return r_min, r_max
def _get_isosurface(self, level=0.0):
if self.ndim != 3 or self.vdim != 1:
raise Exception('Isosurface plotting is only supported'
@@ -614,7 +600,8 @@ class LearnerND(BaseLearner):
db = abs(value_b - level)
dab = da + db
new_pt = db / dab * np.array(vertex_a) + da / dab * np.array(vertex_b)
new_pt = (db / dab * np.array(vertex_a)
+ da / dab * np.array(vertex_b))
new_index = len(vertices)
vertices.append(new_pt)
@@ -642,48 +629,58 @@ class LearnerND(BaseLearner):
faces.append(plane[1:])
if len(faces) == 0:
r_min, r_max = self.get_range()
r_min = min(self.data[v] for v in self.tri.vertices)
r_max = max(self.data[v] for v in self.tri.vertices)
raise ValueError(
f"Could not draw isosurface for level={level}, as"
" this value is not inside the function range. Please choose a level"
f" strictly inside interval ({r_min}, {r_max})"
" this value is not inside the function range. Please choose"
f" a level strictly inside interval ({r_min}, {r_max})"
)
return vertices, faces
def plot_isosurface(self, level=0.0, hull_opacity=0.2):
"""Plots the linearly interpolated iso-surface of the function, based on
the currently evaluated points. This is the 3d analog of an iso-line.
"""Plots the linearly interpolated isosurface of the function,
based on the currently evaluated points. This is the 3D analog
of an isoline.
Parameters
----------
level : float
the function value which you are interested in. Defaults to 0.0.
hull_opacity : float
the opacity of the hull of the domain. Defaults to 0.2
level : float, default 0.0
the function value which you are interested in.
hull_opacity : float, default 0.0
the opacity of the hull of the domain.
Returns
-------
plot : plotly.offline.iplot object
The plot object of the isosurface.
"""
if not with_plotly:
raise Exception('plot_isosurface requires plotly to be installed')
plotly = ensure_plotly()
vertices, faces = self._get_isosurface(level)
x, y, z = zip(*vertices)
fig = plotly.figure_factory.create_trisurf(
x=x, y=y, z=z, plot_edges=False, simplices=faces, title="Isosurface")
x=x, y=y, z=z, plot_edges=False,
simplices=faces, title="Isosurface")
isosurface = fig.data[0]
isosurface.update(lighting=dict(ambient=1, diffuse=1, roughness=1, specular=0, fresnel=0))
if hull_opacity < 1e-3:
# Do not compute the hull_mesh.
return plotly.offline.iplot(fig)
hull_mesh = self._get_hull_mesh(opacity=hull_opacity)
return plotly.offline.iplot([fig.data[0], hull_mesh])
return plotly.offline.iplot([isosurface, hull_mesh])
def _get_hull_mesh(self, opacity=0.2):
plotly = ensure_plotly()
hull = scipy.spatial.ConvexHull(self._bounds_points)
# Find the colors of each plane, giving triangles which are coplanar the
# same color, such that a square face has the same color.
# Find the colors of each plane, giving triangles which are coplanar
# the same color, such that a square face has the same color.
color_dict = {}
def _get_plane_color(simplex):
@@ -691,17 +688,21 @@ class LearnerND(BaseLearner):
# If the volume of the two triangles combined is zero then they
# belong to the same plane.
for simplex_key, color in color_dict.items():
points = np.array([hull.points[i] for i in np.unique(simplex_key + simplex)])
points = [hull.points[i] for i in set(simplex_key + simplex)]
points = np.array(points)
if np.linalg.matrix_rank(points[1:] - points[0]) < 3:
return color
if scipy.spatial.ConvexHull(points).volume < 1e-5:
return color
color_dict[simplex] = tuple(random.randint(0, 255) for _ in range(3))
color_dict[simplex] = tuple(random.randint(0, 255)
for _ in range(3))
return color_dict[simplex]
colors = [_get_plane_color(simplex) for simplex in hull.simplices]
x, y, z = zip(*self._bounds_points)
i, j, k = hull.simplices.T
lighting = dict(ambient=1, diffuse=1, roughness=1, specular=0, fresnel=0)
return plotly.graph_objs.Mesh3d(x=x, y=y, z=z, i=i, j=j, k=k,
facecolor=(colors), opacity=opacity)
facecolor=colors, opacity=opacity,
lighting=lighting)
Loading