Improve transform2d

This commit is contained in:
Alex Clink 2022-01-22 23:44:44 -05:00
parent 5456adad4e
commit 67ad23efe3
3 changed files with 149 additions and 74 deletions

View file

@ -38,6 +38,8 @@ module PF
end
end
delegate :fill, to: @values
def initialize
@values = Slice(T).new(W * H, T.new(0))
end
@ -55,6 +57,16 @@ module PF
nums.each_with_index { |n, i| @values[i] = n }
end
def reset_to_identity!
{% unless W == H %}
raise "Matrix({{W}}x{{H}}) is not square"
{% end %}
fill(T.new(0))
{% for i in 0...W %}
self[{{i}}, {{i}}] = T.new(1)
{% end %}
end
# Width of the matrix
def width
W
@ -66,11 +78,7 @@ module PF
end
def size
{% if W == H %}
W
{% else %}
raise "Matrix({{W}}x{{H}}) is not square"
{% end %}
W * H
end
# Tests the equality of two matricies
@ -102,7 +110,7 @@ module PF
end
def *(other : Matrix)
result = Matrix(T, W, H).new
result = Matrix(typeof(self[0, 0] * other[0, 0]), W, H).new
{% for y in (0...H) %}
{% for x in (0...W) %}
{% for n in (0...W) %}
@ -113,14 +121,16 @@ module PF
result
end
def inspect
String.build do |io|
H.times do |h|
io << '['
W.times { |w| io << self[w, h] }
io << "]\n"
def to_s(io)
io << {{ @type }} << '['
(0...H).each do |y|
(0...W).each do |x|
io << self[x, y]
io << ' ' unless x == W - 1
end
io << ", " unless y == H - 1
end
io << ']'
end
end
end

View file

@ -3,73 +3,155 @@ 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,
]
property matrix : Matrix(Float64, 3, 3)
def self.identity
Matrix[
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
]
end
# Returns a matrix representing a 2d translation
def self.translation(x : Float | Int, y : Float | Int)
Matrix[
1.0, 0.0, x.to_f64,
0.0, 1.0, y.to_f64,
0.0, 0.0, 1.0,
]
end
# Returns a matrix representing a 2d scaling
def self.scale(x : Float | Int, y : Float | Int)
Matrix[
x.to_f64, 0.0, 0.0,
0.0, y.to_f64, 0.0,
0.0, 0.0, 1.0,
]
end
# Returns a matrix representing a 2d rotation
def self.rotation(angle : Float | Int)
cos = Math.cos(angle)
sin = Math.sin(angle)
Matrix[
cos, -sin, 0.0,
sin, cos, 0.0,
0.0, 0.0, 1.0,
]
end
# Returns a matrix representing a 2d shear
def self.shear(x : Float | Int, y : Float | Int)
Matrix[
1.0, x.to_f64, 0.0,
y.to_f64, 1.0, 0.0,
0.0, 0.0, 1.0,
]
end
# Return a new inverted version of the given *matrix*
def self.invert(matrix : Matrix)
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[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,
]
end
def initialize
@matrix = PF::Transform2d.identity
end
def initialize(@matrix)
end
# =============
# Reset the transformation to the identity matrix
def reset
@matrix = Matrix[
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
]
@matrix = PF::Transform2d.identity
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
# =============
# = translate =
# =============
# Translate by *x* and *y*
def translate(x : Number, y : Number)
@matrix = PF::Transform2d.translation(x, y) * @matrix
self
end
def translate(to : Vector2)
translate(to.x, to.y)
# ditto
def translate(point : Vector2)
translate(point.x, point.y)
end
# ==========
# = rotate =
# ==========
# Rotate by *angle* (in radians)
def rotate(angle : Float | Int)
@matrix = PF::Transform2d.rotation(angle) * @matrix
self
end
# =========
# = scale =
# =========
# Scale by *x* and *y*
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
@matrix = PF::Transform2d.scale(x, y) * @matrix
self
end
def scale(n : Float | Int)
# ditto
def scale(point : Vector2)
scale(point.x, point.y)
end
# Scale both x and y by *n*
def scale(n : Number)
scale(n, n)
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
# =========
# = shear =
# =========
# Shear by *x* and *y*
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
@matrix = PF::Transform2d.shear(x, y) * @matrix
self
end
# ditto
def shear(point : Vector2)
shear(point.x, point.y)
end
# ==========
# Return the boudning box of the current transformation matrix
def bounding_box(x : Float | Int, y : Float | Int)
top_left = apply(0.0, 0.0)
top_right = apply(x.to_f, 0.0)
@ -82,31 +164,14 @@ module PF
{Vector[xs.min, ys.min], Vector[xs.max, ys.max]}
end
# Invert the transformation
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,
]
@matrix = PF::Transform2d.invert(@matrix)
self
end
def apply(x : Float | Int, y : Float | Int)
result = Vector[x, y, typeof(x, y).new(1)] * @matrix
result = Vector[x, y, 1.0] * @matrix
Vector[result.x, result.y]
end

View file

@ -139,8 +139,8 @@ module PF
# # => PF::Vector3(Int32)(@x=1, @y=4, @z=3)
# ```
def *(matrix : Matrix)
PF::Vector[{% for col in 0...i %}
{% for row in 0...i %} @{{ vars[row].id }} * matrix[{{row}}, {{col}}] {% if row != i - 1 %} + {% end %}{% end %},
PF::Vector[{% for row in 0...i %}
{% for col in 0...i %} @{{ vars[col].id }} * matrix[{{col}}, {{row}}] {% if col != i - 1 %} + {% end %}{% end %},
{% end %}]
end
@ -150,7 +150,7 @@ module PF
to_u8: UInt8, to_u16: UInt16, to_u32: UInt32, to_u64: UInt64, to_u128: UInt128,
to_f32: Float32, to_f64: Float64,
} %}
# Convert this vector to {{ type }}
# Convert the components in this vector to {{ type }}
def {{ method }}
Vector{{i}}({{ type }}).new({% for arg in 0...i %} @{{vars[arg].id}}.{{method}}, {% end %})
end