fix(mleader): repair creation and editing

This commit is contained in:
Hakan Seven committed 2026-08-28 19:36:48 +03:00
commit ff1f873633
40 files changed
+4504 -1754

No files matched your search

+64 -229
View File
@@ -668,138 +668,87 @@ impl OpenCADStudio {
"DIMCONTINUE" => {
use crate::modules::annotate::dim_continue::DimContinueCommand;
let cmd = if let Some((p1, p2, dp, rot, trot)) =
find_last_linear_dim(&self.tabs[i].scene)
{
DimContinueCommand::from_base(p1, p2, dp, rot, trot)
} else {
DimContinueCommand::new()
};
let scene = &self.tabs[i].scene;
let recent = scene
.last_created_dimension
.filter(|handle| scene.entity_belongs_to_active_space(*handle))
.and_then(|handle| scene.document.get_entity(handle))
.cloned();
let cmd = DimContinueCommand::new(
recent,
self.dimension_continue_mode == 1,
);
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
}
"DIMBASELINE" => {
use crate::modules::annotate::dim_baseline::DimBaselineCommand;
let cmd = if let Some((p1, p2, dp, rot, trot)) =
find_last_linear_dim(&self.tabs[i].scene)
{
let doc = &self.tabs[i].scene.document;
let dimdli = doc
.dim_styles
.iter()
.find(|s| {
s.name
.eq_ignore_ascii_case(&doc.header.current_dimstyle_name)
})
.map(|s| s.dimdli as f32)
.unwrap_or(1.5);
DimBaselineCommand::from_base(p1, p2, dp, rot, trot, dimdli)
let scene = &self.tabs[i].scene;
let recent = scene
.last_created_dimension
.filter(|handle| scene.entity_belongs_to_active_space(*handle))
.and_then(|handle| scene.document.get_entity(handle))
.cloned();
let dimdli_by_style = scene
.document
.dim_styles
.iter()
.map(|style| (style.name.to_ascii_lowercase(), style.dimdli))
.collect();
let fallback_dimdli = if scene.document.header.measurement == 1 {
3.75
} else {
DimBaselineCommand::new()
0.38
};
let current_style_name = scene.document.header.current_dimstyle_name.clone();
let cmd = DimBaselineCommand::new(
recent,
dimdli_by_style,
current_style_name,
fallback_dimdli,
self.dimension_continue_mode == 1,
);
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
}
"QDIM" => {
use crate::modules::annotate::qdim::QdimCommand;
let cmd = QdimCommand::new();
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
}
// QDIM second stage: the front-end relaunched with the picked
// dimension-line point and the gathered geometry now selected. Build
// a continuous chain of linear dimensions across the endpoints.
cmd if cmd.starts_with("QDIM_PLACE ") => {
let nums: Vec<f64> = cmd
.split_whitespace()
.skip(1)
.filter_map(|s| s.parse().ok())
.collect();
if nums.len() < 3 {
return Some(Task::none());
}
let plane = if self.tabs[i].editing_model_space() {
self.tabs[i].ucs_xform().working_plane()
} else {
crate::command::WorkingPlane::default()
};
let place = plane.to_local(glam::DVec3::new(nums[0], nums[1], nums[2]));
let handles: Vec<acadrust::Handle> = self.tabs[i]
let document = &self.tabs[i].scene.document;
let dim_spacing = document
.dim_styles
.iter()
.find(|style| {
style
.name
.eq_ignore_ascii_case(&document.header.current_dimstyle_name)
})
.map(|style| style.dimdli)
.unwrap_or_else(|| {
if document.header.measurement == 1 {
3.75
} else {
0.38
}
});
let selection = self.tabs[i]
.scene
.selected_entities()
.iter()
.map(|(h, _)| *h)
.into_iter()
.map(|(handle, entity)| crate::command::SelectionEntity {
handle,
entity: entity.clone(),
surface_area: None,
})
.collect();
let mut pts: Vec<glam::DVec3> = Vec::new();
for h in &handles {
if let Some(e) = self.tabs[i].scene.document.get_entity(*h) {
qdim_collect_points(e, &mut pts);
}
}
for point in &mut pts {
*point = plane.to_local(*point);
}
if pts.len() < 2 {
self.command_line
.push_error(crate::t!("QDIM: no dimensionable endpoints in the selection.").as_ref());
return Some(Task::none());
}
// Choose the dimension axis from the points' spread: a wider X
// span dimensions horizontally (ordered by X), else vertically.
let (minx, maxx) = pts
.iter()
.fold((f64::MAX, f64::MIN), |(a, b), p| (a.min(p.x), b.max(p.x)));
let (miny, maxy) = pts
.iter()
.fold((f64::MAX, f64::MIN), |(a, b), p| (a.min(p.y), b.max(p.y)));
let horizontal = (maxx - minx) >= (maxy - miny);
if horizontal {
pts.sort_by(|a, b| a.x.total_cmp(&b.x));
pts.dedup_by(|a, b| (a.x - b.x).abs() < 1e-4);
} else {
pts.sort_by(|a, b| a.y.total_cmp(&b.y));
pts.dedup_by(|a, b| (a.y - b.y).abs() < 1e-4);
}
if pts.len() < 2 {
self.command_line
.push_error(crate::t!("QDIM: endpoints collapse to a single position.").as_ref());
return Some(Task::none());
}
self.push_undo_snapshot(i, "QDIM");
let v = |p: glam::DVec3| {
acadrust::types::Vector3::new(p.x, p.y, p.z)
};
let mut made = 0usize;
for w in pts.windows(2) {
let (p1, p2) = (w[0], w[1]);
let mut dim = acadrust::entities::DimensionLinear::new(v(p1), v(p2));
dim.rotation = if horizontal {
0.0
} else {
std::f64::consts::FRAC_PI_2
};
// Dim line passes through the picked perpendicular position.
let def = if horizontal {
glam::DVec3::new((p1.x + p2.x) * 0.5, place.y, 0.0)
} else {
glam::DVec3::new(place.x, (p1.y + p2.y) * 0.5, 0.0)
};
dim.definition_point = v(def);
dim.base.definition_point = v(def);
dim.base.actual_measurement = dim.measurement();
let entity = acadrust::EntityType::Dimension(
acadrust::entities::Dimension::Linear(dim),
);
self.commit_entity(plane.place_entity(entity));
made += 1;
}
self.tabs[i].dirty = true;
self.command_line
.push_output(crate::tf!("QDIM {made} dimensions created.").as_ref());
return Some(Task::none());
let cmd = QdimCommand::new(
selection,
dim_spacing,
self.quick_dimension_snap_priority,
);
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
}
"DIMEDIT" => {
@@ -1398,120 +1347,6 @@ impl OpenCADStudio {
}
}
/// Find the last placed linear or aligned dimension in the document.
/// Returns `(first_point, second_point, definition_point, rotation_rad)` in world-space.
fn find_last_linear_dim(
scene: &crate::scene::Scene,
) -> Option<(glam::Vec3, glam::Vec3, glam::Vec3, f64, f64)> {
use acadrust::entities::Dimension;
let mut best_handle: u64 = 0;
let mut result: Option<(glam::Vec3, glam::Vec3, glam::Vec3, f64, f64)> = None;
for entity in scene.document.entities() {
if let acadrust::EntityType::Dimension(dim) = entity {
let h = entity.common().handle.value();
if h <= best_handle {
continue;
}
let item = match dim {
Dimension::Linear(d) => {
let p1 = glam::Vec3::new(
d.first_point.x as f32,
d.first_point.y as f32,
d.first_point.z as f32,
);
let p2 = glam::Vec3::new(
d.second_point.x as f32,
d.second_point.y as f32,
d.second_point.z as f32,
);
let dp = glam::Vec3::new(
d.base.definition_point.x as f32,
d.base.definition_point.y as f32,
d.base.definition_point.z as f32,
);
Some((p1, p2, dp, d.rotation, d.base.text_rotation))
}
Dimension::Aligned(d) => {
let p1 = glam::Vec3::new(
d.first_point.x as f32,
d.first_point.y as f32,
d.first_point.z as f32,
);
let p2 = glam::Vec3::new(
d.second_point.x as f32,
d.second_point.y as f32,
d.second_point.z as f32,
);
let dp = glam::Vec3::new(
d.base.definition_point.x as f32,
d.base.definition_point.y as f32,
d.base.definition_point.z as f32,
);
let dx = (d.second_point.x - d.first_point.x) as f32;
let dy = (d.second_point.y - d.first_point.y) as f32;
let rot = dy.atan2(dx) as f64;
Some((p1, p2, dp, rot, d.base.text_rotation))
}
_ => None,
};
if let Some(data) = item {
best_handle = h;
result = Some(data);
}
}
}
result
}
/// Collect candidate dimension endpoints from an entity for QDIM — line ends,
/// polyline vertices, arc endpoints — in world space.
fn qdim_collect_points(e: &acadrust::EntityType, out: &mut Vec<glam::DVec3>) {
use acadrust::EntityType as ET;
let p = |v: &acadrust::types::Vector3| glam::DVec3::new(v.x, v.y, v.z);
let ocs = |point: (f64, f64, f64), normal: acadrust::types::Vector3| {
let world = crate::scene::view::transform::ocs_point_to_wcs(
point,
(normal.x, normal.y, normal.z),
);
glam::DVec3::new(world.0, world.1, world.2)
};
match e {
ET::Line(l) => {
out.push(p(&l.start));
out.push(p(&l.end));
}
ET::LwPolyline(pl) => {
for v in &pl.vertices {
out.push(ocs((v.location.x, v.location.y, pl.elevation), pl.normal));
}
}
ET::Polyline2D(pl) => {
for v in &pl.vertices {
out.push(ocs((v.location.x, v.location.y, pl.elevation), pl.normal));
}
}
ET::Polyline(pl) => {
for v in &pl.vertices {
out.push(p(&v.location));
}
}
ET::Arc(a) => {
for &ang in &[a.start_angle, a.end_angle] {
out.push(ocs(
(
a.center.x + a.radius * ang.cos(),
a.center.y + a.radius * ang.sin(),
a.center.z,
),
a.normal,
));
}
}
_ => {}
}
}
// ── TCASE helpers ──────────────────────────────────────────────────────────
fn sentence_case(s: &str) -> String {
let mut c = s.chars();