forked from 1b-agent/opencadstudio-upstream
fix(mesh): complete polygon mesh support
This commit is contained in:
commit
e8cb752fab
48 files changed
+5962
-1701
No files matched your search
+181
-245
@@ -2,144 +2,57 @@ use acadrust::entities::{mesh::Mesh, polygon_mesh::PolygonMesh, Face3D, Polyface
|
||||
use cadkernel::geom2d::{triangulate, Tolerance};
|
||||
use cadkernel::space::{polygon, NurbsSurface3, Plane, Vec3 as KernelVec3};
|
||||
use glam::Vec3;
|
||||
use crate::t;
|
||||
|
||||
use crate::command::EntityTransform;
|
||||
use crate::entities::common::{
|
||||
edit_prop, edit_scalar_prop, parse_f64, ro_prop as ro, square_grip,
|
||||
stepper_prop,
|
||||
edit_prop as edit, edit_scalar_prop as edit_scalar, parse_f64, ro_prop as ro,
|
||||
square_grip, stepper_prop as stepper,
|
||||
};
|
||||
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, RenderConvertible};
|
||||
use crate::entities::traits::{Grippable, PropertyEditable, RenderConvertible, Transformable};
|
||||
use crate::scene::convert::acad_to_render::{RenderEntity, RenderObject};
|
||||
use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
||||
use crate::scene::model::wire_model::SnapHint;
|
||||
use crate::t;
|
||||
|
||||
/// Triangulate a planar (possibly concave) polygon into a flat triangle-soup
|
||||
/// (3 vertices per triangle), preserving the polygon's winding. A simple fan
|
||||
/// from vertex 0 is only valid for convex faces — a concave face (e.g. an
|
||||
/// L-shaped mesh face) fans into triangles that spill outside the outline. Ear
|
||||
/// clipping handles both. Falls back to a fan when the polygon is degenerate.
|
||||
/// Triangulate a planar polygon through the kernel.
|
||||
pub(crate) fn triangulate_planar(poly: &[[f64; 3]]) -> Vec<[f64; 3]> {
|
||||
let n = poly.len();
|
||||
if n < 3 {
|
||||
if poly.len() < 3 {
|
||||
return Vec::new();
|
||||
}
|
||||
if n == 3 {
|
||||
return vec![poly[0], poly[1], poly[2]];
|
||||
}
|
||||
let cross = |a: [f64; 3], b: [f64; 3]| {
|
||||
[
|
||||
a[1] * b[2] - a[2] * b[1],
|
||||
a[2] * b[0] - a[0] * b[2],
|
||||
a[0] * b[1] - a[1] * b[0],
|
||||
]
|
||||
let origin = KernelVec3::from(poly[0]);
|
||||
let tolerance = Tolerance::default().linear();
|
||||
let Some(axis) = poly[1..]
|
||||
.iter()
|
||||
.map(|point| KernelVec3::from(*point) - origin)
|
||||
.find(|axis| axis.length() > tolerance)
|
||||
else {
|
||||
return Vec::new();
|
||||
};
|
||||
let dot = |a: [f64; 3], b: [f64; 3]| a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
||||
let fan = || {
|
||||
let mut out = Vec::new();
|
||||
for i in 1..n - 1 {
|
||||
out.push(poly[0]);
|
||||
out.push(poly[i]);
|
||||
out.push(poly[i + 1]);
|
||||
}
|
||||
out
|
||||
let Some(normal) = polygon::normal(poly) else {
|
||||
return Vec::new();
|
||||
};
|
||||
// Face normal via Newell's method (robust for near-planar polygons).
|
||||
let mut normal = [0.0f64; 3];
|
||||
for i in 0..n {
|
||||
let a = poly[i];
|
||||
let b = poly[(i + 1) % n];
|
||||
normal[0] += (a[1] - b[1]) * (a[2] + b[2]);
|
||||
normal[1] += (a[2] - b[2]) * (a[0] + b[0]);
|
||||
normal[2] += (a[0] - b[0]) * (a[1] + b[1]);
|
||||
}
|
||||
let nlen = dot(normal, normal).sqrt();
|
||||
if nlen < 1e-12 {
|
||||
return fan();
|
||||
}
|
||||
let normal = [normal[0] / nlen, normal[1] / nlen, normal[2] / nlen];
|
||||
// Orthonormal in-plane basis.
|
||||
let seed = if normal[0].abs() < 0.9 { [1.0, 0.0, 0.0] } else { [0.0, 1.0, 0.0] };
|
||||
let mut u = cross(seed, normal);
|
||||
let ul = dot(u, u).sqrt();
|
||||
if ul < 1e-12 {
|
||||
return fan();
|
||||
}
|
||||
u = [u[0] / ul, u[1] / ul, u[2] / ul];
|
||||
let v = cross(normal, u);
|
||||
let p2: Vec<[f64; 2]> = poly.iter().map(|&p| [dot(p, u), dot(p, v)]).collect();
|
||||
// Signed area → winding (CCW when positive in the (u, v) frame).
|
||||
let mut area = 0.0;
|
||||
for i in 0..n {
|
||||
let a = p2[i];
|
||||
let b = p2[(i + 1) % n];
|
||||
area += a[0] * b[1] - b[0] * a[1];
|
||||
}
|
||||
let ccw = area > 0.0;
|
||||
let tri_area2 = |a: [f64; 2], b: [f64; 2], c: [f64; 2]| {
|
||||
(b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0])
|
||||
let Some(plane) = Plane::orthonormal(poly[0], axis.to_array(), normal) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let in_tri = |p: [f64; 2], a: [f64; 2], b: [f64; 2], c: [f64; 2]| {
|
||||
let d1 = tri_area2(a, b, p);
|
||||
let d2 = tri_area2(b, c, p);
|
||||
let d3 = tri_area2(c, a, p);
|
||||
let neg = d1 < 0.0 || d2 < 0.0 || d3 < 0.0;
|
||||
let pos = d1 > 0.0 || d2 > 0.0 || d3 > 0.0;
|
||||
!(neg && pos)
|
||||
};
|
||||
let mut idx: Vec<usize> = (0..n).collect();
|
||||
let mut out: Vec<[f64; 3]> = Vec::with_capacity((n - 2) * 3);
|
||||
let mut guard = 0usize;
|
||||
while idx.len() > 3 && guard < n * n {
|
||||
guard += 1;
|
||||
let m = idx.len();
|
||||
let mut clipped = false;
|
||||
for k in 0..m {
|
||||
let i0 = idx[(k + m - 1) % m];
|
||||
let i1 = idx[k];
|
||||
let i2 = idx[(k + 1) % m];
|
||||
let (a, b, c) = (p2[i0], p2[i1], p2[i2]);
|
||||
let convex = if ccw { tri_area2(a, b, c) > 0.0 } else { tri_area2(a, b, c) < 0.0 };
|
||||
if !convex {
|
||||
continue;
|
||||
}
|
||||
let mut contains = false;
|
||||
for &j in &idx {
|
||||
if j == i0 || j == i1 || j == i2 {
|
||||
continue;
|
||||
}
|
||||
if in_tri(p2[j], a, b, c) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if contains {
|
||||
continue;
|
||||
}
|
||||
out.push(poly[i0]);
|
||||
out.push(poly[i1]);
|
||||
out.push(poly[i2]);
|
||||
idx.remove(k);
|
||||
clipped = true;
|
||||
break;
|
||||
}
|
||||
if !clipped {
|
||||
// No ear found (self-intersecting / numerically degenerate) — bail
|
||||
// to a fan of the remainder rather than loop forever.
|
||||
for i in 1..idx.len() - 1 {
|
||||
out.push(poly[idx[0]]);
|
||||
out.push(poly[idx[i]]);
|
||||
out.push(poly[idx[i + 1]]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
let projected: Vec<[f64; 2]> = poly
|
||||
.iter()
|
||||
.filter_map(|&point| plane.project(point))
|
||||
.collect();
|
||||
if projected.len() != poly.len() {
|
||||
return Vec::new();
|
||||
}
|
||||
if idx.len() == 3 {
|
||||
out.push(poly[idx[0]]);
|
||||
out.push(poly[idx[1]]);
|
||||
out.push(poly[idx[2]]);
|
||||
let (points, triangles) = triangulate(&projected, &[]);
|
||||
let source: Vec<usize> = points
|
||||
.iter()
|
||||
.filter_map(|point| projected.iter().position(|candidate| candidate == point))
|
||||
.collect();
|
||||
if source.len() != points.len() {
|
||||
return Vec::new();
|
||||
}
|
||||
out
|
||||
triangles
|
||||
.into_iter()
|
||||
.flat_map(|triangle| triangle.map(|index| poly[source[index]]))
|
||||
.collect()
|
||||
}
|
||||
|
||||
// ── Face3D ────────────────────────────────────────────────────────────────────
|
||||
@@ -177,32 +90,7 @@ fn face3d_fill(corners: [[f64; 3]; 4]) -> Vec<[f64; 3]> {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let Some(normal) = polygon::normal(&ring) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let axis = (KernelVec3::from(ring[1]) - KernelVec3::from(ring[0])).to_array();
|
||||
let Some(plane) = Plane::orthonormal(ring[0], axis, normal) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let projected: Vec<[f64; 2]> = ring
|
||||
.iter()
|
||||
.filter_map(|&point| plane.project(point))
|
||||
.collect();
|
||||
if projected.len() != ring.len() {
|
||||
return Vec::new();
|
||||
}
|
||||
let (points, triangles) = triangulate(&projected, &[]);
|
||||
let source: Vec<usize> = points
|
||||
.iter()
|
||||
.filter_map(|point| projected.iter().position(|candidate| candidate == point))
|
||||
.collect();
|
||||
if source.len() != points.len() {
|
||||
return Vec::new();
|
||||
}
|
||||
triangles
|
||||
.into_iter()
|
||||
.flat_map(|triangle| triangle.map(|index| ring[source[index]]))
|
||||
.collect()
|
||||
triangulate_planar(&ring)
|
||||
}
|
||||
|
||||
impl RenderConvertible for Face3D {
|
||||
@@ -241,15 +129,6 @@ impl RenderConvertible for Face3D {
|
||||
add_edge(p3, p0);
|
||||
}
|
||||
|
||||
if pts.is_empty() {
|
||||
// All edges invisible — show a tiny cross at centroid.
|
||||
let cx = (p0[0] + p1[0] + p2[0] + p3[0]) / 4.0;
|
||||
let cy = (p0[1] + p1[1] + p2[1] + p3[1]) / 4.0;
|
||||
let cz = (p0[2] + p1[2] + p2[2] + p3[2]) / 4.0;
|
||||
let s = 0.1_f64;
|
||||
pts = vec![[cx - s, cy, cz], [cx + s, cy, cz]];
|
||||
}
|
||||
|
||||
Some(RenderEntity {
|
||||
pick_tris: Vec::new(),
|
||||
object: RenderObject::Lines(pts),
|
||||
@@ -268,15 +147,22 @@ impl RenderConvertible for Face3D {
|
||||
|
||||
impl Grippable for Face3D {
|
||||
fn grips(&self) -> Vec<GripDef> {
|
||||
vec![
|
||||
let mut grips = vec![
|
||||
square_grip(0, dvec3(&self.first_corner)),
|
||||
square_grip(1, dvec3(&self.second_corner)),
|
||||
square_grip(2, dvec3(&self.third_corner)),
|
||||
square_grip(3, dvec3(&self.fourth_corner)),
|
||||
]
|
||||
];
|
||||
if !self.is_triangle() {
|
||||
grips.push(square_grip(3, dvec3(&self.fourth_corner)));
|
||||
}
|
||||
grips
|
||||
}
|
||||
|
||||
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
||||
let was_triangle = self.is_triangle();
|
||||
if was_triangle && grip_id == 3 {
|
||||
return;
|
||||
}
|
||||
let corner = match grip_id {
|
||||
0 => &mut self.first_corner,
|
||||
1 => &mut self.second_corner,
|
||||
@@ -284,69 +170,107 @@ impl Grippable for Face3D {
|
||||
3 => &mut self.fourth_corner,
|
||||
_ => return,
|
||||
};
|
||||
match apply {
|
||||
GripApply::Translate(d) => {
|
||||
corner.x += d.x as f64;
|
||||
corner.y += d.y as f64;
|
||||
corner.z += d.z as f64;
|
||||
}
|
||||
GripApply::Absolute(p) => {
|
||||
corner.x = p.x as f64;
|
||||
corner.y = p.y as f64;
|
||||
corner.z = p.z as f64;
|
||||
}
|
||||
let current = dvec3(corner);
|
||||
let updated = match apply {
|
||||
GripApply::Translate(delta) => current + delta,
|
||||
GripApply::Absolute(position) => position,
|
||||
};
|
||||
if !updated.is_finite() {
|
||||
return;
|
||||
}
|
||||
corner.x = updated.x;
|
||||
corner.y = updated.y;
|
||||
corner.z = updated.z;
|
||||
if was_triangle && grip_id == 2 {
|
||||
self.fourth_corner = self.third_corner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PropertyEditable for Face3D {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||
use crate::entities::common::edit_prop as edit;
|
||||
let was_triangle = self.is_triangle();
|
||||
let vertex_count = if was_triangle { 3 } else { 4 };
|
||||
let current = crate::scene::view::dispatch::prop_current_vertex()
|
||||
.min(vertex_count - 1);
|
||||
let corner = match current {
|
||||
0 => &self.first_corner,
|
||||
1 => &self.second_corner,
|
||||
2 => &self.third_corner,
|
||||
_ => &self.fourth_corner,
|
||||
};
|
||||
let inv = self.invisible_edges;
|
||||
let edge = |hidden: bool| if hidden { "Invisible" } else { "Visible" };
|
||||
let edge = |label: &str, field: &'static str, hidden: bool| Property {
|
||||
label: label.to_string(),
|
||||
field,
|
||||
value: PropValue::Choice {
|
||||
selected: if hidden {
|
||||
t!("Invisible").into_owned()
|
||||
} else {
|
||||
t!("Visible").into_owned()
|
||||
},
|
||||
options: vec![t!("Visible").into_owned(), t!("Invisible").into_owned()],
|
||||
},
|
||||
};
|
||||
vec![PropSection {
|
||||
title: t!("Geometry").into_owned(),
|
||||
props: vec![
|
||||
ro(t!("Current vertex").as_ref(), "f3_current", String::new()),
|
||||
edit(t!("Vertex 1 X").as_ref(), "f3_p1x", self.first_corner.x),
|
||||
edit(t!("Vertex 1 Y").as_ref(), "f3_p1y", self.first_corner.y),
|
||||
edit(t!("Vertex 1 Z").as_ref(), "f3_p1z", self.first_corner.z),
|
||||
edit(t!("Vertex 2 X").as_ref(), "f3_p2x", self.second_corner.x),
|
||||
edit(t!("Vertex 2 Y").as_ref(), "f3_p2y", self.second_corner.y),
|
||||
edit(t!("Vertex 2 Z").as_ref(), "f3_p2z", self.second_corner.z),
|
||||
edit(t!("Vertex 3 X").as_ref(), "f3_p3x", self.third_corner.x),
|
||||
edit(t!("Vertex 3 Y").as_ref(), "f3_p3y", self.third_corner.y),
|
||||
edit(t!("Vertex 3 Z").as_ref(), "f3_p3z", self.third_corner.z),
|
||||
edit(t!("Vertex 4 X").as_ref(), "f3_p4x", self.fourth_corner.x),
|
||||
edit(t!("Vertex 4 Y").as_ref(), "f3_p4y", self.fourth_corner.y),
|
||||
edit(t!("Vertex 4 Z").as_ref(), "f3_p4z", self.fourth_corner.z),
|
||||
ro(t!("Edge 1").as_ref(), "f3_edge1", edge(inv.is_first_invisible())),
|
||||
ro(t!("Edge 2").as_ref(), "f3_edge2", edge(inv.is_second_invisible())),
|
||||
ro(t!("Edge 3").as_ref(), "f3_edge3", edge(inv.is_third_invisible())),
|
||||
ro(t!("Edge 4").as_ref(), "f3_edge4", edge(inv.is_fourth_invisible())),
|
||||
stepper(
|
||||
t!("Current vertex").as_ref(),
|
||||
"current_vertex",
|
||||
format!("{}", current + 1),
|
||||
),
|
||||
edit(t!("Vertex X").as_ref(), "f3_vertex_x", corner.x),
|
||||
edit(t!("Vertex Y").as_ref(), "f3_vertex_y", corner.y),
|
||||
edit(t!("Vertex Z").as_ref(), "f3_vertex_z", corner.z),
|
||||
edge(t!("Edge 1").as_ref(), "f3_edge1", inv.is_first_invisible()),
|
||||
edge(t!("Edge 2").as_ref(), "f3_edge2", inv.is_second_invisible()),
|
||||
edge(t!("Edge 3").as_ref(), "f3_edge3", inv.is_third_invisible()),
|
||||
edge(t!("Edge 4").as_ref(), "f3_edge4", inv.is_fourth_invisible()),
|
||||
],
|
||||
}]
|
||||
}
|
||||
|
||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||
let Ok(v) = value.trim().parse::<f64>() else {
|
||||
if field.starts_with("f3_edge") {
|
||||
let invisible = if value == t!("Invisible").as_ref() {
|
||||
true
|
||||
} else if value == t!("Visible").as_ref() {
|
||||
false
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
match field {
|
||||
"f3_edge1" => self.invisible_edges.set_first_invisible(invisible),
|
||||
"f3_edge2" => self.invisible_edges.set_second_invisible(invisible),
|
||||
"f3_edge3" => self.invisible_edges.set_third_invisible(invisible),
|
||||
"f3_edge4" => self.invisible_edges.set_fourth_invisible(invisible),
|
||||
_ => {}
|
||||
}
|
||||
return;
|
||||
}
|
||||
let Some(value) = parse_f64(value).filter(|value| value.is_finite()) else {
|
||||
return;
|
||||
};
|
||||
let was_triangle = self.is_triangle();
|
||||
let vertex_count = if was_triangle { 3 } else { 4 };
|
||||
let current = crate::scene::view::dispatch::prop_current_vertex()
|
||||
.min(vertex_count - 1);
|
||||
let corner = match current {
|
||||
0 => &mut self.first_corner,
|
||||
1 => &mut self.second_corner,
|
||||
2 => &mut self.third_corner,
|
||||
_ => &mut self.fourth_corner,
|
||||
};
|
||||
match field {
|
||||
"f3_p1x" => self.first_corner.x = v,
|
||||
"f3_p1y" => self.first_corner.y = v,
|
||||
"f3_p1z" => self.first_corner.z = v,
|
||||
"f3_p2x" => self.second_corner.x = v,
|
||||
"f3_p2y" => self.second_corner.y = v,
|
||||
"f3_p2z" => self.second_corner.z = v,
|
||||
"f3_p3x" => self.third_corner.x = v,
|
||||
"f3_p3y" => self.third_corner.y = v,
|
||||
"f3_p3z" => self.third_corner.z = v,
|
||||
"f3_p4x" => self.fourth_corner.x = v,
|
||||
"f3_p4y" => self.fourth_corner.y = v,
|
||||
"f3_p4z" => self.fourth_corner.z = v,
|
||||
"f3_vertex_x" => corner.x = value,
|
||||
"f3_vertex_y" => corner.y = value,
|
||||
"f3_vertex_z" => corner.z = value,
|
||||
_ => {}
|
||||
}
|
||||
if was_triangle && current == 2 {
|
||||
self.fourth_corner = self.third_corner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,16 +317,13 @@ fn polygon_mesh_display(mesh: &PolygonMesh) -> (Vec<[f64; 3]>, usize, usize) {
|
||||
return (base, m, n);
|
||||
}
|
||||
let control_points: Vec<Vec<[f64; 3]>> = base.chunks(n).map(|row| row.to_vec()).collect();
|
||||
let Some(surface) = NurbsSurface3::new(
|
||||
let Some(surface) = NurbsSurface3::from_control_net(
|
||||
u_degree,
|
||||
v_degree,
|
||||
control_points,
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
None,
|
||||
)
|
||||
.map(|surface| surface.with_periodicity(mesh.is_closed_m(), mesh.is_closed_n()))
|
||||
else {
|
||||
mesh.is_closed_m(),
|
||||
mesh.is_closed_n(),
|
||||
) else {
|
||||
return (base, m, n);
|
||||
};
|
||||
|
||||
@@ -499,18 +420,17 @@ impl Grippable for PolygonMesh {
|
||||
|
||||
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
||||
if let Some(v) = self.vertices.get_mut(grip_id) {
|
||||
match apply {
|
||||
GripApply::Translate(d) => {
|
||||
v.location.x += d.x as f64;
|
||||
v.location.y += d.y as f64;
|
||||
v.location.z += d.z as f64;
|
||||
}
|
||||
GripApply::Absolute(p) => {
|
||||
v.location.x = p.x as f64;
|
||||
v.location.y = p.y as f64;
|
||||
v.location.z = p.z as f64;
|
||||
}
|
||||
let current = glam::DVec3::new(v.location.x, v.location.y, v.location.z);
|
||||
let updated = match apply {
|
||||
GripApply::Translate(delta) => current + delta,
|
||||
GripApply::Absolute(position) => position,
|
||||
};
|
||||
if !updated.is_finite() {
|
||||
return;
|
||||
}
|
||||
v.location.x = updated.x;
|
||||
v.location.y = updated.y;
|
||||
v.location.z = updated.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -537,7 +457,7 @@ impl PropertyEditable for PolygonMesh {
|
||||
let smoothed = self.smooth_type != SurfaceSmoothType::NoSmooth;
|
||||
let density_prop = |label: &str, field: &'static str, value: i16| {
|
||||
if smoothed {
|
||||
edit_scalar_prop(label, field, value as f64)
|
||||
edit_scalar(label, field, value as f64)
|
||||
} else {
|
||||
ro(label, field, value.to_string())
|
||||
}
|
||||
@@ -546,16 +466,16 @@ impl PropertyEditable for PolygonMesh {
|
||||
PropSection {
|
||||
title: t!("Geometry").into_owned(),
|
||||
props: vec![
|
||||
stepper_prop(t!("Current Vertex").as_ref(), "pm_current_vertex", display),
|
||||
edit_prop(t!("Vertex X").as_ref(),
|
||||
stepper(t!("Current Vertex").as_ref(), "pm_current_vertex", display),
|
||||
edit(t!("Vertex X").as_ref(),
|
||||
"pm_vx",
|
||||
vertex.map_or(0.0, |vertex| vertex.location.x),
|
||||
),
|
||||
edit_prop(t!("Vertex Y").as_ref(),
|
||||
edit(t!("Vertex Y").as_ref(),
|
||||
"pm_vy",
|
||||
vertex.map_or(0.0, |vertex| vertex.location.y),
|
||||
),
|
||||
edit_prop(t!("Vertex Z").as_ref(),
|
||||
edit(t!("Vertex Z").as_ref(),
|
||||
"pm_vz",
|
||||
vertex.map_or(0.0, |vertex| vertex.location.z),
|
||||
),
|
||||
@@ -605,48 +525,64 @@ impl PropertyEditable for PolygonMesh {
|
||||
|
||||
let bool_value = |current: bool| {
|
||||
if value == "toggle" {
|
||||
!current
|
||||
Some(!current)
|
||||
} else if value.eq_ignore_ascii_case("true")
|
||||
|| value.eq_ignore_ascii_case("yes")
|
||||
{
|
||||
Some(true)
|
||||
} else if value.eq_ignore_ascii_case("false")
|
||||
|| value.eq_ignore_ascii_case("no")
|
||||
{
|
||||
Some(false)
|
||||
} else {
|
||||
value.eq_ignore_ascii_case("true") || value.eq_ignore_ascii_case("yes")
|
||||
None
|
||||
}
|
||||
};
|
||||
match field {
|
||||
"pm_closed_m" => self.flags.set(
|
||||
PolygonMeshFlags::CLOSED_M,
|
||||
bool_value(self.is_closed_m()),
|
||||
),
|
||||
"pm_closed_n" => self.flags.set(
|
||||
PolygonMeshFlags::CLOSED_N,
|
||||
bool_value(self.is_closed_n()),
|
||||
),
|
||||
"pm_closed_m" => {
|
||||
if let Some(closed) = bool_value(self.is_closed_m()) {
|
||||
self.flags.set(PolygonMeshFlags::CLOSED_M, closed);
|
||||
}
|
||||
}
|
||||
"pm_closed_n" => {
|
||||
if let Some(closed) = bool_value(self.is_closed_n()) {
|
||||
self.flags.set(PolygonMeshFlags::CLOSED_N, closed);
|
||||
}
|
||||
}
|
||||
"pm_smooth" => {
|
||||
self.smooth_type = match value.trim().to_ascii_uppercase().as_str() {
|
||||
let smooth = match value.trim().to_ascii_uppercase().as_str() {
|
||||
"NONE" => SurfaceSmoothType::NoSmooth,
|
||||
"QUADRATIC" => SurfaceSmoothType::Quadratic,
|
||||
"CUBIC" => SurfaceSmoothType::Cubic,
|
||||
"BEZIER" => SurfaceSmoothType::Bezier,
|
||||
_ => SurfaceSmoothType::NoSmooth,
|
||||
_ => return,
|
||||
};
|
||||
self.smooth_type = smooth;
|
||||
if self.smooth_type != SurfaceSmoothType::NoSmooth {
|
||||
if self.m_smooth_density < 2 {
|
||||
if !(2..=256).contains(&self.m_smooth_density) {
|
||||
self.m_smooth_density = 6;
|
||||
}
|
||||
if self.n_smooth_density < 2 {
|
||||
if !(2..=256).contains(&self.n_smooth_density) {
|
||||
self.n_smooth_density = 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
"pm_smooth_m" => {
|
||||
if let Ok(density @ 2..=256) = value.trim().parse::<i16>() {
|
||||
self.m_smooth_density = density;
|
||||
if self.smooth_type != SurfaceSmoothType::NoSmooth {
|
||||
if let Ok(density @ 2..=256) = value.trim().parse::<i16>() {
|
||||
self.m_smooth_density = density;
|
||||
}
|
||||
}
|
||||
}
|
||||
"pm_smooth_n" => {
|
||||
if let Ok(density @ 2..=256) = value.trim().parse::<i16>() {
|
||||
self.n_smooth_density = density;
|
||||
if self.smooth_type != SurfaceSmoothType::NoSmooth {
|
||||
if let Ok(density @ 2..=256) = value.trim().parse::<i16>() {
|
||||
self.n_smooth_density = density;
|
||||
}
|
||||
}
|
||||
}
|
||||
"pm_vx" | "pm_vy" | "pm_vz" => {
|
||||
let Some(number) = parse_f64(value) else {
|
||||
let Some(number) = parse_f64(value).filter(|number| number.is_finite()) else {
|
||||
return;
|
||||
};
|
||||
let index = crate::scene::view::dispatch::prop_current_vertex()
|
||||
|
||||
Reference in new issue
Block a user