forked from 1b-agent/opencadstudio-upstream
Complete spline fit and control grip modes
This commit is contained in:
1 parent
6260723937
commit
5497f455ef
4 files changed
+152
-27
No files matched your search
@@ -1723,15 +1723,13 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
.get_entity(popup.handle)
|
||||
.is_some_and(|entity| match (item.action, entity) {
|
||||
(GripMenuAction::ShowFit, acadrust::EntityType::Spline(spline)) => {
|
||||
!spline.cv_frame_visible
|
||||
&& crate::entities::spline::uses_fit_method(spline)
|
||||
crate::entities::spline::shows_fit_points(spline)
|
||||
}
|
||||
(
|
||||
GripMenuAction::ShowControlVertices,
|
||||
acadrust::EntityType::Spline(spline),
|
||||
) => {
|
||||
spline.cv_frame_visible
|
||||
|| !crate::entities::spline::uses_fit_method(spline)
|
||||
crate::entities::spline::shows_control_vertices(spline)
|
||||
}
|
||||
_ => false,
|
||||
});
|
||||
|
||||
+2
-1
@@ -591,7 +591,8 @@ bg={bg_ms:.1}ms n={view_count}"
|
||||
};
|
||||
let control_polygon = tab.selected_handle.and_then(|handle| {
|
||||
let spline = match tab.scene.document.get_entity(handle) {
|
||||
Some(acadrust::EntityType::Spline(spline)) if spline.cv_frame_visible => spline,
|
||||
Some(acadrust::EntityType::Spline(spline))
|
||||
if crate::entities::spline::shows_control_vertices(spline) => spline,
|
||||
_ => return None,
|
||||
};
|
||||
if tab
|
||||
|
||||
+134
-19
@@ -18,7 +18,13 @@ pub(crate) fn uses_fit_method(spline: &Spline) -> bool {
|
||||
}
|
||||
|
||||
pub(crate) fn shows_fit_points(spline: &Spline) -> bool {
|
||||
uses_fit_method(spline) && (!spline.cv_frame_visible || spline.flags.periodic)
|
||||
!spline.fit_points.is_empty() && (!spline.cv_frame_visible || spline.flags.periodic)
|
||||
}
|
||||
|
||||
pub(crate) fn shows_control_vertices(spline: &Spline) -> bool {
|
||||
!shows_fit_points(spline)
|
||||
&& !(uses_fit_method(spline) && spline.flags.periodic)
|
||||
&& control_vertices(spline).len() >= 2
|
||||
}
|
||||
|
||||
pub(crate) fn fit_nurbs3(spline: &Spline) -> Option<NurbsCurve3> {
|
||||
@@ -105,14 +111,24 @@ pub(crate) fn spline_knot_points(spline: &Spline) -> Vec<glam::DVec3> {
|
||||
}
|
||||
|
||||
pub(crate) fn replace_with_nurbs(spline: &mut Spline, curve: &NurbsCurve3) {
|
||||
let mut fit_points = spline.fit_points.clone();
|
||||
spline.degree = curve.degree() as i32;
|
||||
spline.knots = curve.knots().to_vec();
|
||||
spline.control_points = curve.control_points().iter()
|
||||
.map(|point| acadrust::types::Vector3::new(point[0], point[1], point[2])).collect();
|
||||
spline.weights = curve.weights().to_vec();
|
||||
spline.fit_points.clear();
|
||||
if !fit_points.is_empty() {
|
||||
if let (Some(first), Some(point)) = (fit_points.first_mut(), spline.control_points.first()) {
|
||||
*first = *point;
|
||||
}
|
||||
if let (Some(last), Some(point)) = (fit_points.last_mut(), spline.control_points.last()) {
|
||||
*last = *point;
|
||||
}
|
||||
}
|
||||
spline.fit_points = fit_points;
|
||||
spline.begin_tangent = acadrust::types::Vector3::ZERO;
|
||||
spline.end_tangent = acadrust::types::Vector3::ZERO;
|
||||
spline.cv_frame_visible = true;
|
||||
spline.flags.rational = spline.weights.windows(2)
|
||||
.any(|pair| (pair[0] - pair[1]).abs() > 1e-12);
|
||||
spline.flags.closed = false;
|
||||
@@ -615,7 +631,15 @@ fn index_prop(label: &str, field: &'static str, index: usize, count: usize) -> P
|
||||
|
||||
fn convert_to_control_method(spline: &mut Spline) -> bool {
|
||||
if !uses_fit_method(spline) {
|
||||
return true;
|
||||
let valid = spline.control_points.len() >= 2;
|
||||
if valid {
|
||||
spline.fit_points.clear();
|
||||
spline.begin_tangent = acadrust::types::Vector3::ZERO;
|
||||
spline.end_tangent = acadrust::types::Vector3::ZERO;
|
||||
spline.dwg_flags1 &= !1;
|
||||
spline.dxf_flags &= !(32 | 1024);
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
let Some(planar) = crate::entities::curve::spline_curve(spline) else {
|
||||
return false;
|
||||
@@ -647,18 +671,74 @@ fn convert_to_control_method(spline: &mut Spline) -> bool {
|
||||
}
|
||||
|
||||
fn convert_to_fit_method(spline: &mut Spline) -> bool {
|
||||
uses_fit_method(spline)
|
||||
if uses_fit_method(spline) {
|
||||
return true;
|
||||
}
|
||||
if spline.fit_points.len() < 2 {
|
||||
return false;
|
||||
}
|
||||
spline.control_points.clear();
|
||||
spline.knots.clear();
|
||||
spline.weights.clear();
|
||||
spline.flags.rational = false;
|
||||
spline.dwg_flags1 |= 1;
|
||||
spline.dxf_flags |= 32 | 1024;
|
||||
true
|
||||
}
|
||||
|
||||
fn derived_fit_points(spline: &Spline) -> Vec<acadrust::types::Vector3> {
|
||||
if spline.flags.closed || spline.flags.periodic {
|
||||
return Vec::new();
|
||||
}
|
||||
let Some(curve) = nurbs3(spline) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let (start, end) = curve.domain();
|
||||
if !(start.is_finite() && end.is_finite() && end > start) {
|
||||
return Vec::new();
|
||||
}
|
||||
let tolerance = (end - start).abs().max(1.0) * 1.0e-12;
|
||||
let mut parameters = vec![start];
|
||||
for knot in curve.knots().iter().copied() {
|
||||
if knot > start + tolerance
|
||||
&& knot < end - tolerance
|
||||
&& parameters
|
||||
.last()
|
||||
.is_none_or(|previous| (knot - *previous).abs() > tolerance)
|
||||
{
|
||||
parameters.push(knot);
|
||||
}
|
||||
}
|
||||
parameters.push(end);
|
||||
parameters
|
||||
.into_iter()
|
||||
.map(|parameter| {
|
||||
let point = curve.point_at_knot(parameter);
|
||||
acadrust::types::Vector3::new(point[0], point[1], point[2])
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn can_show_fit_points(spline: &Spline) -> bool {
|
||||
spline.fit_points.len() >= 2 || derived_fit_points(spline).len() >= 2
|
||||
}
|
||||
|
||||
fn prepare_fit_point_view(spline: &mut Spline) -> bool {
|
||||
if spline.fit_points.len() < 2 {
|
||||
spline.fit_points = derived_fit_points(spline);
|
||||
}
|
||||
spline.fit_points.len() >= 2
|
||||
}
|
||||
|
||||
fn grips(spline: &Spline) -> Vec<GripDef> {
|
||||
let fit_method = uses_fit_method(spline);
|
||||
let derived_control = control_vertices(spline);
|
||||
let show_control_vertices = (spline.cv_frame_visible || !fit_method)
|
||||
&& !(fit_method && spline.flags.periodic)
|
||||
let show_fit = shows_fit_points(spline);
|
||||
let show_control_vertices = !show_fit
|
||||
&& !(uses_fit_method(spline) && spline.flags.periodic)
|
||||
&& derived_control.len() >= 2;
|
||||
let source = if show_control_vertices {
|
||||
derived_control
|
||||
} else if fit_method {
|
||||
} else if show_fit {
|
||||
spline.fit_points.clone()
|
||||
} else {
|
||||
spline.control_points.clone()
|
||||
@@ -885,7 +965,7 @@ fn apply_geom_prop(spline: &mut Spline, field: &str, value: &str) {
|
||||
match field {
|
||||
"spline_method" => {
|
||||
if value == "Fit" {
|
||||
if convert_to_fit_method(spline) {
|
||||
if prepare_fit_point_view(spline) {
|
||||
spline.cv_frame_visible = false;
|
||||
}
|
||||
} else if value == "Control Vertices" {
|
||||
@@ -896,6 +976,12 @@ fn apply_geom_prop(spline: &mut Spline, field: &str, value: &str) {
|
||||
return;
|
||||
}
|
||||
"knot_param" => {
|
||||
if shows_fit_points(spline)
|
||||
&& !uses_fit_method(spline)
|
||||
&& !convert_to_fit_method(spline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
spline.knot_parameterization = match value {
|
||||
"Chord" => 0,
|
||||
"Square Root" => 1,
|
||||
@@ -930,9 +1016,30 @@ fn apply_geom_prop(spline: &mut Spline, field: &str, value: &str) {
|
||||
{
|
||||
return;
|
||||
}
|
||||
if matches!(field, "ctrl_pt_x" | "ctrl_pt_y" | "ctrl_pt_z" | "weight")
|
||||
&& uses_fit_method(spline)
|
||||
&& !convert_to_control_method(spline)
|
||||
if matches!(field, "ctrl_pt_x" | "ctrl_pt_y" | "ctrl_pt_z" | "weight") {
|
||||
if !shows_fit_points(spline) && !spline.fit_points.is_empty() {
|
||||
if !convert_to_control_method(spline) {
|
||||
return;
|
||||
}
|
||||
} else if uses_fit_method(spline) && !convert_to_control_method(spline) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if shows_fit_points(spline)
|
||||
&& !uses_fit_method(spline)
|
||||
&& matches!(
|
||||
field,
|
||||
"fit_pt_x"
|
||||
| "fit_pt_y"
|
||||
| "fit_pt_z"
|
||||
| "start_tan_x"
|
||||
| "start_tan_y"
|
||||
| "start_tan_z"
|
||||
| "end_tan_x"
|
||||
| "end_tan_y"
|
||||
| "end_tan_z"
|
||||
)
|
||||
&& !convert_to_fit_method(spline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1015,16 +1122,23 @@ fn apply_grip(spline: &mut Spline, grip_id: usize, apply: GripApply) {
|
||||
return;
|
||||
}
|
||||
// First CV edit makes the displayed kernel controls authoritative.
|
||||
let fit_method = uses_fit_method(spline);
|
||||
if spline.cv_frame_visible && fit_method && !spline.flags.periodic {
|
||||
let editing_fit = shows_fit_points(spline);
|
||||
if editing_fit && !uses_fit_method(spline) {
|
||||
if !convert_to_fit_method(spline) {
|
||||
return;
|
||||
}
|
||||
} else if !editing_fit
|
||||
&& (uses_fit_method(spline) || !spline.fit_points.is_empty())
|
||||
&& !spline.flags.periodic
|
||||
{
|
||||
if !convert_to_control_method(spline) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let target = if (spline.cv_frame_visible && !spline.flags.periodic) || !fit_method {
|
||||
spline.control_points.get_mut(grip_id)
|
||||
} else {
|
||||
let target = if editing_fit {
|
||||
spline.fit_points.get_mut(grip_id)
|
||||
} else {
|
||||
spline.control_points.get_mut(grip_id)
|
||||
};
|
||||
if let Some(cp) = target {
|
||||
match apply {
|
||||
@@ -1088,9 +1202,10 @@ impl crate::entities::traits::Grippable for Spline {
|
||||
let fit_method = uses_fit_method(self);
|
||||
let can_show_control = control_vertices(self).len() >= 2
|
||||
&& !(fit_method && self.flags.periodic);
|
||||
let can_show_fit = can_show_fit_points(self);
|
||||
return if !shows_fit_points(self) {
|
||||
vec![
|
||||
(can_show_control && fit_method).then_some(GripMenuItem {
|
||||
can_show_fit.then_some(GripMenuItem {
|
||||
label: "Fit",
|
||||
action: GripMenuAction::ShowFit,
|
||||
}),
|
||||
@@ -1131,7 +1246,7 @@ impl crate::entities::traits::Grippable for Spline {
|
||||
use crate::scene::model::object::GripMenuAction as A;
|
||||
match action {
|
||||
A::ShowFit => {
|
||||
if convert_to_fit_method(self) {
|
||||
if prepare_fit_point_view(self) {
|
||||
self.cv_frame_visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2961,8 +2961,8 @@ fn solve_scope(
|
||||
}
|
||||
}
|
||||
(EntityType::Spline(spline), EntityGeom::Spline { curve, control_z }) => {
|
||||
let mut changed =
|
||||
spline.fit_points.len() > 0 || spline.control_points.len() != curve.poles.len();
|
||||
let mut changed = crate::entities::spline::uses_fit_method(spline)
|
||||
|| spline.control_points.len() != curve.poles.len();
|
||||
let control_points: Vec<_> = curve
|
||||
.poles
|
||||
.iter()
|
||||
@@ -2983,7 +2983,18 @@ fn solve_scope(
|
||||
updated.knots = curve.knots.clone();
|
||||
updated.control_points = control_points;
|
||||
updated.weights = curve.weights.iter().map(|id| store.get(*id)).collect();
|
||||
updated.fit_points.clear();
|
||||
if !updated.fit_points.is_empty() {
|
||||
if let (Some(first), Some(point)) =
|
||||
(updated.fit_points.first_mut(), updated.control_points.first())
|
||||
{
|
||||
*first = *point;
|
||||
}
|
||||
if let (Some(last), Some(point)) =
|
||||
(updated.fit_points.last_mut(), updated.control_points.last())
|
||||
{
|
||||
*last = *point;
|
||||
}
|
||||
}
|
||||
updated.begin_tangent = Vector3::ZERO;
|
||||
updated.end_tangent = Vector3::ZERO;
|
||||
updated.flags.rational = updated
|
||||
|
||||
Reference in new issue
Block a user