Initial MoonModel implementation
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class StandTest < Minitest::Test
|
||||
def setup
|
||||
@config = MoonModel::Config.new("stand" => { "enabled" => true })
|
||||
end
|
||||
|
||||
def test_orbital_stand_is_printable_and_scales_for_common_moons
|
||||
[30.0, 120.0, 203.2].each do |diameter|
|
||||
result = MoonModel::Stand.build(@config, diameter)
|
||||
stats = result.statistics
|
||||
|
||||
assert result.mesh.valid?
|
||||
assert_manifold result.mesh
|
||||
assert_equal "orbital_triskelion_stand", result.mesh.name
|
||||
assert_equal "orbital_triskelion", stats["style"]
|
||||
assert_in_delta 0.0, result.mesh.bounds[2][0], 1e-9
|
||||
assert_operator stats["maximum_underside_slope"], :<=, 1.0
|
||||
assert_operator stats["contact_radius_mm"], :<, diameter / 2.0
|
||||
assert_operator stats["contact_height_mm"], :>, stats["moon_bottom_clearance_mm"]
|
||||
assert_in_delta 10.0, stats["guaranteed_hub_clearance_mm"], 1e-9
|
||||
assert_in_delta stats["hub_height_mm"] + 10.0, stats["guaranteed_moon_bottom_height_mm"], 1e-9
|
||||
refute stats["bed_capped"]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_eight_inch_exaggerated_moon_clears_hub_in_any_rotation
|
||||
config = MoonModel::Config.new(
|
||||
"size" => { "input" => "diameter", "value" => 8.0, "unit" => "in", "meaning" => "datum" },
|
||||
"vertical_exaggeration" => 5.0,
|
||||
"stand" => { "enabled" => true },
|
||||
"resolution" => { "longitude_segments" => 96 }
|
||||
)
|
||||
sizing = MoonModel::Sizing.calculate(config, MoonModel::DataSet.new)
|
||||
result = MoonModel::Stand.build(config, "envelope_mm" => sizing.final_envelope_mm,
|
||||
"minimum_radius_mm" => sizing.minimum_radius_mm,
|
||||
"maximum_radius_mm" => sizing.maximum_radius_mm)
|
||||
stats = result.statistics
|
||||
|
||||
assert_equal "terrain_envelope_v2", stats["safety_model"]
|
||||
assert_in_delta 10.0, stats["guaranteed_hub_clearance_mm"], 1e-9
|
||||
assert_operator stats["guaranteed_moon_bottom_height_mm"], :>=, stats["hub_height_mm"] + 10.0
|
||||
assert_operator stats["contact_height_mm"], :>, 30.0
|
||||
assert_operator stats["maximum_underside_slope"], :<=, 1.0
|
||||
end
|
||||
|
||||
def test_custom_clearance_changes_guaranteed_gap
|
||||
config = MoonModel::Config.new("stand" => { "enabled" => true, "base_clearance_mm" => 4.0 })
|
||||
result = MoonModel::Stand.build(config, 120.0)
|
||||
|
||||
assert_in_delta 4.0, result.statistics["guaranteed_hub_clearance_mm"], 1e-9
|
||||
end
|
||||
|
||||
def test_large_moon_stand_is_capped_to_usable_bed
|
||||
result = MoonModel::Stand.build(@config, 463.0)
|
||||
usable = @config.build_volume.first(2).map { |value| value - 2.0 * @config["edge_clearance_mm"] }
|
||||
|
||||
assert result.statistics["bed_capped"]
|
||||
assert_operator result.statistics["dimensions_mm"][0], :<=, usable[0]
|
||||
assert_operator result.statistics["dimensions_mm"][1], :<=, usable[1]
|
||||
assert_operator result.statistics["dimensions_mm"][2], :<=, @config.build_volume[2]
|
||||
end
|
||||
|
||||
def test_legacy_stand_configuration_gets_new_style
|
||||
config = MoonModel::Config.new("stand" => { "enabled" => true, "material" => nil })
|
||||
|
||||
assert_equal "orbital_triskelion", config["stand"]["style"]
|
||||
assert_equal 10.0, config["stand"]["base_clearance_mm"]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_manifold(mesh)
|
||||
edge_uses = Hash.new(0)
|
||||
mesh.triangles.each do |triangle|
|
||||
triangle.each_index do |index|
|
||||
edge = [triangle[index], triangle[(index + 1) % triangle.length]].sort
|
||||
edge_uses[edge] += 1
|
||||
end
|
||||
end
|
||||
|
||||
assert edge_uses.values.all? { |count| count == 2 }, "every mesh edge must belong to exactly two faces"
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user