Skip to content

Foreshorten to the true perspective eye at view_distance / strength - #87

Merged
bjmorgan merged 4 commits into
mainfrom
feature/perspective-effective-eye
Aug 16, 2026
Merged

Foreshorten to the true perspective eye at view_distance / strength#87
bjmorgan merged 4 commits into
mainfrom
feature/perspective-effective-eye

Conversation

@bjmorgan

Copy link
Copy Markdown
Owner

A Perspective(strength=s, view_distance=D) places the pinhole eye at D / s, but two computations used D directly, so the perspective bulge never converged to the orthographic size as the strength fell. This corrects both, coupled in one commit because atom silhouettes and the bond caps meeting them must share one eye.

  • Silhouette radii now carry (r * s)^2 rather than r^2, measured from the eye at D / s. The denominator is factored as sqrt(d - rs) * sqrt(d + rs) so it stays finite for large view_distance, where the squared form d^2 - (rs)^2 overflows float64 and zeroes every radius. A sphere large enough to contain the eye now warns instead of ballooning silently.
  • Bond end caps foreshorten to the same eye (eye_distance = D / s), so a cap and the atom it meets stay consistent at strengths other than one.
  • Eye-plane warning hygiene: the message is now a constant, so CPython's per-message dedup collapses a viewing-distance keydrag to a single line; this also removes an assert isinstance downcast that only existed to interpolate the parameters.

Unlike the two preceding projection PRs this changes rendered output on purpose, and only under perspective — orthographic output is unchanged. At strength == 1 both corrections are no-ops ((r*1)^2 == r^2, D/1 == D).

Out of scope, each to follow separately: viewport magnification sizing, the look_along convention cleanup, and oblique projection.

A Perspective(strength=s, view_distance=D) places the pinhole eye at
D/s, but silhouette radii and the bond-cap eye distance used D directly,
so the perspective bulge never converged to the orthographic radius as
strength fell. Correct both together, since atom silhouettes and the
bond caps meeting them must share one eye:

- silhouette_radius: the exact silhouette for an eye at D/s carries
  (r*s)^2, not r^2. Computed as sqrt(d - rs) * sqrt(d + rs) so the
  denominator stays finite for large view_distance, where the squared
  form d^2 - (rs)^2 overflows float64 (past ~1.3e154) and zeroes every
  radius. The denominator floor of 1e-6 is the square root of the old
  1e-12 floor on d^2 - r^2, so an eye-inside-sphere still yields a huge
  finite radius rather than a division by zero, and now also warns.
- eye_distance: returns view_distance / strength.
Copilot AI lite review requested due to automatic review settings August 16, 2026 18:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Harden the tests around the perspective silhouette change:

- Pin a hand-computed silhouette radius at strength 0.5, where the
  corrected (r*s)^2 term diverges from the old r^2 form; the existing
  hand-computed assertion was at strength 1, where the two coincide.
- Assert the eye-inside-sphere case yields a huge but finite radius, so
  the 1e-6 denominator floor is guarded rather than only its warning.
- Add a dedup test for the sphere-contains-eye warning, matching the
  eye-plane one.

Also correct the _sqrt_difference_of_squares docstring: the naive
product overflows to inf, and it is the caller's division by that inf
that drives the silhouette to zero.
Copilot AI review requested due to automatic review settings August 16, 2026 19:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@bjmorgan
bjmorgan merged commit de9adcc into main Aug 16, 2026
5 checks passed
@bjmorgan
bjmorgan deleted the feature/perspective-effective-eye branch August 16, 2026 19:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants