Set-of-Marks Label Layout (No Overlap, Readable Colour)
Set-of-Marks overlays a numbered label on every element so a vision model can
say “click 7”. set_of_marks draws each label at a fixed offset, so on dense
UIs the numbers pile on top of each other (unreadable) and a dark label on a
dark element vanishes. marks_layout fixes both with pure geometry.
place_labels()— greedy non-overlap placement: for each mark, try a ring of candidate positions around its box (above, below, inside; left/right aligned) and take the first that stays in bounds and clears every already-placed label.label_color()— pick the label text colour (black or white) with the better WCAG contrast against the element’s background.
Pure standard library; reuses a11y_audit.contrast_ratio(). Fully testable
without rendering. Imports no PySide6.
Headless API
from je_auto_control import mark_elements, place_labels, label_color
marks = mark_elements(elements) # [{id, bbox, ...}]
layout = place_labels(marks, bounds=(1920, 1080))
# [{'id': 1, 'label': [x, y, 22, 16], 'anchor': [bx, by]}, ...]
label_color((30, 30, 30)) # {'rgb': [255, 255, 255], 'contrast': ...}
Feed the label boxes from place_labels() to your renderer instead of a
naive fixed offset, and pick each number’s colour with label_color() so it
stays legible on its background. place_labels is deterministic and ordered by
the input marks, so the same screen always numbers the same way.
Executor commands
AC_place_labels (marks JSON list + label_width / label_height /
bounds [w, h] → {labels}) and AC_label_color (background
[r, g, b] → {rgb, contrast}). They are the matching read-only ac_*
MCP tools and Script Builder commands under Image.