Files
psxrecomp/runtime/include/ws_ui_group.h
T
tetrisgm df41ea6b02 auto_ui_squash: group HUD primitives that are one visual element
The thirds anchor is chosen per union-find run, and runs only joined when the
group key matched within JOIN_GAP. That key folds CLUT, texpage, a 24px Y band
and the poly-vs-rect family together, so a digit and the box it is drawn on
could never land in the same run. They got independent anchors and were
squashed about different points, which pulls one piece of a readout toward a
screen edge and leaves the rest behind -- worse the wider the frame.

On WipEout 3's race HUD at 32:9 the lap readout draws its digits at
y=[219,227] x=[137,202] and the box they label at y=[228,244] x=[138,202].
The digits anchored left, the box anchored centre, and the box landed about
500 screen pixels from the digits it belongs to.

Join on being one visual element as well as on the key: share screen columns
and touch vertically within STACK_GAP. Both halves are needed. Column overlap
alone merged the top-left lap counter with the bottom-left lap timer 200
scanlines below, and since union is transitive that chained all 71 HUD
primitives into one run spanning the full width, which anchors centre and
drags the corners inward. A strict row intersection missed the stacked case
above, whose seam is one pixel.

WsUiGroupItem carries y/height for this; the anchor stays horizontal. After:
9 runs, bottom-left readout whole and anchored left, timer and energy bar
right, speed centre -- matching the 4:3 layout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 72196d3dc9)
2026-08-27 21:17:20 -07:00

55 lines
2.0 KiB
C

#ifndef PSXRECOMP_WS_UI_GROUP_H
#define PSXRECOMP_WS_UI_GROUP_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define WS_UI_GROUP_JOIN_GAP 24
/* Vertical slack, in scanlines, still counted as "stacked on" rather than
* "somewhere else on screen". A readout drawn as a label directly above its
* box leaves a one-pixel seam; the nearest thing that must NOT join is the
* other end of the screen, ~200 scanlines away. */
#define WS_UI_GROUP_STACK_GAP 4
typedef struct {
uint32_t key;
int32_t x;
int32_t width;
/* Vertical extent, needed to tell "drawn on top of each other" (one visual
* element) from "happens to share screen columns" (the top-left lap counter
* and the bottom-left lap timer). Grouping only ever produces a HORIZONTAL
* anchor, but deciding the grouping is a 2-D question. */
int32_t y;
int32_t height;
int32_t anchor;
/* Diagnostic only: index of the union-find representative this item
* merged into, so an observer can tell "these two share an anchor because
* they are one run" from "these two happen to land on the same third".
* Anchor equality cannot distinguish those — there are only three anchor
* values — and that distinction is the whole question when a HUD element
* splits apart under the squash. Written by ws_ui_group_assign; never read
* by the transform path. */
uint32_t root;
} WsUiGroupItem;
/* Assign one thirds anchor to each complete spatial run. Items with the same
* texture/row key join when their horizontal intervals touch or are separated
* by at most WS_UI_GROUP_JOIN_GAP pixels. This is intentionally a whole-frame
* operation: animated text is coherent on its first frame, not one frame late. */
void ws_ui_group_assign(WsUiGroupItem *items, size_t count,
int32_t display_width, int dense_menu);
int32_t ws_ui_anchor_for_bounds(int32_t x, int32_t width,
int32_t display_width);
#ifdef __cplusplus
}
#endif
#endif