forked from 1b-agent/opencadstudio-upstream
fix(solid3d): read cone radius from the major axis
The cone-surface record's trailing real is a parameter scale, not geometry. It equals the radius on many bodies, so cylinders generally came out right, but where the two diverge the lateral surface was built at the wrong size while the flat caps stayed on their own rims -- a gauge dial drew as a ring around its own face, its band at 43.2 against caps at 34.56. The major axis is the radius vector at the reference cross-section, so its length is the radius. Route every read through one helper and keep the record's field only as a fallback for a degenerate major axis, so the band, the silhouette generators, the isolines and the coaxial-rim check all size themselves the same way. Taper is unchanged: the major axis is still the h=0 radius that radius + h*tan(a) extends. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
01d8b87db1
commit
2a1a8f4b94
2 files changed
+30
-12
No files matched your search
@@ -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();
|
||||
|
||||
|
||||
@@ -398,7 +398,7 @@ fn cone_face_geom(sat: &SatDocument, face: &SatFace) -> Option<ConeFaceGeom> {
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user