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