fix(render): close remaining OCS/WCS gaps for mirrored entities
Follow-up to the LINE/POINT WCS fix - the same bug class elsewhere: - 2D SOLID rendered its OCS corners raw in both the wire path and the shared fill builder (solid_hatch_model); SHAPE placed its marker at the raw OCS insertion. Both now map through the arbitrary axis. - Grips: central wrap in view/dispatch.rs shows planar entities' grips at their world position (OCS→WCS) and converts drags back (WCS→OCS via the new wcs_point_to_ocs) - per-entity grip code stays in raw field space, INSERT keeps handling its own OCS. - Pulls acadrust 81ebfee: OCS-aware bounding boxes, translate, transform and mirror for the ten planar entity types, so MOVE / ROTATE / MIRROR and culling work on mirrored entities too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
59a0f5239c
commit
87049967a2
6 changed files with 105 additions and 22 deletions
|
|
@ -1806,11 +1806,20 @@ impl Scene {
|
|||
// `hatch_model_from_dxf` uses. Casting the absolute WCS corner straight
|
||||
// to f32 costs ~0.06 units of resolution at UTM magnitudes (~1e6), so
|
||||
// the quad snapped to a grid and the fill drifted off its outline.
|
||||
// SOLID corners are stored in OCS (like ARC/LWPOLYLINE) — map through
|
||||
// the arbitrary axis so a mirrored solid (normal 0,0,-1) fills on the
|
||||
// correct side of the drawing.
|
||||
let n = (solid.normal.x, solid.normal.y, solid.normal.z);
|
||||
let w = |v: &acadrust::types::Vector3| -> [f64; 2] {
|
||||
let (x, y, _) =
|
||||
crate::scene::view::transform::ocs_point_to_wcs((v.x, v.y, v.z), n);
|
||||
[x, y]
|
||||
};
|
||||
let corners: [[f64; 2]; 4] = [
|
||||
[solid.first_corner.x, solid.first_corner.y],
|
||||
[solid.second_corner.x, solid.second_corner.y],
|
||||
[solid.fourth_corner.x, solid.fourth_corner.y],
|
||||
[solid.third_corner.x, solid.third_corner.y],
|
||||
w(&solid.first_corner),
|
||||
w(&solid.second_corner),
|
||||
w(&solid.fourth_corner),
|
||||
w(&solid.third_corner),
|
||||
];
|
||||
let mut min = [f64::INFINITY; 2];
|
||||
let mut max = [f64::NEG_INFINITY; 2];
|
||||
|
|
|
|||
|
|
@ -28,8 +28,41 @@ pub fn prop_current_vertex() -> usize {
|
|||
PROP_CURRENT_VERTEX.with(|c| c.get())
|
||||
}
|
||||
|
||||
/// Extrusion normal of a planar (OCS-stored) entity when it isn't the
|
||||
/// default +Z. `None` = the entity stores WCS coordinates (LINE, POINT,
|
||||
/// ELLIPSE, MTEXT, ...) or maps its own OCS internally (INSERT) — grips pass
|
||||
/// through untouched. Per-entity grip code works in raw field space, so the
|
||||
/// dispatch layer converts: display grips OCS→WCS, incoming edits WCS→OCS.
|
||||
fn planar_ocs_normal(entity: &EntityType) -> Option<(f64, f64, f64)> {
|
||||
let n = match entity {
|
||||
EntityType::Arc(e) => e.normal,
|
||||
EntityType::Circle(e) => e.normal,
|
||||
EntityType::LwPolyline(e) => e.normal,
|
||||
EntityType::Polyline2D(e) => e.normal,
|
||||
EntityType::Solid(e) => e.normal,
|
||||
EntityType::Text(e) => e.normal,
|
||||
EntityType::AttributeDefinition(e) => e.normal,
|
||||
EntityType::Hatch(e) => e.normal,
|
||||
EntityType::Shape(e) => e.normal,
|
||||
_ => return None,
|
||||
};
|
||||
if n.x == 0.0 && n.y == 0.0 && n.z == 1.0 {
|
||||
None
|
||||
} else {
|
||||
Some((n.x, n.y, n.z))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grips(entity: &EntityType) -> Vec<GripDef> {
|
||||
EntityTypeOps::grips(entity)
|
||||
let mut grips = EntityTypeOps::grips(entity);
|
||||
if let Some(n) = planar_ocs_normal(entity) {
|
||||
for g in &mut grips {
|
||||
let (x, y, z) =
|
||||
super::transform::ocs_point_to_wcs((g.world.x, g.world.y, g.world.z), n);
|
||||
g.world = glam::DVec3::new(x, y, z);
|
||||
}
|
||||
}
|
||||
grips
|
||||
}
|
||||
|
||||
pub fn properties_sectioned(
|
||||
|
|
@ -193,6 +226,21 @@ pub fn apply_geom_prop(entity: &mut EntityType, field: &str, value: &str) {
|
|||
}
|
||||
|
||||
pub fn apply_grip(entity: &mut EntityType, grip_id: usize, apply: crate::scene::model::object::GripApply) {
|
||||
use crate::scene::model::object::GripApply;
|
||||
// Grip drags arrive in world space; per-entity apply code writes raw
|
||||
// fields, which for planar entities live in OCS — convert first (the
|
||||
// pure-rotation map handles points and deltas alike).
|
||||
let apply = match (planar_ocs_normal(entity), apply) {
|
||||
(Some(n), GripApply::Absolute(p)) => {
|
||||
let (x, y, z) = super::transform::wcs_point_to_ocs((p.x, p.y, p.z), n);
|
||||
GripApply::Absolute(glam::DVec3::new(x, y, z))
|
||||
}
|
||||
(Some(n), GripApply::Translate(d)) => {
|
||||
let (x, y, z) = super::transform::wcs_point_to_ocs((d.x, d.y, d.z), n);
|
||||
GripApply::Translate(glam::DVec3::new(x, y, z))
|
||||
}
|
||||
(None, a) => a,
|
||||
};
|
||||
EntityTypeOps::apply_grip(entity, grip_id, apply);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -134,6 +134,22 @@ pub fn ocs_point_to_wcs(ocs: (f64, f64, f64), normal: (f64, f64, f64)) -> (f64,
|
|||
)
|
||||
}
|
||||
|
||||
/// Inverse of [`ocs_point_to_wcs`]: express a WCS point (or direction — the
|
||||
/// map is a pure rotation, so both use the same math) in the OCS frame of
|
||||
/// `normal`. Used when writing user edits made in world space (grip drags)
|
||||
/// back into a planar entity's OCS-stored coordinates.
|
||||
#[inline]
|
||||
pub fn wcs_point_to_ocs(wcs: (f64, f64, f64), normal: (f64, f64, f64)) -> (f64, f64, f64) {
|
||||
let (ax, ay) = ocs_axes(normal);
|
||||
let (wx, wy, wz) = wcs;
|
||||
let (nx, ny, nz) = normal;
|
||||
(
|
||||
wx * ax.0 + wy * ax.1 + wz * ax.2,
|
||||
wx * ay.0 + wy * ay.1 + wz * ay.2,
|
||||
wx * nx + wy * ny + wz * nz,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod mirror_delegation_tests {
|
||||
use super::*;
|
||||
|
|
|
|||
Loading…
Reference in a new issue