feat(dimension): styled placement previews for creation commands

Show the styled dimension and its value while placing a dimension, using
the same builder the command commits with. Each creation command
(DIMLINEAR, DIMALIGNED, DIMANGULAR, DIMRADIUS, DIMDIAMETER, DIMARC,
DIMJOGGED, DIMORDINATE, DIMCONTINUE, DIMBASELINE, QDIM) reports the
dimensions it would commit at the current stage through
`CadCommand::dimension_preview`. The application applies the current
creation style, or the base dimension's style for DIMCONTINUEMODE=1,
and the same viewport measurement scale as commit, then tessellates the
transient entity through the normal dimension renderer.

While the second definition point is being acquired, a hovered model
snap supplies a provisional viewport scale; once both points are fixed,
hovering another viewport does not change the value. Angular dimensions
never receive length compensation. Degenerate geometry, an invalid
scale, or conflicting viewports fall back to the plain rubber-band
preview so no misleading value is shown.
This commit is contained in:
Christopher Overbeck committed 2026-09-16 13:58:44 -05:00
1 parent ca0ee37d26
commit acbbb23297
17 files changed
+724 -138

No files matched your search

+43 -21
View File
@@ -6,7 +6,8 @@ use acadrust::EntityType;
use glam::DVec3;
use crate::command::{
CadCommand, CmdOption, CmdResult, DimensionAssociationInput, InputKind, WorkingPlane,
CadCommand, CmdOption, CmdResult, DimensionAssociationInput, DimensionPreview, InputKind,
WorkingPlane,
};
use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel;
@@ -61,6 +62,29 @@ impl AlignedDimensionCommand {
mtext_override: false,
}
}
fn build_dimension(&self, first: DVec3, second: DVec3, point: DVec3) -> EntityType {
let first = self.plane.to_local(first);
let second = self.plane.to_local(second);
let point = self.plane.to_local(point);
let mut dim = DimensionAligned::new(v3(first), v3(second));
// The placement point is stored so commit matches the preview.
dim.definition_point = v3(point);
dim.base.definition_point = v3(point);
let (d1, d2) = dim_line_endpoints(first, second, point);
dim.base.text_middle_point = v3((d1 + d2) * 0.5);
dim.base.insertion_point = dim.base.text_middle_point;
dim.base.actual_measurement = dim.measurement();
crate::entities::dimension::set_dimension_text_override(
&mut dim.base,
self.text_override.clone(),
);
// An explicit text angle overrides the default rotation.
if let Some(angle) = self.text_angle {
dim.base.text_rotation = angle;
}
self.plane.place_entity(EntityType::Dimension(Dimension::Aligned(dim)))
}
}
impl CadCommand for AlignedDimensionCommand {
@@ -115,27 +139,8 @@ impl CadCommand for AlignedDimensionCommand {
CmdResult::NeedPoint
}
Step::DimLine { p1, p2 } => {
let p1 = self.plane.to_local(p1);
let p2 = self.plane.to_local(p2);
let pt = self.plane.to_local(pt);
let mut dim = DimensionAligned::new(v3(p1), v3(p2));
// Store the cursor position so commit matches the preview.
dim.definition_point = v3(pt);
dim.base.definition_point = v3(pt);
let (d1, d2) = dim_line_endpoints(p1, p2, pt);
dim.base.text_middle_point = v3((d1 + d2) * 0.5);
dim.base.insertion_point = dim.base.text_middle_point;
dim.base.actual_measurement = dim.measurement();
crate::entities::dimension::set_dimension_text_override(
&mut dim.base,
self.text_override.clone(),
);
// An explicit text angle overrides the default rotation.
if let Some(a) = self.text_angle {
dim.base.text_rotation = a;
}
CmdResult::CommitDimension {
entity: self.plane.place_entity(EntityType::Dimension(Dimension::Aligned(dim))),
entity: self.build_dimension(p1, p2, pt),
association: DimensionAssociationInput::Infer(self.source_handle),
preserve_base_style: false,
continue_command: false,
@@ -351,6 +356,23 @@ impl CadCommand for AlignedDimensionCommand {
fill_tris_low: Vec::new(),
})
}
fn dimension_preview(&self, cursor: DVec3) -> Option<Vec<DimensionPreview>> {
if self.selecting_object {
return None;
}
let (first, second) = match self.step {
Step::First => return None,
Step::Second(first) => (first, cursor),
Step::DimLine { p1, p2 } => (p1, p2),
};
if first.distance_squared(second) <= 1e-24 {
return Some(Vec::new());
}
Some(vec![DimensionPreview::current_style(
self.build_dimension(first, second, cursor),
)])
}
}
fn v3(p: DVec3) -> Vector3 {