forked from 1b-agent/opencadstudio-upstream
fix(styles): apply active creation defaults
Resolve style properties before editors and previews open. Use absolute annotation multipliers for style-driven objects. Refs #401 Refs #431
This commit is contained in:
1 parent
53cf31f33d
commit
de2707a23e
14 files changed
+676
-129
No files matched your search
@@ -34,11 +34,36 @@ pub fn tool() -> ToolDef {
|
||||
pub struct LeaderCommand {
|
||||
verts: Vec<DVec3>,
|
||||
ucs: Mat4,
|
||||
dimension_style: String,
|
||||
text_style: String,
|
||||
text_height: f64,
|
||||
display_scale: f64,
|
||||
gap: f64,
|
||||
arrow_size: f64,
|
||||
annotative: bool,
|
||||
}
|
||||
|
||||
impl LeaderCommand {
|
||||
pub fn new() -> Self {
|
||||
Self { verts: Vec::new(), ucs: Mat4::IDENTITY }
|
||||
pub fn with_defaults(
|
||||
defaults: crate::scene::creation_style::DimensionCreationDefaults,
|
||||
annotation_multiplier: f64,
|
||||
) -> Self {
|
||||
let display_scale = if defaults.annotative {
|
||||
annotation_multiplier
|
||||
} else {
|
||||
defaults.scale
|
||||
};
|
||||
Self {
|
||||
verts: Vec::new(),
|
||||
ucs: Mat4::IDENTITY,
|
||||
dimension_style: defaults.style_name,
|
||||
text_style: defaults.text_style_name,
|
||||
text_height: defaults.text_height,
|
||||
display_scale,
|
||||
gap: defaults.gap,
|
||||
arrow_size: defaults.arrow_size,
|
||||
annotative: defaults.annotative,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,9 +99,30 @@ impl CadCommand for LeaderCommand {
|
||||
}
|
||||
// Place the leader plus an empty MText annotation, link them, then open
|
||||
// the in-place MText editor so the user types the annotation text.
|
||||
let leader = build_leader(&self.verts, self.ucs);
|
||||
let (anchor, attach) = annotation_anchor(&self.verts, leader.text_height, self.ucs);
|
||||
let mtext = build_mtext("", anchor, leader.text_height, attach, self.ucs);
|
||||
let leader = build_leader(
|
||||
&self.verts,
|
||||
self.ucs,
|
||||
&self.dimension_style,
|
||||
self.text_height,
|
||||
self.gap,
|
||||
self.arrow_size,
|
||||
);
|
||||
let displayed_height = self.text_height * self.display_scale;
|
||||
let (anchor, attach) = annotation_anchor(&self.verts, displayed_height, self.ucs);
|
||||
let mtext_height = if self.annotative {
|
||||
self.text_height
|
||||
} else {
|
||||
displayed_height
|
||||
};
|
||||
let mtext = build_mtext(
|
||||
"",
|
||||
anchor,
|
||||
mtext_height,
|
||||
attach,
|
||||
self.ucs,
|
||||
&self.text_style,
|
||||
self.annotative,
|
||||
);
|
||||
CmdResult::CommitManyAndEditText {
|
||||
entities: vec![EntityType::Leader(leader), EntityType::MText(mtext)],
|
||||
edit_index: 1,
|
||||
@@ -93,7 +139,10 @@ impl CadCommand for LeaderCommand {
|
||||
}
|
||||
let mut pts: Vec<Vec3> = self.verts.iter().map(|p| p.as_vec3()).collect();
|
||||
pts.push(pt.as_vec3());
|
||||
Some(preview_wire(&pts))
|
||||
Some(preview_wire(
|
||||
&pts,
|
||||
(self.arrow_size * self.display_scale) as f32,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,10 +152,21 @@ fn dv3(p: DVec3) -> Vector3 {
|
||||
Vector3::new(p.x, p.y, p.z)
|
||||
}
|
||||
|
||||
fn build_leader(verts: &[DVec3], ucs: Mat4) -> Leader {
|
||||
fn build_leader(
|
||||
verts: &[DVec3],
|
||||
ucs: Mat4,
|
||||
dimension_style: &str,
|
||||
text_height: f64,
|
||||
gap: f64,
|
||||
arrow_size: f64,
|
||||
) -> Leader {
|
||||
let mut l = Leader::from_vertices(verts.iter().map(|p| dv3(*p)).collect());
|
||||
l.creation_type = LeaderCreationType::WithText;
|
||||
l.hookline_enabled = true;
|
||||
l.dimension_style = dimension_style.to_string();
|
||||
l.text_height = text_height;
|
||||
l.dimension_gap = gap;
|
||||
l.arrow_size = arrow_size;
|
||||
// The hookline / text read along the UCS X axis (DXF "horizontal direction
|
||||
// for text"); the renderer uses it instead of world horizontal.
|
||||
let ux = ucs.transform_vector3(Vec3::X).normalize_or(Vec3::X);
|
||||
@@ -143,11 +203,15 @@ fn build_mtext(
|
||||
height: f64,
|
||||
attach: AttachmentPoint,
|
||||
ucs: Mat4,
|
||||
text_style: &str,
|
||||
annotative: bool,
|
||||
) -> MText {
|
||||
let mut m = MText::new();
|
||||
m.value = text.to_string();
|
||||
m.insertion_point = dv3(pos);
|
||||
m.height = height;
|
||||
m.style = text_style.to_string();
|
||||
m.is_annotative = annotative;
|
||||
m.attachment_point = attach;
|
||||
// Text reads along the UCS X axis.
|
||||
let ux = ucs.transform_vector3(Vec3::X);
|
||||
@@ -155,10 +219,10 @@ fn build_mtext(
|
||||
m
|
||||
}
|
||||
|
||||
fn preview_wire(pts: &[Vec3]) -> WireModel {
|
||||
fn preview_wire(pts: &[Vec3], arrow_size: f32) -> WireModel {
|
||||
let mut points: Vec<[f32; 3]> = pts.iter().map(|p| [p.x, p.y, p.z]).collect();
|
||||
if pts.len() >= 2 {
|
||||
let [w1, w2] = arrowhead_wings(pts[0], pts[1], 2.5);
|
||||
let [w1, w2] = arrowhead_wings(pts[0], pts[1], arrow_size);
|
||||
points.push([f32::NAN; 3]);
|
||||
points.push([w1.x, w1.y, w1.z]);
|
||||
points.push([pts[0].x, pts[0].y, pts[0].z]);
|
||||
|
||||
@@ -29,11 +29,35 @@ pub fn tool() -> ToolDef {
|
||||
pub struct MLeaderCommand {
|
||||
verts: Vec<DVec3>,
|
||||
ucs: Mat4,
|
||||
style: Option<acadrust::objects::MultiLeaderStyle>,
|
||||
display_scale: f64,
|
||||
}
|
||||
|
||||
impl MLeaderCommand {
|
||||
pub fn new() -> Self {
|
||||
Self { verts: Vec::new(), ucs: Mat4::IDENTITY }
|
||||
Self {
|
||||
verts: Vec::new(),
|
||||
ucs: Mat4::IDENTITY,
|
||||
style: None,
|
||||
display_scale: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_style(
|
||||
style: acadrust::objects::MultiLeaderStyle,
|
||||
annotation_multiplier: f64,
|
||||
) -> Self {
|
||||
let display_scale = if style.is_annotative {
|
||||
annotation_multiplier
|
||||
} else {
|
||||
style.scale_factor
|
||||
};
|
||||
Self {
|
||||
verts: Vec::new(),
|
||||
ucs: Mat4::IDENTITY,
|
||||
style: Some(style),
|
||||
display_scale,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +93,13 @@ impl CadCommand for MLeaderCommand {
|
||||
}
|
||||
// Place the leader with empty text, then open the in-place MText editor
|
||||
// so the user types the annotation into the rich editor.
|
||||
let ml = build_mleader("", &self.verts, self.ucs);
|
||||
let ml = build_mleader(
|
||||
"",
|
||||
&self.verts,
|
||||
self.ucs,
|
||||
self.style.as_ref(),
|
||||
self.display_scale,
|
||||
);
|
||||
CmdResult::CommitAndEditText(EntityType::MultiLeader(ml))
|
||||
}
|
||||
|
||||
@@ -84,7 +114,12 @@ impl CadCommand for MLeaderCommand {
|
||||
// Preview / rubber-band is GPU screen-space: downcast to f32 here.
|
||||
let mut pts: Vec<Vec3> = self.verts.iter().map(|p| p.as_vec3()).collect();
|
||||
pts.push(pt.as_vec3());
|
||||
Some(preview_wire(&pts))
|
||||
let arrow_size = self
|
||||
.style
|
||||
.as_ref()
|
||||
.map_or(2.5, |style| style.arrowhead_size)
|
||||
* self.display_scale;
|
||||
Some(preview_wire(&pts, arrow_size as f32))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +129,13 @@ fn v3(p: DVec3) -> Vector3 {
|
||||
Vector3::new(p.x, p.y, p.z)
|
||||
}
|
||||
|
||||
fn build_mleader(text: &str, verts: &[DVec3], ucs: Mat4) -> MultiLeader {
|
||||
fn build_mleader(
|
||||
text: &str,
|
||||
verts: &[DVec3],
|
||||
ucs: Mat4,
|
||||
style: Option<&acadrust::objects::MultiLeaderStyle>,
|
||||
display_scale: f64,
|
||||
) -> MultiLeader {
|
||||
// Last vertex = content/text location; remaining = leader line points
|
||||
let (leader_pts, content_pt) = verts.split_at(verts.len() - 1);
|
||||
let content_pt = content_pt[0];
|
||||
@@ -103,14 +144,22 @@ fn build_mleader(text: &str, verts: &[DVec3], ucs: Mat4) -> MultiLeader {
|
||||
let content_v3 = v3(content_pt);
|
||||
|
||||
let mut ml = MultiLeader::with_text(text, content_v3, leader_v3);
|
||||
if let Some(style) = style {
|
||||
crate::scene::annotative::apply_mleader_style(&mut ml, style);
|
||||
} else {
|
||||
ml.text_height = 2.5;
|
||||
ml.context.text_height = 2.5;
|
||||
ml.arrowhead_size = 2.5;
|
||||
ml.context.arrowhead_size = 2.5;
|
||||
ml.dogleg_length = 2.5;
|
||||
}
|
||||
|
||||
// Match Leader entity defaults
|
||||
ml.text_height = 2.5;
|
||||
ml.context.text_height = 2.5;
|
||||
ml.arrowhead_size = 2.5;
|
||||
ml.dogleg_length = 2.5;
|
||||
|
||||
const DOGLEG: f64 = 2.5;
|
||||
let landing_distance = ml.dogleg_length * display_scale;
|
||||
let landing_gap = ml.context.landing_gap * display_scale;
|
||||
ml.context.scale_factor = display_scale;
|
||||
ml.context.text_height = ml.text_height * display_scale;
|
||||
ml.context.arrowhead_size = ml.arrowhead_size * display_scale;
|
||||
ml.context.landing_gap = landing_gap;
|
||||
// "Horizontal" landing + text run along the active UCS X axis (identity =
|
||||
// world), so the annotation reads square to the user's coordinate system.
|
||||
let ux = ucs.transform_vector3(Vec3::X).normalize_or(Vec3::X);
|
||||
@@ -130,22 +179,22 @@ fn build_mleader(text: &str, verts: &[DVec3], ucs: Mat4) -> MultiLeader {
|
||||
// the text along the UCS X axis.
|
||||
root.direction = Vector3::new(landing.x as f64, landing.y as f64, 0.0);
|
||||
root.connection_point = content_v3;
|
||||
root.landing_distance = DOGLEG;
|
||||
root.landing_distance = landing_distance;
|
||||
}
|
||||
|
||||
// Seed the text one landing-length past the leader end, on the side the
|
||||
// user dragged toward, offset along the UCS X axis.
|
||||
let off = landing * DOGLEG as f32;
|
||||
let off = landing * (landing_distance + landing_gap) as f32;
|
||||
ml.context.text_location =
|
||||
Vector3::new(content_v3.x + off.x as f64, content_v3.y + off.y as f64, content_v3.z);
|
||||
|
||||
ml
|
||||
}
|
||||
|
||||
fn preview_wire(pts: &[Vec3]) -> WireModel {
|
||||
fn preview_wire(pts: &[Vec3], arrow_size: f32) -> WireModel {
|
||||
let mut points: Vec<[f32; 3]> = pts.iter().map(|p| [p.x, p.y, p.z]).collect();
|
||||
if pts.len() >= 2 {
|
||||
let [w1, w2] = arrowhead_wings(pts[0], pts[1], 2.5);
|
||||
let [w1, w2] = arrowhead_wings(pts[0], pts[1], arrow_size);
|
||||
points.push([f32::NAN; 3]);
|
||||
points.push([w1.x, w1.y, w1.z]);
|
||||
points.push([pts[0].x, pts[0].y, pts[0].z]);
|
||||
|
||||
@@ -21,12 +21,14 @@ enum Step {
|
||||
|
||||
pub struct MTextCommand {
|
||||
step: Step,
|
||||
height: f64,
|
||||
}
|
||||
|
||||
impl MTextCommand {
|
||||
pub fn new() -> Self {
|
||||
pub fn with_height(height: f64) -> Self {
|
||||
Self {
|
||||
step: Step::InsertPoint,
|
||||
height,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +50,7 @@ impl CadCommand for MTextCommand {
|
||||
pos: pt,
|
||||
handle: None,
|
||||
initial: String::new(),
|
||||
height: 0.25,
|
||||
height: self.height,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,12 +41,46 @@ enum Step {
|
||||
|
||||
pub struct TableCommand {
|
||||
step: Step,
|
||||
style_handle: Option<acadrust::Handle>,
|
||||
column_width: f64,
|
||||
row_height: f64,
|
||||
preview_scale: f64,
|
||||
}
|
||||
|
||||
impl TableCommand {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
step: Step::Columns,
|
||||
style_handle: None,
|
||||
column_width: COL_WIDTH,
|
||||
row_height: ROW_HEIGHT,
|
||||
preview_scale: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_style(
|
||||
style_handle: acadrust::Handle,
|
||||
style: &acadrust::objects::TableStyle,
|
||||
annotation_multiplier: f64,
|
||||
) -> Self {
|
||||
let text_height = style
|
||||
.data_row_style
|
||||
.text_height
|
||||
.max(style.header_row_style.text_height)
|
||||
.max(style.title_row_style.text_height)
|
||||
.max(1.0e-6);
|
||||
let row_height = text_height * 1.5 + style.vertical_margin * 2.0;
|
||||
let column_width = text_height * 8.0 + style.horizontal_margin * 2.0;
|
||||
Self {
|
||||
step: Step::Columns,
|
||||
style_handle: Some(style_handle),
|
||||
column_width,
|
||||
row_height,
|
||||
preview_scale: if style.annotative {
|
||||
annotation_multiplier
|
||||
} else {
|
||||
1.0
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,11 +164,12 @@ impl CadCommand for TableCommand {
|
||||
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
||||
if let Step::Insertion { cols, rows } = self.step {
|
||||
let ins = Vector3::new(pt.x, pt.y, pt.z);
|
||||
let table = TableBuilder::new(rows, cols)
|
||||
let mut table = TableBuilder::new(rows, cols)
|
||||
.at(ins)
|
||||
.row_height(ROW_HEIGHT)
|
||||
.column_width(COL_WIDTH)
|
||||
.row_height(self.row_height)
|
||||
.column_width(self.column_width)
|
||||
.build();
|
||||
table.table_style_handle = self.style_handle;
|
||||
CmdResult::CommitAndExit(EntityType::Table(table))
|
||||
} else {
|
||||
CmdResult::NeedPoint
|
||||
@@ -144,8 +179,8 @@ impl CadCommand for TableCommand {
|
||||
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> { let pt = pt.as_vec3();
|
||||
if let Step::Insertion { cols, rows } = self.step {
|
||||
// Preview: outline of the table bounding box.
|
||||
let w = (cols as f32) * COL_WIDTH as f32;
|
||||
let h = (rows as f32) * ROW_HEIGHT as f32;
|
||||
let w = (cols as f32) * (self.column_width * self.preview_scale) as f32;
|
||||
let h = (rows as f32) * (self.row_height * self.preview_scale) as f32;
|
||||
let x = pt.x;
|
||||
let y = pt.y;
|
||||
let z = pt.z;
|
||||
|
||||
@@ -21,12 +21,14 @@ enum Step {
|
||||
|
||||
pub struct TextCommand {
|
||||
step: Step,
|
||||
height: f64,
|
||||
}
|
||||
|
||||
impl TextCommand {
|
||||
pub fn new() -> Self {
|
||||
pub fn with_height(height: f64) -> Self {
|
||||
Self {
|
||||
step: Step::InsertPoint,
|
||||
height,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +50,7 @@ impl CadCommand for TextCommand {
|
||||
pos: pt,
|
||||
handle: None,
|
||||
initial: String::new(),
|
||||
height: 0.25,
|
||||
height: self.height,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user