forked from 1b-agent/opencadstudio-upstream
Merge main into Pallet-placement
Resolved conflicts: - app/update/mod.rs: keep the dock's dock_open_at_default for the XREF manager; take main's stable xref row identity (row_index(&id)). - app/view/mod.rs: keep the edge layout from app::dock; drop main's copy of the old edge-stack code (main only reformatted it there). - ui/window/layers.rs: keep main's new visible_indices next to the branch's view_panel. Files the branch only reformatted take main's version. Adds the Lao (lo-LA) translations for the new dock texts. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
This commit is contained in:
136 files changed
+12408
-949
No files matched your search
+21
-21
@@ -1245,30 +1245,30 @@ impl OpenCADStudio {
|
||||
} else {
|
||||
// EXPLMODE 0 keeps references that are not uniformly and
|
||||
// positively scaled whole.
|
||||
let selected: Vec<_> = if super::blockvars::explmode(
|
||||
&self.tabs[i].scene.document,
|
||||
) == 0
|
||||
{
|
||||
selected
|
||||
.into_iter()
|
||||
.filter(|(_, entity)| match entity {
|
||||
codec::EntityType::Insert(ins) => {
|
||||
let (x, y, z) = (ins.x_scale(), ins.y_scale(), ins.z_scale());
|
||||
let uniform =
|
||||
x > 0.0 && (x - y).abs() <= 1e-10 && (x - z).abs() <= 1e-10;
|
||||
if !uniform {
|
||||
self.command_line.push_output(
|
||||
let selected: Vec<_> =
|
||||
if super::blockvars::explmode(&self.tabs[i].scene.document) == 0 {
|
||||
selected
|
||||
.into_iter()
|
||||
.filter(|(_, entity)| match entity {
|
||||
codec::EntityType::Insert(ins) => {
|
||||
let (x, y, z) =
|
||||
(ins.x_scale(), ins.y_scale(), ins.z_scale());
|
||||
let uniform = x > 0.0
|
||||
&& (x - y).abs() <= 1e-10
|
||||
&& (x - z).abs() <= 1e-10;
|
||||
if !uniform {
|
||||
self.command_line.push_output(
|
||||
"The object is not uniformly and positively scaled.",
|
||||
);
|
||||
}
|
||||
uniform
|
||||
}
|
||||
uniform
|
||||
}
|
||||
_ => true,
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
selected
|
||||
};
|
||||
_ => true,
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
selected
|
||||
};
|
||||
let replacements = plan_explode(&selected, &self.tabs[i].scene.document);
|
||||
let exploded = replacements.len();
|
||||
if exploded > 0 {
|
||||
|
||||
@@ -1145,6 +1145,82 @@ impl OpenCADStudio {
|
||||
for (handle, _) in &selected {
|
||||
if let Some(entity) = self.tabs[i].scene.document.get_entity(*handle) {
|
||||
use crate::entities::traits::EntityTypeOps;
|
||||
// A solid's properties come from its B-rep: exact
|
||||
// where the kernel knows the shape, a fine mesh
|
||||
// otherwise — not the display mesh.
|
||||
let acis = match entity {
|
||||
codec::EntityType::Solid3D(solid) => Some(&solid.acis_data),
|
||||
codec::EntityType::Body(body) => Some(&body.acis_data),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(acis) = acis {
|
||||
let properties =
|
||||
crate::scene::convert::solid3d_tess::kernel_acis_body(acis)
|
||||
.and_then(|body| kernel::brep::mass_properties(&body));
|
||||
let Some(p) = properties else {
|
||||
self.command_line.push_error(
|
||||
crate::tf!(
|
||||
"MASSPROP: {} has no closed body to measure.",
|
||||
crate::entities::names::dxf_name(entity),
|
||||
)
|
||||
.as_ref(),
|
||||
);
|
||||
continue;
|
||||
};
|
||||
let [x, y, z] = p.centroid;
|
||||
let [ix, iy, iz] = p.moment_of_inertia;
|
||||
let [pxy, pyz, pzx] = p.product_of_inertia;
|
||||
let [rx, ry, rz] = p.radii_of_gyration;
|
||||
let [i1, i2, i3] = p.principal_moments;
|
||||
self.command_line.push_output(
|
||||
crate::tf!(
|
||||
"{} Volume={:.4} Centroid=({:.4},{:.4},{:.4})",
|
||||
crate::entities::names::dxf_name(entity),
|
||||
p.volume,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
)
|
||||
.as_ref(),
|
||||
);
|
||||
self.command_line.push_output(
|
||||
crate::tf!(
|
||||
"Moments of inertia X={:.4} Y={:.4} Z={:.4}",
|
||||
ix,
|
||||
iy,
|
||||
iz,
|
||||
)
|
||||
.as_ref(),
|
||||
);
|
||||
self.command_line.push_output(
|
||||
crate::tf!(
|
||||
"Products of inertia XY={:.4} YZ={:.4} ZX={:.4}",
|
||||
pxy,
|
||||
pyz,
|
||||
pzx,
|
||||
)
|
||||
.as_ref(),
|
||||
);
|
||||
self.command_line.push_output(
|
||||
crate::tf!(
|
||||
"Radii of gyration X={:.4} Y={:.4} Z={:.4}",
|
||||
rx,
|
||||
ry,
|
||||
rz,
|
||||
)
|
||||
.as_ref(),
|
||||
);
|
||||
self.command_line.push_output(
|
||||
crate::tf!(
|
||||
"Principal moments about centroid I={:.4} J={:.4} K={:.4}",
|
||||
i1,
|
||||
i2,
|
||||
i3,
|
||||
)
|
||||
.as_ref(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if let Some(props) = entity.mass_props() {
|
||||
self.command_line.push_output(
|
||||
crate::tf!(
|
||||
|
||||
@@ -649,6 +649,102 @@ impl OpenCADStudio {
|
||||
.push_info(crate::t!("LAYUNISO: all layers restored.").as_ref());
|
||||
}
|
||||
|
||||
"QLAYER" => {
|
||||
use crate::modules::draw::layers::quick_layer::{
|
||||
QuickLayerCommand, QuickLayerEntry,
|
||||
};
|
||||
|
||||
let layers: Vec<QuickLayerEntry> = self.tabs[i]
|
||||
.layers
|
||||
.layers
|
||||
.iter()
|
||||
.filter(|layer| !layer.name.starts_with('*'))
|
||||
.map(|layer| {
|
||||
QuickLayerEntry::new(
|
||||
layer.name.clone(),
|
||||
layer.color.clone(),
|
||||
layer.visible,
|
||||
layer.frozen,
|
||||
layer.locked,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let selected: Vec<codec::Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(handle, _)| handle)
|
||||
.collect();
|
||||
|
||||
let command = QuickLayerCommand::new_with_entries(layers, selected);
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
|
||||
cmd if cmd.starts_with("QLAYER_APPLY_HEX ") => {
|
||||
use crate::modules::draw::layers::quick_layer::QuickLayerCommand;
|
||||
|
||||
let encoded = cmd.trim_start_matches("QLAYER_APPLY_HEX").trim();
|
||||
|
||||
let Some(requested) = QuickLayerCommand::decode_layer_name(encoded) else {
|
||||
self.command_line
|
||||
.push_error(crate::t!("QLAYER: invalid encoded layer name.").as_ref());
|
||||
return Some(Task::none());
|
||||
};
|
||||
|
||||
let resolved = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.layers
|
||||
.names()
|
||||
.find(|name| name.eq_ignore_ascii_case(&requested))
|
||||
.map(|name| name.to_string());
|
||||
|
||||
let Some(layer) = resolved else {
|
||||
self.command_line
|
||||
.push_error(crate::tf!("QLAYER: no layer named \"{requested}\".").as_ref());
|
||||
return Some(Task::none());
|
||||
};
|
||||
|
||||
let handles: Vec<codec::Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(handle, _)| handle)
|
||||
.collect();
|
||||
|
||||
if handles.is_empty() {
|
||||
match self.set_current_layer_name(i, &layer) {
|
||||
Ok(()) => {
|
||||
self.command_line.push_info(
|
||||
crate::tf!("QLAYER: current layer set to \"{layer}\".").as_ref(),
|
||||
);
|
||||
}
|
||||
Err(error) => {
|
||||
self.command_line.push_error(&error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let count = handles.len();
|
||||
|
||||
self.apply_property_op(i, "QLAYER", &handles, |app, handle| {
|
||||
if let Some(entity) = app.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
crate::scene::view::dispatch::apply_common_prop(
|
||||
entity, "layer", &layer,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
self.ribbon.active_layer = layer.clone();
|
||||
|
||||
self.command_line.push_info(
|
||||
crate::tf!("QLAYER: moved {count} object(s) to layer \"{layer}\".")
|
||||
.as_ref(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
"LAYMATCH" | "LAYMCH" => {
|
||||
use crate::modules::draw::layers::match_layer::LayMatchCommand;
|
||||
let dest: Vec<_> = self.tabs[i]
|
||||
|
||||
@@ -706,6 +706,7 @@ inventory::submit!(crate::command::CommandRegistration {
|
||||
"PDFOSNAP",
|
||||
"UOSNAP",
|
||||
"FIELDDISPLAY",
|
||||
"SURFACEASSOCIATIVITY",
|
||||
"FIELDEVAL",
|
||||
"PDFIMPORTMODE",
|
||||
"PDFIMPORTFILTER",
|
||||
|
||||
@@ -293,7 +293,7 @@ impl OpenCADStudio {
|
||||
if name.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(name.clone())
|
||||
Some(name.to_uppercase())
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@@ -320,6 +320,17 @@ impl OpenCADStudio {
|
||||
.map(|ds| ds.dimtxsty.clone()),
|
||||
)
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.to_uppercase())
|
||||
.collect();
|
||||
// Complex linetypes reach their text and shape styles by handle.
|
||||
let linetype_styles: rustc_hash::FxHashSet<codec::Handle> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.line_types
|
||||
.iter()
|
||||
.flat_map(|lt| lt.elements.iter())
|
||||
.filter_map(|element| element.complex.as_ref())
|
||||
.map(|complex| complex.style_handle)
|
||||
.collect();
|
||||
// Linetypes: entity references plus every layer's own
|
||||
// linetype — a ByLayer entity reaches it through the layer.
|
||||
@@ -344,6 +355,7 @@ impl OpenCADStudio {
|
||||
.map(|l| l.line_type.clone()),
|
||||
)
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.to_uppercase())
|
||||
.collect();
|
||||
|
||||
// Live blocks are reachable from layouts and style roots
|
||||
@@ -409,7 +421,9 @@ impl OpenCADStudio {
|
||||
.document
|
||||
.layers
|
||||
.iter()
|
||||
.filter(|l| l.name != "0" && !used_layers.contains(&l.name))
|
||||
.filter(|l| {
|
||||
l.name != "0" && !used_layers.contains(&l.name.to_uppercase())
|
||||
})
|
||||
.map(|l| l.name.clone())
|
||||
.collect()
|
||||
} else {
|
||||
@@ -421,7 +435,15 @@ impl OpenCADStudio {
|
||||
.document
|
||||
.text_styles
|
||||
.iter()
|
||||
.filter(|s| s.name != "Standard" && !used_text_styles.contains(&s.name))
|
||||
// Unnamed shape-file records serve complex linetypes
|
||||
// and are never listed for purging.
|
||||
.filter(|s| {
|
||||
!s.name.is_empty()
|
||||
&& !s.is_shape_file
|
||||
&& !s.name.eq_ignore_ascii_case("Standard")
|
||||
&& !used_text_styles.contains(&s.name.to_uppercase())
|
||||
&& !linetype_styles.contains(&s.handle)
|
||||
})
|
||||
.map(|s| s.name.clone())
|
||||
.collect()
|
||||
} else {
|
||||
@@ -436,7 +458,7 @@ impl OpenCADStudio {
|
||||
.iter()
|
||||
.filter(|lt| {
|
||||
!standard.iter().any(|s| s.eq_ignore_ascii_case(<.name))
|
||||
&& !used_linetypes.contains(<.name)
|
||||
&& !used_linetypes.contains(<.name.to_uppercase())
|
||||
})
|
||||
.map(|lt| lt.name.clone())
|
||||
.collect()
|
||||
@@ -483,16 +505,23 @@ impl OpenCADStudio {
|
||||
snapshot_pushed = true;
|
||||
}
|
||||
|
||||
// Apply removals (mutable)
|
||||
for name in &layer_remove {
|
||||
self.tabs[i].scene.document.layers.remove(name);
|
||||
}
|
||||
for name in &style_remove {
|
||||
self.tabs[i].scene.document.text_styles.remove(name);
|
||||
}
|
||||
for name in <_remove {
|
||||
self.tabs[i].scene.document.line_types.remove(name);
|
||||
}
|
||||
// Apply removals (mutable). Count what actually went: an
|
||||
// entry the table cannot remove by name would otherwise be
|
||||
// listed again on every pass and never let the loop end.
|
||||
let doc = &mut self.tabs[i].scene.document;
|
||||
let layers_gone = layer_remove
|
||||
.iter()
|
||||
.filter(|n| doc.layers.remove(n).is_some())
|
||||
.count();
|
||||
let styles_gone = style_remove
|
||||
.iter()
|
||||
.filter(|n| doc.text_styles.remove(n).is_some())
|
||||
.count();
|
||||
let lts_gone = lt_remove
|
||||
.iter()
|
||||
.filter(|n| doc.line_types.remove(n).is_some())
|
||||
.count();
|
||||
let mut blocks_gone = 0usize;
|
||||
for name in &block_remove {
|
||||
// Drop the block definition's member entities (and the
|
||||
// BLOCK/ENDBLK delimiters) before the record so no orphaned
|
||||
@@ -509,12 +538,23 @@ impl OpenCADStudio {
|
||||
for h in handles {
|
||||
self.tabs[i].scene.document.remove_entity(h);
|
||||
}
|
||||
self.tabs[i].scene.document.block_records.remove(name);
|
||||
if self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.block_records
|
||||
.remove(name)
|
||||
.is_some()
|
||||
{
|
||||
blocks_gone += 1;
|
||||
}
|
||||
}
|
||||
n_layers += layers_gone;
|
||||
n_styles += styles_gone;
|
||||
n_lts += lts_gone;
|
||||
n_blocks += blocks_gone;
|
||||
if layers_gone + styles_gone + lts_gone + blocks_gone == 0 {
|
||||
break;
|
||||
}
|
||||
n_layers += layer_remove.len();
|
||||
n_styles += style_remove.len();
|
||||
n_lts += lt_remove.len();
|
||||
n_blocks += block_remove.len();
|
||||
}
|
||||
|
||||
// Remove draw-order tables whose owning blocks are gone.
|
||||
@@ -1246,6 +1286,7 @@ impl OpenCADStudio {
|
||||
| "DGNOSNAP"
|
||||
| "UOSNAP"
|
||||
| "FIELDDISPLAY"
|
||||
| "SURFACEASSOCIATIVITY"
|
||||
| "PDFIMPORTMODE"
|
||||
| "PDFIMPORTFILTER"
|
||||
| "PDFIMPORTLAYERS"
|
||||
@@ -1484,6 +1525,36 @@ impl OpenCADStudio {
|
||||
}
|
||||
return Some(self.finish_dispatch(cmd));
|
||||
}
|
||||
if name == "SURFACEASSOCIATIVITY" {
|
||||
use crate::scene::model::sweep_model;
|
||||
let current = i16::from(sweep_model::surface_associativity());
|
||||
match value
|
||||
.as_deref()
|
||||
.map(|v| v.trim().parse::<i16>().ok().filter(|v| (0..=1).contains(v)))
|
||||
{
|
||||
Some(Some(mode)) => {
|
||||
if current != mode {
|
||||
sweep_model::set_surface_associativity(mode == 1);
|
||||
self.save_config();
|
||||
}
|
||||
}
|
||||
Some(None) => {
|
||||
self.command_line
|
||||
.push_error(crate::t!("Requires 0 or 1 only.").as_ref());
|
||||
self.command_line.push_output(&format!(
|
||||
"Enter new value for SURFACEASSOCIATIVITY <{current}>:"
|
||||
));
|
||||
self.pending_setvar = Some(name.clone());
|
||||
}
|
||||
None => {
|
||||
self.command_line.push_output(&format!(
|
||||
"Enter new value for SURFACEASSOCIATIVITY <{current}>:"
|
||||
));
|
||||
self.pending_setvar = Some(name.clone());
|
||||
}
|
||||
}
|
||||
return Some(self.finish_dispatch(cmd));
|
||||
}
|
||||
if name == "FIELDDISPLAY" {
|
||||
let current = i16::from(crate::entities::field::display());
|
||||
match value
|
||||
@@ -3889,6 +3960,13 @@ fn rename_symbol(doc: &mut codec::CadDocument, ty: &str, old: &str, new: &str) -
|
||||
if doc.header.current_text_style_name.eq_ignore_ascii_case(old) {
|
||||
doc.header.current_text_style_name = new.to_string();
|
||||
}
|
||||
// Dimension styles name their text style; follow the rename (same
|
||||
// as rename_style_storage — see #42).
|
||||
for ds in doc.dim_styles.iter_mut() {
|
||||
if ds.dimtxsty.eq_ignore_ascii_case(old) {
|
||||
ds.dimtxsty = new.to_string();
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
"DIMSTYLE" => {
|
||||
|
||||
@@ -814,7 +814,7 @@ impl OpenCADStudio {
|
||||
vp.view_height,
|
||||
vp.height,
|
||||
),
|
||||
vp.status.is_on,
|
||||
vp.is_on(),
|
||||
vp.status.locked,
|
||||
))
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user