Complete LOFT commands, surfaces, previews and editable history

This commit is contained in:
ramox81 committed 2026-09-04 13:03:33 +03:00
1 parent 8e3d851133
commit 479bf87988
15 files changed
+1505 -161

No files matched your search

+151 -56
View File
@@ -54,10 +54,84 @@ pub(crate) use boundary::{
// other tessellation code); re-exported here so this root and sibling topic
// modules (each does `use super::*`) keep referencing them unqualified.
pub(crate) use convert::tess::{
entity_aabb, entity_world_aabb_f64, is_unindexable_entity, tessellate_entity,
entity_aabb, entity_world_aabb_f64, is_unindexable_entity,
tessellate_entity_dim_text,
};
/// Keep construction-history curves in the same per-entity tessellation memo
/// as their parent, so navigation never rebuilds them on every frame.
#[allow(clippy::too_many_arguments)]
pub(crate) fn tessellate_entity(
document: &CadDocument,
selected: &HashSet<Handle>,
active_viewport: Option<Handle>,
bg_color: [f32; 4],
anno_scale: f32,
annotation_scale_handle: Option<Handle>,
entity: &EntityType,
block_cache: Option<&cache::block_cache::BlockCache>,
view_aabb: Option<[f32; 4]>,
world_per_pixel: Option<f32>,
paper_space: bool,
) -> Vec<WireModel> {
let mut wires = convert::tess::tessellate_entity(
document, selected, active_viewport, bg_color, anno_scale,
annotation_scale_handle, entity, block_cache, view_aabb,
world_per_pixel, paper_space,
);
if matches!(entity, EntityType::Solid3D(_) | EntityType::Surface(_)) {
let handle = entity.common().handle;
for source in model::solid_history::loft_visible_history_entities(document, handle) {
// Call the raw converter, not this wrapper: sources must not expand
// the parent's history again through their shared selection handle.
let mut construction = if let EntityType::Region(region) = &source {
// Regions normally render through the mesh layer, so the raw
// wire converter emits nothing. Decode every exact boundary,
// including holes, then sample only for this cached display.
let mut boundaries = Vec::new();
if let Ok(cadkernel::brep::LoftSection::Profile { plane, wires, .. }) =
cadkernel::acis::loft_section_geometry(&[
acadrust::entities::EmbeddedEntity::Region(region.clone()),
])
{
let (color, _, _, line_weight_px, _) =
view::render::render_style_for_viewport(document, &source, active_viewport);
for boundary in wires {
for curve in boundary {
let points = curve.tessellate(64.0 / std::f64::consts::TAU)
.into_iter().map(|point| plane.point_at(point));
let (points, points_low) = convert::tessellate::points_to_ds(points);
let mut wire = WireModel::solid(
handle.value().to_string(), points, color, selected.contains(&handle),
);
wire.points_low = points_low;
wire.line_weight_px = line_weight_px;
boundaries.push(wire);
}
}
}
boundaries
} else {
convert::tess::tessellate_entity(
document, selected, active_viewport, bg_color, anno_scale,
annotation_scale_handle, &source, block_cache, view_aabb,
world_per_pixel, paper_space,
)
};
for wire in &mut construction {
wire.name = handle.value().to_string();
if !wire.selected {
wire.color = [1.0, 0.25, 0.25, wire.color[3]];
}
wire.fill_tris.clear();
wire.fill_tris_low.clear();
}
wires.extend(construction);
}
}
wires
}
/// Result of `Scene::entity_index()`. The wire path queries `tree` for
/// view-rect candidates and also always emits `unbounded_handles`
/// (entities with no usable bbox — legacy `UNBOUNDED_AABB` sentinel).
@@ -2731,12 +2805,12 @@ impl Scene {
})
}
/// Cache and display a Model-tab kernel solid.
pub fn register_solid_model(
&mut self,
/// Prepare display geometry without changing the entity or resident caches.
pub(crate) fn prepare_solid_model_display(
&self,
handle: Handle,
solid: cadkernel::brep::Body,
) -> bool {
solid: &cadkernel::brep::Body,
) -> Option<(MeshLodSet, Vec<acadrust::entities::Wire>, [f64; 3])> {
let color = self
.document
.get_entity(handle)
@@ -2746,67 +2820,88 @@ impl Scene {
let chordal_deflection =
crate::entities::solid3d::display_deflection(&self.document.header, facet_resolution);
let isolines = self.document.header.isolines.max(0) as usize;
let displayed = if let Some((mut set, wires, center)) =
let (mut set, wires, center) =
crate::scene::model::solid_model::display_from_solid(
&solid,
solid,
color,
facet_resolution,
chordal_deflection,
isolines,
)?;
let name = handle.value().to_string();
for mesh in &mut set.lods {
mesh.name = name.clone();
}
if let Some(entity) = self.document.get_entity(handle) {
crate::scene::model::material_model::resolve_material_with_base(
&self.document,
entity,
color,
None,
self.material_base_dir.as_deref(),
)
{
if let Some(entity) = self.document.get_entity_mut(handle) {
let reference = acadrust::types::Vector3::new(center[0], center[1], center[2]);
match entity {
EntityType::Solid3D(entity) => {
entity.point_of_reference = reference;
entity.wires = wires;
entity.silhouettes.clear();
}
EntityType::Surface(entity) => {
entity.point_of_reference = reference;
entity.wires = wires;
entity.silhouettes.clear();
}
_ => {}
.apply_to_with_face_overrides(
&mut set,
&self.document,
self.material_base_dir.as_deref(),
);
crate::scene::model::visual_style_model::apply_mesh_visual_style(
&mut set,
&self.document,
entity,
);
}
Some((set, wires, center))
}
/// Commit a prepared display without tessellating the body a second time.
pub(crate) fn register_prepared_solid_model(
&mut self,
handle: Handle,
solid: cadkernel::brep::Body,
display: (MeshLodSet, Vec<acadrust::entities::Wire>, [f64; 3]),
) -> bool {
let (set, wires, center) = display;
if let Some(entity) = self.document.get_entity_mut(handle) {
let reference = acadrust::types::Vector3::new(center[0], center[1], center[2]);
match entity {
EntityType::Solid3D(entity) => {
entity.point_of_reference = reference;
entity.wires = wires;
entity.silhouettes.clear();
}
EntityType::Surface(entity) => {
entity.point_of_reference = reference;
entity.wires = wires;
entity.silhouettes.clear();
}
_ => {}
}
let name = handle.value().to_string();
for mesh in &mut set.lods {
mesh.name = name.clone();
}
if let Some(entity) = self.document.get_entity(handle) {
crate::scene::model::material_model::resolve_material_with_base(
&self.document,
entity,
color,
None,
self.material_base_dir.as_deref(),
)
.apply_to_with_face_overrides(
&mut set,
&self.document,
self.material_base_dir.as_deref(),
);
crate::scene::model::visual_style_model::apply_mesh_visual_style(
&mut set,
&self.document,
entity,
);
}
self.meshes.insert(handle, set);
true
} else {
self.meshes.remove(&handle);
false
};
}
self.meshes.insert(handle, set);
self.solid_models.insert(handle, solid);
self.sync_solid_reference_point(handle);
// Only this solid's mesh changed — report just its handle so the mesh /
// wire caches patch it in rather than rebuilding the whole drawing (and
// so a bulk register loop stays O(n), not O(n²)).
self.bump_entities(&[(handle, ChangeKind::Modified)]);
displayed
true
}
/// Cache and display a Model-tab kernel solid. A failed preparation leaves
/// the previous entity, body and mesh untouched.
pub fn register_solid_model(
&mut self,
handle: Handle,
solid: cadkernel::brep::Body,
) -> bool {
let Some(display) = self.prepare_solid_model_display(handle, &solid) else {
return false;
};
if !display.0.complete && matches!(
self.document.solid_history_operation(handle),
Some(acadrust::objects::SolidHistoryOperation::Loft(_))
) {
return false;
}
self.register_prepared_solid_model(handle, solid, display)
}
/// `BACKGROUND` change picks up the new `adapt_to_bg` result without