# encoding: UTF-8
m = Sketchup.active_model
MM = 1.0/25.4
mL, xL0, zL0 = 0.1129672, 4423.8, 20.4
cL = 1.0/Math.sqrt(1+mL*mL)
uu = lambda{|x| (x-xL0)/cL}
zL = lambda{|x| zL0 - mL*(x-xL0)}
XN, XW, X1 = 3940.0, 3990.0, 4410.0
T, R, ZTOP, ZUND = 12.0, 30.0, 122.0, 110.0
YMID = 8142.55; mir = lambda{|y| 2*YMID-y}
FY0, FY1 = 7496.2, 7567.2          # 좌 볼팅 플랜지 (경사면 위, 평면)
LY0, LY1 = 7567.2, 7579.2          # 좌 다리(측판)
TY0, TY1 = 7567.2, mir.call(7567.2)# 상판 = 윈치 결합면 (수평)
BOLTX=[3980.0,4075.0,4170.0,4265.0,4360.0]
HX=[4140.0,4240.0]; HY=[7942.55,mir.call(7942.55)]
RINGX=[4115.0,4190.0,4265.0]; RINGY=[7917.55,8142.55,mir.call(7917.55)]
EYEX=[4120.0,4260.0]; EYEY=[7760.0,mir.call(7760.0)]
BEAD=[[4000.0,4100.0],[4280.0,4380.0]]; BY0,BY1 = 7620.0, mir.call(7620.0); BD=15.0
def dedup(p,t=1e-4); o=[]; p.each{|q| o<<q unless o.last && (o.last[0]-q[0]).abs<t && (o.last[1]-q[1]).abs<t}; o.pop if o.length>2 && (o.first[0]-o.last[0]).abs<t && (o.first[1]-o.last[1]).abs<t; o; end
def rrect(x0,x1,y0,y1,r,seg=8)
  p=[]; [[x1-r,y0+r,-90.0,0.0],[x1-r,y1-r,0.0,90.0],[x0+r,y1-r,90.0,180.0],[x0+r,y0+r,180.0,270.0]].each{|cx,cy,a0,a1|
    (0..seg).each{|i| a=(a0+(a1-a0)*i/seg.to_f)*Math::PI/180.0; p << [cx+r*Math.cos(a), cy+r*Math.sin(a)]}}; dedup(p)
end
def circ(cx,cy,r,n=20); (0...n).map{|i| a=2*Math::PI*i/n; [cx+r*Math.cos(a), cy+r*Math.sin(a)]}; end
hmax, hmin = ZUND-zL.call(X1), ZUND-zL.call(XW)
puts "다리 높이 최대 #{hmax.round(2)} @X#{X1} / 최소 #{hmin.round(2)} @X#{XW}  경사흡수 #{(hmax-hmin).round(2)}"
trL = Geom::Transformation.axes(Geom::Point3d.new(xL0*MM,0,zL0*MM),
        Geom::Vector3d.new(1,0,-mL).normalize, Geom::Vector3d.new(0,1,0), Geom::Vector3d.new(mL,0,1).normalize)
