Files
OneCAD-Draft/src/modules/block_editor/distance_parameter.rs
T
Karim Jerbi 6dab5a7229 feat(block-editor): wire Linear, Polar and XY parameter placement
Enable the three existing palette rows and BPARAMETER choices. Pick a base,
endpoint and label location to create the appropriate native parameter, one
end grip and its location expressions in the shared authoring graph.

Render editor dimensions and labels, preserve them when reframing the block,
and include them in editor extents. Reuse local undo/redo, save/discard and
native DWG/DXF persistence. Reject incomplete or invalid placement without
changing document data or history.

This milestone implements parameter placement. Action association/evaluation,
parameter property editing, alternate grip counts and parameter sets remain
separate work.

Validation: focused placement and palette interaction checks; Block Editor and
dock regressions; native append and DWG/DXF round trips in all 17 research
fixtures, preserving existing graph data and source files; app-view renders.
2026-10-15 16:00:15 +01:00

130 lines
3.9 KiB
Rust

//! Three-pick authoring shared by Linear, Polar and XY parameters.
use crate::command::{CadCommand, CmdResult};
use crate::scene::{
block_distance_parameter::{self, Kind},
WireModel,
};
use codec::{types::Vector3, Handle};
use glam::DVec3;
pub struct DistanceParameterCommand {
document_id: u64,
definition: Handle,
kind: Kind,
base: Option<DVec3>,
end: Option<DVec3>,
}
impl DistanceParameterCommand {
pub(crate) fn new(document_id: u64, definition: Handle, kind: Kind) -> Self {
Self {
document_id,
definition,
kind,
base: None,
end: None,
}
}
}
impl CadCommand for DistanceParameterCommand {
fn name(&self) -> &'static str {
"BPARAMETER"
}
fn prompt(&self) -> String {
let step = if self.base.is_none() {
"Specify parameter base point:"
} else if self.end.is_none() {
"Specify parameter endpoint:"
} else {
"Specify parameter label location:"
};
crate::tf!(
"BPARAMETER {} {}",
crate::t!(self.kind.name()),
crate::t!(step)
)
.into_owned()
}
fn on_point(&mut self, point: DVec3) -> CmdResult {
if !point.is_finite() {
return CmdResult::ReportError(
crate::t!("Specify a finite parameter location.").into_owned(),
);
}
let Some(base) = self.base else {
self.base = Some(point);
return CmdResult::NeedPoint;
};
let vector = |p: DVec3| Vector3::new(p.x, p.y, p.z);
if let Some(error) = block_distance_parameter::points_error(
self.kind,
vector(base),
vector(self.end.unwrap_or(point)),
vector(point),
) {
return CmdResult::ReportError(crate::t!(error).into_owned());
}
let Some(end) = self.end else {
self.end = Some(point);
return CmdResult::NeedPoint;
};
CmdResult::PlaceBlockDistanceParameter {
document_id: self.document_id,
definition: self.definition,
kind: self.kind,
base,
end,
label: point,
}
}
fn on_preview_wires(&mut self, point: DVec3) -> Vec<WireModel> {
let Some(base) = self.base.filter(|_| point.is_finite()) else {
return vec![];
};
let end = self.end.unwrap_or(point);
let mut lines = vec![[base, end]];
if self.kind == Kind::XY {
lines = vec![
[base, DVec3::new(end.x, base.y, base.z)],
[base, DVec3::new(base.x, end.y, base.z)],
];
}
if self.end.is_some() {
if self.kind == Kind::XY {
let x = DVec3::new(base.x, point.y, base.z);
let y = DVec3::new(point.x, base.y, base.z);
lines.extend([
[base, x],
[x, DVec3::new(end.x, point.y, base.z)],
[base, y],
[y, DVec3::new(point.x, end.y, base.z)],
]);
} else {
let d = end - base;
let n = DVec3::new(-d.y, d.x, 0.0).normalize_or_zero();
let offset = n * (point - base).dot(n);
lines = vec![
[base, base + offset],
[base + offset, end + offset],
[end + offset, end],
];
}
}
lines
.into_iter()
.map(|line| {
WireModel::solid_f64(
"preview_distance_parameter".into(),
line.into_iter().map(|point| point.to_array()).collect(),
WireModel::CYAN,
false,
)
})
.collect()
}
fn on_enter(&mut self) -> CmdResult {
CmdResult::Cancel
}
}