diff --git a/src/scene/convert/acis_to_truck.rs b/src/scene/convert/acis_to_truck.rs index 3e7f85c3d..173cc6666 100644 --- a/src/scene/convert/acis_to_truck.rs +++ b/src/scene/convert/acis_to_truck.rs @@ -30,9 +30,9 @@ use acadrust::entities::acis::{ }; use crate::scene::convert::solid3d_tess::{ - body_transform, collect_face_loops, cone_axis_span, finalize_mesh, tess_cone_face, - tess_plane_face, tess_sphere_face, tess_torus_face, LodConfig, BOUNDARY_CHORD_FRAC, - TRUCK_CHORD_FRAC, + body_transform, collect_face_loops, cone_axis_span, cone_radius, finalize_mesh, + tess_cone_face, tess_plane_face, tess_sphere_face, tess_torus_face, LodConfig, + BOUNDARY_CHORD_FRAC, TRUCK_CHORD_FRAC, }; use crate::scene::model::mesh_model::MeshLodSet; use rustc_hash::FxHashMap; @@ -268,7 +268,7 @@ fn build_face_group( } "cone-surface" => { let cone = SatConeSurface::from_record(surf_rec)?; - let tol = curve_tol(cone.radius()); + let tol = curve_tol(cone_radius(&cone)); let (faces, out) = cone_faces(sat, face, &cone)?; Some((faces, out, tol)) } @@ -385,7 +385,7 @@ fn cone_faces( let (cx, cy, cz) = cone.center(); let (ax, ay, az) = cone.axis(); let (ux, uy, uz) = cone.major_axis(); - let radius = cone.radius(); + let radius = cone_radius(cone); let sin = cone.sin_half_angle(); let cos = cone.cos_half_angle(); diff --git a/src/scene/convert/solid3d_tess.rs b/src/scene/convert/solid3d_tess.rs index 1d4781a26..2246ac2f3 100644 --- a/src/scene/convert/solid3d_tess.rs +++ b/src/scene/convert/solid3d_tess.rs @@ -398,7 +398,7 @@ fn cone_face_geom(sat: &SatDocument, face: &SatFace) -> Option { let (cx, cy, cz) = cone.center(); let (ax, ay, az) = cone.axis(); let (ux, uy, uz) = cone.major_axis(); - let radius = cone.radius(); + let radius = cone_radius(&cone); let sin_a = cone.sin_half_angle(); let cos_a = cone.cos_half_angle(); let axis = norm3([ax, ay, az]); @@ -802,6 +802,24 @@ fn sphere_cap_window( } } +/// Reference cross-section radius of a cone/cylinder surface. +/// +/// The record's trailing real is a parameter scale, not geometry — it coincides +/// with the radius on many bodies but diverges on others, which draws the +/// lateral surface at the wrong size while the rims stay put (a gauge dial +/// rendering as a ring around its own face). The major axis *is* the radius +/// vector at the reference cross-section, so its length is the radius; fall back +/// to the record's field only when that vector is missing. +pub(crate) fn cone_radius(cone: &SatConeSurface) -> f64 { + let (x, y, z) = cone.major_axis(); + let len = (x * x + y * y + z * z).sqrt(); + if len > 1e-12 { + len + } else { + cone.radius() + } +} + /// Any unit vector square to `axis`, for completing a frame. fn perp_axis(axis: [f64; 3]) -> [f64; 3] { let seed = if axis[0].abs() < 0.9 { @@ -2058,7 +2076,7 @@ pub(crate) fn tess_cone_face( let (cx, cy, cz) = cone.center(); let (ax, ay, az) = cone.axis(); // axis direction (unit) let (ux, uy, uz) = cone.major_axis(); // u=0 direction - let radius = cone.radius(); + let radius = cone_radius(cone); let sin_a = cone.sin_half_angle(); let cos_a = cone.cos_half_angle(); // ≈1 for cylinder, <1 for cone @@ -2328,7 +2346,7 @@ pub(crate) fn cone_axis_span( let major = e.major_axis(); let curve_radius = dot3([major.0, major.1, major.2], [major.0, major.1, major.2]) .sqrt(); - let expected_radius = (cone.radius() + h * tangent).abs(); + let expected_radius = (cone_radius(cone) + h * tangent).abs(); let scale = curve_radius.max(expected_radius).max(1.0); if radial_len < scale * 1e-6 && n_dot > 0.999 @@ -2355,8 +2373,8 @@ pub(crate) fn cone_axis_span( d[2] - h * axis[2], ]; let radial_len = dot3(radial, radial).sqrt(); - let expected = (cone.radius() + h * tangent).abs(); - let tolerance = expected.max(cone.radius().abs()).max(1.0) * 1e-5; + let expected = (cone_radius(cone) + h * tangent).abs(); + let tolerance = expected.max(cone_radius(cone).abs()).max(1.0) * 1e-5; if (radial_len - expected).abs() <= tolerance { heights.push(h); } @@ -2383,7 +2401,7 @@ pub(crate) fn cone_axis_span( return Some((anchor.min(mate), anchor.max(mate))); } if sin_a.abs() > 1e-6 { - let apex = -cone.radius() * cos_a / sin_a; + let apex = -cone_radius(cone) * cos_a / sin_a; if (apex - anchor).abs() >= 1e-9 { return Some((anchor.min(apex), anchor.max(apex))); } @@ -2396,7 +2414,7 @@ pub(crate) fn cone_axis_span( // True cone with a single rim: close the surface at its apex (r = 0). if sin_a.abs() > 1e-6 && heights.len() <= 1 { - let apex = -cone.radius() * cos_a / sin_a; + let apex = -cone_radius(cone) * cos_a / sin_a; h_min = h_min.min(apex); h_max = h_max.max(apex); }