#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SculptOverlayConfig {
pub visible: bool,
pub show_symmetry: bool,
pub show_mask: bool,
pub multires_level: u32,
pub symmetry_color: [f32; 4],
pub mask_color: [f32; 4],
}
impl Default for SculptOverlayConfig {
fn default() -> Self {
Self {
visible: true,
show_symmetry: true,
show_mask: true,
multires_level: 0,
symmetry_color: [0.0, 1.0, 1.0, 1.0],
mask_color: [1.0, 0.0, 0.0, 0.5],
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SculptMask {
pub values: Vec<f32>,
}
impl SculptMask {
#[allow(dead_code)]
pub fn new(n: usize) -> Self {
Self { values: vec![0.0; n] }
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SculptOverlay {
pub config: SculptOverlayConfig,
pub symmetry_plane: Option<u8>,
pub mask: Option<SculptMask>,
}
pub type MaskBoundaryEdges = Vec<(usize, usize)>;
#[allow(dead_code)]
pub fn default_sculpt_overlay_config() -> SculptOverlayConfig {
SculptOverlayConfig::default()
}
#[allow(dead_code)]
pub fn new_sculpt_overlay(config: SculptOverlayConfig) -> SculptOverlay {
SculptOverlay {
config,
symmetry_plane: None,
mask: None,
}
}
#[allow(dead_code)]
pub fn set_symmetry_plane(overlay: &mut SculptOverlay, axis: u8) {
overlay.symmetry_plane = Some(axis.min(2));
}
#[allow(dead_code)]
pub fn symmetry_mirror_point(overlay: &SculptOverlay, point: [f32; 3]) -> [f32; 3] {
match overlay.symmetry_plane {
Some(0) => [-point[0], point[1], point[2]],
Some(1) => [point[0], -point[1], point[2]],
Some(2) => [point[0], point[1], -point[2]],
_ => point,
}
}
#[allow(dead_code)]
pub fn set_mask(overlay: &mut SculptOverlay, mask: SculptMask) {
overlay.mask = Some(mask);
}
#[allow(dead_code)]
pub fn apply_mask_to_strength(overlay: &SculptOverlay, idx: usize, strength: f32) -> f32 {
match &overlay.mask {
Some(mask) if idx < mask.values.len() => strength * (1.0 - mask.values[idx]),
_ => strength,
}
}
#[allow(dead_code)]
pub fn masked_vertex_count(overlay: &SculptOverlay) -> usize {
match &overlay.mask {
Some(mask) => mask.values.iter().filter(|&&v| v > 0.5).count(),
None => 0,
}
}
#[allow(dead_code)]
pub fn sculpt_overlay_to_json(overlay: &SculptOverlay) -> String {
let sym = match overlay.symmetry_plane {
Some(a) => format!("{}", a),
None => "null".to_string(),
};
let mask_len = match &overlay.mask {
Some(m) => m.values.len(),
None => 0,
};
format!(
r#"{{"visible":{},"show_symmetry":{},"show_mask":{},"multires_level":{},"symmetry_plane":{},"mask_vertices":{}}}"#,
overlay.config.visible,
overlay.config.show_symmetry,
overlay.config.show_mask,
overlay.config.multires_level,
sym,
mask_len,
)
}
#[allow(dead_code)]
pub fn toggle_symmetry(overlay: &mut SculptOverlay) {
overlay.config.show_symmetry = !overlay.config.show_symmetry;
}
#[allow(dead_code)]
pub fn toggle_mask_display(overlay: &mut SculptOverlay) {
overlay.config.show_mask = !overlay.config.show_mask;
}
#[allow(dead_code)]
pub fn clear_mask(overlay: &mut SculptOverlay) {
overlay.mask = None;
}
#[allow(dead_code)]
pub fn overlay_visible(overlay: &SculptOverlay) -> bool {
overlay.config.visible
}
#[allow(dead_code)]
pub fn set_multires_level(overlay: &mut SculptOverlay, level: u32) {
overlay.config.multires_level = level;
}
#[allow(dead_code)]
pub fn mask_boundary_edges(overlay: &SculptOverlay, edges: &[(usize, usize)]) -> MaskBoundaryEdges {
let mask_val = |idx: usize| -> f32 {
match &overlay.mask {
Some(m) if idx < m.values.len() => m.values[idx],
_ => 0.0,
}
};
edges
.iter()
.filter(|&&(a, b)| {
let ma = mask_val(a) > 0.5;
let mb = mask_val(b) > 0.5;
ma != mb
})
.copied()
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn make_overlay() -> SculptOverlay {
new_sculpt_overlay(default_sculpt_overlay_config())
}
#[test]
fn test_default_sculpt_overlay_config() {
let cfg = default_sculpt_overlay_config();
assert!(cfg.visible);
assert!(cfg.show_symmetry);
assert!(cfg.show_mask);
assert_eq!(cfg.multires_level, 0);
}
#[test]
fn test_new_sculpt_overlay() {
let overlay = make_overlay();
assert!(overlay.symmetry_plane.is_none());
assert!(overlay.mask.is_none());
}
#[test]
fn test_set_symmetry_plane() {
let mut overlay = make_overlay();
set_symmetry_plane(&mut overlay, 0);
assert_eq!(overlay.symmetry_plane, Some(0));
}
#[test]
fn test_set_symmetry_plane_clamps() {
let mut overlay = make_overlay();
set_symmetry_plane(&mut overlay, 99);
assert_eq!(overlay.symmetry_plane, Some(2));
}
#[test]
fn test_symmetry_mirror_x() {
let mut overlay = make_overlay();
set_symmetry_plane(&mut overlay, 0);
let m = symmetry_mirror_point(&overlay, [1.0, 2.0, 3.0]);
assert!((m[0] + 1.0).abs() < 1e-6);
assert!((m[1] - 2.0).abs() < 1e-6);
assert!((m[2] - 3.0).abs() < 1e-6);
}
#[test]
fn test_symmetry_mirror_y() {
let mut overlay = make_overlay();
set_symmetry_plane(&mut overlay, 1);
let m = symmetry_mirror_point(&overlay, [1.0, 2.0, 3.0]);
assert!((m[1] + 2.0).abs() < 1e-6);
}
#[test]
fn test_symmetry_mirror_z() {
let mut overlay = make_overlay();
set_symmetry_plane(&mut overlay, 2);
let m = symmetry_mirror_point(&overlay, [1.0, 2.0, 3.0]);
assert!((m[2] + 3.0).abs() < 1e-6);
}
#[test]
fn test_symmetry_mirror_none() {
let overlay = make_overlay();
let m = symmetry_mirror_point(&overlay, [1.0, 2.0, 3.0]);
assert_eq!(m, [1.0, 2.0, 3.0]);
}
#[test]
fn test_set_mask_and_masked_count() {
let mut overlay = make_overlay();
let mut mask = SculptMask::new(4);
mask.values[0] = 1.0;
mask.values[2] = 0.8;
set_mask(&mut overlay, mask);
assert_eq!(masked_vertex_count(&overlay), 2);
}
#[test]
fn test_masked_vertex_count_no_mask() {
let overlay = make_overlay();
assert_eq!(masked_vertex_count(&overlay), 0);
}
#[test]
fn test_apply_mask_to_strength() {
let mut overlay = make_overlay();
let mut mask = SculptMask::new(3);
mask.values[0] = 1.0; mask.values[1] = 0.0; set_mask(&mut overlay, mask);
assert!((apply_mask_to_strength(&overlay, 0, 1.0)).abs() < 1e-6);
assert!((apply_mask_to_strength(&overlay, 1, 1.0) - 1.0).abs() < 1e-6);
}
#[test]
fn test_apply_mask_to_strength_no_mask() {
let overlay = make_overlay();
assert!((apply_mask_to_strength(&overlay, 0, 0.7) - 0.7).abs() < 1e-6);
}
#[test]
fn test_toggle_symmetry() {
let mut overlay = make_overlay();
assert!(overlay.config.show_symmetry);
toggle_symmetry(&mut overlay);
assert!(!overlay.config.show_symmetry);
toggle_symmetry(&mut overlay);
assert!(overlay.config.show_symmetry);
}
#[test]
fn test_toggle_mask_display() {
let mut overlay = make_overlay();
assert!(overlay.config.show_mask);
toggle_mask_display(&mut overlay);
assert!(!overlay.config.show_mask);
}
#[test]
fn test_clear_mask() {
let mut overlay = make_overlay();
set_mask(&mut overlay, SculptMask::new(5));
clear_mask(&mut overlay);
assert!(overlay.mask.is_none());
}
#[test]
fn test_overlay_visible() {
let overlay = make_overlay();
assert!(overlay_visible(&overlay));
}
#[test]
fn test_set_multires_level() {
let mut overlay = make_overlay();
set_multires_level(&mut overlay, 3);
assert_eq!(overlay.config.multires_level, 3);
}
#[test]
fn test_mask_boundary_edges() {
let mut overlay = make_overlay();
let mut mask = SculptMask::new(4);
mask.values[0] = 1.0;
mask.values[1] = 0.0;
mask.values[2] = 1.0;
mask.values[3] = 1.0;
set_mask(&mut overlay, mask);
let edges = vec![(0, 1), (1, 2), (2, 3)];
let boundary = mask_boundary_edges(&overlay, &edges);
assert_eq!(boundary.len(), 2);
}
#[test]
fn test_sculpt_overlay_to_json() {
let overlay = make_overlay();
let json = sculpt_overlay_to_json(&overlay);
assert!(json.contains("visible"));
assert!(json.contains("multires_level"));
assert!(json.contains("symmetry_plane"));
}
}