Skip to content

Instantly share code, notes, and snippets.

@Demonstrandum
Created June 5, 2021 22:11
Show Gist options
  • Save Demonstrandum/6be0341d910f4b63fe4524c18b58c67f to your computer and use it in GitHub Desktop.
Save Demonstrandum/6be0341d910f4b63fe4524c18b58c67f to your computer and use it in GitHub Desktop.
Cube in the terminal (old).
WIDTH = `tput cols`.to_i - 1 || 80
HEIGHT = `tput lines`.to_i - 1 || 40
FPS = 60
include Math
class Array
def x; self[0]; end
def y; self[1]; end
def z; self[2]; end
def x=(other_x); self[0] = other_x; end
def y=(other_y); self[1] = other_y; end
def z=(other_z); self[2] = other_z; end
def rotate_x theta
x_p = self.x
y_p = self.y * cos(theta) - self.z * sin(theta)
z_p = self.y * sin(theta) + self.z * cos(theta)
[x_p, y_p, z_p]
end
def rotate_y theta
x_p = self.x * cos(theta) + self.z * sin(theta)
y_p = self.y
z_p = self.z * cos(theta) - self.x * sin(theta)
[x_p, y_p, z_p]
end
def rotate_z theta
x_p = self.x * cos(theta) - self.y * sin(theta)
y_p = self.x * sin(theta) + self.y * cos(theta)
z_p = self.z
[x_p, y_p, z_p]
end
end
class Range
def step n
from, to = self.first, self.last
return Enumerator.new { |y| y << from while true } if n.zero?
count = ((from - to).to_f / n).abs.ceil
Enumerator.new count do |y|
count.times do |i|
y << i * n + from
end
end
end
end
class Canvas
attr_accessor :brush
attr_accessor :background
def initialize width, height
@width = width
@height = height
@grid = [' '] * (width * height)
@scaler = [1, 1]
@translation = [0, 0]
@brush = '▓'
@background = ' '
end
def clear
@grid.each.with_index { |_, i| @grid[i] = @background }
end
def scale w, h
@scaler = [w, h]
end
def translate x, y
@translation = [x, y]
end
def transform x, y; [
(x - @translation.x) / @scaler.x.to_f,
(y - @translation.y) / @scaler.y.to_f
]; end
def untransform x, y; [
x * @scaler.x + @translation.x,
y * @scaler.y + @translation.y
].map(&:round); end
def [](x, y)
x, y = untransform(x, y)
@grid[(@width - 1) * y + x]
end
def []=(x, y, char)
x, y = untransform(x, y)
unless (0..@width).include?(x) && (0..@height).include?(y)
return
end
@grid[@width * y + x] = char;
end
def line p1, p2
up1 = untransform(*p1)
up2 = untransform(*p2)
m = (p2.y - p1.y).to_f / (p2.x - p1.x).to_f
x_sign = p2.x <=> p1.x
dem = @scaler.x
dem *= m.abs if m.abs > 1
x_range = (p1.x..p2.x).step(x_sign * 1.0/dem)
if up1 == up2
self[p1.x, p1.y] = 'O'
return
elsif up1.x == up2.x
y_sign = p1.y <=> p2.y
y_range = (p1.y..p2.y).step(y_sign * 1.0/@scaler.y)
y_range.each { |y| self[p1.x, y] = @brush }
return
elsif up1.y == up2.y
x_range.each { |x| self[x, p1.y] = @brush }
return
end
c = p1.y - m * p1.x
x_range.each do |x|
self[x, m * x + c] = @brush
end
end
def to_s
@grid.each_slice(@width)
.to_a.map(&:join)
.join("\n") + "\033[A" * (@height - 1) + "\033[F"
end
end
CANVAS = Canvas::new WIDTH + 1, HEIGHT + 1
CANVAS.brush = ' ' #'·' #'▓'
CANVAS.background = '░' #' '
ZOOM_OUT = 0.1
CANVAS.translate WIDTH/2.0, HEIGHT/2.0
CANVAS.scale WIDTH/(2 + ZOOM_OUT), -HEIGHT/(2 + ZOOM_OUT)
# Order of [x, y, z]:
CUBE_VERTICES = [
# Front:
[-1, +1, +1], [+1, +1, +1], # left top, right top
[-1, -1, +1], [+1, -1, +1], # left bottom, right bottom
# Back:
[-1, +1, -1], [+1, +1, -1], # left top, right top
[-1, -1, -1], [+1, -1, -1] # left bottom, right bottom
]
Z_POS = -2.4;
n = 0
before = Time::now.to_f
loop do
n += 0.01
vertices = CUBE_VERTICES.dup.map(&:dup);
vertices.map! { |v| v.rotate_x 1.2 * n }
vertices.map! { |v| v.rotate_y 0.9 * n }
vertices.map! { |v| v.rotate_z 1.5 * n }
vertices2D = vertices.map do |vertex|
z = (vertex.z + Z_POS).to_f
[vertex.x / z, vertex.y / z]
end
# Front
CANVAS.line vertices2D[0], vertices2D[1]
CANVAS.line vertices2D[1], vertices2D[3]
CANVAS.line vertices2D[3], vertices2D[2]
CANVAS.line vertices2D[2], vertices2D[0]
# Link back
CANVAS.line vertices2D[0], vertices2D[4]
CANVAS.line vertices2D[1], vertices2D[5]
CANVAS.line vertices2D[2], vertices2D[6]
CANVAS.line vertices2D[3], vertices2D[7]
# Back
CANVAS.line vertices2D[4], vertices2D[5]
CANVAS.line vertices2D[5], vertices2D[7]
CANVAS.line vertices2D[7], vertices2D[6]
CANVAS.line vertices2D[6], vertices2D[4]
=begin
CANVAS.line [-1, +1], [-1, -1]
CANVAS.line [+1, +1], [+1, -1]
CANVAS.line [-1, -1], [+1, -1]
CANVAS.line [-1, +1], [+1, +1]
=end
puts CANVAS
now = Time::now.to_f
delta = now - before
sleep_time = 1.0/FPS - delta
sleep sleep_time if sleep_time > 0
before = now
CANVAS.clear
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment