forked from 1b-agent/opencadstudio-upstream
fix(solid3d): trim sphere caps and pierced cone legs
Sphere faces drew as complete balls. ACIS parametrises a sphere about any pole, so a vessel's dished head is commonly bounded by a circle that is no line of latitude in that frame, and a theta/phi bounding box of such a boundary covers nearly the whole ball. Derive the window analytically instead: when a face's loops are circles lying on the sphere, re-pole the frame onto their common axis, where each seam is an exact latitude at acos(d / R). One ring bounds a cap, two bound a zone; the material side comes from the boundary winding, reusing the helper the torus path already had. Separately, the longitude wrap flag was resetting the colatitude window to the whole meridian. A cap centred on the pole covers every longitude while still ending at its seam, so that reset grew every cap back into a ball. Trim the two angles independently and rename the flag theta_full so the split reads at each use, including the silhouette generator, which skipped the colatitude test the same way. Cone faces took their height span from the first boundary loop alone. A lateral face closed off by one rim and one pierce curve -- a pipe branch meeting its run -- carries its two ends on different loops, so the tube was sized to whichever came first and the leg above the junction went undrawn. Read every loop. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8f009d0058
commit
01d8b87db1
2 files changed
+244
-43
No files matched your search
@@ -2390,14 +2390,20 @@ impl Pipeline {
|
||||
let a = std::f64::consts::TAU * (i as f64 / N as f64);
|
||||
let dir = e1 * a.cos() + e2 * a.sin();
|
||||
// Keep only the arc that lies on the actual face.
|
||||
let on_face = *full || {
|
||||
let phi = dir.dot(pole).clamp(-1.0, 1.0).acos();
|
||||
// `full` is a *longitude* wrap flag: a dish cap sits
|
||||
// on the pole and so covers every longitude while
|
||||
// still ending at its seam, so the colatitude test
|
||||
// always applies. A whole ball reports phi 0..π and
|
||||
// passes it regardless.
|
||||
let phi = dir.dot(pole).clamp(-1.0, 1.0).acos();
|
||||
let in_phi =
|
||||
phi >= *phi_min as f64 && phi <= *phi_max as f64;
|
||||
let in_theta = *full || {
|
||||
let th = dir.dot(v).atan2(dir.dot(u));
|
||||
let toff = (th - *theta_min as f64).rem_euclid(std::f64::consts::TAU);
|
||||
phi >= *phi_min as f64
|
||||
&& phi <= *phi_max as f64
|
||||
&& (toff <= *theta_span as f64)
|
||||
toff <= *theta_span as f64
|
||||
};
|
||||
let on_face = in_phi && in_theta;
|
||||
let p = if on_face { Some(c + dir * r) } else { None };
|
||||
if let (Some(a), Some(b)) = (prev, p) {
|
||||
verts.push(mk(a));
|
||||
|
||||
Reference in new issue
Block a user