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)
|
.get_entity(popup.handle)
|
||||||
.is_some_and(|entity| match (item.action, entity) {
|
.is_some_and(|entity| match (item.action, entity) {
|
||||||
(GripMenuAction::ShowFit, acadrust::EntityType::Spline(spline)) => {
|
(GripMenuAction::ShowFit, acadrust::EntityType::Spline(spline)) => {
|
||||||
!spline.cv_frame_visible
|
crate::entities::spline::shows_fit_points(spline)
|
||||||
&& crate::entities::spline::uses_fit_method(spline)
|
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
GripMenuAction::ShowControlVertices,
|
GripMenuAction::ShowControlVertices,
|
||||||
acadrust::EntityType::Spline(spline),
|
acadrust::EntityType::Spline(spline),
|
||||||
) => {
|
) => {
|
||||||
spline.cv_frame_visible
|
crate::entities::spline::shows_control_vertices(spline)
|
||||||
|| !crate::entities::spline::uses_fit_method(spline)
|
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => 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 control_polygon = tab.selected_handle.and_then(|handle| {
|
||||||
let spline = match tab.scene.document.get_entity(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,
|
_ => return None,
|
||||||
};
|
};
|
||||||
if tab
|
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 {
|
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> {
|
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) {
|
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.degree = curve.degree() as i32;
|
||||||
spline.knots = curve.knots().to_vec();
|
spline.knots = curve.knots().to_vec();
|
||||||
spline.control_points = curve.control_points().iter()
|
spline.control_points = curve.control_points().iter()
|
||||||
.map(|point| acadrust::types::Vector3::new(point[0], point[1], point[2])).collect();
|
.map(|point| acadrust::types::Vector3::new(point[0], point[1], point[2])).collect();
|
||||||
spline.weights = curve.weights().to_vec();
|
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.begin_tangent = acadrust::types::Vector3::ZERO;
|
||||||
spline.end_tangent = acadrust::types::Vector3::ZERO;
|
spline.end_tangent = acadrust::types::Vector3::ZERO;
|
||||||
|
spline.cv_frame_visible = true;
|
||||||
spline.flags.rational = spline.weights.windows(2)
|
spline.flags.rational = spline.weights.windows(2)
|
||||||
.any(|pair| (pair[0] - pair[1]).abs() > 1e-12);
|
.any(|pair| (pair[0] - pair[1]).abs() > 1e-12);
|
||||||
spline.flags.closed = false;
|
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 {
|
fn convert_to_control_method(spline: &mut Spline) -> bool {
|
||||||
if !uses_fit_method(spline) {
|
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 {
|
let Some(planar) = crate::entities::curve::spline_curve(spline) else {
|
||||||
return false;
|
return false;
|
||||||
@@ -647,18 +671,74 @@ fn convert_to_control_method(spline: &mut Spline) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn convert_to_fit_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> {
|
fn grips(spline: &Spline) -> Vec<GripDef> {
|
||||||
let fit_method = uses_fit_method(spline);
|
|
||||||
let derived_control = control_vertices(spline);
|
let derived_control = control_vertices(spline);
|
||||||
let show_control_vertices = (spline.cv_frame_visible || !fit_method)
|
let show_fit = shows_fit_points(spline);
|
||||||
&& !(fit_method && spline.flags.periodic)
|
let show_control_vertices = !show_fit
|
||||||
|
&& !(uses_fit_method(spline) && spline.flags.periodic)
|
||||||
&& derived_control.len() >= 2;
|
&& derived_control.len() >= 2;
|
||||||
let source = if show_control_vertices {
|
let source = if show_control_vertices {
|
||||||
derived_control
|
derived_control
|
||||||
} else if fit_method {
|
} else if show_fit {
|
||||||
spline.fit_points.clone()
|
spline.fit_points.clone()
|
||||||
} else {
|
} else {
|
||||||
spline.control_points.clone()
|
spline.control_points.clone()
|
||||||
@@ -885,7 +965,7 @@ fn apply_geom_prop(spline: &mut Spline, field: &str, value: &str) {
|
|||||||
match field {
|
match field {
|
||||||
"spline_method" => {
|
"spline_method" => {
|
||||||
if value == "Fit" {
|
if value == "Fit" {
|
||||||
if convert_to_fit_method(spline) {
|
if prepare_fit_point_view(spline) {
|
||||||
spline.cv_frame_visible = false;
|
spline.cv_frame_visible = false;
|
||||||
}
|
}
|
||||||
} else if value == "Control Vertices" {
|
} else if value == "Control Vertices" {
|
||||||
@@ -896,6 +976,12 @@ fn apply_geom_prop(spline: &mut Spline, field: &str, value: &str) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
"knot_param" => {
|
"knot_param" => {
|
||||||
|
if shows_fit_points(spline)
|
||||||
|
&& !uses_fit_method(spline)
|
||||||
|
&& !convert_to_fit_method(spline)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
spline.knot_parameterization = match value {
|
spline.knot_parameterization = match value {
|
||||||
"Chord" => 0,
|
"Chord" => 0,
|
||||||
"Square Root" => 1,
|
"Square Root" => 1,
|
||||||
@@ -930,9 +1016,30 @@ fn apply_geom_prop(spline: &mut Spline, field: &str, value: &str) {
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if matches!(field, "ctrl_pt_x" | "ctrl_pt_y" | "ctrl_pt_z" | "weight")
|
if matches!(field, "ctrl_pt_x" | "ctrl_pt_y" | "ctrl_pt_z" | "weight") {
|
||||||
&& uses_fit_method(spline)
|
if !shows_fit_points(spline) && !spline.fit_points.is_empty() {
|
||||||
&& !convert_to_control_method(spline)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1015,16 +1122,23 @@ fn apply_grip(spline: &mut Spline, grip_id: usize, apply: GripApply) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// First CV edit makes the displayed kernel controls authoritative.
|
// First CV edit makes the displayed kernel controls authoritative.
|
||||||
let fit_method = uses_fit_method(spline);
|
let editing_fit = shows_fit_points(spline);
|
||||||
if spline.cv_frame_visible && fit_method && !spline.flags.periodic {
|
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) {
|
if !convert_to_control_method(spline) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let target = if (spline.cv_frame_visible && !spline.flags.periodic) || !fit_method {
|
let target = if editing_fit {
|
||||||
spline.control_points.get_mut(grip_id)
|
|
||||||
} else {
|
|
||||||
spline.fit_points.get_mut(grip_id)
|
spline.fit_points.get_mut(grip_id)
|
||||||
|
} else {
|
||||||
|
spline.control_points.get_mut(grip_id)
|
||||||
};
|
};
|
||||||
if let Some(cp) = target {
|
if let Some(cp) = target {
|
||||||
match apply {
|
match apply {
|
||||||
@@ -1088,9 +1202,10 @@ impl crate::entities::traits::Grippable for Spline {
|
|||||||
let fit_method = uses_fit_method(self);
|
let fit_method = uses_fit_method(self);
|
||||||
let can_show_control = control_vertices(self).len() >= 2
|
let can_show_control = control_vertices(self).len() >= 2
|
||||||
&& !(fit_method && self.flags.periodic);
|
&& !(fit_method && self.flags.periodic);
|
||||||
|
let can_show_fit = can_show_fit_points(self);
|
||||||
return if !shows_fit_points(self) {
|
return if !shows_fit_points(self) {
|
||||||
vec![
|
vec![
|
||||||
(can_show_control && fit_method).then_some(GripMenuItem {
|
can_show_fit.then_some(GripMenuItem {
|
||||||
label: "Fit",
|
label: "Fit",
|
||||||
action: GripMenuAction::ShowFit,
|
action: GripMenuAction::ShowFit,
|
||||||
}),
|
}),
|
||||||
@@ -1131,7 +1246,7 @@ impl crate::entities::traits::Grippable for Spline {
|
|||||||
use crate::scene::model::object::GripMenuAction as A;
|
use crate::scene::model::object::GripMenuAction as A;
|
||||||
match action {
|
match action {
|
||||||
A::ShowFit => {
|
A::ShowFit => {
|
||||||
if convert_to_fit_method(self) {
|
if prepare_fit_point_view(self) {
|
||||||
self.cv_frame_visible = false;
|
self.cv_frame_visible = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2961,8 +2961,8 @@ fn solve_scope(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(EntityType::Spline(spline), EntityGeom::Spline { curve, control_z }) => {
|
(EntityType::Spline(spline), EntityGeom::Spline { curve, control_z }) => {
|
||||||
let mut changed =
|
let mut changed = crate::entities::spline::uses_fit_method(spline)
|
||||||
spline.fit_points.len() > 0 || spline.control_points.len() != curve.poles.len();
|
|| spline.control_points.len() != curve.poles.len();
|
||||||
let control_points: Vec<_> = curve
|
let control_points: Vec<_> = curve
|
||||||
.poles
|
.poles
|
||||||
.iter()
|
.iter()
|
||||||
@@ -2983,7 +2983,18 @@ fn solve_scope(
|
|||||||
updated.knots = curve.knots.clone();
|
updated.knots = curve.knots.clone();
|
||||||
updated.control_points = control_points;
|
updated.control_points = control_points;
|
||||||
updated.weights = curve.weights.iter().map(|id| store.get(*id)).collect();
|
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.begin_tangent = Vector3::ZERO;
|
||||||
updated.end_tangent = Vector3::ZERO;
|
updated.end_tangent = Vector3::ZERO;
|
||||||
updated.flags.rational = updated
|
updated.flags.rational = updated
|
||||||
|
|||||||
Reference in new issue
Block a user