feat(properties): conform entity property groups to PROPERTIES.md
Rewrite each entity's property builder to the per-entity group/row spec: the correct sections (Geometry, Misc, Text, Pattern, Lines & Arrows, Leaders, Block, …) with the spec's rows and real computed values — Diameter, Circumference, Area, Delta X/Y/Z, Angle, Total angle, Arc length, Radius ratio, etc. — plus apply routing for the editable rows. Covers ~26 entities (Line, Circle, Arc, Point, Ellipse, Spline, polylines, Ray/XLine, Insert, Text, MText, Hatch, Leader, MultiLeader, Tolerance, Solid, Solid3D/Region/Body, meshes, MLine, Raster/Wipeout, Underlay, Table, Viewport, Attribute, OLE). The 3D Visualization/Material group and the full Dimension spec are deferred (they need document access for material/dimstyle name resolution). Rows whose data is not in the pinned acadrust revision are shown as empty placeholders, to be filled when the dependency is bumped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b2b2bfb246
commit
b9cebab951
26 changed files with 1913 additions and 1554 deletions
|
|
@ -2,7 +2,9 @@ use acadrust::entities::Arc;
|
||||||
use truck_modeling::{builder, Point3};
|
use truck_modeling::{builder, Point3};
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{center_grip, edit_prop as edit, parse_f64, square_grip};
|
use crate::entities::common::{
|
||||||
|
center_grip, edit_prop as edit, parse_f64, ro_prop as ro, square_grip,
|
||||||
|
};
|
||||||
use crate::entities::traits::TruckConvertible;
|
use crate::entities::traits::TruckConvertible;
|
||||||
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
@ -122,6 +124,31 @@ fn grips(arc: &Arc) -> Vec<GripDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn properties(arc: &Arc) -> Vec<PropSection> {
|
fn properties(arc: &Arc) -> Vec<PropSection> {
|
||||||
|
let r = arc.radius;
|
||||||
|
let sa = arc.start_angle;
|
||||||
|
let ea = arc.end_angle;
|
||||||
|
let sweep = (ea - sa).rem_euclid(TAU);
|
||||||
|
let total_angle = sweep.to_degrees();
|
||||||
|
let arc_length = r * sweep;
|
||||||
|
let area = 0.5 * r * r * sweep;
|
||||||
|
|
||||||
|
let normal = (arc.normal.x, arc.normal.y, arc.normal.z);
|
||||||
|
let (ax, ay) = crate::scene::view::transform::ocs_axes(normal);
|
||||||
|
let (cwx, cwy, cwz) = crate::scene::view::transform::ocs_point_to_wcs(
|
||||||
|
(arc.center.x, arc.center.y, arc.center.z),
|
||||||
|
normal,
|
||||||
|
);
|
||||||
|
let arc_pt = |a: f64| {
|
||||||
|
let (c, s) = (a.cos(), a.sin());
|
||||||
|
(
|
||||||
|
cwx + r * c * ax.0 + r * s * ay.0,
|
||||||
|
cwy + r * c * ax.1 + r * s * ay.1,
|
||||||
|
cwz + r * c * ax.2 + r * s * ay.2,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let (sx, sy, sz) = arc_pt(sa);
|
||||||
|
let (ex, ey, ez) = arc_pt(ea);
|
||||||
|
|
||||||
vec![PropSection {
|
vec![PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
|
|
@ -129,12 +156,21 @@ fn properties(arc: &Arc) -> Vec<PropSection> {
|
||||||
edit("Center Y", "center_y", arc.center.y),
|
edit("Center Y", "center_y", arc.center.y),
|
||||||
edit("Center Z", "center_z", arc.center.z),
|
edit("Center Z", "center_z", arc.center.z),
|
||||||
edit("Radius", "radius", arc.radius),
|
edit("Radius", "radius", arc.radius),
|
||||||
edit(
|
edit("Start angle", "start_angle", sa.to_degrees()),
|
||||||
"Start Angle (deg)",
|
edit("End angle", "end_angle", ea.to_degrees()),
|
||||||
"start_angle",
|
ro("Total angle", "total_angle", format!("{total_angle:.2}")),
|
||||||
arc.start_angle.to_degrees(),
|
ro("Arc length", "arc_length", format!("{arc_length:.4}")),
|
||||||
),
|
ro("Area", "area", format!("{area:.4}")),
|
||||||
edit("End Angle (deg)", "end_angle", arc.end_angle.to_degrees()),
|
ro("Start X", "start_x", format!("{sx:.4}")),
|
||||||
|
ro("Start Y", "start_y", format!("{sy:.4}")),
|
||||||
|
ro("Start Z", "start_z", format!("{sz:.4}")),
|
||||||
|
ro("End X", "end_x", format!("{ex:.4}")),
|
||||||
|
ro("End Y", "end_y", format!("{ey:.4}")),
|
||||||
|
ro("End Z", "end_z", format!("{ez:.4}")),
|
||||||
|
edit("Normal X", "normal_x", arc.normal.x),
|
||||||
|
edit("Normal Y", "normal_y", arc.normal.y),
|
||||||
|
edit("Normal Z", "normal_z", arc.normal.z),
|
||||||
|
edit("Thickness", "thickness", arc.thickness),
|
||||||
],
|
],
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
@ -150,6 +186,10 @@ fn apply_geom_prop(arc: &mut Arc, field: &str, value: &str) {
|
||||||
"radius" if v > 0.0 => arc.radius = v,
|
"radius" if v > 0.0 => arc.radius = v,
|
||||||
"start_angle" => arc.start_angle = v.to_radians(),
|
"start_angle" => arc.start_angle = v.to_radians(),
|
||||||
"end_angle" => arc.end_angle = v.to_radians(),
|
"end_angle" => arc.end_angle = v.to_radians(),
|
||||||
|
"normal_x" => arc.normal.x = v,
|
||||||
|
"normal_y" => arc.normal.y = v,
|
||||||
|
"normal_z" => arc.normal.z = v,
|
||||||
|
"thickness" => arc.thickness = v,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -385,26 +385,33 @@ impl Grippable for AttributeDefinition {
|
||||||
|
|
||||||
impl PropertyEditable for AttributeDefinition {
|
impl PropertyEditable for AttributeDefinition {
|
||||||
fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
let mut props = vec![
|
let mut text_props = vec![
|
||||||
ro("Tag", "att_tag", self.tag.clone()),
|
ro("Tag", "att_tag", self.tag.clone()),
|
||||||
ro("Prompt", "att_prompt", self.prompt.clone()),
|
ro("Prompt", "att_prompt", self.prompt.clone()),
|
||||||
Property {
|
Property {
|
||||||
label: "Default".into(),
|
label: "Value".into(),
|
||||||
field: "att_default",
|
field: "att_default",
|
||||||
value: PropValue::EditText(self.default_value.clone()),
|
value: PropValue::EditText(self.default_value.clone()),
|
||||||
},
|
},
|
||||||
edit("Insert X", "att_ix", self.insertion_point.x),
|
|
||||||
edit("Insert Y", "att_iy", self.insertion_point.y),
|
|
||||||
edit("Insert Z", "att_iz", self.insertion_point.z),
|
|
||||||
edit("Align X", "att_ax", self.alignment_point.x),
|
|
||||||
edit("Align Y", "att_ay", self.alignment_point.y),
|
|
||||||
edit("Align Z", "att_az", self.alignment_point.z),
|
|
||||||
edit("Height", "att_h", self.height),
|
|
||||||
edit("Rotation", "att_rot", self.rotation.to_degrees()),
|
|
||||||
edit("Width Factor", "att_wf", self.width_factor),
|
|
||||||
edit("Oblique Angle", "att_ob", self.oblique_angle.to_degrees()),
|
|
||||||
Property {
|
Property {
|
||||||
label: "H-Align".into(),
|
label: "Style".into(),
|
||||||
|
field: "att_style",
|
||||||
|
value: PropValue::Choice {
|
||||||
|
selected: if self.text_style.trim().is_empty() {
|
||||||
|
"Standard".into()
|
||||||
|
} else {
|
||||||
|
self.text_style.clone()
|
||||||
|
},
|
||||||
|
options: text_style_names.to_vec(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ro(
|
||||||
|
"Annotative",
|
||||||
|
"att_annotative",
|
||||||
|
bool_yn(self.flags.annotative),
|
||||||
|
),
|
||||||
|
Property {
|
||||||
|
label: "Justify".into(),
|
||||||
field: "att_halign",
|
field: "att_halign",
|
||||||
value: PropValue::Choice {
|
value: PropValue::Choice {
|
||||||
selected: halign_str(self.horizontal_alignment).to_string(),
|
selected: halign_str(self.horizontal_alignment).to_string(),
|
||||||
|
|
@ -425,30 +432,59 @@ impl PropertyEditable for AttributeDefinition {
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Property {
|
edit("Height", "att_h", self.height),
|
||||||
label: "Style".into(),
|
edit("Rotation", "att_rot", self.rotation.to_degrees()),
|
||||||
field: "att_style",
|
edit("Width factor", "att_wf", self.width_factor),
|
||||||
value: PropValue::Choice {
|
edit("Obliquing", "att_ob", self.oblique_angle.to_degrees()),
|
||||||
selected: if self.text_style.trim().is_empty() {
|
edit("Text alignment X", "att_ax", self.alignment_point.x),
|
||||||
"Standard".into()
|
edit("Text alignment Y", "att_ay", self.alignment_point.y),
|
||||||
} else {
|
edit("Text alignment Z", "att_az", self.alignment_point.z),
|
||||||
self.text_style.clone()
|
|
||||||
},
|
|
||||||
options: text_style_names.to_vec(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ro(
|
ro(
|
||||||
"Generation",
|
"Boundary width",
|
||||||
"att_gen_flags",
|
|
||||||
format!("{:#06b}", self.text_generation_flags & 0xff),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Field Length",
|
|
||||||
"att_field_len",
|
"att_field_len",
|
||||||
self.field_length.to_string(),
|
self.field_length.to_string(),
|
||||||
),
|
),
|
||||||
|
ro(
|
||||||
|
"Upside down",
|
||||||
|
"att_upside_down",
|
||||||
|
bool_yn(self.text_generation_flags & 0x4 != 0),
|
||||||
|
),
|
||||||
|
ro(
|
||||||
|
"Backward",
|
||||||
|
"att_backward",
|
||||||
|
bool_yn(self.text_generation_flags & 0x2 != 0),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
// Constant attributes can't be edited at insert time — surface that
|
||||||
|
// by marking the Value field read-only.
|
||||||
|
if self.flags.constant {
|
||||||
|
if let Some(p) = text_props.iter_mut().find(|p| p.field == "att_default") {
|
||||||
|
p.value = PropValue::ReadOnly(self.default_value.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
|
title: "Text".into(),
|
||||||
|
props: text_props,
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Geometry".into(),
|
||||||
|
props: vec![
|
||||||
|
edit("Position X", "att_ix", self.insertion_point.x),
|
||||||
|
edit("Position Y", "att_iy", self.insertion_point.y),
|
||||||
|
edit("Position Z", "att_iz", self.insertion_point.z),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Invisible", "att_invisible", bool_yn(self.flags.invisible)),
|
||||||
|
ro("Constant", "att_constant", bool_yn(self.flags.constant)),
|
||||||
|
ro("Verify", "att_verify", bool_yn(self.flags.verify)),
|
||||||
|
ro("Preset", "att_preset", bool_yn(self.flags.preset)),
|
||||||
|
ro("Lock position", "att_lock_pos", bool_yn(self.lock_position)),
|
||||||
Property {
|
Property {
|
||||||
label: "MText Mode".into(),
|
label: "Multiple lines".into(),
|
||||||
field: "att_mtext_flag",
|
field: "att_mtext_flag",
|
||||||
value: PropValue::Choice {
|
value: PropValue::Choice {
|
||||||
selected: mtext_flag_str(self.mtext_flag).to_string(),
|
selected: mtext_flag_str(self.mtext_flag).to_string(),
|
||||||
|
|
@ -458,30 +494,9 @@ impl PropertyEditable for AttributeDefinition {
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ro("Multiline", "att_is_multiline", bool_yn(self.is_multiline)),
|
],
|
||||||
ro("Line Count", "att_line_count", self.line_count.to_string()),
|
},
|
||||||
ro("Lock Position", "att_lock_pos", bool_yn(self.lock_position)),
|
]
|
||||||
ro("Invisible", "att_invisible", bool_yn(self.flags.invisible)),
|
|
||||||
ro("Constant", "att_constant", bool_yn(self.flags.constant)),
|
|
||||||
ro("Verify", "att_verify", bool_yn(self.flags.verify)),
|
|
||||||
ro("Preset", "att_preset", bool_yn(self.flags.preset)),
|
|
||||||
ro(
|
|
||||||
"Annotative",
|
|
||||||
"att_annotative",
|
|
||||||
bool_yn(self.flags.annotative),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
// Constant attributes can't be edited at insert time — surface that
|
|
||||||
// by marking the Default field read-only.
|
|
||||||
if self.flags.constant {
|
|
||||||
if let Some(p) = props.iter_mut().find(|p| p.field == "att_default") {
|
|
||||||
p.value = PropValue::ReadOnly(self.default_value.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vec![PropSection {
|
|
||||||
title: "Geometry".into(),
|
|
||||||
props,
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||||
|
|
@ -626,27 +641,31 @@ impl Grippable for AttributeEntity {
|
||||||
|
|
||||||
impl PropertyEditable for AttributeEntity {
|
impl PropertyEditable for AttributeEntity {
|
||||||
fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
vec![PropSection {
|
vec![
|
||||||
title: "Geometry".into(),
|
PropSection {
|
||||||
|
title: "Text".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Tag", "atte_tag", self.tag.clone()),
|
ro("Tag", "atte_tag", self.tag.clone()),
|
||||||
|
ro("Prompt", "atte_prompt", String::new()),
|
||||||
Property {
|
Property {
|
||||||
label: "Value".into(),
|
label: "Value".into(),
|
||||||
field: "atte_val",
|
field: "atte_val",
|
||||||
value: PropValue::EditText(self.value.clone()),
|
value: PropValue::EditText(self.value.clone()),
|
||||||
},
|
},
|
||||||
edit("Insert X", "atte_ix", self.insertion_point.x),
|
|
||||||
edit("Insert Y", "atte_iy", self.insertion_point.y),
|
|
||||||
edit("Insert Z", "atte_iz", self.insertion_point.z),
|
|
||||||
edit("Align X", "atte_ax", self.alignment_point.x),
|
|
||||||
edit("Align Y", "atte_ay", self.alignment_point.y),
|
|
||||||
edit("Align Z", "atte_az", self.alignment_point.z),
|
|
||||||
edit("Height", "atte_h", self.height),
|
|
||||||
edit("Rotation", "atte_rot", self.rotation.to_degrees()),
|
|
||||||
edit("Width Factor", "atte_wf", self.width_factor),
|
|
||||||
edit("Oblique Angle", "atte_ob", self.oblique_angle.to_degrees()),
|
|
||||||
Property {
|
Property {
|
||||||
label: "H-Align".into(),
|
label: "Style".into(),
|
||||||
|
field: "atte_style",
|
||||||
|
value: PropValue::Choice {
|
||||||
|
selected: if self.text_style.trim().is_empty() {
|
||||||
|
"Standard".into()
|
||||||
|
} else {
|
||||||
|
self.text_style.clone()
|
||||||
|
},
|
||||||
|
options: text_style_names.to_vec(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Property {
|
||||||
|
label: "Justify".into(),
|
||||||
field: "atte_halign",
|
field: "atte_halign",
|
||||||
value: PropValue::Choice {
|
value: PropValue::Choice {
|
||||||
selected: halign_str(self.horizontal_alignment).to_string(),
|
selected: halign_str(self.horizontal_alignment).to_string(),
|
||||||
|
|
@ -667,60 +686,63 @@ impl PropertyEditable for AttributeEntity {
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Property {
|
|
||||||
label: "Style".into(),
|
|
||||||
field: "atte_style",
|
|
||||||
value: PropValue::Choice {
|
|
||||||
selected: if self.text_style.trim().is_empty() {
|
|
||||||
"Standard".into()
|
|
||||||
} else {
|
|
||||||
self.text_style.clone()
|
|
||||||
},
|
|
||||||
options: text_style_names.to_vec(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ro(
|
|
||||||
"Generation",
|
|
||||||
"atte_gen_flags",
|
|
||||||
format!("{:#06b}", self.text_generation_flags & 0xff),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Field Length",
|
|
||||||
"atte_field_len",
|
|
||||||
self.field_length.to_string(),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"MText Mode",
|
|
||||||
"atte_mtext_flag",
|
|
||||||
mtext_flag_str(self.mtext_flag),
|
|
||||||
),
|
|
||||||
ro("Multiline", "atte_is_multiline", bool_yn(self.is_multiline)),
|
|
||||||
ro("Line Count", "atte_line_count", self.line_count.to_string()),
|
|
||||||
ro(
|
|
||||||
"Lock Position",
|
|
||||||
"atte_lock_pos",
|
|
||||||
bool_yn(self.lock_position),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Definition",
|
|
||||||
"atte_attdef",
|
|
||||||
if self.attdef_handle.is_null() {
|
|
||||||
"(none)".into()
|
|
||||||
} else {
|
|
||||||
format!("{:X}", self.attdef_handle.value())
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro("Invisible", "atte_invisible", bool_yn(self.flags.invisible)),
|
|
||||||
ro("Constant", "atte_constant", bool_yn(self.flags.constant)),
|
|
||||||
ro("Verify", "atte_verify", bool_yn(self.flags.verify)),
|
|
||||||
ro("Preset", "atte_preset", bool_yn(self.flags.preset)),
|
|
||||||
ro(
|
ro(
|
||||||
"Annotative",
|
"Annotative",
|
||||||
"atte_annotative",
|
"atte_annotative",
|
||||||
bool_yn(self.flags.annotative),
|
bool_yn(self.flags.annotative),
|
||||||
),
|
),
|
||||||
|
edit("Height", "atte_h", self.height),
|
||||||
|
edit("Rotation", "atte_rot", self.rotation.to_degrees()),
|
||||||
|
edit("Width factor", "atte_wf", self.width_factor),
|
||||||
|
edit("Obliquing", "atte_ob", self.oblique_angle.to_degrees()),
|
||||||
|
edit("Text alignment X", "atte_ax", self.alignment_point.x),
|
||||||
|
edit("Text alignment Y", "atte_ay", self.alignment_point.y),
|
||||||
|
edit("Text alignment Z", "atte_az", self.alignment_point.z),
|
||||||
|
ro(
|
||||||
|
"Boundary width",
|
||||||
|
"atte_field_len",
|
||||||
|
self.field_length.to_string(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Geometry".into(),
|
||||||
|
props: vec![
|
||||||
|
edit("Position X", "atte_ix", self.insertion_point.x),
|
||||||
|
edit("Position Y", "atte_iy", self.insertion_point.y),
|
||||||
|
edit("Position Z", "atte_iz", self.insertion_point.z),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
|
ro(
|
||||||
|
"Upside down",
|
||||||
|
"atte_upside_down",
|
||||||
|
bool_yn(self.text_generation_flags & 0x4 != 0),
|
||||||
|
),
|
||||||
|
ro(
|
||||||
|
"Backward",
|
||||||
|
"atte_backward",
|
||||||
|
bool_yn(self.text_generation_flags & 0x2 != 0),
|
||||||
|
),
|
||||||
|
ro("Invisible", "atte_invisible", bool_yn(self.flags.invisible)),
|
||||||
|
ro(
|
||||||
|
"Multiple lines",
|
||||||
|
"atte_mtext_flag",
|
||||||
|
mtext_flag_str(self.mtext_flag),
|
||||||
|
),
|
||||||
|
ro("Constant", "atte_constant", bool_yn(self.flags.constant)),
|
||||||
|
ro("Verify", "atte_verify", bool_yn(self.flags.verify)),
|
||||||
|
ro("Preset", "atte_preset", bool_yn(self.flags.preset)),
|
||||||
|
ro(
|
||||||
|
"Lock position",
|
||||||
|
"atte_lock_pos",
|
||||||
|
bool_yn(self.lock_position),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||||
|
|
|
||||||
|
|
@ -119,23 +119,26 @@ fn grips(circle: &Circle) -> Vec<GripDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn properties(circle: &Circle) -> Vec<PropSection> {
|
fn properties(circle: &Circle) -> Vec<PropSection> {
|
||||||
|
use std::f64::consts::PI;
|
||||||
|
let r = circle.radius;
|
||||||
vec![PropSection {
|
vec![PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
edit("Center X", "center_x", circle.center.x),
|
edit("Center X", "center_x", circle.center.x),
|
||||||
edit("Center Y", "center_y", circle.center.y),
|
edit("Center Y", "center_y", circle.center.y),
|
||||||
edit("Center Z", "center_z", circle.center.z),
|
edit("Center Z", "center_z", circle.center.z),
|
||||||
edit("Radius", "radius", circle.radius),
|
edit("Radius", "radius", r),
|
||||||
ro(
|
ro("Diameter", "diameter", format!("{:.4}", r * 2.0)),
|
||||||
"Diameter",
|
|
||||||
"diameter",
|
|
||||||
format!("{:.4}", circle.radius * 2.0),
|
|
||||||
),
|
|
||||||
ro(
|
ro(
|
||||||
"Circumference",
|
"Circumference",
|
||||||
"circumference",
|
"circumference",
|
||||||
format!("{:.4}", circle.radius * 2.0 * std::f64::consts::PI),
|
format!("{:.4}", 2.0 * PI * r),
|
||||||
),
|
),
|
||||||
|
ro("Area", "area", format!("{:.4}", PI * r * r)),
|
||||||
|
edit("Normal X", "normal_x", circle.normal.x),
|
||||||
|
edit("Normal Y", "normal_y", circle.normal.y),
|
||||||
|
edit("Normal Z", "normal_z", circle.normal.z),
|
||||||
|
edit("Thickness", "thickness", circle.thickness),
|
||||||
],
|
],
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
@ -149,6 +152,10 @@ fn apply_geom_prop(circle: &mut Circle, field: &str, value: &str) {
|
||||||
"center_y" => circle.center.y = v,
|
"center_y" => circle.center.y = v,
|
||||||
"center_z" => circle.center.z = v,
|
"center_z" => circle.center.z = v,
|
||||||
"radius" if v > 0.0 => circle.radius = v,
|
"radius" if v > 0.0 => circle.radius = v,
|
||||||
|
"normal_x" => circle.normal.x = v,
|
||||||
|
"normal_y" => circle.normal.y = v,
|
||||||
|
"normal_z" => circle.normal.z = v,
|
||||||
|
"thickness" => circle.thickness = v,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ use acadrust::entities::Ellipse;
|
||||||
use truck_modeling::{builder, BSplineCurve, Curve, Edge, KnotVec, Point3, Wire};
|
use truck_modeling::{builder, BSplineCurve, Curve, Edge, KnotVec, Point3, Wire};
|
||||||
|
|
||||||
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, parse_f64, ro_prop as ro, square_grip,
|
||||||
|
};
|
||||||
use crate::entities::traits::TruckConvertible;
|
use crate::entities::traits::TruckConvertible;
|
||||||
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
@ -159,32 +161,81 @@ fn grips(ell: &Ellipse) -> Vec<GripDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn properties(ell: &Ellipse) -> Vec<PropSection> {
|
fn properties(ell: &Ellipse) -> Vec<PropSection> {
|
||||||
let r_major = (ell.major_axis.x * ell.major_axis.x
|
use crate::entities::traits::MassPropsCalc;
|
||||||
+ ell.major_axis.y * ell.major_axis.y
|
|
||||||
+ ell.major_axis.z * ell.major_axis.z)
|
let r_major = ell.major_axis_length();
|
||||||
.sqrt();
|
let r_minor = ell.minor_axis_length();
|
||||||
vec![PropSection {
|
|
||||||
|
// Axis frame in WCS: major-axis unit vector `u`, minor direction `v = normal × u`.
|
||||||
|
let u = glam::DVec3::new(ell.major_axis.x, ell.major_axis.y, ell.major_axis.z);
|
||||||
|
let u = if r_major > 1e-12 {
|
||||||
|
u / r_major
|
||||||
|
} else {
|
||||||
|
glam::DVec3::X
|
||||||
|
};
|
||||||
|
let n = glam::DVec3::new(ell.normal.x, ell.normal.y, ell.normal.z);
|
||||||
|
let v = n.cross(u);
|
||||||
|
|
||||||
|
// Parametric end point in WCS: center + r_major·cos(t1)·u + r_minor·sin(t1)·v.
|
||||||
|
let t1 = ell.end_parameter;
|
||||||
|
let end = glam::DVec3::new(ell.center.x, ell.center.y, ell.center.z)
|
||||||
|
+ u * (r_major * t1.cos())
|
||||||
|
+ v * (r_minor * t1.sin());
|
||||||
|
|
||||||
|
let start_angle = ell.start_parameter.to_degrees().rem_euclid(360.0);
|
||||||
|
let end_angle = ell.end_parameter.to_degrees().rem_euclid(360.0);
|
||||||
|
|
||||||
|
let props = ell.mass_props();
|
||||||
|
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
edit("Center X", "center_x", ell.center.x),
|
edit("Center X", "center_x", ell.center.x),
|
||||||
edit("Center Y", "center_y", ell.center.y),
|
edit("Center Y", "center_y", ell.center.y),
|
||||||
edit("Center Z", "center_z", ell.center.z),
|
edit("Center Z", "center_z", ell.center.z),
|
||||||
ro("Major Radius", "major_r", format!("{r_major:.4}")),
|
ro("End X", "end_x", format!("{:.4}", end.x)),
|
||||||
ro(
|
ro("End Y", "end_y", format!("{:.4}", end.y)),
|
||||||
"Minor Radius",
|
ro("End Z", "end_z", format!("{:.4}", end.z)),
|
||||||
"minor_r",
|
ro("Major radius", "major_r", format!("{r_major:.4}")),
|
||||||
format!("{:.4}", r_major * ell.minor_axis_ratio),
|
ro("Minor radius", "minor_r", format!("{r_minor:.4}")),
|
||||||
),
|
edit("Radius ratio", "ratio", ell.minor_axis_ratio),
|
||||||
ro(
|
ro("Start angle", "start_angle", format!("{start_angle:.2}")),
|
||||||
"Minor/Major",
|
ro("End angle", "end_angle", format!("{end_angle:.2}")),
|
||||||
"ratio",
|
edit("Start parameter", "start_param", ell.start_parameter),
|
||||||
format!("{:.4}", ell.minor_axis_ratio),
|
edit("End parameter", "end_param", ell.end_parameter),
|
||||||
),
|
ro("Area", "area", format!("{:.4}", props.area)),
|
||||||
|
ro("Length", "length", format!("{:.4}", props.perimeter)),
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
|
edit("Normal X", "normal_x", ell.normal.x),
|
||||||
|
edit("Normal Y", "normal_y", ell.normal.y),
|
||||||
|
edit("Normal Z", "normal_z", ell.normal.z),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(_ell: &mut Ellipse, _field: &str, _value: &str) {}
|
fn apply_geom_prop(ell: &mut Ellipse, field: &str, value: &str) {
|
||||||
|
let Some(v) = parse_f64(value) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
match field {
|
||||||
|
"center_x" => ell.center.x = v,
|
||||||
|
"center_y" => ell.center.y = v,
|
||||||
|
"center_z" => ell.center.z = v,
|
||||||
|
"ratio" => ell.minor_axis_ratio = v,
|
||||||
|
"start_param" => ell.start_parameter = v,
|
||||||
|
"end_param" => ell.end_parameter = v,
|
||||||
|
"normal_x" => ell.normal.x = v,
|
||||||
|
"normal_y" => ell.normal.y = v,
|
||||||
|
"normal_z" => ell.normal.z = v,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn apply_grip(ell: &mut Ellipse, grip_id: usize, apply: GripApply) {
|
fn apply_grip(ell: &mut Ellipse, grip_id: usize, apply: GripApply) {
|
||||||
match (grip_id, apply) {
|
match (grip_id, apply) {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,59 @@ use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Pr
|
||||||
use crate::scene::convert::tess_util::{arc_segments, arc_signed_span, wire_chord_tol, FallbackGeometry};
|
use crate::scene::convert::tess_util::{arc_segments, arc_signed_span, wire_chord_tol, FallbackGeometry};
|
||||||
use crate::scene::model::wire_model::SnapHint;
|
use crate::scene::model::wire_model::SnapHint;
|
||||||
|
|
||||||
|
/// Signed area of every boundary path, summed via the shoelace formula over
|
||||||
|
/// the OCS boundary vertices (bulge/curved edges approximated by their end
|
||||||
|
/// points). Returns the absolute total area.
|
||||||
|
fn boundary_area(h: &Hatch) -> f64 {
|
||||||
|
let mut area = 0.0;
|
||||||
|
for path in &h.paths {
|
||||||
|
let mut ring: Vec<[f64; 2]> = Vec::new();
|
||||||
|
for edge in &path.edges {
|
||||||
|
match edge {
|
||||||
|
BoundaryEdge::Polyline(poly) => {
|
||||||
|
for v in &poly.vertices {
|
||||||
|
ring.push([v.x, v.y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BoundaryEdge::Line(l) => {
|
||||||
|
ring.push([l.start.x, l.start.y]);
|
||||||
|
ring.push([l.end.x, l.end.y]);
|
||||||
|
}
|
||||||
|
BoundaryEdge::CircularArc(a) => ring.push([a.center.x, a.center.y]),
|
||||||
|
BoundaryEdge::EllipticArc(e) => ring.push([e.center.x, e.center.y]),
|
||||||
|
BoundaryEdge::Spline(s) => {
|
||||||
|
let src = if !s.fit_points.is_empty() {
|
||||||
|
s.fit_points.iter().map(|p| [p.x, p.y]).collect::<Vec<_>>()
|
||||||
|
} else {
|
||||||
|
s.control_points
|
||||||
|
.iter()
|
||||||
|
.map(|p| [p.x, p.y])
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
ring.extend(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let n = ring.len();
|
||||||
|
if n < 3 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut acc = 0.0;
|
||||||
|
for i in 0..n {
|
||||||
|
let a = ring[i];
|
||||||
|
let b = ring[(i + 1) % n];
|
||||||
|
acc += a[0] * b[1] - b[0] * a[1];
|
||||||
|
}
|
||||||
|
area += acc.abs() * 0.5;
|
||||||
|
}
|
||||||
|
area
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format a color for a read-only row (`ByLayer` / `ByBlock` / index / RGB).
|
||||||
|
fn color_str(c: acadrust::types::Color) -> String {
|
||||||
|
c.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
fn properties(h: &Hatch) -> Vec<PropSection> {
|
fn properties(h: &Hatch) -> Vec<PropSection> {
|
||||||
let pattern_type = match h.pattern_type {
|
let pattern_type = match h.pattern_type {
|
||||||
acadrust::entities::HatchPatternType::Predefined => "Predefined",
|
acadrust::entities::HatchPatternType::Predefined => "Predefined",
|
||||||
|
|
@ -19,61 +72,109 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
|
||||||
acadrust::entities::HatchStyleType::Outer => "Outer",
|
acadrust::entities::HatchStyleType::Outer => "Outer",
|
||||||
acadrust::entities::HatchStyleType::Ignore => "Ignore",
|
acadrust::entities::HatchStyleType::Ignore => "Ignore",
|
||||||
};
|
};
|
||||||
let fill_type = if h.gradient_color.enabled {
|
let area = boundary_area(h);
|
||||||
format!("Gradient ({})", h.gradient_color.name)
|
let g = &h.gradient_color;
|
||||||
} else if h.is_solid {
|
let grad_c1 = g.colors.first().map(|e| color_str(e.color)).unwrap_or_default();
|
||||||
"Solid".into()
|
let grad_c2 = g.colors.get(1).map(|e| color_str(e.color)).unwrap_or_default();
|
||||||
} else {
|
|
||||||
format!("Pattern ({})", h.pattern.name)
|
if g.enabled {
|
||||||
};
|
// ── Gradient fill ──────────────────────────────────────────────────
|
||||||
let boundary_count: usize = h
|
let grad_type = if g.is_single_color { "One color" } else { "Two color" };
|
||||||
.paths
|
let centered = if g.shift.abs() < 1e-9 { "Yes" } else { "No" };
|
||||||
.iter()
|
return vec![
|
||||||
.map(|p| {
|
PropSection {
|
||||||
p.edges
|
title: "Pattern".into(),
|
||||||
.iter()
|
props: vec![
|
||||||
.map(|e| match e {
|
ro("Type", "fill_type", grad_type),
|
||||||
BoundaryEdge::Polyline(poly) => poly.vertices.len(),
|
ro("Gradient name", "gradient_name", g.name.clone()),
|
||||||
_ => 1,
|
ro("Color 1", "gradient_color_1", grad_c1),
|
||||||
})
|
ro("Color 2", "gradient_color_2", grad_c2),
|
||||||
.sum::<usize>()
|
edit("Angle", "pattern_angle", g.angle.to_degrees()),
|
||||||
})
|
ro("Centered", "gradient_centered", centered),
|
||||||
.sum();
|
],
|
||||||
vec![PropSection {
|
},
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Fill Type", "fill_type", fill_type),
|
edit("Elevation", "elevation", h.elevation),
|
||||||
Property {
|
ro("Origin X", "origin_x", String::new()),
|
||||||
label: "Pattern Name".into(),
|
ro("Origin Y", "origin_y", String::new()),
|
||||||
field: "pattern_name",
|
ro("Area", "area", format!("{:.4}", area)),
|
||||||
value: PropValue::HatchPatternChoice(h.pattern.name.clone()),
|
ro("Cumulative area", "cumulative_area", format!("{:.4}", area)),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
ro("Pattern Type", "pattern_type", pattern_type),
|
PropSection {
|
||||||
edit(
|
title: "Misc".into(),
|
||||||
"Pattern Angle",
|
props: vec![
|
||||||
"pattern_angle",
|
|
||||||
h.pattern_angle.to_degrees(),
|
|
||||||
),
|
|
||||||
edit("Pattern Scale", "pattern_scale", h.pattern_scale),
|
|
||||||
ro("Style", "style", style),
|
|
||||||
ro("Boundary Paths", "path_count", h.paths.len().to_string()),
|
|
||||||
ro("Boundary Verts", "vert_count", boundary_count.to_string()),
|
|
||||||
ro("Double", "double", if h.is_double { "Yes" } else { "No" }),
|
|
||||||
ro(
|
ro(
|
||||||
"Associative",
|
"Associative",
|
||||||
"associative",
|
"associative",
|
||||||
if h.is_associative { "Yes" } else { "No" },
|
if h.is_associative { "Yes" } else { "No" },
|
||||||
),
|
),
|
||||||
edit("Elevation", "elevation", h.elevation),
|
ro("Annotative", "annotative", String::new()),
|
||||||
ro("Seed Points", "seed_count", h.seed_points.len().to_string()),
|
ro("Island detection style", "style", style),
|
||||||
ro("Pixel Size", "pixel_size", format!("{:.6}", h.pixel_size)),
|
ro("Background color", "background_color", String::new()),
|
||||||
ro(
|
|
||||||
"Normal",
|
|
||||||
"normal",
|
|
||||||
format!("{:.3}, {:.3}, {:.3}", h.normal.x, h.normal.y, h.normal.z),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Hatch (pattern / solid) ────────────────────────────────────────────
|
||||||
|
let hatch_type = if h.is_solid { "Solid" } else { "Pattern" };
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
|
title: "Pattern".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Type", "pattern_type_label", hatch_type),
|
||||||
|
Property {
|
||||||
|
label: "Pattern name".into(),
|
||||||
|
field: "pattern_name",
|
||||||
|
value: PropValue::HatchPatternChoice(h.pattern.name.clone()),
|
||||||
|
},
|
||||||
|
ro("Color", "color", color_str(h.common.color)),
|
||||||
|
ro("Background color", "background_color", String::new()),
|
||||||
|
ro(
|
||||||
|
"Transparency",
|
||||||
|
"transparency",
|
||||||
|
format!("{}%", (h.common.transparency.as_percent() * 100.0).round() as i32),
|
||||||
|
),
|
||||||
|
edit("Angle", "pattern_angle", h.pattern_angle.to_degrees()),
|
||||||
|
edit("Scale", "pattern_scale", h.pattern_scale),
|
||||||
|
ro("Origin X", "origin_x", String::new()),
|
||||||
|
ro("Origin Y", "origin_y", String::new()),
|
||||||
|
ro("Annotative", "annotative", String::new()),
|
||||||
|
ro(
|
||||||
|
"Associative",
|
||||||
|
"associative",
|
||||||
|
if h.is_associative { "Yes" } else { "No" },
|
||||||
|
),
|
||||||
|
ro("Layer override", "layer_override", String::new()),
|
||||||
|
ro("Double", "double", if h.is_double { "Yes" } else { "No" }),
|
||||||
|
ro("Spacing", "spacing", String::new()),
|
||||||
|
ro("ISO pen width", "iso_pen_width", String::new()),
|
||||||
|
ro("Gradient colors", "gradient_colors", g.colors.len().to_string()),
|
||||||
|
ro("Gradient tint", "gradient_tint", format!("{:.4}", g.color_tint)),
|
||||||
|
ro("Gradient color 1", "gradient_color_1", grad_c1),
|
||||||
|
ro("Gradient color 2", "gradient_color_2", grad_c2),
|
||||||
|
ro("Pattern type", "pattern_type", pattern_type),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Geometry".into(),
|
||||||
|
props: vec![
|
||||||
|
edit("Elevation", "elevation", h.elevation),
|
||||||
|
ro("Area", "area", format!("{:.4}", area)),
|
||||||
|
ro("Cumulative area", "cumulative_area", format!("{:.4}", area)),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Boundary retention", "boundary_retention", String::new()),
|
||||||
|
ro("Island detection style", "style", style),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) {
|
fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) {
|
||||||
|
|
@ -81,6 +182,7 @@ fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match field {
|
match field {
|
||||||
|
"pattern_angle" if h.gradient_color.enabled => h.gradient_color.angle = v.to_radians(),
|
||||||
"pattern_angle" => h.pattern_angle = v.to_radians(),
|
"pattern_angle" => h.pattern_angle = v.to_radians(),
|
||||||
"pattern_scale" if v > 0.0 => h.pattern_scale = v,
|
"pattern_scale" if v > 0.0 => h.pattern_scale = v,
|
||||||
"elevation" => h.elevation = v,
|
"elevation" => h.elevation = v,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ 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::scene::model::object::{GripApply, GripDef, PropSection};
|
use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
||||||
use crate::scene::model::wire_model::WireModel;
|
use crate::scene::model::wire_model::WireModel;
|
||||||
use crate::scene::cache::block_cache;
|
use crate::scene::cache::block_cache;
|
||||||
use crate::scene::convert::tessellate;
|
use crate::scene::convert::tessellate;
|
||||||
|
|
@ -20,19 +20,58 @@ fn grips(ins: &Insert) -> Vec<GripDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn properties(ins: &Insert) -> Vec<PropSection> {
|
fn properties(ins: &Insert) -> Vec<PropSection> {
|
||||||
vec![PropSection {
|
let annotative = ins
|
||||||
|
.common
|
||||||
|
.extended_data
|
||||||
|
.get_record("AcAnnotativeData")
|
||||||
|
.is_some();
|
||||||
|
let attrs: Vec<Property> = ins
|
||||||
|
.attributes
|
||||||
|
.iter()
|
||||||
|
.map(|a| Property {
|
||||||
|
label: a.tag.clone(),
|
||||||
|
field: "attr",
|
||||||
|
value: PropValue::AttrText {
|
||||||
|
tag: a.tag.clone(),
|
||||||
|
value: a.value.clone(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
edit("Insert X", "ins_x", ins.insert_point.x),
|
edit("Position X", "ins_x", ins.insert_point.x),
|
||||||
edit("Insert Y", "ins_y", ins.insert_point.y),
|
edit("Position Y", "ins_y", ins.insert_point.y),
|
||||||
edit("Insert Z", "ins_z", ins.insert_point.z),
|
edit("Position Z", "ins_z", ins.insert_point.z),
|
||||||
edit("Scale X", "x_scale", ins.x_scale()),
|
edit("Scale X", "x_scale", ins.x_scale()),
|
||||||
edit("Scale Y", "y_scale", ins.y_scale()),
|
edit("Scale Y", "y_scale", ins.y_scale()),
|
||||||
edit("Scale Z", "z_scale", ins.z_scale()),
|
edit("Scale Z", "z_scale", ins.z_scale()),
|
||||||
edit("Rotation", "rotation", ins.rotation.to_degrees()),
|
|
||||||
ro("Block", "block", ins.block_name.clone()),
|
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Name", "block", ins.block_name.clone()),
|
||||||
|
edit("Rotation", "rotation", ins.rotation.to_degrees()),
|
||||||
|
ro("Block Unit", "block_unit", String::new()),
|
||||||
|
ro("Unit factor", "unit_factor", String::new()),
|
||||||
|
ro(
|
||||||
|
"Annotative",
|
||||||
|
"annotative",
|
||||||
|
if annotative { "Yes" } else { "No" },
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Attributes".into(),
|
||||||
|
props: attrs,
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Custom".into(),
|
||||||
|
props: Vec::new(),
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(ins: &mut Insert, field: &str, value: &str) {
|
fn apply_geom_prop(ins: &mut Insert, field: &str, value: &str) {
|
||||||
|
|
|
||||||
|
|
@ -191,102 +191,94 @@ fn hookline_dir_str(hd: &HooklineDirection) -> &'static str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn color_str(c: &acadrust::types::Color) -> String {
|
||||||
|
match c.rgb() {
|
||||||
|
Some((r, g, b)) => format!("RGB({},{},{})", r, g, b),
|
||||||
|
None => format!("{:?}", c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn properties(leader: &Leader) -> Vec<PropSection> {
|
fn properties(leader: &Leader) -> Vec<PropSection> {
|
||||||
let n = leader.vertices.len();
|
let n = leader.vertices.len();
|
||||||
let mut props = vec![
|
|
||||||
// Style
|
let arrow_size = leader.text_height.max(1.0);
|
||||||
Property {
|
|
||||||
label: "Dim Style".into(),
|
let misc = vec![
|
||||||
field: "dimension_style",
|
|
||||||
value: PropValue::EditText(leader.dimension_style.clone()),
|
|
||||||
},
|
|
||||||
// Path type
|
|
||||||
choice_prop(
|
choice_prop(
|
||||||
"Path Type",
|
"Type",
|
||||||
"path_type",
|
"path_type",
|
||||||
path_type_str(&leader.path_type),
|
path_type_str(&leader.path_type),
|
||||||
&["Straight", "Spline"],
|
&["Straight", "Spline"],
|
||||||
),
|
),
|
||||||
// Creation type
|
|
||||||
choice_prop(
|
choice_prop(
|
||||||
"Creation Type",
|
"Annotation type",
|
||||||
"creation_type",
|
"creation_type",
|
||||||
creation_type_str(&leader.creation_type),
|
creation_type_str(&leader.creation_type),
|
||||||
&["With Text", "With Tolerance", "With Block", "No Annotation"],
|
&["With Text", "With Tolerance", "With Block", "No Annotation"],
|
||||||
),
|
),
|
||||||
// Arrow
|
bool_toggle("Arrowhead", "arrow_enabled", leader.arrow_enabled),
|
||||||
bool_toggle("Arrow", "arrow_enabled", leader.arrow_enabled),
|
Property {
|
||||||
// Hookline
|
label: "Dim style".into(),
|
||||||
bool_toggle("Hookline", "hookline_enabled", leader.hookline_enabled),
|
field: "dimension_style",
|
||||||
choice_prop(
|
value: PropValue::EditText(leader.dimension_style.clone()),
|
||||||
"Hookline Dir",
|
|
||||||
"hookline_direction",
|
|
||||||
hookline_dir_str(&leader.hookline_direction),
|
|
||||||
&["Opposite", "Same"],
|
|
||||||
),
|
|
||||||
// Text dims
|
|
||||||
edit("Text Height", "text_height", leader.text_height),
|
|
||||||
edit("Text Width", "text_width", leader.text_width),
|
|
||||||
// Normal
|
|
||||||
edit("Normal X", "normal_x", leader.normal.x),
|
|
||||||
edit("Normal Y", "normal_y", leader.normal.y),
|
|
||||||
edit("Normal Z", "normal_z", leader.normal.z),
|
|
||||||
// Horizontal direction
|
|
||||||
edit("H Dir X", "h_dir_x", leader.horizontal_direction.x),
|
|
||||||
edit("H Dir Y", "h_dir_y", leader.horizontal_direction.y),
|
|
||||||
edit("H Dir Z", "h_dir_z", leader.horizontal_direction.z),
|
|
||||||
// Block offset
|
|
||||||
edit("Block Offset X", "block_offset_x", leader.block_offset.x),
|
|
||||||
edit("Block Offset Y", "block_offset_y", leader.block_offset.y),
|
|
||||||
edit("Block Offset Z", "block_offset_z", leader.block_offset.z),
|
|
||||||
// Annotation offset
|
|
||||||
edit("Ann Offset X", "ann_offset_x", leader.annotation_offset.x),
|
|
||||||
edit("Ann Offset Y", "ann_offset_y", leader.annotation_offset.y),
|
|
||||||
edit("Ann Offset Z", "ann_offset_z", leader.annotation_offset.z),
|
|
||||||
// Stats
|
|
||||||
ro("Vertices", "vertex_count", n.to_string()),
|
|
||||||
ro("Length", "length", format!("{:.4}", leader.length())),
|
|
||||||
// Annotation reference + dim-style colour override (read-only —
|
|
||||||
// they are written by the file and survive a round-trip).
|
|
||||||
ro(
|
|
||||||
"Annotation",
|
|
||||||
"annotation_handle",
|
|
||||||
if leader.annotation_handle.is_null() {
|
|
||||||
"(none)".to_string()
|
|
||||||
} else {
|
|
||||||
format!("{:X}", leader.annotation_handle.value())
|
|
||||||
},
|
},
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Override Color",
|
|
||||||
"override_color",
|
|
||||||
match leader.override_color.rgb() {
|
|
||||||
Some((r, g, b)) => format!("RGB({},{},{})", r, g, b),
|
|
||||||
None => format!("{:?}", leader.override_color),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Arrow point (vertex[0])
|
let lines_arrows = vec![
|
||||||
|
ro("Dim line color", "override_color", color_str(&leader.override_color)),
|
||||||
|
ro(
|
||||||
|
"Dim line lineweight",
|
||||||
|
"line_weight",
|
||||||
|
format!("{:?}", leader.common.line_weight),
|
||||||
|
),
|
||||||
|
ro("Dim line linetype", "linetype", leader.common.linetype.clone()),
|
||||||
|
bool_toggle("Arrowhead", "arrow_enabled", leader.arrow_enabled),
|
||||||
|
ro("Arrow size", "arrow_size", format!("{:.4}", arrow_size)),
|
||||||
|
];
|
||||||
|
|
||||||
|
let text = vec![
|
||||||
|
edit("Text height", "text_height", leader.text_height),
|
||||||
|
ro("Text offset", "text_offset", String::new()),
|
||||||
|
ro("Text style", "text_style", String::new()),
|
||||||
|
ro("Text color", "text_color", color_str(&leader.override_color)),
|
||||||
|
ro("Text position vert", "text_pos_vert", String::new()),
|
||||||
|
];
|
||||||
|
|
||||||
|
let fit = vec![ro("Dim scale overall", "dim_scale_overall", String::new())];
|
||||||
|
|
||||||
|
let mut geometry = vec![ro("Vertex", "vertex_count", n.to_string())];
|
||||||
if let Some(a) = leader.arrow_point() {
|
if let Some(a) = leader.arrow_point() {
|
||||||
props.push(edit("Arrow X", "arrow_x", a.x));
|
geometry.push(edit("Vertex X", "vertex_x", a.x));
|
||||||
props.push(edit("Arrow Y", "arrow_y", a.y));
|
geometry.push(edit("Vertex Y", "vertex_y", a.y));
|
||||||
props.push(edit("Arrow Z", "arrow_z", a.z));
|
geometry.push(edit("Vertex Z", "vertex_z", a.z));
|
||||||
|
} else {
|
||||||
|
geometry.push(ro("Vertex X", "vertex_x", String::new()));
|
||||||
|
geometry.push(ro("Vertex Y", "vertex_y", String::new()));
|
||||||
|
geometry.push(ro("Vertex Z", "vertex_z", String::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// End point (last vertex)
|
vec![
|
||||||
if n >= 2 {
|
PropSection {
|
||||||
if let Some(e) = leader.end_point() {
|
title: "Misc".into(),
|
||||||
props.push(edit("End X", "end_x", e.x));
|
props: misc,
|
||||||
props.push(edit("End Y", "end_y", e.y));
|
},
|
||||||
props.push(edit("End Z", "end_z", e.z));
|
PropSection {
|
||||||
}
|
title: "Lines & Arrows".into(),
|
||||||
}
|
props: lines_arrows,
|
||||||
|
},
|
||||||
vec![PropSection {
|
PropSection {
|
||||||
|
title: "Text".into(),
|
||||||
|
props: text,
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Fit".into(),
|
||||||
|
props: fit,
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props,
|
props: geometry,
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(leader: &mut Leader, field: &str, value: &str) {
|
fn apply_geom_prop(leader: &mut Leader, field: &str, value: &str) {
|
||||||
|
|
@ -398,45 +390,21 @@ fn apply_geom_prop(leader: &mut Leader, field: &str, value: &str) {
|
||||||
leader.annotation_offset.z = v;
|
leader.annotation_offset.z = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"arrow_x" => {
|
"vertex_x" => {
|
||||||
if let (Some(v), Some(vert)) = (f64(value), leader.vertices.get_mut(0)) {
|
if let (Some(v), Some(vert)) = (f64(value), leader.vertices.get_mut(0)) {
|
||||||
vert.x = v;
|
vert.x = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"arrow_y" => {
|
"vertex_y" => {
|
||||||
if let (Some(v), Some(vert)) = (f64(value), leader.vertices.get_mut(0)) {
|
if let (Some(v), Some(vert)) = (f64(value), leader.vertices.get_mut(0)) {
|
||||||
vert.y = v;
|
vert.y = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"arrow_z" => {
|
"vertex_z" => {
|
||||||
if let (Some(v), Some(vert)) = (f64(value), leader.vertices.get_mut(0)) {
|
if let (Some(v), Some(vert)) = (f64(value), leader.vertices.get_mut(0)) {
|
||||||
vert.z = v;
|
vert.z = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"end_x" => {
|
|
||||||
let last = leader.vertices.len().saturating_sub(1);
|
|
||||||
if last > 0 {
|
|
||||||
if let (Some(v), Some(vert)) = (f64(value), leader.vertices.get_mut(last)) {
|
|
||||||
vert.x = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"end_y" => {
|
|
||||||
let last = leader.vertices.len().saturating_sub(1);
|
|
||||||
if last > 0 {
|
|
||||||
if let (Some(v), Some(vert)) = (f64(value), leader.vertices.get_mut(last)) {
|
|
||||||
vert.y = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"end_z" => {
|
|
||||||
let last = leader.vertices.len().saturating_sub(1);
|
|
||||||
if last > 0 {
|
|
||||||
if let (Some(v), Some(vert)) = (f64(value), leader.vertices.get_mut(last)) {
|
|
||||||
vert.z = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,10 @@ fn grips(line: &Line) -> Vec<GripDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn properties(line: &Line) -> Vec<PropSection> {
|
fn properties(line: &Line) -> Vec<PropSection> {
|
||||||
|
let dx = line.end.x - line.start.x;
|
||||||
|
let dy = line.end.y - line.start.y;
|
||||||
|
let dz = line.end.z - line.start.z;
|
||||||
|
let angle = dy.atan2(dx).to_degrees().rem_euclid(360.0);
|
||||||
vec![PropSection {
|
vec![PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
|
|
@ -82,7 +86,11 @@ fn properties(line: &Line) -> Vec<PropSection> {
|
||||||
edit("End X", "end_x", line.end.x),
|
edit("End X", "end_x", line.end.x),
|
||||||
edit("End Y", "end_y", line.end.y),
|
edit("End Y", "end_y", line.end.y),
|
||||||
edit("End Z", "end_z", line.end.z),
|
edit("End Z", "end_z", line.end.z),
|
||||||
|
ro("Delta X", "delta_x", format!("{dx:.4}")),
|
||||||
|
ro("Delta Y", "delta_y", format!("{dy:.4}")),
|
||||||
|
ro("Delta Z", "delta_z", format!("{dz:.4}")),
|
||||||
ro("Length", "length", format!("{:.4}", line.length())),
|
ro("Length", "length", format!("{:.4}", line.length())),
|
||||||
|
ro("Angle", "angle", format!("{angle:.2}")),
|
||||||
],
|
],
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -323,21 +323,40 @@ fn grips(pline: &LwPolyline) -> Vec<GripDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn properties(pline: &LwPolyline) -> Vec<PropSection> {
|
fn properties(pline: &LwPolyline) -> Vec<PropSection> {
|
||||||
let (length, area) = length_and_area(pline);
|
let v0 = pline.vertices.first();
|
||||||
vec![PropSection {
|
let vx = v0.map_or(0.0, |v| v.location.x);
|
||||||
|
let vy = v0.map_or(0.0, |v| v.location.y);
|
||||||
|
let start_w = v0.map_or(0.0, |v| v.start_width);
|
||||||
|
let end_w = v0.map_or(0.0, |v| v.end_width);
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Vertices", "vertices", pline.vertices.len().to_string()),
|
ro("Current Vertex", "current_vertex", String::new()),
|
||||||
|
edit("Vertex X", "vertex_x", vx),
|
||||||
|
edit("Vertex Y", "vertex_y", vy),
|
||||||
|
edit("Start segment width", "start_width", start_w),
|
||||||
|
edit("End segment width", "end_width", end_w),
|
||||||
|
edit("Global width", "global_width", pline.constant_width),
|
||||||
|
edit("Elevation", "elevation", pline.elevation),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
ro(
|
ro(
|
||||||
"Closed",
|
"Closed",
|
||||||
"closed",
|
"closed",
|
||||||
if pline.is_closed { "Yes" } else { "No" },
|
if pline.is_closed { "Yes" } else { "No" },
|
||||||
),
|
),
|
||||||
edit("Elevation", "elevation", pline.elevation),
|
ro(
|
||||||
ro("Length", "length", format!("{length:.4}")),
|
"Linetype generation",
|
||||||
ro("Area", "area", format!("{area:.4}")),
|
"plinegen",
|
||||||
|
if pline.plinegen { "Yes" } else { "No" },
|
||||||
|
),
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Path length and enclosed area of a polyline, accounting for arc
|
/// Path length and enclosed area of a polyline, accounting for arc
|
||||||
|
|
@ -382,8 +401,30 @@ fn apply_geom_prop(pline: &mut LwPolyline, field: &str, value: &str) {
|
||||||
let Some(v) = parse_f64(value) else {
|
let Some(v) = parse_f64(value) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if field == "elevation" {
|
match field {
|
||||||
pline.elevation = v;
|
"elevation" => pline.elevation = v,
|
||||||
|
"global_width" => pline.constant_width = v,
|
||||||
|
"vertex_x" => {
|
||||||
|
if let Some(vtx) = pline.vertices.first_mut() {
|
||||||
|
vtx.location.x = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"vertex_y" => {
|
||||||
|
if let Some(vtx) = pline.vertices.first_mut() {
|
||||||
|
vtx.location.y = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"start_width" => {
|
||||||
|
if let Some(vtx) = pline.vertices.first_mut() {
|
||||||
|
vtx.start_width = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"end_width" => {
|
||||||
|
if let Some(vtx) = pline.vertices.first_mut() {
|
||||||
|
vtx.end_width = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,21 +117,28 @@ impl Grippable for Face3D {
|
||||||
impl PropertyEditable for Face3D {
|
impl PropertyEditable for Face3D {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
use crate::entities::common::edit_prop as edit;
|
use crate::entities::common::edit_prop as edit;
|
||||||
|
let inv = self.invisible_edges;
|
||||||
|
let edge = |hidden: bool| if hidden { "Invisible" } else { "Visible" };
|
||||||
vec![PropSection {
|
vec![PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
edit("P1 X", "f3_p1x", self.first_corner.x),
|
ro("Current vertex", "f3_current", String::new()),
|
||||||
edit("P1 Y", "f3_p1y", self.first_corner.y),
|
edit("Vertex 1 X", "f3_p1x", self.first_corner.x),
|
||||||
edit("P1 Z", "f3_p1z", self.first_corner.z),
|
edit("Vertex 1 Y", "f3_p1y", self.first_corner.y),
|
||||||
edit("P2 X", "f3_p2x", self.second_corner.x),
|
edit("Vertex 1 Z", "f3_p1z", self.first_corner.z),
|
||||||
edit("P2 Y", "f3_p2y", self.second_corner.y),
|
edit("Vertex 2 X", "f3_p2x", self.second_corner.x),
|
||||||
edit("P2 Z", "f3_p2z", self.second_corner.z),
|
edit("Vertex 2 Y", "f3_p2y", self.second_corner.y),
|
||||||
edit("P3 X", "f3_p3x", self.third_corner.x),
|
edit("Vertex 2 Z", "f3_p2z", self.second_corner.z),
|
||||||
edit("P3 Y", "f3_p3y", self.third_corner.y),
|
edit("Vertex 3 X", "f3_p3x", self.third_corner.x),
|
||||||
edit("P3 Z", "f3_p3z", self.third_corner.z),
|
edit("Vertex 3 Y", "f3_p3y", self.third_corner.y),
|
||||||
edit("P4 X", "f3_p4x", self.fourth_corner.x),
|
edit("Vertex 3 Z", "f3_p3z", self.third_corner.z),
|
||||||
edit("P4 Y", "f3_p4y", self.fourth_corner.y),
|
edit("Vertex 4 X", "f3_p4x", self.fourth_corner.x),
|
||||||
edit("P4 Z", "f3_p4z", self.fourth_corner.z),
|
edit("Vertex 4 Y", "f3_p4y", self.fourth_corner.y),
|
||||||
|
edit("Vertex 4 Z", "f3_p4z", self.fourth_corner.z),
|
||||||
|
ro("Edge 1", "f3_edge1", edge(inv.is_first_invisible())),
|
||||||
|
ro("Edge 2", "f3_edge2", edge(inv.is_second_invisible())),
|
||||||
|
ro("Edge 3", "f3_edge3", edge(inv.is_third_invisible())),
|
||||||
|
ro("Edge 4", "f3_edge4", edge(inv.is_fourth_invisible())),
|
||||||
],
|
],
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
@ -283,38 +290,49 @@ impl PropertyEditable for PolygonMesh {
|
||||||
acadrust::entities::polygon_mesh::SurfaceSmoothType::Cubic => "Cubic",
|
acadrust::entities::polygon_mesh::SurfaceSmoothType::Cubic => "Cubic",
|
||||||
acadrust::entities::polygon_mesh::SurfaceSmoothType::Bezier => "Bezier",
|
acadrust::entities::polygon_mesh::SurfaceSmoothType::Bezier => "Bezier",
|
||||||
};
|
};
|
||||||
vec![PropSection {
|
let yesno = |b: bool| if b { "Yes" } else { "No" };
|
||||||
|
let first = self.vertices.first();
|
||||||
|
// Grid faces: one quad per cell; closed direction adds a wrap row/column.
|
||||||
|
let m = self.m_vertex_count.max(0) as i64;
|
||||||
|
let n = self.n_vertex_count.max(0) as i64;
|
||||||
|
let cells_m = if self.is_closed_m() { m } else { (m - 1).max(0) };
|
||||||
|
let cells_n = if self.is_closed_n() { n } else { (n - 1).max(0) };
|
||||||
|
let face_count = cells_m * cells_n;
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("M count", "pm_m", self.m_vertex_count.to_string()),
|
ro("Vertex", "pm_vertex", String::new()),
|
||||||
ro("N count", "pm_n", self.n_vertex_count.to_string()),
|
|
||||||
ro("Vertices", "pm_v", self.vertices.len().to_string()),
|
|
||||||
ro("Smooth Type", "pm_smooth", smooth),
|
|
||||||
ro(
|
ro(
|
||||||
"Smooth M Density",
|
"Vertex X",
|
||||||
"pm_smooth_m",
|
"pm_vx",
|
||||||
self.m_smooth_density.to_string(),
|
first.map(|v| format!("{:.4}", v.location.x)).unwrap_or_default(),
|
||||||
),
|
),
|
||||||
ro(
|
ro(
|
||||||
"Smooth N Density",
|
"Vertex Y",
|
||||||
"pm_smooth_n",
|
"pm_vy",
|
||||||
self.n_smooth_density.to_string(),
|
first.map(|v| format!("{:.4}", v.location.y)).unwrap_or_default(),
|
||||||
),
|
),
|
||||||
ro(
|
ro(
|
||||||
"Elevation",
|
"Vertex Z",
|
||||||
"pm_elevation",
|
"pm_vz",
|
||||||
format!("{:.4}", self.elevation),
|
first.map(|v| format!("{:.4}", v.location.z)).unwrap_or_default(),
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Normal",
|
|
||||||
"pm_normal",
|
|
||||||
format!(
|
|
||||||
"{:.3}, {:.3}, {:.3}",
|
|
||||||
self.normal.x, self.normal.y, self.normal.z
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
ro("M vertex count", "pm_m", self.m_vertex_count.to_string()),
|
||||||
|
ro("N vertex count", "pm_n", self.n_vertex_count.to_string()),
|
||||||
|
ro("M closed", "pm_closed_m", yesno(self.is_closed_m())),
|
||||||
|
ro("N closed", "pm_closed_n", yesno(self.is_closed_n())),
|
||||||
|
ro("M density", "pm_smooth_m", self.m_smooth_density.to_string()),
|
||||||
|
ro("N density", "pm_smooth_n", self.n_smooth_density.to_string()),
|
||||||
|
ro("Vertex count", "pm_v", self.vertices.len().to_string()),
|
||||||
|
ro("Face count", "pm_faces", face_count.to_string()),
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![ro("Fit/smooth", "pm_smooth", smooth)],
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, _field: &str, _value: &str) {}
|
fn apply_geom_prop(&mut self, _field: &str, _value: &str) {}
|
||||||
|
|
@ -431,40 +449,44 @@ impl PropertyEditable for PolyfaceMesh {
|
||||||
acadrust::entities::PolyfaceSmoothType::Cubic => "Cubic",
|
acadrust::entities::PolyfaceSmoothType::Cubic => "Cubic",
|
||||||
acadrust::entities::PolyfaceSmoothType::Bezier => "Bezier",
|
acadrust::entities::PolyfaceSmoothType::Bezier => "Bezier",
|
||||||
};
|
};
|
||||||
vec![PropSection {
|
let first = self.vertices.first();
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Vertices", "pfm_v", self.vertices.len().to_string()),
|
ro("Vertex", "pfm_vertex", String::new()),
|
||||||
ro("Faces", "pfm_f", self.faces.len().to_string()),
|
|
||||||
ro("Smooth Surface", "pfm_smooth", smooth),
|
|
||||||
ro(
|
ro(
|
||||||
"Seqend",
|
"Vertex X",
|
||||||
"pfm_seqend_handle",
|
"pfm_vx",
|
||||||
match self.seqend_handle {
|
first.map(|v| format!("{:.4}", v.location.x)).unwrap_or_default(),
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
|
||||||
_ => "(none)".to_string(),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
ro(
|
ro(
|
||||||
"Elevation",
|
"Vertex Y",
|
||||||
"pfm_elevation",
|
"pfm_vy",
|
||||||
format!("{:.4}", self.elevation),
|
first.map(|v| format!("{:.4}", v.location.y)).unwrap_or_default(),
|
||||||
),
|
),
|
||||||
ro(
|
ro(
|
||||||
"Normal",
|
"Vertex Z",
|
||||||
"pfm_normal",
|
"pfm_vz",
|
||||||
format!(
|
first.map(|v| format!("{:.4}", v.location.z)).unwrap_or_default(),
|
||||||
"{:.3}, {:.3}, {:.3}",
|
|
||||||
self.normal.x, self.normal.y, self.normal.z
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Thickness",
|
|
||||||
"pfm_thickness",
|
|
||||||
format!("{:.4}", self.thickness),
|
|
||||||
),
|
),
|
||||||
|
// Polyface meshes store an explicit vertex/face list rather
|
||||||
|
// than an M×N grid, so the grid-only rows are not applicable.
|
||||||
|
ro("M vertex count", "pfm_m", String::new()),
|
||||||
|
ro("N vertex count", "pfm_n", String::new()),
|
||||||
|
ro("M closed", "pfm_closed_m", String::new()),
|
||||||
|
ro("N closed", "pfm_closed_n", String::new()),
|
||||||
|
ro("M density", "pfm_density_m", String::new()),
|
||||||
|
ro("N density", "pfm_density_n", String::new()),
|
||||||
|
ro("Vertex count", "pfm_v", self.vertices.len().to_string()),
|
||||||
|
ro("Face count", "pfm_f", self.faces.len().to_string()),
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![ro("Fit/smooth", "pfm_smooth", smooth)],
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, _field: &str, _value: &str) {}
|
fn apply_geom_prop(&mut self, _field: &str, _value: &str) {}
|
||||||
|
|
@ -598,24 +620,35 @@ impl Grippable for Mesh {
|
||||||
|
|
||||||
impl PropertyEditable for Mesh {
|
impl PropertyEditable for Mesh {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
let creased_edges = self.edges.iter().filter(|e| e.has_crease()).count();
|
// Watertight when every face edge is shared by exactly two faces
|
||||||
|
// (closed manifold). Empty meshes are not watertight.
|
||||||
|
let mut edge_use: std::collections::HashMap<(usize, usize), u32> =
|
||||||
|
std::collections::HashMap::new();
|
||||||
|
for face in &self.faces {
|
||||||
|
let vs = &face.vertices;
|
||||||
|
for i in 0..vs.len() {
|
||||||
|
let a = vs[i];
|
||||||
|
let b = vs[(i + 1) % vs.len()];
|
||||||
|
let key = if a < b { (a, b) } else { (b, a) };
|
||||||
|
*edge_use.entry(key).or_insert(0) += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let watertight =
|
||||||
|
!self.faces.is_empty() && edge_use.values().all(|&c| c == 2);
|
||||||
vec![PropSection {
|
vec![PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Vertices", "msh_v", self.vertices.len().to_string()),
|
|
||||||
ro("Faces", "msh_f", self.faces.len().to_string()),
|
|
||||||
ro("Edges", "msh_e", self.edges.len().to_string()),
|
|
||||||
ro("Creased Edges", "msh_creased", creased_edges.to_string()),
|
|
||||||
ro("Version", "msh_version", self.version.to_string()),
|
|
||||||
ro(
|
ro(
|
||||||
"Subdivision Level",
|
"Level of Smoothness",
|
||||||
"msh_subdiv",
|
"msh_subdiv",
|
||||||
self.subdivision_level.to_string(),
|
self.subdivision_level.to_string(),
|
||||||
),
|
),
|
||||||
|
ro("Number of Faces", "msh_f", self.faces.len().to_string()),
|
||||||
|
ro("Number of Grips", "msh_grips", self.vertices.len().to_string()),
|
||||||
ro(
|
ro(
|
||||||
"Blend Crease",
|
"Watertight",
|
||||||
"msh_blend_crease",
|
"msh_watertight",
|
||||||
if self.blend_crease { "Yes" } else { "No" },
|
if watertight { "Yes" } else { "No" },
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
}]
|
}]
|
||||||
|
|
|
||||||
|
|
@ -194,27 +194,26 @@ impl PropertyEditable for MLine {
|
||||||
acadrust::entities::MLineJustification::Zero => "Zero",
|
acadrust::entities::MLineJustification::Zero => "Zero",
|
||||||
acadrust::entities::MLineJustification::Bottom => "Bottom",
|
acadrust::entities::MLineJustification::Bottom => "Bottom",
|
||||||
};
|
};
|
||||||
vec![PropSection {
|
let cur = self.vertices.first();
|
||||||
|
let cur_x = cur.map(|v| v.position.x).unwrap_or(0.0);
|
||||||
|
let cur_y = cur.map(|v| v.position.y).unwrap_or(0.0);
|
||||||
|
let cur_z = cur.map(|v| v.position.z).unwrap_or(0.0);
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Style", "ml_style", self.style_name.clone()),
|
ro("Vertex", "ml_vertex", if self.vertices.is_empty() { String::new() } else { "1".to_string() }),
|
||||||
ro(
|
edit("Vertex X", "ml_vertex_x", cur_x),
|
||||||
"Style Handle",
|
edit("Vertex Y", "ml_vertex_y", cur_y),
|
||||||
"ml_style_handle",
|
edit("Vertex Z", "ml_vertex_z", cur_z),
|
||||||
match self.style_handle {
|
],
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
|
||||||
_ => "(none)".to_string(),
|
|
||||||
},
|
},
|
||||||
),
|
PropSection {
|
||||||
ro("Vertices", "ml_verts", self.vertices.len().to_string()),
|
title: "Misc".into(),
|
||||||
ro(
|
props: vec![
|
||||||
"Style Elements",
|
ro("Style", "ml_style", self.style_name.clone()),
|
||||||
"ml_style_elem_count",
|
|
||||||
self.style_element_count.to_string(),
|
|
||||||
),
|
|
||||||
edit("Scale", "ml_scale", self.scale_factor),
|
|
||||||
Property {
|
Property {
|
||||||
label: "Justification".into(),
|
label: "Style justification".into(),
|
||||||
field: "ml_justification",
|
field: "ml_justification",
|
||||||
value: PropValue::Choice {
|
value: PropValue::Choice {
|
||||||
selected: just_str.to_string(),
|
selected: just_str.to_string(),
|
||||||
|
|
@ -224,16 +223,10 @@ impl PropertyEditable for MLine {
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Property {
|
edit("Style scale", "ml_scale", self.scale_factor),
|
||||||
label: "Closed".into(),
|
|
||||||
field: "ml_closed",
|
|
||||||
value: PropValue::BoolToggle {
|
|
||||||
field: "ml_closed",
|
|
||||||
value: self.flags.contains(acadrust::entities::MLineFlags::CLOSED),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||||
|
|
@ -261,8 +254,24 @@ impl PropertyEditable for MLine {
|
||||||
let Ok(v) = value.trim().parse::<f64>() else {
|
let Ok(v) = value.trim().parse::<f64>() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if field == "ml_scale" && v != 0.0 {
|
match field {
|
||||||
self.scale_factor = v;
|
"ml_scale" if v != 0.0 => self.scale_factor = v,
|
||||||
|
"ml_vertex_x" => {
|
||||||
|
if let Some(vx) = self.vertices.first_mut() {
|
||||||
|
vx.position.x = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"ml_vertex_y" => {
|
||||||
|
if let Some(vx) = self.vertices.first_mut() {
|
||||||
|
vx.position.y = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"ml_vertex_z" => {
|
||||||
|
if let Some(vx) = self.vertices.first_mut() {
|
||||||
|
vx.position.z = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -192,20 +192,48 @@ fn grips(t: &MText) -> Vec<GripDef> {
|
||||||
vec![square_grip(0, p), triangle_grip(1, width_grip)]
|
vec![square_grip(0, p), triangle_grip(1, width_grip)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn columns_str(c: &acadrust::entities::MTextColumnData) -> &'static str {
|
||||||
|
match c.column_type {
|
||||||
|
1 => "Static",
|
||||||
|
2 => "Dynamic",
|
||||||
|
_ => "None",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn properties(t: &MText, text_style_names: &[String]) -> Vec<PropSection> {
|
fn properties(t: &MText, text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
vec![PropSection {
|
// Absolute line-space distance for single spacing (~1.66 * height) scaled
|
||||||
title: "Geometry".into(),
|
// by the line-spacing factor.
|
||||||
|
let line_space_distance = t.height * 1.666_666_666_666_667 * t.line_spacing_factor;
|
||||||
|
let bg_mask_on = (t.background_fill_flags & 0x03) != 0;
|
||||||
|
let text_frame_on = (t.background_fill_flags & 0x10) != 0;
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
|
title: "Text".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
edit("Insert X", "ins_x", t.insertion_point.x),
|
|
||||||
edit("Insert Y", "ins_y", t.insertion_point.y),
|
|
||||||
edit("Insert Z", "ins_z", t.insertion_point.z),
|
|
||||||
edit("Height", "height", t.height),
|
|
||||||
edit("Width", "rect_w", t.rectangle_width),
|
|
||||||
edit("Rect Height", "rect_h", t.rectangle_height.unwrap_or(0.0)),
|
|
||||||
edit("Rotation", "rotation", t.rotation.to_degrees()),
|
|
||||||
edit("Line Spacing", "line_spacing", t.line_spacing_factor),
|
|
||||||
Property {
|
Property {
|
||||||
label: "H-Align".into(),
|
label: "Contents".into(),
|
||||||
|
field: "content",
|
||||||
|
value: PropValue::EditText(t.value.clone()),
|
||||||
|
},
|
||||||
|
Property {
|
||||||
|
label: "Style".into(),
|
||||||
|
field: "style",
|
||||||
|
value: PropValue::Choice {
|
||||||
|
selected: if t.style.trim().is_empty() {
|
||||||
|
"Standard".into()
|
||||||
|
} else {
|
||||||
|
t.style.clone()
|
||||||
|
},
|
||||||
|
options: text_style_names.to_vec(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ro(
|
||||||
|
"Annotative",
|
||||||
|
"annotative",
|
||||||
|
if t.is_annotative { "Yes" } else { "No" },
|
||||||
|
),
|
||||||
|
Property {
|
||||||
|
label: "Justify".into(),
|
||||||
field: "h_align",
|
field: "h_align",
|
||||||
value: PropValue::Choice {
|
value: PropValue::Choice {
|
||||||
selected: mtext_halign_str(&t.attachment_point).to_string(),
|
selected: mtext_halign_str(&t.attachment_point).to_string(),
|
||||||
|
|
@ -236,25 +264,43 @@ fn properties(t: &MText, text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
"direction",
|
"direction",
|
||||||
drawing_dir_str(&t.drawing_direction).to_string(),
|
drawing_dir_str(&t.drawing_direction).to_string(),
|
||||||
),
|
),
|
||||||
Property {
|
edit("Text height", "height", t.height),
|
||||||
label: "Content".into(),
|
edit("Rotation", "rotation", t.rotation.to_degrees()),
|
||||||
field: "content",
|
edit("Line space factor", "line_spacing", t.line_spacing_factor),
|
||||||
value: PropValue::EditText(t.value.clone()),
|
ro(
|
||||||
},
|
"Line space distance",
|
||||||
Property {
|
"line_space_distance",
|
||||||
label: "Style".into(),
|
format!("{line_space_distance:.4}"),
|
||||||
field: "style",
|
),
|
||||||
value: PropValue::Choice {
|
ro("Line space style", "line_space_style", String::new()),
|
||||||
selected: if t.style.trim().is_empty() {
|
ro(
|
||||||
"Standard".into()
|
"Background mask",
|
||||||
} else {
|
"background_mask",
|
||||||
t.style.clone()
|
if bg_mask_on { "On" } else { "Off" },
|
||||||
},
|
),
|
||||||
options: text_style_names.to_vec(),
|
edit("Defined width", "rect_w", t.rectangle_width),
|
||||||
},
|
edit("Defined height", "rect_h", t.rectangle_height.unwrap_or(0.0)),
|
||||||
},
|
ro(
|
||||||
|
"Text frame",
|
||||||
|
"text_frame",
|
||||||
|
if text_frame_on { "On" } else { "Off" },
|
||||||
|
),
|
||||||
|
ro(
|
||||||
|
"Columns",
|
||||||
|
"columns",
|
||||||
|
columns_str(&t.column_data).to_string(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Geometry".into(),
|
||||||
|
props: vec![
|
||||||
|
edit("Position X", "ins_x", t.insertion_point.x),
|
||||||
|
edit("Position Y", "ins_y", t.insertion_point.y),
|
||||||
|
edit("Position Z", "ins_z", t.insertion_point.z),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(t: &mut MText, field: &str, value: &str) {
|
fn apply_geom_prop(t: &mut MText, field: &str, value: &str) {
|
||||||
|
|
|
||||||
|
|
@ -520,53 +520,108 @@ fn choice(label: &str, field: &'static str, selected: &str, opts: &[&str]) -> Pr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hexh(h: Option<acadrust::Handle>) -> String {
|
||||||
|
match h {
|
||||||
|
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
||||||
|
_ => "(none)".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn properties(ml: &MultiLeader) -> Vec<PropSection> {
|
fn properties(ml: &MultiLeader) -> Vec<PropSection> {
|
||||||
let ctx = &ml.context;
|
let ctx = &ml.context;
|
||||||
let total_pts: usize = ctx
|
let first_root = ctx.leader_roots.first();
|
||||||
.leader_roots
|
|
||||||
.iter()
|
|
||||||
.flat_map(|r| r.lines.iter())
|
|
||||||
.map(|l| l.points.len())
|
|
||||||
.sum();
|
|
||||||
|
|
||||||
let mut props = vec![
|
// ── Misc ─────────────────────────────────────────────────────────────
|
||||||
// Content
|
let misc = PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Leader type", "path_type", path_type_str(&ml.path_type)),
|
||||||
choice(
|
choice(
|
||||||
"Content Type",
|
"Content type",
|
||||||
"content_type",
|
"content_type",
|
||||||
content_type_str(&ml.content_type),
|
content_type_str(&ml.content_type),
|
||||||
&["None", "MText", "Block", "Tolerance"],
|
&["None", "MText", "Block", "Tolerance"],
|
||||||
),
|
),
|
||||||
Property {
|
edit("Overall scale", "scale_factor", ml.scale_factor),
|
||||||
label: "Text".into(),
|
ro(
|
||||||
field: "text_string",
|
"Annotative",
|
||||||
value: PropValue::EditText(ctx.text_string.clone()),
|
|
||||||
},
|
|
||||||
edit("Text Height", "text_height", ml.text_height),
|
|
||||||
edit("Text X", "text_x", ctx.text_location.x),
|
|
||||||
edit("Text Y", "text_y", ctx.text_location.y),
|
|
||||||
edit("Text Z", "text_z", ctx.text_location.z),
|
|
||||||
bool_toggle("Text Frame", "text_frame", ml.text_frame),
|
|
||||||
// Leader line
|
|
||||||
choice(
|
|
||||||
"Path Type",
|
|
||||||
"path_type",
|
|
||||||
path_type_str(&ml.path_type),
|
|
||||||
&["Straight", "Spline", "Invisible"],
|
|
||||||
),
|
|
||||||
bool_toggle("Landing", "enable_landing", ml.enable_landing),
|
|
||||||
bool_toggle("Dogleg", "enable_dogleg", ml.enable_dogleg),
|
|
||||||
edit("Dogleg Length", "dogleg_length", ml.dogleg_length),
|
|
||||||
edit("Arrow Size", "arrowhead_size", ml.arrowhead_size),
|
|
||||||
edit("Scale", "scale_factor", ml.scale_factor),
|
|
||||||
bool_toggle(
|
|
||||||
"Annotation Scale",
|
|
||||||
"enable_annotation_scale",
|
"enable_annotation_scale",
|
||||||
ml.enable_annotation_scale,
|
if ml.enable_annotation_scale { "Yes" } else { "No" },
|
||||||
),
|
),
|
||||||
// Text attachment
|
// No stored annotative-scale name on the entity.
|
||||||
|
ro("Annotative scale", "annotative_scale", String::new()),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Leaders ──────────────────────────────────────────────────────────
|
||||||
|
let leaders = PropSection {
|
||||||
|
title: "Leaders".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Leader type", "path_type", path_type_str(&ml.path_type)),
|
||||||
|
ro("Leader color", "line_color", format!("{:?}", ml.line_color)),
|
||||||
|
ro(
|
||||||
|
"Leader linetype",
|
||||||
|
"line_type_handle",
|
||||||
|
hexh(ml.line_type_handle),
|
||||||
|
),
|
||||||
|
ro(
|
||||||
|
"Leader linetype scale",
|
||||||
|
"leader_linetype_scale",
|
||||||
|
format!("{:.4}", ml.common.linetype_scale),
|
||||||
|
),
|
||||||
|
ro(
|
||||||
|
"Leader lineweight",
|
||||||
|
"line_weight",
|
||||||
|
format!("{:?}", ml.line_weight),
|
||||||
|
),
|
||||||
|
ro("Arrowhead", "arrowhead_handle", hexh(ml.arrowhead_handle)),
|
||||||
|
edit("Arrowhead size", "arrowhead_size", ml.arrowhead_size),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Leader Structure ─────────────────────────────────────────────────
|
||||||
|
let structure = PropSection {
|
||||||
|
title: "Leader Structure".into(),
|
||||||
|
props: vec![
|
||||||
|
// Maximum leader points / segment-angle constraints are leader-style
|
||||||
|
// settings not stored on the entity instance.
|
||||||
|
ro("Maximum leader points", "max_leader_points", String::new()),
|
||||||
|
ro("First segment angle", "first_segment_angle", String::new()),
|
||||||
|
ro("Second segment angle", "second_segment_angle", String::new()),
|
||||||
|
ro(
|
||||||
|
"Landing",
|
||||||
|
"enable_landing",
|
||||||
|
if ml.enable_landing { "Yes" } else { "No" },
|
||||||
|
),
|
||||||
|
edit(
|
||||||
|
"Landing distance",
|
||||||
|
"landing_distance",
|
||||||
|
first_root.map(|r| r.landing_distance).unwrap_or(0.0),
|
||||||
|
),
|
||||||
|
edit("Dogleg length", "dogleg_length", ml.dogleg_length),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Text ─────────────────────────────────────────────────────────────
|
||||||
|
let text = PropSection {
|
||||||
|
title: "Text".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Text style", "text_style_handle", hexh(ml.text_style_handle)),
|
||||||
|
ro(
|
||||||
|
"Text angle",
|
||||||
|
"text_angle_type",
|
||||||
|
format!("{:?}", ml.text_angle_type),
|
||||||
|
),
|
||||||
|
ro("Text color", "text_color", format!("{:?}", ml.text_color)),
|
||||||
|
edit("Text height", "text_height", ml.text_height),
|
||||||
|
ro(
|
||||||
|
"Justification",
|
||||||
|
"text_alignment",
|
||||||
|
format!("{:?}", ml.text_alignment),
|
||||||
|
),
|
||||||
|
bool_toggle("Frame text", "text_frame", ml.text_frame),
|
||||||
choice(
|
choice(
|
||||||
"Left Attach",
|
"Left attachment type",
|
||||||
"text_left_attachment",
|
"text_left_attachment",
|
||||||
attachment_str(&ml.text_left_attachment),
|
attachment_str(&ml.text_left_attachment),
|
||||||
&[
|
&[
|
||||||
|
|
@ -579,7 +634,7 @@ fn properties(ml: &MultiLeader) -> Vec<PropSection> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
choice(
|
choice(
|
||||||
"Right Attach",
|
"Right attachment type",
|
||||||
"text_right_attachment",
|
"text_right_attachment",
|
||||||
attachment_str(&ml.text_right_attachment),
|
attachment_str(&ml.text_right_attachment),
|
||||||
&[
|
&[
|
||||||
|
|
@ -591,133 +646,60 @@ fn properties(ml: &MultiLeader) -> Vec<PropSection> {
|
||||||
"Bottom Line",
|
"Bottom Line",
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// Top / Bottom attach (used when text_attachment_direction = Vertical)
|
ro(
|
||||||
choice(
|
"Text align type",
|
||||||
"Top Attach",
|
"text_attachment_direction",
|
||||||
"text_top_attachment",
|
format!("{:?}", ml.text_attachment_direction),
|
||||||
attachment_str(&ml.text_top_attachment),
|
),
|
||||||
&[
|
edit("Landing gap", "landing_gap", ctx.landing_gap),
|
||||||
"Top of Top",
|
ro(
|
||||||
"Mid of Top",
|
"Background mask",
|
||||||
"Mid of Text",
|
"background_fill_enabled",
|
||||||
"Mid of Bot",
|
if ctx.background_fill_enabled {
|
||||||
"Bot of Bot",
|
"Yes"
|
||||||
"Bottom Line",
|
} else {
|
||||||
|
"No"
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
};
|
||||||
choice(
|
|
||||||
"Bottom Attach",
|
// ── Block ────────────────────────────────────────────────────────────
|
||||||
"text_bottom_attachment",
|
let block = PropSection {
|
||||||
attachment_str(&ml.text_bottom_attachment),
|
title: "Block".into(),
|
||||||
&[
|
props: vec![
|
||||||
"Top of Top",
|
|
||||||
"Mid of Top",
|
|
||||||
"Mid of Text",
|
|
||||||
"Mid of Bot",
|
|
||||||
"Bot of Bot",
|
|
||||||
"Bottom Line",
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// Stats
|
|
||||||
ro("Leader Pts", "total_pts", total_pts.to_string()),
|
|
||||||
ro("Roots", "root_count", ctx.leader_roots.len().to_string()),
|
|
||||||
// Style references / handles (read-only — the multileader's own
|
|
||||||
// copies of the style values are the authoritative render inputs).
|
|
||||||
ro(
|
ro(
|
||||||
"Style Handle",
|
"Source block",
|
||||||
"style_handle",
|
|
||||||
match ml.style_handle {
|
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
|
||||||
_ => "(none)".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Text Style Handle",
|
|
||||||
"text_style_handle",
|
|
||||||
match ml.text_style_handle {
|
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
|
||||||
_ => "(none)".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Arrow Handle",
|
|
||||||
"arrowhead_handle",
|
|
||||||
match ml.arrowhead_handle {
|
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
|
||||||
_ => "(none)".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Line Type Handle",
|
|
||||||
"line_type_handle",
|
|
||||||
match ml.line_type_handle {
|
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
|
||||||
_ => "(none)".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Block Content Handle",
|
|
||||||
"block_content_handle",
|
"block_content_handle",
|
||||||
match ml.block_content_handle {
|
hexh(ml.block_content_handle),
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
|
||||||
_ => "(none)".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// Less common toggles surfaced read-only.
|
|
||||||
ro(
|
|
||||||
"Extend Leader",
|
|
||||||
"extend_leader_to_text",
|
|
||||||
if ml.extend_leader_to_text {
|
|
||||||
"Yes"
|
|
||||||
} else {
|
|
||||||
"No"
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
ro(
|
ro(
|
||||||
"Text Direction Negative",
|
"Block attachment",
|
||||||
"text_direction_negative",
|
"block_connection_type",
|
||||||
if ml.text_direction_negative {
|
format!("{:?}", ml.block_connection_type),
|
||||||
"Yes"
|
|
||||||
} else {
|
|
||||||
"No"
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
ro(
|
ro(
|
||||||
"Text Align In IPE",
|
"Block color",
|
||||||
"text_align_in_ipe",
|
"block_content_color",
|
||||||
ml.text_align_in_ipe.to_string(),
|
format!("{:?}", ml.block_content_color),
|
||||||
),
|
),
|
||||||
ro(
|
ro(
|
||||||
"Property Override Flags",
|
"Block scale",
|
||||||
"property_override_flags",
|
|
||||||
format!("{:#018b}", ml.property_override_flags.bits()),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Block Scale",
|
|
||||||
"block_scale",
|
"block_scale",
|
||||||
format!(
|
format!(
|
||||||
"{:.3} × {:.3} × {:.3}",
|
"{:.3} × {:.3} × {:.3}",
|
||||||
ml.block_scale.x, ml.block_scale.y, ml.block_scale.z
|
ml.block_scale.x, ml.block_scale.y, ml.block_scale.z
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ro(
|
edit(
|
||||||
"Block Rotation",
|
"Block rotation",
|
||||||
"block_rotation",
|
"block_rotation",
|
||||||
format!("{:.3}°", ml.block_rotation.to_degrees()),
|
ml.block_rotation.to_degrees(),
|
||||||
),
|
),
|
||||||
];
|
],
|
||||||
|
};
|
||||||
|
|
||||||
// Connection point for first root (most common case)
|
vec![misc, leaders, structure, text, block]
|
||||||
if let Some(root) = ctx.leader_roots.first() {
|
|
||||||
props.push(edit("Root Conn X", "conn_x", root.connection_point.x));
|
|
||||||
props.push(edit("Root Conn Y", "conn_y", root.connection_point.y));
|
|
||||||
props.push(edit("Root Conn Z", "conn_z", root.connection_point.z));
|
|
||||||
}
|
|
||||||
|
|
||||||
vec![PropSection {
|
|
||||||
title: "Geometry".into(),
|
|
||||||
props,
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(ml: &mut MultiLeader, field: &str, value: &str) {
|
fn apply_geom_prop(ml: &mut MultiLeader, field: &str, value: &str) {
|
||||||
|
|
@ -804,6 +786,21 @@ fn apply_geom_prop(ml: &mut MultiLeader, field: &str, value: &str) {
|
||||||
ml.scale_factor = v;
|
ml.scale_factor = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"landing_distance" => {
|
||||||
|
if let (Some(v), Some(root)) = (f64(value), ml.context.leader_roots.first_mut()) {
|
||||||
|
root.landing_distance = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"landing_gap" => {
|
||||||
|
if let Some(v) = f64(value) {
|
||||||
|
ml.context.landing_gap = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"block_rotation" => {
|
||||||
|
if let Some(v) = f64(value) {
|
||||||
|
ml.block_rotation = v.to_radians();
|
||||||
|
}
|
||||||
|
}
|
||||||
"conn_x" => {
|
"conn_x" => {
|
||||||
if let (Some(v), Some(root)) = (f64(value), ml.context.leader_roots.first_mut()) {
|
if let (Some(v), Some(root)) = (f64(value), ml.context.leader_roots.first_mut()) {
|
||||||
root.connection_point.x = v;
|
root.connection_point.x = v;
|
||||||
|
|
|
||||||
|
|
@ -79,42 +79,30 @@ fn properties(ole: &Ole2Frame) -> Vec<PropSection> {
|
||||||
OleObjectType::Embedded => "Embedded",
|
OleObjectType::Embedded => "Embedded",
|
||||||
OleObjectType::Static => "Static",
|
OleObjectType::Static => "Static",
|
||||||
};
|
};
|
||||||
vec![PropSection {
|
let width = (ole.lower_right_corner.x - ole.upper_left_corner.x).abs();
|
||||||
|
let height = (ole.upper_left_corner.y - ole.lower_right_corner.y).abs();
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Type", "ole_type", type_str),
|
edit("Position X", "ole_ulx", ole.upper_left_corner.x),
|
||||||
edit("Upper Left X", "ole_ulx", ole.upper_left_corner.x),
|
edit("Position Y", "ole_uly", ole.upper_left_corner.y),
|
||||||
edit("Upper Left Y", "ole_uly", ole.upper_left_corner.y),
|
edit("Position Z", "ole_ulz", ole.upper_left_corner.z),
|
||||||
edit("Lower Right X", "ole_lrx", ole.lower_right_corner.x),
|
ro("Width", "ole_width", format!("{:.4}", width)),
|
||||||
edit("Lower Right Y", "ole_lry", ole.lower_right_corner.y),
|
ro("Height", "ole_height", format!("{:.4}", height)),
|
||||||
ro("Version", "ole_version", ole.version.to_string()),
|
ro("Scale width", "ole_scale_width", String::new()),
|
||||||
ro(
|
ro("Scale height", "ole_scale_height", String::new()),
|
||||||
"Source App",
|
ro("Lock aspect", "ole_lock_aspect", String::new()),
|
||||||
"ole_source_app",
|
|
||||||
if ole.source_application.is_empty() {
|
|
||||||
"(unknown)".to_string()
|
|
||||||
} else {
|
|
||||||
ole.source_application.clone()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Data Size",
|
|
||||||
"ole_data_size",
|
|
||||||
format!("{} bytes", ole.binary_data.len()),
|
|
||||||
),
|
|
||||||
ro("DWG Mode", "ole_dwg_mode", ole.dwg_mode.to_string()),
|
|
||||||
ro(
|
|
||||||
"DWG Trailing Byte",
|
|
||||||
"ole_dwg_trailing",
|
|
||||||
format!("{:#04x}", ole.dwg_trailing_byte),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Paper Space",
|
|
||||||
"ole_paper_space",
|
|
||||||
if ole.is_paper_space { "Yes" } else { "No" },
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Type", "ole_type", type_str),
|
||||||
|
ro("Plot quality", "ole_plot_quality", String::new()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(ole: &mut Ole2Frame, field: &str, value: &str) {
|
fn apply_geom_prop(ole: &mut Ole2Frame, field: &str, value: &str) {
|
||||||
|
|
@ -124,6 +112,7 @@ fn apply_geom_prop(ole: &mut Ole2Frame, field: &str, value: &str) {
|
||||||
match field {
|
match field {
|
||||||
"ole_ulx" => ole.upper_left_corner.x = v,
|
"ole_ulx" => ole.upper_left_corner.x = v,
|
||||||
"ole_uly" => ole.upper_left_corner.y = v,
|
"ole_uly" => ole.upper_left_corner.y = v,
|
||||||
|
"ole_ulz" => ole.upper_left_corner.z = v,
|
||||||
"ole_lrx" => ole.lower_right_corner.x = v,
|
"ole_lrx" => ole.lower_right_corner.x = v,
|
||||||
"ole_lry" => ole.lower_right_corner.y = v,
|
"ole_lry" => ole.lower_right_corner.y = v,
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
||||||
|
|
@ -182,9 +182,9 @@ fn properties(pt: &Point) -> Vec<PropSection> {
|
||||||
vec![PropSection {
|
vec![PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
edit("X", "loc_x", pt.location.x),
|
edit("Position X", "loc_x", pt.location.x),
|
||||||
edit("Y", "loc_y", pt.location.y),
|
edit("Position Y", "loc_y", pt.location.y),
|
||||||
edit("Z", "loc_z", pt.location.z),
|
edit("Position Z", "loc_z", pt.location.z),
|
||||||
],
|
],
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use acadrust::entities::{Polyline, Polyline2D, Polyline3D};
|
||||||
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::{edit_prop as edit, 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::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
||||||
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
||||||
|
|
@ -387,29 +387,42 @@ impl Grippable for Polyline2D {
|
||||||
|
|
||||||
impl PropertyEditable for Polyline2D {
|
impl PropertyEditable for Polyline2D {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
let smooth = match self.smooth_surface {
|
let n = self.vertices.len();
|
||||||
acadrust::entities::SmoothSurfaceType::None => "None",
|
let mut area = 0.0;
|
||||||
acadrust::entities::SmoothSurfaceType::QuadraticBSpline => "Quadratic",
|
let mut length = 0.0;
|
||||||
acadrust::entities::SmoothSurfaceType::CubicBSpline => "Cubic",
|
let seg_count = if self.is_closed() { n } else { n.saturating_sub(1) };
|
||||||
acadrust::entities::SmoothSurfaceType::Bezier => "Bezier",
|
for i in 0..seg_count {
|
||||||
};
|
let a = &self.vertices[i].location;
|
||||||
vec![PropSection {
|
let b = &self.vertices[(i + 1) % n].location;
|
||||||
|
area += a.x * b.y - b.x * a.y;
|
||||||
|
length += ((b.x - a.x).powi(2) + (b.y - a.y).powi(2)).sqrt();
|
||||||
|
}
|
||||||
|
area = (area * 0.5).abs();
|
||||||
|
|
||||||
|
let v0 = self.vertices.first();
|
||||||
|
let vertex_x = v0.map(|v| format!("{:.4}", v.location.x)).unwrap_or_default();
|
||||||
|
let vertex_y = v0.map(|v| format!("{:.4}", v.location.y)).unwrap_or_default();
|
||||||
|
let seg_start_w = v0.map(|v| format!("{:.4}", v.start_width)).unwrap_or_default();
|
||||||
|
let seg_end_w = v0.map(|v| format!("{:.4}", v.end_width)).unwrap_or_default();
|
||||||
|
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Vertices", "vertices", self.vertices.len().to_string()),
|
ro("Current Vertex", "pl2_current_vertex", if n > 0 { "1" } else { "" }),
|
||||||
|
ro("Vertex X", "pl2_vertex_x", vertex_x),
|
||||||
|
ro("Vertex Y", "pl2_vertex_y", vertex_y),
|
||||||
|
ro("Start segment width", "pl2_seg_start_w", seg_start_w),
|
||||||
|
ro("End segment width", "pl2_seg_end_w", seg_end_w),
|
||||||
|
edit("Global width", "pl2_start_w", self.start_width),
|
||||||
edit("Elevation", "pl2_elevation", self.elevation),
|
edit("Elevation", "pl2_elevation", self.elevation),
|
||||||
edit("Default Start W", "pl2_start_w", self.start_width),
|
ro("Area", "pl2_area", format!("{area:.4}")),
|
||||||
edit("Default End W", "pl2_end_w", self.end_width),
|
ro("Length", "pl2_length", format!("{length:.4}")),
|
||||||
edit("Thickness", "pl2_thickness", self.thickness),
|
],
|
||||||
ro("Smooth Surface", "pl2_smooth", smooth),
|
},
|
||||||
ro(
|
PropSection {
|
||||||
"Normal",
|
title: "Misc".into(),
|
||||||
"pl2_normal",
|
props: vec![
|
||||||
format!(
|
|
||||||
"{:.3}, {:.3}, {:.3}",
|
|
||||||
self.normal.x, self.normal.y, self.normal.z
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Property {
|
Property {
|
||||||
label: "Closed".into(),
|
label: "Closed".into(),
|
||||||
field: "pl2_closed",
|
field: "pl2_closed",
|
||||||
|
|
@ -418,8 +431,14 @@ impl PropertyEditable for Polyline2D {
|
||||||
value: self.is_closed(),
|
value: self.is_closed(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ro(
|
||||||
|
"Linetype generation",
|
||||||
|
"pl2_ltype_gen",
|
||||||
|
if self.flags.bits() & 128 != 0 { "Enabled" } else { "Disabled" },
|
||||||
|
),
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||||
|
|
@ -437,29 +456,18 @@ impl PropertyEditable for Polyline2D {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"pl2_elevation" => {
|
"pl2_elevation" => {
|
||||||
if let Ok(v) = value.trim().parse::<f64>() {
|
if let Some(v) = parse_f64(value) {
|
||||||
self.elevation = v;
|
self.elevation = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"pl2_start_w" => {
|
"pl2_start_w" => {
|
||||||
if let Ok(v) = value.trim().parse::<f64>() {
|
if let Some(v) = parse_f64(value) {
|
||||||
if v >= 0.0 {
|
if v >= 0.0 {
|
||||||
self.start_width = v;
|
self.start_width = v;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"pl2_end_w" => {
|
|
||||||
if let Ok(v) = value.trim().parse::<f64>() {
|
|
||||||
if v >= 0.0 {
|
|
||||||
self.end_width = v;
|
self.end_width = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"pl2_thickness" => {
|
|
||||||
if let Ok(v) = value.trim().parse::<f64>() {
|
|
||||||
self.thickness = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -606,31 +614,31 @@ impl Grippable for Polyline3D {
|
||||||
impl PropertyEditable for Polyline3D {
|
impl PropertyEditable for Polyline3D {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
use acadrust::entities::polyline3d::SmoothSurfaceType as SST;
|
use acadrust::entities::polyline3d::SmoothSurfaceType as SST;
|
||||||
let smooth = match self.smooth_type {
|
let n = self.vertices.len();
|
||||||
|
let v0 = self.vertices.first();
|
||||||
|
let vertex_x = v0.map(|v| format!("{:.4}", v.position.x)).unwrap_or_default();
|
||||||
|
let vertex_y = v0.map(|v| format!("{:.4}", v.position.y)).unwrap_or_default();
|
||||||
|
let vertex_z = v0.map(|v| format!("{:.4}", v.position.z)).unwrap_or_default();
|
||||||
|
let fit_smooth = match self.smooth_type {
|
||||||
SST::None => "None",
|
SST::None => "None",
|
||||||
SST::QuadraticBSpline => "Quadratic",
|
SST::QuadraticBSpline => "Quadratic",
|
||||||
SST::CubicBSpline => "Cubic",
|
SST::CubicBSpline => "Cubic",
|
||||||
SST::Bezier => "Bezier",
|
SST::Bezier => "Bezier",
|
||||||
};
|
};
|
||||||
vec![PropSection {
|
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Vertices", "vertices", self.vertices.len().to_string()),
|
ro("Vertex", "pl3_vertex", if n > 0 { "1" } else { "" }),
|
||||||
edit("Default Start W", "pl3_start_w", self.default_start_width),
|
ro("Vertex X", "pl3_vertex_x", vertex_x),
|
||||||
edit("Default End W", "pl3_end_w", self.default_end_width),
|
ro("Vertex Y", "pl3_vertex_y", vertex_y),
|
||||||
ro("Smooth", "pl3_smooth", smooth),
|
ro("Vertex Z", "pl3_vertex_z", vertex_z),
|
||||||
ro("Mesh M", "pl3_mesh_m", self.mesh_m_count.to_string()),
|
],
|
||||||
ro("Mesh N", "pl3_mesh_n", self.mesh_n_count.to_string()),
|
},
|
||||||
ro(
|
PropSection {
|
||||||
"Smooth M Density",
|
title: "Misc".into(),
|
||||||
"pl3_smooth_m",
|
props: vec![
|
||||||
self.smooth_m_density.to_string(),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Smooth N Density",
|
|
||||||
"pl3_smooth_n",
|
|
||||||
self.smooth_n_density.to_string(),
|
|
||||||
),
|
|
||||||
Property {
|
Property {
|
||||||
label: "Closed".into(),
|
label: "Closed".into(),
|
||||||
field: "pl3_closed",
|
field: "pl3_closed",
|
||||||
|
|
@ -639,8 +647,10 @@ impl PropertyEditable for Polyline3D {
|
||||||
value: self.is_closed(),
|
value: self.is_closed(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ro("Fit/Smooth", "pl3_smooth", fit_smooth),
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||||
|
|
@ -655,14 +665,6 @@ impl PropertyEditable for Polyline3D {
|
||||||
} else {
|
} else {
|
||||||
self.open();
|
self.open();
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
if let Ok(v) = value.trim().parse::<f64>() {
|
|
||||||
match field {
|
|
||||||
"pl3_start_w" if v >= 0.0 => self.default_start_width = v,
|
|
||||||
"pl3_end_w" if v >= 0.0 => self.default_end_width = v,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,57 +163,101 @@ impl Grippable for RasterImage {
|
||||||
|
|
||||||
impl PropertyEditable for RasterImage {
|
impl PropertyEditable for RasterImage {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
vec![PropSection {
|
let rotation_deg = self.u_vector.y.atan2(self.u_vector.x).to_degrees();
|
||||||
|
let scale = self.u_vector.length();
|
||||||
|
let show_image = self.flags.contains(acadrust::entities::ImageDisplayFlags::SHOW_IMAGE);
|
||||||
|
let show_clipped = self
|
||||||
|
.flags
|
||||||
|
.contains(acadrust::entities::ImageDisplayFlags::USE_CLIPPING_BOUNDARY);
|
||||||
|
let clip_inverted = self.clip_boundary.clip_mode == acadrust::entities::ClipMode::Inside;
|
||||||
|
let transparency = format!("{:.0}%", self.common.transparency.as_percent() * 100.0);
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("File", "ri_file", self.file_path.clone()),
|
edit("Position X", "ri_ox", self.insertion_point.x),
|
||||||
edit("Insert X", "ri_ox", self.insertion_point.x),
|
edit("Position Y", "ri_oy", self.insertion_point.y),
|
||||||
edit("Insert Y", "ri_oy", self.insertion_point.y),
|
edit("Position Z", "ri_oz", self.insertion_point.z),
|
||||||
edit("Insert Z", "ri_oz", self.insertion_point.z),
|
ro("Rotation", "ri_rotation", format!("{:.4}", rotation_deg)),
|
||||||
|
ro("Width", "ri_width", format!("{:.4}", self.width())),
|
||||||
|
ro("Height", "ri_height", format!("{:.4}", self.height())),
|
||||||
|
ro("Scale", "ri_scale", format!("{:.4}", scale)),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Name", "ri_name", self.file_name().to_string()),
|
||||||
edit("Brightness", "ri_bright", self.brightness as f64),
|
edit("Brightness", "ri_bright", self.brightness as f64),
|
||||||
edit("Contrast", "ri_contrast", self.contrast as f64),
|
edit("Contrast", "ri_contrast", self.contrast as f64),
|
||||||
edit("Fade", "ri_fade", self.fade as f64),
|
edit("Fade", "ri_fade", self.fade as f64),
|
||||||
|
ro("Transparency", "ri_transparency", transparency),
|
||||||
Property {
|
Property {
|
||||||
label: "Clipping".into(),
|
label: "Show image".into(),
|
||||||
field: "ri_clip",
|
field: "ri_show_image",
|
||||||
value: PropValue::BoolToggle {
|
value: PropValue::BoolToggle {
|
||||||
field: "ri_clip",
|
field: "ri_show_image",
|
||||||
value: self.clipping_enabled,
|
value: show_image,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ro(
|
Property {
|
||||||
"Class Version",
|
label: "Show clipped".into(),
|
||||||
"ri_class_version",
|
field: "ri_show_clipped",
|
||||||
self.class_version.to_string(),
|
value: PropValue::BoolToggle {
|
||||||
),
|
field: "ri_show_clipped",
|
||||||
ro(
|
value: show_clipped,
|
||||||
"Definition",
|
},
|
||||||
"ri_def_handle",
|
},
|
||||||
match self.definition_handle {
|
Property {
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
label: "Clip inverted".into(),
|
||||||
_ => "(none)".to_string(),
|
field: "ri_clip_inverted",
|
||||||
|
value: PropValue::BoolToggle {
|
||||||
|
field: "ri_clip_inverted",
|
||||||
|
value: clip_inverted,
|
||||||
},
|
},
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Def Reactor",
|
|
||||||
"ri_def_reactor_handle",
|
|
||||||
match self.definition_reactor_handle {
|
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
|
||||||
_ => "(none)".to_string(),
|
|
||||||
},
|
},
|
||||||
),
|
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||||
match field {
|
match field {
|
||||||
"ri_clip" => {
|
"ri_show_image" => {
|
||||||
self.clipping_enabled = if value == "toggle" {
|
let on = if value == "toggle" {
|
||||||
!self.clipping_enabled
|
!self.flags.contains(acadrust::entities::ImageDisplayFlags::SHOW_IMAGE)
|
||||||
} else {
|
} else {
|
||||||
value == "true"
|
value == "true"
|
||||||
};
|
};
|
||||||
|
self.set_visible(on);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
"ri_show_clipped" => {
|
||||||
|
let on = if value == "toggle" {
|
||||||
|
!self
|
||||||
|
.flags
|
||||||
|
.contains(acadrust::entities::ImageDisplayFlags::USE_CLIPPING_BOUNDARY)
|
||||||
|
} else {
|
||||||
|
value == "true"
|
||||||
|
};
|
||||||
|
if on {
|
||||||
|
self.flags |= acadrust::entities::ImageDisplayFlags::USE_CLIPPING_BOUNDARY;
|
||||||
|
} else {
|
||||||
|
self.flags &= !acadrust::entities::ImageDisplayFlags::USE_CLIPPING_BOUNDARY;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
"ri_clip_inverted" => {
|
||||||
|
let on = if value == "toggle" {
|
||||||
|
self.clip_boundary.clip_mode != acadrust::entities::ClipMode::Inside
|
||||||
|
} else {
|
||||||
|
value == "true"
|
||||||
|
};
|
||||||
|
self.clip_boundary.clip_mode = if on {
|
||||||
|
acadrust::entities::ClipMode::Inside
|
||||||
|
} else {
|
||||||
|
acadrust::entities::ClipMode::Outside
|
||||||
|
};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -424,57 +468,91 @@ impl Grippable for Wipeout {
|
||||||
|
|
||||||
impl PropertyEditable for Wipeout {
|
impl PropertyEditable for Wipeout {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
vec![PropSection {
|
let show_image = self.flags.contains(acadrust::entities::WipeoutDisplayFlags::SHOW_IMAGE);
|
||||||
|
let show_clipped = self
|
||||||
|
.flags
|
||||||
|
.contains(acadrust::entities::WipeoutDisplayFlags::USE_CLIPPING_BOUNDARY);
|
||||||
|
let bg_transparency = self
|
||||||
|
.flags
|
||||||
|
.contains(acadrust::entities::WipeoutDisplayFlags::TRANSPARENCY_ON);
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
edit("Insert X", "wo_ox", self.insertion_point.x),
|
edit("Position X", "wo_ox", self.insertion_point.x),
|
||||||
edit("Insert Y", "wo_oy", self.insertion_point.y),
|
edit("Position Y", "wo_oy", self.insertion_point.y),
|
||||||
edit("Insert Z", "wo_oz", self.insertion_point.z),
|
edit("Position Z", "wo_oz", self.insertion_point.z),
|
||||||
edit("Brightness", "wo_bright", self.brightness as f64),
|
|
||||||
edit("Contrast", "wo_contrast", self.contrast as f64),
|
|
||||||
edit("Fade", "wo_fade", self.fade as f64),
|
|
||||||
Property {
|
|
||||||
label: "Clipping".into(),
|
|
||||||
field: "wo_clip",
|
|
||||||
value: PropValue::BoolToggle {
|
|
||||||
field: "wo_clip",
|
|
||||||
value: self.clipping_enabled,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ro(
|
|
||||||
"Class Version",
|
|
||||||
"wo_class_version",
|
|
||||||
self.class_version.to_string(),
|
|
||||||
),
|
|
||||||
ro("Clip Mode", "wo_clip_mode", format!("{:?}", self.clip_mode)),
|
|
||||||
ro(
|
|
||||||
"Definition",
|
|
||||||
"wo_def_handle",
|
|
||||||
match self.definition_handle {
|
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
|
||||||
_ => "(none)".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Def Reactor",
|
|
||||||
"wo_def_reactor_handle",
|
|
||||||
match self.definition_reactor_handle {
|
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
|
||||||
_ => "(none)".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
|
Property {
|
||||||
|
label: "Show image".into(),
|
||||||
|
field: "wo_show_image",
|
||||||
|
value: PropValue::BoolToggle {
|
||||||
|
field: "wo_show_image",
|
||||||
|
value: show_image,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Property {
|
||||||
|
label: "Show clipped".into(),
|
||||||
|
field: "wo_show_clipped",
|
||||||
|
value: PropValue::BoolToggle {
|
||||||
|
field: "wo_show_clipped",
|
||||||
|
value: show_clipped,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Property {
|
||||||
|
label: "Background transparency".into(),
|
||||||
|
field: "wo_bg_transparency",
|
||||||
|
value: PropValue::BoolToggle {
|
||||||
|
field: "wo_bg_transparency",
|
||||||
|
value: bg_transparency,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||||
match field {
|
match field {
|
||||||
"wo_clip" => {
|
"wo_show_image" => {
|
||||||
self.clipping_enabled = if value == "toggle" {
|
let on = if value == "toggle" {
|
||||||
!self.clipping_enabled
|
!self.flags.contains(acadrust::entities::WipeoutDisplayFlags::SHOW_IMAGE)
|
||||||
} else {
|
} else {
|
||||||
value == "true"
|
value == "true"
|
||||||
};
|
};
|
||||||
|
self.set_frame_visible(on);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
"wo_show_clipped" => {
|
||||||
|
let on = if value == "toggle" {
|
||||||
|
!self
|
||||||
|
.flags
|
||||||
|
.contains(acadrust::entities::WipeoutDisplayFlags::USE_CLIPPING_BOUNDARY)
|
||||||
|
} else {
|
||||||
|
value == "true"
|
||||||
|
};
|
||||||
|
if on {
|
||||||
|
self.flags |= acadrust::entities::WipeoutDisplayFlags::USE_CLIPPING_BOUNDARY;
|
||||||
|
} else {
|
||||||
|
self.flags -= acadrust::entities::WipeoutDisplayFlags::USE_CLIPPING_BOUNDARY;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
"wo_bg_transparency" => {
|
||||||
|
let on = if value == "toggle" {
|
||||||
|
!self.flags.contains(acadrust::entities::WipeoutDisplayFlags::TRANSPARENCY_ON)
|
||||||
|
} else {
|
||||||
|
value == "true"
|
||||||
|
};
|
||||||
|
if on {
|
||||||
|
self.flags |= acadrust::entities::WipeoutDisplayFlags::TRANSPARENCY_ON;
|
||||||
|
} else {
|
||||||
|
self.flags -= acadrust::entities::WipeoutDisplayFlags::TRANSPARENCY_ON;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -486,9 +564,6 @@ impl PropertyEditable for Wipeout {
|
||||||
"wo_ox" => self.insertion_point.x = v,
|
"wo_ox" => self.insertion_point.x = v,
|
||||||
"wo_oy" => self.insertion_point.y = v,
|
"wo_oy" => self.insertion_point.y = v,
|
||||||
"wo_oz" => self.insertion_point.z = v,
|
"wo_oz" => self.insertion_point.z = v,
|
||||||
"wo_bright" => self.brightness = v.clamp(0.0, 100.0) as u8,
|
|
||||||
"wo_contrast" => self.contrast = v.clamp(0.0, 100.0) as u8,
|
|
||||||
"wo_fade" => self.fade = v.clamp(0.0, 100.0) as u8,
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,12 +92,12 @@ impl PropertyEditable for Ray {
|
||||||
vec![PropSection {
|
vec![PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
edit("Base X", "ray_bx", self.base_point.x),
|
edit("Start X", "ray_bx", self.base_point.x),
|
||||||
edit("Base Y", "ray_by", self.base_point.y),
|
edit("Start Y", "ray_by", self.base_point.y),
|
||||||
edit("Base Z", "ray_bz", self.base_point.z),
|
edit("Start Z", "ray_bz", self.base_point.z),
|
||||||
edit("Dir X", "ray_dx", self.direction.x),
|
edit("Direction vector X", "ray_dx", self.direction.x),
|
||||||
edit("Dir Y", "ray_dy", self.direction.y),
|
edit("Direction vector Y", "ray_dy", self.direction.y),
|
||||||
edit("Dir Z", "ray_dz", self.direction.z),
|
edit("Direction vector Z", "ray_dz", self.direction.z),
|
||||||
],
|
],
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
@ -232,9 +232,9 @@ impl PropertyEditable for XLine {
|
||||||
edit("Base X", "xl_bx", self.base_point.x),
|
edit("Base X", "xl_bx", self.base_point.x),
|
||||||
edit("Base Y", "xl_by", self.base_point.y),
|
edit("Base Y", "xl_by", self.base_point.y),
|
||||||
edit("Base Z", "xl_bz", self.base_point.z),
|
edit("Base Z", "xl_bz", self.base_point.z),
|
||||||
edit("Dir X", "xl_dx", self.direction.x),
|
edit("Direction vector X", "xl_dx", self.direction.x),
|
||||||
edit("Dir Y", "xl_dy", self.direction.y),
|
edit("Direction vector Y", "xl_dy", self.direction.y),
|
||||||
edit("Dir Z", "xl_dz", self.direction.z),
|
edit("Direction vector Z", "xl_dz", self.direction.z),
|
||||||
],
|
],
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
use acadrust::entities::Solid;
|
use acadrust::entities::Solid;
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{edit_prop as edit, square_grip};
|
use crate::entities::common::{edit_prop as edit, ro_prop as ro, square_grip};
|
||||||
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
||||||
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
@ -98,21 +98,25 @@ impl Grippable for Solid {
|
||||||
|
|
||||||
impl PropertyEditable for Solid {
|
impl PropertyEditable for Solid {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
|
// Elevation is the OCS Z shared by the planar corners (no dedicated
|
||||||
|
// field on the entity); reported from the first corner's Z.
|
||||||
|
let elevation = self.first_corner.z;
|
||||||
vec![PropSection {
|
vec![PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
edit("P1 X", "sl_p1x", self.first_corner.x),
|
edit("Point 1 X", "sl_p1x", self.first_corner.x),
|
||||||
edit("P1 Y", "sl_p1y", self.first_corner.y),
|
edit("Point 1 Y", "sl_p1y", self.first_corner.y),
|
||||||
edit("P1 Z", "sl_p1z", self.first_corner.z),
|
edit("Point 1 Z", "sl_p1z", self.first_corner.z),
|
||||||
edit("P2 X", "sl_p2x", self.second_corner.x),
|
edit("Point 2 X", "sl_p2x", self.second_corner.x),
|
||||||
edit("P2 Y", "sl_p2y", self.second_corner.y),
|
edit("Point 2 Y", "sl_p2y", self.second_corner.y),
|
||||||
edit("P2 Z", "sl_p2z", self.second_corner.z),
|
edit("Point 2 Z", "sl_p2z", self.second_corner.z),
|
||||||
edit("P3 X", "sl_p3x", self.third_corner.x),
|
edit("Point 3 X", "sl_p3x", self.third_corner.x),
|
||||||
edit("P3 Y", "sl_p3y", self.third_corner.y),
|
edit("Point 3 Y", "sl_p3y", self.third_corner.y),
|
||||||
edit("P3 Z", "sl_p3z", self.third_corner.z),
|
edit("Point 3 Z", "sl_p3z", self.third_corner.z),
|
||||||
edit("P4 X", "sl_p4x", self.fourth_corner.x),
|
edit("Point 4 X", "sl_p4x", self.fourth_corner.x),
|
||||||
edit("P4 Y", "sl_p4y", self.fourth_corner.y),
|
edit("Point 4 Y", "sl_p4y", self.fourth_corner.y),
|
||||||
edit("P4 Z", "sl_p4z", self.fourth_corner.z),
|
edit("Point 4 Z", "sl_p4z", self.fourth_corner.z),
|
||||||
|
ro("Elevation", "sl_elev", format!("{:.4}", elevation)),
|
||||||
edit("Thickness", "sl_thick", self.thickness),
|
edit("Thickness", "sl_thick", self.thickness),
|
||||||
],
|
],
|
||||||
}]
|
}]
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use acadrust::entities::{Body, Region, Solid3D, Surface};
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{center_grip, 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, PropertyEditable, Transformable};
|
use crate::entities::traits::{Grippable, PropertyEditable, Transformable};
|
||||||
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
||||||
|
|
@ -52,17 +52,6 @@ fn translate_wires(wires: &mut Vec<acadrust::entities::Wire>, d: Vec3) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn acis_size_str(has_data: bool, sat_len: usize, sab_len: usize, is_binary: bool) -> String {
|
|
||||||
if !has_data {
|
|
||||||
return "none".to_string();
|
|
||||||
}
|
|
||||||
if is_binary {
|
|
||||||
format!("{} bytes (SAB)", sab_len)
|
|
||||||
} else {
|
|
||||||
format!("{} bytes (SAT)", sat_len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Solid3D ───────────────────────────────────────────────────────────────────
|
// ── Solid3D ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
impl Grippable for Solid3D {
|
impl Grippable for Solid3D {
|
||||||
|
|
@ -85,58 +74,40 @@ impl Grippable for Solid3D {
|
||||||
|
|
||||||
impl PropertyEditable for Solid3D {
|
impl PropertyEditable for Solid3D {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
let size = acis_size_str(
|
let history = match self.history_handle {
|
||||||
self.acis_data.has_data(),
|
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
||||||
self.acis_data.sat_data.len(),
|
_ => "None".to_string(),
|
||||||
self.acis_data.sab_data.len(),
|
};
|
||||||
self.acis_data.is_binary,
|
vec![
|
||||||
);
|
PropSection {
|
||||||
vec![PropSection {
|
title: "Solid History".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("History", "s3d_history", history),
|
||||||
|
ro("Show History", "s3d_show_history", String::new()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro(
|
edit("Position X", "s3d_px", self.point_of_reference.x),
|
||||||
"Ref Pt X",
|
edit("Position Y", "s3d_py", self.point_of_reference.y),
|
||||||
"s3d_px",
|
edit("Position Z", "s3d_pz", self.point_of_reference.z),
|
||||||
format!("{:.4}", self.point_of_reference.x),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Ref Pt Y",
|
|
||||||
"s3d_py",
|
|
||||||
format!("{:.4}", self.point_of_reference.y),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Ref Pt Z",
|
|
||||||
"s3d_pz",
|
|
||||||
format!("{:.4}", self.point_of_reference.z),
|
|
||||||
),
|
|
||||||
ro("ACIS Data", "s3d_acis", size),
|
|
||||||
ro(
|
|
||||||
"UID",
|
|
||||||
"s3d_uid",
|
|
||||||
if self.uid.is_empty() {
|
|
||||||
"(none)".to_string()
|
|
||||||
} else {
|
|
||||||
self.uid.clone()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Silhouettes",
|
|
||||||
"s3d_silhouettes",
|
|
||||||
self.silhouettes.len().to_string(),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"History",
|
|
||||||
"s3d_history",
|
|
||||||
match self.history_handle {
|
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
|
||||||
_ => "(none)".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, _field: &str, _value: &str) {}
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||||
|
let Some(v) = parse_f64(value) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
match field {
|
||||||
|
"s3d_px" => self.point_of_reference.x = v,
|
||||||
|
"s3d_py" => self.point_of_reference.y = v,
|
||||||
|
"s3d_pz" => self.point_of_reference.z = v,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Region ────────────────────────────────────────────────────────────────────
|
// ── Region ────────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -161,45 +132,11 @@ impl Grippable for Region {
|
||||||
|
|
||||||
impl PropertyEditable for Region {
|
impl PropertyEditable for Region {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
let size = acis_size_str(
|
|
||||||
self.acis_data.has_data(),
|
|
||||||
self.acis_data.sat_data.len(),
|
|
||||||
self.acis_data.sab_data.len(),
|
|
||||||
self.acis_data.is_binary,
|
|
||||||
);
|
|
||||||
vec![PropSection {
|
vec![PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro(
|
ro("Area", "rgn_area", String::new()),
|
||||||
"Ref Pt X",
|
ro("Perimeter", "rgn_perimeter", String::new()),
|
||||||
"rgn_px",
|
|
||||||
format!("{:.4}", self.point_of_reference.x),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Ref Pt Y",
|
|
||||||
"rgn_py",
|
|
||||||
format!("{:.4}", self.point_of_reference.y),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Ref Pt Z",
|
|
||||||
"rgn_pz",
|
|
||||||
format!("{:.4}", self.point_of_reference.z),
|
|
||||||
),
|
|
||||||
ro("ACIS Data", "rgn_acis", size),
|
|
||||||
ro(
|
|
||||||
"UID",
|
|
||||||
"rgn_uid",
|
|
||||||
if self.uid.is_empty() {
|
|
||||||
"(none)".to_string()
|
|
||||||
} else {
|
|
||||||
self.uid.clone()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Silhouettes",
|
|
||||||
"rgn_silhouettes",
|
|
||||||
self.silhouettes.len().to_string(),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
@ -229,45 +166,11 @@ impl Grippable for Body {
|
||||||
|
|
||||||
impl PropertyEditable for Body {
|
impl PropertyEditable for Body {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
let size = acis_size_str(
|
|
||||||
self.acis_data.has_data(),
|
|
||||||
self.acis_data.sat_data.len(),
|
|
||||||
self.acis_data.sab_data.len(),
|
|
||||||
self.acis_data.is_binary,
|
|
||||||
);
|
|
||||||
vec![PropSection {
|
vec![PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Solid History".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro(
|
ro("History", "bdy_history", String::new()),
|
||||||
"Ref Pt X",
|
ro("Show History", "bdy_show_history", String::new()),
|
||||||
"bdy_px",
|
|
||||||
format!("{:.4}", self.point_of_reference.x),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Ref Pt Y",
|
|
||||||
"bdy_py",
|
|
||||||
format!("{:.4}", self.point_of_reference.y),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Ref Pt Z",
|
|
||||||
"bdy_pz",
|
|
||||||
format!("{:.4}", self.point_of_reference.z),
|
|
||||||
),
|
|
||||||
ro("ACIS Data", "bdy_acis", size),
|
|
||||||
ro(
|
|
||||||
"UID",
|
|
||||||
"bdy_uid",
|
|
||||||
if self.uid.is_empty() {
|
|
||||||
"(none)".to_string()
|
|
||||||
} else {
|
|
||||||
self.uid.clone()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Silhouettes",
|
|
||||||
"bdy_silhouettes",
|
|
||||||
self.silhouettes.len().to_string(),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use truck_modeling::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{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::traits::TruckConvertible;
|
use crate::entities::traits::TruckConvertible;
|
||||||
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
||||||
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
@ -107,21 +107,110 @@ fn grips(spline: &Spline) -> Vec<GripDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn properties(spline: &Spline) -> Vec<PropSection> {
|
fn properties(spline: &Spline) -> Vec<PropSection> {
|
||||||
vec![PropSection {
|
let show = if spline.fit_points.is_empty() {
|
||||||
title: "Geometry".into(),
|
"Control Vertices"
|
||||||
|
} else {
|
||||||
|
"Fit Points"
|
||||||
|
};
|
||||||
|
let cp0 = spline.control_points.first();
|
||||||
|
let fp0 = spline.fit_points.first();
|
||||||
|
let w0 = spline.weights.first().copied();
|
||||||
|
|
||||||
|
// Polyline-approximation length over the effective defining points.
|
||||||
|
let pts = if spline.fit_points.is_empty() {
|
||||||
|
&spline.control_points
|
||||||
|
} else {
|
||||||
|
&spline.fit_points
|
||||||
|
};
|
||||||
|
let mut length = 0.0;
|
||||||
|
for w in pts.windows(2) {
|
||||||
|
let dx = w[1].x - w[0].x;
|
||||||
|
let dy = w[1].y - w[0].y;
|
||||||
|
let dz = w[1].z - w[0].z;
|
||||||
|
length += (dx * dx + dy * dy + dz * dz).sqrt();
|
||||||
|
}
|
||||||
|
|
||||||
|
let closed = spline.flags.closed || spline.flags.periodic;
|
||||||
|
let yes_no = |b: bool| if b { "Yes" } else { "No" };
|
||||||
|
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
|
title: "Data Points".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Degree", "degree", spline.degree.to_string()),
|
ro("Show", "show", show),
|
||||||
|
edit("Degree", "degree", spline.degree as f64),
|
||||||
ro(
|
ro(
|
||||||
"Control Pts",
|
"Control Point Count",
|
||||||
"ctrl_pts",
|
"ctrl_pt_count",
|
||||||
spline.control_points.len().to_string(),
|
spline.control_points.len().to_string(),
|
||||||
),
|
),
|
||||||
ro("Fit Pts", "fit_pts", spline.fit_points.len().to_string()),
|
ro("Control Point", "ctrl_pt_index", "0"),
|
||||||
|
ro(
|
||||||
|
"Control Point X",
|
||||||
|
"ctrl_pt_x",
|
||||||
|
format!("{:.4}", cp0.map(|p| p.x).unwrap_or(0.0)),
|
||||||
|
),
|
||||||
|
ro(
|
||||||
|
"Control Point Y",
|
||||||
|
"ctrl_pt_y",
|
||||||
|
format!("{:.4}", cp0.map(|p| p.y).unwrap_or(0.0)),
|
||||||
|
),
|
||||||
|
ro(
|
||||||
|
"Control Point Z",
|
||||||
|
"ctrl_pt_z",
|
||||||
|
format!("{:.4}", cp0.map(|p| p.z).unwrap_or(0.0)),
|
||||||
|
),
|
||||||
|
ro("Weight", "weight", format!("{:.4}", w0.unwrap_or(1.0))),
|
||||||
|
ro("Knot Parameterization", "knot_param", String::new()),
|
||||||
|
ro(
|
||||||
|
"Fit Point Count",
|
||||||
|
"fit_pt_count",
|
||||||
|
spline.fit_points.len().to_string(),
|
||||||
|
),
|
||||||
|
ro("Fit Point", "fit_pt_index", "0"),
|
||||||
|
ro(
|
||||||
|
"Fit Point X",
|
||||||
|
"fit_pt_x",
|
||||||
|
format!("{:.4}", fp0.map(|p| p.x).unwrap_or(0.0)),
|
||||||
|
),
|
||||||
|
ro(
|
||||||
|
"Fit Point Y",
|
||||||
|
"fit_pt_y",
|
||||||
|
format!("{:.4}", fp0.map(|p| p.y).unwrap_or(0.0)),
|
||||||
|
),
|
||||||
|
ro(
|
||||||
|
"Fit Point Z",
|
||||||
|
"fit_pt_z",
|
||||||
|
format!("{:.4}", fp0.map(|p| p.z).unwrap_or(0.0)),
|
||||||
|
),
|
||||||
|
ro("Fit Tolerance", "fit_tolerance", String::new()),
|
||||||
|
ro("Start Tangent X", "start_tan_x", String::new()),
|
||||||
|
ro("Start Tangent Y", "start_tan_y", String::new()),
|
||||||
|
ro("Start Tangent Z", "start_tan_z", String::new()),
|
||||||
|
ro("End Tangent X", "end_tan_x", String::new()),
|
||||||
|
ro("End Tangent Y", "end_tan_y", String::new()),
|
||||||
|
ro("End Tangent Z", "end_tan_z", String::new()),
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Closed", "closed", yes_no(closed)),
|
||||||
|
ro("Planar", "planar", yes_no(spline.flags.planar)),
|
||||||
|
ro("Length", "length", format!("{length:.4}")),
|
||||||
|
ro("Area", "area", String::new()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(_spline: &mut Spline, _field: &str, _value: &str) {}
|
fn apply_geom_prop(spline: &mut Spline, field: &str, value: &str) {
|
||||||
|
if field == "degree" {
|
||||||
|
if let Some(v) = parse_f64(value) {
|
||||||
|
spline.degree = v.round() as i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn apply_grip(spline: &mut Spline, grip_id: usize, apply: GripApply) {
|
fn apply_grip(spline: &mut Spline, grip_id: usize, apply: GripApply) {
|
||||||
if let Some(cp) = spline.control_points.get_mut(grip_id) {
|
if let Some(cp) = spline.control_points.get_mut(grip_id) {
|
||||||
|
|
|
||||||
|
|
@ -819,112 +819,106 @@ impl Grippable for Table {
|
||||||
|
|
||||||
impl PropertyEditable for Table {
|
impl PropertyEditable for Table {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
|
use crate::entities::common::edit_prop as edit;
|
||||||
|
use acadrust::entities::table::BreakOptionFlags;
|
||||||
|
|
||||||
let fmt_h = |oh: &Option<acadrust::types::Handle>| -> String {
|
let fmt_h = |oh: &Option<acadrust::types::Handle>| -> String {
|
||||||
match oh {
|
match oh {
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
||||||
_ => "(none)".to_string(),
|
_ => "(none)".to_string(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
vec![PropSection {
|
let yes_no = |b: bool| -> String {
|
||||||
|
if b { "Yes".into() } else { "No".into() }
|
||||||
|
};
|
||||||
|
// Direction = angle of the horizontal direction vector in the XY plane.
|
||||||
|
let direction_deg =
|
||||||
|
(self.horizontal_direction.y.atan2(self.horizontal_direction.x)).to_degrees();
|
||||||
|
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Table".into(),
|
title: "Table".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Rows", "tbl_rows", self.rows.len().to_string()),
|
|
||||||
ro("Columns", "tbl_cols", self.columns.len().to_string()),
|
|
||||||
ro(
|
ro(
|
||||||
"Insert X",
|
"Table style",
|
||||||
"tbl_ix",
|
|
||||||
format!("{:.4}", self.insertion_point.x),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Insert Y",
|
|
||||||
"tbl_iy",
|
|
||||||
format!("{:.4}", self.insertion_point.y),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Insert Z",
|
|
||||||
"tbl_iz",
|
|
||||||
format!("{:.4}", self.insertion_point.z),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Table Style",
|
|
||||||
"tbl_style_handle",
|
"tbl_style_handle",
|
||||||
fmt_h(&self.table_style_handle),
|
fmt_h(&self.table_style_handle),
|
||||||
),
|
),
|
||||||
|
ro("Rows", "tbl_rows", self.rows.len().to_string()),
|
||||||
|
ro("Columns", "tbl_cols", self.columns.len().to_string()),
|
||||||
|
ro("Direction", "tbl_direction", format!("{:.4}", direction_deg)),
|
||||||
ro(
|
ro(
|
||||||
"Block Record",
|
"Table width",
|
||||||
"tbl_block_rec_handle",
|
"tbl_width",
|
||||||
fmt_h(&self.block_record_handle),
|
format!("{:.4}", self.total_width()),
|
||||||
),
|
),
|
||||||
ro(
|
ro(
|
||||||
"Data Version",
|
"Table height",
|
||||||
"tbl_data_version",
|
"tbl_height",
|
||||||
self.data_version.to_string(),
|
format!("{:.4}", self.total_height()),
|
||||||
),
|
),
|
||||||
ro(
|
],
|
||||||
"Value Flags",
|
|
||||||
"tbl_value_flags",
|
|
||||||
format!("{:#010x}", self.value_flags),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Override Flag",
|
|
||||||
"tbl_override_flag",
|
|
||||||
if self.override_flag { "Yes" } else { "No" },
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Override Border Color",
|
|
||||||
"tbl_override_border_color",
|
|
||||||
if self.override_border_color {
|
|
||||||
"Yes"
|
|
||||||
} else {
|
|
||||||
"No"
|
|
||||||
},
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Table Breaks".into(),
|
||||||
|
props: vec![
|
||||||
|
ro(
|
||||||
|
"Enabled",
|
||||||
|
"tbl_break_enabled",
|
||||||
|
yes_no(self.break_options.contains(BreakOptionFlags::ENABLE_BREAKS)),
|
||||||
),
|
),
|
||||||
ro(
|
ro(
|
||||||
"Override Border LW",
|
"Direction",
|
||||||
"tbl_override_border_lw",
|
"tbl_break_direction",
|
||||||
if self.override_border_line_weight {
|
|
||||||
"Yes"
|
|
||||||
} else {
|
|
||||||
"No"
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Override Border Vis",
|
|
||||||
"tbl_override_border_vis",
|
|
||||||
if self.override_border_visibility {
|
|
||||||
"Yes"
|
|
||||||
} else {
|
|
||||||
"No"
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Break Spacing",
|
|
||||||
"tbl_break_spacing",
|
|
||||||
format!("{:.4}", self.break_spacing),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Break Flow",
|
|
||||||
"tbl_break_flow",
|
|
||||||
format!("{:?}", self.break_flow_direction),
|
format!("{:?}", self.break_flow_direction),
|
||||||
),
|
),
|
||||||
ro(
|
ro(
|
||||||
"Break Options",
|
"Repeat top labels",
|
||||||
"tbl_break_options",
|
"tbl_break_repeat_top",
|
||||||
format!("{:#018b}", self.break_options.bits()),
|
yes_no(
|
||||||
|
self.break_options
|
||||||
|
.contains(BreakOptionFlags::REPEAT_TOP_LABELS),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
ro(
|
ro(
|
||||||
"Normal",
|
"Repeat bottom labels",
|
||||||
"tbl_normal",
|
"tbl_break_repeat_bottom",
|
||||||
format!(
|
yes_no(
|
||||||
"{:.3}, {:.3}, {:.3}",
|
self.break_options
|
||||||
self.normal.x, self.normal.y, self.normal.z
|
.contains(BreakOptionFlags::REPEAT_BOTTOM_LABELS),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
ro(
|
||||||
|
"Manual positions",
|
||||||
|
"tbl_break_manual_positions",
|
||||||
|
yes_no(
|
||||||
|
self.break_options
|
||||||
|
.contains(BreakOptionFlags::ALLOW_MANUAL_POSITIONS),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ro(
|
||||||
|
"Manual heights",
|
||||||
|
"tbl_break_manual_heights",
|
||||||
|
yes_no(
|
||||||
|
self.break_options
|
||||||
|
.contains(BreakOptionFlags::ALLOW_MANUAL_HEIGHTS),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ro("Break height", "tbl_break_height", String::new()),
|
||||||
|
edit("Spacing", "tbl_break_spacing", self.break_spacing),
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, _field: &str, _value: &str) {}
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||||
|
use crate::entities::common::parse_f64;
|
||||||
|
if field == "tbl_break_spacing" {
|
||||||
|
if let Some(v) = parse_f64(value) {
|
||||||
|
self.break_spacing = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transformable for Table {
|
impl Transformable for Table {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use acadrust::entities::{Text, TextHorizontalAlignment as HA, TextVerticalAlignment as VA};
|
use acadrust::entities::{Text, TextHorizontalAlignment as HA, TextVerticalAlignment as VA};
|
||||||
|
|
||||||
use crate::command::EntityTransform;
|
use crate::command::EntityTransform;
|
||||||
use crate::entities::common::{edit_prop as edit, parse_f64, 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::{
|
||||||
resolve_dxf_special_chars, resolve_text_style, text_local_bounds,
|
resolve_dxf_special_chars, resolve_text_style, text_local_bounds,
|
||||||
};
|
};
|
||||||
|
|
@ -170,22 +170,34 @@ fn grips(t: &Text) -> Vec<GripDef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn properties(t: &Text, text_style_names: &[String]) -> Vec<PropSection> {
|
fn properties(t: &Text, text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
vec![PropSection {
|
// Text alignment point (second alignment / justify point). Falls back to
|
||||||
title: "Geometry".into(),
|
// the insertion point for Left/Baseline text where no second point exists.
|
||||||
|
let ap = t.alignment_point.unwrap_or(t.insertion_point);
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
|
title: "Text".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
edit("Insert X", "ins_x", t.insertion_point.x),
|
|
||||||
edit("Insert Y", "ins_y", t.insertion_point.y),
|
|
||||||
edit("Insert Z", "ins_z", t.insertion_point.z),
|
|
||||||
edit("Height", "height", t.height),
|
|
||||||
edit("Rotation", "rotation", t.rotation.to_degrees()),
|
|
||||||
edit("Width Factor", "width_factor", t.width_factor),
|
|
||||||
edit(
|
|
||||||
"Oblique Angle",
|
|
||||||
"oblique_angle",
|
|
||||||
t.oblique_angle.to_degrees(),
|
|
||||||
),
|
|
||||||
Property {
|
Property {
|
||||||
label: "H-Align".into(),
|
label: "Contents".into(),
|
||||||
|
field: "content",
|
||||||
|
value: PropValue::EditText(t.value.clone()),
|
||||||
|
},
|
||||||
|
Property {
|
||||||
|
label: "Style".into(),
|
||||||
|
field: "style",
|
||||||
|
value: PropValue::Choice {
|
||||||
|
selected: if t.style.trim().is_empty() {
|
||||||
|
"Standard".into()
|
||||||
|
} else {
|
||||||
|
t.style.clone()
|
||||||
|
},
|
||||||
|
options: text_style_names.to_vec(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ro("Annotative", "annotative", String::new()),
|
||||||
|
ro("Annotative scale", "annotative_scale", String::new()),
|
||||||
|
Property {
|
||||||
|
label: "Justify".into(),
|
||||||
field: "h_align",
|
field: "h_align",
|
||||||
value: PropValue::Choice {
|
value: PropValue::Choice {
|
||||||
selected: text_halign_str(&t.horizontal_alignment).to_string(),
|
selected: text_halign_str(&t.horizontal_alignment).to_string(),
|
||||||
|
|
@ -206,25 +218,47 @@ fn properties(t: &Text, text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Property {
|
edit("Height", "height", t.height),
|
||||||
label: "Content".into(),
|
edit("Rotation", "rotation", t.rotation.to_degrees()),
|
||||||
field: "content",
|
edit("Width factor", "width_factor", t.width_factor),
|
||||||
value: PropValue::EditText(t.value.clone()),
|
edit("Obliquing", "oblique_angle", t.oblique_angle.to_degrees()),
|
||||||
},
|
ro("Text alignment X", "align_x", format!("{:.4}", ap.x)),
|
||||||
Property {
|
ro("Text alignment Y", "align_y", format!("{:.4}", ap.y)),
|
||||||
label: "Style".into(),
|
ro("Text alignment Z", "align_z", format!("{:.4}", ap.z)),
|
||||||
field: "style",
|
|
||||||
value: PropValue::Choice {
|
|
||||||
selected: if t.style.trim().is_empty() {
|
|
||||||
"Standard".into()
|
|
||||||
} else {
|
|
||||||
t.style.clone()
|
|
||||||
},
|
|
||||||
options: text_style_names.to_vec(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Geometry".into(),
|
||||||
|
props: vec![
|
||||||
|
edit("Position X", "ins_x", t.insertion_point.x),
|
||||||
|
edit("Position Y", "ins_y", t.insertion_point.y),
|
||||||
|
edit("Position Z", "ins_z", t.insertion_point.z),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
|
ro(
|
||||||
|
"Upside down",
|
||||||
|
"upside_down",
|
||||||
|
if t.generation_flags & 0x4 != 0 {
|
||||||
|
"Yes"
|
||||||
|
} else {
|
||||||
|
"No"
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ro(
|
||||||
|
"Backward",
|
||||||
|
"backward",
|
||||||
|
if t.generation_flags & 0x2 != 0 {
|
||||||
|
"Yes"
|
||||||
|
} else {
|
||||||
|
"No"
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(t: &mut Text, field: &str, value: &str) {
|
fn apply_geom_prop(t: &mut Text, field: &str, value: &str) {
|
||||||
|
|
|
||||||
|
|
@ -294,15 +294,27 @@ impl Grippable for Tolerance {
|
||||||
|
|
||||||
impl PropertyEditable for Tolerance {
|
impl PropertyEditable for Tolerance {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
vec![PropSection {
|
vec![
|
||||||
|
PropSection {
|
||||||
|
title: "Text".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Text style", "tol_text_style", String::new()),
|
||||||
|
edit("Text height", "tol_text_height", self.text_height),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Text", "tol_text", self.text.clone()),
|
edit("Position X", "tol_ix", self.insertion_point.x),
|
||||||
edit("Insert X", "tol_ix", self.insertion_point.x),
|
edit("Position Y", "tol_iy", self.insertion_point.y),
|
||||||
edit("Insert Y", "tol_iy", self.insertion_point.y),
|
edit("Position Z", "tol_iz", self.insertion_point.z),
|
||||||
edit("Insert Z", "tol_iz", self.insertion_point.z),
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
ro(
|
ro(
|
||||||
"Dim Style",
|
"Dimension style",
|
||||||
"tol_dim_style",
|
"tol_dim_style",
|
||||||
if self.dimension_style_name.is_empty() {
|
if self.dimension_style_name.is_empty() {
|
||||||
"(default)".to_string()
|
"(default)".to_string()
|
||||||
|
|
@ -310,34 +322,12 @@ impl PropertyEditable for Tolerance {
|
||||||
self.dimension_style_name.clone()
|
self.dimension_style_name.clone()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ro(
|
edit("Direction X", "tol_dir_x", self.direction.x),
|
||||||
"Dim Style Handle",
|
edit("Direction Y", "tol_dir_y", self.direction.y),
|
||||||
"tol_dim_style_handle",
|
edit("Direction Z", "tol_dir_z", self.direction.z),
|
||||||
match self.dimension_style_handle {
|
|
||||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
|
||||||
_ => "(none)".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
edit("Text Height", "tol_text_height", self.text_height),
|
|
||||||
edit("Dim Gap", "tol_dim_gap", self.dimension_gap),
|
|
||||||
ro(
|
|
||||||
"Direction",
|
|
||||||
"tol_direction",
|
|
||||||
format!(
|
|
||||||
"{:.3}, {:.3}, {:.3}",
|
|
||||||
self.direction.x, self.direction.y, self.direction.z
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Normal",
|
|
||||||
"tol_normal",
|
|
||||||
format!(
|
|
||||||
"{:.3}, {:.3}, {:.3}",
|
|
||||||
self.normal.x, self.normal.y, self.normal.z
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||||
|
|
@ -349,7 +339,9 @@ impl PropertyEditable for Tolerance {
|
||||||
"tol_iy" => self.insertion_point.y = v,
|
"tol_iy" => self.insertion_point.y = v,
|
||||||
"tol_iz" => self.insertion_point.z = v,
|
"tol_iz" => self.insertion_point.z = v,
|
||||||
"tol_text_height" if v > 0.0 => self.text_height = v,
|
"tol_text_height" if v > 0.0 => self.text_height = v,
|
||||||
"tol_dim_gap" => self.dimension_gap = v,
|
"tol_dir_x" => self.direction.x = v,
|
||||||
|
"tol_dir_y" => self.direction.y = v,
|
||||||
|
"tol_dir_z" => self.direction.z = v,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -147,58 +147,74 @@ impl Grippable for Underlay {
|
||||||
|
|
||||||
impl PropertyEditable for Underlay {
|
impl PropertyEditable for Underlay {
|
||||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
let type_str = match self.underlay_type {
|
let yes_no = |b: bool| if b { "Yes" } else { "No" };
|
||||||
acadrust::entities::UnderlayType::Pdf => "PDF",
|
|
||||||
acadrust::entities::UnderlayType::Dwf => "DWF",
|
// Width / Height derived from the world-space clip boundary bounds
|
||||||
acadrust::entities::UnderlayType::Dgn => "DGN",
|
// (the only in-entity source of the placed footprint size).
|
||||||
|
let (width, height) = if self.clip_boundary_vertices.is_empty() {
|
||||||
|
(0.0_f64, 0.0_f64)
|
||||||
|
} else {
|
||||||
|
let verts = self.world_clip_boundary();
|
||||||
|
let mut min_x = f64::INFINITY;
|
||||||
|
let mut min_y = f64::INFINITY;
|
||||||
|
let mut max_x = f64::NEG_INFINITY;
|
||||||
|
let mut max_y = f64::NEG_INFINITY;
|
||||||
|
for v in &verts {
|
||||||
|
min_x = min_x.min(v.x);
|
||||||
|
min_y = min_y.min(v.y);
|
||||||
|
max_x = max_x.max(v.x);
|
||||||
|
max_y = max_y.max(v.y);
|
||||||
|
}
|
||||||
|
(max_x - min_x, max_y - min_y)
|
||||||
};
|
};
|
||||||
vec![PropSection {
|
|
||||||
|
let show = self.flags.contains(UnderlayDisplayFlags::ON);
|
||||||
|
let clipping = self.flags.contains(UnderlayDisplayFlags::CLIPPING);
|
||||||
|
let monochrome = self.flags.contains(UnderlayDisplayFlags::MONOCHROME);
|
||||||
|
let adjust_bg = self.flags.contains(UnderlayDisplayFlags::ADJUST_FOR_BACKGROUND);
|
||||||
|
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
ro("Type", "ul_type", type_str),
|
edit("Position X", "ul_ix", self.insertion_point.x),
|
||||||
edit("Ins X", "ul_ix", self.insertion_point.x),
|
edit("Position Y", "ul_iy", self.insertion_point.y),
|
||||||
edit("Ins Y", "ul_iy", self.insertion_point.y),
|
edit("Position Z", "ul_iz", self.insertion_point.z),
|
||||||
edit("Ins Z", "ul_iz", self.insertion_point.z),
|
edit("Scale X", "ul_sx", self.x_scale),
|
||||||
edit("X Scale", "ul_sx", self.x_scale),
|
edit("Scale Y", "ul_sy", self.y_scale),
|
||||||
edit("Y Scale", "ul_sy", self.y_scale),
|
edit("Scale Z", "ul_sz", self.z_scale),
|
||||||
edit("Z Scale", "ul_sz", self.z_scale),
|
ro("Width", "ul_width", format!("{:.4}", width)),
|
||||||
|
ro("Height", "ul_height", format!("{:.4}", height)),
|
||||||
edit("Rotation", "ul_rot", self.rotation.to_degrees()),
|
edit("Rotation", "ul_rot", self.rotation.to_degrees()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Underlay Adjust".into(),
|
||||||
|
props: vec![
|
||||||
edit("Contrast", "ul_contrast", self.contrast as f64),
|
edit("Contrast", "ul_contrast", self.contrast as f64),
|
||||||
edit("Fade", "ul_fade", self.fade as f64),
|
edit("Fade", "ul_fade", self.fade as f64),
|
||||||
|
ro("Monochrome", "ul_mono", yes_no(monochrome)),
|
||||||
ro(
|
ro(
|
||||||
"On",
|
"Adjust Colors for Background",
|
||||||
"ul_on",
|
"ul_adjust_bg",
|
||||||
if self.flags.contains(UnderlayDisplayFlags::ON) {
|
yes_no(adjust_bg),
|
||||||
"Yes"
|
|
||||||
} else {
|
|
||||||
"No"
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Clipping",
|
|
||||||
"ul_clip",
|
|
||||||
if self.flags.contains(UnderlayDisplayFlags::CLIPPING) {
|
|
||||||
"Yes"
|
|
||||||
} else {
|
|
||||||
"No"
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Monochrome",
|
|
||||||
"ul_mono",
|
|
||||||
if self.flags.contains(UnderlayDisplayFlags::MONOCHROME) {
|
|
||||||
"Yes"
|
|
||||||
} else {
|
|
||||||
"No"
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Clip Inverted",
|
|
||||||
"ul_clip_inverted",
|
|
||||||
if self.clip_inverted { "Yes" } else { "No" },
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
|
// Underlay name/path/layers live on the separate
|
||||||
|
// UnderlayDefinition object, not reachable from the entity.
|
||||||
|
ro("Underlay name", "ul_name", String::new()),
|
||||||
|
ro("Underlay path", "ul_path", String::new()),
|
||||||
|
ro("Show underlay", "ul_on", yes_no(show)),
|
||||||
|
ro("Clipping", "ul_clip", yes_no(clipping)),
|
||||||
|
ro("Show clipped", "ul_clip_inverted", yes_no(self.clip_inverted)),
|
||||||
|
ro("Underlay layers", "ul_layers", String::new()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||||
|
|
|
||||||
|
|
@ -122,70 +122,29 @@ fn properties(vp: &Viewport) -> Vec<PropSection> {
|
||||||
crate::scene::vp_effective_scale(vp.custom_scale, vp.view_height, vp.height);
|
crate::scene::vp_effective_scale(vp.custom_scale, 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 current_view = viewport_view_label(vp);
|
|
||||||
|
|
||||||
let render_opts: Vec<String> = RENDER_MODES.iter().map(|(s, _)| s.to_string()).collect();
|
|
||||||
let current_render = render_mode_label(&vp.render_mode).to_string();
|
|
||||||
|
|
||||||
let shade_opts: Vec<String> = SHADE_PLOT_LABELS.iter().map(|s| s.to_string()).collect();
|
let shade_opts: Vec<String> = SHADE_PLOT_LABELS.iter().map(|s| s.to_string()).collect();
|
||||||
let current_shade = shade_plot_label(vp.shade_plot_mode).to_string();
|
let current_shade = shade_plot_label(vp.shade_plot_mode).to_string();
|
||||||
|
|
||||||
vec![PropSection {
|
let annotation_scale = if effective_scale.abs() > 1e-9 {
|
||||||
|
format!("1:{}", scale_label(effective_scale))
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
props: vec![
|
props: vec![
|
||||||
edit("Center X", "center_x", vp.center.x),
|
edit("Center X", "center_x", vp.center.x),
|
||||||
edit("Center Y", "center_y", vp.center.y),
|
edit("Center Y", "center_y", vp.center.y),
|
||||||
edit("Center Z", "center_z", vp.center.z),
|
edit("Center Z", "center_z", vp.center.z),
|
||||||
edit("Width", "vp_w", vp.width),
|
|
||||||
edit("Height", "vp_h", vp.height),
|
edit("Height", "vp_h", vp.height),
|
||||||
// Numeric scale entry (derived from view_height; writes both).
|
edit("Width", "vp_w", vp.width),
|
||||||
edit("Scale (num)", "vscale", effective_scale),
|
],
|
||||||
// Standard scale picker.
|
|
||||||
Property {
|
|
||||||
label: "Scale".into(),
|
|
||||||
field: "vscale_std",
|
|
||||||
value: PropValue::Choice {
|
|
||||||
selected: current_scale_label,
|
|
||||||
options: scale_opts,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Standard view picker.
|
|
||||||
Property {
|
|
||||||
label: "View".into(),
|
|
||||||
field: "vp_view",
|
|
||||||
value: PropValue::Choice {
|
|
||||||
selected: current_view,
|
|
||||||
options: view_opts,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Render mode picker.
|
|
||||||
Property {
|
|
||||||
label: "Render Mode".into(),
|
|
||||||
field: "vp_render",
|
|
||||||
value: PropValue::Choice {
|
|
||||||
selected: current_render,
|
|
||||||
options: render_opts,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Shade plot mode picker.
|
|
||||||
Property {
|
|
||||||
label: "Shade Plot".into(),
|
|
||||||
field: "vp_shade_plot",
|
|
||||||
value: PropValue::Choice {
|
|
||||||
selected: current_shade,
|
|
||||||
options: shade_opts,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Display state toggles.
|
|
||||||
Property {
|
|
||||||
label: "Locked".into(),
|
|
||||||
field: "vp_locked",
|
|
||||||
value: PropValue::BoolToggle {
|
|
||||||
field: "vp_locked",
|
|
||||||
value: vp.status.locked,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
PropSection {
|
||||||
|
title: "Misc".into(),
|
||||||
|
props: vec![
|
||||||
Property {
|
Property {
|
||||||
label: "On".into(),
|
label: "On".into(),
|
||||||
field: "vp_on",
|
field: "vp_on",
|
||||||
|
|
@ -194,99 +153,44 @@ fn properties(vp: &Viewport) -> Vec<PropSection> {
|
||||||
value: vp.status.is_on,
|
value: vp.status.is_on,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ro("Clipped", "vp_clipped", String::new()),
|
||||||
Property {
|
Property {
|
||||||
label: "Perspective".into(),
|
label: "Display locked".into(),
|
||||||
field: "vp_perspective",
|
field: "vp_locked",
|
||||||
value: PropValue::BoolToggle {
|
value: PropValue::BoolToggle {
|
||||||
field: "vp_perspective",
|
field: "vp_locked",
|
||||||
value: vp.status.perspective,
|
value: vp.status.locked,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ro("Annotation scale", "vp_anno_scale", annotation_scale),
|
||||||
Property {
|
Property {
|
||||||
label: "Hide Plot".into(),
|
label: "Standard scale".into(),
|
||||||
field: "vp_hide_plot",
|
field: "vscale_std",
|
||||||
value: PropValue::BoolToggle {
|
value: PropValue::Choice {
|
||||||
field: "vp_hide_plot",
|
selected: current_scale_label,
|
||||||
value: vp.status.hide_plot,
|
options: scale_opts,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
edit("Custom scale", "vscale", effective_scale),
|
||||||
Property {
|
Property {
|
||||||
label: "UCS Icon".into(),
|
label: "UCS per viewport".into(),
|
||||||
field: "vp_ucs_icon",
|
|
||||||
value: PropValue::BoolToggle {
|
|
||||||
field: "vp_ucs_icon",
|
|
||||||
value: vp.ucs_icon_visible,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
edit("Target X", "vtgt_x", vp.view_target.x),
|
|
||||||
edit("Target Z", "vtgt_z", vp.view_target.z),
|
|
||||||
// Perspective lens length.
|
|
||||||
edit("Lens Length (mm)", "vp_lens", vp.lens_length),
|
|
||||||
// Per-Viewport UCS.
|
|
||||||
Property {
|
|
||||||
label: "UCS Per Viewport".into(),
|
|
||||||
field: "vp_ucs_per_vp",
|
field: "vp_ucs_per_vp",
|
||||||
value: PropValue::BoolToggle {
|
value: PropValue::BoolToggle {
|
||||||
field: "vp_ucs_per_vp",
|
field: "vp_ucs_per_vp",
|
||||||
value: vp.ucs_per_viewport,
|
value: vp.ucs_per_viewport,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
edit("UCS Origin X", "vp_ucs_ox", vp.ucs_origin.x),
|
|
||||||
edit("UCS Origin Y", "vp_ucs_oy", vp.ucs_origin.y),
|
|
||||||
edit("UCS Origin Z", "vp_ucs_oz", vp.ucs_origin.z),
|
|
||||||
edit("UCS X-Axis X", "vp_ucs_xx", vp.ucs_x_axis.x),
|
|
||||||
edit("UCS X-Axis Y", "vp_ucs_xy", vp.ucs_x_axis.y),
|
|
||||||
edit("UCS X-Axis Z", "vp_ucs_xz", vp.ucs_x_axis.z),
|
|
||||||
edit("UCS Y-Axis X", "vp_ucs_yx", vp.ucs_y_axis.x),
|
|
||||||
edit("UCS Y-Axis Y", "vp_ucs_yy", vp.ucs_y_axis.y),
|
|
||||||
edit("UCS Y-Axis Z", "vp_ucs_yz", vp.ucs_y_axis.z),
|
|
||||||
// Snap / grid metadata (drives the snap-aware UI; not rendered
|
|
||||||
// into the viewport contents directly).
|
|
||||||
edit("Snap Base X", "vp_snap_bx", vp.snap_base.x),
|
|
||||||
edit("Snap Base Y", "vp_snap_by", vp.snap_base.y),
|
|
||||||
edit("Snap Spacing X", "vp_snap_sx", vp.snap_spacing.x),
|
|
||||||
edit("Snap Spacing Y", "vp_snap_sy", vp.snap_spacing.y),
|
|
||||||
edit("Snap Angle", "vp_snap_ang", vp.snap_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("Back Clip Z", "vp_back_clip", vp.back_clip_z),
|
|
||||||
edit("Circle Sides", "vp_circle_sides", vp.circle_sides as f64),
|
|
||||||
ro(
|
ro(
|
||||||
"Grid Flags",
|
"Layer property overrides",
|
||||||
"vp_grid_flags",
|
"vp_layer_overrides",
|
||||||
format!("{:#06b}", vp.grid_flags.to_bits()),
|
if vp.frozen_layers.is_empty() {
|
||||||
),
|
"No".to_string()
|
||||||
ro("Grid Major", "vp_grid_major", vp.grid_major.to_string()),
|
|
||||||
ro(
|
|
||||||
"Base UCS",
|
|
||||||
"vp_base_ucs_handle",
|
|
||||||
if vp.base_ucs_handle.is_null() {
|
|
||||||
"(none)".to_string()
|
|
||||||
} else {
|
} else {
|
||||||
format!("{:X}", vp.base_ucs_handle.value())
|
format!("Yes ({})", vp.frozen_layers.len())
|
||||||
},
|
|
||||||
),
|
|
||||||
ro("UCS Ortho", "vp_ucs_ortho", vp.ucs_ortho_type.to_string()),
|
|
||||||
ro(
|
|
||||||
"Background",
|
|
||||||
"vp_background_handle",
|
|
||||||
if vp.background_handle.is_null() {
|
|
||||||
"(none)".to_string()
|
|
||||||
} else {
|
|
||||||
format!("{:X}", vp.background_handle.value())
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ro(
|
ro(
|
||||||
"Shade Plot",
|
"Visual style",
|
||||||
"vp_shade_plot_handle",
|
|
||||||
if vp.shade_plot_handle.is_null() {
|
|
||||||
"(none)".to_string()
|
|
||||||
} else {
|
|
||||||
format!("{:X}", vp.shade_plot_handle.value())
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Visual Style",
|
|
||||||
"vp_visual_style_handle",
|
"vp_visual_style_handle",
|
||||||
if vp.visual_style_handle.is_null() {
|
if vp.visual_style_handle.is_null() {
|
||||||
"(none)".to_string()
|
"(none)".to_string()
|
||||||
|
|
@ -294,24 +198,18 @@ fn properties(vp: &Viewport) -> Vec<PropSection> {
|
||||||
format!("{:X}", vp.visual_style_handle.value())
|
format!("{:X}", vp.visual_style_handle.value())
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// 3D lighting (not yet applied to the viewport mesh path).
|
Property {
|
||||||
ro(
|
label: "Shade plot".into(),
|
||||||
"Default Lighting",
|
field: "vp_shade_plot",
|
||||||
"vp_default_lighting",
|
value: PropValue::Choice {
|
||||||
if vp.default_lighting { "Yes" } else { "No" },
|
selected: current_shade,
|
||||||
),
|
options: shade_opts,
|
||||||
ro(
|
},
|
||||||
"Lighting Type",
|
},
|
||||||
"vp_lighting_type",
|
ro("Linked to Sheet View", "vp_sheet_view", String::new()),
|
||||||
vp.default_lighting_type.to_string(),
|
|
||||||
),
|
|
||||||
ro(
|
|
||||||
"Ambient Color",
|
|
||||||
"vp_ambient_color",
|
|
||||||
format!("{:#010x}", vp.ambient_color as u32),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Identify which standard view matches the viewport's view direction.
|
/// Identify which standard view matches the viewport's view direction.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue