diff --git a/src/app/properties.rs b/src/app/properties.rs index cbc8e8d5b..b90424c64 100644 --- a/src/app/properties.rs +++ b/src/app/properties.rs @@ -437,9 +437,12 @@ impl OpenCADStudio { let grips = dispatch::grips(entity) .into_iter() .map(|mut g| { - g.world.x -= wo[0] as f32; - g.world.y -= wo[1] as f32; - g.world.z -= wo[2] as f32; + // Subtract in f64: at UTM magnitudes an f32 cast before + // the offset costs ~1 unit and draws the grip off the + // wire. + g.world.x -= wo[0]; + g.world.y -= wo[1]; + g.world.z -= wo[2]; g }) .collect(); diff --git a/src/app/update.rs b/src/app/update.rs index 55fde2316..84072f9c7 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -304,10 +304,13 @@ impl OpenCADStudio { // header so the ✓ marks the right one (text/dim/mline come from // the document header directly). DXF provides these via // $CTABLESTYLE / $CMLEADERSTYLE; DWG leaves them at "Standard". - self.ribbon.active_table_style = - self.tabs[i].scene.document.header.current_table_style_name.clone(); - self.tabs[i].active_mleader_style = self - .tabs[i] + self.ribbon.active_table_style = self.tabs[i] + .scene + .document + .header + .current_table_style_name + .clone(); + self.tabs[i].active_mleader_style = self.tabs[i] .scene .document .header @@ -3838,8 +3841,8 @@ impl OpenCADStudio { handle: popup.handle, grip_id, is_translate, - origin_world: g.world, - last_world: g.world, + origin_world: g.world.as_vec3(), + last_world: g.world.as_vec3(), }); } return Task::none(); @@ -3900,8 +3903,8 @@ impl OpenCADStudio { handle: popup.handle, grip_id: new_gid, is_translate: false, - origin_world: g.world, - last_world: g.world, + origin_world: g.world.as_vec3(), + last_world: g.world.as_vec3(), }); self.grip_add_provisional = Some((popup.handle, new_gid)); } @@ -6746,7 +6749,11 @@ impl OpenCADStudio { if exists { // Staged: header field is the round-trip source of truth // ($CMLEADERSTYLE); the ribbon/tab mirror it. - self.tabs[i].scene.document.header.current_mleader_style_name = name.clone(); + self.tabs[i] + .scene + .document + .header + .current_mleader_style_name = name.clone(); self.tabs[i].active_mleader_style = name.clone(); self.ribbon.active_mleader_style = name.clone(); self.command_line diff --git a/src/entities/arc.rs b/src/entities/arc.rs index e2560371f..56fcdc34d 100644 --- a/src/entities/arc.rs +++ b/src/entities/arc.rs @@ -108,20 +108,17 @@ fn angle_span(start: f32, end: f32) -> f32 { } fn grips(arc: &Arc) -> Vec { - let ctr = Vec3::new( - arc.center.x as f32, - arc.center.y as f32, - arc.center.z as f32, - ); - let r = arc.radius as f32; + let ctr = glam::DVec3::new(arc.center.x, arc.center.y, arc.center.z); + let r = arc.radius; let sa = arc.start_angle as f32; let ea = arc.end_angle as f32; - let ma = sa + angle_span(sa, ea) * 0.5; + let ma = (sa + angle_span(sa, ea) * 0.5) as f64; + let (sa, ea) = (arc.start_angle, arc.end_angle); vec![ center_grip(0, ctr), - square_grip(1, ctr + Vec3::new(r * sa.cos(), r * sa.sin(), 0.0)), - square_grip(2, ctr + Vec3::new(r * ea.cos(), r * ea.sin(), 0.0)), - center_grip(3, ctr + Vec3::new(r * ma.cos(), r * ma.sin(), 0.0)), + square_grip(1, ctr + glam::DVec3::new(r * sa.cos(), r * sa.sin(), 0.0)), + square_grip(2, ctr + glam::DVec3::new(r * ea.cos(), r * ea.sin(), 0.0)), + center_grip(3, ctr + glam::DVec3::new(r * ma.cos(), r * ma.sin(), 0.0)), ] } @@ -230,29 +227,40 @@ impl crate::entities::traits::Grippable for Arc { fn apply_grip(&mut self, grip_id: usize, apply: GripApply) { apply_grip(self, grip_id, apply); } - fn grip_menu( - &self, - grip_id: usize, - ) -> Vec { + fn grip_menu(&self, grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; match grip_id { - 0 => vec![GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }], + 0 => vec![GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }], 3 => vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Radius", action: GripMenuAction::Radius }, - GripMenuItem { label: "Arc Length", action: GripMenuAction::ArcLength }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Radius", + action: GripMenuAction::Radius, + }, + GripMenuItem { + label: "Arc Length", + action: GripMenuAction::ArcLength, + }, ], _ => vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Lengthen", action: GripMenuAction::Lengthen }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Lengthen", + action: GripMenuAction::Lengthen, + }, ], } } - fn apply_grip_menu( - &mut self, - _grip_id: usize, - _action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, _grip_id: usize, _action: crate::scene::object::GripMenuAction) { // Radius / Arc Length / Lengthen all need a follow-up prompt; // the actual edit happens in `apply_grip_menu_value`. } @@ -306,10 +314,7 @@ impl crate::entities::traits::Grippable for Arc { } impl crate::entities::traits::PropertyEditable for Arc { - fn geometry_properties( - &self, - _text_style_names: &[String], - ) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { properties(self) } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/attribute.rs b/src/entities/attribute.rs index 15e82d524..118ce60c4 100644 --- a/src/entities/attribute.rs +++ b/src/entities/attribute.rs @@ -8,8 +8,8 @@ use glam::Vec3; use crate::command::EntityTransform; use crate::entities::common::{edit_prop as edit, parse_f64, ro_prop as ro, square_grip}; use crate::entities::text_support::{ - layout_mtext, resolve_dxf_special_chars, resolve_text_style, text_local_bounds, MTextRenderOpts, - MTextVAnchor, ResolvedTextStyle, + layout_mtext, resolve_dxf_special_chars, resolve_text_style, text_local_bounds, + MTextRenderOpts, MTextVAnchor, ResolvedTextStyle, }; use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible}; use crate::scene::acad_to_truck::{TextStroke, TruckEntity, TruckObject}; @@ -162,7 +162,10 @@ fn build_attr_truck(input: AttrTextInputs<'_>, document: &acadrust::CadDocument) // stacked fractions, …) reaches the stroke output. SingleLine attribs // keep the Text-style anchor math below — they don't accept MText codes // in the DXF spec. - if matches!(input.mtext_flag, MTextFlag::MultiLine | MTextFlag::ConstantMultiLine) { + if matches!( + input.mtext_flag, + MTextFlag::MultiLine | MTextFlag::ConstantMultiLine + ) { let display_value = if input.value.is_empty() { String::new() } else { @@ -282,7 +285,11 @@ fn build_attr_truck(input: AttrTextInputs<'_>, document: &acadrust::CadDocument) &resolved.font_name, line, ); - strokes_all.push(TextStroke { strokes, origin, color: None }); + strokes_all.push(TextStroke { + strokes, + origin, + color: None, + }); } let _ = input.line_count; // round-trip only — recomputed above @@ -338,10 +345,10 @@ impl Grippable for AttributeDefinition { } vec![square_grip( 0, - Vec3::new( - self.insertion_point.x as f32, - self.insertion_point.y as f32, - self.insertion_point.z as f32, + glam::DVec3::new( + self.insertion_point.x, + self.insertion_point.y, + self.insertion_point.z, ), )] } @@ -448,21 +455,9 @@ impl PropertyEditable for AttributeDefinition { .collect(), }, }, - ro( - "Multiline", - "att_is_multiline", - bool_yn(self.is_multiline), - ), - ro( - "Line Count", - "att_line_count", - self.line_count.to_string(), - ), - ro( - "Lock Position", - "att_lock_pos", - bool_yn(self.lock_position), - ), + ro("Multiline", "att_is_multiline", bool_yn(self.is_multiline)), + ro("Line Count", "att_line_count", self.line_count.to_string()), + ro("Lock Position", "att_lock_pos", bool_yn(self.lock_position)), ro("Invisible", "att_invisible", bool_yn(self.flags.invisible)), ro("Constant", "att_constant", bool_yn(self.flags.constant)), ro("Verify", "att_verify", bool_yn(self.flags.verify)), @@ -591,10 +586,10 @@ impl Grippable for AttributeEntity { } vec![square_grip( 0, - Vec3::new( - self.insertion_point.x as f32, - self.insertion_point.y as f32, - self.insertion_point.z as f32, + glam::DVec3::new( + self.insertion_point.x, + self.insertion_point.y, + self.insertion_point.z, ), )] } @@ -691,17 +686,13 @@ impl PropertyEditable for AttributeEntity { "atte_field_len", self.field_length.to_string(), ), - ro("MText Mode", "atte_mtext_flag", mtext_flag_str(self.mtext_flag)), ro( - "Multiline", - "atte_is_multiline", - bool_yn(self.is_multiline), - ), - ro( - "Line Count", - "atte_line_count", - self.line_count.to_string(), + "MText Mode", + "atte_mtext_flag", + mtext_flag_str(self.mtext_flag), ), + ro("Multiline", "atte_is_multiline", bool_yn(self.is_multiline)), + ro("Line Count", "atte_line_count", self.line_count.to_string()), ro( "Lock Position", "atte_lock_pos", @@ -716,11 +707,7 @@ impl PropertyEditable for AttributeEntity { format!("{:X}", self.attdef_handle.value()) }, ), - ro( - "Invisible", - "atte_invisible", - bool_yn(self.flags.invisible), - ), + ro("Invisible", "atte_invisible", bool_yn(self.flags.invisible)), ro("Constant", "atte_constant", bool_yn(self.flags.constant)), ro("Verify", "atte_verify", bool_yn(self.flags.verify)), ro("Preset", "atte_preset", bool_yn(self.flags.preset)), diff --git a/src/entities/circle.rs b/src/entities/circle.rs index b82764b9c..b9a0fb496 100644 --- a/src/entities/circle.rs +++ b/src/entities/circle.rs @@ -112,18 +112,14 @@ fn to_truck(circle: &Circle) -> TruckEntity { } fn grips(circle: &Circle) -> Vec { - let ctr = Vec3::new( - circle.center.x as f32, - circle.center.y as f32, - circle.center.z as f32, - ); - let r = circle.radius as f32; + let ctr = glam::DVec3::new(circle.center.x, circle.center.y, circle.center.z); + let r = circle.radius; vec![ center_grip(0, ctr), - square_grip(1, ctr + Vec3::new(r, 0.0, 0.0)), - square_grip(2, ctr + Vec3::new(0.0, r, 0.0)), - square_grip(3, ctr - Vec3::new(r, 0.0, 0.0)), - square_grip(4, ctr - Vec3::new(0.0, r, 0.0)), + square_grip(1, ctr + glam::DVec3::new(r, 0.0, 0.0)), + square_grip(2, ctr + glam::DVec3::new(0.0, r, 0.0)), + square_grip(3, ctr - glam::DVec3::new(r, 0.0, 0.0)), + square_grip(4, ctr - glam::DVec3::new(0.0, r, 0.0)), ] } diff --git a/src/entities/common.rs b/src/entities/common.rs index 6288f73ab..283aa75ac 100644 --- a/src/entities/common.rs +++ b/src/entities/common.rs @@ -1,7 +1,5 @@ use std::cell::Cell; -use glam::Vec3; - use crate::scene::object::{GripDef, GripShape, PropValue, Property}; /// Linear / angular unit format pulled from the document header so the @@ -121,7 +119,7 @@ pub fn format_angle(value_rad: f64) -> String { } } -pub fn square_grip(id: usize, world: Vec3) -> GripDef { +pub fn square_grip(id: usize, world: glam::DVec3) -> GripDef { GripDef { id, world, @@ -133,7 +131,7 @@ pub fn square_grip(id: usize, world: Vec3) -> GripDef { /// Centre / translate grip — same square marker as a vertex grip but /// flagged as a "whole-object move" handle for the grip-edit code. -pub fn center_grip(id: usize, world: Vec3) -> GripDef { +pub fn center_grip(id: usize, world: glam::DVec3) -> GripDef { GripDef { id, world, @@ -146,7 +144,7 @@ pub fn center_grip(id: usize, world: Vec3) -> GripDef { /// Mid-segment stretch grip oriented along `dir` (the segment's in-plane /// world-XY direction). Drawn as a small rectangle elongated along the /// segment so the affordance reads as "stretch perpendicular". -pub fn rectangle_grip(id: usize, world: Vec3, dir: [f32; 2]) -> GripDef { +pub fn rectangle_grip(id: usize, world: glam::DVec3, dir: [f32; 2]) -> GripDef { GripDef { id, world, @@ -157,7 +155,7 @@ pub fn rectangle_grip(id: usize, world: Vec3, dir: [f32; 2]) -> GripDef { } #[allow(dead_code)] -pub fn triangle_grip(id: usize, world: Vec3) -> GripDef { +pub fn triangle_grip(id: usize, world: glam::DVec3) -> GripDef { GripDef { id, world, diff --git a/src/entities/dimension.rs b/src/entities/dimension.rs index dca31cdf9..4e76c6605 100644 --- a/src/entities/dimension.rs +++ b/src/entities/dimension.rs @@ -565,8 +565,10 @@ impl Transformable for Dimension { // ── Grippable ───────────────────────────────────────────────────────────────── -fn v3(v: &acadrust::types::Vector3) -> Vec3 { - Vec3::new(v.x as f32, v.y as f32, v.z as f32) +/// f64 variant for grip positions — grips must not round UTM-scale +/// coordinates through f32 before the world-offset subtraction. +fn dv3(v: &acadrust::types::Vector3) -> glam::DVec3 { + glam::DVec3::new(v.x, v.y, v.z) } fn set_v3(target: &mut acadrust::types::Vector3, p: Vec3) { @@ -590,48 +592,48 @@ fn apply_to_v3(target: &mut acadrust::types::Vector3, apply: &GripApply) { impl Grippable for Dimension { fn grips(&self) -> Vec { - let text = v3(&self.base().text_middle_point); + let text = dv3(&self.base().text_middle_point); match self { Dimension::Linear(d) => vec![ - square_grip(0, v3(&d.first_point)), - center_grip(1, v3(&d.second_point)), - center_grip(2, v3(&d.definition_point)), + square_grip(0, dv3(&d.first_point)), + center_grip(1, dv3(&d.second_point)), + center_grip(2, dv3(&d.definition_point)), center_grip(3, text), ], Dimension::Aligned(d) => vec![ - square_grip(0, v3(&d.first_point)), - center_grip(1, v3(&d.second_point)), - center_grip(2, v3(&d.definition_point)), + square_grip(0, dv3(&d.first_point)), + center_grip(1, dv3(&d.second_point)), + center_grip(2, dv3(&d.definition_point)), center_grip(3, text), ], Dimension::Radius(d) => vec![ - square_grip(0, v3(&d.angle_vertex)), - center_grip(1, v3(&d.definition_point)), + square_grip(0, dv3(&d.angle_vertex)), + center_grip(1, dv3(&d.definition_point)), center_grip(2, text), ], Dimension::Diameter(d) => vec![ - square_grip(0, v3(&d.angle_vertex)), - center_grip(1, v3(&d.definition_point)), + square_grip(0, dv3(&d.angle_vertex)), + center_grip(1, dv3(&d.definition_point)), center_grip(2, text), ], Dimension::Angular2Ln(d) => vec![ - square_grip(0, v3(&d.angle_vertex)), - center_grip(1, v3(&d.first_point)), - center_grip(2, v3(&d.second_point)), - center_grip(3, v3(&d.definition_point)), + square_grip(0, dv3(&d.angle_vertex)), + center_grip(1, dv3(&d.first_point)), + center_grip(2, dv3(&d.second_point)), + center_grip(3, dv3(&d.definition_point)), center_grip(4, text), ], Dimension::Angular3Pt(d) => vec![ - square_grip(0, v3(&d.angle_vertex)), - center_grip(1, v3(&d.first_point)), - center_grip(2, v3(&d.second_point)), - center_grip(3, v3(&d.definition_point)), + square_grip(0, dv3(&d.angle_vertex)), + center_grip(1, dv3(&d.first_point)), + center_grip(2, dv3(&d.second_point)), + center_grip(3, dv3(&d.definition_point)), center_grip(4, text), ], Dimension::Ordinate(d) => vec![ - square_grip(0, v3(&d.definition_point)), - center_grip(1, v3(&d.feature_location)), - center_grip(2, v3(&d.leader_endpoint)), + square_grip(0, dv3(&d.definition_point)), + center_grip(1, dv3(&d.feature_location)), + center_grip(2, dv3(&d.leader_endpoint)), center_grip(3, text), ], } @@ -697,10 +699,7 @@ impl Grippable for Dimension { self.base_mut().actual_measurement = self.measurement(); } - fn grip_menu( - &self, - grip_id: usize, - ) -> Vec { + fn grip_menu(&self, grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; let (dim_line_grip, text_grip) = match self { Dimension::Linear(_) | Dimension::Aligned(_) => (2, 3), @@ -710,30 +709,59 @@ impl Grippable for Dimension { }; if grip_id == text_grip { vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Move with Dim Line", action: GripMenuAction::MoveWithDimLine }, - GripMenuItem { label: "Move with Leader", action: GripMenuAction::MoveWithLeader }, - GripMenuItem { label: "Move Independent", action: GripMenuAction::MoveIndependent }, - GripMenuItem { label: "Reset Text", action: GripMenuAction::ResetText }, - GripMenuItem { label: "Rotate Text", action: GripMenuAction::RotateText }, - GripMenuItem { label: "Above Dim Line", action: GripMenuAction::AboveDimLine }, - GripMenuItem { label: "Center", action: GripMenuAction::Center }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Move with Dim Line", + action: GripMenuAction::MoveWithDimLine, + }, + GripMenuItem { + label: "Move with Leader", + action: GripMenuAction::MoveWithLeader, + }, + GripMenuItem { + label: "Move Independent", + action: GripMenuAction::MoveIndependent, + }, + GripMenuItem { + label: "Reset Text", + action: GripMenuAction::ResetText, + }, + GripMenuItem { + label: "Rotate Text", + action: GripMenuAction::RotateText, + }, + GripMenuItem { + label: "Above Dim Line", + action: GripMenuAction::AboveDimLine, + }, + GripMenuItem { + label: "Center", + action: GripMenuAction::Center, + }, ] } else if grip_id == dim_line_grip { vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Reverse Arrows", action: GripMenuAction::ReverseArrows }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Reverse Arrows", + action: GripMenuAction::ReverseArrows, + }, ] } else { - vec![GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }] + vec![GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }] } } - fn apply_grip_menu( - &mut self, - grip_id: usize, - action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, grip_id: usize, action: crate::scene::object::GripMenuAction) { use crate::scene::object::GripMenuAction as A; let (_dim_line_grip, text_grip) = match self { Dimension::Linear(_) | Dimension::Aligned(_) => (2, 3), @@ -1056,14 +1084,8 @@ fn tessellate_dimension_inner( // DIMLWD (dim line + arrows) and DIMLWE (extension lines). Negative // codes fall through to the entity's own resolved weight. - let lw_dim = resolve_dim_lineweight_px( - style.map(|s| s.dimlwd).unwrap_or(-2), - line_weight_px, - ); - let lw_ext = resolve_dim_lineweight_px( - style.map(|s| s.dimlwe).unwrap_or(-2), - line_weight_px, - ); + let lw_dim = resolve_dim_lineweight_px(style.map(|s| s.dimlwd).unwrap_or(-2), line_weight_px); + let lw_ext = resolve_dim_lineweight_px(style.map(|s| s.dimlwe).unwrap_or(-2), line_weight_px); // DIMLTEX (dim line) / DIMLTEX1 (ext1) / DIMLTEX2 (ext2) — linetype // handles → pattern. Looked up in document.line_types by handle. @@ -1311,10 +1333,7 @@ fn split_ext_lines(points: &[[f32; 3]]) -> (Vec<[f32; 3]>, Vec<[f32; 3]>) { /// text_middle_point). Returns None when the dim has no saved text position /// or has no well-defined dim-line midpoint (radius/diameter handled by /// their own leg). -fn dimtmove_leader_endpoints( - dim: &Dimension, - world_offset: [f64; 3], -) -> Option<(Vec3, Vec3)> { +fn dimtmove_leader_endpoints(dim: &Dimension, world_offset: [f64; 3]) -> Option<(Vec3, Vec3)> { let base = dim.base(); let txt = base.text_middle_point; if txt.x * txt.x + txt.y * txt.y + txt.z * txt.z <= 1e-16 { @@ -1366,8 +1385,7 @@ fn text_fill_rect( let dimgap = style.map(|s| s.dimgap).unwrap_or(0.0).max(0.0); // ~0.6 × text_height per character; matches average glyph aspect for // the bundled stick fonts. Inflate by 1 DIMGAP on each side. - let approx_w = - value.chars().count() as f64 * text_height * 0.6 + dimgap * 2.0; + let approx_w = value.chars().count() as f64 * text_height * 0.6 + dimgap * 2.0; let approx_h = text_height + dimgap * 2.0; let rot = if dim.base().text_rotation.abs() > 1e-9 { dim.base().text_rotation @@ -1471,7 +1489,12 @@ fn dimension_geometry( text }; add_segment(&mut g.dim_lines, point, leader); - append_arrow(&mut g, point, normalized_or(center - point, Vec3::X), arrow1); + append_arrow( + &mut g, + point, + normalized_or(center - point, Vec3::X), + arrow1, + ); let radius = (point - center).length(); append_center_mark(&mut g, center, params.dimcen, radius); } @@ -2040,7 +2063,13 @@ fn build_tolerance_suffix( return Some(format!("±{}{}", fmt(s.dimtp), unit)); } if s.dimtp.abs() > 1e-12 || s.dimtm.abs() > 1e-12 { - return Some(format!("+{}{} / -{}{}", fmt(s.dimtp), unit, fmt(s.dimtm), unit)); + return Some(format!( + "+{}{} / -{}{}", + fmt(s.dimtp), + unit, + fmt(s.dimtm), + unit + )); } } None @@ -2084,7 +2113,11 @@ fn alternate_units_text(measurement: f64, style: Option<&DimStyle>) -> Option -primary_height * 0.5 + tol_height * 0.5, // bottom-aligned with primary baseline 2 => primary_height * 0.5 - tol_height * 0.5, // top-aligned with primary top - _ => 0.0, // centred (default for ±) + _ => 0.0, // centred (default for ±) }; let rot = primary_rotation; let (sr, cr) = rot.sin_cos(); @@ -2422,11 +2455,7 @@ fn vec3_local(v: Vector3, off: [f64; 3]) -> Vec3 { ) } -fn dimension_text_pos_f64( - dim: &Dimension, - style: Option<&DimStyle>, - text_height: f64, -) -> Vector3 { +fn dimension_text_pos_f64(dim: &Dimension, style: Option<&DimStyle>, text_height: f64) -> Vector3 { let base = dim.base(); let p = base.text_middle_point; if p.x * p.x + p.y * p.y + p.z * p.z > 1e-16 { @@ -2545,4 +2574,3 @@ fn dimension_text_pos_f64( fn perp_sign_default() -> f64 { 1.0 } - diff --git a/src/entities/ellipse.rs b/src/entities/ellipse.rs index 921710c68..b3fc421ff 100644 --- a/src/entities/ellipse.rs +++ b/src/entities/ellipse.rs @@ -140,15 +140,11 @@ fn to_truck(ell: &Ellipse) -> TruckEntity { } fn grips(ell: &Ellipse) -> Vec { - let ctr = Vec3::new( - ell.center.x as f32, - ell.center.y as f32, - ell.center.z as f32, - ); - let maj = Vec3::new( - (ell.center.x + ell.major_axis.x) as f32, - (ell.center.y + ell.major_axis.y) as f32, - (ell.center.z + ell.major_axis.z) as f32, + let ctr = glam::DVec3::new(ell.center.x, ell.center.y, ell.center.z); + let maj = glam::DVec3::new( + ell.center.x + ell.major_axis.x, + ell.center.y + ell.major_axis.y, + ell.center.z + ell.major_axis.z, ); let major_xy = ((ell.major_axis.x * ell.major_axis.x + ell.major_axis.y * ell.major_axis.y) as f64).sqrt(); @@ -158,11 +154,7 @@ fn grips(ell: &Ellipse) -> Vec { } else { (0.0, ell.major_axis_length() * ell.minor_axis_ratio) }; - let min = Vec3::new( - (ell.center.x + px) as f32, - (ell.center.y + py) as f32, - ell.center.z as f32, - ); + let min = glam::DVec3::new(ell.center.x + px, ell.center.y + py, ell.center.z); vec![ center_grip(0, ctr), square_grip(1, maj), diff --git a/src/entities/hatch.rs b/src/entities/hatch.rs index 484599884..d4e06b695 100644 --- a/src/entities/hatch.rs +++ b/src/entities/hatch.rs @@ -3,7 +3,7 @@ use glam::Vec3; use crate::command::EntityTransform; use crate::entities::common::{center_grip, edit_prop as edit, parse_f64, ro_prop as ro}; -use crate::entities::traits::{Grippable, FallbackTess, PropertyEditable, Transformable}; +use crate::entities::traits::{FallbackTess, Grippable, PropertyEditable, Transformable}; use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property}; use crate::scene::tess_util::{arc_segments, arc_signed_span, wire_chord_tol, FallbackGeometry}; use crate::scene::wire_model::SnapHint; @@ -66,11 +66,7 @@ fn properties(h: &Hatch) -> PropSection { ), edit("Elevation", "elevation", h.elevation), ro("Seed Points", "seed_count", h.seed_points.len().to_string()), - ro( - "Pixel Size", - "pixel_size", - format!("{:.6}", h.pixel_size), - ), + ro("Pixel Size", "pixel_size", format!("{:.6}", h.pixel_size)), ro( "Normal", "normal", @@ -174,7 +170,7 @@ impl Transformable for Hatch { /// Spline → fit points if present, else control points (x, y) impl Grippable for Hatch { fn grips(&self) -> Vec { - let elev = self.elevation as f32; + let elev = self.elevation; let mut out = Vec::new(); let mut id = 0usize; for path in &self.paths { @@ -182,33 +178,30 @@ impl Grippable for Hatch { match edge { BoundaryEdge::Polyline(p) => { for v in &p.vertices { - out.push(center_grip(id, Vec3::new(v.x as f32, v.y as f32, elev))); + out.push(center_grip(id, glam::DVec3::new(v.x, v.y, elev))); id += 1; } } BoundaryEdge::Line(l) => { out.push(center_grip( id, - Vec3::new(l.start.x as f32, l.start.y as f32, elev), + glam::DVec3::new(l.start.x, l.start.y, elev), )); id += 1; - out.push(center_grip( - id, - Vec3::new(l.end.x as f32, l.end.y as f32, elev), - )); + out.push(center_grip(id, glam::DVec3::new(l.end.x, l.end.y, elev))); id += 1; } BoundaryEdge::CircularArc(a) => { out.push(center_grip( id, - Vec3::new(a.center.x as f32, a.center.y as f32, elev), + glam::DVec3::new(a.center.x, a.center.y, elev), )); id += 1; } BoundaryEdge::EllipticArc(e) => { out.push(center_grip( id, - Vec3::new(e.center.x as f32, e.center.y as f32, elev), + glam::DVec3::new(e.center.x, e.center.y, elev), )); id += 1; } @@ -219,7 +212,7 @@ impl Grippable for Hatch { s.control_points.iter().map(|p| [p.x, p.y]).collect() }; for [x, y] in pts { - out.push(center_grip(id, Vec3::new(x as f32, y as f32, elev))); + out.push(center_grip(id, glam::DVec3::new(x, y, elev))); id += 1; } } @@ -330,24 +323,29 @@ impl Grippable for Hatch { } } - fn grip_menu( - &self, - _grip_id: usize, - ) -> Vec { + fn grip_menu(&self, _grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Origin Point", action: GripMenuAction::OriginPoint }, - GripMenuItem { label: "Hatch Angle", action: GripMenuAction::HatchAngle }, - GripMenuItem { label: "Hatch Scale", action: GripMenuAction::HatchScale }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Origin Point", + action: GripMenuAction::OriginPoint, + }, + GripMenuItem { + label: "Hatch Angle", + action: GripMenuAction::HatchAngle, + }, + GripMenuItem { + label: "Hatch Scale", + action: GripMenuAction::HatchScale, + }, ] } - fn apply_grip_menu( - &mut self, - _grip_id: usize, - _action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, _grip_id: usize, _action: crate::scene::object::GripMenuAction) { // Origin / Angle / Scale need a follow-up value — handled by // `apply_grip_menu_value`. } @@ -446,7 +444,11 @@ impl FallbackTess for Hatch { key_verts.push(p); continue; }; - let segs = arc_segments(arc.radius, arc.sweep.abs(), wire_chord_tol(arc.radius)); + let segs = arc_segments( + arc.radius, + arc.sweep.abs(), + wire_chord_tol(arc.radius), + ); for j in 0..segs { let s = arc.sample(j as f64 / segs as f64); let p = to_wcs(s[0], s[1]); @@ -509,10 +511,7 @@ impl FallbackTess for Hatch { + ell.major_axis_endpoint.y * ell.major_axis_endpoint.y) .sqrt(); let r_min = r_maj * ell.minor_axis_ratio; - let rot = ell - .major_axis_endpoint - .y - .atan2(ell.major_axis_endpoint.x); + let rot = ell.major_axis_endpoint.y.atan2(ell.major_axis_endpoint.x); let (sa, span) = arc_signed_span(ell.start_angle, ell.end_angle, ell.counter_clockwise); let segs = arc_segments(r_maj, span.abs(), wire_chord_tol(r_maj)); diff --git a/src/entities/insert.rs b/src/entities/insert.rs index df2a211a3..93526d987 100644 --- a/src/entities/insert.rs +++ b/src/entities/insert.rs @@ -7,15 +7,11 @@ use crate::command::EntityTransform; use crate::entities::common::{edit_prop as edit, parse_f64, ro_prop as ro, square_grip}; use crate::scene::object::{GripApply, GripDef, PropSection}; -use crate::scene::{block_cache, render, tessellate}; use crate::scene::wire_model::WireModel; +use crate::scene::{block_cache, render, tessellate}; fn grips(ins: &Insert) -> Vec { - let p = Vec3::new( - ins.insert_point.x as f32, - ins.insert_point.y as f32, - ins.insert_point.z as f32, - ); + let p = glam::DVec3::new(ins.insert_point.x, ins.insert_point.y, ins.insert_point.z); vec![square_grip(0, p)] } @@ -99,7 +95,10 @@ fn apply_transform(ins: &mut Insert, t: &EntityTransform) { crate::impl_entity_basics!(Insert); impl crate::entities::traits::FallbackTess for Insert { - fn fallback_geometry(&self, world_offset: [f64; 3]) -> crate::scene::tess_util::FallbackGeometry { + fn fallback_geometry( + &self, + world_offset: [f64; 3], + ) -> crate::scene::tess_util::FallbackGeometry { let [ox, oy, oz] = world_offset; let ip = Vec3::new( (self.insert_point.x - ox) as f32, @@ -154,15 +153,14 @@ pub(crate) fn append_insert_attribute_wires( continue; } let attr_entity = EntityType::AttributeEntity(attr.clone()); - let (sub_color, sub_plen, sub_pat, sub_lw_px, sub_aci) = - render::render_style_for_block_sub( - document, - &attr_entity, - ins_color, - ins_pat_len, - ins_pat, - ins_lw_px, - ); + let (sub_color, sub_plen, sub_pat, sub_lw_px, sub_aci) = render::render_style_for_block_sub( + document, + &attr_entity, + ins_color, + ins_pat_len, + ins_pat, + ins_lw_px, + ); let sub_color = render::adapt_to_bg(sub_color, bg_color); let sub_color = if is_xref && !sel { block_cache::fade_toward_bg(sub_color, bg_color) @@ -192,4 +190,3 @@ pub(crate) fn append_insert_attribute_wires( wires.extend(attr_wires); } } - diff --git a/src/entities/leader.rs b/src/entities/leader.rs index e7c81a4db..96d484600 100644 --- a/src/entities/leader.rs +++ b/src/entities/leader.rs @@ -4,7 +4,7 @@ use glam::Vec3; use crate::command::EntityTransform; use crate::entities::common::{center_grip, edit_prop as edit, ro_prop as ro, square_grip}; -use crate::entities::traits::{TruckConvertible}; +use crate::entities::traits::TruckConvertible; use crate::scene::acad_to_truck::{TruckEntity, TruckObject}; use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property}; use crate::scene::wire_model::TangentGeom; @@ -15,9 +15,7 @@ fn to_truck(leader: &Leader) -> TruckEntity { let verts = &leader.vertices; let nan = [f64::NAN; 3]; let p3 = |v: &acadrust::types::Vector3| -> [f64; 3] { [v.x, v.y, v.z] }; - let p3f = |v: &acadrust::types::Vector3| -> [f32; 3] { - [v.x as f32, v.y as f32, v.z as f32] - }; + let p3f = |v: &acadrust::types::Vector3| -> [f32; 3] { [v.x as f32, v.y as f32, v.z as f32] }; let mut points: Vec<[f64; 3]> = Vec::new(); let mut tangents: Vec = Vec::new(); @@ -67,7 +65,11 @@ fn to_truck(leader: &Leader) -> TruckEntity { if leader.hookline_enabled && verts.len() >= 2 { let last = verts.last().unwrap(); let prev = &verts[verts.len() - 2]; - let sign = if (last.x - prev.x) >= 0.0 { 1.0_f64 } else { -1.0_f64 }; + let sign = if (last.x - prev.x) >= 0.0 { + 1.0_f64 + } else { + -1.0_f64 + }; let len = leader.text_height * 1.5; let last_f = p3(last); points.push(nan); @@ -92,14 +94,14 @@ fn grips(leader: &Leader) -> Vec { .vertices .iter() .enumerate() - .map(|(i, v)| square_grip(i, Vec3::new(v.x as f32, v.y as f32, v.z as f32))) + .map(|(i, v)| square_grip(i, glam::DVec3::new(v.x, v.y, v.z))) .collect(); if n >= 2 { - let sum = leader.vertices.iter().fold(Vec3::ZERO, |acc, v| { - acc + Vec3::new(v.x as f32, v.y as f32, v.z as f32) + let sum = leader.vertices.iter().fold(glam::DVec3::ZERO, |acc, v| { + acc + glam::DVec3::new(v.x, v.y, v.z) }); - grips.push(center_grip(n, sum / n as f32)); + grips.push(center_grip(n, sum / n as f64)); } grips @@ -463,31 +465,39 @@ impl crate::entities::traits::Grippable for Leader { fn apply_grip(&mut self, grip_id: usize, apply: GripApply) { apply_grip(self, grip_id, apply); } - fn grip_menu( - &self, - grip_id: usize, - ) -> Vec { + fn grip_menu(&self, grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; let n = self.vertices.len(); if grip_id == 0 { // Arrow head — stretch only. - vec![GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }] + vec![GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }] } else if grip_id < n { vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Add Vertex", action: GripMenuAction::AddVertex }, - GripMenuItem { label: "Remove Vertex", action: GripMenuAction::RemoveVertex }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Add Vertex", + action: GripMenuAction::AddVertex, + }, + GripMenuItem { + label: "Remove Vertex", + action: GripMenuAction::RemoveVertex, + }, ] } else { // Centroid grip — move whole leader. - vec![GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }] + vec![GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }] } } - fn apply_grip_menu( - &mut self, - grip_id: usize, - action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, grip_id: usize, action: crate::scene::object::GripMenuAction) { use crate::scene::object::GripMenuAction as A; let n = self.vertices.len(); match action { @@ -514,10 +524,7 @@ impl crate::entities::traits::Grippable for Leader { } impl crate::entities::traits::PropertyEditable for Leader { - fn geometry_properties( - &self, - _text_style_names: &[String], - ) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { properties(self) } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/line.rs b/src/entities/line.rs index 8220c0f51..0c9fba4a8 100644 --- a/src/entities/line.rs +++ b/src/entities/line.rs @@ -1,5 +1,4 @@ use acadrust::{entities::Line, Entity}; -use glam::Vec3; use truck_modeling::{builder, Point3}; use crate::command::EntityTransform; @@ -67,12 +66,8 @@ fn to_truck(line: &Line) -> TruckEntity { } fn grips(line: &Line) -> Vec { - let s = Vec3::new( - line.start.x as f32, - line.start.y as f32, - line.start.z as f32, - ); - let e = Vec3::new(line.end.x as f32, line.end.y as f32, line.end.z as f32); + let s = glam::DVec3::new(line.start.x, line.start.y, line.start.z); + let e = glam::DVec3::new(line.end.x, line.end.y, line.end.z); let m = (s + e) * 0.5; vec![square_grip(0, s), square_grip(1, e), center_grip(2, m)] } @@ -163,25 +158,27 @@ impl crate::entities::traits::Grippable for Line { fn apply_grip(&mut self, grip_id: usize, apply: GripApply) { apply_grip(self, grip_id, apply); } - fn grip_menu( - &self, - grip_id: usize, - ) -> Vec { + fn grip_menu(&self, grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; if grip_id == 2 { - vec![GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }] + vec![GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }] } else { vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Lengthen", action: GripMenuAction::Lengthen }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Lengthen", + action: GripMenuAction::Lengthen, + }, ] } } - fn apply_grip_menu( - &mut self, - _grip_id: usize, - _action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, _grip_id: usize, _action: crate::scene::object::GripMenuAction) { // Lengthen needs a follow-up distance — handled by // `apply_grip_menu_value`. } @@ -235,10 +232,7 @@ impl crate::entities::traits::Grippable for Line { } impl crate::entities::traits::PropertyEditable for Line { - fn geometry_properties( - &self, - _text_style_names: &[String], - ) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { properties(self) } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/lwpolyline.rs b/src/entities/lwpolyline.rs index f67cc267d..714ef2356 100644 --- a/src/entities/lwpolyline.rs +++ b/src/entities/lwpolyline.rs @@ -1,12 +1,11 @@ use acadrust::entities::{LwPolyline, LwVertex}; -use glam::Vec3; use truck_modeling::{builder, Edge, Point3, Wire}; use crate::command::EntityTransform; use crate::entities::common::{ edit_prop as edit, parse_f64, rectangle_grip, ro_prop as ro, square_grip, }; -use crate::entities::traits::{TruckConvertible}; +use crate::entities::traits::TruckConvertible; use crate::scene::acad_to_truck::{TruckEntity, TruckObject}; use crate::scene::object::{GripApply, GripDef, PropSection}; use crate::scene::wire_model::TangentGeom; @@ -62,9 +61,7 @@ fn thick_segments( ) -> TruckEntity { let (nx, ny, nz) = normal; let t = thickness; - let off = |p: [f64; 3]| -> [f64; 3] { - [p[0] + t * nx, p[1] + t * ny, p[2] + t * nz] - }; + let off = |p: [f64; 3]| -> [f64; 3] { [p[0] + t * nx, p[1] + t * ny, p[2] + t * nz] }; let mut pts: Vec<[f64; 3]> = Vec::with_capacity(path_pts.len() * 2 + seg_data.len() * 3 + 4); // Bottom path pts.extend_from_slice(path_pts); @@ -280,7 +277,7 @@ fn to_truck(pline: &LwPolyline) -> TruckEntity { } fn grips(pline: &LwPolyline) -> Vec { - let elev = pline.elevation as f32; + let elev = pline.elevation; let n = pline.vertices.len(); let seg_count = if pline.is_closed { n @@ -292,7 +289,7 @@ fn grips(pline: &LwPolyline) -> Vec { .vertices .iter() .enumerate() - .map(|(i, v)| square_grip(i, Vec3::new(v.location.x as f32, v.location.y as f32, elev))) + .map(|(i, v)| square_grip(i, glam::DVec3::new(v.location.x, v.location.y, elev))) .collect(); // One mid-segment stretch grip per segment (straight or arc). The @@ -318,7 +315,7 @@ fn grips(pline: &LwPolyline) -> Vec { let dy = (v1.location.y - v0.location.y) as f32; out.push(rectangle_grip( n + i, - Vec3::new(mx as f32, my as f32, elev), + glam::DVec3::new(mx, my, elev), [dx, dy], )); } @@ -412,14 +409,8 @@ fn apply_grip(pline: &mut LwPolyline, grip_id: usize, apply: GripApply) { [old[0] + d.x as f64, old[1] + d.y as f64] } }; - let p0 = [ - pline.vertices[i0].location.x, - pline.vertices[i0].location.y, - ]; - let p1 = [ - pline.vertices[i1].location.x, - pline.vertices[i1].location.y, - ]; + let p0 = [pline.vertices[i0].location.x, pline.vertices[i0].location.y]; + let p1 = [pline.vertices[i1].location.x, pline.vertices[i1].location.y]; if let Some(new_bulge) = bulge_from_midpoint(p0, p1, new_mid) { pline.vertices[i0].bulge = new_bulge.clamp(-1e6, 1e6); } @@ -444,25 +435,27 @@ impl crate::entities::traits::Grippable for LwPolyline { fn grips(&self) -> Vec { grips(self) } - fn apply_grip( - &mut self, - grip_id: usize, - apply: crate::scene::object::GripApply, - ) { + fn apply_grip(&mut self, grip_id: usize, apply: crate::scene::object::GripApply) { apply_grip(self, grip_id, apply); } - fn grip_menu( - &self, - grip_id: usize, - ) -> Vec { + fn grip_menu(&self, grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; let n = self.vertices.len(); if grip_id < n { // Vertex grip. return vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Add Vertex", action: GripMenuAction::AddVertex }, - GripMenuItem { label: "Remove Vertex", action: GripMenuAction::RemoveVertex }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Add Vertex", + action: GripMenuAction::AddVertex, + }, + GripMenuItem { + label: "Remove Vertex", + action: GripMenuAction::RemoveVertex, + }, ]; } // Segment midpoint grip. @@ -472,21 +465,29 @@ impl crate::entities::traits::Grippable for LwPolyline { .get(seg) .map_or(false, |v| v.bulge.abs() >= 1e-9); let convert = if is_arc { - GripMenuItem { label: "Convert to Line", action: GripMenuAction::ConvertToLine } + GripMenuItem { + label: "Convert to Line", + action: GripMenuAction::ConvertToLine, + } } else { - GripMenuItem { label: "Convert to Arc", action: GripMenuAction::ConvertToArc } + GripMenuItem { + label: "Convert to Arc", + action: GripMenuAction::ConvertToArc, + } }; vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Add Vertex", action: GripMenuAction::AddVertex }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Add Vertex", + action: GripMenuAction::AddVertex, + }, convert, ] } - fn apply_grip_menu( - &mut self, - grip_id: usize, - action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, grip_id: usize, action: crate::scene::object::GripMenuAction) { use crate::scene::object::GripMenuAction as A; let n = self.vertices.len(); match action { @@ -583,7 +584,9 @@ pub(crate) fn wide_fills(pl: &acadrust::entities::LwPolyline) -> Vec [f64; 3] { [v.x, v.y, v.z] } +fn dvec3(v: &acadrust::types::Vector3) -> glam::DVec3 { + glam::DVec3::new(v.x, v.y, v.z) +} + fn v3f32(v: &acadrust::types::Vector3) -> [f32; 3] { [v.x as f32, v.y as f32, v.z as f32] } @@ -80,10 +84,10 @@ impl TruckConvertible for Face3D { impl Grippable for Face3D { fn grips(&self) -> Vec { vec![ - square_grip(0, Vec3::from(v3f32(&self.first_corner))), - square_grip(1, Vec3::from(v3f32(&self.second_corner))), - square_grip(2, Vec3::from(v3f32(&self.third_corner))), - square_grip(3, Vec3::from(v3f32(&self.fourth_corner))), + square_grip(0, dvec3(&self.first_corner)), + square_grip(1, dvec3(&self.second_corner)), + square_grip(2, dvec3(&self.third_corner)), + square_grip(3, dvec3(&self.fourth_corner)), ] } @@ -247,11 +251,7 @@ impl Grippable for PolygonMesh { .map(|(i, v)| { square_grip( i, - Vec3::new( - v.location.x as f32, - v.location.y as f32, - v.location.z as f32, - ), + glam::DVec3::new(v.location.x, v.location.y, v.location.z), ) }) .collect() @@ -300,7 +300,11 @@ impl PropertyEditable for PolygonMesh { "pm_smooth_n", self.n_smooth_density.to_string(), ), - ro("Elevation", "pm_elevation", format!("{:.4}", self.elevation)), + ro( + "Elevation", + "pm_elevation", + format!("{:.4}", self.elevation), + ), ro( "Normal", "pm_normal", @@ -395,11 +399,7 @@ impl Grippable for PolyfaceMesh { .map(|(i, v)| { square_grip( i, - Vec3::new( - v.location.x as f32, - v.location.y as f32, - v.location.z as f32, - ), + glam::DVec3::new(v.location.x, v.location.y, v.location.z), ) }) .collect() @@ -445,7 +445,11 @@ impl PropertyEditable for PolyfaceMesh { _ => "(none)".to_string(), }, ), - ro("Elevation", "pfm_elevation", format!("{:.4}", self.elevation)), + ro( + "Elevation", + "pfm_elevation", + format!("{:.4}", self.elevation), + ), ro( "Normal", "pfm_normal", @@ -454,7 +458,11 @@ impl PropertyEditable for PolyfaceMesh { self.normal.x, self.normal.y, self.normal.z ), ), - ro("Thickness", "pfm_thickness", format!("{:.4}", self.thickness)), + ro( + "Thickness", + "pfm_thickness", + format!("{:.4}", self.thickness), + ), ], } } @@ -490,9 +498,7 @@ impl TruckConvertible for Mesh { if self.vertices.is_empty() { return None; } - let get = |i: usize| -> Option<[f64; 3]> { - self.vertices.get(i).map(|v| [v.x, v.y, v.z]) - }; + let get = |i: usize| -> Option<[f64; 3]> { self.vertices.get(i).map(|v| [v.x, v.y, v.z]) }; let mut pts: Vec<[f64; 3]> = Vec::new(); if !self.edges.is_empty() { @@ -549,7 +555,12 @@ impl TruckConvertible for Mesh { let snap_pts: Vec<(Vec3, SnapHint)> = self .vertices .iter() - .map(|v| (Vec3::new(v.x as f32, v.y as f32, v.z as f32), SnapHint::Node)) + .map(|v| { + ( + Vec3::new(v.x as f32, v.y as f32, v.z as f32), + SnapHint::Node, + ) + }) .collect(); let key_vertices: Vec<[f64; 3]> = self.vertices.iter().map(|v| [v.x, v.y, v.z]).collect(); @@ -568,9 +579,7 @@ impl Grippable for Mesh { self.vertices .iter() .enumerate() - .map(|(i, v)| { - square_grip(i, Vec3::new(v.x as f32, v.y as f32, v.z as f32)) - }) + .map(|(i, v)| square_grip(i, glam::DVec3::new(v.x, v.y, v.z))) .collect() } diff --git a/src/entities/mline.rs b/src/entities/mline.rs index f2653039c..a6919e49a 100644 --- a/src/entities/mline.rs +++ b/src/entities/mline.rs @@ -126,11 +126,7 @@ impl Grippable for MLine { .map(|(i, v)| { square_grip( i, - Vec3::new( - v.position.x as f32, - v.position.y as f32, - v.position.z as f32, - ), + glam::DVec3::new(v.position.x, v.position.y, v.position.z), ) }) .collect() @@ -153,23 +149,25 @@ impl Grippable for MLine { } } - fn grip_menu( - &self, - _grip_id: usize, - ) -> Vec { + fn grip_menu(&self, _grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Add Vertex", action: GripMenuAction::AddVertex }, - GripMenuItem { label: "Remove Vertex", action: GripMenuAction::RemoveVertex }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Add Vertex", + action: GripMenuAction::AddVertex, + }, + GripMenuItem { + label: "Remove Vertex", + action: GripMenuAction::RemoveVertex, + }, ] } - fn apply_grip_menu( - &mut self, - grip_id: usize, - action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, grip_id: usize, action: crate::scene::object::GripMenuAction) { use crate::scene::object::GripMenuAction as A; let n = self.vertices.len(); match action { diff --git a/src/entities/mtext.rs b/src/entities/mtext.rs index 0a74e8588..482e35168 100644 --- a/src/entities/mtext.rs +++ b/src/entities/mtext.rs @@ -106,7 +106,11 @@ pub fn glyph_boxes(t: &MText, document: &acadrust::CadDocument) -> Vec }; let layout = layout_mtext(&MTextRenderOpts { value: &t.value, - insertion: [t.insertion_point.x, t.insertion_point.y, t.insertion_point.z], + insertion: [ + t.insertion_point.x, + t.insertion_point.y, + t.insertion_point.z, + ], height: t.height as f32, rect_w: t.rectangle_width as f32, rotation, @@ -149,7 +153,11 @@ fn to_truck(t: &MText, document: &acadrust::CadDocument) -> TruckEntity { }; let layout = layout_mtext(&MTextRenderOpts { value: &t.value, - insertion: [t.insertion_point.x, t.insertion_point.y, t.insertion_point.z], + insertion: [ + t.insertion_point.x, + t.insertion_point.y, + t.insertion_point.z, + ], height: t.height as f32, rect_w: t.rectangle_width as f32, rotation, @@ -175,13 +183,13 @@ fn to_truck(t: &MText, document: &acadrust::CadDocument) -> TruckEntity { } fn grips(t: &MText) -> Vec { - let p = Vec3::new( - t.insertion_point.x as f32, - t.insertion_point.y as f32, - t.insertion_point.z as f32, + let p = glam::DVec3::new( + t.insertion_point.x, + t.insertion_point.y, + t.insertion_point.z, ); - let dir = Vec3::new((t.rotation as f32).cos(), (t.rotation as f32).sin(), 0.0); - let width_grip = p + dir * t.rectangle_width.max(0.0) as f32; + let dir = glam::DVec3::new(t.rotation.cos(), t.rotation.sin(), 0.0); + let width_grip = p + dir * t.rectangle_width.max(0.0); vec![square_grip(0, p), triangle_grip(1, width_grip)] } @@ -348,29 +356,34 @@ impl Grippable for MText { apply_grip(self, grip_id, apply); } - fn grip_menu( - &self, - grip_id: usize, - ) -> Vec { + fn grip_menu(&self, grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; if grip_id == 0 { // Insertion point vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Move with Text", action: GripMenuAction::MoveWithText }, - GripMenuItem { label: "Rotate", action: GripMenuAction::RotateText }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Move with Text", + action: GripMenuAction::MoveWithText, + }, + GripMenuItem { + label: "Rotate", + action: GripMenuAction::RotateText, + }, ] } else { // Width grip - vec![GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }] + vec![GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }] } } - fn apply_grip_menu( - &mut self, - _grip_id: usize, - _action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, _grip_id: usize, _action: crate::scene::object::GripMenuAction) { // Rotate needs a follow-up angle handled by // `apply_grip_menu_value`; Move-with-Text is the default drag. } diff --git a/src/entities/multileader.rs b/src/entities/multileader.rs index 8ebef03da..8dd3ed202 100644 --- a/src/entities/multileader.rs +++ b/src/entities/multileader.rs @@ -1,6 +1,4 @@ -use acadrust::entities::{ - LeaderContentType, MultiLeader, MultiLeaderPathType, TextAttachmentType, -}; +use acadrust::entities::{LeaderContentType, MultiLeader, MultiLeaderPathType, TextAttachmentType}; use crate::entities::text_support::{ layout_mtext, resolve_text_style, MTextRenderOpts, MTextVAnchor, ResolvedTextStyle, @@ -32,7 +30,7 @@ use crate::command::EntityTransform; use crate::entities::common::{ center_grip, edit_prop as edit, ro_prop as ro, square_grip, triangle_grip, }; -use crate::entities::traits::{TruckConvertible}; +use crate::entities::traits::TruckConvertible; use crate::scene::acad_to_truck::{TruckEntity, TruckObject}; use crate::scene::object::{GripApply, GripDef, PropSection, PropValue, Property}; use crate::scene::wire_model::{SnapHint, TangentGeom}; @@ -104,58 +102,61 @@ fn to_truck(ml: &MultiLeader, document: &acadrust::CadDocument) -> Option= leader_ref.x { 1.0 } else { -1.0 }; + let text_sign: f64 = if text_loc.x >= leader_ref.x { + 1.0 + } else { + -1.0 + }; // Lay the text out once, up front: its width fixes where the landing meets // the text. The same layout is reused below to emit the glyph strokes. - let text_layout = if ml.content_type == LeaderContentType::MText - && !ml.context.text_string.is_empty() - { - let ctx = &ml.context; - let height = if ctx.text_height > 0.0 { - ctx.text_height as f32 + let text_layout = + if ml.content_type == LeaderContentType::MText && !ml.context.text_string.is_empty() { + let ctx = &ml.context; + let height = if ctx.text_height > 0.0 { + ctx.text_height as f32 + } else { + ml.text_height as f32 * ml.scale_factor as f32 + }; + let td = ctx.text_direction; + let mut rot = if td.x.abs() > 1e-9 || td.y.abs() > 1e-9 { + (td.y as f32).atan2(td.x as f32) + } else { + ctx.text_rotation as f32 + }; + let style_name = ctx + .text_style_handle + .as_ref() + .and_then(|h| { + document + .text_styles + .iter() + .find(|s| s.handle == *h) + .map(|s| s.name.clone()) + }) + .unwrap_or_else(|| "STANDARD".to_string()); + let resolved = resolve_text_style(&style_name, document); + if resolved.is_upside_down { + rot += std::f32::consts::PI; + } + Some(layout_mtext(&MTextRenderOpts { + value: &ctx.text_string, + insertion: [text_loc.x, text_loc.y, text_loc.z], + height, + rect_w: ctx.text_width as f32, + rotation: rot, + style: &resolved, + // Side-anchored on the leader-facing edge so the text reads + // outward; flips live with the leader/text side. + attach_h_anchor: if text_sign >= 0.0 { 0.0 } else { 1.0 }, + v_anchor: mleader_v_anchor(ctx.text_left_attachment), + line_spacing_factor: ctx.line_spacing_factor as f32, + vertical_text: false, + want_glyph_boxes: false, + })) } else { - ml.text_height as f32 * ml.scale_factor as f32 + None }; - let td = ctx.text_direction; - let mut rot = if td.x.abs() > 1e-9 || td.y.abs() > 1e-9 { - (td.y as f32).atan2(td.x as f32) - } else { - ctx.text_rotation as f32 - }; - let style_name = ctx - .text_style_handle - .as_ref() - .and_then(|h| { - document - .text_styles - .iter() - .find(|s| s.handle == *h) - .map(|s| s.name.clone()) - }) - .unwrap_or_else(|| "STANDARD".to_string()); - let resolved = resolve_text_style(&style_name, document); - if resolved.is_upside_down { - rot += std::f32::consts::PI; - } - Some(layout_mtext(&MTextRenderOpts { - value: &ctx.text_string, - insertion: [text_loc.x, text_loc.y, text_loc.z], - height, - rect_w: ctx.text_width as f32, - rotation: rot, - style: &resolved, - // Side-anchored on the leader-facing edge so the text reads - // outward; flips live with the leader/text side. - attach_h_anchor: if text_sign >= 0.0 { 0.0 } else { 1.0 }, - v_anchor: mleader_v_anchor(ctx.text_left_attachment), - line_spacing_factor: ctx.line_spacing_factor as f32, - vertical_text: false, - want_glyph_boxes: false, - })) - } else { - None - }; let dogleg = if ml.enable_landing && ml.enable_dogleg { ml.dogleg_length.max(0.0) } else { @@ -184,8 +185,9 @@ fn to_truck(ml: &MultiLeader, document: &acadrust::CadDocument) -> Option = line.points.iter().map(|p| p3(p)).collect(); let last_f = *ctrl.last().unwrap_or(&landing_pt); - let dist = - ((last_f[0] - landing_pt[0]).powi(2) + (last_f[1] - landing_pt[1]).powi(2)).sqrt(); + let dist = ((last_f[0] - landing_pt[0]).powi(2) + + (last_f[1] - landing_pt[1]).powi(2)) + .sqrt(); if dist > 1e-9 { ctrl.push(landing_pt); } @@ -348,10 +350,7 @@ fn grips(ml: &MultiLeader) -> Vec { for root in &ml.context.leader_roots { for line in &root.lines { for p in &line.points { - result.push(square_grip( - id, - Vec3::new(p.x as f32, p.y as f32, p.z as f32), - )); + result.push(square_grip(id, glam::DVec3::new(p.x, p.y, p.z))); id += 1; } } @@ -360,16 +359,10 @@ fn grips(ml: &MultiLeader) -> Vec { if ml.content_type == LeaderContentType::MText { let tl = &ml.context.text_location; // Text-location grip, then the wrap-width grip at the box's far edge. - result.push(center_grip( - id, - Vec3::new(tl.x as f32, tl.y as f32, tl.z as f32), - )); + result.push(center_grip(id, glam::DVec3::new(tl.x, tl.y, tl.z))); id += 1; let (_, far) = text_box_geom(ml); - result.push(triangle_grip( - id, - Vec3::new(far[0] as f32, far[1] as f32, far[2] as f32), - )); + result.push(triangle_grip(id, glam::DVec3::new(far[0], far[1], far[2]))); } result @@ -661,12 +654,20 @@ fn properties(ml: &MultiLeader) -> PropSection { ro( "Extend Leader", "extend_leader_to_text", - if ml.extend_leader_to_text { "Yes" } else { "No" }, + if ml.extend_leader_to_text { + "Yes" + } else { + "No" + }, ), ro( "Text Direction Negative", "text_direction_negative", - if ml.text_direction_negative { "Yes" } else { "No" }, + if ml.text_direction_negative { + "Yes" + } else { + "No" + }, ), ro( "Text Align In IPE", @@ -935,10 +936,7 @@ impl crate::entities::traits::Grippable for MultiLeader { fn apply_grip(&mut self, grip_id: usize, apply: GripApply) { apply_grip(self, grip_id, apply); } - fn grip_menu( - &self, - grip_id: usize, - ) -> Vec { + fn grip_menu(&self, grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; let n_vertices: usize = self .context @@ -951,7 +949,10 @@ impl crate::entities::traits::Grippable for MultiLeader { if grip_id == n_vertices { // Text-location grip. vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, GripMenuItem { label: "Move with Leader", action: GripMenuAction::MoveWithLeader, @@ -968,17 +969,22 @@ impl crate::entities::traits::Grippable for MultiLeader { } else { // Leader-line vertex. vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Add Leader", action: GripMenuAction::AddLeader }, - GripMenuItem { label: "Remove Leader", action: GripMenuAction::RemoveLeader }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Add Leader", + action: GripMenuAction::AddLeader, + }, + GripMenuItem { + label: "Remove Leader", + action: GripMenuAction::RemoveLeader, + }, ] } } - fn apply_grip_menu( - &mut self, - grip_id: usize, - action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, grip_id: usize, action: crate::scene::object::GripMenuAction) { use crate::scene::object::GripMenuAction as A; // Locate the (root, line) and vertex position owning this grip id. let mut idx = 0usize; @@ -996,8 +1002,12 @@ impl crate::entities::traits::Grippable for MultiLeader { let Some((ri, li, vpos)) = loc else { return }; match action { A::RemoveLeader => { - let total: usize = - self.context.leader_roots.iter().map(|r| r.lines.len()).sum(); + let total: usize = self + .context + .leader_roots + .iter() + .map(|r| r.lines.len()) + .sum(); if total > 1 { self.context.leader_roots[ri].lines.remove(li); if self.context.leader_roots[ri].lines.is_empty() @@ -1024,10 +1034,7 @@ impl crate::entities::traits::Grippable for MultiLeader { } impl crate::entities::traits::PropertyEditable for MultiLeader { - fn geometry_properties( - &self, - _text_style_names: &[String], - ) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { properties(self) } fn apply_geom_prop(&mut self, field: &str, value: &str) { @@ -1088,9 +1095,7 @@ impl MultiLeaderTess for MultiLeader { // (ByLayer/ByBlock/Default) fall through to the entity's already-resolved // pixel width. let leader_lw_px = match ml.line_weight { - acadrust::types::LineWeight::Value(v) if v >= 0 => { - (v as f32 / 100.0) * (96.0 / 25.4) - } + acadrust::types::LineWeight::Value(v) if v >= 0 => (v as f32 / 100.0) * (96.0 / 25.4), _ => line_weight_px, }; // ml.line_type_handle — resolve via line_types table by handle and apply @@ -1104,7 +1109,9 @@ impl MultiLeaderTess for MultiLeader { .find(|lt| lt.handle == h) .map(|lt| lt.name.clone()); match name { - Some(n) => crate::scene::render::resolve_pattern(&document.line_types, &n, lt_scale), + Some(n) => { + crate::scene::render::resolve_pattern(&document.line_types, &n, lt_scale) + } None => (0.0, [0.0; 8]), } } @@ -1163,7 +1170,11 @@ impl MultiLeaderTess for MultiLeader { .copied() .or_else(|| ml.context.leader_roots.first().map(|r| r.connection_point)) .unwrap_or(text_loc_w); - let text_sign_w: f64 = if text_loc_w.x >= leader_ref_w.x { 1.0 } else { -1.0 }; + let text_sign_w: f64 = if text_loc_w.x >= leader_ref_w.x { + 1.0 + } else { + -1.0 + }; for root in &ml.context.leader_roots { let cp = &root.connection_point; @@ -1183,7 +1194,8 @@ impl MultiLeaderTess for MultiLeader { let mut ctrl: Vec<[f32; 3]> = line.points.iter().map(|p| p3(p)).collect(); let last_f = *ctrl.last().unwrap_or(&cp_f); - let dist = ((last_f[0] - cp_f[0]).powi(2) + (last_f[1] - cp_f[1]).powi(2)).sqrt(); + let dist = + ((last_f[0] - cp_f[0]).powi(2) + (last_f[1] - cp_f[1]).powi(2)).sqrt(); if dist > 1e-9 { ctrl.push(cp_f); } @@ -1251,8 +1263,7 @@ impl MultiLeaderTess for MultiLeader { points.push(landing_end); // Continue the landing to the text's near edge so it stays // attached as the text moves. - if ml.content_type == LeaderContentType::MText - && !ml.context.text_string.is_empty() + if ml.content_type == LeaderContentType::MText && !ml.context.text_string.is_empty() { let tx = (ml.context.text_location.x - ox) as f32; let ty = (ml.context.text_location.y - oy) as f32; @@ -1520,10 +1531,7 @@ impl MultiLeaderTess for MultiLeader { pattern_length: 0.0, pattern: [0.0; 8], line_weight_px, - snap_pts: vec![( - Vec3::new(local_ins_x, local_ins_y, z), - SnapHint::Node, - )], + snap_pts: vec![(Vec3::new(local_ins_x, local_ins_y, z), SnapHint::Node)], tangent_geoms: vec![], key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, @@ -1563,10 +1571,7 @@ impl MultiLeaderTess for MultiLeader { pattern_length: 0.0, pattern: [0.0; 8], line_weight_px: 1.0, - snap_pts: vec![( - Vec3::new(local_ins_x, local_ins_y, z), - SnapHint::Node, - )], + snap_pts: vec![(Vec3::new(local_ins_x, local_ins_y, z), SnapHint::Node)], tangent_geoms: vec![], key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, @@ -1604,10 +1609,7 @@ impl MultiLeaderTess for MultiLeader { pattern_length: 0.0, pattern: [0.0; 8], line_weight_px, - snap_pts: vec![( - Vec3::new(local_ins_x, local_ins_y, z), - SnapHint::Node, - )], + snap_pts: vec![(Vec3::new(local_ins_x, local_ins_y, z), SnapHint::Node)], tangent_geoms: vec![], key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, diff --git a/src/entities/ole2frame.rs b/src/entities/ole2frame.rs index f3138d460..2b1d75213 100644 --- a/src/entities/ole2frame.rs +++ b/src/entities/ole2frame.rs @@ -3,7 +3,7 @@ use glam::Vec3; use crate::command::EntityTransform; use crate::entities::common::{center_grip, edit_prop as edit, ro_prop as ro, square_grip}; -use crate::entities::traits::{TruckConvertible}; +use crate::entities::traits::TruckConvertible; use crate::scene::acad_to_truck::{TruckEntity, TruckObject}; use crate::scene::object::{GripApply, GripDef, PropSection}; use crate::scene::wire_model::SnapHint; @@ -56,15 +56,15 @@ fn to_truck(ole: &Ole2Frame) -> TruckEntity { } fn grips(ole: &Ole2Frame) -> Vec { - let ul = Vec3::new( - ole.upper_left_corner.x as f32, - ole.upper_left_corner.y as f32, - ole.upper_left_corner.z as f32, + let ul = glam::DVec3::new( + ole.upper_left_corner.x, + ole.upper_left_corner.y, + ole.upper_left_corner.z, ); - let lr = Vec3::new( - ole.lower_right_corner.x as f32, - ole.lower_right_corner.y as f32, - ole.lower_right_corner.z as f32, + let lr = glam::DVec3::new( + ole.lower_right_corner.x, + ole.lower_right_corner.y, + ole.lower_right_corner.z, ); let center = (ul + lr) * 0.5; vec![ @@ -181,7 +181,10 @@ impl TruckConvertible for Ole2Frame { crate::impl_entity_basics!(Ole2Frame); impl crate::entities::traits::FallbackTess for Ole2Frame { - fn fallback_geometry(&self, world_offset: [f64; 3]) -> crate::scene::tess_util::FallbackGeometry { + fn fallback_geometry( + &self, + world_offset: [f64; 3], + ) -> crate::scene::tess_util::FallbackGeometry { // OLE objects carry a bounding rectangle in model space. // Render a simple X-through-rectangle placeholder. let [ox, oy, oz] = world_offset; @@ -193,12 +196,7 @@ impl crate::entities::traits::FallbackTess for Ole2Frame { if (x1 - x0).abs() < 1e-6 && (y1 - y0).abs() < 1e-6 { // Degenerate / unknown size — show a small cross. let s = 0.5_f32; - return ( - vec![[-s, 0.0, 0.0], [s, 0.0, 0.0]], - vec![], - vec![], - vec![], - ); + return (vec![[-s, 0.0, 0.0], [s, 0.0, 0.0]], vec![], vec![], vec![]); } let pts = vec![ // Outer rectangle diff --git a/src/entities/point.rs b/src/entities/point.rs index b29b63cf4..c9ca95f16 100644 --- a/src/entities/point.rs +++ b/src/entities/point.rs @@ -132,11 +132,7 @@ fn to_truck(pt: &Point, document: &acadrust::CadDocument) -> TruckEntity { } fn grips(pt: &Point) -> Vec { - let p = Vec3::new( - pt.location.x as f32, - pt.location.y as f32, - pt.location.z as f32, - ); + let p = glam::DVec3::new(pt.location.x, pt.location.y, pt.location.z); vec![square_grip(0, p)] } @@ -196,4 +192,3 @@ impl TruckConvertible for Point { } crate::impl_entity_basics!(Point); - diff --git a/src/entities/polyline.rs b/src/entities/polyline.rs index e744fc278..ea222386d 100644 --- a/src/entities/polyline.rs +++ b/src/entities/polyline.rs @@ -1,5 +1,4 @@ use acadrust::entities::{Polyline, Polyline2D, Polyline3D}; -use glam::Vec3; use truck_modeling::{builder, Edge, Point3, Wire}; use crate::command::EntityTransform; @@ -47,11 +46,7 @@ impl Grippable for Polyline { .map(|(i, v)| { square_grip( i, - Vec3::new( - v.location.x as f32, - v.location.y as f32, - v.location.z as f32, - ), + glam::DVec3::new(v.location.x, v.location.y, v.location.z), ) }) .collect() @@ -74,23 +69,25 @@ impl Grippable for Polyline { } } - fn grip_menu( - &self, - _grip_id: usize, - ) -> Vec { + fn grip_menu(&self, _grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Add Vertex", action: GripMenuAction::AddVertex }, - GripMenuItem { label: "Remove Vertex", action: GripMenuAction::RemoveVertex }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Add Vertex", + action: GripMenuAction::AddVertex, + }, + GripMenuItem { + label: "Remove Vertex", + action: GripMenuAction::RemoveVertex, + }, ] } - fn apply_grip_menu( - &mut self, - grip_id: usize, - action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, grip_id: usize, action: crate::scene::object::GripMenuAction) { use crate::scene::object::GripMenuAction as A; let n = self.vertices.len(); match action { @@ -197,12 +194,8 @@ fn tessellate_polyline2d(pl: &Polyline2D) -> TruckEntity { if pl.thickness.abs() > 1e-10 { let (nx, ny, nz) = normal; let t = pl.thickness; - let off = |p: [f64; 3]| -> [f64; 3] { - [p[0] + t * nx, p[1] + t * ny, p[2] + t * nz] - }; - let to_f32 = |p: [f64; 3]| -> [f32; 3] { - [p[0] as f32, p[1] as f32, p[2] as f32] - }; + let off = |p: [f64; 3]| -> [f64; 3] { [p[0] + t * nx, p[1] + t * ny, p[2] + t * nz] }; + let to_f32 = |p: [f64; 3]| -> [f32; 3] { [p[0] as f32, p[1] as f32, p[2] as f32] }; let mut path: Vec<[f64; 3]> = Vec::new(); let mut kv: Vec<[f64; 3]> = Vec::new(); let mut tgs: Vec = Vec::new(); @@ -322,11 +315,11 @@ impl TruckConvertible for Polyline2D { impl Grippable for Polyline2D { fn grips(&self) -> Vec { - let elev = self.elevation as f32; + let elev = self.elevation; self.vertices .iter() .enumerate() - .map(|(i, v)| square_grip(i, Vec3::new(v.location.x as f32, v.location.y as f32, elev))) + .map(|(i, v)| square_grip(i, glam::DVec3::new(v.location.x, v.location.y, elev))) .collect() } @@ -345,23 +338,25 @@ impl Grippable for Polyline2D { } } - fn grip_menu( - &self, - _grip_id: usize, - ) -> Vec { + fn grip_menu(&self, _grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Add Vertex", action: GripMenuAction::AddVertex }, - GripMenuItem { label: "Remove Vertex", action: GripMenuAction::RemoveVertex }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Add Vertex", + action: GripMenuAction::AddVertex, + }, + GripMenuItem { + label: "Remove Vertex", + action: GripMenuAction::RemoveVertex, + }, ] } - fn apply_grip_menu( - &mut self, - grip_id: usize, - action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, grip_id: usize, action: crate::scene::object::GripMenuAction) { use crate::scene::object::GripMenuAction as A; let n = self.vertices.len(); let elev = self.elevation; @@ -535,11 +530,7 @@ impl Grippable for Polyline3D { .map(|(i, v)| { square_grip( i, - Vec3::new( - v.position.x as f32, - v.position.y as f32, - v.position.z as f32, - ), + glam::DVec3::new(v.position.x, v.position.y, v.position.z), ) }) .collect() @@ -562,23 +553,25 @@ impl Grippable for Polyline3D { } } - fn grip_menu( - &self, - _grip_id: usize, - ) -> Vec { + fn grip_menu(&self, _grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Add Vertex", action: GripMenuAction::AddVertex }, - GripMenuItem { label: "Remove Vertex", action: GripMenuAction::RemoveVertex }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Add Vertex", + action: GripMenuAction::AddVertex, + }, + GripMenuItem { + label: "Remove Vertex", + action: GripMenuAction::RemoveVertex, + }, ] } - fn apply_grip_menu( - &mut self, - grip_id: usize, - action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, grip_id: usize, action: crate::scene::object::GripMenuAction) { use crate::scene::object::GripMenuAction as A; let n = self.vertices.len(); match action { @@ -713,7 +706,9 @@ pub(crate) fn wide_fills(pl: &acadrust::entities::Polyline2D) -> Vec [f32; 3] { - [p[0] as f32, p[1] as f32, p[2] as f32] -} - /// Rectangle border + X diagonals — used as a placeholder for images. fn image_wire(corners: [[f64; 3]; 4], with_x: bool) -> Vec<[f64; 3]> { let [p0, p1, p2, p3] = corners; @@ -139,10 +134,10 @@ impl Grippable for RasterImage { self.size.y, ); vec![ - square_grip(0, Vec3::from(to_f32_3(corners[0]))), - center_grip(1, Vec3::from(to_f32_3(corners[1]))), - center_grip(2, Vec3::from(to_f32_3(corners[2]))), - center_grip(3, Vec3::from(to_f32_3(corners[3]))), + square_grip(0, glam::DVec3::from(corners[0])), + center_grip(1, glam::DVec3::from(corners[1])), + center_grip(2, glam::DVec3::from(corners[2])), + center_grip(3, glam::DVec3::from(corners[3])), ] } @@ -329,9 +324,9 @@ impl Grippable for Wipeout { ); if is_polygon { - let ox = self.insertion_point.x as f32; - let oy = self.insertion_point.y as f32; - let oz = self.insertion_point.z as f32; + let ox = self.insertion_point.x; + let oy = self.insertion_point.y; + let oz = self.insertion_point.z; // Same image-pixel-space → WCS mapping as `to_truck` so grips // sit exactly on the rendered polygon vertices. self.clip_boundary_vertices @@ -340,13 +335,13 @@ impl Grippable for Wipeout { .map(|(i, v)| { let cx = v.x + self.size.x * 0.5; let cy = self.size.y * 0.5 - v.y; - let wx = (self.u_vector.x * cx + self.v_vector.x * cy) as f32; - let wy = (self.u_vector.y * cx + self.v_vector.y * cy) as f32; - let wz = (self.u_vector.z * cx + self.v_vector.z * cy) as f32; + let wx = self.u_vector.x * cx + self.v_vector.x * cy; + let wy = self.u_vector.y * cx + self.v_vector.y * cy; + let wz = self.u_vector.z * cx + self.v_vector.z * cy; if i == 0 { - square_grip(i, Vec3::new(ox + wx, oy + wy, oz + wz)) + square_grip(i, glam::DVec3::new(ox + wx, oy + wy, oz + wz)) } else { - center_grip(i, Vec3::new(ox + wx, oy + wy, oz + wz)) + center_grip(i, glam::DVec3::new(ox + wx, oy + wy, oz + wz)) } }) .collect() @@ -359,10 +354,10 @@ impl Grippable for Wipeout { self.size.y, ); vec![ - square_grip(0, Vec3::from(to_f32_3(corners[0]))), - center_grip(1, Vec3::from(to_f32_3(corners[1]))), - center_grip(2, Vec3::from(to_f32_3(corners[2]))), - center_grip(3, Vec3::from(to_f32_3(corners[3]))), + square_grip(0, glam::DVec3::from(corners[0])), + center_grip(1, glam::DVec3::from(corners[1])), + center_grip(2, glam::DVec3::from(corners[2])), + center_grip(3, glam::DVec3::from(corners[3])), ] } } @@ -451,11 +446,7 @@ impl PropertyEditable for Wipeout { "wo_class_version", self.class_version.to_string(), ), - ro( - "Clip Mode", - "wo_clip_mode", - format!("{:?}", self.clip_mode), - ), + ro("Clip Mode", "wo_clip_mode", format!("{:?}", self.clip_mode)), ro( "Definition", "wo_def_handle", diff --git a/src/entities/ray.rs b/src/entities/ray.rs index c94c5488f..de39a02d2 100644 --- a/src/entities/ray.rs +++ b/src/entities/ray.rs @@ -1,5 +1,4 @@ use acadrust::entities::{Ray, XLine}; -use glam::Vec3; use crate::command::EntityTransform; use crate::entities::common::{center_grip, edit_prop as edit, square_grip}; @@ -47,13 +46,13 @@ impl Grippable for Ray { // Grip 1: a point along the direction (changes direction) let guide_dist = 10.0_f64; vec![ - square_grip(0, Vec3::new(bp.x as f32, bp.y as f32, bp.z as f32)), + square_grip(0, glam::DVec3::new(bp.x, bp.y, bp.z)), center_grip( 1, - Vec3::new( - (bp.x + dir.x * guide_dist) as f32, - (bp.y + dir.y * guide_dist) as f32, - (bp.z + dir.z * guide_dist) as f32, + glam::DVec3::new( + bp.x + dir.x * guide_dist, + bp.y + dir.y * guide_dist, + bp.z + dir.z * guide_dist, ), ), ] @@ -185,13 +184,13 @@ impl Grippable for XLine { let dir = &self.direction; let guide_dist = 10.0_f64; vec![ - square_grip(0, Vec3::new(bp.x as f32, bp.y as f32, bp.z as f32)), + square_grip(0, glam::DVec3::new(bp.x, bp.y, bp.z)), center_grip( 1, - Vec3::new( - (bp.x + dir.x * guide_dist) as f32, - (bp.y + dir.y * guide_dist) as f32, - (bp.z + dir.z * guide_dist) as f32, + glam::DVec3::new( + bp.x + dir.x * guide_dist, + bp.y + dir.y * guide_dist, + bp.z + dir.z * guide_dist, ), ), ] diff --git a/src/entities/shape.rs b/src/entities/shape.rs index 87d9cc296..11085242f 100644 --- a/src/entities/shape.rs +++ b/src/entities/shape.rs @@ -31,7 +31,11 @@ fn shape_marker( oblique_angle: f64, ) -> Vec<[f64; 3]> { let s = size.abs().max(0.001) * 0.5; - let rx = s * if rel_x_scale.abs() < 1e-9 { 1.0 } else { rel_x_scale }; + let rx = s * if rel_x_scale.abs() < 1e-9 { + 1.0 + } else { + rel_x_scale + }; let ry = s; let local = [(0.0, ry), (rx, 0.0), (0.0, -ry), (-rx, 0.0)]; let (sin_r, cos_r) = (rotation.sin(), rotation.cos()); @@ -86,10 +90,10 @@ impl Grippable for Shape { fn grips(&self) -> Vec { vec![square_grip( 0, - Vec3::new( - self.insertion_point.x as f32, - self.insertion_point.y as f32, - self.insertion_point.z as f32, + glam::DVec3::new( + self.insertion_point.x, + self.insertion_point.y, + self.insertion_point.z, ), )] } diff --git a/src/entities/solid.rs b/src/entities/solid.rs index 557da0e28..74e7a66e3 100644 --- a/src/entities/solid.rs +++ b/src/entities/solid.rs @@ -18,6 +18,10 @@ fn v3(v: &acadrust::types::Vector3) -> [f64; 3] { [v.x, v.y, v.z] } +fn dvec3(v: &acadrust::types::Vector3) -> glam::DVec3 { + glam::DVec3::new(v.x, v.y, v.z) +} + fn v3f32(v: &acadrust::types::Vector3) -> [f32; 3] { [v.x as f32, v.y as f32, v.z as f32] } @@ -66,10 +70,10 @@ impl TruckConvertible for Solid { impl Grippable for Solid { fn grips(&self) -> Vec { vec![ - square_grip(0, Vec3::from(v3f32(&self.first_corner))), - square_grip(1, Vec3::from(v3f32(&self.second_corner))), - square_grip(2, Vec3::from(v3f32(&self.third_corner))), - square_grip(3, Vec3::from(v3f32(&self.fourth_corner))), + square_grip(0, dvec3(&self.first_corner)), + square_grip(1, dvec3(&self.second_corner)), + square_grip(2, dvec3(&self.third_corner)), + square_grip(3, dvec3(&self.fourth_corner)), ] } diff --git a/src/entities/solid3d.rs b/src/entities/solid3d.rs index 58bd5cb77..1ca2a305e 100644 --- a/src/entities/solid3d.rs +++ b/src/entities/solid3d.rs @@ -15,8 +15,8 @@ use crate::scene::object::{GripApply, GripDef, PropSection}; // ── shared helpers ──────────────────────────────────────────────────────────── -fn por_to_vec3(v: &acadrust::types::Vector3) -> Vec3 { - Vec3::new(v.x as f32, v.y as f32, v.z as f32) +fn dvec3(v: &acadrust::types::Vector3) -> glam::DVec3 { + glam::DVec3::new(v.x, v.y, v.z) } fn translate_wires(wires: &mut Vec, d: Vec3) { @@ -44,7 +44,7 @@ fn acis_size_str(has_data: bool, sat_len: usize, sab_len: usize, is_binary: bool impl Grippable for Solid3D { fn grips(&self) -> Vec { - vec![center_grip(0, por_to_vec3(&self.point_of_reference))] + vec![center_grip(0, dvec3(&self.point_of_reference))] } fn apply_grip(&mut self, grip_id: usize, apply: GripApply) { @@ -120,7 +120,7 @@ impl PropertyEditable for Solid3D { impl Grippable for Region { fn grips(&self) -> Vec { - vec![center_grip(0, por_to_vec3(&self.point_of_reference))] + vec![center_grip(0, dvec3(&self.point_of_reference))] } fn apply_grip(&mut self, grip_id: usize, apply: GripApply) { @@ -188,7 +188,7 @@ impl PropertyEditable for Region { impl Grippable for Body { fn grips(&self) -> Vec { - vec![center_grip(0, por_to_vec3(&self.point_of_reference))] + vec![center_grip(0, dvec3(&self.point_of_reference))] } fn apply_grip(&mut self, grip_id: usize, apply: GripApply) { @@ -260,9 +260,9 @@ impl PropertyEditable for Body { // to repeat a three-arm `match entity` block at every callsite — the // helpers below collapse those to a single call. -use acadrust::{types::Vector3, EntityType}; use crate::scene::mesh_model::MeshLodSet; use crate::scene::solid3d_tess; +use acadrust::{types::Vector3, EntityType}; /// `point_of_reference` of an ACIS-backed volume entity, if applicable. pub fn point_of_reference(e: &EntityType) -> Option<&Vector3> { @@ -288,11 +288,7 @@ pub fn fallback_wires(e: &EntityType) -> Option<&[acadrust::entities::Wire]> { /// Run the appropriate `solid3d_tess::tessellate_*` for the entity, /// returning `None` for non-volume entities or when the kernel fails. -pub fn tessellate_volume( - e: &EntityType, - color: [f32; 4], - facet_res: f64, -) -> Option { +pub fn tessellate_volume(e: &EntityType, color: [f32; 4], facet_res: f64) -> Option { match e { EntityType::Solid3D(s) => solid3d_tess::tessellate_solid3d(s, color, facet_res), EntityType::Region(r) => solid3d_tess::tessellate_region(r, color, facet_res), diff --git a/src/entities/spline.rs b/src/entities/spline.rs index 933e0ccb6..01b57c0d4 100644 --- a/src/entities/spline.rs +++ b/src/entities/spline.rs @@ -1,5 +1,4 @@ use acadrust::entities::Spline; -use glam::Vec3; use truck_modeling::{ base::{BoundedCurve, ParametricCurve, Vector4}, builder, BSplineCurve, Curve, Edge, KnotVec, NurbsCurve, Point3, Wire, @@ -64,10 +63,7 @@ fn to_truck(spl: &Spline) -> TruckEntity { } else { &spl.control_points }; - let key_vertices: Vec<[f64; 3]> = snap_source - .iter() - .map(|p| [p.x, p.y, p.z]) - .collect(); + let key_vertices: Vec<[f64; 3]> = snap_source.iter().map(|p| [p.x, p.y, p.z]).collect(); let is_closed = spl.flags.closed || spl.flags.periodic; let gap = { @@ -106,7 +102,7 @@ fn grips(spline: &Spline) -> Vec { .control_points .iter() .enumerate() - .map(|(i, p)| square_grip(i, Vec3::new(p.x as f32, p.y as f32, p.z as f32))) + .map(|(i, p)| square_grip(i, glam::DVec3::new(p.x, p.y, p.z))) .collect() } @@ -168,23 +164,28 @@ impl crate::entities::traits::Grippable for Spline { fn apply_grip(&mut self, grip_id: usize, apply: GripApply) { apply_grip(self, grip_id, apply); } - fn grip_menu( - &self, - _grip_id: usize, - ) -> Vec { + fn grip_menu(&self, _grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Add Vertex", action: GripMenuAction::AddVertex }, - GripMenuItem { label: "Remove Vertex", action: GripMenuAction::RemoveVertex }, - GripMenuItem { label: "Refine Vertices", action: GripMenuAction::RefineVertices }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Add Vertex", + action: GripMenuAction::AddVertex, + }, + GripMenuItem { + label: "Remove Vertex", + action: GripMenuAction::RemoveVertex, + }, + GripMenuItem { + label: "Refine Vertices", + action: GripMenuAction::RefineVertices, + }, ] } - fn apply_grip_menu( - &mut self, - grip_id: usize, - action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, grip_id: usize, action: crate::scene::object::GripMenuAction) { use crate::scene::object::GripMenuAction as A; let n = self.control_points.len(); let min_cv = (self.degree as usize).saturating_add(1).max(2); @@ -203,7 +204,8 @@ impl crate::entities::traits::Grippable for Spline { ); self.control_points.insert(i1, mid); if !self.weights.is_empty() && self.weights.len() == n { - let w = (self.weights[grip_id] + self.weights[i1.min(self.weights.len() - 1)]) * 0.5; + let w = (self.weights[grip_id] + self.weights[i1.min(self.weights.len() - 1)]) + * 0.5; self.weights.insert(i1, w); } // Clear knots so to_truck rebuilds a uniform knot vector @@ -255,10 +257,7 @@ impl crate::entities::traits::Grippable for Spline { } impl crate::entities::traits::PropertyEditable for Spline { - fn geometry_properties( - &self, - _text_style_names: &[String], - ) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { properties(self) } fn apply_geom_prop(&mut self, field: &str, value: &str) { @@ -271,4 +270,3 @@ impl crate::entities::traits::Transformable for Spline { apply_transform(self, t); } } - diff --git a/src/entities/table.rs b/src/entities/table.rs index 416360cb9..617992971 100644 --- a/src/entities/table.rs +++ b/src/entities/table.rs @@ -3,12 +3,14 @@ use glam::Vec3; use crate::command::EntityTransform; use crate::entities::common::{ro_prop as ro, square_grip}; -use crate::entities::text_support::{layout_mtext, MTextRenderOpts, MTextVAnchor, ResolvedTextStyle}; +use crate::entities::text_support::{ + layout_mtext, MTextRenderOpts, MTextVAnchor, ResolvedTextStyle, +}; use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible}; use crate::scene::acad_to_truck::{TruckEntity, TruckObject}; use crate::scene::object::{GripApply, GripDef, PropSection}; -use crate::scene::wire_model::SnapHint; use crate::scene::transform; +use crate::scene::wire_model::SnapHint; fn v3(v: &acadrust::types::Vector3) -> Vec3 { Vec3::new(v.x as f32, v.y as f32, v.z as f32) @@ -61,7 +63,11 @@ impl TruckConvertible for Table { // twice we coalesce the segments by their (start, end) coordinates. use rustc_hash::FxHashSet as HashSet; let mut emitted: HashSet<(i32, i32, i32, i32)> = HashSet::default(); - let try_add = |a: Vec3, b: Vec3, vis: bool, emitted: &mut HashSet<(i32, i32, i32, i32)>, pts: &mut Vec<[f32; 3]>| { + let try_add = |a: Vec3, + b: Vec3, + vis: bool, + emitted: &mut HashSet<(i32, i32, i32, i32)>, + pts: &mut Vec<[f32; 3]>| { if !vis { return; } @@ -90,11 +96,9 @@ impl TruckConvertible for Table { .unwrap_or(row_top + row.height as f32); for (ci, cell) in row.cells.iter().enumerate() { let col_left = col_offsets[ci]; - let col_right = col_offsets - .get(ci + 1) - .copied() - .unwrap_or(col_left - + self.columns.get(ci).map(|c| c.width as f32).unwrap_or(1.0)); + let col_right = col_offsets.get(ci + 1).copied().unwrap_or( + col_left + self.columns.get(ci).map(|c| c.width as f32).unwrap_or(1.0), + ); // Default to visible when no style override is present. let (top_vis, right_vis, bottom_vis, left_vis) = cell .style @@ -160,18 +164,17 @@ impl TruckConvertible for Table { }; // Build a ResolvedTextStyle for the cell — needed by the shared MText // pipeline so inline `\W`, `\Q`, etc. compose with the style baseline. - let resolved_style_for_handle = |handle: Option, - font_name: String| - -> ResolvedTextStyle { - let style = handle.and_then(|h| lookup_style(h)); - ResolvedTextStyle { - font_name, - width_factor: style.map(|s| s.width_factor as f32).unwrap_or(1.0), - oblique_angle: style.map(|s| s.oblique_angle as f32).unwrap_or(0.0), - is_backward: style.map(|s| s.is_backward()).unwrap_or(false), - is_upside_down: style.map(|s| s.is_upside_down()).unwrap_or(false), - } - }; + let resolved_style_for_handle = + |handle: Option, font_name: String| -> ResolvedTextStyle { + let style = handle.and_then(|h| lookup_style(h)); + ResolvedTextStyle { + font_name, + width_factor: style.map(|s| s.width_factor as f32).unwrap_or(1.0), + oblique_angle: style.map(|s| s.oblique_angle as f32).unwrap_or(0.0), + is_backward: style.map(|s| s.is_backward()).unwrap_or(false), + is_upside_down: style.map(|s| s.is_upside_down()).unwrap_or(false), + } + }; for (ri, row) in self.rows.iter().enumerate() { let row_top = row_offsets[ri]; @@ -182,20 +185,19 @@ impl TruckConvertible for Table { let row_mid = (row_top + row_bot) * 0.5; // Pick the appropriate row_style from TableStyle for this row's role. - let row_style: Option<&acadrust::objects::RowCellStyle> = table_style - .map(|ts| { - let kind = match (title_suppressed, header_suppressed, ri) { - (false, _, 0) => 0, // title - (false, false, 1) => 1, // header - (true, false, 0) => 1, // header pulled up - _ => 2, // data - }; - match kind { - 0 => &ts.title_row_style, - 1 => &ts.header_row_style, - _ => &ts.data_row_style, - } - }); + let row_style: Option<&acadrust::objects::RowCellStyle> = table_style.map(|ts| { + let kind = match (title_suppressed, header_suppressed, ri) { + (false, _, 0) => 0, // title + (false, false, 1) => 1, // header + (true, false, 0) => 1, // header pulled up + _ => 2, // data + }; + match kind { + 0 => &ts.title_row_style, + 1 => &ts.header_row_style, + _ => &ts.data_row_style, + } + }); for (ci, cell) in row.cells.iter().enumerate() { let text = cell.text_value(); @@ -212,7 +214,12 @@ impl TruckConvertible for Table { let cell_h = content .map(|c| c.text_height) .filter(|h| *h > 1e-6) - .or_else(|| cell.style.as_ref().map(|s| s.text_height).filter(|h| *h > 1e-6)) + .or_else(|| { + cell.style + .as_ref() + .map(|s| s.text_height) + .filter(|h| *h > 1e-6) + }) .or_else(|| row_style.map(|s| s.text_height).filter(|h| *h > 1e-6)) .map(|h| h as f32) .unwrap_or(0.18); @@ -223,8 +230,7 @@ impl TruckConvertible for Table { .and_then(|c| c.text_style_handle) .or_else(|| cell.style.as_ref().and_then(|s| s.text_style_handle)) .or_else(|| row_style.and_then(|s| s.text_style_handle)); - let font_owned = - font_for_handle(style_handle).unwrap_or_else(|| "txt".to_string()); + let font_owned = font_for_handle(style_handle).unwrap_or_else(|| "txt".to_string()); let resolved = resolved_style_for_handle(style_handle, font_owned); // Alignment resolution: cell.style.alignment (1-9) overrides; @@ -254,8 +260,7 @@ impl TruckConvertible for Table { let text_origin = origin + h * x_offset + v_down * y_offset; // Content rotation (radians) on top of table cell rotation. - let rot = content.map(|c| c.rotation as f32).unwrap_or(0.0) - + cell.rotation as f32; + let rot = content.map(|c| c.rotation as f32).unwrap_or(0.0) + cell.rotation as f32; let layout = layout_mtext(&MTextRenderOpts { value: text, insertion: [text_origin.x as f64, text_origin.y as f64, origin.z as f64], @@ -378,12 +383,13 @@ pub fn tessellate_table( }; let v_down = Vec3::new(h.y, -h.x, 0.0); // Flow direction: `Up` stacks rows upward instead of downward. - let table_style: Option<&acadrust::objects::TableStyle> = tab.table_style_handle.and_then(|h| { - document.objects.get(&h).and_then(|obj| match obj { - acadrust::objects::ObjectType::TableStyle(ts) => Some(ts), - _ => None, - }) - }); + let table_style: Option<&acadrust::objects::TableStyle> = + tab.table_style_handle.and_then(|h| { + document.objects.get(&h).and_then(|obj| match obj { + acadrust::objects::ObjectType::TableStyle(ts) => Some(ts), + _ => None, + }) + }); let flow_up = matches!( table_style.map(|t| t.flow_direction), Some(acadrust::objects::TableFlowDirection::Up) @@ -411,7 +417,9 @@ pub fn tessellate_table( let title_suppressed = table_style.map(|t| t.title_suppressed).unwrap_or(false); let header_suppressed = table_style.map(|t| t.header_suppressed).unwrap_or(false); - let h_margin = table_style.map(|t| t.horizontal_margin as f32).unwrap_or(0.0); + let h_margin = table_style + .map(|t| t.horizontal_margin as f32) + .unwrap_or(0.0); let v_margin = table_style.map(|t| t.vertical_margin as f32).unwrap_or(0.0); let lookup_style = |hh: acadrust::Handle| -> Option<&acadrust::tables::TextStyle> { @@ -444,28 +452,27 @@ pub fn tessellate_table( let mut emitted: rustc_hash::FxHashSet<(i32, i32, i32, i32)> = rustc_hash::FxHashSet::default(); let sel_col = WireModel::SELECTED; - let mut add_edge = - |a: Vec3, b: Vec3, col: [f32; 4], lw: f32| { - let k = ( - (a.x * 1000.0) as i32, - (a.y * 1000.0) as i32, - (b.x * 1000.0) as i32, - (b.y * 1000.0) as i32, - ); - let kr = (k.2, k.3, k.0, k.1); - if emitted.contains(&k) || emitted.contains(&kr) { - return; - } - emitted.insert(k); - let entry = borders - .entry((key4(col), (lw * 100.0) as u32)) - .or_insert_with(|| (col, lw, Vec::new())); - if !entry.2.is_empty() { - entry.2.push([f32::NAN; 3]); - } - entry.2.push(rel(a)); - entry.2.push(rel(b)); - }; + let mut add_edge = |a: Vec3, b: Vec3, col: [f32; 4], lw: f32| { + let k = ( + (a.x * 1000.0) as i32, + (a.y * 1000.0) as i32, + (b.x * 1000.0) as i32, + (b.y * 1000.0) as i32, + ); + let kr = (k.2, k.3, k.0, k.1); + if emitted.contains(&k) || emitted.contains(&kr) { + return; + } + emitted.insert(k); + let entry = borders + .entry((key4(col), (lw * 100.0) as u32)) + .or_insert_with(|| (col, lw, Vec::new())); + if !entry.2.is_empty() { + entry.2.push([f32::NAN; 3]); + } + entry.2.push(rel(a)); + entry.2.push(rel(b)); + }; for (ri, row) in tab.rows.iter().enumerate() { let row_top = row_offsets[ri]; @@ -507,7 +514,10 @@ pub fn tessellate_table( }; if fill_on { let col = resolve_col(&fill_color, entity_color); - let buf = &mut fills.entry(key4(col)).or_insert_with(|| (col, Vec::new())).1; + let buf = &mut fills + .entry(key4(col)) + .or_insert_with(|| (col, Vec::new())) + .1; for v in [bl, br_, tr, bl, tr, tl] { buf.push(rel(v)); } @@ -526,7 +536,11 @@ pub fn tessellate_table( }; ( !b.invisible, - if selected { sel_col } else { resolve_col(&b.color, entity_color) }, + if selected { + sel_col + } else { + resolve_col(&b.color, entity_color) + }, lw_px(&b.line_weight), ) } else if let Some(rs) = row_style { @@ -538,21 +552,37 @@ pub fn tessellate_table( }; ( !b.is_invisible, - if selected { sel_col } else { resolve_col(&b.color, entity_color) }, + if selected { + sel_col + } else { + resolve_col(&b.color, entity_color) + }, lw_px(&b.line_weight), ) } else { - (true, if selected { sel_col } else { entity_color }, line_weight_px) + ( + true, + if selected { sel_col } else { entity_color }, + line_weight_px, + ) } }; let (tv, tc, tw) = edge(0); - if tv { add_edge(tl, tr, tc, tw); } + if tv { + add_edge(tl, tr, tc, tw); + } let (rv, rc, rw) = edge(1); - if rv { add_edge(tr, br_, rc, rw); } + if rv { + add_edge(tr, br_, rc, rw); + } let (bv, bc, bw) = edge(2); - if bv { add_edge(bl, br_, bc, bw); } + if bv { + add_edge(bl, br_, bc, bw); + } let (lv, lc, lw) = edge(3); - if lv { add_edge(tl, bl, lc, lw); } + if lv { + add_edge(tl, bl, lc, lw); + } // ── Text ────────────────────────────────────────────────────── let text = cell.text_value(); @@ -563,12 +593,25 @@ pub fn tessellate_table( let cell_h = content .map(|c| c.text_height) .filter(|h| *h > 1e-6) - .or_else(|| cell.style.as_ref().map(|s| s.text_height).filter(|h| *h > 1e-6)) + .or_else(|| { + cell.style + .as_ref() + .map(|s| s.text_height) + .filter(|h| *h > 1e-6) + }) .or_else(|| row_style.map(|s| s.text_height).filter(|h| *h > 1e-6)) .map(|h| h as f32) .unwrap_or(0.18); - let m_h = if h_margin > 1e-6 { h_margin } else { cell_h * 0.5 }; - let m_v = if v_margin > 1e-6 { v_margin } else { cell_h * 0.5 }; + let m_h = if h_margin > 1e-6 { + h_margin + } else { + cell_h * 0.5 + }; + let m_v = if v_margin > 1e-6 { + v_margin + } else { + cell_h * 0.5 + }; let style_handle = content .and_then(|c| c.text_style_handle) @@ -621,7 +664,10 @@ pub fn tessellate_table( } else { entity_color }; - let buf = &mut texts.entry(key4(tcol)).or_insert_with(|| (tcol, Vec::new())).1; + let buf = &mut texts + .entry(key4(tcol)) + .or_insert_with(|| (tcol, Vec::new())) + .1; for ts in &layout.strokes { let sx = ts.origin[0] as f32; let sy = ts.origin[1] as f32; @@ -641,25 +687,26 @@ pub fn tessellate_table( } let name = tab.common.handle.value().to_string(); - let mk = |color: [f32; 4], points: Vec<[f32; 3]>, fill_tris: Vec<[f32; 3]>, lw: f32| -> WireModel { - WireModel { - name: name.clone(), - points, - color, - selected, - pattern_length: 0.0, - pattern: [0.0; 8], - line_weight_px: lw, - aci: 0, - snap_pts: vec![], - tangent_geoms: vec![], - key_vertices: vec![], - aabb: WireModel::UNBOUNDED_AABB, - plinegen: true, - vp_scissor: None, - fill_tris, - } - }; + let mk = + |color: [f32; 4], points: Vec<[f32; 3]>, fill_tris: Vec<[f32; 3]>, lw: f32| -> WireModel { + WireModel { + name: name.clone(), + points, + color, + selected, + pattern_length: 0.0, + pattern: [0.0; 8], + line_weight_px: lw, + aci: 0, + snap_pts: vec![], + tangent_geoms: vec![], + key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, + plinegen: true, + vp_scissor: None, + fill_tris, + } + }; let mut out: Vec = Vec::new(); // Fills first (drawn under borders/text). @@ -683,7 +730,14 @@ pub fn tessellate_table( impl Grippable for Table { fn grips(&self) -> Vec { - vec![square_grip(0, v3(&self.insertion_point))] + vec![square_grip( + 0, + glam::DVec3::new( + self.insertion_point.x, + self.insertion_point.y, + self.insertion_point.z, + ), + )] } fn apply_grip(&mut self, grip_id: usize, apply: GripApply) { @@ -742,7 +796,11 @@ impl PropertyEditable for Table { "tbl_block_rec_handle", fmt_h(&self.block_record_handle), ), - ro("Data Version", "tbl_data_version", self.data_version.to_string()), + ro( + "Data Version", + "tbl_data_version", + self.data_version.to_string(), + ), ro( "Value Flags", "tbl_value_flags", @@ -756,7 +814,11 @@ impl PropertyEditable for Table { ro( "Override Border Color", "tbl_override_border_color", - if self.override_border_color { "Yes" } else { "No" }, + if self.override_border_color { + "Yes" + } else { + "No" + }, ), ro( "Override Border LW", diff --git a/src/entities/text.rs b/src/entities/text.rs index a30612ab7..88136b046 100644 --- a/src/entities/text.rs +++ b/src/entities/text.rs @@ -141,7 +141,11 @@ fn to_truck(t: &Text, document: &acadrust::CadDocument) -> TruckEntity { &t.value, ); TruckEntity { - object: TruckObject::Text(vec![TextStroke { strokes, origin, color: None }]), + object: TruckObject::Text(vec![TextStroke { + strokes, + origin, + color: None, + }]), snap_pts: vec![(snap_pt, SnapHint::Insertion)], tangent_geoms: vec![], key_vertices: vec![], @@ -150,10 +154,10 @@ fn to_truck(t: &Text, document: &acadrust::CadDocument) -> TruckEntity { } fn grips(t: &Text) -> Vec { - let p = Vec3::new( - t.insertion_point.x as f32, - t.insertion_point.y as f32, - t.insertion_point.z as f32, + let p = glam::DVec3::new( + t.insertion_point.x, + t.insertion_point.y, + t.insertion_point.z, ); vec![square_grip(0, p)] } @@ -316,23 +320,25 @@ impl Grippable for Text { apply_grip(self, grip_id, apply); } - fn grip_menu( - &self, - _grip_id: usize, - ) -> Vec { + fn grip_menu(&self, _grip_id: usize) -> Vec { use crate::scene::object::{GripMenuAction, GripMenuItem}; vec![ - GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }, - GripMenuItem { label: "Move with Text", action: GripMenuAction::MoveWithText }, - GripMenuItem { label: "Rotate", action: GripMenuAction::RotateText }, + GripMenuItem { + label: "Stretch", + action: GripMenuAction::Stretch, + }, + GripMenuItem { + label: "Move with Text", + action: GripMenuAction::MoveWithText, + }, + GripMenuItem { + label: "Rotate", + action: GripMenuAction::RotateText, + }, ] } - fn apply_grip_menu( - &mut self, - _grip_id: usize, - _action: crate::scene::object::GripMenuAction, - ) { + fn apply_grip_menu(&mut self, _grip_id: usize, _action: crate::scene::object::GripMenuAction) { // Move-with-Text falls through to Stretch (single grip moves // the whole text); Rotate needs a follow-up angle handled by // `apply_grip_menu_value`. diff --git a/src/entities/tolerance.rs b/src/entities/tolerance.rs index 8e9b52f81..205d6fa6c 100644 --- a/src/entities/tolerance.rs +++ b/src/entities/tolerance.rs @@ -244,7 +244,11 @@ impl TruckConvertible for Tolerance { let origin = [self.insertion_point.x, self.insertion_point.y]; Some(TruckEntity { - object: TruckObject::Text(vec![TextStroke { strokes, origin, color: None }]), + object: TruckObject::Text(vec![TextStroke { + strokes, + origin, + color: None, + }]), snap_pts: vec![(snap_pt, SnapHint::Insertion)], tangent_geoms: vec![], key_vertices: vec![], @@ -259,10 +263,10 @@ impl Grippable for Tolerance { fn grips(&self) -> Vec { vec![square_grip( 0, - Vec3::new( - self.insertion_point.x as f32, - self.insertion_point.y as f32, - self.insertion_point.z as f32, + glam::DVec3::new( + self.insertion_point.x, + self.insertion_point.y, + self.insertion_point.z, ), )] } diff --git a/src/entities/underlay.rs b/src/entities/underlay.rs index 86f95981b..37bb18124 100644 --- a/src/entities/underlay.rs +++ b/src/entities/underlay.rs @@ -46,10 +46,7 @@ impl TruckConvertible for Underlay { if !self.clip_boundary_vertices.is_empty() { // Draw clip boundary polygon + close it. let world_verts = self.world_clip_boundary(); - let mut pts: Vec<[f64; 3]> = world_verts - .iter() - .map(|v| [v.x, v.y, v.z]) - .collect(); + let mut pts: Vec<[f64; 3]> = world_verts.iter().map(|v| [v.x, v.y, v.z]).collect(); // Close polygon. if let Some(&first) = pts.first() { pts.push(first); @@ -81,16 +78,17 @@ impl TruckConvertible for Underlay { impl Grippable for Underlay { fn grips(&self) -> Vec { - let origin = Vec3::from(v3f32(&self.insertion_point)); + let origin = glam::DVec3::new( + self.insertion_point.x, + self.insertion_point.y, + self.insertion_point.z, + ); let mut grips = vec![square_grip(0, origin)]; if !self.clip_boundary_vertices.is_empty() { let world_verts = self.world_clip_boundary(); for (i, v) in world_verts.iter().enumerate() { - grips.push(center_grip( - i + 1, - Vec3::new(v.x as f32, v.y as f32, v.z as f32), - )); + grips.push(center_grip(i + 1, glam::DVec3::new(v.x, v.y, v.z))); } } diff --git a/src/entities/viewport.rs b/src/entities/viewport.rs index e85a46196..592206f37 100644 --- a/src/entities/viewport.rs +++ b/src/entities/viewport.rs @@ -1,5 +1,4 @@ use acadrust::entities::{Viewport, ViewportRenderMode}; -use glam::Vec3; use crate::command::EntityTransform; use crate::entities::common::{ @@ -105,27 +104,22 @@ const STD_VIEWS: &[&str] = &[ ]; fn grips(vp: &Viewport) -> Vec { - let cx = vp.center.x as f32; - let cy = vp.center.y as f32; - let cz = vp.center.z as f32; - let hw = (vp.width / 2.0) as f32; - let hh = (vp.height / 2.0) as f32; + let (cx, cy, cz) = (vp.center.x, vp.center.y, vp.center.z); + let hw = vp.width / 2.0; + let hh = vp.height / 2.0; vec![ - center_grip(0, Vec3::new(cx, cy, cz)), - square_grip(1, Vec3::new(cx + hw, cy + hh, cz)), - square_grip(2, Vec3::new(cx - hw, cy + hh, cz)), - square_grip(3, Vec3::new(cx - hw, cy - hh, cz)), - square_grip(4, Vec3::new(cx + hw, cy - hh, cz)), + center_grip(0, glam::DVec3::new(cx, cy, cz)), + square_grip(1, glam::DVec3::new(cx + hw, cy + hh, cz)), + square_grip(2, glam::DVec3::new(cx - hw, cy + hh, cz)), + square_grip(3, glam::DVec3::new(cx - hw, cy - hh, cz)), + square_grip(4, glam::DVec3::new(cx + hw, cy - hh, cz)), ] } fn properties(vp: &Viewport) -> PropSection { let scale_opts: Vec = STANDARD_SCALES.iter().map(|(s, _)| s.to_string()).collect(); - let effective_scale = crate::scene::vp_effective_scale( - vp.custom_scale, - vp.view_height, - vp.height, - ); + let effective_scale = + crate::scene::vp_effective_scale(vp.custom_scale, vp.view_height, vp.height); let current_scale_label = scale_label(effective_scale); let view_opts: Vec = STD_VIEWS.iter().map(|s| s.to_string()).collect(); @@ -256,11 +250,7 @@ fn properties(vp: &Viewport) -> PropSection { edit("Twist Angle", "vp_twist", vp.twist_angle.to_degrees()), edit("Front Clip Z", "vp_front_clip", vp.front_clip_z), edit("Back Clip Z", "vp_back_clip", vp.back_clip_z), - edit( - "Circle Sides", - "vp_circle_sides", - vp.circle_sides as f64, - ), + edit("Circle Sides", "vp_circle_sides", vp.circle_sides as f64), ro( "Grid Flags", "vp_grid_flags", @@ -276,11 +266,7 @@ fn properties(vp: &Viewport) -> PropSection { format!("{:X}", vp.base_ucs_handle.value()) }, ), - ro( - "UCS Ortho", - "vp_ucs_ortho", - vp.ucs_ortho_type.to_string(), - ), + ro("UCS Ortho", "vp_ucs_ortho", vp.ucs_ortho_type.to_string()), ro( "Background", "vp_background_handle", @@ -560,7 +546,10 @@ fn apply_transform(vp: &mut Viewport, t: &EntityTransform) { crate::impl_entity_basics!(Viewport); impl crate::entities::traits::FallbackTess for Viewport { - fn fallback_geometry(&self, world_offset: [f64; 3]) -> crate::scene::tess_util::FallbackGeometry { + fn fallback_geometry( + &self, + world_offset: [f64; 3], + ) -> crate::scene::tess_util::FallbackGeometry { let [ox, oy, oz] = world_offset; let cx = (self.center.x - ox) as f32; let cy = (self.center.y - oy) as f32; diff --git a/src/scene/grip.rs b/src/scene/grip.rs index bb2f3faea..dfc2c24d5 100644 --- a/src/scene/grip.rs +++ b/src/scene/grip.rs @@ -40,7 +40,7 @@ pub fn grips_to_screen( grips .iter() .map(|g| { - let ndc = view_proj.project_point3(g.world); + let ndc = view_proj.project_point3(g.world.as_vec3()); let screen = Point::new( bounds.x + (ndc.x + 1.0) * 0.5 * bounds.width, bounds.y + (1.0 - ndc.y) * 0.5 * bounds.height, @@ -64,8 +64,8 @@ pub fn grips_to_screen_paper( .iter() .map(|g| { let screen = Point::new( - (g.world.x - tx + half_w) / (2.0 * half_w) * bounds.width, - (ty + half_h - g.world.y) / (2.0 * half_h) * bounds.height, + (g.world.x as f32 - tx + half_w) / (2.0 * half_w) * bounds.width, + (ty + half_h - g.world.y as f32) / (2.0 * half_h) * bounds.height, ); (g.id, screen, g.is_midpoint, g.shape, g.dir) }) @@ -87,15 +87,15 @@ pub fn find_hit_grip_paper( for g in grips { let screen = Point::new( - (g.world.x - tx + half_w) / (2.0 * half_w) * bounds.width, - (ty + half_h - g.world.y) / (2.0 * half_h) * bounds.height, + (g.world.x as f32 - tx + half_w) / (2.0 * half_w) * bounds.width, + (ty + half_h - g.world.y as f32) / (2.0 * half_h) * bounds.height, ); let dx = screen.x - cursor.x; let dy = screen.y - cursor.y; let d = (dx * dx + dy * dy).sqrt(); if d < best_dist { best_dist = d; - best = Some((g.id, g.is_midpoint, g.world)); + best = Some((g.id, g.is_midpoint, g.world.as_vec3())); } } best @@ -113,7 +113,7 @@ pub fn find_hit_grip( let mut best: Option<(usize, bool, Vec3)> = None; for g in grips { - let ndc = view_proj.project_point3(g.world); + let ndc = view_proj.project_point3(g.world.as_vec3()); let screen = Point::new( (ndc.x + 1.0) * 0.5 * bounds.width, (1.0 - ndc.y) * 0.5 * bounds.height, @@ -123,7 +123,7 @@ pub fn find_hit_grip( let d = (dx * dx + dy * dy).sqrt(); if d < best_dist { best_dist = d; - best = Some((g.id, g.is_midpoint, g.world)); + best = Some((g.id, g.is_midpoint, g.world.as_vec3())); } } best diff --git a/src/scene/object.rs b/src/scene/object.rs index 49e0c7de3..18d8e86f6 100644 --- a/src/scene/object.rs +++ b/src/scene/object.rs @@ -75,8 +75,12 @@ pub enum GripShape { pub struct GripDef { /// Object-local identifier (stable index, unique per object instance). pub id: usize, - /// World-space position of the grip. - pub world: Vec3, + /// World-space position of the grip, in f64. Entity coordinates can sit at + /// UTM magnitudes (1e7); casting to f32 before the world-offset subtraction + /// loses ~1 drawing unit and draws the grip visibly off the wire. Producers + /// fill this straight from the f64 entity data; the offset is subtracted in + /// f64 and only then cast for screen-space math. + pub world: glam::DVec3, /// `true` → midpoint / centre grip (drags the whole shape). /// `false` → endpoint / vertex grip (moves a single point). pub is_midpoint: bool,