Record a region profile as its boundary curve and step

A swept, extruded or revolved surface made from a region records the
region as the curve round it, as the reference does: a lone circle or
whole ellipse as itself, a loop of lines and arcs as a closed polyline in
the region's plane. A region recorded as a region made the reference
refuse to open the drawing (modeling error 58000).

A region read by an associative action carries the step its edge names
belong to (ACAD_STEPID), so the reference finds the edges again and the
surface follows the region.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-10-09 18:54:52 +03:00
1 parent 65910d1cad
commit c227de26c5
3 files changed
+97 -1

No files matched your search

+5
View File
@@ -2851,6 +2851,11 @@ fn source_reads(
};
let sizes = loops.iter().map(Vec::len).collect::<Vec<_>>();
let ids = register_region_subents(document, &sizes)?;
// The region carries the step its edge names belong to; without it (and
// without named edges in its modeler data) the reference cannot find
// the edges again.
crate::scene::view::dispatch::set_entity_xdata(document, source, "ACAD_STEPID",
Some(vec![codec::xdata::XDataValue::Integer32(ids.first()? - 1)]));
let curves = loops.into_iter().flatten().collect::<Vec<_>>();
let pairs = curves.iter().map(|_| (document.allocate_handle(), document.allocate_handle())).collect::<Vec<_>>();
for (index, (curve, (edge, read))) in curves.into_iter().zip(pairs.iter().copied()).enumerate() {
+4 -1
View File
@@ -12,7 +12,10 @@ use super::sweep_model::{embedded_path, embedded_revolve_profile};
/// A 3D polyline profile or path as the reference records it: a wire body.
fn embedded_sweep_profile(entity: &EntityType) -> Option<(codec::entities::EmbeddedEntity, [f64; 16])> {
match entity {
EntityType::Region(region) => Some((codec::entities::EmbeddedEntity::Region(region.clone()), glam::DMat4::IDENTITY.to_cols_array())),
// A region as the curve round it, as the reference records it.
EntityType::Region(region) => super::sweep_model::region_boundary_entity(entity).or_else(|| {
Some((codec::entities::EmbeddedEntity::Region(region.clone()), glam::DMat4::IDENTITY.to_cols_array()))
}),
EntityType::Polyline3D(value) => Some((polyline_wire(value)?, glam::DMat4::IDENTITY.to_cols_array())),
_ => embedded_revolve_profile(entity),
}
+88
View File
@@ -662,10 +662,98 @@ pub fn embedded_path(entity: &EntityType) -> Option<EmbeddedEntity> {
}
}
/// The frame placing a plane's coordinates in the world: its axes, normal
/// and origin as matrix columns.
fn plane_frame(plane: &Plane) -> Option<[f64; 16]> {
let normal = plane.normal()?;
let column = |v: [f64; 3], w: f64| glam::DVec4::new(v[0], v[1], v[2], w);
Some(glam::DMat4::from_cols(
column(plane.x_axis, 0.0),
column(plane.y_axis, 0.0),
column(normal, 0.0),
column(plane.origin, 1.0),
)
.to_cols_array())
}
/// A region of one boundary loop as the curve the reference records for it
/// as a profile: a lone circle or whole ellipse as itself, a loop of lines
/// and arcs as a closed polyline in the region's plane (with that plane's
/// frame), read from the region's modeler edges.
pub fn region_boundary_entity(entity: &EntityType) -> Option<(EmbeddedEntity, [f64; 16])> {
let EntityType::Region(region) = entity else { return None };
if !region.acis_data.has_data() {
return None;
}
let (plane, loops, _) = super::presspull_model::profile_geometry(entity)?;
let [boundary] = loops.as_slice() else { return None };
let world = |v: [f64; 3]| Vector3::new(v[0], v[1], v[2]);
let normal = world(plane.normal()?);
let identity = glam::DMat4::IDENTITY.to_cols_array();
match boundary.as_slice() {
[Curve::Circle(circle)] => {
let mut value = codec::entities::Circle::new();
value.center = world(plane.point_at(circle.centre));
value.radius = circle.radius;
value.normal = normal;
return Some((EmbeddedEntity::Circle(value), identity));
}
[Curve::Ellipse(arc)] if (arc.sweep() - std::f64::consts::TAU).abs() <= 1e-9 => {
let ellipse = arc.ellipse;
let centre = Vec3::from(plane.point_at(ellipse.centre));
let tip = Vec3::from(plane.point_at([
ellipse.centre[0] + ellipse.major_axis[0] * ellipse.major_radius,
ellipse.centre[1] + ellipse.major_axis[1] * ellipse.major_radius,
]));
let mut value = codec::entities::Ellipse::new();
value.center = world(centre.to_array());
value.major_axis = world((tip - centre).to_array());
value.minor_axis_ratio = ellipse.minor_radius / ellipse.major_radius;
value.start_parameter = arc.start_parameter;
value.end_parameter = arc.start_parameter + std::f64::consts::TAU;
value.normal = normal;
return Some((EmbeddedEntity::Ellipse(value), identity));
}
_ => {}
}
// Walk the loop head to tail; an arc run against its direction turns
// clockwise (negative bulge).
let close = |a: [f64; 2], b: [f64; 2]| (a[0] - b[0]).hypot(a[1] - b[1]) <= 1e-9 * (1.0 + a[0].abs().max(a[1].abs()));
let ends = |piece: &Curve| match piece {
Curve::Line(line) => Some((line.start, line.end, 0.0)),
Curve::Arc(arc) => {
let at = |angle: f64| [arc.centre[0] + arc.radius * angle.cos(), arc.centre[1] + arc.radius * angle.sin()];
Some((at(arc.start_angle), at(arc.end_angle), (arc.sweep() / 4.0).tan()))
}
_ => None,
};
let (first_start, first_end, _) = ends(boundary.first()?)?;
let second = boundary.get(1).and_then(ends);
let mut at = if second.is_some_and(|(start, end, _)| close(first_start, start) || close(first_start, end)) {
first_end
} else {
first_start
};
let mut polyline = LwPolyline::new();
for piece in boundary {
let (start, end, bulge) = ends(piece)?;
let (from, to, bulge) = if close(start, at) { (start, end, bulge) } else if close(end, at) { (end, start, -bulge) } else { return None };
let mut vertex = LwVertex::new(Vector2::new(from[0], from[1]));
vertex.bulge = bulge;
polyline.vertices.push(vertex);
at = to;
}
polyline.is_closed = true;
Some((EmbeddedEntity::LwPolyline(polyline), plane_frame(&plane)?))
}
fn embedded_planar_entity(entity: &EntityType) -> Option<(EmbeddedEntity, [f64; 16])> {
if region_with_holes(entity) {
return None;
}
if let Some(found) = region_boundary_entity(entity) {
return Some(found);
}
if !matches!(entity, EntityType::Polyline3D(_)) {
if let Some(embedded) = embedded_path(entity) {
return Some((embedded, glam::DMat4::IDENTITY.to_cols_array()));