forked from 1b-agent/opencadstudio-upstream
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:
1 parent
b2b2bfb246
commit
b9cebab951
26 files changed
+1917
-1558
No files matched your search
+47
-7
@@ -2,7 +2,9 @@ use acadrust::entities::Arc;
|
||||
use truck_modeling::{builder, Point3};
|
||||
|
||||
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::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
||||
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
||||
@@ -122,6 +124,31 @@ fn grips(arc: &Arc) -> Vec<GripDef> {
|
||||
}
|
||||
|
||||
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 {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
@@ -129,12 +156,21 @@ fn properties(arc: &Arc) -> Vec<PropSection> {
|
||||
edit("Center Y", "center_y", arc.center.y),
|
||||
edit("Center Z", "center_z", arc.center.z),
|
||||
edit("Radius", "radius", arc.radius),
|
||||
edit(
|
||||
"Start Angle (deg)",
|
||||
"start_angle",
|
||||
arc.start_angle.to_degrees(),
|
||||
),
|
||||
edit("End Angle (deg)", "end_angle", arc.end_angle.to_degrees()),
|
||||
edit("Start angle", "start_angle", sa.to_degrees()),
|
||||
edit("End angle", "end_angle", ea.to_degrees()),
|
||||
ro("Total angle", "total_angle", format!("{total_angle:.2}")),
|
||||
ro("Arc length", "arc_length", format!("{arc_length:.4}")),
|
||||
ro("Area", "area", format!("{area:.4}")),
|
||||
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,
|
||||
"start_angle" => arc.start_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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
+171
-149
@@ -385,26 +385,33 @@ impl Grippable for AttributeDefinition {
|
||||
|
||||
impl PropertyEditable for AttributeDefinition {
|
||||
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("Prompt", "att_prompt", self.prompt.clone()),
|
||||
Property {
|
||||
label: "Default".into(),
|
||||
label: "Value".into(),
|
||||
field: "att_default",
|
||||
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 {
|
||||
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",
|
||||
value: PropValue::Choice {
|
||||
selected: halign_str(self.horizontal_alignment).to_string(),
|
||||
@@ -425,63 +432,71 @@ impl PropertyEditable for AttributeDefinition {
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
Property {
|
||||
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(),
|
||||
},
|
||||
},
|
||||
edit("Height", "att_h", self.height),
|
||||
edit("Rotation", "att_rot", self.rotation.to_degrees()),
|
||||
edit("Width factor", "att_wf", self.width_factor),
|
||||
edit("Obliquing", "att_ob", self.oblique_angle.to_degrees()),
|
||||
edit("Text alignment X", "att_ax", self.alignment_point.x),
|
||||
edit("Text alignment Y", "att_ay", self.alignment_point.y),
|
||||
edit("Text alignment Z", "att_az", self.alignment_point.z),
|
||||
ro(
|
||||
"Generation",
|
||||
"att_gen_flags",
|
||||
format!("{:#06b}", self.text_generation_flags & 0xff),
|
||||
),
|
||||
ro(
|
||||
"Field Length",
|
||||
"Boundary width",
|
||||
"att_field_len",
|
||||
self.field_length.to_string(),
|
||||
),
|
||||
Property {
|
||||
label: "MText Mode".into(),
|
||||
field: "att_mtext_flag",
|
||||
value: PropValue::Choice {
|
||||
selected: mtext_flag_str(self.mtext_flag).to_string(),
|
||||
options: ["SingleLine", "MultiLine", "ConstantMultiLine"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.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),
|
||||
"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 Default field read-only.
|
||||
// by marking the Value field read-only.
|
||||
if self.flags.constant {
|
||||
if let Some(p) = props.iter_mut().find(|p| p.field == "att_default") {
|
||||
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: "Geometry".into(),
|
||||
props,
|
||||
}]
|
||||
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 {
|
||||
label: "Multiple lines".into(),
|
||||
field: "att_mtext_flag",
|
||||
value: PropValue::Choice {
|
||||
selected: mtext_flag_str(self.mtext_flag).to_string(),
|
||||
options: ["SingleLine", "MultiLine", "ConstantMultiLine"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||
@@ -626,101 +641,108 @@ impl Grippable for AttributeEntity {
|
||||
|
||||
impl PropertyEditable for AttributeEntity {
|
||||
fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> {
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Tag", "atte_tag", self.tag.clone()),
|
||||
Property {
|
||||
label: "Value".into(),
|
||||
field: "atte_val",
|
||||
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 {
|
||||
label: "H-Align".into(),
|
||||
field: "atte_halign",
|
||||
value: PropValue::Choice {
|
||||
selected: halign_str(self.horizontal_alignment).to_string(),
|
||||
options: ["Left", "Center", "Right", "Aligned", "Middle", "Fit"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
vec![
|
||||
PropSection {
|
||||
title: "Text".into(),
|
||||
props: vec![
|
||||
ro("Tag", "atte_tag", self.tag.clone()),
|
||||
ro("Prompt", "atte_prompt", String::new()),
|
||||
Property {
|
||||
label: "Value".into(),
|
||||
field: "atte_val",
|
||||
value: PropValue::EditText(self.value.clone()),
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "V-Align".into(),
|
||||
field: "atte_valign",
|
||||
value: PropValue::Choice {
|
||||
selected: valign_str(self.vertical_alignment).to_string(),
|
||||
options: ["Baseline", "Bottom", "Middle", "Top"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.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()
|
||||
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(),
|
||||
},
|
||||
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())
|
||||
Property {
|
||||
label: "Justify".into(),
|
||||
field: "atte_halign",
|
||||
value: PropValue::Choice {
|
||||
selected: halign_str(self.horizontal_alignment).to_string(),
|
||||
options: ["Left", "Center", "Right", "Aligned", "Middle", "Fit"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
),
|
||||
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(
|
||||
"Annotative",
|
||||
"atte_annotative",
|
||||
bool_yn(self.flags.annotative),
|
||||
),
|
||||
],
|
||||
}]
|
||||
Property {
|
||||
label: "V-Align".into(),
|
||||
field: "atte_valign",
|
||||
value: PropValue::Choice {
|
||||
selected: valign_str(self.vertical_alignment).to_string(),
|
||||
options: ["Baseline", "Bottom", "Middle", "Top"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
ro(
|
||||
"Annotative",
|
||||
"atte_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) {
|
||||
|
||||
+14
-7
@@ -119,23 +119,26 @@ fn grips(circle: &Circle) -> Vec<GripDef> {
|
||||
}
|
||||
|
||||
fn properties(circle: &Circle) -> Vec<PropSection> {
|
||||
use std::f64::consts::PI;
|
||||
let r = circle.radius;
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
edit("Center X", "center_x", circle.center.x),
|
||||
edit("Center Y", "center_y", circle.center.y),
|
||||
edit("Center Z", "center_z", circle.center.z),
|
||||
edit("Radius", "radius", circle.radius),
|
||||
ro(
|
||||
"Diameter",
|
||||
"diameter",
|
||||
format!("{:.4}", circle.radius * 2.0),
|
||||
),
|
||||
edit("Radius", "radius", r),
|
||||
ro("Diameter", "diameter", format!("{:.4}", r * 2.0)),
|
||||
ro(
|
||||
"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_z" => circle.center.z = 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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
+76
-25
@@ -2,7 +2,9 @@ use acadrust::entities::Ellipse;
|
||||
use truck_modeling::{builder, BSplineCurve, Curve, Edge, KnotVec, Point3, Wire};
|
||||
|
||||
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::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
||||
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
||||
@@ -159,32 +161,81 @@ fn grips(ell: &Ellipse) -> Vec<GripDef> {
|
||||
}
|
||||
|
||||
fn properties(ell: &Ellipse) -> Vec<PropSection> {
|
||||
let r_major = (ell.major_axis.x * ell.major_axis.x
|
||||
+ ell.major_axis.y * ell.major_axis.y
|
||||
+ ell.major_axis.z * ell.major_axis.z)
|
||||
.sqrt();
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
edit("Center X", "center_x", ell.center.x),
|
||||
edit("Center Y", "center_y", ell.center.y),
|
||||
edit("Center Z", "center_z", ell.center.z),
|
||||
ro("Major Radius", "major_r", format!("{r_major:.4}")),
|
||||
ro(
|
||||
"Minor Radius",
|
||||
"minor_r",
|
||||
format!("{:.4}", r_major * ell.minor_axis_ratio),
|
||||
),
|
||||
ro(
|
||||
"Minor/Major",
|
||||
"ratio",
|
||||
format!("{:.4}", ell.minor_axis_ratio),
|
||||
),
|
||||
],
|
||||
}]
|
||||
use crate::entities::traits::MassPropsCalc;
|
||||
|
||||
let r_major = ell.major_axis_length();
|
||||
let r_minor = ell.minor_axis_length();
|
||||
|
||||
// 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(),
|
||||
props: vec![
|
||||
edit("Center X", "center_x", ell.center.x),
|
||||
edit("Center Y", "center_y", ell.center.y),
|
||||
edit("Center Z", "center_z", ell.center.z),
|
||||
ro("End X", "end_x", format!("{:.4}", end.x)),
|
||||
ro("End Y", "end_y", format!("{:.4}", end.y)),
|
||||
ro("End Z", "end_z", format!("{:.4}", end.z)),
|
||||
ro("Major radius", "major_r", format!("{r_major:.4}")),
|
||||
ro("Minor radius", "minor_r", format!("{r_minor:.4}")),
|
||||
edit("Radius ratio", "ratio", ell.minor_axis_ratio),
|
||||
ro("Start angle", "start_angle", format!("{start_angle:.2}")),
|
||||
ro("End angle", "end_angle", format!("{end_angle:.2}")),
|
||||
edit("Start parameter", "start_param", ell.start_parameter),
|
||||
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) {
|
||||
match (grip_id, apply) {
|
||||
|
||||
+156
-54
@@ -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::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> {
|
||||
let pattern_type = match h.pattern_type {
|
||||
acadrust::entities::HatchPatternType::Predefined => "Predefined",
|
||||
@@ -19,61 +72,109 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
|
||||
acadrust::entities::HatchStyleType::Outer => "Outer",
|
||||
acadrust::entities::HatchStyleType::Ignore => "Ignore",
|
||||
};
|
||||
let fill_type = if h.gradient_color.enabled {
|
||||
format!("Gradient ({})", h.gradient_color.name)
|
||||
} else if h.is_solid {
|
||||
"Solid".into()
|
||||
} else {
|
||||
format!("Pattern ({})", h.pattern.name)
|
||||
};
|
||||
let boundary_count: usize = h
|
||||
.paths
|
||||
.iter()
|
||||
.map(|p| {
|
||||
p.edges
|
||||
.iter()
|
||||
.map(|e| match e {
|
||||
BoundaryEdge::Polyline(poly) => poly.vertices.len(),
|
||||
_ => 1,
|
||||
})
|
||||
.sum::<usize>()
|
||||
})
|
||||
.sum();
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Fill Type", "fill_type", fill_type),
|
||||
Property {
|
||||
label: "Pattern Name".into(),
|
||||
field: "pattern_name",
|
||||
value: PropValue::HatchPatternChoice(h.pattern.name.clone()),
|
||||
let area = boundary_area(h);
|
||||
let g = &h.gradient_color;
|
||||
let grad_c1 = g.colors.first().map(|e| color_str(e.color)).unwrap_or_default();
|
||||
let grad_c2 = g.colors.get(1).map(|e| color_str(e.color)).unwrap_or_default();
|
||||
|
||||
if g.enabled {
|
||||
// ── Gradient fill ──────────────────────────────────────────────────
|
||||
let grad_type = if g.is_single_color { "One color" } else { "Two color" };
|
||||
let centered = if g.shift.abs() < 1e-9 { "Yes" } else { "No" };
|
||||
return vec![
|
||||
PropSection {
|
||||
title: "Pattern".into(),
|
||||
props: vec![
|
||||
ro("Type", "fill_type", grad_type),
|
||||
ro("Gradient name", "gradient_name", g.name.clone()),
|
||||
ro("Color 1", "gradient_color_1", grad_c1),
|
||||
ro("Color 2", "gradient_color_2", grad_c2),
|
||||
edit("Angle", "pattern_angle", g.angle.to_degrees()),
|
||||
ro("Centered", "gradient_centered", centered),
|
||||
],
|
||||
},
|
||||
ro("Pattern Type", "pattern_type", pattern_type),
|
||||
edit(
|
||||
"Pattern Angle",
|
||||
"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(
|
||||
"Associative",
|
||||
"associative",
|
||||
if h.is_associative { "Yes" } else { "No" },
|
||||
),
|
||||
edit("Elevation", "elevation", h.elevation),
|
||||
ro("Seed Points", "seed_count", h.seed_points.len().to_string()),
|
||||
ro("Pixel Size", "pixel_size", format!("{:.6}", h.pixel_size)),
|
||||
ro(
|
||||
"Normal",
|
||||
"normal",
|
||||
format!("{:.3}, {:.3}, {:.3}", h.normal.x, h.normal.y, h.normal.z),
|
||||
),
|
||||
],
|
||||
}]
|
||||
PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
edit("Elevation", "elevation", h.elevation),
|
||||
ro("Origin X", "origin_x", String::new()),
|
||||
ro("Origin Y", "origin_y", String::new()),
|
||||
ro("Area", "area", format!("{:.4}", area)),
|
||||
ro("Cumulative area", "cumulative_area", format!("{:.4}", area)),
|
||||
],
|
||||
},
|
||||
PropSection {
|
||||
title: "Misc".into(),
|
||||
props: vec![
|
||||
ro(
|
||||
"Associative",
|
||||
"associative",
|
||||
if h.is_associative { "Yes" } else { "No" },
|
||||
),
|
||||
ro("Annotative", "annotative", String::new()),
|
||||
ro("Island detection style", "style", style),
|
||||
ro("Background color", "background_color", String::new()),
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// ── 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) {
|
||||
@@ -81,6 +182,7 @@ fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) {
|
||||
return;
|
||||
};
|
||||
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_scale" if v > 0.0 => h.pattern_scale = v,
|
||||
"elevation" => h.elevation = v,
|
||||
|
||||
+53
-14
@@ -6,7 +6,7 @@ use glam::Vec3;
|
||||
use crate::command::EntityTransform;
|
||||
use crate::entities::common::{edit_prop as edit, parse_f64, ro_prop as ro, square_grip};
|
||||
|
||||
use crate::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::cache::block_cache;
|
||||
use crate::scene::convert::tessellate;
|
||||
@@ -20,19 +20,58 @@ fn grips(ins: &Insert) -> Vec<GripDef> {
|
||||
}
|
||||
|
||||
fn properties(ins: &Insert) -> Vec<PropSection> {
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
edit("Insert X", "ins_x", ins.insert_point.x),
|
||||
edit("Insert Y", "ins_y", ins.insert_point.y),
|
||||
edit("Insert Z", "ins_z", ins.insert_point.z),
|
||||
edit("Scale X", "x_scale", ins.x_scale()),
|
||||
edit("Scale Y", "y_scale", ins.y_scale()),
|
||||
edit("Scale Z", "z_scale", ins.z_scale()),
|
||||
edit("Rotation", "rotation", ins.rotation.to_degrees()),
|
||||
ro("Block", "block", ins.block_name.clone()),
|
||||
],
|
||||
}]
|
||||
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(),
|
||||
props: vec![
|
||||
edit("Position X", "ins_x", ins.insert_point.x),
|
||||
edit("Position Y", "ins_y", ins.insert_point.y),
|
||||
edit("Position Z", "ins_z", ins.insert_point.z),
|
||||
edit("Scale X", "x_scale", ins.x_scale()),
|
||||
edit("Scale Y", "y_scale", ins.y_scale()),
|
||||
edit("Scale Z", "z_scale", ins.z_scale()),
|
||||
],
|
||||
},
|
||||
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) {
|
||||
|
||||
+74
-106
@@ -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> {
|
||||
let n = leader.vertices.len();
|
||||
let mut props = vec![
|
||||
// Style
|
||||
Property {
|
||||
label: "Dim Style".into(),
|
||||
field: "dimension_style",
|
||||
value: PropValue::EditText(leader.dimension_style.clone()),
|
||||
},
|
||||
// Path type
|
||||
|
||||
let arrow_size = leader.text_height.max(1.0);
|
||||
|
||||
let misc = vec![
|
||||
choice_prop(
|
||||
"Path Type",
|
||||
"Type",
|
||||
"path_type",
|
||||
path_type_str(&leader.path_type),
|
||||
&["Straight", "Spline"],
|
||||
),
|
||||
// Creation type
|
||||
choice_prop(
|
||||
"Creation Type",
|
||||
"Annotation type",
|
||||
"creation_type",
|
||||
creation_type_str(&leader.creation_type),
|
||||
&["With Text", "With Tolerance", "With Block", "No Annotation"],
|
||||
),
|
||||
// Arrow
|
||||
bool_toggle("Arrow", "arrow_enabled", leader.arrow_enabled),
|
||||
// Hookline
|
||||
bool_toggle("Hookline", "hookline_enabled", leader.hookline_enabled),
|
||||
choice_prop(
|
||||
"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),
|
||||
},
|
||||
),
|
||||
bool_toggle("Arrowhead", "arrow_enabled", leader.arrow_enabled),
|
||||
Property {
|
||||
label: "Dim style".into(),
|
||||
field: "dimension_style",
|
||||
value: PropValue::EditText(leader.dimension_style.clone()),
|
||||
},
|
||||
];
|
||||
|
||||
// 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() {
|
||||
props.push(edit("Arrow X", "arrow_x", a.x));
|
||||
props.push(edit("Arrow Y", "arrow_y", a.y));
|
||||
props.push(edit("Arrow Z", "arrow_z", a.z));
|
||||
geometry.push(edit("Vertex X", "vertex_x", a.x));
|
||||
geometry.push(edit("Vertex Y", "vertex_y", a.y));
|
||||
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)
|
||||
if n >= 2 {
|
||||
if let Some(e) = leader.end_point() {
|
||||
props.push(edit("End X", "end_x", e.x));
|
||||
props.push(edit("End Y", "end_y", e.y));
|
||||
props.push(edit("End Z", "end_z", e.z));
|
||||
}
|
||||
}
|
||||
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props,
|
||||
}]
|
||||
vec![
|
||||
PropSection {
|
||||
title: "Misc".into(),
|
||||
props: misc,
|
||||
},
|
||||
PropSection {
|
||||
title: "Lines & Arrows".into(),
|
||||
props: lines_arrows,
|
||||
},
|
||||
PropSection {
|
||||
title: "Text".into(),
|
||||
props: text,
|
||||
},
|
||||
PropSection {
|
||||
title: "Fit".into(),
|
||||
props: fit,
|
||||
},
|
||||
PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: geometry,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
"arrow_x" => {
|
||||
"vertex_x" => {
|
||||
if let (Some(v), Some(vert)) = (f64(value), leader.vertices.get_mut(0)) {
|
||||
vert.x = v;
|
||||
}
|
||||
}
|
||||
"arrow_y" => {
|
||||
"vertex_y" => {
|
||||
if let (Some(v), Some(vert)) = (f64(value), leader.vertices.get_mut(0)) {
|
||||
vert.y = v;
|
||||
}
|
||||
}
|
||||
"arrow_z" => {
|
||||
"vertex_z" => {
|
||||
if let (Some(v), Some(vert)) = (f64(value), leader.vertices.get_mut(0)) {
|
||||
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> {
|
||||
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 {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
@@ -82,7 +86,11 @@ fn properties(line: &Line) -> Vec<PropSection> {
|
||||
edit("End X", "end_x", line.end.x),
|
||||
edit("End Y", "end_y", line.end.y),
|
||||
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("Angle", "angle", format!("{angle:.2}")),
|
||||
],
|
||||
}]
|
||||
}
|
||||
|
||||
+58
-17
@@ -323,21 +323,40 @@ fn grips(pline: &LwPolyline) -> Vec<GripDef> {
|
||||
}
|
||||
|
||||
fn properties(pline: &LwPolyline) -> Vec<PropSection> {
|
||||
let (length, area) = length_and_area(pline);
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Vertices", "vertices", pline.vertices.len().to_string()),
|
||||
ro(
|
||||
"Closed",
|
||||
"closed",
|
||||
if pline.is_closed { "Yes" } else { "No" },
|
||||
),
|
||||
edit("Elevation", "elevation", pline.elevation),
|
||||
ro("Length", "length", format!("{length:.4}")),
|
||||
ro("Area", "area", format!("{area:.4}")),
|
||||
],
|
||||
}]
|
||||
let v0 = pline.vertices.first();
|
||||
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(),
|
||||
props: vec![
|
||||
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(
|
||||
"Closed",
|
||||
"closed",
|
||||
if pline.is_closed { "Yes" } else { "No" },
|
||||
),
|
||||
ro(
|
||||
"Linetype generation",
|
||||
"plinegen",
|
||||
if pline.plinegen { "Yes" } else { "No" },
|
||||
),
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
return;
|
||||
};
|
||||
if field == "elevation" {
|
||||
pline.elevation = v;
|
||||
match field {
|
||||
"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;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+119
-86
@@ -117,21 +117,28 @@ impl Grippable for Face3D {
|
||||
impl PropertyEditable for Face3D {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||
use crate::entities::common::edit_prop as edit;
|
||||
let inv = self.invisible_edges;
|
||||
let edge = |hidden: bool| if hidden { "Invisible" } else { "Visible" };
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
edit("P1 X", "f3_p1x", self.first_corner.x),
|
||||
edit("P1 Y", "f3_p1y", self.first_corner.y),
|
||||
edit("P1 Z", "f3_p1z", self.first_corner.z),
|
||||
edit("P2 X", "f3_p2x", self.second_corner.x),
|
||||
edit("P2 Y", "f3_p2y", self.second_corner.y),
|
||||
edit("P2 Z", "f3_p2z", self.second_corner.z),
|
||||
edit("P3 X", "f3_p3x", self.third_corner.x),
|
||||
edit("P3 Y", "f3_p3y", self.third_corner.y),
|
||||
edit("P3 Z", "f3_p3z", self.third_corner.z),
|
||||
edit("P4 X", "f3_p4x", self.fourth_corner.x),
|
||||
edit("P4 Y", "f3_p4y", self.fourth_corner.y),
|
||||
edit("P4 Z", "f3_p4z", self.fourth_corner.z),
|
||||
ro("Current vertex", "f3_current", String::new()),
|
||||
edit("Vertex 1 X", "f3_p1x", self.first_corner.x),
|
||||
edit("Vertex 1 Y", "f3_p1y", self.first_corner.y),
|
||||
edit("Vertex 1 Z", "f3_p1z", self.first_corner.z),
|
||||
edit("Vertex 2 X", "f3_p2x", self.second_corner.x),
|
||||
edit("Vertex 2 Y", "f3_p2y", self.second_corner.y),
|
||||
edit("Vertex 2 Z", "f3_p2z", self.second_corner.z),
|
||||
edit("Vertex 3 X", "f3_p3x", self.third_corner.x),
|
||||
edit("Vertex 3 Y", "f3_p3y", self.third_corner.y),
|
||||
edit("Vertex 3 Z", "f3_p3z", self.third_corner.z),
|
||||
edit("Vertex 4 X", "f3_p4x", self.fourth_corner.x),
|
||||
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::Bezier => "Bezier",
|
||||
};
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("M count", "pm_m", self.m_vertex_count.to_string()),
|
||||
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(
|
||||
"Smooth M Density",
|
||||
"pm_smooth_m",
|
||||
self.m_smooth_density.to_string(),
|
||||
),
|
||||
ro(
|
||||
"Smooth N Density",
|
||||
"pm_smooth_n",
|
||||
self.n_smooth_density.to_string(),
|
||||
),
|
||||
ro(
|
||||
"Elevation",
|
||||
"pm_elevation",
|
||||
format!("{:.4}", self.elevation),
|
||||
),
|
||||
ro(
|
||||
"Normal",
|
||||
"pm_normal",
|
||||
format!(
|
||||
"{:.3}, {:.3}, {:.3}",
|
||||
self.normal.x, self.normal.y, self.normal.z
|
||||
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(),
|
||||
props: vec![
|
||||
ro("Vertex", "pm_vertex", String::new()),
|
||||
ro(
|
||||
"Vertex X",
|
||||
"pm_vx",
|
||||
first.map(|v| format!("{:.4}", v.location.x)).unwrap_or_default(),
|
||||
),
|
||||
),
|
||||
],
|
||||
}]
|
||||
ro(
|
||||
"Vertex Y",
|
||||
"pm_vy",
|
||||
first.map(|v| format!("{:.4}", v.location.y)).unwrap_or_default(),
|
||||
),
|
||||
ro(
|
||||
"Vertex Z",
|
||||
"pm_vz",
|
||||
first.map(|v| format!("{:.4}", v.location.z)).unwrap_or_default(),
|
||||
),
|
||||
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) {}
|
||||
@@ -431,40 +449,44 @@ impl PropertyEditable for PolyfaceMesh {
|
||||
acadrust::entities::PolyfaceSmoothType::Cubic => "Cubic",
|
||||
acadrust::entities::PolyfaceSmoothType::Bezier => "Bezier",
|
||||
};
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Vertices", "pfm_v", self.vertices.len().to_string()),
|
||||
ro("Faces", "pfm_f", self.faces.len().to_string()),
|
||||
ro("Smooth Surface", "pfm_smooth", smooth),
|
||||
ro(
|
||||
"Seqend",
|
||||
"pfm_seqend_handle",
|
||||
match self.seqend_handle {
|
||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
||||
_ => "(none)".to_string(),
|
||||
},
|
||||
),
|
||||
ro(
|
||||
"Elevation",
|
||||
"pfm_elevation",
|
||||
format!("{:.4}", self.elevation),
|
||||
),
|
||||
ro(
|
||||
"Normal",
|
||||
"pfm_normal",
|
||||
format!(
|
||||
"{:.3}, {:.3}, {:.3}",
|
||||
self.normal.x, self.normal.y, self.normal.z
|
||||
let first = self.vertices.first();
|
||||
vec![
|
||||
PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Vertex", "pfm_vertex", String::new()),
|
||||
ro(
|
||||
"Vertex X",
|
||||
"pfm_vx",
|
||||
first.map(|v| format!("{:.4}", v.location.x)).unwrap_or_default(),
|
||||
),
|
||||
),
|
||||
ro(
|
||||
"Thickness",
|
||||
"pfm_thickness",
|
||||
format!("{:.4}", self.thickness),
|
||||
),
|
||||
],
|
||||
}]
|
||||
ro(
|
||||
"Vertex Y",
|
||||
"pfm_vy",
|
||||
first.map(|v| format!("{:.4}", v.location.y)).unwrap_or_default(),
|
||||
),
|
||||
ro(
|
||||
"Vertex Z",
|
||||
"pfm_vz",
|
||||
first.map(|v| format!("{:.4}", v.location.z)).unwrap_or_default(),
|
||||
),
|
||||
// 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) {}
|
||||
@@ -598,24 +620,35 @@ impl Grippable for Mesh {
|
||||
|
||||
impl PropertyEditable for Mesh {
|
||||
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 {
|
||||
title: "Geometry".into(),
|
||||
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(
|
||||
"Subdivision Level",
|
||||
"Level of Smoothness",
|
||||
"msh_subdiv",
|
||||
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(
|
||||
"Blend Crease",
|
||||
"msh_blend_crease",
|
||||
if self.blend_crease { "Yes" } else { "No" },
|
||||
"Watertight",
|
||||
"msh_watertight",
|
||||
if watertight { "Yes" } else { "No" },
|
||||
),
|
||||
],
|
||||
}]
|
||||
|
||||
+50
-41
@@ -194,46 +194,39 @@ impl PropertyEditable for MLine {
|
||||
acadrust::entities::MLineJustification::Zero => "Zero",
|
||||
acadrust::entities::MLineJustification::Bottom => "Bottom",
|
||||
};
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Style", "ml_style", self.style_name.clone()),
|
||||
ro(
|
||||
"Style Handle",
|
||||
"ml_style_handle",
|
||||
match self.style_handle {
|
||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
||||
_ => "(none)".to_string(),
|
||||
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(),
|
||||
props: vec![
|
||||
ro("Vertex", "ml_vertex", if self.vertices.is_empty() { String::new() } else { "1".to_string() }),
|
||||
edit("Vertex X", "ml_vertex_x", cur_x),
|
||||
edit("Vertex Y", "ml_vertex_y", cur_y),
|
||||
edit("Vertex Z", "ml_vertex_z", cur_z),
|
||||
],
|
||||
},
|
||||
PropSection {
|
||||
title: "Misc".into(),
|
||||
props: vec![
|
||||
ro("Style", "ml_style", self.style_name.clone()),
|
||||
Property {
|
||||
label: "Style justification".into(),
|
||||
field: "ml_justification",
|
||||
value: PropValue::Choice {
|
||||
selected: just_str.to_string(),
|
||||
options: ["Top", "Zero", "Bottom"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
),
|
||||
ro("Vertices", "ml_verts", self.vertices.len().to_string()),
|
||||
ro(
|
||||
"Style Elements",
|
||||
"ml_style_elem_count",
|
||||
self.style_element_count.to_string(),
|
||||
),
|
||||
edit("Scale", "ml_scale", self.scale_factor),
|
||||
Property {
|
||||
label: "Justification".into(),
|
||||
field: "ml_justification",
|
||||
value: PropValue::Choice {
|
||||
selected: just_str.to_string(),
|
||||
options: ["Top", "Zero", "Bottom"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "Closed".into(),
|
||||
field: "ml_closed",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "ml_closed",
|
||||
value: self.flags.contains(acadrust::entities::MLineFlags::CLOSED),
|
||||
},
|
||||
},
|
||||
],
|
||||
}]
|
||||
edit("Style scale", "ml_scale", self.scale_factor),
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
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 {
|
||||
return;
|
||||
};
|
||||
if field == "ml_scale" && v != 0.0 {
|
||||
self.scale_factor = v;
|
||||
match field {
|
||||
"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;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+105
-59
@@ -192,69 +192,115 @@ fn grips(t: &MText) -> Vec<GripDef> {
|
||||
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> {
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
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 {
|
||||
label: "H-Align".into(),
|
||||
field: "h_align",
|
||||
value: PropValue::Choice {
|
||||
selected: mtext_halign_str(&t.attachment_point).to_string(),
|
||||
options: ["Left", "Center", "Right"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
// Absolute line-space distance for single spacing (~1.66 * height) scaled
|
||||
// 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![
|
||||
Property {
|
||||
label: "Contents".into(),
|
||||
field: "content",
|
||||
value: PropValue::EditText(t.value.clone()),
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "V-Align".into(),
|
||||
field: "v_align",
|
||||
value: PropValue::Choice {
|
||||
selected: mtext_valign_str(&t.attachment_point).to_string(),
|
||||
options: ["Top", "Middle", "Bottom"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
ro(
|
||||
"Attachment",
|
||||
"attachment",
|
||||
attachment_str(&t.attachment_point).to_string(),
|
||||
),
|
||||
ro(
|
||||
"Direction",
|
||||
"direction",
|
||||
drawing_dir_str(&t.drawing_direction).to_string(),
|
||||
),
|
||||
Property {
|
||||
label: "Content".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()
|
||||
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(),
|
||||
},
|
||||
options: text_style_names.to_vec(),
|
||||
},
|
||||
},
|
||||
],
|
||||
}]
|
||||
ro(
|
||||
"Annotative",
|
||||
"annotative",
|
||||
if t.is_annotative { "Yes" } else { "No" },
|
||||
),
|
||||
Property {
|
||||
label: "Justify".into(),
|
||||
field: "h_align",
|
||||
value: PropValue::Choice {
|
||||
selected: mtext_halign_str(&t.attachment_point).to_string(),
|
||||
options: ["Left", "Center", "Right"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "V-Align".into(),
|
||||
field: "v_align",
|
||||
value: PropValue::Choice {
|
||||
selected: mtext_valign_str(&t.attachment_point).to_string(),
|
||||
options: ["Top", "Middle", "Bottom"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
ro(
|
||||
"Attachment",
|
||||
"attachment",
|
||||
attachment_str(&t.attachment_point).to_string(),
|
||||
),
|
||||
ro(
|
||||
"Direction",
|
||||
"direction",
|
||||
drawing_dir_str(&t.drawing_direction).to_string(),
|
||||
),
|
||||
edit("Text height", "height", t.height),
|
||||
edit("Rotation", "rotation", t.rotation.to_degrees()),
|
||||
edit("Line space factor", "line_spacing", t.line_spacing_factor),
|
||||
ro(
|
||||
"Line space distance",
|
||||
"line_space_distance",
|
||||
format!("{line_space_distance:.4}"),
|
||||
),
|
||||
ro("Line space style", "line_space_style", String::new()),
|
||||
ro(
|
||||
"Background mask",
|
||||
"background_mask",
|
||||
if bg_mask_on { "On" } else { "Off" },
|
||||
),
|
||||
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) {
|
||||
|
||||
+189
-192
@@ -520,204 +520,186 @@ 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> {
|
||||
let ctx = &ml.context;
|
||||
let total_pts: usize = ctx
|
||||
.leader_roots
|
||||
.iter()
|
||||
.flat_map(|r| r.lines.iter())
|
||||
.map(|l| l.points.len())
|
||||
.sum();
|
||||
let first_root = ctx.leader_roots.first();
|
||||
|
||||
let mut props = vec![
|
||||
// Content
|
||||
choice(
|
||||
"Content Type",
|
||||
"content_type",
|
||||
content_type_str(&ml.content_type),
|
||||
&["None", "MText", "Block", "Tolerance"],
|
||||
),
|
||||
Property {
|
||||
label: "Text".into(),
|
||||
field: "text_string",
|
||||
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",
|
||||
ml.enable_annotation_scale,
|
||||
),
|
||||
// Text attachment
|
||||
choice(
|
||||
"Left Attach",
|
||||
"text_left_attachment",
|
||||
attachment_str(&ml.text_left_attachment),
|
||||
&[
|
||||
"Top of Top",
|
||||
"Mid of Top",
|
||||
"Mid of Text",
|
||||
"Mid of Bot",
|
||||
"Bot of Bot",
|
||||
"Bottom Line",
|
||||
],
|
||||
),
|
||||
choice(
|
||||
"Right Attach",
|
||||
"text_right_attachment",
|
||||
attachment_str(&ml.text_right_attachment),
|
||||
&[
|
||||
"Top of Top",
|
||||
"Mid of Top",
|
||||
"Mid of Text",
|
||||
"Mid of Bot",
|
||||
"Bot of Bot",
|
||||
"Bottom Line",
|
||||
],
|
||||
),
|
||||
// Top / Bottom attach (used when text_attachment_direction = Vertical)
|
||||
choice(
|
||||
"Top Attach",
|
||||
"text_top_attachment",
|
||||
attachment_str(&ml.text_top_attachment),
|
||||
&[
|
||||
"Top of Top",
|
||||
"Mid of Top",
|
||||
"Mid of Text",
|
||||
"Mid of Bot",
|
||||
"Bot of Bot",
|
||||
"Bottom Line",
|
||||
],
|
||||
),
|
||||
choice(
|
||||
"Bottom Attach",
|
||||
"text_bottom_attachment",
|
||||
attachment_str(&ml.text_bottom_attachment),
|
||||
&[
|
||||
"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(
|
||||
"Style Handle",
|
||||
"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",
|
||||
match 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(
|
||||
"Text Direction Negative",
|
||||
"text_direction_negative",
|
||||
if ml.text_direction_negative {
|
||||
"Yes"
|
||||
} else {
|
||||
"No"
|
||||
},
|
||||
),
|
||||
ro(
|
||||
"Text Align In IPE",
|
||||
"text_align_in_ipe",
|
||||
ml.text_align_in_ipe.to_string(),
|
||||
),
|
||||
ro(
|
||||
"Property Override Flags",
|
||||
"property_override_flags",
|
||||
format!("{:#018b}", ml.property_override_flags.bits()),
|
||||
),
|
||||
ro(
|
||||
"Block Scale",
|
||||
"block_scale",
|
||||
format!(
|
||||
"{:.3} × {:.3} × {:.3}",
|
||||
ml.block_scale.x, ml.block_scale.y, ml.block_scale.z
|
||||
// ── Misc ─────────────────────────────────────────────────────────────
|
||||
let misc = PropSection {
|
||||
title: "Misc".into(),
|
||||
props: vec![
|
||||
ro("Leader type", "path_type", path_type_str(&ml.path_type)),
|
||||
choice(
|
||||
"Content type",
|
||||
"content_type",
|
||||
content_type_str(&ml.content_type),
|
||||
&["None", "MText", "Block", "Tolerance"],
|
||||
),
|
||||
),
|
||||
ro(
|
||||
"Block Rotation",
|
||||
"block_rotation",
|
||||
format!("{:.3}°", ml.block_rotation.to_degrees()),
|
||||
),
|
||||
];
|
||||
edit("Overall scale", "scale_factor", ml.scale_factor),
|
||||
ro(
|
||||
"Annotative",
|
||||
"enable_annotation_scale",
|
||||
if ml.enable_annotation_scale { "Yes" } else { "No" },
|
||||
),
|
||||
// No stored annotative-scale name on the entity.
|
||||
ro("Annotative scale", "annotative_scale", String::new()),
|
||||
],
|
||||
};
|
||||
|
||||
// Connection point for first root (most common case)
|
||||
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));
|
||||
}
|
||||
// ── 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),
|
||||
],
|
||||
};
|
||||
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props,
|
||||
}]
|
||||
// ── 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(
|
||||
"Left attachment type",
|
||||
"text_left_attachment",
|
||||
attachment_str(&ml.text_left_attachment),
|
||||
&[
|
||||
"Top of Top",
|
||||
"Mid of Top",
|
||||
"Mid of Text",
|
||||
"Mid of Bot",
|
||||
"Bot of Bot",
|
||||
"Bottom Line",
|
||||
],
|
||||
),
|
||||
choice(
|
||||
"Right attachment type",
|
||||
"text_right_attachment",
|
||||
attachment_str(&ml.text_right_attachment),
|
||||
&[
|
||||
"Top of Top",
|
||||
"Mid of Top",
|
||||
"Mid of Text",
|
||||
"Mid of Bot",
|
||||
"Bot of Bot",
|
||||
"Bottom Line",
|
||||
],
|
||||
),
|
||||
ro(
|
||||
"Text align type",
|
||||
"text_attachment_direction",
|
||||
format!("{:?}", ml.text_attachment_direction),
|
||||
),
|
||||
edit("Landing gap", "landing_gap", ctx.landing_gap),
|
||||
ro(
|
||||
"Background mask",
|
||||
"background_fill_enabled",
|
||||
if ctx.background_fill_enabled {
|
||||
"Yes"
|
||||
} else {
|
||||
"No"
|
||||
},
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
// ── Block ────────────────────────────────────────────────────────────
|
||||
let block = PropSection {
|
||||
title: "Block".into(),
|
||||
props: vec![
|
||||
ro(
|
||||
"Source block",
|
||||
"block_content_handle",
|
||||
hexh(ml.block_content_handle),
|
||||
),
|
||||
ro(
|
||||
"Block attachment",
|
||||
"block_connection_type",
|
||||
format!("{:?}", ml.block_connection_type),
|
||||
),
|
||||
ro(
|
||||
"Block color",
|
||||
"block_content_color",
|
||||
format!("{:?}", ml.block_content_color),
|
||||
),
|
||||
ro(
|
||||
"Block scale",
|
||||
"block_scale",
|
||||
format!(
|
||||
"{:.3} × {:.3} × {:.3}",
|
||||
ml.block_scale.x, ml.block_scale.y, ml.block_scale.z
|
||||
),
|
||||
),
|
||||
edit(
|
||||
"Block rotation",
|
||||
"block_rotation",
|
||||
ml.block_rotation.to_degrees(),
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
vec![misc, leaders, structure, text, block]
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
"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" => {
|
||||
if let (Some(v), Some(root)) = (f64(value), ml.context.leader_roots.first_mut()) {
|
||||
root.connection_point.x = v;
|
||||
|
||||
+25
-36
@@ -79,42 +79,30 @@ fn properties(ole: &Ole2Frame) -> Vec<PropSection> {
|
||||
OleObjectType::Embedded => "Embedded",
|
||||
OleObjectType::Static => "Static",
|
||||
};
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Type", "ole_type", type_str),
|
||||
edit("Upper Left X", "ole_ulx", ole.upper_left_corner.x),
|
||||
edit("Upper Left Y", "ole_uly", ole.upper_left_corner.y),
|
||||
edit("Lower Right X", "ole_lrx", ole.lower_right_corner.x),
|
||||
edit("Lower Right Y", "ole_lry", ole.lower_right_corner.y),
|
||||
ro("Version", "ole_version", ole.version.to_string()),
|
||||
ro(
|
||||
"Source App",
|
||||
"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" },
|
||||
),
|
||||
],
|
||||
}]
|
||||
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(),
|
||||
props: vec![
|
||||
edit("Position X", "ole_ulx", ole.upper_left_corner.x),
|
||||
edit("Position Y", "ole_uly", ole.upper_left_corner.y),
|
||||
edit("Position Z", "ole_ulz", ole.upper_left_corner.z),
|
||||
ro("Width", "ole_width", format!("{:.4}", width)),
|
||||
ro("Height", "ole_height", format!("{:.4}", height)),
|
||||
ro("Scale width", "ole_scale_width", String::new()),
|
||||
ro("Scale height", "ole_scale_height", String::new()),
|
||||
ro("Lock aspect", "ole_lock_aspect", String::new()),
|
||||
],
|
||||
},
|
||||
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) {
|
||||
@@ -124,6 +112,7 @@ fn apply_geom_prop(ole: &mut Ole2Frame, field: &str, value: &str) {
|
||||
match field {
|
||||
"ole_ulx" => ole.upper_left_corner.x = 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_lry" => ole.lower_right_corner.y = v,
|
||||
_ => {}
|
||||
|
||||
@@ -182,9 +182,9 @@ fn properties(pt: &Point) -> Vec<PropSection> {
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
edit("X", "loc_x", pt.location.x),
|
||||
edit("Y", "loc_y", pt.location.y),
|
||||
edit("Z", "loc_z", pt.location.z),
|
||||
edit("Position X", "loc_x", pt.location.x),
|
||||
edit("Position Y", "loc_y", pt.location.y),
|
||||
edit("Position Z", "loc_z", pt.location.z),
|
||||
],
|
||||
}]
|
||||
}
|
||||
|
||||
+83
-81
@@ -2,7 +2,7 @@ use acadrust::entities::{Polyline, Polyline2D, Polyline3D};
|
||||
use truck_modeling::{builder, Edge, Point3, Wire};
|
||||
|
||||
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::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
||||
use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
||||
@@ -387,39 +387,58 @@ impl Grippable for Polyline2D {
|
||||
|
||||
impl PropertyEditable for Polyline2D {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||
let smooth = match self.smooth_surface {
|
||||
acadrust::entities::SmoothSurfaceType::None => "None",
|
||||
acadrust::entities::SmoothSurfaceType::QuadraticBSpline => "Quadratic",
|
||||
acadrust::entities::SmoothSurfaceType::CubicBSpline => "Cubic",
|
||||
acadrust::entities::SmoothSurfaceType::Bezier => "Bezier",
|
||||
};
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Vertices", "vertices", self.vertices.len().to_string()),
|
||||
edit("Elevation", "pl2_elevation", self.elevation),
|
||||
edit("Default Start W", "pl2_start_w", self.start_width),
|
||||
edit("Default End W", "pl2_end_w", self.end_width),
|
||||
edit("Thickness", "pl2_thickness", self.thickness),
|
||||
ro("Smooth Surface", "pl2_smooth", smooth),
|
||||
ro(
|
||||
"Normal",
|
||||
"pl2_normal",
|
||||
format!(
|
||||
"{:.3}, {:.3}, {:.3}",
|
||||
self.normal.x, self.normal.y, self.normal.z
|
||||
),
|
||||
),
|
||||
Property {
|
||||
label: "Closed".into(),
|
||||
field: "pl2_closed",
|
||||
value: PropValue::BoolToggle {
|
||||
let n = self.vertices.len();
|
||||
let mut area = 0.0;
|
||||
let mut length = 0.0;
|
||||
let seg_count = if self.is_closed() { n } else { n.saturating_sub(1) };
|
||||
for i in 0..seg_count {
|
||||
let a = &self.vertices[i].location;
|
||||
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(),
|
||||
props: vec![
|
||||
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),
|
||||
ro("Area", "pl2_area", format!("{area:.4}")),
|
||||
ro("Length", "pl2_length", format!("{length:.4}")),
|
||||
],
|
||||
},
|
||||
PropSection {
|
||||
title: "Misc".into(),
|
||||
props: vec![
|
||||
Property {
|
||||
label: "Closed".into(),
|
||||
field: "pl2_closed",
|
||||
value: self.is_closed(),
|
||||
value: PropValue::BoolToggle {
|
||||
field: "pl2_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) {
|
||||
@@ -437,29 +456,18 @@ impl PropertyEditable for Polyline2D {
|
||||
}
|
||||
}
|
||||
"pl2_elevation" => {
|
||||
if let Ok(v) = value.trim().parse::<f64>() {
|
||||
if let Some(v) = parse_f64(value) {
|
||||
self.elevation = v;
|
||||
}
|
||||
}
|
||||
"pl2_start_w" => {
|
||||
if let Ok(v) = value.trim().parse::<f64>() {
|
||||
if let Some(v) = parse_f64(value) {
|
||||
if v >= 0.0 {
|
||||
self.start_width = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
"pl2_end_w" => {
|
||||
if let Ok(v) = value.trim().parse::<f64>() {
|
||||
if v >= 0.0 {
|
||||
self.end_width = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
"pl2_thickness" => {
|
||||
if let Ok(v) = value.trim().parse::<f64>() {
|
||||
self.thickness = v;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -606,41 +614,43 @@ impl Grippable for Polyline3D {
|
||||
impl PropertyEditable for Polyline3D {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||
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::QuadraticBSpline => "Quadratic",
|
||||
SST::CubicBSpline => "Cubic",
|
||||
SST::Bezier => "Bezier",
|
||||
};
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Vertices", "vertices", self.vertices.len().to_string()),
|
||||
edit("Default Start W", "pl3_start_w", self.default_start_width),
|
||||
edit("Default End W", "pl3_end_w", self.default_end_width),
|
||||
ro("Smooth", "pl3_smooth", smooth),
|
||||
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(
|
||||
"Smooth M Density",
|
||||
"pl3_smooth_m",
|
||||
self.smooth_m_density.to_string(),
|
||||
),
|
||||
ro(
|
||||
"Smooth N Density",
|
||||
"pl3_smooth_n",
|
||||
self.smooth_n_density.to_string(),
|
||||
),
|
||||
Property {
|
||||
label: "Closed".into(),
|
||||
field: "pl3_closed",
|
||||
value: PropValue::BoolToggle {
|
||||
|
||||
vec![
|
||||
PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Vertex", "pl3_vertex", if n > 0 { "1" } else { "" }),
|
||||
ro("Vertex X", "pl3_vertex_x", vertex_x),
|
||||
ro("Vertex Y", "pl3_vertex_y", vertex_y),
|
||||
ro("Vertex Z", "pl3_vertex_z", vertex_z),
|
||||
],
|
||||
},
|
||||
PropSection {
|
||||
title: "Misc".into(),
|
||||
props: vec![
|
||||
Property {
|
||||
label: "Closed".into(),
|
||||
field: "pl3_closed",
|
||||
value: self.is_closed(),
|
||||
value: PropValue::BoolToggle {
|
||||
field: "pl3_closed",
|
||||
value: self.is_closed(),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}]
|
||||
ro("Fit/Smooth", "pl3_smooth", fit_smooth),
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||
@@ -655,14 +665,6 @@ impl PropertyEditable for Polyline3D {
|
||||
} else {
|
||||
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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+160
-85
@@ -163,57 +163,101 @@ impl Grippable for RasterImage {
|
||||
|
||||
impl PropertyEditable for RasterImage {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("File", "ri_file", self.file_path.clone()),
|
||||
edit("Insert X", "ri_ox", self.insertion_point.x),
|
||||
edit("Insert Y", "ri_oy", self.insertion_point.y),
|
||||
edit("Insert Z", "ri_oz", self.insertion_point.z),
|
||||
edit("Brightness", "ri_bright", self.brightness as f64),
|
||||
edit("Contrast", "ri_contrast", self.contrast as f64),
|
||||
edit("Fade", "ri_fade", self.fade as f64),
|
||||
Property {
|
||||
label: "Clipping".into(),
|
||||
field: "ri_clip",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "ri_clip",
|
||||
value: self.clipping_enabled,
|
||||
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(),
|
||||
props: vec![
|
||||
edit("Position X", "ri_ox", self.insertion_point.x),
|
||||
edit("Position Y", "ri_oy", self.insertion_point.y),
|
||||
edit("Position 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("Contrast", "ri_contrast", self.contrast as f64),
|
||||
edit("Fade", "ri_fade", self.fade as f64),
|
||||
ro("Transparency", "ri_transparency", transparency),
|
||||
Property {
|
||||
label: "Show image".into(),
|
||||
field: "ri_show_image",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "ri_show_image",
|
||||
value: show_image,
|
||||
},
|
||||
},
|
||||
},
|
||||
ro(
|
||||
"Class Version",
|
||||
"ri_class_version",
|
||||
self.class_version.to_string(),
|
||||
),
|
||||
ro(
|
||||
"Definition",
|
||||
"ri_def_handle",
|
||||
match self.definition_handle {
|
||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
||||
_ => "(none)".to_string(),
|
||||
Property {
|
||||
label: "Show clipped".into(),
|
||||
field: "ri_show_clipped",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "ri_show_clipped",
|
||||
value: show_clipped,
|
||||
},
|
||||
},
|
||||
),
|
||||
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(),
|
||||
Property {
|
||||
label: "Clip inverted".into(),
|
||||
field: "ri_clip_inverted",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "ri_clip_inverted",
|
||||
value: clip_inverted,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
}]
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||
match field {
|
||||
"ri_clip" => {
|
||||
self.clipping_enabled = if value == "toggle" {
|
||||
!self.clipping_enabled
|
||||
"ri_show_image" => {
|
||||
let on = if value == "toggle" {
|
||||
!self.flags.contains(acadrust::entities::ImageDisplayFlags::SHOW_IMAGE)
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
_ => {}
|
||||
@@ -424,57 +468,91 @@ impl Grippable for Wipeout {
|
||||
|
||||
impl PropertyEditable for Wipeout {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
edit("Insert X", "wo_ox", self.insertion_point.x),
|
||||
edit("Insert Y", "wo_oy", self.insertion_point.y),
|
||||
edit("Insert 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,
|
||||
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(),
|
||||
props: vec![
|
||||
edit("Position X", "wo_ox", self.insertion_point.x),
|
||||
edit("Position Y", "wo_oy", self.insertion_point.y),
|
||||
edit("Position Z", "wo_oz", self.insertion_point.z),
|
||||
],
|
||||
},
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
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(),
|
||||
Property {
|
||||
label: "Show clipped".into(),
|
||||
field: "wo_show_clipped",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "wo_show_clipped",
|
||||
value: show_clipped,
|
||||
},
|
||||
},
|
||||
),
|
||||
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(),
|
||||
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) {
|
||||
match field {
|
||||
"wo_clip" => {
|
||||
self.clipping_enabled = if value == "toggle" {
|
||||
!self.clipping_enabled
|
||||
"wo_show_image" => {
|
||||
let on = if value == "toggle" {
|
||||
!self.flags.contains(acadrust::entities::WipeoutDisplayFlags::SHOW_IMAGE)
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
_ => {}
|
||||
@@ -486,9 +564,6 @@ impl PropertyEditable for Wipeout {
|
||||
"wo_ox" => self.insertion_point.x = v,
|
||||
"wo_oy" => self.insertion_point.y = 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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-9
@@ -92,12 +92,12 @@ impl PropertyEditable for Ray {
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
edit("Base X", "ray_bx", self.base_point.x),
|
||||
edit("Base Y", "ray_by", self.base_point.y),
|
||||
edit("Base Z", "ray_bz", self.base_point.z),
|
||||
edit("Dir X", "ray_dx", self.direction.x),
|
||||
edit("Dir Y", "ray_dy", self.direction.y),
|
||||
edit("Dir Z", "ray_dz", self.direction.z),
|
||||
edit("Start X", "ray_bx", self.base_point.x),
|
||||
edit("Start Y", "ray_by", self.base_point.y),
|
||||
edit("Start Z", "ray_bz", self.base_point.z),
|
||||
edit("Direction vector X", "ray_dx", self.direction.x),
|
||||
edit("Direction vector Y", "ray_dy", self.direction.y),
|
||||
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 Y", "xl_by", self.base_point.y),
|
||||
edit("Base Z", "xl_bz", self.base_point.z),
|
||||
edit("Dir X", "xl_dx", self.direction.x),
|
||||
edit("Dir Y", "xl_dy", self.direction.y),
|
||||
edit("Dir Z", "xl_dz", self.direction.z),
|
||||
edit("Direction vector X", "xl_dx", self.direction.x),
|
||||
edit("Direction vector Y", "xl_dy", self.direction.y),
|
||||
edit("Direction vector Z", "xl_dz", self.direction.z),
|
||||
],
|
||||
}]
|
||||
}
|
||||
|
||||
+17
-13
@@ -7,7 +7,7 @@
|
||||
use acadrust::entities::Solid;
|
||||
|
||||
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::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
||||
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
||||
@@ -98,21 +98,25 @@ impl Grippable for Solid {
|
||||
|
||||
impl PropertyEditable for Solid {
|
||||
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 {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
edit("P1 X", "sl_p1x", self.first_corner.x),
|
||||
edit("P1 Y", "sl_p1y", self.first_corner.y),
|
||||
edit("P1 Z", "sl_p1z", self.first_corner.z),
|
||||
edit("P2 X", "sl_p2x", self.second_corner.x),
|
||||
edit("P2 Y", "sl_p2y", self.second_corner.y),
|
||||
edit("P2 Z", "sl_p2z", self.second_corner.z),
|
||||
edit("P3 X", "sl_p3x", self.third_corner.x),
|
||||
edit("P3 Y", "sl_p3y", self.third_corner.y),
|
||||
edit("P3 Z", "sl_p3z", self.third_corner.z),
|
||||
edit("P4 X", "sl_p4x", self.fourth_corner.x),
|
||||
edit("P4 Y", "sl_p4y", self.fourth_corner.y),
|
||||
edit("P4 Z", "sl_p4z", self.fourth_corner.z),
|
||||
edit("Point 1 X", "sl_p1x", self.first_corner.x),
|
||||
edit("Point 1 Y", "sl_p1y", self.first_corner.y),
|
||||
edit("Point 1 Z", "sl_p1z", self.first_corner.z),
|
||||
edit("Point 2 X", "sl_p2x", self.second_corner.x),
|
||||
edit("Point 2 Y", "sl_p2y", self.second_corner.y),
|
||||
edit("Point 2 Z", "sl_p2z", self.second_corner.z),
|
||||
edit("Point 3 X", "sl_p3x", self.third_corner.x),
|
||||
edit("Point 3 Y", "sl_p3y", self.third_corner.y),
|
||||
edit("Point 3 Z", "sl_p3z", self.third_corner.z),
|
||||
edit("Point 4 X", "sl_p4x", self.fourth_corner.x),
|
||||
edit("Point 4 Y", "sl_p4y", self.fourth_corner.y),
|
||||
edit("Point 4 Z", "sl_p4z", self.fourth_corner.z),
|
||||
ro("Elevation", "sl_elev", format!("{:.4}", elevation)),
|
||||
edit("Thickness", "sl_thick", self.thickness),
|
||||
],
|
||||
}]
|
||||
|
||||
+38
-135
@@ -10,7 +10,7 @@ use acadrust::entities::{Body, Region, Solid3D, Surface};
|
||||
use glam::Vec3;
|
||||
|
||||
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::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 ───────────────────────────────────────────────────────────────────
|
||||
|
||||
impl Grippable for Solid3D {
|
||||
@@ -85,58 +74,40 @@ impl Grippable for Solid3D {
|
||||
|
||||
impl PropertyEditable for Solid3D {
|
||||
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 {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro(
|
||||
"Ref Pt X",
|
||||
"s3d_px",
|
||||
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(),
|
||||
},
|
||||
),
|
||||
],
|
||||
}]
|
||||
let history = match self.history_handle {
|
||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
||||
_ => "None".to_string(),
|
||||
};
|
||||
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(),
|
||||
props: vec![
|
||||
edit("Position X", "s3d_px", self.point_of_reference.x),
|
||||
edit("Position Y", "s3d_py", self.point_of_reference.y),
|
||||
edit("Position Z", "s3d_pz", self.point_of_reference.z),
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
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 ────────────────────────────────────────────────────────────────────
|
||||
@@ -161,45 +132,11 @@ impl Grippable for Region {
|
||||
|
||||
impl PropertyEditable for Region {
|
||||
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 {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro(
|
||||
"Ref Pt X",
|
||||
"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(),
|
||||
),
|
||||
ro("Area", "rgn_area", String::new()),
|
||||
ro("Perimeter", "rgn_perimeter", String::new()),
|
||||
],
|
||||
}]
|
||||
}
|
||||
@@ -229,45 +166,11 @@ impl Grippable for Body {
|
||||
|
||||
impl PropertyEditable for Body {
|
||||
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 {
|
||||
title: "Geometry".into(),
|
||||
title: "Solid History".into(),
|
||||
props: vec![
|
||||
ro(
|
||||
"Ref Pt X",
|
||||
"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(),
|
||||
),
|
||||
ro("History", "bdy_history", String::new()),
|
||||
ro("Show History", "bdy_show_history", String::new()),
|
||||
],
|
||||
}]
|
||||
}
|
||||
|
||||
+103
-14
@@ -5,7 +5,7 @@ use truck_modeling::{
|
||||
};
|
||||
|
||||
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::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
||||
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
||||
@@ -107,21 +107,110 @@ fn grips(spline: &Spline) -> Vec<GripDef> {
|
||||
}
|
||||
|
||||
fn properties(spline: &Spline) -> Vec<PropSection> {
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Degree", "degree", spline.degree.to_string()),
|
||||
ro(
|
||||
"Control Pts",
|
||||
"ctrl_pts",
|
||||
spline.control_points.len().to_string(),
|
||||
),
|
||||
ro("Fit Pts", "fit_pts", spline.fit_points.len().to_string()),
|
||||
],
|
||||
}]
|
||||
let show = if spline.fit_points.is_empty() {
|
||||
"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![
|
||||
ro("Show", "show", show),
|
||||
edit("Degree", "degree", spline.degree as f64),
|
||||
ro(
|
||||
"Control Point Count",
|
||||
"ctrl_pt_count",
|
||||
spline.control_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) {
|
||||
if let Some(cp) = spline.control_points.get_mut(grip_id) {
|
||||
|
||||
+91
-97
@@ -819,112 +819,106 @@ impl Grippable for Table {
|
||||
|
||||
impl PropertyEditable for Table {
|
||||
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 {
|
||||
match oh {
|
||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
||||
_ => "(none)".to_string(),
|
||||
}
|
||||
};
|
||||
vec![PropSection {
|
||||
title: "Table".into(),
|
||||
props: vec![
|
||||
ro("Rows", "tbl_rows", self.rows.len().to_string()),
|
||||
ro("Columns", "tbl_cols", self.columns.len().to_string()),
|
||||
ro(
|
||||
"Insert X",
|
||||
"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",
|
||||
fmt_h(&self.table_style_handle),
|
||||
),
|
||||
ro(
|
||||
"Block Record",
|
||||
"tbl_block_rec_handle",
|
||||
fmt_h(&self.block_record_handle),
|
||||
),
|
||||
ro(
|
||||
"Data Version",
|
||||
"tbl_data_version",
|
||||
self.data_version.to_string(),
|
||||
),
|
||||
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"
|
||||
},
|
||||
),
|
||||
ro(
|
||||
"Override Border LW",
|
||||
"tbl_override_border_lw",
|
||||
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),
|
||||
),
|
||||
ro(
|
||||
"Break Options",
|
||||
"tbl_break_options",
|
||||
format!("{:#018b}", self.break_options.bits()),
|
||||
),
|
||||
ro(
|
||||
"Normal",
|
||||
"tbl_normal",
|
||||
format!(
|
||||
"{:.3}, {:.3}, {:.3}",
|
||||
self.normal.x, self.normal.y, self.normal.z
|
||||
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(),
|
||||
props: vec![
|
||||
ro(
|
||||
"Table style",
|
||||
"tbl_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(
|
||||
"Table width",
|
||||
"tbl_width",
|
||||
format!("{:.4}", self.total_width()),
|
||||
),
|
||||
ro(
|
||||
"Table height",
|
||||
"tbl_height",
|
||||
format!("{:.4}", self.total_height()),
|
||||
),
|
||||
],
|
||||
},
|
||||
PropSection {
|
||||
title: "Table Breaks".into(),
|
||||
props: vec![
|
||||
ro(
|
||||
"Enabled",
|
||||
"tbl_break_enabled",
|
||||
yes_no(self.break_options.contains(BreakOptionFlags::ENABLE_BREAKS)),
|
||||
),
|
||||
ro(
|
||||
"Direction",
|
||||
"tbl_break_direction",
|
||||
format!("{:?}", self.break_flow_direction),
|
||||
),
|
||||
ro(
|
||||
"Repeat top labels",
|
||||
"tbl_break_repeat_top",
|
||||
yes_no(
|
||||
self.break_options
|
||||
.contains(BreakOptionFlags::REPEAT_TOP_LABELS),
|
||||
),
|
||||
),
|
||||
ro(
|
||||
"Repeat bottom labels",
|
||||
"tbl_break_repeat_bottom",
|
||||
yes_no(
|
||||
self.break_options
|
||||
.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 {
|
||||
|
||||
+87
-53
@@ -1,7 +1,7 @@
|
||||
use acadrust::entities::{Text, TextHorizontalAlignment as HA, TextVerticalAlignment as VA};
|
||||
|
||||
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::{
|
||||
resolve_dxf_special_chars, resolve_text_style, text_local_bounds,
|
||||
};
|
||||
@@ -170,61 +170,95 @@ fn grips(t: &Text) -> Vec<GripDef> {
|
||||
}
|
||||
|
||||
fn properties(t: &Text, text_style_names: &[String]) -> Vec<PropSection> {
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
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 {
|
||||
label: "H-Align".into(),
|
||||
field: "h_align",
|
||||
value: PropValue::Choice {
|
||||
selected: text_halign_str(&t.horizontal_alignment).to_string(),
|
||||
options: ["Left", "Center", "Right", "Aligned", "Middle", "Fit"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
// Text alignment point (second alignment / justify point). Falls back to
|
||||
// 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![
|
||||
Property {
|
||||
label: "Contents".into(),
|
||||
field: "content",
|
||||
value: PropValue::EditText(t.value.clone()),
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "V-Align".into(),
|
||||
field: "v_align",
|
||||
value: PropValue::Choice {
|
||||
selected: text_valign_str(&t.vertical_alignment).to_string(),
|
||||
options: ["Baseline", "Bottom", "Middle", "Top"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "Content".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()
|
||||
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(),
|
||||
},
|
||||
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",
|
||||
value: PropValue::Choice {
|
||||
selected: text_halign_str(&t.horizontal_alignment).to_string(),
|
||||
options: ["Left", "Center", "Right", "Aligned", "Middle", "Fit"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "V-Align".into(),
|
||||
field: "v_align",
|
||||
value: PropValue::Choice {
|
||||
selected: text_valign_str(&t.vertical_alignment).to_string(),
|
||||
options: ["Baseline", "Bottom", "Middle", "Top"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
edit("Height", "height", t.height),
|
||||
edit("Rotation", "rotation", t.rotation.to_degrees()),
|
||||
edit("Width factor", "width_factor", t.width_factor),
|
||||
edit("Obliquing", "oblique_angle", t.oblique_angle.to_degrees()),
|
||||
ro("Text alignment X", "align_x", format!("{:.4}", ap.x)),
|
||||
ro("Text alignment Y", "align_y", format!("{:.4}", ap.y)),
|
||||
ro("Text alignment Z", "align_z", format!("{:.4}", ap.z)),
|
||||
],
|
||||
},
|
||||
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) {
|
||||
|
||||
+36
-44
@@ -294,50 +294,40 @@ impl Grippable for Tolerance {
|
||||
|
||||
impl PropertyEditable for Tolerance {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Text", "tol_text", self.text.clone()),
|
||||
edit("Insert X", "tol_ix", self.insertion_point.x),
|
||||
edit("Insert Y", "tol_iy", self.insertion_point.y),
|
||||
edit("Insert Z", "tol_iz", self.insertion_point.z),
|
||||
ro(
|
||||
"Dim Style",
|
||||
"tol_dim_style",
|
||||
if self.dimension_style_name.is_empty() {
|
||||
"(default)".to_string()
|
||||
} else {
|
||||
self.dimension_style_name.clone()
|
||||
},
|
||||
),
|
||||
ro(
|
||||
"Dim Style Handle",
|
||||
"tol_dim_style_handle",
|
||||
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
|
||||
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(),
|
||||
props: vec![
|
||||
edit("Position X", "tol_ix", self.insertion_point.x),
|
||||
edit("Position Y", "tol_iy", self.insertion_point.y),
|
||||
edit("Position Z", "tol_iz", self.insertion_point.z),
|
||||
],
|
||||
},
|
||||
PropSection {
|
||||
title: "Misc".into(),
|
||||
props: vec![
|
||||
ro(
|
||||
"Dimension style",
|
||||
"tol_dim_style",
|
||||
if self.dimension_style_name.is_empty() {
|
||||
"(default)".to_string()
|
||||
} else {
|
||||
self.dimension_style_name.clone()
|
||||
},
|
||||
),
|
||||
),
|
||||
ro(
|
||||
"Normal",
|
||||
"tol_normal",
|
||||
format!(
|
||||
"{:.3}, {:.3}, {:.3}",
|
||||
self.normal.x, self.normal.y, self.normal.z
|
||||
),
|
||||
),
|
||||
],
|
||||
}]
|
||||
edit("Direction X", "tol_dir_x", self.direction.x),
|
||||
edit("Direction Y", "tol_dir_y", self.direction.y),
|
||||
edit("Direction Z", "tol_dir_z", self.direction.z),
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
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_iz" => self.insertion_point.z = 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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
+67
-51
@@ -147,58 +147,74 @@ impl Grippable for Underlay {
|
||||
|
||||
impl PropertyEditable for Underlay {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||
let type_str = match self.underlay_type {
|
||||
acadrust::entities::UnderlayType::Pdf => "PDF",
|
||||
acadrust::entities::UnderlayType::Dwf => "DWF",
|
||||
acadrust::entities::UnderlayType::Dgn => "DGN",
|
||||
let yes_no = |b: bool| if b { "Yes" } else { "No" };
|
||||
|
||||
// Width / Height derived from the world-space clip boundary bounds
|
||||
// (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 {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
ro("Type", "ul_type", type_str),
|
||||
edit("Ins X", "ul_ix", self.insertion_point.x),
|
||||
edit("Ins Y", "ul_iy", self.insertion_point.y),
|
||||
edit("Ins Z", "ul_iz", self.insertion_point.z),
|
||||
edit("X Scale", "ul_sx", self.x_scale),
|
||||
edit("Y Scale", "ul_sy", self.y_scale),
|
||||
edit("Z Scale", "ul_sz", self.z_scale),
|
||||
edit("Rotation", "ul_rot", self.rotation.to_degrees()),
|
||||
edit("Contrast", "ul_contrast", self.contrast as f64),
|
||||
edit("Fade", "ul_fade", self.fade as f64),
|
||||
ro(
|
||||
"On",
|
||||
"ul_on",
|
||||
if self.flags.contains(UnderlayDisplayFlags::ON) {
|
||||
"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" },
|
||||
),
|
||||
],
|
||||
}]
|
||||
|
||||
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(),
|
||||
props: vec![
|
||||
edit("Position X", "ul_ix", self.insertion_point.x),
|
||||
edit("Position Y", "ul_iy", self.insertion_point.y),
|
||||
edit("Position Z", "ul_iz", self.insertion_point.z),
|
||||
edit("Scale X", "ul_sx", self.x_scale),
|
||||
edit("Scale Y", "ul_sy", self.y_scale),
|
||||
edit("Scale Z", "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()),
|
||||
],
|
||||
},
|
||||
PropSection {
|
||||
title: "Underlay Adjust".into(),
|
||||
props: vec![
|
||||
edit("Contrast", "ul_contrast", self.contrast as f64),
|
||||
edit("Fade", "ul_fade", self.fade as f64),
|
||||
ro("Monochrome", "ul_mono", yes_no(monochrome)),
|
||||
ro(
|
||||
"Adjust Colors for Background",
|
||||
"ul_adjust_bg",
|
||||
yes_no(adjust_bg),
|
||||
),
|
||||
],
|
||||
},
|
||||
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) {
|
||||
|
||||
+78
-180
@@ -122,196 +122,94 @@ fn properties(vp: &Viewport) -> Vec<PropSection> {
|
||||
crate::scene::vp_effective_scale(vp.custom_scale, vp.view_height, vp.height);
|
||||
let current_scale_label = scale_label(effective_scale);
|
||||
|
||||
let view_opts: Vec<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 current_shade = shade_plot_label(vp.shade_plot_mode).to_string();
|
||||
|
||||
vec![PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
edit("Center X", "center_x", vp.center.x),
|
||||
edit("Center Y", "center_y", vp.center.y),
|
||||
edit("Center Z", "center_z", vp.center.z),
|
||||
edit("Width", "vp_w", vp.width),
|
||||
edit("Height", "vp_h", vp.height),
|
||||
// Numeric scale entry (derived from view_height; writes both).
|
||||
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,
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "On".into(),
|
||||
field: "vp_on",
|
||||
value: PropValue::BoolToggle {
|
||||
let annotation_scale = if effective_scale.abs() > 1e-9 {
|
||||
format!("1:{}", scale_label(effective_scale))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
vec![
|
||||
PropSection {
|
||||
title: "Geometry".into(),
|
||||
props: vec![
|
||||
edit("Center X", "center_x", vp.center.x),
|
||||
edit("Center Y", "center_y", vp.center.y),
|
||||
edit("Center Z", "center_z", vp.center.z),
|
||||
edit("Height", "vp_h", vp.height),
|
||||
edit("Width", "vp_w", vp.width),
|
||||
],
|
||||
},
|
||||
PropSection {
|
||||
title: "Misc".into(),
|
||||
props: vec![
|
||||
Property {
|
||||
label: "On".into(),
|
||||
field: "vp_on",
|
||||
value: vp.status.is_on,
|
||||
value: PropValue::BoolToggle {
|
||||
field: "vp_on",
|
||||
value: vp.status.is_on,
|
||||
},
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "Perspective".into(),
|
||||
field: "vp_perspective",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "vp_perspective",
|
||||
value: vp.status.perspective,
|
||||
ro("Clipped", "vp_clipped", String::new()),
|
||||
Property {
|
||||
label: "Display locked".into(),
|
||||
field: "vp_locked",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "vp_locked",
|
||||
value: vp.status.locked,
|
||||
},
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "Hide Plot".into(),
|
||||
field: "vp_hide_plot",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "vp_hide_plot",
|
||||
value: vp.status.hide_plot,
|
||||
ro("Annotation scale", "vp_anno_scale", annotation_scale),
|
||||
Property {
|
||||
label: "Standard scale".into(),
|
||||
field: "vscale_std",
|
||||
value: PropValue::Choice {
|
||||
selected: current_scale_label,
|
||||
options: scale_opts,
|
||||
},
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "UCS Icon".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",
|
||||
value: PropValue::BoolToggle {
|
||||
edit("Custom scale", "vscale", effective_scale),
|
||||
Property {
|
||||
label: "UCS per viewport".into(),
|
||||
field: "vp_ucs_per_vp",
|
||||
value: vp.ucs_per_viewport,
|
||||
value: PropValue::BoolToggle {
|
||||
field: "vp_ucs_per_vp",
|
||||
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(
|
||||
"Grid Flags",
|
||||
"vp_grid_flags",
|
||||
format!("{:#06b}", vp.grid_flags.to_bits()),
|
||||
),
|
||||
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 {
|
||||
format!("{:X}", vp.base_ucs_handle.value())
|
||||
ro(
|
||||
"Layer property overrides",
|
||||
"vp_layer_overrides",
|
||||
if vp.frozen_layers.is_empty() {
|
||||
"No".to_string()
|
||||
} else {
|
||||
format!("Yes ({})", vp.frozen_layers.len())
|
||||
},
|
||||
),
|
||||
ro(
|
||||
"Visual style",
|
||||
"vp_visual_style_handle",
|
||||
if vp.visual_style_handle.is_null() {
|
||||
"(none)".to_string()
|
||||
} else {
|
||||
format!("{:X}", vp.visual_style_handle.value())
|
||||
},
|
||||
),
|
||||
Property {
|
||||
label: "Shade plot".into(),
|
||||
field: "vp_shade_plot",
|
||||
value: PropValue::Choice {
|
||||
selected: current_shade,
|
||||
options: shade_opts,
|
||||
},
|
||||
},
|
||||
),
|
||||
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(
|
||||
"Shade Plot",
|
||||
"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",
|
||||
if vp.visual_style_handle.is_null() {
|
||||
"(none)".to_string()
|
||||
} else {
|
||||
format!("{:X}", vp.visual_style_handle.value())
|
||||
},
|
||||
),
|
||||
// 3D lighting (not yet applied to the viewport mesh path).
|
||||
ro(
|
||||
"Default Lighting",
|
||||
"vp_default_lighting",
|
||||
if vp.default_lighting { "Yes" } else { "No" },
|
||||
),
|
||||
ro(
|
||||
"Lighting Type",
|
||||
"vp_lighting_type",
|
||||
vp.default_lighting_type.to_string(),
|
||||
),
|
||||
ro(
|
||||
"Ambient Color",
|
||||
"vp_ambient_color",
|
||||
format!("{:#010x}", vp.ambient_color as u32),
|
||||
),
|
||||
],
|
||||
}]
|
||||
ro("Linked to Sheet View", "vp_sheet_view", String::new()),
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
/// Identify which standard view matches the viewport's view direction.
|
||||
|
||||
Reference in new issue
Block a user