diff --git a/glue_ar/export_dialog.ui b/glue_ar/export_dialog.ui
index b382746..10f7ed9 100644
--- a/glue_ar/export_dialog.ui
+++ b/glue_ar/export_dialog.ui
@@ -14,12 +14,11 @@
Export 3D Volume
- -
-
-
- Set the export settings for each layer
-
-
+
-
+
+
+ -
+
-
@@ -60,12 +59,6 @@
- -
-
-
- -
-
-
-
@@ -73,8 +66,15 @@
- -
-
+
-
+
+
+ -
+
+
+ Set the export settings for each layer
+
+
diff --git a/glue_ar/scatter.py b/glue_ar/scatter.py
index 3b33cf0..749fd77 100644
--- a/glue_ar/scatter.py
+++ b/glue_ar/scatter.py
@@ -1,3 +1,5 @@
+from math import floor
+
import pyvista as pv
from glue_ar.utils import layer_color, xyz_bounds, xyz_for_layer
@@ -6,7 +8,7 @@
def scatter_layer_as_points(viewer_state, layer_state):
xyz = xyz_for_layer(viewer_state, layer_state)
return {
- "data": xyz,
+ "mesh": xyz,
"color": layer_color(layer_state),
"opacity": layer_state.alpha,
"style": "points_gaussian",
@@ -18,7 +20,7 @@ def scatter_layer_as_points(viewer_state, layer_state):
def scatter_layer_as_spheres(viewer_state, layer_state):
data = xyz_for_layer(viewer_state, layer_state)
return {
- "data": [pv.Sphere(center=p) for p in data]
+ "mesh": [pv.Sphere(center=p) for p in data]
}
@@ -27,7 +29,7 @@ def scatter_layer_as_glyphs(viewer_state, layer_state, glyph):
points = pv.PointSet(data)
glyphs = points.glyph(geom=glyph, orient=False, scale=False)
return {
- "data": glyphs,
+ "mesh": glyphs,
"color": layer_color(layer_state),
"opacity": layer_state.alpha,
}
@@ -36,16 +38,21 @@ def scatter_layer_as_glyphs(viewer_state, layer_state, glyph):
def scatter_layer_as_multiblock(viewer_state, layer_state,
theta_resolution=8,
phi_resolution=8,
- scaled=True):
- data = xyz_for_layer(viewer_state, layer_state, scaled=scaled)
+ scaled=True,
+ clip_to_bounds=True):
+ data = xyz_for_layer(viewer_state, layer_state,
+ preserve_aspect=viewer_state.native_aspect,
+ clip_to_bounds=clip_to_bounds,
+ scaled=scaled)
bounds = xyz_bounds(viewer_state)
factor = max((abs(b[1] - b[0]) for b in bounds))
- radius = layer_state.size_scaling * layer_state.size / factor
+ radius = (layer_state.size_scaling * layer_state.size) / factor
spheres = [pv.Sphere(center=p, radius=radius, phi_resolution=phi_resolution, theta_resolution=theta_resolution) for p in data]
blocks = pv.MultiBlock(spheres)
geometry = blocks.extract_geometry()
+
info = {
- "data": geometry,
+ "mesh": geometry,
"opacity": layer_state.alpha
}
if layer_state.color_mode == "Fixed":
@@ -66,4 +73,5 @@ def scatter_layer_as_multiblock(viewer_state, layer_state,
info["cmap"] = cmap
info["clim"] = clim
info["scalars"] = "colors"
+
return info
diff --git a/glue_ar/tools.py b/glue_ar/tools.py
index f1b90d1..7fa5ca8 100644
--- a/glue_ar/tools.py
+++ b/glue_ar/tools.py
@@ -13,6 +13,7 @@
from glue_ar.scatter import scatter_layer_as_multiblock
from glue_ar.export import export_gl, export_modelviewer
+from glue_ar.utils import bounds_3d_from_layers
from glue_ar.volume import bounds_3d, meshes_for_volume_layer
__all__ = ["GLScatterExportTool", "GLVolumeExportTool"]
@@ -45,9 +46,13 @@ def activate(self):
layer_states = [layer.state for layer in self.viewer.layers if layer.enabled and layer.state.visible]
for layer_state in layer_states:
layer_info = dialog.state_dictionary[layer_state.layer.label].as_dict()
- mesh_info = scatter_layer_as_multiblock(self.viewer.state, layer_state, **layer_info, scaled=True)
- data = mesh_info.pop("data")
- plotter.add_mesh(data, **mesh_info)
+ mesh_info = scatter_layer_as_multiblock(self.viewer.state, layer_state,
+ scaled=True,
+ clip_to_bounds=self.viewer.state.clip_data,
+ **layer_info)
+ mesh = mesh_info.pop("mesh")
+ if len(mesh.points) > 0:
+ plotter.add_mesh(mesh, **mesh_info)
dir, base = split(export_path)
name, ext = splitext(base)
@@ -79,8 +84,11 @@ def activate(self):
plotter = pv.Plotter()
layer_states = [layer.state for layer in self.viewer.layers if layer.enabled and layer.state.visible]
- bounds = bounds_3d(self.viewer.state)
frbs = {}
+ if self.viewer.state.clip_data:
+ bounds = bounds_3d(self.viewer.state)
+ else:
+ bounds = bounds_3d_from_layers(self.viewer.state, layer_states)
for layer_state in layer_states:
layer_info = dialog.state_dictionary[layer_state.layer.label].as_dict()
if isinstance(layer_state, VolumeLayerState):
@@ -89,9 +97,13 @@ def activate(self):
precomputed_frbs=frbs,
**layer_info)
else:
- mesh_info = scatter_layer_as_multiblock(self.viewer.state, layer_state, **layer_info, scaled=False)
- data = mesh_info.pop("data")
- plotter.add_mesh(data, **mesh_info)
+ mesh_info = scatter_layer_as_multiblock(self.viewer.state, layer_state,
+ scaled=False,
+ clip_to_bounds=self.viewer.state.clip_data,
+ **layer_info)
+ mesh = mesh_info.pop("mesh")
+ if len(mesh.points) > 0:
+ plotter.add_mesh(mesh, **mesh_info)
dir, base = split(export_path)
name, ext = splitext(base)
diff --git a/glue_ar/utils.py b/glue_ar/utils.py
index d638157..855d183 100644
--- a/glue_ar/utils.py
+++ b/glue_ar/utils.py
@@ -1,5 +1,5 @@
from glue.core.subset_group import GroupedSubset
-from numpy import array
+from numpy import array, inf
def isomin_for_layer(viewer_state, layer):
@@ -17,6 +17,18 @@ def xyz_bounds(viewer_state):
(viewer_state.z_min, viewer_state.z_max)]
+def bounds_3d_from_layers(viewer_state, layer_states):
+ mins = [inf, inf, inf]
+ maxes = [-inf, -inf, -inf]
+ atts = viewer_state.x_att, viewer_state.y_att, viewer_state.z_att
+ for state in layer_states:
+ data = state.layer.layer
+ mins = [min(min(data[att]), m) for m, att in zip(mins, atts)]
+ maxes = [max(max(data[att]), m) for m, att in zip(maxes, atts)]
+ print(mins)
+ return [(l, u) for l, u in zip(mins, maxes)]
+
+
# TODO: Make this better?
# glue-plotly has had to deal with similar issues,
# the utilities there are at least better than this
@@ -27,8 +39,6 @@ def layer_color(layer_state):
return layer_color
-# data should be list of numpy arrays
-# Think about being more format-agnostic later
def scale(data, bounds, preserve_aspect=True):
if preserve_aspect:
ranges = [abs(bds[1] - bds[0]) for bds in bounds]
@@ -37,25 +47,42 @@ def scale(data, bounds, preserve_aspect=True):
bds = bounds[index]
m = 2 / (bds[1] - bds[0])
b = (bds[0] + bds[1]) / (bds[1] - bds[0])
- return [m * d + b for d in data]
+ scaled = [[m * v + b for v in d] for d in data]
else:
scaled = []
for idx, bds in enumerate(bounds):
m = 2 / (bds[1] - bds[0])
b = (bds[0] + bds[1]) / (bds[1] - bds[0])
- scaled.append(m * data[idx] + b)
- return scaled
+ scaled.append([m * d + b for d in data[idx]])
+
+ return scaled
# TODO: Worry about efficiency later
-def xyz_for_layer(viewer_state, layer_state, scaled=False, preserve_aspect=True):
+# and just generally make this better
+def xyz_for_layer(viewer_state, layer_state,
+ scaled=False,
+ preserve_aspect=True,
+ clip_to_bounds=True):
xs = layer_state.layer[viewer_state.x_att]
ys = layer_state.layer[viewer_state.y_att]
zs = layer_state.layer[viewer_state.z_att]
vals = [xs, ys, zs]
- if scaled:
+ if scaled or clip_to_bounds:
bounds = xyz_bounds(viewer_state)
- vals = scale(vals, bounds, preserve_aspect=preserve_aspect)
+ if clip_to_bounds:
+ xs, ys, zs = [], [], []
+ for x, y, z in zip(*vals):
+ if (x >= bounds[0][0] and x <= bounds[0][1]) and \
+ (y >= bounds[1][0] and y <= bounds[1][1]) and \
+ (z >= bounds[2][0] and z <= bounds[2][1]):
+ xs.append(x)
+ ys.append(y)
+ zs.append(z)
+ vals = [xs, ys, zs]
+
+ if scaled:
+ vals = scale(vals, bounds, preserve_aspect=preserve_aspect)
return array(list(zip(*vals)))
diff --git a/glue_ar/volume.py b/glue_ar/volume.py
index 532167b..977b142 100644
--- a/glue_ar/volume.py
+++ b/glue_ar/volume.py
@@ -16,20 +16,19 @@ def meshes_for_volume_layer(viewer_state, layer_state, bounds,
use_gaussian_filter=False, smoothing_iterations=0,
precomputed_frbs=None):
- precomputed_frbs = precomputed_frbs or {}
-
layer_content = layer_state.layer
parent = layer_content.data if isinstance(layer_content, GroupedSubset) else layer_content
parent_label = parent.label
- if parent_label in precomputed_frbs:
+ if precomputed_frbs is not None and parent_label in precomputed_frbs:
data = precomputed_frbs[parent_label]
else:
data = parent.compute_fixed_resolution_buffer(
target_data=viewer_state.reference_data,
bounds=bounds,
target_cid=layer_state.attribute)
- precomputed_frbs[parent_label] = data
+ if precomputed_frbs is not None:
+ precomputed_frbs[parent_label] = data
if isinstance(layer_state.layer, GroupedSubset):
subcube = parent.compute_fixed_resolution_buffer(
@@ -60,7 +59,7 @@ def meshes_for_volume_layer(viewer_state, layer_state, bounds,
isodata = isodata.smooth(n_iter=int(smoothing_iterations))
return {
- "data": isodata,
+ "mesh": isodata,
"color": layer_color(layer_state),
"opacity": layer_state.alpha,
# "isomin": isomin,