# encoding: UTF-8
# ridge1 — 하판(조립부원판) 민 철판부에 hat(집지붕) 성형 능선 추가 + union 으로 같은 판의 성형부화
#   사양: 폭 100, 깊이 25(하방 돌출), 상부폭 60(지붕각), 양단 램프 20
#   ③ 윈치 브라켓용 selection 도 함께 읽어 보고
HB = 'E:/도진팩토리/3D스캔및티칭시스템/_자동화/inbox/_보류/_ridge_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.outer_loop.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.abs < 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
def zdn(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")
# ③ selection 먼저
sel = m.selection.to_a
out << "[③ selection] #{sel.length}개" + (sel.empty? ? " (비어 있음 -> 윈치 브라켓 미지정)" : "")
sel.each do |e|
  nm = e.respond_to?(:name) ? e.name : ""
  nm = e.definition.name if nm.to_s.empty? && e.respond_to?(:definition)
  b = e.bounds
  out << "   #{e.class} #{nm.inspect} bbox (#{(b.min.x*25.4).round(1)},#{(b.min.y*25.4).round(1)},#{(b.min.z*25.4).round(1)})..(#{(b.max.x*25.4).round(1)},#{(b.max.y*25.4).round(1)},#{(b.max.z*25.4).round(1)})"
end
won = m.entities.grep(Sketchup::Group).find { |g| g.name == "조립부원판_20260919" }
if won.nil?
  out << "하판 없음 -> 중단"
else
  fw = []; collect(won, won.transformation, fw)
  wb = won.bounds
  x0 = wb.min.x * 25.4; x1 = wb.max.x * 25.4
  y0 = wb.min.y * 25.4; y1 = wb.max.y * 25.4
  out << "하판: x #{x0.round(1)}..#{x1.round(1)} y #{y0.round(1)}..#{y1.round(1)} z #{(wb.min.z*25.4).round(1)}..#{(wb.max.z*25.4).round(1)} 체적 #{vol(won).round(0)}mm3"
  # 능선 배치: 볼팅면(양 끝 y) 피해 중앙부, x 는 박스 아래 평판부 2줄
  ry0 = 10950.0; ry1 = 11800.0
  ramp = 20.0
  wtop = 100.0; wbot = 60.0; dep = 25.0
  xs_c = [8950.0, 9130.0]
  made = []
  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')}_ridge직전.skp"))
    hb("BACKUP OK")
    m.start_operation("하판 hat 능선 성형", true)
    body = won
    xs_c.each_with_index do |xc, idx|
      # 하판 하면 z (해당 x, 중앙 y)
      zb = zdn(fw, xc / MM, ((ry0 + ry1) / 2.0) / MM)
      if zb.empty?
        zu = zup(fw, xc / MM, ((ry0 + ry1) / 2.0) / MM)
        next if zu.empty?
        zbot = zu.min * 25.4
      else
        zbot = zb.min * 25.4
      end
      g = m.entities.add_group
      # 단면(x-z): 지붕형 사다리꼴, 하판 하면에서 아래로 dep
      pts = [
        Geom::Point3d.new((xc - wtop / 2) / MM, ry0 / MM, (zbot + 2.0) / MM),
        Geom::Point3d.new((xc + wtop / 2) / MM, ry0 / MM, (zbot + 2.0) / MM),
        Geom::Point3d.new((xc + wbot / 2) / MM, ry0 / MM, (zbot - dep) / MM),
        Geom::Point3d.new((xc - wbot / 2) / MM, ry0 / MM, (zbot - dep) / MM)
      ]
      f = g.entities.add_face(pts)
      f.pushpull(f.normal.y > 0 ? -(ry1 - ry0) / MM : (ry1 - ry0) / MM)
      hb("RIDGE#{idx} solid=#{g.manifold?}")
      if g.manifold?
        u = body.union(g)
        body = u if u
        hb("UNION#{idx} -> #{u.class}")
        made << [xc, zbot]
      else
        g.erase!
      end
    end
    body.name = "조립부원판_20260919"
    m.commit_operation
    hb("COMMIT")
    out << "능선 #{made.length}줄 성형(하판과 union = 같은 판의 성형부, 용접 0)"
    made.each_with_index { |(xc, zb), i| out << "   능선#{i + 1}: x중심 #{xc}, 폭 상100/하60, 깊이 #{dep}mm(하방), 길이 #{(ry1 - ry0).round(0)}mm (y #{ry0}~#{ry1}), 하판 하면 z #{zb.round(2)}" }
    out << "하판 결과: solid=#{body.manifold?} 체적 #{vol(body).round(0)}mm3 면수 #{body.entities.grep(Sketchup::Face).length}"
  rescue Exception => e
    m.commit_operation rescue nil
    out << "ERR #{e.class}: #{e.message}"
    hb("ERR #{e.message}")
  end
end
v = m.active_view
v.camera = Sketchup::Camera.new(Geom::Point3d.new(8300 / 25.4, 10200 / 25.4, -300 / 25.4), Geom::Point3d.new(9020 / 25.4, 11380 / 25.4, 100 / 25.4), Geom::Vector3d.new(0, 0, 1))
out << "capture=#{v.write_image('E:/도진팩토리/3D스캔및티칭시스템/_자동화/inbox/_보류/_capR.png', 1400, 900, true)}"
puts out.join("\n")
hb("END")
"RIDGE1"
