# encoding: UTF-8
# side5 — 사이드막음판 A/B 겹침 근본 해소.
#   원인: 하판(경사 슬래브)이 막음판 폭 전체를 가로질러 지나가므로, 하판을 빼면 판이 위/아래 두 덩이로
#         분리된다 -> SketchUp subtract 가 결과를 못 만들고 원본을 그대로 돌려줬다(제거 0mm3).
#   해법: 하판 위쪽 부분만 남긴다(아래쪽은 하판 밑이라 불필요). 외곽 폭·상단 높이는 CEO 값 유지.
HB = 'E:/도진팩토리/3D스캔및티칭시스템/_자동화/inbox/_보류/_side5_heartbeat.txt'
def hb(s)
  begin
    File.open(HB, 'a:UTF-8') { |f| f.write("[#{Time.now.strftime('%H:%M:%S')}] #{s}\n"); f.flush }
  rescue Exception
    nil
  end
end
MM = 25.4
def vol(g); (g.volume rescue -1) * (25.4**3); end
def ents_of(e); e.is_a?(Sketchup::ComponentInstance) ? e.definition.entities : e.entities; end
def collect(ent, tr, faces)
  ents_of(ent).each do |e|
    case e
    when Sketchup::Face
      pts = e.vertices.map { |v| v.position.transform(tr) }
      n = e.normal.transform(tr); n.normalize!
      faces << [pts, n]
    when Sketchup::Group, Sketchup::ComponentInstance
      collect(e, tr * e.transformation, faces)
    end
  end
end
def zup(faces, x, y)
  res = []
  faces.each do |pts, n|
    next if n.z < 0.05
    inside = false
    j = pts.length - 1
    (0...pts.length).each do |i|
      xi = pts[i].x; yi = pts[i].y; xj = pts[j].x; yj = pts[j].y
      inside = !inside if ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi)
      j = i
    end
    next unless inside
    p0 = pts[0]
    d = n.x * (x - p0.x) + n.y * (y - p0.y)
    res << (p0.z - d / n.z)
  end
  res
end
m = Sketchup.active_model
out = []
hb("START")
won = m.entities.grep(Sketchup::Group).find { |g| g.name == "조립부원판_20260919" }
pa = m.entities.grep(Sketchup::Group).find { |g| g.name == "사이드막음판_9T_A_20260919" }
pb = m.entities.grep(Sketchup::Group).find { |g| g.name == "사이드막음판_9T_B_20260919" }
fw = []; collect(won, won.transformation, fw)
# CEO 외곽값 확보
ab = pa.bounds; bb = pb.bounds
x1 = [ab.min.x, bb.min.x].min * 25.4
x2 = [ab.max.x, bb.max.x].max * 25.4
ztop = [ab.max.z, bb.max.z].max * 25.4
out << "CEO 외곽 유지: x #{x1.round(2)}..#{x2.round(2)} (폭 #{(x2 - x1).round(2)}), 상단 z #{ztop.round(2)}"
# 하판 상면 라인 (두 지점 실측 -> 직선)
ys = 11376.0
sx1 = x1 + 5.0; sx2 = x2 - 5.0
z1a = zup(fw, sx1 / MM, ys / MM); z2a = zup(fw, sx2 / MM, ys / MM)
if z1a.empty? || z2a.empty?
  out << "하판 상면 실측 실패 -> 중단"
  hb("ABORT")
else
  za = z1a.max * 25.4; zb2 = z2a.max * 25.4
  slope = (zb2 - za) / (sx2 - sx1)
  ztop_line = lambda { |x| za + slope * (x - sx1) }
  out << "하판 상면 라인: z(#{sx1.round(1)})=#{za.round(2)}, z(#{sx2.round(1)})=#{zb2.round(2)}, 기울기 #{(Math.atan(-slope) * 180 / Math::PI).round(4)}도"
  begin
    base = File.basename(m.path, '.skp'); dir = File.dirname(m.path)
    m.save_copy(File.join(dir, "#{base}.bak_#{Time.now.strftime('%Y%m%d_%H%M%S')}_side5직전.skp"))
    hb("BACKUP OK")
    m.start_operation("사이드막음판 하판 위쪽만 남기고 재작성", true)
    [[pa, 10870.34, "사이드막음판_9T_A_20260919"], [pb, 11872.77, "사이드막음판_9T_B_20260919"]].each do |old, yface, nm|
      old.erase!
      g = m.entities.add_group
      poly = [
        Geom::Point3d.new(x1 / MM, yface / MM, ztop / MM),
        Geom::Point3d.new(x2 / MM, yface / MM, ztop / MM),
        Geom::Point3d.new(x2 / MM, yface / MM, ztop_line.call(x2) / MM),
        Geom::Point3d.new(x1 / MM, yface / MM, ztop_line.call(x1) / MM)
      ]
      f = g.entities.add_face(poly)
      f.pushpull(f.normal.y > 0 ? 9.0 / MM : -9.0 / MM)
      g.name = nm
      b = g.bounds
      # y 위치 보정
      hb("#{nm} solid=#{g.manifold?}")
      out << "#{nm}: solid=#{g.manifold?} x #{(b.min.x*25.4).round(2)}..#{(b.max.x*25.4).round(2)} y #{(b.min.y*25.4).round(2)}..#{(b.max.y*25.4).round(2)} z #{(b.min.z*25.4).round(2)}..#{(b.max.z*25.4).round(2)} 좌높이 #{(ztop - ztop_line.call(x1)).round(2)}mm 우높이 #{(ztop - ztop_line.call(x2)).round(2)}mm 체적 #{vol(g).round(0)}mm3"
    end
    m.commit_operation
    hb("COMMIT")
  rescue Exception => e
    m.commit_operation rescue nil
    out << "ERR #{e.class}: #{e.message}"
    hb("ERR #{e.message}")
  end
end
# 확대 캡처 2장 (A단, B단)
v = m.active_view
[["A", 10879.34, 'E:/도진팩토리/3D스캔및티칭시스템/_자동화/inbox/_보류/_capA.png'],
 ["B", 11872.77, 'E:/도진팩토리/3D스캔및티칭시스템/_자동화/inbox/_보류/_capB.png']].each do |lb, yy, path|
  eye = Geom::Point3d.new(9600 / 25.4, (yy + (lb == "A" ? -900.0 : 900.0)) / 25.4, 420 / 25.4)
  tgt = Geom::Point3d.new(9020 / 25.4, yy / 25.4, 110 / 25.4)
  v.camera = Sketchup::Camera.new(eye, tgt, Geom::Vector3d.new(0, 0, 1))
  out << "capture#{lb}=#{v.write_image(path, 1400, 900, true)}"
end
puts out.join("\n")
hb("END")
"SIDE5"
