chore: import 3d-drawing history from local Code/3d-drawing (subfolder, history preserved)

This commit is contained in:
SeanJo committed 2026-10-27 13:30:09 +09:00
commit 7b754e810b
51 files changed
+20300

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
import sys, time, pickle
sys.path += ['/root/.local/lib/python3.13/site-packages','/usr/local/lib/python3.13/dist-packages','/usr/lib/python3/dist-packages']
from build123d import import_step, Face
import numpy as np
shape = import_step("/mnt/user-data/uploads/97.과제/Code/3d-drawing/model/tunnel_solids.step")
solids = shape.solids()
pickle_out = {}
import json
meta=json.load(open('/mnt/user-data/uploads/97.과제/Code/3d-drawing/model/tunnel_solids_meta.json'))
comps=[c for c in meta['components'] if c['group']!='시설한계(참조선)']
print("non-clearance comps", len(comps))
# match by volume greedily in meta order
used=set(); mapping={}
for c in comps:
best=None;bd=1e9
for j,s in enumerate(solids):
if j in used: continue
d=abs(s.volume-c['volume_m3'])
if d<bd: bd=d;best=j
if bd<0.02:
used.add(best); mapping[c['id']]=best
else:
mapping[c['id']]=None
print("matched", sum(v is not None for v in mapping.values()), "of", len(comps), "unused solids", len(solids)-len(used))
print("first 10 mapping", list(mapping.items())[:10])
pickle.dump({'mapping':mapping}, open('/tmp/verify_daesan2_run/mapping.pkl','wb'))
# faces of solid 0 (LIN_01)
for idx in [mapping['LIN_01_P-3']]:
s=solids[idx]
fs=s.faces()
print("solid",idx,"nfaces",len(fs), "vol", round(s.volume,3))
rows=[]
for f in fs:
gt=f.geom_type
c=f.center(); a=f.area
try:
n=f.normal_at(c)
except Exception as ex:
n=None
rows.append((gt, round(a,1), [round(x,3) for x in (c.X,c.Y,c.Z)], None if n is None else [round(x,4) for x in (n.X,n.Y,n.Z)]))
rows.sort(key=lambda r: -r[1])
for r in rows[:25]: print(r)
+69
View File
@@ -0,0 +1,69 @@
import sys
exec(open('/tmp/verify_daesan2_run/scripts/hd2.py').read().split('allres={}')[0])
from scipy.spatial import cKDTree
import numpy as np
extra={
'PAV_sideL': seg(9526.3,15764.0,9526.3,15484.0), 'PAV_sideR': seg(18726.3,15580.0,18726.3,15300.0),
'DRN_topL': seg(9026.3,15694.0,9476.3,15703.0), 'DRN_topR': seg(18776.3,15519.0,19226.3,15510.0),
}
REF.update(extra)
def dd(A,B):
d1,_=cKDTree(A).query(B); d2,_=cKDTree(B).query(A); return d1,d2
for st in ['4745.0005','5100.0','5469.9995']:
print("=== station", st, "sigma=+1 slope(dxf)", round(FIT[st]['slope_top'],5))
L=to_dxf(pts(st,lambda g,i:g=='라이닝'),st,1); E=to_dxf(pts(st,lambda g,i:g=='굴착면'),st,1)
P=to_dxf(pts(st,lambda g,i:g=='노면(계획고)'),st,1); B=to_dxf(pts(st,lambda g,i:g=='기층·인버트(가정)'),st,1)
D=to_dxf(pts(st,lambda g,i:g=='배수(측구)'),st,1)
# LIN full arcs (no clipping) and clipped to model extent
arcs=['LIN_crown_outer','LIN_crown_inner','LIN_haunchL_outer','LIN_haunchL_inner','LIN_haunchR_outer','LIN_haunchR_inner']
for k in arcs:
d,_=cKDTree(L).query(REF[k]); print(f" LIN {k:18s} max={d.max():7.1f} mm (at y_min_ref={REF[k][:,1].min():.0f})")
ymin_L=L[L[:,0]<C0[0]][:,1].min(); ymin_R=L[L[:,0]>C0[0]][:,1].min()
lr=np.vstack([REF[k] for k in arcs]); d,_=cKDTree(L).query(lr)
inside=((lr[:,0]<C0[0])&(lr[:,1]>=ymin_L))|((lr[:,0]>=C0[0])&(lr[:,1]>=ymin_R))
print(f" LIN within model extent (y>={ymin_L:.0f}/{ymin_R:.0f}) max={d[inside].max():.1f} mm; drawn part below model extent (not modelled) n={int((~inside).sum())} pts, y range {lr[~inside,1].min():.0f}-{lr[~inside,1].max():.0f}")
fl=np.vstack([REF['LIN_footL'],REF['LIN_footR']]); a,b=dd(L,fl); print(f" LIN foot lines (CS-CONC-MAJR wall/foot) ref->model max={a.max():.1f} model->ref(near band) ")
# EXC
ea=['EXC_crown','EXC_haunchL','EXC_haunchR']
eymL=E[E[:,0]<C0[0]][:,1].min(); eymR=E[E[:,0]>C0[0]][:,1].min()
er=np.vstack([REF[k] for k in ea]); d,_=cKDTree(E).query(er)
ins=((er[:,0]<C0[0])&(er[:,1]>=eymL))|((er[:,0]>=C0[0])&(er[:,1]>=eymR))
print(f" EXC arcs within model extent (y>={eymL:.0f}/{eymR:.0f}) max={d[ins].max():.1f} mm; crown max={cKDTree(E).query(REF['EXC_crown'])[0].max():.1f}; below model extent n={int((~ins).sum())} y {er[~ins,1].min():.0f}-{er[~ins,1].max():.0f}")
# PAV model vs drawn top/bottom/sides
pref=np.vstack([REF['PAV_top'],REF['PAV_bot'],REF['PAV_sideL'],REF['PAV_sideR']])
Pm=P[(P[:,0]>9526.3-150)&(P[:,0]<18726.3+150)]
a,b=dd(Pm,pref); print(f" PAV ref->model max={a.max():.1f} mm ; model->ref max={b.max():.1f} mm (top/bottom/sides)")
# BASE: top envelope vs base top line; bottom envelope vs base bottom line, within road span
bw=B[(B[:,0]>=9526.3)&(B[:,0]<=18726.3)]
xs=np.linspace(9526.3,18726.3,921)
top_line=15484.0+(xs-9526.3)*(15300-15484)/9200.0; bot_line=15334.0+(xs-9526.3)*(15150-15334)/9200.0
# model envelope per x-bin (1 m bins for robust): max y (top) and min y (bottom)
tops=[];bots=[]
for x0 in np.arange(9526.3,18726.3,200):
m=(bw[:,0]>=x0)&(bw[:,0]<x0+200)
if m.sum()==0: continue
xm=bw[m,0].mean(); tops.append((xm,bw[m,1].max())); bots.append((xm,bw[m,1].min()))
tops=np.array(tops); bots=np.array(bots)
et=np.abs(tops[:,1]-np.interp(tops[:,0],xs,top_line)); eb=np.abs(bots[:,1]-np.interp(bots[:,0],xs,bot_line))
print(f" BASE top envelope vs drawn top (15484->15300): max={et.max():.1f} mm, mean={et.mean():.1f}; bottom envelope vs drawn bottom (15334->15150): max={eb.max():.1f} mm, mean={eb.mean():.1f}")
print(f" BASE model bottom envelope at span ends: x={bots[0,0]:.0f} y={bots[0,1]:.1f} / x={bots[-1,0]:.0f} y={bots[-1,1]:.1f}; top envelope ends y={tops[0,1]:.1f}/{tops[-1,1]:.1f}")
print(f" BASE model extent outside road span: x {B[:,0].min():.0f}..{B[:,0].max():.0f}, y {B[:,1].min():.0f}..{B[:,1].max():.0f}")
# DRN
dref=np.vstack([REF[k] for k in ['DRN_wallL1','DRN_wallL2','DRN_botL','DRN_pipeL','DRN_wallR1','DRN_wallR2','DRN_botR','DRN_pipeR','DRN_topL','DRN_topR']])
dm=D[(D[:,0]>8900)&(D[:,0]<19300)]
a,b=dd(dm,dref); print(f" DRN ref->model max={a.max():.1f} mm ; model->ref max={b.max():.1f} mm (walls, bottoms, pipes, tops)")
# vertical datum check at tunnel centre and road centre
fit=FIT[st]; uc,vc,_=fit['outer']
top_c=fit['slope_top']*(0-uc)+0 ;
# top envelope at u for DXF tunnel centre (u=uc) and road centre (x=9776.3 -> u=uc-4.35)
uu=P[:,0];
def top_env(xdxf):
um=uc+(xdxf-C0[0])/1000.0
m=np.abs(pts(st,lambda g,i:g=='노면(계획고)')[:,0]-um)<0.05
return pts(st,lambda g,i:g=='노면(계획고)')[m,1].max()
Pm_raw=pts(st,lambda g,i:g=='노면(계획고)')
for lab,xdxf,ydxf in [('tunnel centre x=14126.3',14126.3,15672.0),('road centre x=9776.3',9776.3,15759.0)]:
um=uc+(xdxf-C0[0])/1000.0
m=np.abs(Pm_raw[:,0]-um)<0.05
yt=(Pm_raw[m,1].max()-vc)*1000; yb=(Pm_raw[m,1].min()-vc)*1000
print(f" datum {lab}: model top={yt:.1f} mm, bottom={yb:.1f} mm above lining centre | DXF top at this x = {ydxf-15305:.1f} mm above lining centre")
+23
View File
@@ -0,0 +1,23 @@
import sys
exec(open('/tmp/verify_daesan2_run/scripts/final.py').read().split("for st in ['4745.0005','5100.0','5469.9995']:")[0])
for st in ['4745.0005','5100.0']:
B=to_dxf(pts(st,lambda g,i:g=='기층·인버트(가정)'),st,1); D=to_dxf(pts(st,lambda g,i:g=='배수(측구)'),st,1)
bw=B[(B[:,0]>=9526.3+150)&(B[:,0]<=18726.3-150)]
xs=np.arange(9676.3,18576.3,50.0)
top_line=lambda x: 15484.0+(x-9526.3)*(15300-15484)/9200.0
bot_line=lambda x: 15334.0+(x-9526.3)*(15150-15334)/9200.0
tt=[];bb=[]
for x0 in xs:
m=(bw[:,0]>=x0-25)&(bw[:,0]<x0+25)
if m.sum()==0: continue
tt.append(bw[m,1].max()-top_line(x0)); bb.append(bw[m,1].min()-bot_line(x0))
tt=np.array(tt); bb=np.array(bb)
print(f"{st}: BASE top (pavement-bottom interface), interior 150mm from ends: model-drawn max|dev|={np.abs(tt).max():.1f} mm, mean={tt.mean():.1f} mm (signed mean)")
print(f"{st}: BASE bottom (drawn excavation floor 15334->15150), interior: max|dev|={np.abs(bb).max():.1f} mm, mean={bb.mean():.1f} mm (signed; negative=model below drawing)")
# drains body vs lid
body=np.vstack([REF[k] for k in ['DRN_wallL1','DRN_wallL2','DRN_botL','DRN_pipeL','DRN_wallR1','DRN_wallR2','DRN_botR','DRN_pipeR']])
lid=np.vstack([REF['DRN_topL'],REF['DRN_topR']])
dm=D[(D[:,0]>8900)&(D[:,0]<19300)]
a,_=cKDTree(dm).query(body); b,_=cKDTree(dm).query(lid)
print(f"{st}: DRN body (walls/bottom/pipe) ref->model max={a.max():.1f} mm; lid (450-wide top line) ref->model max={b.max():.1f} mm")
print(f"{st}: DRN model x-extent per ditch: left {dm[dm[:,0]<14126,0].min():.0f}-{dm[dm[:,0]<14126,0].max():.0f}, right {dm[dm[:,0]>14126,0].min():.0f}-{dm[dm[:,0]>14126,0].max():.0f}")
@@ -0,0 +1,102 @@
import sys, json, pickle, collections
sys.path += ['/root/.local/lib/python3.13/site-packages','/usr/local/lib/python3.13/dist-packages','/usr/lib/python3/dist-packages']
import numpy as np
from build123d import import_step, Shape
from OCP.BRepAlgoAPI import BRepAlgoAPI_Section
from OCP.gp import gp_Pln, gp_Pnt, gp_Dir
from OCP.BRepBuilderAPI import BRepBuilderAPI_MakeFace
from OCP.TopExp import TopExp_Explorer
from OCP.TopAbs import TopAbs_EDGE
solids = import_step("/mnt/user-data/uploads/97.과제/Code/3d-drawing/model/tunnel_solids.step").solids()
mp = pickle.load(open('/tmp/verify_daesan2_run/mapping.pkl','rb'))['mapping']
caps = json.load(open('/tmp/verify_daesan2_run/caps.json'))
ch = json.load(open('/tmp/verify_daesan2_run/chain.json'))
meta = json.load(open('/mnt/user-data/uploads/97.과제/Code/3d-drawing/model/tunnel_solids_meta.json'))
comps = {c['id']: c for c in meta['components'] if c['group']!='시설한계(참조선)'}
solid_comp = {v:k for k,v in mp.items()}
# 1) boundary points from lining chain
pts = []
for k in ch['lin']:
pts.append(np.array(ch['starts'][k])); pts.append(np.array(ch['ends'][k]))
P = np.array(pts)[:, :2]
uniq = []
for p in P:
if not any(np.linalg.norm(p-q) < 1e-3 for q in uniq): uniq.append(p)
uniq = np.array(uniq)
print("unique boundary points", len(uniq), "(expect 41)")
D = P[-1]-P[0] if False else (np.array(ch['ends'][ch['lin'][-1]])[:2]-np.array(ch['starts'][ch['lin'][0]])[:2])
proj = uniq@D/np.linalg.norm(D)
order = np.argsort(proj); B = uniq[order]
cum = np.concatenate([[0], np.cumsum(np.linalg.norm(np.diff(B,axis=0),axis=1))])
Sb = 4745 + cum
M = sorted(set([c['from_sta'] for c in comps.values()] + [5480]))
print("boundary count", len(Sb), "meta boundary count", len(M))
print("max |station_chain - meta_boundary| (m):", round(float(np.max(np.abs(Sb-np.array(M)))),3) if len(Sb)==len(M) else 'count mismatch')
# 2) station for each solid by centroid projection
def station_of(p):
best=None
for k in range(len(B)-1):
a,b=B[k],B[k+1]; t=b-a; L2=t@t
f=np.clip(((p-a)@t)/L2,0,1); q=a+f*t; d=np.linalg.norm(p-q)
if best is None or d<best[0]: best=(d, Sb[k]+f*(Sb[k+1]-Sb[k]), k, f)
return best
sol_info={}
for i,s in enumerate(solids):
c=s.center(); d,st,k,f=station_of(np.array([c.X,c.Y]))
cid=solid_comp[i]; sol_info[i]=dict(cid=cid, group=comps[cid]['group'], st=st, lat=d)
# 3) pair within group by sorted mid
assign={}
groups=collections.defaultdict(list)
for i,v in sol_info.items(): groups[v['group']].append(i)
maxres=0
for g,idxs in groups.items():
cs=[c for c in comps.values() if c['group']==g]
idxs_s=sorted(idxs, key=lambda i: sol_info[i]['st'])
cs_s=sorted(cs, key=lambda c: (c['from_sta']+c['to_sta'])/2)
if len(idxs_s)!=len(cs_s): print("COUNT MISMATCH", g, len(idxs_s), len(cs_s))
for i,c in zip(idxs_s, cs_s):
mid=(c['from_sta']+c['to_sta'])/2
res=abs(sol_info[i]['st']-mid); maxres=max(maxres,res)
assign[i]=dict(id=c['id'], group=g, frm=c['from_sta'], to=c['to_sta'], mid_chain=sol_info[i]['st'], lat=sol_info[i]['lat'])
print("max residual solid-centre station vs comp mid (m):", round(maxres,3))
# lateral offset sanity
print("max lateral offset of solid centroid from chain (m):", round(max(v['lat'] for v in assign.values()),3))
# 4) slice
def slice_solid(idx, Q, T):
nrm=np.array([T[0],T[1],0.0])
pl=gp_Pln(gp_Pnt(float(Q[0]),float(Q[1]),0.0), gp_Dir(float(nrm[0]),float(nrm[1]),0.0))
face=BRepBuilderAPI_MakeFace(pl,-80,80,-80,80).Face()
sec=BRepAlgoAPI_Section(solids[idx].wrapped, face); sec.Build()
polys=[]
exp=TopExp_Explorer(sec.Shape(), TopAbs_EDGE)
while exp.More():
e=Shape.cast(exp.Current()); L=e.length; N=max(2,int(L/0.02))
polys.append([[p.X,p.Y,p.Z] for p in (e.position_at(j/N) for j in range(N+1))])
exp.Next()
return polys
def plane_at(s):
k=int(np.searchsorted(Sb, s, side='right')-1); k=min(max(k,0),len(B)-2)
f=(s-Sb[k])/(Sb[k+1]-Sb[k])
Q=(1-f)*B[k]+f*B[k+1]; T=(B[k+1]-B[k]); T=T/np.linalg.norm(T)
return Q, T
out={}
for s in [4745.0005, 5100.0, 5469.9995]:
Q,T=plane_at(s); eh=np.array([-T[1],T[0]])
print(f"== station {s}: Q=({Q[0]:.3f},{Q[1]:.3f}) T=({T[0]:.5f},{T[1]:.5f})")
out[str(s)]={'Q':Q.tolist(),'T':T.tolist(),'eh':eh.tolist(),'solids':{}}
cnt=collections.Counter()
for i,a in assign.items():
if not (a['frm'] <= s <= a['to']): continue
polys=slice_solid(i,Q,T)
uv=[]
for pl_ in polys:
arr=np.array(pl_); u=(arr[:,:2]-Q)@eh; uv.append(np.stack([u,arr[:,2]],1).tolist())
out[str(s)]['solids'][str(i)]={'id':a['id'],'group':a['group'],'polys':uv}
cnt[a['group']]+=1
allp=np.vstack([np.array(x) for x in uv]) if uv else np.zeros((0,2))
if len(allp): print(f" {a['group']:<12} {a['id']:<16} nloops={len(uv)} u[{allp[:,0].min():.3f},{allp[:,0].max():.3f}] v[{allp[:,1].min():.3f},{allp[:,1].max():.3f}]")
else: print(" EMPTY", a['group'], a['id'])
print(" counts", dict(cnt))
json.dump(out, open('/tmp/verify_daesan2_run/sections_final.json','w'))
json.dump({str(i):a for i,a in assign.items()}, open('/tmp/verify_daesan2_run/assign.json','w'), ensure_ascii=False)
print("saved")
+110
View File
@@ -0,0 +1,110 @@
import sys, json, math
sys.path += ['/root/.local/lib/python3.13/site-packages','/usr/local/lib/python3.13/dist-packages','/usr/lib/python3/dist-packages']
import numpy as np
from scipy.spatial import cKDTree
SEC=json.load(open('/tmp/verify_daesan2_run/sections_final.json'))
FIT=json.load(open('/tmp/verify_daesan2_run/fits.json'))
C0=(14126.3,15305.0)
def pts(st, pred):
out=[]
for sid,v in SEC[st]['solids'].items():
if pred(v['group'], v['id']):
for poly in v['polys']: out.append(np.array(poly)[:,:2])
return np.vstack(out)
def arc(cx,cy,r,a0,a1,step=1.0):
if a1<a0: a1+=360
n=max(2,int(r*math.radians(a1-a0)/step)); th=np.radians(np.linspace(a0,a1,n))
return np.c_[cx+r*np.cos(th), cy+r*np.sin(th)]
def seg(x0,y0,x1,y1,step=1.0):
n=max(2,int(math.hypot(x1-x0,y1-y0)/step)); t=np.linspace(0,1,n); return np.c_[x0+(x1-x0)*t,y0+(y1-y0)*t]
def circ(cx,cy,r,step=1.0):
n=max(8,int(2*math.pi*r/step)); th=np.linspace(0,2*math.pi,n,endpoint=False); return np.c_[cx+r*np.cos(th),cy+r*np.sin(th)]
REF={
'LIN_crown_outer': arc(*C0,7282,40,140), 'LIN_crown_inner': arc(*C0,6982,40,140),
'LIN_haunchL_outer': arc(11959.9,17122.8,4454,140,204.67), 'LIN_haunchL_inner': arc(11621.3,17406.9,3712,140,195.56),
'LIN_haunchR_outer': arc(16105.0,16965.3,4699,336.35,40), 'LIN_haunchR_inner': arc(16479.6,17279.7,3910,344.38,40),
'LIN_footL': np.vstack([seg(8171.3,16410.9,8471.3,16410.9), seg(8471.3,16410.9,8621.3,16410.9), seg(8621.3,16410.9,8651.3,16000.9), seg(8651.3,16000.9,8771.3,15825.9), seg(8771.3,15825.9,8771.3,15750.9)]),
'LIN_footR': np.vstack([seg(20181.3,16226.9,19781.3,16226.9), seg(19781.3,16226.9,19631.3,16226.9), seg(19631.3,16226.9,19601.3,15816.9), seg(19601.3,15816.9,19481.3,15641.9), seg(19481.3,15641.9,19481.3,15566.9)]),
'EXC_crown': arc(*C0,7402,40,140), 'EXC_haunchL': arc(11959.9,17122.8,4574,140,203.98), 'EXC_haunchR': arc(16105.0,16965.3,4819,336.97,40),
'PAV_top': seg(9526.3,15764.0,18726.3,15580.0), 'PAV_bot': seg(9526.3,15484.0,18726.3,15300.0), 'PAV_sideL': seg(9526.3,15764.0,9526.3,15484.0), 'PAV_sideR': seg(18726.3,15580.0,18726.3,15300.0),
'BASE_top': seg(9526.3,15484.0,18726.3,15300.0), 'BASE_bot': seg(9526.3,15334.0,18726.3,15150.0),
'DRN_wallL1': seg(9101.3,14864.0,9101.3,15695.5), 'DRN_wallL2': seg(9401.3,14864.0,9401.3,15701.5), 'DRN_botL': seg(9101.3,14864.0,9401.3,14864.0), 'DRN_pipeL': circ(9251.3,15064.0,150),
'DRN_wallR1': seg(18851.3,14680.0,18851.3,15517.5), 'DRN_wallR2': seg(19151.3,14680.0,19151.3,15511.5), 'DRN_botR': seg(18851.3,14680.0,19151.3,14680.0), 'DRN_pipeR': circ(19001.3,14880.0,150),
}
def to_dxf(P, st, sig):
uc,vc,_=FIT[st]['outer']
return np.c_[C0[0]+sig*(P[:,0]-uc)*1000.0, C0[1]+(P[:,1]-vc)*1000.0]
def dist_stats(M, R, label):
"""M model pts (dxf frame), R reference pts. Returns d(ref->model) max, d(model->ref) max within band."""
if len(M)==0 or len(R)==0: return None
tM=cKDTree(M); tR=cKDTree(R)
dR,_=tM.query(R); dM,_=tR.query(M)
return dict(ref_to_model_max=float(dR.max()), ref_to_model_mean=float(dR.mean()), model_to_ref_max=float(dM.max()), model_to_ref_mean=float(dM.mean()), n_ref=len(R), n_model=len(M))
def run(sig, st, feats):
res={}
L=to_dxf(pts(st,lambda g,i:g=='라이닝'),st,sig); E=to_dxf(pts(st,lambda g,i:g=='굴착면'),st,sig)
P=to_dxf(pts(st,lambda g,i:g=='노면(계획고)'),st,sig); B=to_dxf(pts(st,lambda g,i:g=='기층·인버트(가정)'),st,sig)
D=to_dxf(pts(st,lambda g,i:g=='배수(측구)'),st,sig)
def side_clip(R, M, side):
# keep reference points on given side (L: x<C0x, R: x>C0x) and above model's lowest y on that side minus 50mm
sm = M[(M[:,0]<C0[0])] if side=='L' else M[(M[:,0]>C0[0])]
sr = R[(R[:,0]<C0[0])] if side=='L' else R[(R[:,0]>C0[0])]
if len(sm)==0: return sr, 0, None
ymin=sm[:,1].min()-0.05*1000
keep=sr[sr[:,1]>=ymin]
dropped=len(sr)-len(keep)
rng=None if dropped==0 else [round(float(sr[sr[:,1]<ymin,1].min())),round(float(sr[sr[:,1]<ymin,1].max()))]
return keep, dropped, rng
out={}
# Lining: arcs and foot lines; clip below model extent per side
lin_ref=np.vstack([REF[k] for k in ['LIN_crown_outer','LIN_crown_inner','LIN_haunchL_outer','LIN_haunchL_inner','LIN_haunchR_outer','LIN_haunchR_inner']])
keepL,dL,rL=side_clip(lin_ref[lin_ref[:,0]<C0[0]],L,'L'); keepR,dR_,rR=side_clip(lin_ref[lin_ref[:,0]>C0[0]],L,'R')
crown=REF['LIN_crown_outer'];
out['LIN_arcs']=dist_stats(L, np.vstack([crown, REF['LIN_crown_inner'], keepL, keepR]), 'LIN')
out['LIN_arcs']['clipped_below_model_extent']={'L':dL,'R':dR_,'yrange_L':rL,'yrange_R':rR}
out['LIN_footlines']=dist_stats(L, np.vstack([REF['LIN_footL'],REF['LIN_footR']]), 'LIN_foot')
# per-arc max deviations (ref->model), by arc name
perarc={}
for k in ['LIN_crown_outer','LIN_crown_inner','LIN_haunchL_outer','LIN_haunchL_inner','LIN_haunchR_outer','LIN_haunchR_inner']:
R=REF[k]; tM=cKDTree(L); d,_=tM.query(R); perarc[k]=round(float(d.max())*1,1)
out['LIN_perarc_ref2model_max_mm']=perarc
# Excavation
exc_ref=np.vstack([REF['EXC_crown'],REF['EXC_haunchL'],REF['EXC_haunchR']])
keepE_L,eL,reL=side_clip(exc_ref,E,'L'); keepE_R,eR,reR=side_clip(exc_ref,E,'R')
out['EXC_arcs']=dist_stats(E, np.vstack([REF['EXC_crown'],keepE_L,keepE_R]),'EXC')
out['EXC_arcs']['clipped_below_model_extent']={'L':eL,'R':eR,'yrange_L':reL,'yrange_R':reR}
tE=cKDTree(E); d,_=tE.query(REF['EXC_crown']); out['EXC_crown_ref2model_max_mm']=round(float(d.max()),1)
# Pavement: top/bottom/sides
pav_ref=np.vstack([REF['PAV_top'],REF['PAV_bot'],REF['PAV_sideL'],REF['PAV_sideR']])
out['PAV']=dist_stats(P[(P[:,0]>9526.3-150)&(P[:,0]<18726.3+150)], pav_ref,'PAV')
# Base: model points restricted to road width (drawn span)
Bw=B[(B[:,0]>=9526.3-1)&(B[:,0]<=18726.3+1)]
out['BASE_drawn_span']=dist_stats(Bw, np.vstack([REF['BASE_top'],REF['BASE_bot']]),'BASE')
out['BASE_model_extent_x']=[round(float(B[:,0].min())),round(float(B[:,0].max()))]
out['BASE_model_ymin_ymax']=[round(float(B[:,1].min())),round(float(B[:,1].max()))]
# Drains: both ditches vs both references
dref=np.vstack([REF[k] for k in ['DRN_wallL1','DRN_wallL2','DRN_botL','DRN_pipeL','DRN_wallR1','DRN_wallR2','DRN_botR','DRN_pipeR']])
dm=D[(D[:,0]>8900)&(D[:,0]<19300)]
out['DRN']=dist_stats(dm, dref,'DRN')
# per-ditch: ref walls/bottom/pipe to model
tD=cKDTree(dm)
out['DRN_perref_ref2model_max_mm']={k: round(float(tD.query(REF[k])[0].max()),1) for k in ['DRN_wallL1','DRN_wallL2','DRN_botL','DRN_pipeL','DRN_wallR1','DRN_wallR2','DRN_botR','DRN_pipeR']}
out['DRN_model_xrange']=[round(float(dm[:,0].min())),round(float(dm[:,0].max()))]
out['DRN_model_yrange']=[round(float(dm[:,1].min())),round(float(dm[:,1].max()))]
return out
allres={}
for sig in [1,-1]:
for st in ['4745.0005','5100.0','5469.9995']:
r=run(sig,st,None); allres[f"{sig}|{st}"]=r
print(f"=== sigma={sig} st={st} slope={FIT[st]['slope_top']*sig:.5f}")
for k in ['LIN_arcs','EXC_arcs','PAV','BASE_drawn_span','DRN']:
v=r[k];
if v is None: print(' ',k,'none'); continue
print(f" {k:16s} ref->model max={v['ref_to_model_max']*1000:7.1f}mm mean={v['ref_to_model_mean']*1000:6.1f} | model->ref max={v['model_to_ref_max']*1000:7.1f}mm mean={v['model_to_ref_mean']*1000:6.1f}")
if 'clipped_below_model_extent' in v: print(' clipped', v['clipped_below_model_extent'])
print(' LIN foot lines model->ref / ref->model:', None if r['LIN_footlines'] is None else (round(r['LIN_footlines']['model_to_ref_max']*1000,1), round(r['LIN_footlines']['ref_to_model_max']*1000,1)))
print(' LIN per-arc ref->model max mm:', r['LIN_perarc_ref2model_max_mm'])
print(' EXC crown ref->model max mm:', r['EXC_crown_ref2model_max_mm'])
print(' BASE model x-range', r['BASE_model_extent_x'], 'y-range', r['BASE_model_ymin_ymax'])
print(' DRN per-ref ref->model max mm:', r['DRN_perref_ref2model_max_mm'], 'model x', r['DRN_model_xrange'], 'y', r['DRN_model_yrange'])
json.dump(allres, open('/tmp/verify_daesan2_run/hd2_results.json','w'), indent=1, default=str)
+44
View File
@@ -0,0 +1,44 @@
import sys, json, math
sys.path += ['/root/.local/lib/python3.13/site-packages','/usr/local/lib/python3.13/dist-packages','/usr/lib/python3/dist-packages']
import numpy as np
from scipy.spatial import cKDTree
from scipy.optimize import least_squares
import matplotlib; matplotlib.use('Agg'); import matplotlib.pyplot as plt
SEC = json.load(open('/tmp/verify_daesan2_run/sections_final.json'))
def pts_of(st, pred):
out=[]; ids=[]
for sid,v in SEC[st]['solids'].items():
if pred(v['group'], v['id']):
for poly in v['polys']:
out.append(np.array(poly)[:, :2]); ids.append(v['id'])
return (np.vstack(out) if out else np.zeros((0,2))), ids
def fit_circle(P):
x,y=P[:,0],P[:,1]
A=np.c_[2*x,2*y,np.ones(len(x))]; b=x*x+y*y
c,_,_,_=np.linalg.lstsq(A,b,rcond=None)
xc,yc=c[0],c[1]; r=math.sqrt(c[2]+xc*xc+yc*yc)
return xc,yc,r
# ---- lining crown fit per station
fits={}
for st in SEC:
P,_=pts_of(st, lambda g,i: g=='라이닝')
u0=(P[:,0].min()+P[:,0].max())/2; v0=P[:,1].max()-7.282
d=np.hypot(P[:,0]-u0,P[:,1]-v0)
sel_o=P[(np.abs(d-7.282)<0.03)&(P[:,1]-v0>4.0)]
sel_i=P[(np.abs(d-6.982)<0.03)&(P[:,1]-v0>4.0)]
co=fit_circle(sel_o); ci=fit_circle(sel_i)
fits[st]={'outer':co,'inner':ci,'n_o':len(sel_o),'n_i':len(sel_i)}
print(f"st {st}: outer n={len(sel_o)} centre=({co[0]:.4f},{co[1]:.4f}) R={co[2]:.4f} | inner n={len(sel_i)} centre=({ci[0]:.4f},{ci[1]:.4f}) R={ci[2]:.4f}")
# ---- pavement slope (top/bottom envelopes) per station
for st in SEC:
P,_=pts_of(st, lambda g,i: g=='노면(계획고)')
bins=np.arange(P[:,0].min(),P[:,0].max()+0.25,0.25)
tops=[];bots=[]
for a,b in zip(bins[:-1],bins[1:]):
m=(P[:,0]>=a)&(P[:,0]<b)
if m.sum()>0: tops.append((P[m,0].mean(),P[m,1].max())); bots.append((P[m,0].mean(),P[m,1].min()))
tops=np.array(tops); bots=np.array(bots)
mt=np.polyfit(tops[:,0],tops[:,1],1); mb=np.polyfit(bots[:,0],bots[:,1],1)
fits[st]['slope_top']=mt[0]; fits[st]['slope_bot']=mb[0]
print(f"st {st}: PAV top slope dv/du={mt[0]:.5f} (S={mt[0]*100:.3f}%), bottom slope={mb[0]:.5f}; top@u=0 {mt[1]:.4f}, bottom@u=0 {mb[1]:.4f}; u-range [{P[:,0].min():.3f},{P[:,0].max():.3f}]")
json.dump(fits, open('/tmp/verify_daesan2_run/fits.json','w'))
+77
View File
@@ -0,0 +1,77 @@
import sys, json, pickle, math
sys.path += ['/root/.local/lib/python3.13/site-packages','/usr/local/lib/python3.13/dist-packages','/usr/lib/python3/dist-packages']
from build123d import import_step, Edge, Shape
from OCP.BRepAlgoAPI import BRepAlgoAPI_Section
from OCP.gp import gp_Pln, gp_Pnt, gp_Dir
from OCP.BRepBuilderAPI import BRepBuilderAPI_MakeFace
from OCP.TopExp import TopExp_Explorer
from OCP.TopAbs import TopAbs_EDGE
from OCP.TopoDS import TopoDS
import numpy as np
solids = import_step("/mnt/user-data/uploads/97.과제/Code/3d-drawing/model/tunnel_solids.step").solids()
mp = pickle.load(open('/tmp/verify_daesan2_run/mapping.pkl','rb'))['mapping']
caps = json.load(open('/tmp/verify_daesan2_run/caps.json'))
ch = json.load(open('/tmp/verify_daesan2_run/chain.json'))
lin = ch['lin']
starts = {k: np.array(v) for k,v in ch['starts'].items()}
ends = {k: np.array(v) for k,v in ch['ends'].items()}
# station knots
S = [caps[k]['from_sta'] for k in lin] + [caps[lin[-1]]['to_sta']]
P = [starts[k] for k in lin] + [ends[lin[-1]]]
def plane_at(s):
for i in range(len(lin)):
if S[i] <= s <= S[i+1]:
f=(s-S[i])/(S[i+1]-S[i])
Q=(1-f)*P[i]+f*P[i+1]
T=(P[i+1]-P[i])[:2]; T=T/np.linalg.norm(T)
return Q, T, lin[i]
raise ValueError(s)
def slice_solid(idx, Q, T):
nrm=np.array([T[0],T[1],0.0])
pl=gp_Pln(gp_Pnt(*Q), gp_Dir(*nrm))
face=BRepBuilderAPI_MakeFace(pl,-80,80,-80,80).Face()
sec=BRepAlgoAPI_Section(solids[idx].wrapped, face); sec.Build()
sh=sec.Shape()
polys=[]
exp=TopExp_Explorer(sh, TopAbs_EDGE)
while exp.More():
e=Shape.cast(exp.Current())
L=e.length; N=max(2,int(L/0.02))
pts=[e.position_at(i/N) for i in range(N+1)]
polys.append([[p.X,p.Y,p.Z] for p in pts])
exp.Next()
return polys
out={}
stations=[4745.0005, 5100.0, 5469.9995]
for s in stations:
Q,T,lk=plane_at(s)
eh=np.array([-T[1],T[0]])
print(f"== station {s}: comp {lk}, Q=({Q[0]:.4f},{Q[1]:.4f},{Q[2]:.4f}) T=({T[0]:.5f},{T[1]:.5f}) ")
out[str(s)]={'Q':Q.tolist(),'T':T.tolist(),'eh':eh.tolist(),'comps':{}}
for cid,info in caps.items():
if not (info['from_sta'] <= s <= info['to_sta']): continue
polys=slice_solid(mp[cid], Q, T)
uv=[]
for pl_ in polys:
arr=np.array(pl_)
dx=arr[:,:2]-Q[:2]
u=dx@eh; v=arr[:,2]
uv.append(np.stack([u,v],1).tolist())
out[str(s)]['comps'][cid]={'group':info['group'],'polys':uv}
allp=np.vstack([np.array(x) for x in uv]) if uv else np.zeros((0,2))
if info['group'] in ('라이닝','굴착면','노면(계획고)','기층·인버트(가정)','공동구(가정)') or cid.startswith('DRN') or 'DRN' in cid or 'HI' in cid:
pass
# summary per group
import collections
summ=collections.defaultdict(list)
for cid,v in out[str(s)]['comps'].items():
summ[v['group']].append(cid)
for g,cids in summ.items():
allp=[p for cid in cids for poly in out[str(s)]['comps'][cid]['polys'] for p in poly]
A=np.array(allp) if allp else np.zeros((0,2))
if len(A):
print(f" {g}: n_comp={len(cids)} n_pts={len(A)} u[{A[:,0].min():.3f},{A[:,0].max():.3f}] v[{A[:,1].min():.3f},{A[:,1].max():.3f}] ids={cids[:4]}")
else:
print(f" {g}: n_comp={len(cids)} EMPTY ids={cids[:4]}")
json.dump(out, open('/tmp/verify_daesan2_run/sections_raw.json','w'))
print("saved sections_raw.json")