Fuse & Order On-Screen Element Boxes
set_of_marks.mark_elements numbers a single, already-clean element list — but
nothing produces that list. A real screen parse yields three overlapping sources
(OCR text boxes, icon/shape boxes, accessibility boxes) with heavy duplication and no
consistent order. This module is the missing connective tissue between the locators
(locate_text, find_shapes, the a11y tree) and set_of_marks: de-duplicate
by overlap, union the sources keeping the most trustworthy box, and sort into reading
order with a stable index.
Every box is a plain dict with x, y, width, height (plus any extra keys such
as text / source / score), so the whole module is pure-stdlib and fully
unit-testable. Imports no PySide6.
Headless API
from je_auto_control import iou, merge_boxes, fuse_elements, reading_order
iou(box_a, box_b) # overlap of two boxes, 0..1
deduped = merge_boxes(raw_boxes, iou_threshold=0.9)
# Union three detector outputs; on overlap the a11y box wins, then OCR, then icon.
elements = fuse_elements(ocr_boxes=ocr, icon_boxes=icons, a11y_boxes=tree)
# Sort top-to-bottom, left-to-right and add an "index" to each.
for el in reading_order(elements):
print(el["index"], el.get("text"), el["x"], el["y"])
iou returns the intersection-over-union of two boxes. merge_boxes keeps the
largest of any cluster overlapping above iou_threshold. fuse_elements tags
each input with its source, then drops cross-source overlaps preferring
source_priority (default a11y > ocr > icon, then larger area).
reading_order bands rows within row_tol pixels, orders by x within each
row, and returns new dicts carrying a sequential index.
Executor commands
AC_fuse_elements (ocr / icon / a11y JSON arrays + iou_threshold
→ {count, elements}) and AC_reading_order (elements + row_tol →
{count, elements}). They are exposed as the MCP tools ac_fuse_elements /
ac_reading_order and as Script Builder commands under Image.