"""O etichetă de diagramă nu are voie să stea peste desen. `audit_diagrams.py` prinde doar text peste text. Astea sunt celelalte trei feluri de suprapunere, găsite vizual pe 02.10.2026 și ușor de regresat la următoarea mutare de etichetă: - `` — niveluri, laturi de wedge, bracket-uri, cozi de săgeată; - `` — mediile mobile (punctul orb al primei versiuni); - `` — corpurile candelelor și barele de volum. Verificarea e opt-in, pe etichete numite: pe un grafic de candele aproape orice text atinge ceva, iar o regulă globală ar bloca tot repo-ul. eticheta_libera(svg, "wedge_desc", {"baza = 4.00"}, rects=True) """ import re CW = 6.2 # lățimea unui caracter bold de 11-12px WICK = "#1a1a1a" # fitilele: desenul le traversează oricum, prin construcție def _bbox(m, min_px): """Caseta etichetei, lărgită cu marginea cerută.""" tx, ty, attrs, s = float(m.group(1)), float(m.group(2)), m.group(3), m.group(4) w = CW * len(s) x0 = tx if 'anchor="start"' in attrs else tx - w if 'anchor="end"' in attrs else tx - w / 2 return s, (x0 - min_px, x0 + w + min_px, ty - 9 - min_px, ty + 3 + min_px) def _segmente(svg): """Toate segmentele desenate, ca perechi de capete: linii + polilinii.""" for m in re.finditer(r' x1: return False if ax == bx: ys = (ay, by) else: # y-ul segmentului pe porțiunea comună ys = tuple(ay + (by - ay) * (min(max(x, min(ax, bx)), max(ax, bx)) - ax) / (bx - ax) for x in (x0, x1)) return min(ys) <= bot and max(ys) >= top def eticheta_libera(svg, name, etichete, min_px=5.0, rects=False, segmente=True): """Ridică AssertionError dacă vreuna din `etichete` stă peste desen. `segmente=False` pentru etichetele de nivel: alea stau lipite de linia lor prin construcție (y − 6), deci doar candelele sunt o suprapunere reală. """ segs = list(_segmente(svg)) if segmente else [] boxes = [] if rects: for m in re.finditer(r'', svg): x, y, w, h = map(float, m.groups()[:4]) if "fill-opacity" in m.group(5): # zonele colorate stau în fundal continue boxes.append((x, y, x + w, y + h)) for m in re.finditer(r']*)>([^<]*)', svg): if m.group(4) not in etichete: continue s, box = _bbox(m, min_px) for kind, ax, ay, bx, by in segs: if _atinge(box, ax, ay, bx, by): raise AssertionError(f"{name}: eticheta '{s}' sta pe {kind} " f"({ax:.0f},{ay:.0f})-({bx:.0f},{by:.0f})") for rx0, ry0, rx1, ry1 in boxes: if rx0 < box[1] and rx1 > box[0] and ry0 < box[3] and ry1 > box[2]: raise AssertionError(f"{name}: eticheta '{s}' sta pe dreptunghiul " f"({rx0:.0f},{ry0:.0f})-({rx1:.0f},{ry1:.0f})") def demo(): svg = ('' '' '' 'pe linie' 'pe curba' 'pe candela' 'libera') for et in ("pe linie", "pe curba", "pe candela"): try: eticheta_libera(svg, "demo", {et}, rects=True) except AssertionError: pass else: raise AssertionError(f"'{et}' trebuia prinsa") eticheta_libera(svg, "demo", {"libera"}, rects=True) print("eticheta_libera: ok") if __name__ == "__main__": demo()