FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Axis: Fit and centre rotated wrapped tick labels; `tickTextOverlapTolerance` and `tickTextMaxLines` options by rokotyan · Pull Request #131 · ExaForce/unovis · GitHub

Repository navigation

Axis: Fit and centre rotated wrapped tick labels; tickTextOverlapTolerance and tickTextMaxLines options - #131

Draft
rokotyan wants to merge 19 commits into
mainfrom
fix/axis
Draft

rokotyan wants to merge 19 commits into
mainfrom
fix/axis

Conversation

rokotyan commented Sep 21, 2026 •
edited
Loading

Copy link
Copy Markdown

Mirror of the upstream PR f5#927

Fixes the adaptive tick fitting for rotated, wrapped X axis labels, and adds two related tick label options.

Rotated labels (fix)

A categorical chart with 10 long labels at tickTextAngle: -90 and tickTextAdaptiveSets labeled only 5 of them, off-centre. Two causes, both from treating every label as horizontal:

  • The wrap budget was the tick's horizontal slot. Rotated to -90°, a label's on-screen width is its line count times the line height, so denser sets shrank the wrap width, added lines and made every label wider — the fitting rejected the very set it was making room for. The budget now follows the angle and stops shrinking with the label count as the text turns across the axis. Unchanged at 0°.
  • A wrapped block was anchored on its first line, sliding a rotated block sideways by half its extra height. The block is now centred on the tick, with the predicted geometry matching the render.

Rotated labels are compared in their own frame in both the fitting and the overlap pass, instead of via screen bounding boxes that overlap long before steep labels do (hideOverlappingLabels gains a rotationAngle option).

New options

  • tickTextOverlapTolerance — the minimum gap between tick labels in pixels (0 by default). Replaces a hard-coded constant. Also fixes rectIntersect moving one edge by twice the tolerance, so a tolerance now means twice its gap rather than three times.
  • tickTextMaxLines — caps the lines a wrapped label can take; longer labels are trimmed to fit according to tickTextTrimType (start, middle or end). Backed by new maxLines / trimMode text options in getWrappedText.
Screen.Recording.2026-09-21.at.4.42.02.PM.mp4 Screen.Recording.2026-09-21.at.3.58.44.PM.mp4
  • Dev examples (Rotated Category Labels, Tick Label Max Lines)
  • Wrappers (Angular regenerated)
  • Gallery example (not applicable)
  • Docs (tickTextOverlapTolerance, tickTextMaxLines)
  • Specs (axis.spec.ts, text.spec.ts)

rokotyan and others added 12 commits September 23, 2026 14:40
…ice on one side

