# encoding: UTF-8
# lvl1 — 주황판(조립부원판_20260919) 을 완전 수평(0.000도)으로 재배치. 형상 무변경, 자세만 회전.
#   회전축: Y축, 피벗 = 상면 중심(상면 z 평균) -> 판의 높이 레벨 유지.
HB = 'E:/도진팩토리/3D스캔및티칭시스템/_자동화/inbox/_보류/_lvl1_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
def r1(v); (v * 25.4).round(2); end
def deg(a); (a * 180.0 / Math::PI); 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, e.area * 645.16]
    when Sketchup::Group, Sketchup::ComponentInstance
      collect(e, tr * e.transformation, faces)
    end
  end
end
def top_face(obj)
  fs = []
  collect(obj, obj.transformation, fs)
  fs.select { |_, n, _| n.z > 0.5 }.max_by { |_, _, a| a }
end

m = Sketchup.active_model
out = []
hb("START")
obj = m.entities.find { |e| e.is_a?(Sketchup::Group) && e.name == "조립부원판_20260919" }
if obj.nil?
  out << "조립부원판_20260919 없음 -> 중단"
  hb("ABORT none")
else
  pts, n, a = top_face(obj)
  tilt0 = deg(Math.acos([n.z, 1.0].min))
  cx = pts.map { |p| p.x }.inject(:+) / pts.length
  cy = pts.map { |p| p.y }.inject(:+) / pts.length
  cz = pts.map { |p| p.z }.inject(:+) / pts.length
  out << "전: 상면 법선=(#{n.x.round(5)},#{n.y.round(5)},#{n.z.round(5)}) 기울기=#{tilt0.round(4)}도, 상면중심 z=#{r1(cz)}, 상면 z범위 #{r1(pts.map { |p| p.z }.min)}..#{r1(pts.map { |p| p.z }.max)}"
  b0 = obj.bounds
  out << "전: bbox z #{r1(b0.min.z)}..#{r1(b0.max.z)}"
  if tilt0 < 0.001
    out << "이미 수평 -> 변경 없음"
    hb("ALREADY LEVEL")
  else
    begin
      base = File.basename(m.path, '.skp'); dir = File.dirname(m.path)
      bak = File.join(dir, "#{base}.bak_#{Time.now.strftime('%Y%m%d_%H%M%S')}_lvl1직전.skp")
      m.save_copy(bak)
      out << "사전 백업 OK -> #{bak}"
      hb("BACKUP OK")
      ang = -Math.atan2(n.x, n.z)   # Y축 회전으로 법선을 +Z 로
      pivot = Geom::Point3d.new(cx, cy, cz)
      m.start_operation("주황판 수평 정렬", true)
      obj.transform!(Geom::Transformation.rotation(pivot, Geom::Vector3d.new(0, 1, 0), ang))
      m.commit_operation
      hb("ROT1 DONE ang=#{deg(ang).round(4)}")
      pts2, n2, = top_face(obj)
      tilt1 = deg(Math.acos([[n2.z, 1.0].min, -1.0].max))
      out << "회전 적용 #{deg(ang).round(4)}도 -> 기울기 #{tilt1.round(4)}도"
      if tilt1 > 0.01
        m.start_operation("주황판 수평 정렬 보정", true)
        obj.transform!(Geom::Transformation.rotation(pivot, Geom::Vector3d.new(0, 1, 0), -2 * ang))
        m.commit_operation
        pts2, n2, = top_face(obj)
        tilt1 = deg(Math.acos([[n2.z, 1.0].min, -1.0].max))
        out << "역방향 보정 적용 -> 기울기 #{tilt1.round(4)}도"
        hb("ROT2 DONE")
      end
      zs2 = pts2.map { |p| p.z }
      b1 = obj.bounds
      out << "후: 상면 법선 z=#{n2.z.round(6)} 기울기=#{tilt1.round(4)}도, 상면 z #{r1(zs2.min)}..#{r1(zs2.max)} (평탄도 #{r1(zs2.max - zs2.min)}mm)"
      out << "후: bbox z #{r1(b1.min.z)}..#{r1(b1.max.z)}"
      out << "참고: 윈치 베이스판 상면 z=181.8 (수평 0.0도)"
      hb("VERIFY OK tilt=#{tilt1.round(4)}")
    rescue Exception => e
      m.commit_operation rescue nil
      out << "ERR #{e.class}: #{e.message}"
      hb("ERR #{e.message}")
    end
  end
end
puts out.join("\n")
hb("END")
"LVL1"
