Initial MoonModel implementation

This commit is contained in:
Nick Estes
2026-08-11 14:41:33 -07:00
commit 518fe64b13
36 changed files with 2587 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
# frozen_string_literal: true
module MoonModel
class Mesh
attr_reader :name, :vertices, :triangles, :material
def initialize(name:, material: nil)
@name = name
@material = material
@vertices = []
@triangles = []
end
def vertex(point)
@vertices << point.map(&:to_f)
@vertices.length - 1
end
def triangle(a, b, c)
@triangles << [a, b, c]
end
def quad(a, b, c, d, flip: false)
flip ? (triangle(a, c, b); triangle(a, d, c)) : (triangle(a, b, c); triangle(a, c, d))
end
def add_box(center, size)
cx, cy, cz = center
sx, sy, sz = size.map { |v| v / 2.0 }
ids = [[-sx,-sy,-sz],[sx,-sy,-sz],[sx,sy,-sz],[-sx,sy,-sz],[-sx,-sy,sz],[sx,-sy,sz],[sx,sy,sz],[-sx,sy,sz]].map do |x,y,z|
vertex([cx+x, cy+y, cz+z])
end
quad(ids[0], ids[3], ids[2], ids[1]); quad(ids[4], ids[5], ids[6], ids[7])
quad(ids[0], ids[1], ids[5], ids[4]); quad(ids[1], ids[2], ids[6], ids[5])
quad(ids[2], ids[3], ids[7], ids[6]); quad(ids[3], ids[0], ids[4], ids[7])
end
def add_tapered_box(center, length, width_a, width_b, height)
cx, cy, cz = center
x0 = cx - length / 2.0; x1 = cx + length / 2.0
z0 = cz - height / 2.0; z1 = cz + height / 2.0
ids = [[x0,cy-width_a/2,z0],[x0,cy+width_a/2,z0],[x1,cy+width_b/2,z0],[x1,cy-width_b/2,z0],
[x0,cy-width_a/2,z1],[x0,cy+width_a/2,z1],[x1,cy+width_b/2,z1],[x1,cy-width_b/2,z1]].map { |p| vertex(p) }
quad(ids[0],ids[3],ids[2],ids[1]); quad(ids[4],ids[5],ids[6],ids[7])
quad(ids[0],ids[1],ids[5],ids[4]); quad(ids[1],ids[2],ids[6],ids[5])
quad(ids[2],ids[3],ids[7],ids[6]); quad(ids[3],ids[0],ids[4],ids[7])
end
def add_cylinder(center, radius, height, segments: 24)
cx, cy, cz = center
bottom = segments.times.map { |i| a = i * 2*Math::PI/segments; vertex([cx+radius*Math.cos(a), cy+radius*Math.sin(a), cz-height/2]) }
top = segments.times.map { |i| a = i * 2*Math::PI/segments; vertex([cx+radius*Math.cos(a), cy+radius*Math.sin(a), cz+height/2]) }
cb = vertex([cx,cy,cz-height/2]); ct = vertex([cx,cy,cz+height/2])
segments.times do |i|
j = (i+1)%segments
triangle(cb,bottom[j],bottom[i]); triangle(ct,top[i],top[j]); quad(bottom[i],bottom[j],top[j],top[i])
end
end
def add_annular_cylinder(center, inner_radius, outer_radius, z0, z1, segments: 32)
cx, cy = center
oi=[]; oo=[]; ti=[]; to=[]
segments.times do |i|
a=i*2*Math::PI/segments
oi << vertex([cx+inner_radius*Math.cos(a),cy+inner_radius*Math.sin(a),z0])
oo << vertex([cx+outer_radius*Math.cos(a),cy+outer_radius*Math.sin(a),z0])
ti << vertex([cx+inner_radius*Math.cos(a),cy+inner_radius*Math.sin(a),z1])
to << vertex([cx+outer_radius*Math.cos(a),cy+outer_radius*Math.sin(a),z1])
end
segments.times do |i|
j=(i+1)%segments
quad(oo[i],oo[j],to[j],to[i]); quad(oi[i],ti[i],ti[j],oi[j])
quad(oi[i],oi[j],oo[j],oo[i]); quad(ti[i],to[i],to[j],ti[j])
end
end
def add_tri_prism(outer, inner)
o = outer.map { |p| vertex(p) }
i = inner.map { |p| vertex(p) }
triangle(o[0], o[1], o[2]); triangle(i[0], i[2], i[1])
3.times { |n| quad(o[n], o[(n + 1) % 3], i[(n + 1) % 3], i[n]) }
end
def transform
vertices.map! { |point| yield(point) }
self
end
def bounds
axes = vertices.transpose
axes.map { |axis| [axis.min || 0.0, axis.max || 0.0] }
end
def valid?
!vertices.empty? && triangles.all? { |tri| tri.uniq.length == 3 && tri.all? { |i| i.between?(0, vertices.length - 1) } }
end
end
end