# frozen_string_literal: true # Regenerate only the stand, preserving the source model and its print files. # Usage: bundle exec ruby script/generate_stand.rb CONFIG NEW_OUTPUT_DIRECTORY require "bundler/setup" $LOAD_PATH.unshift(File.expand_path("../lib", __dir__)) require "moon_model" abort "Usage: #{$PROGRAM_NAME} CONFIG NEW_OUTPUT_DIRECTORY" unless ARGV.length == 2 source, output = ARGV.map { |path| File.expand_path(path) } abort "Output already exists: #{output}; choose a new directory" if File.exist?(output) config = MoonModel::Config.new(YAML.safe_load_file(source, aliases: false)) sizing = MoonModel::Sizing.calculate(config, MoonModel::DataSet.new, validate_safety: false) profile = { "envelope_mm" => sizing.final_envelope_mm, "minimum_radius_mm" => sizing.minimum_radius_mm, "maximum_radius_mm" => sizing.maximum_radius_mm } result = MoonModel::Stand.build(config, profile) FileUtils.mkdir_p(File.dirname(output)) temporary = Dir.mktmpdir(".stand-", File.dirname(output)) begin MoonModel::ThreeMF.write(File.join(temporary, "orbital_triskelion_stand.3mf"), [result.mesh], metadata: { "Part" => "material-unassigned stand", "SourceConfig" => source }, palette: config["palette"]) config.save(File.join(temporary, "config.yml")) File.write(File.join(temporary, "statistics.json"), JSON.pretty_generate(result.statistics) + "\n") File.write(File.join(temporary, "README.md"), <<~MD) # Revised orbital stand Generated from `#{source}`. The source Moon and original stand were not modified. Three curved stems fork toward neighboring pads; each concave pad joins two branches. The mesh is one connected, watertight, material-unassigned solid. Dimensions: #{result.statistics["dimensions_mm"].map { |v| format("%.2f", v) }.join(" × ")} mm. #{result.statistics["print_guidance"]} Inspect the sliced layer preview before printing. Start with build-plate-only supports; adjust local support painting beneath the cradles if automatic supports wrap around them. The generator reports geometric overhangs, not a guarantee of a slicer's support layout. The dish follows the conservative lunar sphere; terrain peaks may still limit actual contact. See `statistics.json` for sizing, clearance, connectivity, and overhang measurements. MD File.rename(temporary, output) ensure FileUtils.remove_entry(temporary) if File.directory?(temporary) end puts output puts JSON.pretty_generate(result.statistics)