begin
  m.start_operation("v5 개구부 하향 재구성", true)
  oldv5 = m.entities.grep(Sketchup::Group).find{|g| g.name=="하부마운트커버_v5_20260919"}
  if oldv5; oldv5.name="하부마운트커버_v5_구안_hidden"; oldv5.hidden=true; puts "  v5 구안 rename+hidden"; end
  root = m.entities.add_group; root.name = "하부마운트커버_v5_20260919"
  # === 상판 = 윈치 결합면(수평) + 비드창/점검구/M12/아이볼트 ===
  cuts=[]
  cuts << rrect(HX[0],HX[1],HY[0],HY[1],R)
  RINGX.each{|x| RINGY.each{|y| next if x==RINGX[1]&&y==RINGY[1]; cuts << circ(x,y,6.5)}}
  EYEX.each{|x| EYEY.each{|y| cuts << circ(x,y,9.0)}}
  BEAD.each{|a,b| cuts << [[a,BY0],[b,BY0],[b,BY1],[a,BY1]]}
  g = root.entities.add_group; g.name="부재1a_상판_결합면_수평"
  e = g.entities
  f = e.add_face(rrect(XW,X1,TY0,TY1,R).map{|x,y| Geom::Point3d.new(x*MM,y*MM,ZUND*MM)})
  ma=f.area
  cuts.each{|lp| e.add_face(lp.map{|x,y| Geom::Point3d.new(x*MM,y*MM,ZUND*MM)})}
  e.grep(Sketchup::Face).each{|ff| ff.erase! if ff.valid? && ff.area < ma*0.5}
  big = e.grep(Sketchup::Face).select{|ff| ff.valid?}.max_by{|ff| ff.area}
  big.pushpull((big.normal.z>0 ? T : -T)*MM)
  puts "  상판 solid=#{g.manifold?}"
  # === 성형 비드 2개 (상판 같은 판의 성형부, 하방 돌출) ===
  BEAD.each_with_index do |(xa,xb),i|
    prof=[[xa,ZTOP],[xa+20,ZTOP-BD],[xb-20,ZTOP-BD],[xb,ZTOP],[xb,ZUND],[xb-20,ZUND-BD],[xa+20,ZUND-BD],[xa,ZUND]]
    gb = root.entities.add_group; gb.name="부재1b_성형비드#{i+1}_W100xD15"
    fb = gb.entities.add_face(prof.map{|x,z| Geom::Point3d.new(x*MM,BY0*MM,z*MM)})
    fb.pushpull((fb.normal.y>0 ? (BY1-BY0) : -(BY1-BY0))*MM)
    puts "  비드#{i+1} solid=#{gb.manifold?}"
  end
  # === 다리(측판) 좌/우 : 높이 가변 (경사 흡수) ===
  [["부재1c_다리좌_H가변",LY0,LY1],["부재1d_다리우_H가변",mir.call(LY1),mir.call(LY0)]].each do |nm,ya,yb|
    gg = root.entities.add_group; gg.name=nm
    ff = gg.entities.add_face([[XW,zL.call(XW)],[X1,zL.call(X1)],[X1,ZUND],[XW,ZUND]].map{|x,z| Geom::Point3d.new(x*MM,ya*MM,z*MM)})
    ff.pushpull((ff.normal.y>0 ? (yb-ya) : -(yb-ya))*MM)
    puts "  #{nm} solid=#{gg.manifold?} 높이 #{(ZUND-zL.call(XW)).round(1)}~#{(ZUND-zL.call(X1)).round(1)}"
  end
  # === 볼팅 플랜지 좌/우 (경사면 위 평면) + 선단 업스윕 ===
  loc = root.entities.add_group; loc.name="_경사면부"
  [["부재1e_볼팅플랜지좌",FY0,FY1,7518.0],["부재1f_볼팅플랜지우",mir.call(FY1),mir.call(FY0),mir.call(7518.0)]].each do |nm,ya,yb,yb2|
    gg = loc.entities.add_group; gg.name=nm
    ee = gg.entities
    ff = ee.add_face([[uu.call(XN),ya],[uu.call(X1),ya],[uu.call(X1),yb],[uu.call(XN),yb]].map{|x,y| Geom::Point3d.new(x*MM,y*MM,0)})
    maa=ff.area
    BOLTX.each{|x| ee.add_face(circ(uu.call(x),yb2,9.0).map{|a,b| Geom::Point3d.new(a*MM,b*MM,0)})}
    ee.grep(Sketchup::Face).each{|q| q.erase! if q.valid? && q.area < maa*0.5}
    bb = ee.grep(Sketchup::Face).select{|q| q.valid?}.max_by{|q| q.area}
    bb.pushpull((bb.normal.z>0 ? -T : T)*MM)
    puts "  #{nm} solid=#{gg.manifold?}"
  end
  a=30.0*Math::PI/180.0; dxv,dzv=-Math.cos(a),Math.sin(a); pxv,pzv=-0.5,-Math.cos(a)
  U0=uu.call(XN); tipo=[U0+60.0*dxv,60.0*dzv]; tipi=[tipo[0]+T*pxv,tipo[1]+T*pzv]
  [["부재1g_선단업스윕좌",FY0,FY1],["부재1h_선단업스윕우",mir.call(FY1),mir.call(FY0)]].each do |nm,ya,yb|
    gg = loc.entities.add_group; gg.name=nm
    ff = gg.entities.add_face([[U0,0.0],tipo,tipi,[U0+T*pxv,T*pzv],[U0,-T]].map{|x,z| Geom::Point3d.new(x*MM,ya*MM,z*MM)})
    ff.pushpull((ff.normal.y>0 ? (yb-ya) : -(yb-ya))*MM)
    puts "  #{nm} solid=#{gg.manifold?}"
  end
  loc.transformation = trL
  m.commit_operation
rescue Exception => ex
  m.commit_operation rescue nil
  puts "ERR #{ex.class}: #{ex.message}"; puts ex.backtrace[0,3].join("\n")
end
"V5R"
