#!/usr/bin/env ruby # frozen_string_literal: true require "fileutils" require "open3" ROOT = File.expand_path(__dir__) SCAD = File.join(ROOT, "copy_stand.scad") OPENSCAD = ENV.fetch("OPENSCAD", "openscad") PYTHON = ENV.fetch("PYTHON", "python3") EXPORT_3MF = File.join(ROOT, "exports", "3mf") EXPORT_STL = File.join(ROOT, "exports", "stl") COMPONENT_3MF = File.join(EXPORT_3MF, "components") COMPONENT_STL = File.join(EXPORT_STL, "components") def run!(*command) puts command.join(" ") output, status = Open3.capture2e(*command) puts output unless output.empty? raise "Command failed: #{command.join(' ')}" unless status.success? end def export(part, directory, extension) output = File.join(directory, "#{part}.#{extension}") return output if ENV["REBUILD"] != "1" && File.size?(output) run!(OPENSCAD, "-o", output, "-D", %(part="#{part}"), SCAD) output end def export_segment(part, index, directory, extension) output = File.join(directory, "#{part}_#{index}.#{extension}") return output if ENV["REBUILD"] != "1" && File.size?(output) run!(OPENSCAD, "-o", output, "-D", %(part="#{part}"), "-D", "segment_index=#{index}", SCAD) output end def package(output, title, bases, accents = []) command = [ PYTHON, File.join(ROOT, "tools", "package_multimaterial_3mf.py"), output, "--title", title ] bases.each { |path| command.concat(["--base", path]) } accents.each { |path| command.concat(["--accent", path]) } run!(*command) end FileUtils.mkdir_p([EXPORT_3MF, EXPORT_STL, COMPONENT_3MF, COMPONENT_STL]) %w[base_accent socket coupon coupon_black coupon_key coupon_accent].each do |part| export(part, EXPORT_3MF, "3mf") end %w[socket coupon coupon_key].each do |part| export(part, EXPORT_STL, "stl") end base_segments = (0..5).map do |index| export_segment("base_segment", index, COMPONENT_3MF, "3mf") end black_segments = (0..5).map do |index| export_segment("base_black_segment", index, COMPONENT_3MF, "3mf") end package(File.join(EXPORT_3MF, "base.3mf"), "Copy Stand Base - Single Color", base_segments) package(File.join(EXPORT_3MF, "base_black.3mf"), "Copy Stand Base - Black Mesh", black_segments) package(File.join(EXPORT_3MF, "base_multicolor.3mf"), "Copy Stand Base", black_segments, [File.join(EXPORT_3MF, "base_accent.3mf")]) package(File.join(EXPORT_3MF, "coupon_multicolor.3mf"), "Copy Stand Calibration Coupon", [File.join(EXPORT_3MF, "coupon_black.3mf"), File.join(EXPORT_3MF, "coupon_key.3mf")], [File.join(EXPORT_3MF, "coupon_accent.3mf")]) # A multi-solid ASCII STL fallback avoids forcing OpenSCAD/CGAL to union the # large stepped base in one expensive operation. Slicers merge the touching # solids at slice time. segment_stls = (0..5).map do |index| export_segment("base_segment", index, COMPONENT_STL, "stl") end File.open(File.join(EXPORT_STL, "base.stl"), "wb") do |combined| segment_stls.each { |path| combined.write(File.binread(path)) } end puts "Exports written to #{File.join(ROOT, 'exports')}"