Skip to main content

polyscope_rs/
slice_plane.rs

1//! Slice plane management.
2//!
3//! Slice planes cut through geometry to reveal interior structure.
4//! They can be positioned interactively via gizmos or programmatically.
5//!
6//! # Example
7//!
8//! ```no_run
9//! use polyscope_rs::*;
10//!
11//! fn main() -> Result<()> {
12//!     init()?;
13//!
14//!     // Register some geometry first...
15//!     register_point_cloud("points", vec![Vec3::ZERO, Vec3::X, Vec3::Y]);
16//!
17//!     // Add a slice plane
18//!     let plane = add_slice_plane("my slice");
19//!     plane.set_pose(Vec3::ZERO, Vec3::X); // origin and normal
20//!     plane.set_draw_plane(true);
21//!     plane.set_draw_widget(true);
22//!
23//!     show();
24//!     Ok(())
25//! }
26//! ```
27
28use crate::{Vec3, Vec4, with_context, with_context_mut};
29
30/// Adds a new slice plane to cut through geometry.
31///
32/// Slice planes allow visualizing the interior of 3D geometry by
33/// discarding fragments on one side of the plane.
34///
35/// The plane is created at the scene center with a size proportional to the
36/// scene's length scale, ensuring it's visible regardless of the scene scale.
37pub fn add_slice_plane(name: impl Into<String>) -> SlicePlaneHandle {
38    let name = name.into();
39    with_context_mut(|ctx| {
40        let length_scale = ctx.length_scale;
41        // Get scene center before creating the plane (to avoid borrow issues)
42        let center = (ctx.bounding_box.0 + ctx.bounding_box.1) * 0.5;
43        let plane = ctx.add_slice_plane(&name);
44        // Set plane_size to be visible relative to the scene
45        // Using length_scale * 0.25 gives a reasonably sized plane
46        plane.set_plane_size(length_scale * 0.25);
47        // Position the plane at the scene center
48        plane.set_origin(center);
49    });
50    SlicePlaneHandle { name }
51}
52
53/// Adds a slice plane with a specific pose.
54pub fn add_slice_plane_with_pose(
55    name: impl Into<String>,
56    origin: Vec3,
57    normal: Vec3,
58) -> SlicePlaneHandle {
59    let name = name.into();
60    with_context_mut(|ctx| {
61        let plane = ctx.add_slice_plane(&name);
62        plane.set_pose(origin, normal);
63    });
64    SlicePlaneHandle { name }
65}
66
67/// Adds a slice plane with an auto-generated name like "Scene Slice Plane 0".
68///
69/// Mirrors C++ Polyscope's `addSlicePlane()` (no-args overload). Returns a
70/// handle to the newly created plane. The chosen index is the smallest
71/// non-negative integer N for which "Scene Slice Plane N" is not already in
72/// use, so removing a middle plane and re-adding will reclaim that index.
73///
74/// Search and creation happen under a single `with_context_mut` lock — without
75/// that, two concurrent callers could pick the same name and one would receive
76/// a handle to the other's plane (since core's `add_slice_plane` is an upsert).
77pub fn add_slice_plane_auto() -> SlicePlaneHandle {
78    let name = with_context_mut(|ctx| {
79        let mut i = 0usize;
80        let candidate = loop {
81            let c = format!("Scene Slice Plane {i}");
82            if !ctx.has_slice_plane(&c) {
83                break c;
84            }
85            i += 1;
86        };
87        let length_scale = ctx.length_scale;
88        let center = (ctx.bounding_box.0 + ctx.bounding_box.1) * 0.5;
89        let plane = ctx.add_slice_plane(&candidate);
90        plane.set_plane_size(length_scale * 0.25);
91        plane.set_origin(center);
92        candidate
93    });
94    SlicePlaneHandle { name }
95}
96
97/// Gets an existing slice plane by name.
98#[must_use]
99pub fn get_slice_plane(name: &str) -> Option<SlicePlaneHandle> {
100    with_context(|ctx| {
101        if ctx.has_slice_plane(name) {
102            Some(SlicePlaneHandle {
103                name: name.to_string(),
104            })
105        } else {
106            None
107        }
108    })
109}
110
111/// Removes a slice plane by name.
112pub fn remove_slice_plane(name: &str) {
113    with_context_mut(|ctx| {
114        ctx.remove_slice_plane(name);
115    });
116}
117
118/// Removes all slice planes.
119pub fn remove_all_slice_planes() {
120    with_context_mut(|ctx| {
121        ctx.slice_planes.clear();
122    });
123}
124
125/// Returns all slice plane names.
126#[must_use]
127pub fn get_all_slice_planes() -> Vec<String> {
128    with_context(|ctx| {
129        ctx.slice_plane_names()
130            .into_iter()
131            .map(std::string::ToString::to_string)
132            .collect()
133    })
134}
135
136/// Handle for a slice plane.
137#[derive(Clone)]
138pub struct SlicePlaneHandle {
139    name: String,
140}
141
142impl SlicePlaneHandle {
143    /// Returns the name of this slice plane.
144    #[must_use]
145    pub fn name(&self) -> &str {
146        &self.name
147    }
148
149    /// Removes this slice plane from the scene.
150    ///
151    /// Consumes the handle since the underlying plane is gone afterwards.
152    /// Mirrors C++ Polyscope's `SlicePlane::remove()`.
153    pub fn remove(self) {
154        remove_slice_plane(&self.name);
155    }
156
157    /// Sets the pose (origin and normal) of the slice plane.
158    pub fn set_pose(&self, origin: Vec3, normal: Vec3) -> &Self {
159        with_context_mut(|ctx| {
160            if let Some(plane) = ctx.get_slice_plane_mut(&self.name) {
161                plane.set_pose(origin, normal);
162            }
163        });
164        self
165    }
166
167    /// Sets the origin point of the plane.
168    pub fn set_origin(&self, origin: Vec3) -> &Self {
169        with_context_mut(|ctx| {
170            if let Some(plane) = ctx.get_slice_plane_mut(&self.name) {
171                plane.set_origin(origin);
172            }
173        });
174        self
175    }
176
177    /// Gets the origin point of the plane.
178    #[must_use]
179    pub fn origin(&self) -> Vec3 {
180        with_context(|ctx| {
181            ctx.get_slice_plane(&self.name)
182                .map_or(Vec3::ZERO, polyscope_core::SlicePlane::origin)
183        })
184    }
185
186    /// Sets the normal direction of the plane.
187    pub fn set_normal(&self, normal: Vec3) -> &Self {
188        with_context_mut(|ctx| {
189            if let Some(plane) = ctx.get_slice_plane_mut(&self.name) {
190                plane.set_normal(normal);
191            }
192        });
193        self
194    }
195
196    /// Gets the normal direction of the plane.
197    #[must_use]
198    pub fn normal(&self) -> Vec3 {
199        with_context(|ctx| {
200            ctx.get_slice_plane(&self.name)
201                .map_or(Vec3::Y, polyscope_core::SlicePlane::normal)
202        })
203    }
204
205    /// Sets whether the slice plane is enabled.
206    pub fn set_enabled(&self, enabled: bool) -> &Self {
207        with_context_mut(|ctx| {
208            if let Some(plane) = ctx.get_slice_plane_mut(&self.name) {
209                plane.set_enabled(enabled);
210            }
211        });
212        self
213    }
214
215    /// Returns whether the slice plane is enabled.
216    #[must_use]
217    pub fn is_enabled(&self) -> bool {
218        with_context(|ctx| {
219            ctx.get_slice_plane(&self.name)
220                .is_some_and(polyscope_core::SlicePlane::is_enabled)
221        })
222    }
223
224    /// Sets whether to draw the plane visualization.
225    pub fn set_draw_plane(&self, draw: bool) -> &Self {
226        with_context_mut(|ctx| {
227            if let Some(plane) = ctx.get_slice_plane_mut(&self.name) {
228                plane.set_draw_plane(draw);
229            }
230        });
231        self
232    }
233
234    /// Returns whether the plane visualization is drawn.
235    #[must_use]
236    pub fn draw_plane(&self) -> bool {
237        with_context(|ctx| {
238            ctx.get_slice_plane(&self.name)
239                .is_some_and(polyscope_core::SlicePlane::draw_plane)
240        })
241    }
242
243    /// Sets whether to draw the widget.
244    pub fn set_draw_widget(&self, draw: bool) -> &Self {
245        with_context_mut(|ctx| {
246            if let Some(plane) = ctx.get_slice_plane_mut(&self.name) {
247                plane.set_draw_widget(draw);
248            }
249        });
250        self
251    }
252
253    /// Returns whether the widget is drawn.
254    #[must_use]
255    pub fn draw_widget(&self) -> bool {
256        with_context(|ctx| {
257            ctx.get_slice_plane(&self.name)
258                .is_some_and(polyscope_core::SlicePlane::draw_widget)
259        })
260    }
261
262    /// Sets the color of the plane visualization.
263    pub fn set_color(&self, color: Vec3) -> &Self {
264        with_context_mut(|ctx| {
265            if let Some(plane) = ctx.get_slice_plane_mut(&self.name) {
266                plane.set_color(color);
267            }
268        });
269        self
270    }
271
272    /// Gets the color of the plane visualization.
273    #[must_use]
274    pub fn color(&self) -> Vec4 {
275        with_context(|ctx| {
276            ctx.get_slice_plane(&self.name).map_or(
277                Vec4::new(0.5, 0.5, 0.5, 1.0),
278                polyscope_core::SlicePlane::color,
279            )
280        })
281    }
282
283    /// Sets the transparency of the plane visualization.
284    pub fn set_transparency(&self, transparency: f32) -> &Self {
285        with_context_mut(|ctx| {
286            if let Some(plane) = ctx.get_slice_plane_mut(&self.name) {
287                plane.set_transparency(transparency);
288            }
289        });
290        self
291    }
292
293    /// Gets the transparency of the plane visualization.
294    #[must_use]
295    pub fn transparency(&self) -> f32 {
296        with_context(|ctx| {
297            ctx.get_slice_plane(&self.name)
298                .map_or(0.3, polyscope_core::SlicePlane::transparency)
299        })
300    }
301
302    /// Sets the size of the plane visualization (half-extent in each direction).
303    pub fn set_plane_size(&self, size: f32) -> &Self {
304        with_context_mut(|ctx| {
305            if let Some(plane) = ctx.get_slice_plane_mut(&self.name) {
306                plane.set_plane_size(size);
307            }
308        });
309        self
310    }
311
312    /// Gets the size of the plane visualization (half-extent in each direction).
313    #[must_use]
314    pub fn plane_size(&self) -> f32 {
315        with_context(|ctx| {
316            ctx.get_slice_plane(&self.name)
317                .map_or(0.1, polyscope_core::SlicePlane::plane_size)
318        })
319    }
320}