The far edges moved by twice the tolerance while the near edges moved by it, so two
rects collided within three times the tolerance instead of twice. `resolveRectsOverlap`
prunes by the same factor, and the heatmap keeps its 6px label gap with a `-3` tolerance.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…appingLabels`

Labels all rotated by the same angle can be passed as `rotationAngle`; they are then
measured with their own box offset by their position rotated into the labels' frame,
where they are axis-aligned. Their on-screen bounding boxes overlap long before the
labels do. Adds `getRotatedPoint` next to `getRotatedRectAabb`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The tick fitting treated every label as horizontal, with two consequences for
rotated X labels:

- The wrap budget was the tick's horizontal slot, `containerWidth / (labelCount + 1)`.
  Rotated to -90°, a label's on-screen width is its line count times the line height,
  so a denser set shrank the wrap width, added lines and made every label wider: the
  pass meant to make rotation viable is what rejected it. The budget is now the slot
  and the margin depth (a third of the container height) projected onto the text
  direction, so it stops shrinking with the label count as the text turns across the
  axis. Unchanged at 0°.
- A wrapped block was anchored on its first line, sliding a rotated block sideways by
  half its extra height. The anchor now centres the block on the tick and pushes it
  away from the axis by the block's extra depth; the two cancel out for horizontal
  labels under the axis.

Labels are compared in their own rotated frame in the fitting and the overlap pass
alike, instead of via screen bounding boxes that overlap long before steep labels do.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The minimum gap between tick labels in pixels, `0` by default. Labels closer than that
count as overlapping for `tickTextHideOverlapping` and `tickTextAdaptiveSets`; a negative
value lets them overlap by that much. Replaces the hard-coded 15px gap.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nction

`getRotatedTickTextMaxWidth` in `tick-fit.ts` takes plain numbers, so it can be tested
directly. The margin depth share moves to `AXIS_ROTATED_TICK_LABEL_MAX_DEPTH_SHARE` in a
new `constants.ts`. No behaviour change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`getWrappedText` and the text options take `maxLines` and `trimMode`. A block that breaks
into more lines, or has a word wider than the width, is trimmed from the start, the middle
or the end (see `trimString`) just enough for every line to fit, and never down to a bare
ellipsis. Lines are measured the way the line breaker measures them, font weight included.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Caps the lines a wrapped tick label can take; longer labels are trimmed to fit according
to `tickTextTrimType`, which now applies to wrapped labels as well as to `FitMode.Trim`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`getWrappedText` and the text options take `balance`: the text keeps the number of lines
it takes at the width, but breaks at the narrowest width still giving that many, so the
lines come out even (like CSS `text-wrap: balance`). Works with `maxLines` trimming, whose
helper now returns the trimmed text so it can be broken either way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Balances the lines of wrapped tick labels so they don't end with a short line. The label
keeps its number of lines, and the tick fitting measures the balanced labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`tickTextOverlapTolerance`, `tickTextMaxLines` and `tickTextWrapBalanced`. Also drops the note
about inaccurate overlap detection for rotated labels, which no longer applies.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es examples

`Rotated Category Labels`: ten long category labels with controls for the angle, the chart
width and the tick label options, and a live count of the labeled bars. `Tick Label Max
Lines`: one chart per trim type plus a short rotated chart, with `tickTextMaxLines` and
`tickTextWrapBalanced` controls.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
devgru and others added 7 commits October 2, 2026 10:48
`tickTextAdaptiveSets` now also accepts `'uniform'`; `true` keeps meaning
`'nice'`, the previous behavior. On time scales the "nice" d3 sets can be
uneven: their day intervals restart the count at every month boundary, so a
week-wide domain gets Jul 27, Jul 29, Jul 31, Aug 1 — a 2-2-1 day step whose
last label the usual multi-scale formats then print as a bare "Aug".

The uniform mode instead picks one calendar unit (the coarsest that still
provides `numTicks` boundaries), builds the grid of all of its boundaries
within the domain, and labels every k-th of them, searching k and the starting
phase for the densest placement that fits. On units with a natural cycle
(60 seconds / minutes, 24 hours, 12 months) k snaps to the cycle's divisors or
whole multiples and the phases prefer cycle-aligned positions, so the labels
land on familiar wall-clock steps. The remaining grid values render as
unlabeled tick marks, the way the nice sets already do.

The chosen unit reaches `tickFormat` as a new fourth argument, letting the
consumer format a whole set homogeneously instead of relabeling the tick that
happens to cross a month.

The search stays near-linear in the grid size: a single measuring pass over
the whole grid bounds k from below and warms the text measurement cache, so
every distinct label is measured once.
…the uniform tick search

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`preRender` renders the fitted ticks without the labeled subset, so the fitted flag was
false there: the margin was measured with the fair-share wrapping while the render gave
fitted labels the whole axis. Rotated labels then overflowed the chart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fitted labels only wrapped when wider than the whole axis. Rotated, that made them run
deep into the margin as single long lines, leaving the chart little room. Rotated fitted
labels now also wrap by the rotated labels' depth bound; unrotated ones are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in the `tickTextAdaptiveSets` doc comment about rotated fitted labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
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


Back | FazBrowse Home | New Git URL