# -*- coding: utf-8 -*-
"""Pass 3: LINE/ARC 끝점을 union-find로 이어 '부재 윤곽 후보' 덩어리를 만든다."""
import json, math, numpy as np, collections
SCR = r'C:\Users\user\AppData\Local\Temp\claude\E-----Claude--20260728-claude-code-2-1-219\8defc145-8ae5-48d0-9058-355f3d9ae8ed\scratchpad'
d2 = json.load(open(SCR + r'\index2.json', encoding='utf-8'))
L, A = d2['lines'], d2['arcs']
TOL = 0.5

segs = []  # (p0, p1, kind, payload)
for l in L:
    segs.append(((l[0], l[1]), (l[2], l[3]), 'L', l))
for a in A:
    cx, cy, r, s, e = a[0], a[1], a[2], a[3], a[4]
    p0 = (cx + r*math.cos(math.radians(s)), cy + r*math.sin(math.radians(s)))
    p1 = (cx + r*math.cos(math.radians(e)), cy + r*math.sin(math.radians(e)))
    segs.append((p0, p1, 'A', a))

# 끝점 격자 해싱으로 노드 병합
node = {}
def nid(p):
    k = (round(p[0]/TOL), round(p[1]/TOL))
    for dx in (-1,0,1):
        for dy in (-1,0,1):
            kk = (k[0]+dx, k[1]+dy)
            if kk in node: return node[kk]
    v = len(node); node[k] = v; return v

par = {}
def find(x):
    while par.get(x, x) != x: par[x] = par.get(par[x], par[x]); x = par[x]
    return x
def uni(a, b):
    ra, rb = find(a), find(b)
    if ra != rb: par[ra] = rb

pairs = []
for s in segs:
    a, b = nid(s[0]), nid(s[1])
    par.setdefault(a, a); par.setdefault(b, b)
    uni(a, b); pairs.append((a, b))

grp = collections.defaultdict(list)
for i, (a, b) in enumerate(pairs):
    grp[find(a)].append(i)

print('components:', len(grp))
d = json.load(open(SCR + r'\index.json', encoding='utf-8'))
T = d['texts']; C = d['circles']
tp = np.array([[t[2], t[3]] for t in T], float)
cc = np.array([[c[2], c[3], c[4]] for c in C], float)

import re
pat = re.compile(r'(\d+(?:\.\d+)?)\s*T\b', re.I)

out = []
for root, idxs in grp.items():
    if not (4 <= len(idxs) <= 400): continue
    pts = []
    for i in idxs:
        s = segs[i]; pts.append(s[0]); pts.append(s[1])
    pts = np.array(pts, float)
    x0, y0 = pts.min(0); x1, y1 = pts.max(0)
    w, h = x1-x0, y1-y0
    if not (200 < max(w, h) < 3000 and min(w, h) > 40): continue
    nholes = int(((cc[:,0] > x0) & (cc[:,0] < x1) & (cc[:,1] > y0) & (cc[:,1] < y1)).sum())
    m = (tp[:,0] > x0-500) & (tp[:,0] < x1+500) & (tp[:,1] > y0-500) & (tp[:,1] < y1+500)
    thk = []
    for k in np.where(m)[0]:
        for g in pat.findall(T[k][4]): thk.append((float(g), T[k][4][:60]))
    out.append(dict(root=int(root), nseg=len(idxs), bbox=[x0, y0, x1, y1], w=w, h=h,
                    nholes=nholes, thk=thk))

out.sort(key=lambda r: -(r['nholes']*10 + min(r['nseg'], 40)))
print('candidates:', len(out))
for r in out[:20]:
    print('nseg=%-4d %.0f x %.0f mm holes=%-3d @(%.0f,%.0f) thk=%s'
          % (r['nseg'], r['w'], r['h'], r['nholes'], r['bbox'][0], r['bbox'][1],
             sorted(set(t[0] for t in r['thk']))))
json.dump(out, open(SCR + r'\cands.json', 'w', encoding='utf-8'))
