"""Current-drawing dowel detail inventory, separate from body D-mark bars. This module imports source evidence; it never infers a helix, an installation phase, a bearing gap, or a host transform from a familiar mark or note. """ import copy import math import re from cad_accessory_detail import detail_role, installed_detail_candidates from cad_standard_section import _text_anchor from local_assembly_discovery import selector def parse_note(text): text=re.sub(r'\s+','',text).upper() bar=re.fullmatch(r'(?:H|S|D|Ø|Φ|⌀)(\d+(?:\.\d+)?)@(\d+(?:\.\d+)?)(?:MM)?',text) if bar and min(float(v)for v in bar.groups())>0: return {'kind':'dowel_bar_pitch','diameter_mm':float(bar[1]),'pitch_mm':float(bar[2])} pipe=re.fullmatch(r'(?:Ø|Φ|⌀)(\d+(?:\.\d+)?)[X×](\d+(?:\.\d+)?)(?:MM)?',text) if pipe and min(float(v)for v in pipe.groups())>0: return {'kind':'pipe_dimensions','diameter_mm':float(pipe[1]),'length_mm':float(pipe[2])} thick=re.fullmatch(r'T=(\d+(?:\.\d+)?)MM',text) if thick and float(thick[1])>0:return {'kind':'layer_thickness','thickness_mm':float(thick[1])} layers=re.fullmatch(r'(\d+)-?LAYERS?',text) if layers and int(layers[1])>0:return {'kind':'layer_count','layers':int(layers[1])} return None def outlined_straight_bar(source, row, note, interfaces): """A drawn cylinder outline is not a bent four-leg bar. Resolve its centreline only when its width is the annotated diameter, its length is the original fabrication L, and the *dowel note leader* reaches that actual closed outline. A nearby pipe cannot qualify. """ from local_assembly_discovery import leader_targets shape=row.get('fabrication_local')or{} if len(shape.get('points_m',[]))!=2:return [] material=row['material'];length=shape['length_m'];diameter=material['diameter_mm']/1000 if abs(note['diameter_mm']-material['diameter_mm'])>1e-6:return [] labels=[source.entity(r)for r in note['label_candidates']if re.search('다[웰월]바|DOWEL',r['text'],re.I)] if len(labels)!=1:return [] label=labels[0];x,y=_text_anchor(label);h=label.get('text_height')or 0 lines=[e for e in source.entities if e.get('type')=='LINE' and len(e.get('points',[]))==2] underlines=[e for e in lines if any(tag in e.get('layer','').upper()for tag in ('LEAL','DIM')) and abs(e['points'][0][1]-e['points'][1][1])<1e-6 and 0<=y-e['points'][0][1]<=h and min(p[0]for p in e['points'])-h<=x<=max(p[0]for p in e['points'])] if len(underlines)!=1:return [] try:targets,leaders=leader_targets(source,label,1.,underline=underlines[0]) except ValueError:return [] bars=[e for e in lines if 'RBAR'in e.get('layer','').upper()] key=lambda p:tuple(round(v,6)for v in p) lookup={tuple(sorted((key(e['points'][0]),key(e['points'][1])))):e for e in bars} results=[] for interface in interfaces: scale=interface['cad_to_m'] for target in targets: a,b=target['points'] for axis in (0,1): if abs(a[axis]-b[axis])>1e-6 or abs(math.dist(a,b)*scale-diameter)>.001:continue # The leader reaches an end cap. Both side legs and the # opposite cap must exist in the original source graph. for sign in (-1,1): c=a[:];d=b[:];c[axis]+=sign*length/scale;d[axis]+=sign*length/scale edges=[] for p,q in ((a,c),(c,d),(d,b)): found=lookup.get(tuple(sorted((key(p),key(q))))) if found is None:break edges.append(found) if len(edges)!=3:continue centres=[[(a[k]+b[k])/2 for k in (0,1)],[(c[k]+d[k])/2 for k in (0,1)]] results.append({'source_sha256':source.sha256,'source_region_id':getattr(source,'region_id',None), 'mark':material['mark'],'diameter_mm':material['diameter_mm'],'length_m':length, 'pitch_mm':note['pitch_mm'],'centreline_points_cad':centres,'cad_to_m':scale, 'outline_entities':[selector(e)for e in [target,*edges]], 'note':copy.deepcopy(note),'note_leaders':leaders,'note_underline':selector(underlines[0]), 'interface':copy.deepcopy(interface),'placement_authorized':False,'host_binding_complete':False}) unique={tuple(sorted(tuple(round(v,6)for v in p)for p in r['centreline_points_cad'])):r for r in results} return list(unique.values()) def detail_inventory(sources, rows): from cad_sheet_regions import all_regions from source_rebar_catalog import sheet_block_witnesses result=[] for source in all_regions(sources): titles=[e for e in source.entities if e.get('points') and detail_role(e.get('text',''))=='dowel'] if not titles:continue all_titles=[e for e in source.entities if e.get('points') and ('상세'in e.get('text','') or 'DETAIL'in e.get('text','').upper())] installed=installed_detail_candidates(source,roles=('dowel',)) from cad_approach_detail import approach_interfaces interfaces=approach_interfaces(source.entities) blocks=sorted({v['block']for v in sheet_block_witnesses(source.entities)}) for title in titles: tx,ty=_text_anchor(title);height=title.get('text_height')or 0 if height<=0:continue near=[] for e in source.entities: if not e.get('text') or not e.get('points') or e.get('type')=='ATTDEF':continue x,y=_text_anchor(e) if not(abs(x-tx)<24*height and 0