forked from 1b-agent/opencadstudio-upstream
feat(ucs): integrate coordinate systems
Route coordinate input, geometry creation, transforms, properties,\nviews, and viewport persistence through a shared working plane.\n\nRefs #484
This commit is contained in:
1 parent
b1d55c6a7e
commit
657d023fb7
61 files changed
+2218
-747
No files matched your search
@@ -5,7 +5,7 @@ use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use glam::DVec3;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::command::{CadCommand, CmdResult, WorkingPlane};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use crate::t;
|
||||
@@ -29,6 +29,7 @@ enum Step {
|
||||
|
||||
pub struct AlignedDimensionCommand {
|
||||
step: Step,
|
||||
plane: WorkingPlane,
|
||||
/// Optional text that replaces the measured value (None = measurement).
|
||||
text_override: Option<String>,
|
||||
/// True while the next typed line is captured as the text override.
|
||||
@@ -43,6 +44,7 @@ impl AlignedDimensionCommand {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
step: Step::First,
|
||||
plane: WorkingPlane::default(),
|
||||
text_override: None,
|
||||
awaiting_text: false,
|
||||
text_angle: None,
|
||||
@@ -52,6 +54,10 @@ impl AlignedDimensionCommand {
|
||||
}
|
||||
|
||||
impl CadCommand for AlignedDimensionCommand {
|
||||
fn set_working_plane(&mut self, plane: WorkingPlane) {
|
||||
self.plane = plane;
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"DIMALIGNED"
|
||||
}
|
||||
@@ -85,6 +91,9 @@ 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));
|
||||
// The dimension line runs through the cursor: store it as the
|
||||
// definition point and let the renderer project it onto the
|
||||
@@ -106,7 +115,9 @@ impl CadCommand for AlignedDimensionCommand {
|
||||
if let Some(a) = self.text_angle {
|
||||
dim.base.text_rotation = a;
|
||||
}
|
||||
CmdResult::CommitAndExit(EntityType::Dimension(Dimension::Aligned(dim)))
|
||||
CmdResult::CommitAndExit(self.plane.place_entity(EntityType::Dimension(
|
||||
Dimension::Aligned(dim),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,7 +191,29 @@ impl CadCommand for AlignedDimensionCommand {
|
||||
Step::First => return None,
|
||||
Step::Second(p1) => (p1, pt),
|
||||
Step::DimLine { p1, p2 } => {
|
||||
return Some(preview_aligned(p1, p2, pt));
|
||||
let p1 = self.plane.to_local(p1);
|
||||
let p2 = self.plane.to_local(p2);
|
||||
let pt = self.plane.to_local(pt);
|
||||
let mut preview = preview_aligned(p1, p2, pt);
|
||||
preview.points = preview
|
||||
.points
|
||||
.iter()
|
||||
.map(|point| {
|
||||
if point[0].is_nan() {
|
||||
*point
|
||||
} else {
|
||||
self.plane
|
||||
.to_world(DVec3::new(
|
||||
point[0] as f64,
|
||||
point[1] as f64,
|
||||
point[2] as f64,
|
||||
))
|
||||
.as_vec3()
|
||||
.to_array()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
return Some(preview);
|
||||
}
|
||||
};
|
||||
// Preview WireModel points are screen/GPU-side: downcast to f32.
|
||||
|
||||
@@ -2,7 +2,7 @@ use acadrust::entities::{Dimension, DimensionAngular3Pt};
|
||||
use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::command::{CadCommand, CmdResult, WorkingPlane};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use glam::{DVec3, Vec3};
|
||||
@@ -35,15 +35,23 @@ enum Step {
|
||||
|
||||
pub struct AngularDimensionCommand {
|
||||
step: Step,
|
||||
plane: WorkingPlane,
|
||||
}
|
||||
|
||||
impl AngularDimensionCommand {
|
||||
pub fn new() -> Self {
|
||||
Self { step: Step::Vertex }
|
||||
Self {
|
||||
step: Step::Vertex,
|
||||
plane: WorkingPlane::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for AngularDimensionCommand {
|
||||
fn set_working_plane(&mut self, plane: WorkingPlane) {
|
||||
self.plane = plane;
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"DIMANGULAR"
|
||||
}
|
||||
@@ -84,13 +92,19 @@ impl CadCommand for AngularDimensionCommand {
|
||||
first,
|
||||
second,
|
||||
} => {
|
||||
let vertex = self.plane.to_local(vertex);
|
||||
let first = self.plane.to_local(first);
|
||||
let second = self.plane.to_local(second);
|
||||
let pt = self.plane.to_local(pt);
|
||||
let mut dim = DimensionAngular3Pt::new(v3(vertex), v3(first), v3(second));
|
||||
dim.definition_point = v3(pt);
|
||||
dim.base.definition_point = v3(pt);
|
||||
dim.base.text_middle_point = v3(pt);
|
||||
dim.base.insertion_point = v3(pt);
|
||||
dim.base.actual_measurement = dim.measurement_degrees();
|
||||
CmdResult::CommitAndExit(EntityType::Dimension(Dimension::Angular3Pt(dim)))
|
||||
CmdResult::CommitAndExit(self.plane.place_entity(EntityType::Dimension(
|
||||
Dimension::Angular3Pt(dim),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,12 +133,24 @@ impl CadCommand for AngularDimensionCommand {
|
||||
vertex,
|
||||
first,
|
||||
second,
|
||||
} => Some(preview_wire(angular_preview(
|
||||
vertex.as_vec3(),
|
||||
first.as_vec3(),
|
||||
second.as_vec3(),
|
||||
pt,
|
||||
))),
|
||||
} => {
|
||||
let points = angular_preview(
|
||||
self.plane.to_local(vertex).as_vec3(),
|
||||
self.plane.to_local(first).as_vec3(),
|
||||
self.plane.to_local(second).as_vec3(),
|
||||
self.plane.to_local(pt.as_dvec3()).as_vec3(),
|
||||
)
|
||||
.into_iter()
|
||||
.map(|point| {
|
||||
if point.x.is_nan() {
|
||||
point
|
||||
} else {
|
||||
self.plane.to_world(point.as_dvec3()).as_vec3()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Some(preview_wire(points))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use glam::{DVec3, Vec3};
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::command::{CadCommand, CmdResult, WorkingPlane};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use crate::t;
|
||||
@@ -29,6 +29,7 @@ enum Step {
|
||||
|
||||
pub struct DiameterDimensionCommand {
|
||||
step: Step,
|
||||
plane: WorkingPlane,
|
||||
/// Optional text that replaces the measured value (None = measurement).
|
||||
text_override: Option<String>,
|
||||
/// True while the next typed line is captured as the text override.
|
||||
@@ -43,6 +44,7 @@ impl DiameterDimensionCommand {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
step: Step::CenterPoint,
|
||||
plane: WorkingPlane::default(),
|
||||
text_override: None,
|
||||
awaiting_text: false,
|
||||
text_angle: None,
|
||||
@@ -52,6 +54,10 @@ impl DiameterDimensionCommand {
|
||||
}
|
||||
|
||||
impl CadCommand for DiameterDimensionCommand {
|
||||
fn set_working_plane(&mut self, plane: WorkingPlane) {
|
||||
self.plane = plane;
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"DIMDIAMETER"
|
||||
}
|
||||
@@ -83,6 +89,9 @@ impl CadCommand for DiameterDimensionCommand {
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
Step::TextPoint { center, arc_pt } => {
|
||||
let center = self.plane.to_local(center);
|
||||
let arc_pt = self.plane.to_local(arc_pt);
|
||||
let pt = self.plane.to_local(pt);
|
||||
let mut dim = DimensionDiameter::new(v3(center), v3(arc_pt));
|
||||
dim.base.definition_point = v3(arc_pt);
|
||||
dim.base.text_middle_point = v3(pt);
|
||||
@@ -94,7 +103,9 @@ impl CadCommand for DiameterDimensionCommand {
|
||||
if let Some(a) = self.text_angle {
|
||||
dim.base.text_rotation = a;
|
||||
}
|
||||
CmdResult::CommitAndExit(EntityType::Dimension(Dimension::Diameter(dim)))
|
||||
CmdResult::CommitAndExit(self.plane.place_entity(EntityType::Dimension(
|
||||
Dimension::Diameter(dim),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use glam::{DVec3, Mat4, Vec3};
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::command::{CadCommand, CmdResult, WorkingPlane};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use crate::t;
|
||||
@@ -33,7 +33,7 @@ pub fn tool() -> ToolDef {
|
||||
|
||||
pub struct LeaderCommand {
|
||||
verts: Vec<DVec3>,
|
||||
ucs: Mat4,
|
||||
plane: WorkingPlane,
|
||||
dimension_style: String,
|
||||
text_style: String,
|
||||
text_height: f64,
|
||||
@@ -55,7 +55,7 @@ impl LeaderCommand {
|
||||
};
|
||||
Self {
|
||||
verts: Vec::new(),
|
||||
ucs: Mat4::IDENTITY,
|
||||
plane: WorkingPlane::default(),
|
||||
dimension_style: defaults.style_name,
|
||||
text_style: defaults.text_style_name,
|
||||
text_height: defaults.text_height,
|
||||
@@ -72,8 +72,8 @@ impl CadCommand for LeaderCommand {
|
||||
"LEADER"
|
||||
}
|
||||
|
||||
fn set_ucs(&mut self, ucs: Mat4) {
|
||||
self.ucs = ucs;
|
||||
fn set_working_plane(&mut self, plane: WorkingPlane) {
|
||||
self.plane = plane;
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
@@ -99,16 +99,21 @@ impl CadCommand for LeaderCommand {
|
||||
}
|
||||
// Place the leader plus an empty MText annotation, link them, then open
|
||||
// the in-place MText editor so the user types the annotation text.
|
||||
let local: Vec<DVec3> = self
|
||||
.verts
|
||||
.iter()
|
||||
.map(|point| self.plane.to_local(*point))
|
||||
.collect();
|
||||
let leader = build_leader(
|
||||
&self.verts,
|
||||
self.ucs,
|
||||
&local,
|
||||
Mat4::IDENTITY,
|
||||
&self.dimension_style,
|
||||
self.text_height,
|
||||
self.gap,
|
||||
self.arrow_size,
|
||||
);
|
||||
let displayed_height = self.text_height * self.display_scale;
|
||||
let (anchor, attach) = annotation_anchor(&self.verts, displayed_height, self.ucs);
|
||||
let (anchor, attach) = annotation_anchor(&local, displayed_height, Mat4::IDENTITY);
|
||||
let mtext_height = if self.annotative {
|
||||
self.text_height
|
||||
} else {
|
||||
@@ -119,12 +124,15 @@ impl CadCommand for LeaderCommand {
|
||||
anchor,
|
||||
mtext_height,
|
||||
attach,
|
||||
self.ucs,
|
||||
Mat4::IDENTITY,
|
||||
&self.text_style,
|
||||
self.annotative,
|
||||
);
|
||||
CmdResult::CommitManyAndEditText {
|
||||
entities: vec![EntityType::Leader(leader), EntityType::MText(mtext)],
|
||||
entities: vec![
|
||||
self.plane.place_entity(EntityType::Leader(leader)),
|
||||
self.plane.place_entity(EntityType::MText(mtext)),
|
||||
],
|
||||
edit_index: 1,
|
||||
}
|
||||
}
|
||||
@@ -137,12 +145,31 @@ impl CadCommand for LeaderCommand {
|
||||
if self.verts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let mut pts: Vec<Vec3> = self.verts.iter().map(|p| p.as_vec3()).collect();
|
||||
pts.push(pt.as_vec3());
|
||||
Some(preview_wire(
|
||||
let mut pts: Vec<Vec3> = self
|
||||
.verts
|
||||
.iter()
|
||||
.map(|point| self.plane.to_local(*point).as_vec3())
|
||||
.collect();
|
||||
pts.push(self.plane.to_local(pt).as_vec3());
|
||||
let mut preview = preview_wire(
|
||||
&pts,
|
||||
(self.arrow_size * self.display_scale) as f32,
|
||||
))
|
||||
);
|
||||
preview.points = preview
|
||||
.points
|
||||
.iter()
|
||||
.map(|point| {
|
||||
if point[0].is_nan() {
|
||||
*point
|
||||
} else {
|
||||
self.plane
|
||||
.to_world(Vec3::from_array(*point).as_dvec3())
|
||||
.as_vec3()
|
||||
.to_array()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Some(preview)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,23 +2,18 @@ use acadrust::entities::{Dimension, DimensionLinear};
|
||||
use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::command::{CadCommand, CmdResult, WorkingPlane};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use glam::{DVec3, Mat4, Vec3};
|
||||
use glam::DVec3;
|
||||
use crate::t;
|
||||
|
||||
/// World-space measurement axis for a linear dimension between `first` and
|
||||
/// `second`, chosen as the UCS X or Y axis (whichever the span is closer to).
|
||||
/// `ucs` is the UCS→wire affine; identity gives the world X/Y behaviour.
|
||||
fn measure_axis(first: DVec3, second: DVec3, ucs: Mat4) -> DVec3 {
|
||||
let ux = ucs.transform_vector3(Vec3::X).normalize_or(Vec3::X).as_dvec3();
|
||||
let uy = ucs.transform_vector3(Vec3::Y).normalize_or(Vec3::Y).as_dvec3();
|
||||
let du = ucs.inverse().transform_vector3((second - first).as_vec3());
|
||||
if du.x.abs() >= du.y.abs() {
|
||||
ux
|
||||
fn measure_axis(first: DVec3, second: DVec3) -> DVec3 {
|
||||
let delta = second - first;
|
||||
if delta.x.abs() >= delta.y.abs() {
|
||||
DVec3::X
|
||||
} else {
|
||||
uy
|
||||
DVec3::Y
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +36,7 @@ enum Step {
|
||||
|
||||
pub struct LinearDimensionCommand {
|
||||
step: Step,
|
||||
ucs: Mat4,
|
||||
plane: WorkingPlane,
|
||||
/// Optional text that replaces the measured value (None = measurement).
|
||||
text_override: Option<String>,
|
||||
/// True while the next typed line is captured as the text override.
|
||||
@@ -56,7 +51,7 @@ impl LinearDimensionCommand {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
step: Step::FirstPoint,
|
||||
ucs: Mat4::IDENTITY,
|
||||
plane: WorkingPlane::default(),
|
||||
text_override: None,
|
||||
awaiting_text: false,
|
||||
text_angle: None,
|
||||
@@ -70,8 +65,8 @@ impl CadCommand for LinearDimensionCommand {
|
||||
"DIMLINEAR"
|
||||
}
|
||||
|
||||
fn set_ucs(&mut self, ucs: Mat4) {
|
||||
self.ucs = ucs;
|
||||
fn set_working_plane(&mut self, plane: WorkingPlane) {
|
||||
self.plane = plane;
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
@@ -103,21 +98,12 @@ impl CadCommand for LinearDimensionCommand {
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
Step::DimensionLine { first, second } => {
|
||||
let first = self.plane.to_local(first);
|
||||
let second = self.plane.to_local(second);
|
||||
let pt = self.plane.to_local(pt);
|
||||
let mut dim = DimensionLinear::new(v3(first), v3(second));
|
||||
// Measure along the UCS axis the span is closest to; the DXF
|
||||
// rotation is that axis's angle in world space.
|
||||
let axis = measure_axis(first, second, self.ucs);
|
||||
let axis = measure_axis(first, second);
|
||||
dim.rotation = axis.y.atan2(axis.x);
|
||||
// Bake the UCS X-axis angle as the text rotation so the
|
||||
// measurement text reads horizontally in the UCS (i.e. square on
|
||||
// screen) regardless of the style's force-horizontal flags,
|
||||
// which would otherwise pin it to world-horizontal and tilt it
|
||||
// under a rotated UCS. Left at 0 (natural) when there's no UCS.
|
||||
let ux = self.ucs.transform_vector3(Vec3::X);
|
||||
let ucs_ang = (ux.y as f64).atan2(ux.x as f64);
|
||||
if ucs_ang.abs() > 1e-9 {
|
||||
dim.base.text_rotation = ucs_ang;
|
||||
}
|
||||
dim.definition_point = v3(pt);
|
||||
dim.base.definition_point = v3(pt);
|
||||
dim.base.text_middle_point = v3(linear_text_pos(first, second, pt, axis));
|
||||
@@ -128,7 +114,9 @@ impl CadCommand for LinearDimensionCommand {
|
||||
if let Some(a) = self.text_angle {
|
||||
dim.base.text_rotation = a;
|
||||
}
|
||||
CmdResult::CommitAndExit(EntityType::Dimension(Dimension::Linear(dim)))
|
||||
CmdResult::CommitAndExit(self.plane.place_entity(EntityType::Dimension(
|
||||
Dimension::Linear(dim),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,8 +194,15 @@ impl CadCommand for LinearDimensionCommand {
|
||||
Step::FirstPoint => None,
|
||||
Step::SecondPoint(first) => Some(preview_wire(vec![first, pt])),
|
||||
Step::DimensionLine { first, second } => {
|
||||
let axis = measure_axis(first, second, self.ucs);
|
||||
Some(preview_wire(linear_dimension_preview(first, second, pt, axis)))
|
||||
let first = self.plane.to_local(first);
|
||||
let second = self.plane.to_local(second);
|
||||
let pt = self.plane.to_local(pt);
|
||||
let axis = measure_axis(first, second);
|
||||
let points = linear_dimension_preview(first, second, pt, axis)
|
||||
.into_iter()
|
||||
.map(|point| self.plane.to_world(point))
|
||||
.collect();
|
||||
Some(preview_wire(points))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use glam::{DVec3, Mat4, Vec3};
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::command::{CadCommand, CmdResult, WorkingPlane};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use crate::t;
|
||||
@@ -28,7 +28,7 @@ pub fn tool() -> ToolDef {
|
||||
|
||||
pub struct MLeaderCommand {
|
||||
verts: Vec<DVec3>,
|
||||
ucs: Mat4,
|
||||
plane: WorkingPlane,
|
||||
style: Option<acadrust::objects::MultiLeaderStyle>,
|
||||
display_scale: f64,
|
||||
}
|
||||
@@ -37,7 +37,7 @@ impl MLeaderCommand {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
verts: Vec::new(),
|
||||
ucs: Mat4::IDENTITY,
|
||||
plane: WorkingPlane::default(),
|
||||
style: None,
|
||||
display_scale: 1.0,
|
||||
}
|
||||
@@ -54,7 +54,7 @@ impl MLeaderCommand {
|
||||
};
|
||||
Self {
|
||||
verts: Vec::new(),
|
||||
ucs: Mat4::IDENTITY,
|
||||
plane: WorkingPlane::default(),
|
||||
style: Some(style),
|
||||
display_scale,
|
||||
}
|
||||
@@ -66,8 +66,8 @@ impl CadCommand for MLeaderCommand {
|
||||
"MLEADER"
|
||||
}
|
||||
|
||||
fn set_ucs(&mut self, ucs: Mat4) {
|
||||
self.ucs = ucs;
|
||||
fn set_working_plane(&mut self, plane: WorkingPlane) {
|
||||
self.plane = plane;
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
@@ -93,14 +93,21 @@ impl CadCommand for MLeaderCommand {
|
||||
}
|
||||
// Place the leader with empty text, then open the in-place MText editor
|
||||
// so the user types the annotation into the rich editor.
|
||||
let local: Vec<DVec3> = self
|
||||
.verts
|
||||
.iter()
|
||||
.map(|point| self.plane.to_local(*point))
|
||||
.collect();
|
||||
let ml = build_mleader(
|
||||
"",
|
||||
&self.verts,
|
||||
self.ucs,
|
||||
&local,
|
||||
Mat4::IDENTITY,
|
||||
self.style.as_ref(),
|
||||
self.display_scale,
|
||||
);
|
||||
CmdResult::CommitAndEditText(EntityType::MultiLeader(ml))
|
||||
CmdResult::CommitAndEditText(
|
||||
self.plane.place_entity(EntityType::MultiLeader(ml)),
|
||||
)
|
||||
}
|
||||
|
||||
fn on_escape(&mut self) -> CmdResult {
|
||||
@@ -112,14 +119,33 @@ impl CadCommand for MLeaderCommand {
|
||||
return None;
|
||||
}
|
||||
// Preview / rubber-band is GPU screen-space: downcast to f32 here.
|
||||
let mut pts: Vec<Vec3> = self.verts.iter().map(|p| p.as_vec3()).collect();
|
||||
pts.push(pt.as_vec3());
|
||||
let mut pts: Vec<Vec3> = self
|
||||
.verts
|
||||
.iter()
|
||||
.map(|point| self.plane.to_local(*point).as_vec3())
|
||||
.collect();
|
||||
pts.push(self.plane.to_local(pt).as_vec3());
|
||||
let arrow_size = self
|
||||
.style
|
||||
.as_ref()
|
||||
.map_or(2.5, |style| style.arrowhead_size)
|
||||
* self.display_scale;
|
||||
Some(preview_wire(&pts, arrow_size as f32))
|
||||
let mut preview = preview_wire(&pts, arrow_size as f32);
|
||||
preview.points = preview
|
||||
.points
|
||||
.iter()
|
||||
.map(|point| {
|
||||
if point[0].is_nan() {
|
||||
*point
|
||||
} else {
|
||||
self.plane
|
||||
.to_world(Vec3::from_array(*point).as_dvec3())
|
||||
.as_vec3()
|
||||
.to_array()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Some(preview)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use glam::{DVec3, Vec3};
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::command::{CadCommand, CmdResult, WorkingPlane};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use crate::t;
|
||||
@@ -34,17 +34,23 @@ enum Step {
|
||||
|
||||
pub struct OrdinateDimCommand {
|
||||
step: Step,
|
||||
plane: WorkingPlane,
|
||||
}
|
||||
|
||||
impl OrdinateDimCommand {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
step: Step::FeaturePoint,
|
||||
plane: WorkingPlane::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for OrdinateDimCommand {
|
||||
fn set_working_plane(&mut self, plane: WorkingPlane) {
|
||||
self.plane = plane;
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"DIMORDINATE"
|
||||
}
|
||||
@@ -63,6 +69,8 @@ impl CadCommand for OrdinateDimCommand {
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
Step::LeaderEndpoint { feature } => {
|
||||
let feature = self.plane.to_local(feature);
|
||||
let pt = self.plane.to_local(pt);
|
||||
let is_x = is_x_type(feature, pt);
|
||||
let elbow = ordinate_elbow(feature, pt, is_x);
|
||||
let mut dim = DimensionOrdinate::new(v3(feature), v3(pt), is_x);
|
||||
@@ -73,7 +81,9 @@ impl CadCommand for OrdinateDimCommand {
|
||||
// the wrong (XZ) plane, dropping the Y coordinate. (#150)
|
||||
dim.definition_point = v3(elbow);
|
||||
dim.base.definition_point = v3(elbow);
|
||||
CmdResult::CommitAndExit(EntityType::Dimension(Dimension::Ordinate(dim)))
|
||||
CmdResult::CommitAndExit(self.plane.place_entity(EntityType::Dimension(
|
||||
Dimension::Ordinate(dim),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,8 +96,13 @@ impl CadCommand for OrdinateDimCommand {
|
||||
Step::LeaderEndpoint { feature } => feature,
|
||||
_ => return None,
|
||||
};
|
||||
let feature = self.plane.to_local(feature);
|
||||
let pt = self.plane.to_local(pt);
|
||||
let is_x = is_x_type(feature, pt);
|
||||
let elbow = ordinate_elbow(feature, pt, is_x);
|
||||
let feature = self.plane.to_world(feature);
|
||||
let elbow = self.plane.to_world(elbow);
|
||||
let pt = self.plane.to_world(pt);
|
||||
// Screen-only rubber band: downcast to f32 at the preview boundary.
|
||||
Some(preview_wire(vec![
|
||||
feature.as_vec3(),
|
||||
|
||||
@@ -2,7 +2,7 @@ use acadrust::entities::{Dimension, DimensionRadius};
|
||||
use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::command::{CadCommand, CmdResult, WorkingPlane};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use glam::{DVec3, Vec3};
|
||||
@@ -27,6 +27,7 @@ enum Step {
|
||||
|
||||
pub struct RadiusDimensionCommand {
|
||||
step: Step,
|
||||
plane: WorkingPlane,
|
||||
/// Optional text that replaces the measured value (None = measurement).
|
||||
text_override: Option<String>,
|
||||
/// True while the next typed line is captured as the text override.
|
||||
@@ -41,6 +42,7 @@ impl RadiusDimensionCommand {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
step: Step::CenterPoint,
|
||||
plane: WorkingPlane::default(),
|
||||
text_override: None,
|
||||
awaiting_text: false,
|
||||
text_angle: None,
|
||||
@@ -50,6 +52,10 @@ impl RadiusDimensionCommand {
|
||||
}
|
||||
|
||||
impl CadCommand for RadiusDimensionCommand {
|
||||
fn set_working_plane(&mut self, plane: WorkingPlane) {
|
||||
self.plane = plane;
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"DIMRADIUS"
|
||||
}
|
||||
@@ -81,6 +87,9 @@ impl CadCommand for RadiusDimensionCommand {
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
Step::TextPoint { center, point } => {
|
||||
let center = self.plane.to_local(center);
|
||||
let point = self.plane.to_local(point);
|
||||
let pt = self.plane.to_local(pt);
|
||||
let mut dim = DimensionRadius::new(v3(center), v3(point));
|
||||
dim.base.definition_point = v3(point);
|
||||
dim.base.text_middle_point = v3(pt);
|
||||
@@ -92,7 +101,9 @@ impl CadCommand for RadiusDimensionCommand {
|
||||
if let Some(a) = self.text_angle {
|
||||
dim.base.text_rotation = a;
|
||||
}
|
||||
CmdResult::CommitAndExit(EntityType::Dimension(Dimension::Radius(dim)))
|
||||
CmdResult::CommitAndExit(self.plane.place_entity(EntityType::Dimension(
|
||||
Dimension::Radius(dim),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use glam::DVec3;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::command::{CadCommand, CmdResult, WorkingPlane};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use crate::t;
|
||||
@@ -45,6 +45,7 @@ pub struct TableCommand {
|
||||
column_width: f64,
|
||||
row_height: f64,
|
||||
preview_scale: f64,
|
||||
plane: WorkingPlane,
|
||||
}
|
||||
|
||||
impl TableCommand {
|
||||
@@ -55,6 +56,7 @@ impl TableCommand {
|
||||
column_width: COL_WIDTH,
|
||||
row_height: ROW_HEIGHT,
|
||||
preview_scale: 1.0,
|
||||
plane: WorkingPlane::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,11 +83,16 @@ impl TableCommand {
|
||||
} else {
|
||||
1.0
|
||||
},
|
||||
plane: WorkingPlane::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for TableCommand {
|
||||
fn set_working_plane(&mut self, plane: WorkingPlane) {
|
||||
self.plane = plane;
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"TABLE"
|
||||
}
|
||||
@@ -163,27 +170,34 @@ impl CadCommand for TableCommand {
|
||||
|
||||
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
||||
if let Step::Insertion { cols, rows } = self.step {
|
||||
let ins = Vector3::new(pt.x, pt.y, pt.z);
|
||||
let point = self.plane.to_local(pt);
|
||||
let ins = Vector3::new(point.x, point.y, point.z);
|
||||
let mut table = TableBuilder::new(rows, cols)
|
||||
.at(ins)
|
||||
.row_height(self.row_height)
|
||||
.column_width(self.column_width)
|
||||
.build();
|
||||
table.table_style_handle = self.style_handle;
|
||||
CmdResult::CommitAndExit(EntityType::Table(table))
|
||||
CmdResult::CommitAndExit(self.plane.place_entity(EntityType::Table(table)))
|
||||
} else {
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
}
|
||||
|
||||
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> { let pt = pt.as_vec3();
|
||||
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> {
|
||||
if let Step::Insertion { cols, rows } = self.step {
|
||||
// Preview: outline of the table bounding box.
|
||||
let w = (cols as f32) * (self.column_width * self.preview_scale) as f32;
|
||||
let h = (rows as f32) * (self.row_height * self.preview_scale) as f32;
|
||||
let x = pt.x;
|
||||
let y = pt.y;
|
||||
let z = pt.z;
|
||||
let point = self.plane.to_local(pt);
|
||||
let corners = [
|
||||
point,
|
||||
point + DVec3::X * w as f64,
|
||||
point + DVec3::new(w as f64, -(h as f64), 0.0),
|
||||
point - DVec3::Y * h as f64,
|
||||
]
|
||||
.map(|corner| self.plane.to_world(corner).as_vec3().to_array());
|
||||
let [p0, p1, p2, p3] = corners;
|
||||
Some(WireModel {
|
||||
taper_widths: Vec::new(),
|
||||
world_width: 0.0,
|
||||
@@ -197,14 +211,7 @@ impl CadCommand for TableCommand {
|
||||
text_verts: Vec::new(),
|
||||
name: "table_preview".into(),
|
||||
points: vec![
|
||||
[x, y, z],
|
||||
[x + w, y, z],
|
||||
[x + w, y, z],
|
||||
[x + w, y, z - h],
|
||||
[x + w, y, z - h],
|
||||
[x, y, z - h],
|
||||
[x, y, z - h],
|
||||
[x, y, z],
|
||||
p0, p1, p1, p2, p2, p3, p3, p0,
|
||||
],
|
||||
points_low: Vec::new(),
|
||||
color: WireModel::CYAN,
|
||||
|
||||
@@ -9,7 +9,7 @@ use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use glam::DVec3;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::command::{CadCommand, CmdResult, WorkingPlane};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use crate::t;
|
||||
@@ -32,15 +32,23 @@ enum Step {
|
||||
|
||||
pub struct ToleranceCommand {
|
||||
step: Step,
|
||||
plane: WorkingPlane,
|
||||
}
|
||||
|
||||
impl ToleranceCommand {
|
||||
pub fn new() -> Self {
|
||||
Self { step: Step::Text }
|
||||
Self {
|
||||
step: Step::Text,
|
||||
plane: WorkingPlane::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for ToleranceCommand {
|
||||
fn set_working_plane(&mut self, plane: WorkingPlane) {
|
||||
self.plane = plane;
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"TOLERANCE"
|
||||
}
|
||||
@@ -69,9 +77,10 @@ impl CadCommand for ToleranceCommand {
|
||||
|
||||
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
||||
if let Step::Insertion { text } = &self.step {
|
||||
let ins = Vector3::new(pt.x, pt.y, pt.z);
|
||||
let point = self.plane.to_local(pt);
|
||||
let ins = Vector3::new(point.x, point.y, point.z);
|
||||
let tol = Tolerance::with_text(ins, text.clone());
|
||||
CmdResult::CommitAndExit(EntityType::Tolerance(tol))
|
||||
CmdResult::CommitAndExit(self.plane.place_entity(EntityType::Tolerance(tol)))
|
||||
} else {
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
@@ -81,11 +90,18 @@ impl CadCommand for ToleranceCommand {
|
||||
CmdResult::Cancel
|
||||
}
|
||||
|
||||
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> { let pt = pt.as_vec3();
|
||||
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> {
|
||||
if !matches!(self.step, Step::Insertion { .. }) {
|
||||
return None;
|
||||
}
|
||||
let d = 0.15_f32;
|
||||
let d = 0.15;
|
||||
let points = [
|
||||
pt - self.plane.x * d,
|
||||
pt + self.plane.x * d,
|
||||
DVec3::splat(f64::NAN),
|
||||
pt - self.plane.y * d,
|
||||
pt + self.plane.y * d,
|
||||
];
|
||||
Some(WireModel {
|
||||
taper_widths: Vec::new(),
|
||||
world_width: 0.0,
|
||||
@@ -98,13 +114,10 @@ impl CadCommand for ToleranceCommand {
|
||||
dash_align_end: None,
|
||||
text_verts: Vec::new(),
|
||||
name: "tolerance_preview".into(),
|
||||
points: vec![
|
||||
[pt.x - d, pt.y, pt.z],
|
||||
[pt.x + d, pt.y, pt.z],
|
||||
[f32::NAN, 0.0, 0.0],
|
||||
[pt.x, pt.y, pt.z - d],
|
||||
[pt.x, pt.y, pt.z + d],
|
||||
],
|
||||
points: points
|
||||
.iter()
|
||||
.map(|point| point.as_vec3().to_array())
|
||||
.collect(),
|
||||
points_low: Vec::new(),
|
||||
color: WireModel::CYAN,
|
||||
selected: false,
|
||||
|
||||
Reference in new issue
Block a user