Add transform2d class

This commit is contained in:
Alex Clink 2022-01-06 00:38:47 -05:00
parent f4409b4496
commit 5f7880c171
3 changed files with 160 additions and 0 deletions

BIN
assets/bricks.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

47
examples/affine.cr Normal file
View file

@ -0,0 +1,47 @@
require "../src/game"
require "../src/sprite"
require "../src/transform2d"
module PF
class Affine < Game
@bricks : Sprite
@transform : Transform2d = Transform2d.new
@angle = 0.0
def initialize(*args, **kwargs)
super
@bricks = Sprite.new("./assets/bricks.png")
end
def update(dt, event)
@angle += 1.0 * dt
end
def draw
clear(50, 127, 200)
@transform
.reset
.translate(-(@bricks.size // 2))
.rotate(@angle)
.translate(viewport // 2)
b1, b2 = @transform.bounding_box(@bricks.size.x, @bricks.size.y).map(&.to_i)
@transform.invert
b1.y.upto(b2.y) do |y|
b1.x.upto(b2.x) do |x|
point = @transform.apply(x, y).to_i
if point >= Vector[0, 0] && point < @bricks.size
color = @bricks.sample(point.x, point.y)
draw_point(x.to_i, y.to_i, color)
end
end
end
end
end
end
game = PF::Affine.new(300, 200, 2)
game.run!

113
src/transform2d.cr Normal file
View file

@ -0,0 +1,113 @@
require "./matrix"
require "./vector"
module PF
class Transform2d
property matrix : Matrix(Float64, 3, 3) = Matrix[
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
]
def initialize
end
def initialize(@matrix)
end
def reset
@matrix = Matrix[
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
]
self
end
def translate(x : Float | Int, y : Float | Int)
@matrix = Matrix[
1.0, 0.0, x.to_f64,
0.0, 1.0, y.to_f64,
0.0, 0.0, 1.0,
] * @matrix
self
end
def translate(to : Vector)
translate(to.x, to.y)
end
def scale(x : Float | Int, y : Float | Int)
@matrix = Matrix[
x.to_f64, 0.0, 0.0,
0.0, y.to_f64, 0.0,
0.0, 0.0, 1.0,
] * @matrix
self
end
def rotate(angle : Float | Int)
cos = Math.cos(angle)
sin = Math.sin(angle)
@matrix = Matrix[
cos, -sin, 0.0,
sin, cos, 0.0,
0.0, 0.0, 1.0,
] * @matrix
self
end
def shear(x : Float | Int, y : Float | Int)
@matrix = Matrix[
1.0, x.to_f64, 0.0,
y.to_f64, 1.0, 0.0,
0.0, 0.0, 1.0,
] * @matrix
self
end
def bounding_box(x : Float | Int, y : Float | Int)
top_left = apply(0.0, 0.0)
top_right = apply(x.to_f, 0.0)
bot_right = apply(x.to_f, y.to_f)
bot_left = apply(0.0, y.to_f)
xs = Float64[top_left.x, top_right.x, bot_right.x, bot_left.x]
ys = Float64[top_left.y, top_right.y, bot_right.y, bot_left.y]
{Vector[xs.min, ys.min], Vector[xs.max, ys.max]}
end
def invert
det = @matrix[0, 0] * (@matrix[1, 1] * @matrix[2, 2] - @matrix[1, 2] * @matrix[2, 1]) -
@matrix[1, 0] * (@matrix[0, 1] * @matrix[2, 2] - @matrix[2, 1] * @matrix[0, 2]) +
@matrix[2, 0] * (@matrix[0, 1] * @matrix[1, 2] - @matrix[1, 1] * @matrix[0, 2])
idet = 1.0 / det
@matrix = Matrix[
(@matrix[1, 1] * @matrix[2, 2] - @matrix[1, 2] * @matrix[2, 1]) * idet,
(@matrix[2, 0] * @matrix[1, 2] - @matrix[1, 0] * @matrix[2, 2]) * idet,
(@matrix[1, 0] * @matrix[2, 1] - @matrix[2, 0] * @matrix[1, 1]) * idet,
(@matrix[2, 1] * @matrix[0, 2] - @matrix[0, 1] * @matrix[2, 2]) * idet,
(@matrix[0, 0] * @matrix[2, 2] - @matrix[2, 0] * @matrix[0, 2]) * idet,
(@matrix[0, 1] * @matrix[2, 0] - @matrix[0, 0] * @matrix[2, 1]) * idet,
(@matrix[0, 1] * @matrix[1, 2] - @matrix[0, 2] * @matrix[1, 1]) * idet,
(@matrix[0, 2] * @matrix[1, 0] - @matrix[0, 0] * @matrix[1, 2]) * idet,
(@matrix[0, 0] * @matrix[1, 1] - @matrix[0, 1] * @matrix[1, 0]) * idet,
]
self
end
def apply(x : Float | Int, y : Float | Int)
result = Vector[x, y, 1.0] * @matrix
Vector[result.x, result.y]
end
def apply(point : Vector)
apply(point.x, point.y)
end
end
end