chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합
- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환 - Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용 - pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관) - worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md) - D:\Rhino-MCP 경로 참조를 새 위치로 치환 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
964a841de1
3978 files changed
+58013356
No files matched your search
@@ -0,0 +1,140 @@
|
||||
"""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<ty-y<32*height):continue
|
||||
owners=[t for t in all_titles if 0<_text_anchor(t)[1]-y<32*(t.get('text_height')or 0)]
|
||||
if owners and min(owners,key=lambda t:math.dist(_text_anchor(t),(x,y))) is not title:continue
|
||||
near.append(e)
|
||||
notes=[]
|
||||
for e in near:
|
||||
parsed=parse_note(e['text'])
|
||||
if not parsed:continue
|
||||
x,y=_text_anchor(e);h=e.get('text_height')or 0
|
||||
owners=[v for v in near if v is not e and
|
||||
re.search(r'다[웰월]바|DOWEL|ST\s*PIPE|스티로폼|TAR\s*PAPER',v.get('text',''),re.I)
|
||||
and 0<_text_anchor(v)[1]-y<=2*h and abs(_text_anchor(v)[0]-x)<=2*h]
|
||||
notes.append({**parsed,'source':{**selector(e),'text':e['text']},
|
||||
'label_candidates':[{**selector(v),'text':v['text']}for v in owners],
|
||||
'placement_authorized':False})
|
||||
bars=[]
|
||||
for item in installed:
|
||||
if item['title']!=selector(title):continue
|
||||
# Exact sheet ownership matters: body D1/D2/D3 and accessory
|
||||
# D1/D2/D3 can have the same marks and even the same diameter.
|
||||
matches=[r for r in rows if r['source_sha256']==source.sha256 and
|
||||
r.get('source_region_id')==getattr(source,'region_id',None) and
|
||||
r['material']['mark'].strip().upper()==item['mark'].strip().upper()]
|
||||
straight=[]
|
||||
if len(matches)==1:
|
||||
for note in notes:
|
||||
if note['kind']=='dowel_bar_pitch':straight.extend(outlined_straight_bar(source,matches[0],note,interfaces))
|
||||
bars.append({'mark':item['mark'],'installation':copy.deepcopy(item),
|
||||
'fabrication_rows':copy.deepcopy(matches),
|
||||
'straight_outline_bindings':straight,
|
||||
'row_binding_unique':len(matches)==1,'host_binding_complete':False,
|
||||
'pending':'full detail/pipe/bearing relationship and physical station binding required'})
|
||||
result.append({'source_sha256':source.sha256,'source_region_id':getattr(source,'region_id',None),
|
||||
'title':{**selector(title),'text':title['text']},'blocks':blocks,'notes':notes,'reinforcement':bars,
|
||||
'installation_bound':False,'placement_authorized':False,
|
||||
'pending':'do not treat a pipe outline or break symbol as the straight dowel centreline'})
|
||||
return {'details':result,'source_sha256s':sorted(sources),'external_calls':0,
|
||||
'placement_authorized':False,'body_mark_binding_unchanged':True}
|
||||
Reference in new issue
Block a user