diff --git a/docs/source/conf.py b/docs/source/conf.py
index 8973b1e9..4b168a31 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -81,8 +81,8 @@
# from https://github.com/ipython/ipywidgets/blob/master/docs/source/conf.py
_release = {}
exec(compile(open('../../ipyvolume/_version.py').read(), '../../ipyvolume/_version.py', 'exec'), _release)
-version = '.'.join(map(str, _release['__version_tuple__'][:2]))
release = _release['__version__']
+version = ".".join(_release['__version__'].split(".")[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -386,5 +386,6 @@
'examples/popup': 'examples/screenshot/ipyvolume-popup-legend-iris.gif',
'examples/lighting': 'examples/screenshot/volume-rendering-specular.gif',
'examples/slice': 'examples/screenshot/ipyvolume-slice-head.gif',
+ 'examples/volume-clipping': 'examples/screenshot/volume-clip.gif',
}
exclude_patterns = ['**.ipynb_checkpoints']
diff --git a/docs/source/examples.rst b/docs/source/examples.rst
index 62f5f943..374ebf38 100644
--- a/docs/source/examples.rst
+++ b/docs/source/examples.rst
@@ -15,6 +15,8 @@ Examples
examples/lighting
examples/popup
examples/slice
+ examples/slice
+ examples/volume-clipping
Feel free to contribute new examples:
diff --git a/docs/source/examples/screenshot/volume-clip.gif b/docs/source/examples/screenshot/volume-clip.gif
new file mode 100644
index 00000000..b6b31740
Binary files /dev/null and b/docs/source/examples/screenshot/volume-clip.gif differ
diff --git a/docs/source/examples/volume-clipping.ipynb b/docs/source/examples/volume-clipping.ipynb
new file mode 100644
index 00000000..7b81c929
--- /dev/null
+++ b/docs/source/examples/volume-clipping.ipynb
@@ -0,0 +1,90 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "580c6579",
+ "metadata": {},
+ "source": [
+ "# Clipping of volume\n",
+ "\n",
+ "In order to inspect volumetric renderings, it may be useful to remove parts of it. Using the `clip_x_min`, `clip_x_max`, `clip_y_min`, `clip_y_max`, `clip_z_min` and `clip_z_max` traits, we can control which part of the volume is rendered. In the example below you can use the sliders labels 'xmin' and 'xmax' to inspect regions inside the volume."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c7e47337",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import ipyvolume as ipv\n",
+ "fig = ipv.figure()\n",
+ "volume = ipv.examples.head(show=False, description=\"Patient X\")\n",
+ "ipv.show()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "34ea1395",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import ipywidgets as w\n",
+ "clip_min = w.IntSlider(description=\"xmin\", min=0, max=128, value=50)\n",
+ "clip_max = w.IntSlider(description=\"xmax\", min=0, max=128, value=100)\n",
+ "w.jslink((clip_min, 'value'), (volume, 'clip_x_min'))\n",
+ "w.jslink((clip_max, 'value'), (volume, 'clip_x_max'))\n",
+ "container = ipv.gcc()\n",
+ "container.children = container.children + [clip_min, clip_max]\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "81702c3d",
+ "metadata": {},
+ "source": [
+ "Note that you can also link the instance the `slice_x` trait of the figure to the `clip_x_max` trait. Now we can hold the shift key and the clip plane will follow the mouse cursor."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "68b54115",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "w.jslink((fig, 'slice_x'), (volume, 'clip_x_max'));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "22f48b7f",
+ "metadata": {},
+ "source": [
+ "[screencapture](screenshot/volume-clip.gif)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.16"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/ipyvolume/vue/container.vue b/ipyvolume/vue/container.vue
index f31f517f..25c1097c 100644
--- a/ipyvolume/vue/container.vue
+++ b/ipyvolume/vue/container.vue
@@ -12,7 +12,9 @@
Misc
-
+
+
+
diff --git a/ipyvolume/widgets.py b/ipyvolume/widgets.py
index 6f2eb07a..b6b512bb 100644
--- a/ipyvolume/widgets.py
+++ b/ipyvolume/widgets.py
@@ -193,6 +193,13 @@ class Volume(widgets.Widget, LegendData):
extent = traitlets.Any().tag(sync=True)
extent_original = traitlets.Any()
+ clip_x_min = traitlets.CFloat(None, allow_none=True).tag(sync=True)
+ clip_x_max = traitlets.CFloat(None, allow_none=True).tag(sync=True)
+ clip_y_min = traitlets.CFloat(None, allow_none=True).tag(sync=True)
+ clip_y_max = traitlets.CFloat(None, allow_none=True).tag(sync=True)
+ clip_z_min = traitlets.CFloat(None, allow_none=True).tag(sync=True)
+ clip_z_max = traitlets.CFloat(None, allow_none=True).tag(sync=True)
+
def __init__(self, **kwargs):
super(Volume, self).__init__(**kwargs)
self._update_data()
diff --git a/js/src/volume.ts b/js/src/volume.ts
index 873abd0d..8f8e0ea7 100644
--- a/js/src/volume.ts
+++ b/js/src/volume.ts
@@ -118,6 +118,9 @@ class VolumeView extends widgets.WidgetView {
this.renderer.rebuild_multivolume_rendering_material();
this.renderer.update();
});
+ this.model.on("change:clip_x_min change:clip_x_max change:clip_y_min change:clip_y_max change:clip_z_min change:clip_z_max", () => {
+ this.renderer.update();
+ });
(window as any).last_volume = this; // for debugging purposes
@@ -168,6 +171,7 @@ class VolumeModel extends widgets.WidgetModel {
};
volume: any;
+ scales?: any;
texture_volume: THREE.DataTexture;
uniform_volumes_values: {
data_range?: any,
@@ -321,13 +325,26 @@ class VolumeModel extends widgets.WidgetModel {
return this.get("rendering_method") === "NORMAL";
}
set_scales(scales) {
- const sx = createD3Scale(scales.x).range([0, 1]);
- const sy = createD3Scale(scales.y).range([0, 1]);
- const sz = createD3Scale(scales.z).range([0, 1]);
+ this.scales = scales;
+ this.update_geometry();
+ }
+ update_geometry() {
+ const sx = createD3Scale(this.scales.x).range([0, 1]);
+ const sy = createD3Scale(this.scales.y).range([0, 1]);
+ const sz = createD3Scale(this.scales.z).range([0, 1]);
const extent = this.get("extent");
- // normalized coordinates of the corners of the box
+ // return v, of the clipped value
+ const or_clip = (v, name) => {
+ const clip_value = this.get("clip_" + name);
+ if ((clip_value === undefined) || (clip_value === null)) {
+ return v;
+ }
+ return clip_value;
+ }
+
+ // normalized coordinates of the corners of the box
const x0n = sx(extent[0][0]);
const x1n = sx(extent[0][1]);
const y0n = sy(extent[1][0]);
@@ -335,13 +352,15 @@ class VolumeModel extends widgets.WidgetModel {
const z0n = sz(extent[2][0]);
const z1n = sz(extent[2][1]);
- // clipped coordinates
- const cx0 = Math.max(x0n, 0);
- const cx1 = Math.min(x1n, 1);
- const cy0 = Math.max(y0n, 0);
- const cy1 = Math.min(y1n, 1);
- const cz0 = Math.max(z0n, 0);
- const cz1 = Math.min(z1n, 1);
+ // normalized coordinates of the corners of the box
+ // including the custom clipping, and viewport clipping
+ const cx0 = Math.max(sx(or_clip(extent[0][0], "x_min")), 0);
+ const cx1 = Math.min(sx(or_clip(extent[0][1], "x_max")), 1);
+ const cy0 = Math.max(sy(or_clip(extent[1][0], "y_min")), 0);
+ const cy1 = Math.min(sy(or_clip(extent[1][1], "y_max")), 1);
+ const cz0 = Math.max(sz(or_clip(extent[2][0], "z_min")), 0);
+ const cz1 = Math.min(sz(or_clip(extent[2][1], "z_max")), 1);
+
// the clipped coordinates back to world space, then normalized to extend
// these are example calculations, the transform goes into scale and offset uniforms below
diff --git a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_0.png b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_0.png
index a8c2efe7..852c456c 100644
Binary files a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_0.png and b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_0.png differ
diff --git a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_1.png b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_1.png
index a8c2efe7..852c456c 100644
Binary files a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_1.png and b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_1.png differ
diff --git a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_2.png b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_2.png
index 9e66a8c8..98efe8d9 100644
Binary files a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_2.png and b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_2.png differ
diff --git a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_3.png b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_3.png
index 214e8155..47c2b1c8 100644
Binary files a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_3.png and b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_3.png differ
diff --git a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_4.png b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_4.png
index 2056eb44..c070d9b0 100644
Binary files a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_4.png and b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_4.png differ
diff --git a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_5.png b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_5.png
index 0c417f27..526ad857 100644
Binary files a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_5.png and b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_5.png differ
diff --git a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_6.png b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_6.png
index b9f092a1..acd04d2d 100644
Binary files a/ui-tests/reference-output/screenshots/lighting_ipynb_cell_6.png and b/ui-tests/reference-output/screenshots/lighting_ipynb_cell_6.png differ
diff --git a/ui-tests/reference-output/screenshots/scatter_ipynb_cell_0.png b/ui-tests/reference-output/screenshots/scatter_ipynb_cell_0.png
index 49689576..62fc8838 100644
Binary files a/ui-tests/reference-output/screenshots/scatter_ipynb_cell_0.png and b/ui-tests/reference-output/screenshots/scatter_ipynb_cell_0.png differ