Skip to content

Fix Cartesian text clipping by iterative range-space extent convergence in _finalize()#224

Merged
tshead2 merged 3 commits intosandialabs:mainfrom
eaton-lab:cartesian-iter-finalize-clip
Mar 13, 2026
Merged

Fix Cartesian text clipping by iterative range-space extent convergence in _finalize()#224
tshead2 merged 3 commits intosandialabs:mainfrom
eaton-lab:cartesian-iter-finalize-clip

Conversation

@eaton-lab
Copy link
Contributor

Problem

Cartesian._finalize() had two related issues:

  1. Large text near plot boundaries could be clipped because range-space extent fitting was solved in a single pass.
  2. In some scenarios, overflow computation could attempt numpy.max() on empty extent arrays, raising:
    ValueError: zero-size array to reduction operation maximum which has no identity.

What this PR changes

  • Refactors Cartesian finalization into:
    • _finalize_once() (original single-pass finalize logic),
    • _finalize() wrapper that iteratively refines extent fitting.
  • Uses screen-space overflow as convergence criterion.
  • Adds private convergence bounds:
    • max_iter = 6
    • px_tol = 0.05
  • Corrects y-axis overflow orientation handling in screen coordinates:
    • _ymin_range is top, _ymax_range is bottom.
  • Adds safe overflow reduction logic for empty extent arrays (no zero-size numpy.max()).
  • Keeps unreachable-fit behavior bounded: when text cannot fit in drawable range, iteration stops at max_iter and allows remaining overflow.

Why this is safe

  • No public API changes.
  • Behavior changes are internal to Cartesian finalization.
  • Existing rendering semantics are preserved outside bug-fix behavior.

Convergence constants (private)

  • _CARTESIAN_FINALIZE_MAX_ITER = 6
  • _CARTESIAN_FINALIZE_PX_TOL = 0.05

Tests

Adds regression coverage in features/cartesian-coordinates.feature and corresponding steps:

  • Unreachable text extent uses bounded fallback iterations
    • constructs an intentionally unreachable-fit text case,
    • asserts finalize passes are bounded (<= max_iter),
    • asserts remaining overflow is still above tolerance (expected).

This codifies expected fallback behavior for geometrically impossible fit cases.

Validation

  • Clipping repro for large boundary text is resolved by iterative finalize.
  • Prior mass-error path from empty extent reductions is removed.
  • Cartesian rendering scenarios no longer fail due to zero-size overflow reductions.

Commits

  • d5023b79 Add regression scenario for unreachable cartesian text extents
  • 7dfb935f Fix cartesian finalize text extent convergence and overflow bounds

Example

import toyplot
import toyplot.html
import numpy as np

rng = np.random.default_rng(123)
canvas = toyplot.canvas.Canvas(width=400, height=400)
axes = canvas.cartesian()
axes.scatterplot(rng.random(100), rng.random(100))
mark = axes.text(
    5,
    5,
    "hello world",
    color="black",
    style={"text-anchor": "start", "font-size": 28},
    annotation=False,
)
toyplot.html.render(canvas)

(xdat, _), (_, right, _, _) = mark.extents(["x", "y"])
overflow = float((axes.project("x", xdat) + right)[0] - axes._xmax_range)
print("right-overflow-px:", overflow)
# right-overflow-px: 0.04236587863653085
canvas
image

@eaton-lab
Copy link
Contributor Author

Note: all tests passed when run locally.

@coveralls
Copy link

Coverage Status

coverage: 94.148% (+0.03%) from 94.119%
when pulling dc2ef04 on eaton-lab:cartesian-iter-finalize-clip
into f71796a on sandialabs:main.

@eaton-lab
Copy link
Contributor Author

I believe these errors are unrelated to this PR. I raised separate PRs for them:

#226 ReportLab / Python 3.8 compatibility issue.

#225 : numpy v1/2 compatibility error during toyplot.graph

@tshead2 tshead2 merged commit 5b60bc0 into sandialabs:main Mar 13, 2026
7 of 10 checks passed
@tshead2
Copy link
Member

tshead2 commented Mar 13, 2026

This looks like a winner, thanks for your contribution!

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.

3 participants