Last active
June 24, 2017 00:55
-
-
Save JoshCheek/ba839e738a8489c96f76ad8d00e8f6ab to your computer and use it in GitHub Desktop.
3d Cubes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Video is @ https://vimeo.com/222905527 | |
# The math is prob wrong, but it's close enough to actually render them in 3D. | |
# It's just me trying to think through what should make sense based on how | |
# people describe things, and then trying to work out what the correct math | |
# is to pull it off, but obv I'm not quite there. | |
require 'graphics' | |
require 'matrix' | |
def √ n | |
Math.sqrt(n) | |
end | |
class Matrix | |
def unit | |
self / length.to_f | |
end | |
def length | |
√ reduce(0) { |sum, n| sum + n**2 } | |
end | |
end | |
class Cubes < Graphics::Simulation | |
def initialize | |
super 800, 600, 24 | |
color.default_proc = -> h, k { k } | |
end | |
include Math | |
def draw(t) | |
clear | |
# various variables | |
camera = Matrix[[0, 5, 0]] | |
bobble = 2 | |
ø = PI*2 * t/20 | |
# viewbox | |
origin = [4, -7, 2] | |
xaxis = [1, 0, -1] | |
yaxis = [-1, 0.7, -1] | |
# cube center | |
x, y, z = 40, 40, 40 | |
cube = Cube.new(x: x+0, y: y+bobble*cos(ø)-20, z: z+bobble*cos(ø)+10, side: 10) | |
cube.spin_x(ø/15) | |
.spin_z(ø/43) | |
.draw(self, origin: origin, xaxis: xaxis, yaxis: yaxis, camera: camera, color: :cyan) | |
cube = Cube.new(x: x+40, y: y-30, z: z+bobble*sin(ø), side: 10) | |
cube.spin_z(ø/10) | |
.draw(self, n: t, origin: origin, xaxis: xaxis, yaxis: yaxis, camera: camera, color: :green) | |
cube = Cube.new(x: x-20, y: y-20+bobble*sin(ø)-40, z: z, side: 10) | |
cube.spin_z(ø/15) | |
.draw(self, origin: origin, xaxis: xaxis, yaxis: yaxis, camera: camera, color: :white) | |
cube = Cube.new(x: x-30+bobble*sin(ø), y: y-30, z: z, side: 10) | |
cube.spin_z(ø/20) | |
.spin_x(ø/20) | |
.lol(ø) | |
.draw(self, origin: origin, xaxis: xaxis, yaxis: yaxis, camera: camera, color: :yellow) | |
end | |
end | |
class Cube | |
BASE = [ | |
# bottom square | |
Matrix[[0, 0, 0]], | |
Matrix[[0, 0, 1]], | |
Matrix[[1, 0, 1]], | |
Matrix[[1, 0, 0]], | |
# top square | |
Matrix[[0, 1, 0]], | |
Matrix[[0, 1, 1]], | |
Matrix[[1, 1, 1]], | |
Matrix[[1, 1, 0]], | |
].map { |p| p - Matrix[[0.5, 0.5, 0.5]] }.freeze # center it | |
attr_accessor :x, :y, :z, :side, :points | |
include Math | |
def initialize(x:, y:, z:, side:, points: BASE) | |
self.x, self.y, self.z, self.side, self.points = x, y, z, side, points | |
end | |
def spin_x(ø) | |
spin Matrix[[1, 0, 0], [0, cos(ø), -sin(ø)], [0, sin(ø), cos(ø)]] | |
end | |
def spin_y(ø) | |
spin Matrix[[cos(ø), 0, sin(ø)], [0, 1, 0], [-sin(ø), 0, cos(ø)]] | |
end | |
def lol(ø) | |
m = Matrix[[x/2, y/2, z/2]] | |
r = Matrix[[cos(ø), 0, sin(ø)], [0, 1, 0], [-sin(ø), 0, cos(ø)]] | |
points = self.points.map do |point| | |
point * r | |
end | |
self.class.new x: x, y: y, z: z, side: side, points: points | |
end | |
def spin_z(ø) | |
spin Matrix[[cos(ø), -sin(ø), 0], [sin(ø), cos(ø), 0], [0, 0, 1]] | |
end | |
def spin(rotation) | |
origin = Matrix[[x, y, z]] | |
points = self.points.map do |point| | |
point * rotation | |
end | |
self.class.new x: x, y: y, z: z, side: side, points: points | |
end | |
# xaxis and yaxis should be unit vectors | |
def draw(canvas, origin:, xaxis:, yaxis:, camera:, color:, n: 0) | |
ox, oy, oz = origin | |
xx, xy, xz = xaxis | |
yx, yy, yz = yaxis | |
px = xx+yx # plane | |
py = xy+yy | |
pz = xz+yz | |
ø = 2*PI*n/100 | |
r = Matrix[[1, 0, 0], [0, cos(ø), -sin(ø)], [0, sin(ø), cos(ø)]] * | |
Matrix[[cos(3*ø), 0, sin(3*ø)], [0, 1, 0], [-sin(3*ø), 0, cos(3*ø)]] * | |
Matrix[[cos(2*ø), -sin(2*ø), 0], [sin(2*ø), cos(2*ø), 0], [0, 0, 1]] | |
screen_points = points.map(&:unit).map do |point| | |
point = ((point * side + Matrix[[x, y, z]]) - camera*r) | |
d = (ox*px + oy*py + oz*pz).to_f / (px*point[0, 0] + py*point[0, 1] + pz*point[0, 2]) | |
plane_point = point * d # should be a point on our plane | |
((ppx, ppy, ppz)) = plane_point.to_a | |
# 0 = (a*xx + b*xx)*ppx + (a*xy + b*xy)*ppy + (a*xz + b*xz)*ppz | |
# 0 = a*xx*ppx + b*xx*ppx + a*xy*ppy + b*xy*ppy + a*xz*ppz + b*xz*ppz | |
# 0 = a*xx*ppx + a*xy*ppy + a*xz*ppz + b*xz*ppz + b*xx*ppx + b*xy*ppy | |
# 0 = a*(xx*ppx + xy*ppy + xz*ppz) + b(xz*ppz + xx*ppx + xy*ppy) | |
# a*(xx*ppx + xy*ppy + xz*ppz) = -b(xz*ppz + xx*ppx + xy*ppy) | |
# a = -b(xz*ppz + xx*ppx + xy*ppy) / (xx*ppx + xy*ppy + xz*ppz) | |
# ppx = ox + a*xx + b*yx | |
# -a*xx = ox + b*yx - ppx | |
# a = -(ox + b*yx - ppx)/xx | |
# | |
# ppy = oy + a*xy + b*yy | |
# ppy = oy - (ox + b*yx - ppx)*xy/xx + b*yy | |
# ppy = oy - ox*xy/xx - b*yx*xy/xx + ppx*xy/xx + b*yy | |
# ppy - oy + ox*xy/xx - ppx*xy/xx = b*yy - b*yx*xy/xx | |
# ppy - oy + ox*xy/xx - ppx*xy/xx = b(yy - yx*xy/xx) | |
# (ppy - oy + ox*xy/xx - ppx*xy/xx)/(yy - yx*xy/xx) = b | |
plane_y = (ppy - oy + ox*xy/xx - ppx*xy/xx)/(yy - yx*xy/xx) | |
plane_x = (ppx - ox - plane_y*yx)/xx | |
# plane_x = (ppy*xx - ppy*xy) / (yy*xx - yx*xy) | |
# plane_y = (ppx - plane_x * yx) / xx | |
screen_x = plane_x * 30 | |
screen_y = plane_y * 30 | |
[screen_x, screen_y] | |
end | |
p000, p001, p101, p100, p010, p011, p111, p110 = screen_points | |
line canvas, *p000, *p001, color if display? canvas, p000, p001 | |
line canvas, *p000, *p010, color if display? canvas, p000, p010 | |
line canvas, *p000, *p100, color if display? canvas, p000, p100 | |
line canvas, *p001, *p011, color if display? canvas, p001, p011 | |
line canvas, *p001, *p101, color if display? canvas, p001, p101 | |
line canvas, *p010, *p011, color if display? canvas, p010, p011 | |
line canvas, *p010, *p110, color if display? canvas, p010, p110 | |
line canvas, *p011, *p111, color if display? canvas, p011, p111 | |
line canvas, *p100, *p101, color if display? canvas, p100, p101 | |
line canvas, *p100, *p110, color if display? canvas, p100, p110 | |
line canvas, *p101, *p111, color if display? canvas, p101, p111 | |
line canvas, *p110, *p111, color if display? canvas, p110, p111 | |
end | |
def line(canvas, x1, y1, x2, y2, color) | |
canvas.line x1, y1, x2, y2, color | |
canvas.line x1+1, y1, x2+1, y2, color | |
canvas.line x1, y1+1, x2, y2+1, color | |
canvas.line x1-1, y1, x2-1, y2, color | |
canvas.line x1, y1-1, x2, y2-1, color | |
end | |
def display?(canvas, *points) | |
points.all? do |x, y| | |
-canvas.w < x && -canvas.h < y && x <= 2*canvas.w && y <= 2*canvas.h | |
end | |
end | |
end | |
Cubes.new.run |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment