forked from 1b-agent/opencadstudio-upstream
feat(properties): audit editable vs read-only across all entities
Reclassify every property row to match standard palette behaviour: a row is editable only when its value maps directly to a stored field AND its apply_geom_prop handler is wired; computed/derived/count/handle/ cross-object values stay read-only. Newly editable (all apply-wired): attribute tag/prompt; ellipse major radius; hatch Double; lwpolyline closed/linetype-gen; mline style; multileader annotative/landing/background-fill; polyline 2D+3D vertex coords/segment widths/linetype-gen; shape style; spline control/fit point coords, weight, fit tolerance, tangents; table break-option flags; text alignment point, upside-down, backward; underlay display flags. Corrections: spline Degree -> read-only (editing desynced the knot vector); removed a duplicate leader "Arrowhead" row. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
35468004e6
commit
4d2471cafd
13 files changed
+464
-143
No files matched your search
@@ -386,8 +386,16 @@ impl Grippable for AttributeDefinition {
|
||||
impl PropertyEditable for AttributeDefinition {
|
||||
fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> {
|
||||
let mut text_props = vec![
|
||||
ro("Tag", "att_tag", self.tag.clone()),
|
||||
ro("Prompt", "att_prompt", self.prompt.clone()),
|
||||
Property {
|
||||
label: "Tag".into(),
|
||||
field: "att_tag",
|
||||
value: PropValue::EditText(self.tag.clone()),
|
||||
},
|
||||
Property {
|
||||
label: "Prompt".into(),
|
||||
field: "att_prompt",
|
||||
value: PropValue::EditText(self.prompt.clone()),
|
||||
},
|
||||
Property {
|
||||
label: "Value".into(),
|
||||
field: "att_default",
|
||||
@@ -501,6 +509,14 @@ impl PropertyEditable for AttributeDefinition {
|
||||
|
||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||
// String / enum fields first.
|
||||
if field == "att_tag" {
|
||||
self.tag = value.to_string();
|
||||
return;
|
||||
}
|
||||
if field == "att_prompt" {
|
||||
self.prompt = value.to_string();
|
||||
return;
|
||||
}
|
||||
if field == "att_default" {
|
||||
if !self.flags.constant {
|
||||
self.default_value = value.to_string();
|
||||
@@ -645,7 +661,11 @@ impl PropertyEditable for AttributeEntity {
|
||||
PropSection {
|
||||
title: "Text".into(),
|
||||
props: vec![
|
||||
ro("Tag", "atte_tag", self.tag.clone()),
|
||||
Property {
|
||||
label: "Tag".into(),
|
||||
field: "atte_tag",
|
||||
value: PropValue::EditText(self.tag.clone()),
|
||||
},
|
||||
ro("Prompt", "atte_prompt", String::new()),
|
||||
Property {
|
||||
label: "Value".into(),
|
||||
@@ -746,6 +766,10 @@ impl PropertyEditable for AttributeEntity {
|
||||
}
|
||||
|
||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||
if field == "atte_tag" {
|
||||
self.tag = value.to_string();
|
||||
return;
|
||||
}
|
||||
if field == "atte_val" {
|
||||
if !self.flags.constant {
|
||||
self.value = value.to_string();
|
||||
|
||||
+10
-1
@@ -197,7 +197,7 @@ fn properties(ell: &Ellipse) -> Vec<PropSection> {
|
||||
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}")),
|
||||
edit("Major radius", "major_r", r_major),
|
||||
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}")),
|
||||
@@ -227,6 +227,15 @@ fn apply_geom_prop(ell: &mut Ellipse, field: &str, value: &str) {
|
||||
"center_x" => ell.center.x = v,
|
||||
"center_y" => ell.center.y = v,
|
||||
"center_z" => ell.center.z = v,
|
||||
"major_r" => {
|
||||
let cur = ell.major_axis_length();
|
||||
if cur > 1e-12 && v > 0.0 {
|
||||
let s = v / cur;
|
||||
ell.major_axis.x *= s;
|
||||
ell.major_axis.y *= s;
|
||||
ell.major_axis.z *= s;
|
||||
}
|
||||
}
|
||||
"ratio" => ell.minor_axis_ratio = v,
|
||||
"start_param" => ell.start_parameter = v,
|
||||
"end_param" => ell.end_parameter = v,
|
||||
|
||||
+16
-1
@@ -149,7 +149,14 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
|
||||
if h.is_associative { "Yes" } else { "No" },
|
||||
),
|
||||
ro("Layer override", "layer_override", String::new()),
|
||||
ro("Double", "double", if h.is_double { "Yes" } else { "No" }),
|
||||
Property {
|
||||
label: "Double".into(),
|
||||
field: "double",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "double",
|
||||
value: h.is_double,
|
||||
},
|
||||
},
|
||||
ro(
|
||||
"Spacing",
|
||||
"spacing",
|
||||
@@ -186,6 +193,14 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
|
||||
}
|
||||
|
||||
fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) {
|
||||
if field == "double" {
|
||||
h.is_double = if value == "toggle" {
|
||||
!h.is_double
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
return;
|
||||
}
|
||||
let Some(v) = parse_f64(value) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -209,7 +209,6 @@ fn properties(leader: &Leader) -> Vec<PropSection> {
|
||||
creation_type_str(&leader.creation_type),
|
||||
&["With Text", "With Tolerance", "With Block", "No Annotation"],
|
||||
),
|
||||
bool_toggle("Arrowhead", "arrow_enabled", leader.arrow_enabled),
|
||||
Property {
|
||||
label: "Dim style".into(),
|
||||
field: "dimension_style",
|
||||
|
||||
+36
-11
@@ -7,7 +7,7 @@ use crate::entities::common::{
|
||||
};
|
||||
use crate::entities::traits::TruckConvertible;
|
||||
use crate::scene::convert::acad_to_truck::{TruckEntity, TruckObject};
|
||||
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
||||
use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
||||
use crate::scene::model::wire_model::TangentGeom;
|
||||
|
||||
const TAU: f64 = std::f64::consts::TAU;
|
||||
@@ -344,22 +344,47 @@ fn properties(pline: &LwPolyline) -> Vec<PropSection> {
|
||||
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" },
|
||||
),
|
||||
Property {
|
||||
label: "Closed".into(),
|
||||
field: "closed",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "closed",
|
||||
value: pline.is_closed,
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "Linetype generation".into(),
|
||||
field: "plinegen",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "plinegen",
|
||||
value: pline.plinegen,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn apply_geom_prop(pline: &mut LwPolyline, field: &str, value: &str) {
|
||||
match field {
|
||||
"closed" => {
|
||||
pline.is_closed = if value == "toggle" {
|
||||
!pline.is_closed
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
return;
|
||||
}
|
||||
"plinegen" => {
|
||||
pline.plinegen = if value == "toggle" {
|
||||
!pline.plinegen
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let Some(v) = parse_f64(value) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -211,7 +211,11 @@ impl PropertyEditable for MLine {
|
||||
PropSection {
|
||||
title: "Misc".into(),
|
||||
props: vec![
|
||||
ro("Style", "ml_style", self.style_name.clone()),
|
||||
Property {
|
||||
label: "Style".into(),
|
||||
field: "ml_style",
|
||||
value: PropValue::EditText(self.style_name.clone()),
|
||||
},
|
||||
Property {
|
||||
label: "Style justification".into(),
|
||||
field: "ml_justification",
|
||||
@@ -249,6 +253,10 @@ impl PropertyEditable for MLine {
|
||||
};
|
||||
return;
|
||||
}
|
||||
"ml_style" => {
|
||||
self.style_name = value.to_string();
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let Ok(v) = value.trim().parse::<f64>() else {
|
||||
|
||||
+12
-13
@@ -543,10 +543,10 @@ fn properties(ml: &MultiLeader) -> Vec<PropSection> {
|
||||
&["None", "MText", "Block", "Tolerance"],
|
||||
),
|
||||
edit("Overall scale", "scale_factor", ml.scale_factor),
|
||||
ro(
|
||||
bool_toggle(
|
||||
"Annotative",
|
||||
"enable_annotation_scale",
|
||||
if ml.enable_annotation_scale { "Yes" } else { "No" },
|
||||
ml.enable_annotation_scale,
|
||||
),
|
||||
// No stored annotative-scale name on the entity.
|
||||
ro("Annotative scale", "annotative_scale", String::new()),
|
||||
@@ -588,11 +588,7 @@ fn properties(ml: &MultiLeader) -> Vec<PropSection> {
|
||||
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" },
|
||||
),
|
||||
bool_toggle("Landing", "enable_landing", ml.enable_landing),
|
||||
edit(
|
||||
"Landing distance",
|
||||
"landing_distance",
|
||||
@@ -652,14 +648,10 @@ fn properties(ml: &MultiLeader) -> Vec<PropSection> {
|
||||
format!("{:?}", ml.text_attachment_direction),
|
||||
),
|
||||
edit("Landing gap", "landing_gap", ctx.landing_gap),
|
||||
ro(
|
||||
bool_toggle(
|
||||
"Background mask",
|
||||
"background_fill_enabled",
|
||||
if ctx.background_fill_enabled {
|
||||
"Yes"
|
||||
} else {
|
||||
"No"
|
||||
},
|
||||
ctx.background_fill_enabled,
|
||||
),
|
||||
],
|
||||
};
|
||||
@@ -771,6 +763,13 @@ fn apply_geom_prop(ml: &mut MultiLeader, field: &str, value: &str) {
|
||||
value == "true"
|
||||
}
|
||||
}
|
||||
"background_fill_enabled" => {
|
||||
ml.context.background_fill_enabled = if value == "toggle" {
|
||||
!ml.context.background_fill_enabled
|
||||
} else {
|
||||
value == "true"
|
||||
}
|
||||
}
|
||||
"dogleg_length" => {
|
||||
if let Some(v) = f64(value) {
|
||||
ml.dogleg_length = v;
|
||||
|
||||
+87
-29
@@ -400,20 +400,20 @@ impl PropertyEditable for Polyline2D {
|
||||
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();
|
||||
let vertex_x = v0.map(|v| v.location.x).unwrap_or_default();
|
||||
let vertex_y = v0.map(|v| v.location.y).unwrap_or_default();
|
||||
let seg_start_w = v0.map(|v| v.start_width).unwrap_or_default();
|
||||
let seg_end_w = v0.map(|v| 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("Vertex X", "pl2_vertex_x", vertex_x),
|
||||
edit("Vertex Y", "pl2_vertex_y", vertex_y),
|
||||
edit("Start segment width", "pl2_seg_start_w", seg_start_w),
|
||||
edit("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}")),
|
||||
@@ -431,11 +431,14 @@ impl PropertyEditable for Polyline2D {
|
||||
value: self.is_closed(),
|
||||
},
|
||||
},
|
||||
ro(
|
||||
"Linetype generation",
|
||||
"pl2_ltype_gen",
|
||||
if self.flags.bits() & 128 != 0 { "Enabled" } else { "Disabled" },
|
||||
),
|
||||
Property {
|
||||
label: "Linetype generation".into(),
|
||||
field: "pl2_ltype_gen",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "pl2_ltype_gen",
|
||||
value: self.flags.bits() & 128 != 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@@ -468,6 +471,43 @@ impl PropertyEditable for Polyline2D {
|
||||
}
|
||||
}
|
||||
}
|
||||
"pl2_vertex_x" => {
|
||||
if let (Some(v), Some(vert)) = (parse_f64(value), self.vertices.first_mut()) {
|
||||
vert.location.x = v;
|
||||
}
|
||||
}
|
||||
"pl2_vertex_y" => {
|
||||
if let (Some(v), Some(vert)) = (parse_f64(value), self.vertices.first_mut()) {
|
||||
vert.location.y = v;
|
||||
}
|
||||
}
|
||||
"pl2_seg_start_w" => {
|
||||
if let (Some(v), Some(vert)) = (parse_f64(value), self.vertices.first_mut()) {
|
||||
if v >= 0.0 {
|
||||
vert.start_width = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
"pl2_seg_end_w" => {
|
||||
if let (Some(v), Some(vert)) = (parse_f64(value), self.vertices.first_mut()) {
|
||||
if v >= 0.0 {
|
||||
vert.end_width = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
"pl2_ltype_gen" => {
|
||||
let on = if value == "toggle" {
|
||||
self.flags.bits() & 128 == 0
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
let bits = if on {
|
||||
self.flags.bits() | 128
|
||||
} else {
|
||||
self.flags.bits() & !128
|
||||
};
|
||||
self.flags = acadrust::entities::polyline::PolylineFlags::from_bits(bits);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -616,9 +656,9 @@ impl PropertyEditable for Polyline3D {
|
||||
use acadrust::entities::polyline3d::SmoothSurfaceType as SST;
|
||||
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 vertex_x = v0.map(|v| v.position.x).unwrap_or_default();
|
||||
let vertex_y = v0.map(|v| v.position.y).unwrap_or_default();
|
||||
let vertex_z = v0.map(|v| v.position.z).unwrap_or_default();
|
||||
let fit_smooth = match self.smooth_type {
|
||||
SST::None => "None",
|
||||
SST::QuadraticBSpline => "Quadratic",
|
||||
@@ -631,9 +671,9 @@ impl PropertyEditable for Polyline3D {
|
||||
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),
|
||||
edit("Vertex X", "pl3_vertex_x", vertex_x),
|
||||
edit("Vertex Y", "pl3_vertex_y", vertex_y),
|
||||
edit("Vertex Z", "pl3_vertex_z", vertex_z),
|
||||
],
|
||||
},
|
||||
PropSection {
|
||||
@@ -654,17 +694,35 @@ impl PropertyEditable for Polyline3D {
|
||||
}
|
||||
|
||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||
if field == "pl3_closed" {
|
||||
let closed = if value == "toggle" {
|
||||
!self.is_closed()
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
if closed {
|
||||
self.close();
|
||||
} else {
|
||||
self.open();
|
||||
match field {
|
||||
"pl3_closed" => {
|
||||
let closed = if value == "toggle" {
|
||||
!self.is_closed()
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
if closed {
|
||||
self.close();
|
||||
} else {
|
||||
self.open();
|
||||
}
|
||||
}
|
||||
"pl3_vertex_x" => {
|
||||
if let (Some(v), Some(vert)) = (parse_f64(value), self.vertices.first_mut()) {
|
||||
vert.position.x = v;
|
||||
}
|
||||
}
|
||||
"pl3_vertex_y" => {
|
||||
if let (Some(v), Some(vert)) = (parse_f64(value), self.vertices.first_mut()) {
|
||||
vert.position.y = v;
|
||||
}
|
||||
}
|
||||
"pl3_vertex_z" => {
|
||||
if let (Some(v), Some(vert)) = (parse_f64(value), self.vertices.first_mut()) {
|
||||
vert.position.z = v;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-3
@@ -11,7 +11,7 @@ use crate::command::EntityTransform;
|
||||
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};
|
||||
use crate::scene::model::object::{GripApply, GripDef, PropSection, Property, PropValue};
|
||||
use crate::scene::view::transform;
|
||||
use crate::scene::model::wire_model::SnapHint;
|
||||
|
||||
@@ -118,7 +118,7 @@ impl Grippable for Shape {
|
||||
// ── PropertyEditable ──────────────────────────────────────────────────────────
|
||||
|
||||
impl PropertyEditable for Shape {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||
fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> {
|
||||
let style_handle_display = match self.style_handle {
|
||||
Some(h) if !h.is_null() => format!("{:X}", h.value()),
|
||||
_ => "(none)".to_string(),
|
||||
@@ -128,7 +128,14 @@ impl PropertyEditable for Shape {
|
||||
props: vec![
|
||||
ro("Name", "shp_name", self.shape_name.clone()),
|
||||
ro("Number", "shp_number", self.shape_number.to_string()),
|
||||
ro("Style", "shp_style", self.style_name.clone()),
|
||||
Property {
|
||||
label: "Style".into(),
|
||||
field: "shp_style",
|
||||
value: PropValue::Choice {
|
||||
selected: self.style_name.clone(),
|
||||
options: text_style_names.to_vec(),
|
||||
},
|
||||
},
|
||||
ro("Style Handle", "shp_style_handle", style_handle_display),
|
||||
edit("Insert X", "shp_ix", self.insertion_point.x),
|
||||
edit("Insert Y", "shp_iy", self.insertion_point.y),
|
||||
@@ -146,6 +153,10 @@ impl PropertyEditable for Shape {
|
||||
}
|
||||
|
||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||
if field == "shp_style" {
|
||||
self.style_name = value.to_string();
|
||||
return;
|
||||
}
|
||||
let Ok(v) = value.trim().parse::<f64>() else {
|
||||
return;
|
||||
};
|
||||
|
||||
+65
-24
@@ -162,29 +162,29 @@ fn properties(spline: &Spline) -> Vec<PropSection> {
|
||||
title: "Data Points".into(),
|
||||
props: vec![
|
||||
ro("Show", "show", show),
|
||||
edit("Degree", "degree", spline.degree as f64),
|
||||
ro("Degree", "degree", spline.degree.to_string()),
|
||||
ro(
|
||||
"Control Point Count",
|
||||
"ctrl_pt_count",
|
||||
spline.control_points.len().to_string(),
|
||||
),
|
||||
ro("Control Point", "ctrl_pt_index", "0"),
|
||||
ro(
|
||||
edit(
|
||||
"Control Point X",
|
||||
"ctrl_pt_x",
|
||||
format!("{:.4}", cp0.map(|p| p.x).unwrap_or(0.0)),
|
||||
cp0.map(|p| p.x).unwrap_or(0.0),
|
||||
),
|
||||
ro(
|
||||
edit(
|
||||
"Control Point Y",
|
||||
"ctrl_pt_y",
|
||||
format!("{:.4}", cp0.map(|p| p.y).unwrap_or(0.0)),
|
||||
cp0.map(|p| p.y).unwrap_or(0.0),
|
||||
),
|
||||
ro(
|
||||
edit(
|
||||
"Control Point Z",
|
||||
"ctrl_pt_z",
|
||||
format!("{:.4}", cp0.map(|p| p.z).unwrap_or(0.0)),
|
||||
cp0.map(|p| p.z).unwrap_or(0.0),
|
||||
),
|
||||
ro("Weight", "weight", format!("{:.4}", w0.unwrap_or(1.0))),
|
||||
edit("Weight", "weight", w0.unwrap_or(1.0)),
|
||||
ro("Knot Parameterization", "knot_param", knot_param),
|
||||
ro(
|
||||
"Fit Point Count",
|
||||
@@ -192,28 +192,28 @@ fn properties(spline: &Spline) -> Vec<PropSection> {
|
||||
spline.fit_points.len().to_string(),
|
||||
),
|
||||
ro("Fit Point", "fit_pt_index", "0"),
|
||||
ro(
|
||||
edit(
|
||||
"Fit Point X",
|
||||
"fit_pt_x",
|
||||
format!("{:.4}", fp0.map(|p| p.x).unwrap_or(0.0)),
|
||||
fp0.map(|p| p.x).unwrap_or(0.0),
|
||||
),
|
||||
ro(
|
||||
edit(
|
||||
"Fit Point Y",
|
||||
"fit_pt_y",
|
||||
format!("{:.4}", fp0.map(|p| p.y).unwrap_or(0.0)),
|
||||
fp0.map(|p| p.y).unwrap_or(0.0),
|
||||
),
|
||||
ro(
|
||||
edit(
|
||||
"Fit Point Z",
|
||||
"fit_pt_z",
|
||||
format!("{:.4}", fp0.map(|p| p.z).unwrap_or(0.0)),
|
||||
fp0.map(|p| p.z).unwrap_or(0.0),
|
||||
),
|
||||
ro("Fit Tolerance", "fit_tolerance", format!("{:.6}", spline.fit_tolerance)),
|
||||
ro("Start Tangent X", "start_tan_x", format!("{:.4}", spline.begin_tangent.x)),
|
||||
ro("Start Tangent Y", "start_tan_y", format!("{:.4}", spline.begin_tangent.y)),
|
||||
ro("Start Tangent Z", "start_tan_z", format!("{:.4}", spline.begin_tangent.z)),
|
||||
ro("End Tangent X", "end_tan_x", format!("{:.4}", spline.end_tangent.x)),
|
||||
ro("End Tangent Y", "end_tan_y", format!("{:.4}", spline.end_tangent.y)),
|
||||
ro("End Tangent Z", "end_tan_z", format!("{:.4}", spline.end_tangent.z)),
|
||||
edit("Fit Tolerance", "fit_tolerance", spline.fit_tolerance),
|
||||
edit("Start Tangent X", "start_tan_x", spline.begin_tangent.x),
|
||||
edit("Start Tangent Y", "start_tan_y", spline.begin_tangent.y),
|
||||
edit("Start Tangent Z", "start_tan_z", spline.begin_tangent.z),
|
||||
edit("End Tangent X", "end_tan_x", spline.end_tangent.x),
|
||||
edit("End Tangent Y", "end_tan_y", spline.end_tangent.y),
|
||||
edit("End Tangent Z", "end_tan_z", spline.end_tangent.z),
|
||||
],
|
||||
},
|
||||
PropSection {
|
||||
@@ -229,10 +229,51 @@ fn properties(spline: &Spline) -> Vec<PropSection> {
|
||||
}
|
||||
|
||||
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;
|
||||
let Some(v) = parse_f64(value) else { return };
|
||||
match field {
|
||||
"ctrl_pt_x" => {
|
||||
if let Some(cp) = spline.control_points.first_mut() {
|
||||
cp.x = v;
|
||||
}
|
||||
}
|
||||
"ctrl_pt_y" => {
|
||||
if let Some(cp) = spline.control_points.first_mut() {
|
||||
cp.y = v;
|
||||
}
|
||||
}
|
||||
"ctrl_pt_z" => {
|
||||
if let Some(cp) = spline.control_points.first_mut() {
|
||||
cp.z = v;
|
||||
}
|
||||
}
|
||||
"weight" => {
|
||||
if let Some(w) = spline.weights.first_mut() {
|
||||
*w = v;
|
||||
}
|
||||
}
|
||||
"fit_pt_x" => {
|
||||
if let Some(fp) = spline.fit_points.first_mut() {
|
||||
fp.x = v;
|
||||
}
|
||||
}
|
||||
"fit_pt_y" => {
|
||||
if let Some(fp) = spline.fit_points.first_mut() {
|
||||
fp.y = v;
|
||||
}
|
||||
}
|
||||
"fit_pt_z" => {
|
||||
if let Some(fp) = spline.fit_points.first_mut() {
|
||||
fp.z = v;
|
||||
}
|
||||
}
|
||||
"fit_tolerance" => spline.fit_tolerance = v,
|
||||
"start_tan_x" => spline.begin_tangent.x = v,
|
||||
"start_tan_y" => spline.begin_tangent.y = v,
|
||||
"start_tan_z" => spline.begin_tangent.z = v,
|
||||
"end_tan_x" => spline.end_tangent.x = v,
|
||||
"end_tan_y" => spline.end_tangent.y = v,
|
||||
"end_tan_z" => spline.end_tangent.z = v,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+39
-25
@@ -8,7 +8,7 @@ use crate::entities::text_support::{
|
||||
};
|
||||
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};
|
||||
use crate::scene::model::object::{GripApply, GripDef, PropSection, Property, PropValue};
|
||||
use crate::scene::view::transform;
|
||||
use crate::scene::model::wire_model::SnapHint;
|
||||
|
||||
@@ -828,8 +828,12 @@ impl PropertyEditable for Table {
|
||||
_ => "(none)".to_string(),
|
||||
}
|
||||
};
|
||||
let yes_no = |b: bool| -> String {
|
||||
if b { "Yes".into() } else { "No".into() }
|
||||
let toggle = |label: &'static str, field: &'static str, b: bool| -> Property {
|
||||
Property {
|
||||
label: label.into(),
|
||||
field,
|
||||
value: PropValue::BoolToggle { field, value: b },
|
||||
}
|
||||
};
|
||||
// Direction = angle of the horizontal direction vector in the XY plane.
|
||||
let direction_deg =
|
||||
@@ -862,47 +866,39 @@ impl PropertyEditable for Table {
|
||||
PropSection {
|
||||
title: "Table Breaks".into(),
|
||||
props: vec![
|
||||
ro(
|
||||
toggle(
|
||||
"Enabled",
|
||||
"tbl_break_enabled",
|
||||
yes_no(self.break_options.contains(BreakOptionFlags::ENABLE_BREAKS)),
|
||||
self.break_options.contains(BreakOptionFlags::ENABLE_BREAKS),
|
||||
),
|
||||
ro(
|
||||
"Direction",
|
||||
"tbl_break_direction",
|
||||
format!("{:?}", self.break_flow_direction),
|
||||
),
|
||||
ro(
|
||||
toggle(
|
||||
"Repeat top labels",
|
||||
"tbl_break_repeat_top",
|
||||
yes_no(
|
||||
self.break_options
|
||||
.contains(BreakOptionFlags::REPEAT_TOP_LABELS),
|
||||
),
|
||||
self.break_options
|
||||
.contains(BreakOptionFlags::REPEAT_TOP_LABELS),
|
||||
),
|
||||
ro(
|
||||
toggle(
|
||||
"Repeat bottom labels",
|
||||
"tbl_break_repeat_bottom",
|
||||
yes_no(
|
||||
self.break_options
|
||||
.contains(BreakOptionFlags::REPEAT_BOTTOM_LABELS),
|
||||
),
|
||||
self.break_options
|
||||
.contains(BreakOptionFlags::REPEAT_BOTTOM_LABELS),
|
||||
),
|
||||
ro(
|
||||
toggle(
|
||||
"Manual positions",
|
||||
"tbl_break_manual_positions",
|
||||
yes_no(
|
||||
self.break_options
|
||||
.contains(BreakOptionFlags::ALLOW_MANUAL_POSITIONS),
|
||||
),
|
||||
self.break_options
|
||||
.contains(BreakOptionFlags::ALLOW_MANUAL_POSITIONS),
|
||||
),
|
||||
ro(
|
||||
toggle(
|
||||
"Manual heights",
|
||||
"tbl_break_manual_heights",
|
||||
yes_no(
|
||||
self.break_options
|
||||
.contains(BreakOptionFlags::ALLOW_MANUAL_HEIGHTS),
|
||||
),
|
||||
self.break_options
|
||||
.contains(BreakOptionFlags::ALLOW_MANUAL_HEIGHTS),
|
||||
),
|
||||
ro("Break height", "tbl_break_height", String::new()),
|
||||
edit("Spacing", "tbl_break_spacing", self.break_spacing),
|
||||
@@ -913,6 +909,24 @@ impl PropertyEditable for Table {
|
||||
|
||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||
use crate::entities::common::parse_f64;
|
||||
use acadrust::entities::table::BreakOptionFlags;
|
||||
let flag = match field {
|
||||
"tbl_break_enabled" => Some(BreakOptionFlags::ENABLE_BREAKS),
|
||||
"tbl_break_repeat_top" => Some(BreakOptionFlags::REPEAT_TOP_LABELS),
|
||||
"tbl_break_repeat_bottom" => Some(BreakOptionFlags::REPEAT_BOTTOM_LABELS),
|
||||
"tbl_break_manual_positions" => Some(BreakOptionFlags::ALLOW_MANUAL_POSITIONS),
|
||||
"tbl_break_manual_heights" => Some(BreakOptionFlags::ALLOW_MANUAL_HEIGHTS),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(flag) = flag {
|
||||
let on = if value == "toggle" {
|
||||
!self.break_options.contains(flag)
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
self.break_options.set(flag, on);
|
||||
return;
|
||||
}
|
||||
if field == "tbl_break_spacing" {
|
||||
if let Some(v) = parse_f64(value) {
|
||||
self.break_spacing = v;
|
||||
|
||||
+52
-19
@@ -222,9 +222,9 @@ fn properties(t: &Text, text_style_names: &[String]) -> Vec<PropSection> {
|
||||
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)),
|
||||
edit("Text alignment X", "align_x", ap.x),
|
||||
edit("Text alignment Y", "align_y", ap.y),
|
||||
edit("Text alignment Z", "align_z", ap.z),
|
||||
],
|
||||
},
|
||||
PropSection {
|
||||
@@ -238,24 +238,22 @@ fn properties(t: &Text, text_style_names: &[String]) -> Vec<PropSection> {
|
||||
PropSection {
|
||||
title: "Misc".into(),
|
||||
props: vec![
|
||||
ro(
|
||||
"Upside down",
|
||||
"upside_down",
|
||||
if t.generation_flags & 0x4 != 0 {
|
||||
"Yes"
|
||||
} else {
|
||||
"No"
|
||||
Property {
|
||||
label: "Upside down".into(),
|
||||
field: "upside_down",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "upside_down",
|
||||
value: t.generation_flags & 0x4 != 0,
|
||||
},
|
||||
),
|
||||
ro(
|
||||
"Backward",
|
||||
"backward",
|
||||
if t.generation_flags & 0x2 != 0 {
|
||||
"Yes"
|
||||
} else {
|
||||
"No"
|
||||
},
|
||||
Property {
|
||||
label: "Backward".into(),
|
||||
field: "backward",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "backward",
|
||||
value: t.generation_flags & 0x2 != 0,
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@@ -295,6 +293,32 @@ fn apply_geom_prop(t: &mut Text, field: &str, value: &str) {
|
||||
sync_text_alignment_point(t);
|
||||
return;
|
||||
}
|
||||
"upside_down" => {
|
||||
let set = if value == "toggle" {
|
||||
t.generation_flags & 0x4 == 0
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
if set {
|
||||
t.generation_flags |= 0x4;
|
||||
} else {
|
||||
t.generation_flags &= !0x4;
|
||||
}
|
||||
return;
|
||||
}
|
||||
"backward" => {
|
||||
let set = if value == "toggle" {
|
||||
t.generation_flags & 0x2 == 0
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
if set {
|
||||
t.generation_flags |= 0x2;
|
||||
} else {
|
||||
t.generation_flags &= !0x2;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let Some(v) = parse_f64(value) else {
|
||||
@@ -304,6 +328,15 @@ fn apply_geom_prop(t: &mut Text, field: &str, value: &str) {
|
||||
"ins_x" => t.insertion_point.x = v,
|
||||
"ins_y" => t.insertion_point.y = v,
|
||||
"ins_z" => t.insertion_point.z = v,
|
||||
"align_x" | "align_y" | "align_z" => {
|
||||
let ins = t.insertion_point;
|
||||
let ap = t.alignment_point.get_or_insert(ins);
|
||||
match field {
|
||||
"align_x" => ap.x = v,
|
||||
"align_y" => ap.y = v,
|
||||
_ => ap.z = v,
|
||||
}
|
||||
}
|
||||
"height" if v > 0.0 => t.height = v,
|
||||
"rotation" => t.rotation = v.to_radians(),
|
||||
"width_factor" if v > 0.0 => t.width_factor = v,
|
||||
|
||||
+97
-12
@@ -10,7 +10,7 @@ use crate::command::EntityTransform;
|
||||
use crate::entities::common::{center_grip, 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};
|
||||
use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
||||
use crate::scene::model::wire_model::SnapHint;
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
@@ -147,8 +147,6 @@ impl Grippable for Underlay {
|
||||
|
||||
impl PropertyEditable for Underlay {
|
||||
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||
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() {
|
||||
@@ -193,12 +191,22 @@ impl PropertyEditable for Underlay {
|
||||
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),
|
||||
),
|
||||
Property {
|
||||
label: "Monochrome".into(),
|
||||
field: "ul_mono",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "ul_mono",
|
||||
value: monochrome,
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "Adjust Colors for Background".into(),
|
||||
field: "ul_adjust_bg",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "ul_adjust_bg",
|
||||
value: adjust_bg,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
PropSection {
|
||||
@@ -208,9 +216,30 @@ impl PropertyEditable for Underlay {
|
||||
// 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)),
|
||||
Property {
|
||||
label: "Show underlay".into(),
|
||||
field: "ul_on",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "ul_on",
|
||||
value: show,
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "Clipping".into(),
|
||||
field: "ul_clip",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "ul_clip",
|
||||
value: clipping,
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: "Show clipped".into(),
|
||||
field: "ul_clip_inverted",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "ul_clip_inverted",
|
||||
value: self.clip_inverted,
|
||||
},
|
||||
},
|
||||
ro("Underlay layers", "ul_layers", String::new()),
|
||||
],
|
||||
},
|
||||
@@ -218,6 +247,62 @@ impl PropertyEditable for Underlay {
|
||||
}
|
||||
|
||||
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||
match field {
|
||||
"ul_mono" => {
|
||||
let on = if value == "toggle" {
|
||||
!self.flags.contains(UnderlayDisplayFlags::MONOCHROME)
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
self.set_monochrome(on);
|
||||
return;
|
||||
}
|
||||
"ul_adjust_bg" => {
|
||||
let on = if value == "toggle" {
|
||||
!self.flags.contains(UnderlayDisplayFlags::ADJUST_FOR_BACKGROUND)
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
if on {
|
||||
self.flags |= UnderlayDisplayFlags::ADJUST_FOR_BACKGROUND;
|
||||
} else {
|
||||
self.flags -= UnderlayDisplayFlags::ADJUST_FOR_BACKGROUND;
|
||||
}
|
||||
return;
|
||||
}
|
||||
"ul_on" => {
|
||||
let on = if value == "toggle" {
|
||||
!self.flags.contains(UnderlayDisplayFlags::ON)
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
self.set_on(on);
|
||||
return;
|
||||
}
|
||||
"ul_clip" => {
|
||||
let on = if value == "toggle" {
|
||||
!self.flags.contains(UnderlayDisplayFlags::CLIPPING)
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
if on {
|
||||
self.flags |= UnderlayDisplayFlags::CLIPPING;
|
||||
} else {
|
||||
self.flags -= UnderlayDisplayFlags::CLIPPING;
|
||||
}
|
||||
return;
|
||||
}
|
||||
"ul_clip_inverted" => {
|
||||
let on = if value == "toggle" {
|
||||
!self.clip_inverted
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
self.clip_inverted = on;
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Ok(v) = value.trim().parse::<f64>() {
|
||||
match field {
|
||||
"ul_ix" => self.insertion_point.x = v,
|
||||
|
||||
Reference in new issue
Block a user