Merge main into codex/graphic-attributes-cleanup

Conflicts were additions on both sides, kept together: the Count palette
and Sheet Set toggles next to the Graphic Attributes toggle in the ribbon,
main's new locale groups and catalog entries next to graphic-attributes,
and module declarations. The palette code follows main's crate renames
(acadrust -> codec, cadkernel -> kernel).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VgkJN8DEojuPTumAXLHotu
This commit is contained in:
Claude committed 2026-10-07 20:11:05 +00:00
commit 7a03e1c43c
625 files changed
+104397 -28053

No files matched your search

+267 -99
View File
@@ -5,8 +5,8 @@ pub struct CreateBlockOptions {
pub name: String,
pub handles: Vec<Handle>,
pub base_point: glam::DVec3,
pub world_to_block: acadrust::types::Transform,
pub block_to_world: acadrust::types::Transform,
pub world_to_block: codec::types::Transform,
pub block_to_world: codec::types::Transform,
pub mode: crate::ui::window::block_definition::BlockObjectMode,
pub annotative: bool,
pub match_orientation: bool,
@@ -86,7 +86,7 @@ pub(super) fn chain_path_edges_with_directions(
}
fn family_from_stored_line(
ln: &acadrust::entities::hatch::HatchPatternLine,
ln: &codec::entities::hatch::HatchPatternLine,
) -> crate::scene::model::hatch_model::PatFamily {
let (ca, sa) = (ln.angle.cos(), ln.angle.sin());
let dx = ln.offset.x * ca + ln.offset.y * sa;
@@ -143,27 +143,30 @@ impl Scene {
/// layer 0. Auto-registering keeps it on its own layer (#252). Names are
/// registered verbatim so the writer's (case-insensitive) lookup matches;
/// the always-present default layer "0" and empty names are no-ops.
/// Flags `layer_table_dirty` so the app refreshes its layer lists.
pub fn ensure_layer(&mut self, name: &str) {
if name.trim().is_empty() || self.document.layers.contains(name) {
return;
}
let mut layer = acadrust::tables::Layer::new(name);
let mut layer = codec::tables::Layer::new(name);
layer.handle = self.document.allocate_handle();
let _ = self.document.layers.add(layer);
self.invalidate_layer_dependencies(&[name.to_string()]);
self.layer_table_dirty = true;
}
pub(super) fn ensure_app_id(&mut self, name: &str) {
pub(crate) fn ensure_app_id(&mut self, name: &str) {
if name.trim().is_empty() || self.document.app_ids.contains(name) {
return;
}
let mut app_id = acadrust::tables::AppId::new(name);
let mut app_id = codec::tables::AppId::new(name);
app_id.handle = self.document.allocate_handle();
let _ = self.document.app_ids.add(app_id);
}
fn prepare_section_objects(&mut self, entity: &mut EntityType) -> bool {
use acadrust::entities::ExtendedEntityData;
use acadrust::objects::{
use codec::entities::ExtendedEntityData;
use codec::objects::{
ClassObject, ClassObjectData, SectionManager, SectionSettings, SectionTypeSettings,
};
@@ -319,7 +322,7 @@ impl Scene {
let facet_res = self.document.header.facet_resolution;
let chordal_deflection =
crate::entities::solid3d::display_deflection(&self.document.header, facet_res);
let isolines = self.document.header.isolines.max(0) as usize;
let isolines = crate::entities::solid3d::clamp_header_isolines(self.document.header.isolines);
let mesh_seed = if matches!(
&entity,
EntityType::Solid3D(_)
@@ -343,28 +346,7 @@ impl Scene {
None
};
// Auto-create an ImageDefinition object for new RasterImage entities
// that don't already reference one.
if let EntityType::RasterImage(ref mut img) = entity {
if img.definition_handle.is_none() {
use acadrust::objects::{ImageDefinition, ObjectType};
let def_handle = Handle::new(self.document.next_handle());
if self.is_recording_undo() {
self.record_undo_object_before(def_handle, None);
}
let mut img_def = ImageDefinition::with_dimensions(
&img.file_path,
img.size.x as u32,
img.size.y as u32,
);
img_def.handle = def_handle;
img_def.is_loaded = true;
self.document
.objects
.insert(def_handle, ObjectType::ImageDefinition(img_def));
img.definition_handle = Some(def_handle);
}
}
self.ensure_image_definition(&mut entity);
// Register the entity's layer if it names one no LAYER command created
// (e.g. a plugin-supplied layer) so it survives a DWG save instead of
@@ -535,7 +517,53 @@ impl Scene {
/// [`add_entity`](Self::add_entity) used to commit a plugin's edit of an
/// existing entity.
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
pub fn update_entity(&mut self, mut entity: EntityType) -> bool {
pub fn update_entity(&mut self, entity: EntityType) -> bool {
let mut changed = Vec::with_capacity(1);
let mut block_change = false;
if !self.update_entity_impl(entity, &mut changed, &mut block_change) {
return false;
}
self.publish_entity_updates(changed, block_change);
true
}
/// Batched variant of [`update_entity`]: writes every replacement, then
/// issues a single geometry bump for the whole set instead of one bump per
/// entity.
pub fn update_entities(&mut self, updates: Vec<EntityType>) -> usize {
let mut moved = 0usize;
let mut changed = Vec::with_capacity(updates.len());
let mut block_change = false;
for entity in updates {
if self.update_entity_impl(entity, &mut changed, &mut block_change) {
moved += 1;
}
}
if !changed.is_empty() || block_change {
self.publish_entity_updates(changed, block_change);
}
moved
}
/// One geometry bump for a set of slot writes already reflected in
/// `changed` / `block_change`.
fn publish_entity_updates(&mut self, changed: Vec<(Handle, ChangeKind)>, block_change: bool) {
if block_change {
self.bump_geometry();
} else {
self.bump_entities(&changed);
}
}
/// Shared write path for `update_entity` / `update_entities`: validates,
/// records undo, writes the slot, and reseeds derived caches, recording
/// the change instead of bumping geometry.
fn update_entity_impl(
&mut self,
mut entity: EntityType,
changed: &mut Vec<(Handle, ChangeKind)>,
block_change: &mut bool,
) -> bool {
let handle = entity.common().handle;
if self.is_layer_locked(handle) {
return false;
@@ -578,7 +606,7 @@ impl Scene {
let facet_res = self.document.header.facet_resolution;
let chordal_deflection =
crate::entities::solid3d::display_deflection(&self.document.header, facet_res);
let isolines = self.document.header.isolines.max(0) as usize;
let isolines = crate::entities::solid3d::clamp_header_isolines(self.document.header.isolines);
let mesh_seed = if matches!(
&entity,
EntityType::Solid3D(_)
@@ -658,12 +686,12 @@ impl Scene {
if affects_blocks {
self.mark_entity_dirty(handle);
self.bump_geometry();
*block_change = true;
} else {
// One entity changed in place: report just this handle so every
// derived cache patches it instead of rebuilding (bump_entities also
// drops it from the tessellation memos).
self.bump_entities(&[(handle, ChangeKind::Modified)]);
changed.push((handle, ChangeKind::Modified));
}
true
}
@@ -673,6 +701,25 @@ impl Scene {
/// or drop them all if the handle is now absent. Mirrors the reseed block in
/// [`Scene::update_entity`]; used by delta-undo when it re-applies an
/// entity's before / after image so the fills and meshes follow.
/// Rebuild every underlay's raster and wire, after a setting they draw
/// or snap by (PDFOSNAP / UOSNAP) changed.
pub(crate) fn reseed_underlays(&mut self) {
let handles: Vec<Handle> = self
.document
.entities()
.filter(|entity| matches!(entity, EntityType::Underlay(_)))
.map(|entity| entity.common().handle)
.collect();
for handle in &handles {
self.reseed_derived_caches(*handle);
}
let changes: Vec<_> = handles
.iter()
.map(|handle| (*handle, crate::scene::ChangeKind::Modified))
.collect();
self.bump_entities(&changes);
}
pub(crate) fn reseed_derived_caches(&mut self, handle: Handle) {
let (hatch_seed, image_seed) = match self.document.get_entity(handle) {
None => (None, None),
@@ -705,15 +752,16 @@ impl Scene {
}
pub fn restore_solid_models(&mut self, handles: &[Handle]) {
let bodies: Vec<(Handle, cadkernel::brep::Body)> = handles
let bodies: Vec<(Handle, kernel::brep::Body)> = handles
.iter()
.filter(|handle| !self.solid_models.contains_key(handle))
.filter_map(|&handle| {
let from_history = self
.document
.solid_history_operations(handle)
.and_then(|operations| cadkernel::acis::rebuild_history(&operations).ok());
let body = from_history.or_else(|| match self.document.get_entity(handle) {
let tree = self.document.solid_history_tree(handle);
let rebuilt = || {
tree.as_ref()
.and_then(|tree| kernel::acis::rebuild_history_tree(tree).ok())
};
let stored = || match self.document.get_entity(handle) {
Some(EntityType::Solid3D(solid)) => {
crate::scene::convert::solid3d_tess::kernel_body(solid)
}
@@ -724,7 +772,14 @@ impl Scene {
crate::scene::convert::solid3d_tess::kernel_surface_body(surface)
}
_ => None,
})?;
};
// A composite's saved body is what its booleans produced;
// replaying them is for edits, not for reading the file.
let body = if tree.as_ref().is_some_and(|tree| tree.has_boolean()) {
stored().or_else(rebuilt)
} else {
rebuilt().or_else(stored)
}?;
Some((handle, body))
})
.collect();
@@ -771,7 +826,7 @@ impl Scene {
.objects
.values()
.filter_map(|o| match o {
acadrust::objects::ObjectType::Layout(l) if !l.block_record.is_null() => {
codec::objects::ObjectType::Layout(l) if !l.block_record.is_null() => {
Some(l.block_record)
}
_ => None,
@@ -788,7 +843,7 @@ impl Scene {
let facet_res = self.document.header.facet_resolution;
let chordal_deflection =
crate::entities::solid3d::display_deflection(&self.document.header, facet_res);
let isolines = self.document.header.isolines.max(0) as usize;
let isolines = crate::entities::solid3d::clamp_header_isolines(self.document.header.isolines);
use crate::par::prelude::*;
let built: Vec<(Handle, MeshLodSet, bool)> = entries
.into_par_iter()
@@ -842,7 +897,7 @@ impl Scene {
let layer_entry = self.document.layers.get(layer);
let color = layer_entry
.map(|l| &l.color)
.unwrap_or(&acadrust::types::Color::WHITE);
.unwrap_or(&codec::types::Color::WHITE);
let [r, g, b, _] = crate::scene::convert::tess_util::aci_to_rgba(color);
let alpha = layer_entry
.map(|layer| 1.0 - layer.transparency.as_percent() as f32)
@@ -863,8 +918,8 @@ impl Scene {
&mut self,
handles: &[Handle],
name: &str,
world_to_block: &acadrust::types::Transform,
block_to_world: &acadrust::types::Transform,
world_to_block: &codec::types::Transform,
block_to_world: &codec::types::Transform,
) -> Result<Handle, String> {
self.create_block_with_options(CreateBlockOptions {
name: name.to_string(),
@@ -932,7 +987,7 @@ impl Scene {
b.description = options.description.clone();
}
if options.annotative {
let annotative_vals = vec![acadrust::xdata::XDataValue::Integer16(1)];
let annotative_vals = vec![codec::xdata::XDataValue::Integer16(1)];
view::dispatch::set_entity_xdata(
&mut self.document,
existing_blk_h,
@@ -941,7 +996,7 @@ impl Scene {
);
}
if !options.hyperlink_url.is_empty() {
let link_vals = vec![acadrust::xdata::XDataValue::String(
let link_vals = vec![codec::xdata::XDataValue::String(
options.hyperlink_url.clone(),
)];
view::dispatch::set_entity_xdata(
@@ -958,7 +1013,7 @@ impl Scene {
let block_handle = Handle::new(next + 1);
let end_handle = Handle::new(next + 2);
let mut block_record = acadrust::tables::BlockRecord::new(name);
let mut block_record = codec::tables::BlockRecord::new(name);
block_record.handle = br_handle;
block_record.block_entity_handle = block_handle;
block_record.block_end_handle = end_handle;
@@ -971,7 +1026,7 @@ impl Scene {
.add(block_record)
.map_err(|e| e.to_string())?;
let mut block = Block::new(name, acadrust::types::Vector3::ZERO);
let mut block = Block::new(name, codec::types::Vector3::ZERO);
block.common.handle = block_handle;
block.common.owner_handle = br_handle;
block.description = options.description.clone();
@@ -987,7 +1042,7 @@ impl Scene {
.map_err(|e| e.to_string())?;
if options.annotative {
let annotative_vals = vec![acadrust::xdata::XDataValue::Integer16(1)];
let annotative_vals = vec![codec::xdata::XDataValue::Integer16(1)];
view::dispatch::set_entity_xdata(
&mut self.document,
block_handle,
@@ -996,7 +1051,7 @@ impl Scene {
);
}
if !options.hyperlink_url.is_empty() {
let link_vals = vec![acadrust::xdata::XDataValue::String(
let link_vals = vec![codec::xdata::XDataValue::String(
options.hyperlink_url.clone(),
)];
view::dispatch::set_entity_xdata(
@@ -1034,8 +1089,8 @@ impl Scene {
let mut insert_handle = Handle::NULL;
if options.mode == crate::ui::window::block_definition::BlockObjectMode::Convert {
let mut insert = DxfInsert::new(name, acadrust::types::Vector3::ZERO);
acadrust::Entity::apply_transform(&mut insert, &options.block_to_world);
let mut insert = DxfInsert::new(name, codec::types::Vector3::ZERO);
codec::Entity::apply_transform(&mut insert, &options.block_to_world);
insert_handle = self.add_entity(EntityType::Insert(insert));
}
@@ -1050,6 +1105,32 @@ impl Scene {
/// caller starts an interactive insert so paste-as-block can prompt for the
/// drop point. The geometry comes from the clipboard rather than live
/// entities, so there is nothing to stage or erase. (#129)
/// Give a new RasterImage the ImageDefinition object it must reference,
/// unless it already names one.
fn ensure_image_definition(&mut self, entity: &mut EntityType) {
let EntityType::RasterImage(img) = entity else {
return;
};
if img.definition_handle.is_some() {
return;
}
use codec::objects::{ImageDefinition, ObjectType};
// Allocate, not peek: a peeked handle is then handed to the image
// itself, and the DWG writer drops the definition that shares it. (#1421)
let def_handle = self.document.allocate_handle();
if self.is_recording_undo() {
self.record_undo_object_before(def_handle, None);
}
let mut img_def =
ImageDefinition::with_dimensions(&img.file_path, img.size.x as u32, img.size.y as u32);
img_def.handle = def_handle;
img_def.is_loaded = true;
self.document
.objects
.insert(def_handle, ObjectType::ImageDefinition(img_def));
img.definition_handle = Some(def_handle);
}
pub fn define_block_from_owned_entities(
&mut self,
entities: Vec<EntityType>,
@@ -1075,7 +1156,7 @@ impl Scene {
let block_handle = Handle::new(next + 1);
let end_handle = Handle::new(next + 2);
let mut block_record = acadrust::tables::BlockRecord::new(name);
let mut block_record = codec::tables::BlockRecord::new(name);
block_record.handle = br_handle;
block_record.block_entity_handle = block_handle;
block_record.block_end_handle = end_handle;
@@ -1084,7 +1165,7 @@ impl Scene {
.add(block_record)
.map_err(|e| e.to_string())?;
let mut block = Block::new(name, acadrust::types::Vector3::ZERO);
let mut block = Block::new(name, codec::types::Vector3::ZERO);
block.common.handle = block_handle;
block.common.owner_handle = br_handle;
self.document
@@ -1106,6 +1187,7 @@ impl Scene {
Self::reset_clone_subhandles(&mut self.document, &mut entity);
entity.common_mut().handle = Handle::NULL;
entity.common_mut().owner_handle = br_handle;
self.ensure_image_definition(&mut entity);
let handle = self
.document
.add_entity(entity)
@@ -1125,7 +1207,7 @@ impl Scene {
pub fn define_block_raw(
&mut self,
name: &str,
base_point: acadrust::types::Vector3,
base_point: codec::types::Vector3,
entities: Vec<EntityType>,
) {
if name.is_empty() || self.document.block_records.get(name).is_some() {
@@ -1136,7 +1218,7 @@ impl Scene {
let block_handle = Handle::new(next + 1);
let end_handle = Handle::new(next + 2);
let mut block_record = acadrust::tables::BlockRecord::new(name);
let mut block_record = codec::tables::BlockRecord::new(name);
block_record.handle = br_handle;
block_record.block_entity_handle = block_handle;
block_record.block_end_handle = end_handle;
@@ -1163,6 +1245,46 @@ impl Scene {
self.bump_geometry();
}
/// Replace the contents and base point of block `name`, defining it when
/// absent. References keep pointing at it and show the new contents.
pub fn redefine_block_raw(
&mut self,
name: &str,
base_point: codec::types::Vector3,
entities: Vec<EntityType>,
) {
let Some((br_handle, block_handle)) = self
.document
.block_records
.get(name)
.map(|br| (br.handle, br.block_entity_handle))
else {
return self.define_block_raw(name, base_point, entities);
};
let owned: Vec<Handle> = self
.document
.entities()
.filter(|e| {
e.common().owner_handle == br_handle
&& !matches!(e, EntityType::Block(_) | EntityType::BlockEnd(_))
})
.map(|e| e.common().handle)
.collect();
for h in owned {
self.document.remove_entity(h);
}
if let Some(EntityType::Block(block)) = self.document.get_entity_mut(block_handle) {
block.base_point = base_point;
}
for mut entity in entities {
Self::reset_clone_subhandles(&mut self.document, &mut entity);
entity.common_mut().handle = Handle::NULL;
entity.common_mut().owner_handle = br_handle;
let _ = self.document.add_entity(entity);
}
self.bump_geometry();
}
pub(super) fn synced_hatch_models(
&self,
target_block: Handle,
@@ -1285,11 +1407,11 @@ impl Scene {
// normal style chain instead of the raw ACI table
// (#415).
let (bg_color, bg_aci) = match bg {
acadrust::types::Color::ByLayer => {
codec::types::Color::ByLayer => {
let layer = self.document.layers.get(&dxf.common.layer);
let aci = layer
.and_then(|layer| match &layer.color {
acadrust::types::Color::Index(index) => Some(*index),
codec::types::Color::Index(index) => Some(*index),
_ => None,
})
.unwrap_or(0);
@@ -1303,10 +1425,10 @@ impl Scene {
aci,
)
}
acadrust::types::Color::ByBlock => (style.0, style.4),
acadrust::types::Color::Index(index) => (
codec::types::Color::ByBlock => (style.0, style.4),
codec::types::Color::Index(index) => (
crate::scene::convert::tess_util::aci_to_rgba(
&acadrust::types::Color::Index(index),
&codec::types::Color::Index(index),
),
index,
),
@@ -1339,6 +1461,10 @@ impl Scene {
}
}
}
m.color = self.count_color(handle, m.color, self.current_bg());
if let Some(b) = &mut backdrop {
b.color = self.count_color(handle, b.color, self.current_bg());
}
if tint_selected && self.selected.contains(&handle) {
m.color = [0.15, 0.55, 1.00, m.color[3]];
}
@@ -1576,7 +1702,7 @@ impl Scene {
}
let style = context.style_for(&self.document, entity);
let preserve_white_mask = source_hatch.is_solid
&& matches!(source_hatch.common.color, acadrust::types::Color::Index(7));
&& matches!(source_hatch.common.color, codec::types::Color::Index(7));
let color = if preserve_white_mask {
style.0
} else {
@@ -1610,6 +1736,37 @@ impl Scene {
}
model.boundary = std::sync::Arc::new(clipped);
}
if !context.clips.is_empty() {
// The plane-local outline is a second copy of the boundary
// that viewport plotting prefers; rebuild it from the
// clipped outline or the XCLIP is lost there. (#1508)
model.fill_plane_boundary = model.fill_plane.map(|plane| {
let origin = glam::DVec3::from_array(plane.origin);
let (x_axis, y_axis) = (
glam::DVec3::from_array(plane.x_axis),
glam::DVec3::from_array(plane.y_axis),
);
std::sync::Arc::new(
model
.boundary
.iter()
.map(|&[x, y]| {
if x.is_nan() || y.is_nan() {
return [f32::NAN, f32::NAN];
}
let world = glam::DVec3::new(
model.world_origin[0] + x as f64,
model.world_origin[1] + y as f64,
origin.z,
);
let local = world - origin;
[local.dot(x_axis) as f32, local.dot(y_axis) as f32]
})
.collect(),
)
});
}
model.color = self.count_color(context.root_handle, model.color, hatch_bg);
if tint_selected && self.selected.contains(&context.root_handle) {
model.color = [0.15, 0.55, 1.00, model.color[3]];
}
@@ -1793,7 +1950,7 @@ impl Scene {
boundary_exterior: None,
boundary_sources: None,
boundary_paths: None,
style: acadrust::entities::HatchStyleType::Normal,
style: codec::entities::HatchStyleType::Normal,
pattern: model::hatch_model::HatchPattern::Solid,
name: "WIPEOUT_FILL".into(),
color,
@@ -1813,10 +1970,10 @@ impl Scene {
/// Wipeout fill boundary as small f32 offsets from the returned world_origin
/// (the insertion point, kept in f64).
pub(super) fn wipeout_boundary_2d(
wo: &acadrust::entities::Wipeout,
wo: &codec::entities::Wipeout,
) -> ([f64; 2], Vec<[f32; 2]>) {
let origin = [wo.insertion_point.x, wo.insertion_point.y];
let plane = cadkernel::space::Plane::from_axes(
let plane = kernel::space::Plane::from_axes(
[
wo.insertion_point.x,
wo.insertion_point.y,
@@ -1839,8 +1996,8 @@ impl Scene {
(origin, boundary)
}
fn wipeout_local_boundary(wo: &acadrust::entities::Wipeout) -> Vec<[f64; 2]> {
use acadrust::entities::{WipeoutClipMode, WipeoutClipType};
fn wipeout_local_boundary(wo: &codec::entities::Wipeout) -> Vec<[f64; 2]> {
use codec::entities::{WipeoutClipMode, WipeoutClipType};
let mut outer = vec![
[0.0, 0.0],
[wo.size.x, 0.0],
@@ -1872,7 +2029,7 @@ impl Scene {
}
fn wipeout_fill_plane(
wipeout: &acadrust::entities::Wipeout,
wipeout: &codec::entities::Wipeout,
) -> Option<(model::hatch_model::FillPlane, Vec<[f32; 2]>)> {
let origin = [
wipeout.insertion_point.x,
@@ -1881,7 +2038,7 @@ impl Scene {
];
let x_axis = [wipeout.u_vector.x, wipeout.u_vector.y, wipeout.u_vector.z];
let y_axis = [wipeout.v_vector.x, wipeout.v_vector.y, wipeout.v_vector.z];
cadkernel::space::Plane::from_axes(origin, x_axis, y_axis).normal()?;
kernel::space::Plane::from_axes(origin, x_axis, y_axis).normal()?;
let boundary = Self::wipeout_local_boundary(wipeout)
.into_iter()
.map(|point| [point[0] as f32, point[1] as f32])
@@ -1901,7 +2058,7 @@ impl Scene {
world_origin: [f64; 2],
boundary: &[[f32; 2]],
) -> Option<Vec<[f32; 2]>> {
let plane = cadkernel::space::Plane::from_axes(plane.origin, plane.x_axis, plane.y_axis);
let plane = kernel::space::Plane::from_axes(plane.origin, plane.x_axis, plane.y_axis);
boundary
.iter()
.map(|point| {
@@ -1950,7 +2107,7 @@ impl Scene {
let mut edge_polys: Vec<Vec<[f64; 2]>> = Vec::new();
for edge in &path.edges {
if let Some(curve) = crate::entities::hatch::edge_curve(edge) {
edge_polys.push(curve.tessellate_angle(cadkernel::tessellation::DEFAULT_ANGLE));
edge_polys.push(curve.tessellate_angle(kernel::tessellation::DEFAULT_ANGLE));
}
}
let mut local_ring = chain_path_edges(edge_polys);
@@ -1977,7 +2134,7 @@ impl Scene {
return None;
}
let depths = cadkernel::geom2d::ring_nesting_depths(&rings);
let depths = kernel::geom2d::ring_nesting_depths(&rings);
let mut boundary = Vec::new();
let mut local_boundary = Vec::new();
let mut boundary_exterior = Vec::new();
@@ -1990,9 +2147,9 @@ impl Scene {
.zip(depths)
{
let keep = match dxf.style {
acadrust::entities::HatchStyleType::Normal => true,
acadrust::entities::HatchStyleType::Outer => depth <= 1,
acadrust::entities::HatchStyleType::Ignore => depth == 0,
codec::entities::HatchStyleType::Normal => true,
codec::entities::HatchStyleType::Outer => depth <= 1,
codec::entities::HatchStyleType::Ignore => depth == 0,
};
if !keep {
continue;
@@ -2005,7 +2162,7 @@ impl Scene {
boundary.extend(ring);
local_boundary.extend(local_ring.iter().map(|&[x, y]| [x as f32, y as f32]));
if path.edges.iter().any(|edge| {
matches!(edge, acadrust::entities::BoundaryEdge::Spline(_))
matches!(edge, codec::entities::BoundaryEdge::Spline(_))
}) {
spline_paths.push((path, local_ring, range));
}
@@ -2090,7 +2247,7 @@ impl Scene {
entry.gpu.clone()
} else if matches!(
dxf.pattern_type,
acadrust::entities::hatch::HatchPatternType::UserDefined
codec::entities::hatch::HatchPatternType::UserDefined
) {
// User-defined hatch: parallel lines at `pattern_angle`, spaced
// `pattern_scale` apart, plus a perpendicular set when
@@ -2220,7 +2377,7 @@ impl Scene {
}
let curves = || path.edges.iter().filter_map(crate::entities::hatch::edge_curve).collect();
if let Some(refined) =
cadkernel::geom2d::refine_spline_boundary(&ring, curves, &project)
kernel::geom2d::refine_spline_boundary(&ring, curves, &project)
{
if boundary.len() - range.len() + refined.len() > MAX_HATCH_MODEL_VERTS {
continue;
@@ -2288,7 +2445,7 @@ impl Scene {
fn populate_images_from_document_unbumped(&mut self) {
self.images.clear();
let entries: Vec<(Handle, acadrust::entities::EntityType)> = self
let entries: Vec<(Handle, codec::entities::EntityType)> = self
.document
.entities()
.filter(|e| {
@@ -2384,14 +2541,21 @@ impl Scene {
/// ImageModel for an image-bearing entity. `None` when it cannot decode.
pub(super) fn image_seed_for(
&self,
entity: &acadrust::entities::EntityType,
entity: &codec::entities::EntityType,
) -> Option<ImageModel> {
match entity {
EntityType::RasterImage(img) => ImageModel::from_raster_image(img),
EntityType::Ole2Frame(ole) => ImageModel::from_ole2frame(ole),
EntityType::Underlay(u) => match self.document.objects.get(&u.definition_handle) {
Some(acadrust::objects::ObjectType::UnderlayDefinition(def)) => {
ImageModel::from_underlay(u, def)
Some(codec::objects::ObjectType::UnderlayDefinition(def)) => {
// A paper-space underlay adjusts to the sheet, not the canvas.
let owner = u.common.owner_handle;
let background = if owner.is_null() || owner == self.model_space_block_handle() {
self.bg_color
} else {
self.paper_bg_color
};
ImageModel::from_underlay(u, def, background, self.underlay_world_per_pixel)
}
_ => None,
},
@@ -2421,6 +2585,10 @@ impl Scene {
if !incremental {
self.meshes.clear();
self.block_meshes.clear();
// The B-rep cache follows the entities too: an undo that rebuilds
// everything left the undone fillet's body here, and the next
// edge operation brought it back (#1107). Restored on demand.
self.solid_models.clear();
}
// BLOCK-entity handles of the layout (model + paper) blocks. A solid
// owned by one of these is top-level; anything else lives in a block
@@ -2430,7 +2598,7 @@ impl Scene {
.objects
.values()
.filter_map(|o| match o {
acadrust::objects::ObjectType::Layout(l) if !l.block_record.is_null() => {
codec::objects::ObjectType::Layout(l) if !l.block_record.is_null() => {
Some(l.block_record)
}
_ => None,
@@ -2472,7 +2640,7 @@ impl Scene {
let facet_res = self.document.header.facet_resolution;
let chordal_deflection =
crate::entities::solid3d::display_deflection(&self.document.header, facet_res);
let isolines = self.document.header.isolines.max(0) as usize;
let isolines = crate::entities::solid3d::clamp_header_isolines(self.document.header.isolines);
// Top-level solids: offset into the render frame, drawn flat.
// Block-definition solids: keep block-local coords for per-INSERT
// instancing (no offset applied here).
@@ -2585,7 +2753,7 @@ impl Scene {
boundary_exterior: None,
boundary_sources: None,
boundary_paths: None,
style: acadrust::entities::HatchStyleType::Normal,
style: codec::entities::HatchStyleType::Normal,
pattern: model::hatch_model::HatchPattern::Solid,
name: "SOLID".into(),
color,
@@ -2602,7 +2770,7 @@ impl Scene {
&mut self,
model: HatchModel,
layer: Option<&str>,
entity_style: Option<(acadrust::types::Color, acadrust::types::Transparency)>,
entity_style: Option<(codec::types::Color, codec::types::Transparency)>,
) -> Handle {
let mut dxf = DxfHatch::new();
dxf.style = model.style;
@@ -2610,7 +2778,7 @@ impl Scene {
let x = glam::DVec3::from_array(plane.x_axis);
let y = glam::DVec3::from_array(plane.y_axis);
let normal = x.cross(y).normalize_or(glam::DVec3::Z);
dxf.normal = acadrust::types::Vector3::new(normal.x, normal.y, normal.z);
dxf.normal = codec::types::Vector3::new(normal.x, normal.y, normal.z);
dxf.elevation = glam::DVec3::from_array(plane.origin).dot(normal);
}
dxf.is_solid = matches!(
@@ -2660,11 +2828,11 @@ impl Scene {
.collect();
let mut bits = 0;
if is_outer {
bits |= acadrust::entities::hatch::BoundaryPathFlags::OUTERMOST.bits();
bits |= acadrust::entities::hatch::BoundaryPathFlags::EXTERNAL.bits();
bits |= codec::entities::hatch::BoundaryPathFlags::OUTERMOST.bits();
bits |= codec::entities::hatch::BoundaryPathFlags::EXTERNAL.bits();
}
let mut path = BoundaryPath::with_flags(
acadrust::entities::hatch::BoundaryPathFlags::from_bits(bits),
codec::entities::hatch::BoundaryPathFlags::from_bits(bits),
);
path.add_edge(BoundaryEdge::Polyline(edge));
for handle in handles {
@@ -2718,7 +2886,7 @@ impl Scene {
.map(|point| [point[0] as f64, point[1] as f64])
.unwrap_or(model.world_origin);
if let crate::scene::model::hatch_model::HatchPattern::Pattern(families) = &model.pattern {
let mut pattern = acadrust::entities::HatchPattern::new(&model.name);
let mut pattern = codec::entities::HatchPattern::new(&model.name);
let rotation = model.angle_offset as f64;
let (rotation_sin, rotation_cos) = rotation.sin_cos();
for family in families {
@@ -2729,7 +2897,7 @@ impl Scene {
let local_offset_y = family.dx as f64 * family_sin + family.dy as f64 * family_cos;
let base_x = family.x0 as f64 * pattern_scale;
let base_y = family.y0 as f64 * pattern_scale;
pattern.lines.push(acadrust::entities::HatchPatternLine {
pattern.lines.push(codec::entities::HatchPatternLine {
angle,
base_point: Vector2::new(
pattern_origin[0] + base_x * rotation_cos - base_y * rotation_sin,
@@ -2764,7 +2932,7 @@ impl Scene {
shift,
} = &model.pattern
{
let to_color = |c: [f32; 4]| acadrust::types::Color::Rgb {
let to_color = |c: [f32; 4]| codec::types::Color::Rgb {
r: (c[0] * 255.0).round().clamp(0.0, 255.0) as u8,
g: (c[1] * 255.0).round().clamp(0.0, 255.0) as u8,
b: (c[2] * 255.0).round().clamp(0.0, 255.0) as u8,
@@ -2787,11 +2955,11 @@ impl Scene {
(model.color, *color2)
};
dxf.gradient_color.colors = vec![
acadrust::entities::hatch::GradientColorEntry {
codec::entities::hatch::GradientColorEntry {
value: 0.0,
color: to_color(c0),
},
acadrust::entities::hatch::GradientColorEntry {
codec::entities::hatch::GradientColorEntry {
value: 1.0,
color: to_color(c1),
},
@@ -2871,7 +3039,7 @@ impl Scene {
// a DWG writes the current style by handle only.
self.document.header.current_dimstyle_handle = iso;
for obj in self.document.objects.values_mut() {
if let acadrust::objects::ObjectType::Layout(l) = obj {
if let codec::objects::ObjectType::Layout(l) = obj {
if l.name != "Model" {
crate::scene::apply_default_page_setup(l, "");
}