99 lines
3.6 KiB
Ruby
99 lines
3.6 KiB
Ruby
# 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
|