diff --git a/lib/gears/init.lua b/lib/gears/init.lua index 696bed7b..3289483a 100644 --- a/lib/gears/init.lua +++ b/lib/gears/init.lua @@ -16,6 +16,7 @@ return wallpaper = require("gears.wallpaper"); timer = require("gears.timer"); cache = require("gears.cache"); + matrix = require("gears.matrix"); } -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/gears/matrix.lua b/lib/gears/matrix.lua new file mode 100644 index 00000000..c8ea2860 --- /dev/null +++ b/lib/gears/matrix.lua @@ -0,0 +1,59 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter +-- @copyright 2015 Uli Schlachter +-- @release @AWESOME_VERSION@ +-- @module gears.matrix +--------------------------------------------------------------------------- + +local cairo = require("lgi").cairo +local debug = require("gears.debug") +local matrix = {} + +--- Copy a cairo matrix +-- @param matrix The matrix to copy. +-- @return A copy of the given cairo matrix. +function matrix.copy(matrix) + debug.assert(cairo.Matrix:is_type_of(matrix), "Argument should be a cairo matrix") + local ret = cairo.Matrix() + ret:init(matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0) + return ret +end + +--- Check if two cairo matrices are equal +-- @param m1 The first matrix to compare with. +-- @param m2 The second matrix to compare with. +-- @return True if they are equal. +function matrix.equals(m1, m2) + for _, k in pairs{ "xx", "xy", "yx", "yy", "x0", "y0" } do + if m1[k] ~= m2[k] then + return false + end + end + return true +end + +--- Calculate a bounding rectangle for transforming a rectangle by a matrix. +-- @param matrix The cairo matrix that describes the transformation. +-- @param x The x coordinate of the rectangle. +-- @param y The y coordinate of the rectangle. +-- @param width The width of the rectangle. +-- @param height The height of the rectangle. +-- @return The x, y, width and height of the bounding rectangle. +function matrix.transform_rectangle(matrix, x, y, width, height) + -- Transform all four corners of the rectangle + local x1, y1 = matrix:transform_point(x, y) + local x2, y2 = matrix:transform_point(x, y + height) + local x3, y3 = matrix:transform_point(x + width, y + height) + local x4, y4 = matrix:transform_point(x + width, y) + -- Find the extremal points of the result + local x = math.min(x1, x2, x3, x4) + local y = math.min(y1, y2, y3, y4) + local width = math.max(x1, x2, x3, x4) - x + local height = math.max(y1, y2, y3, y4) - y + + return x, y, width, height +end + +return matrix + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/spec/gears/matrix_spec.lua b/spec/gears/matrix_spec.lua new file mode 100644 index 00000000..0f9ef7a6 --- /dev/null +++ b/spec/gears/matrix_spec.lua @@ -0,0 +1,82 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter +-- @copyright 2015 Uli Schlachter +--------------------------------------------------------------------------- + +local matrix = require("gears.matrix") +local cairo = require("lgi").cairo + +describe("gears.matrix", function() + describe("copy", function() + it("Test copy", function() + local m1 = cairo.Matrix() + m1:init(1, 2, 3, 4, 5, 6) + local m2 = matrix.copy(m1) + assert.is.equal(m1.xx, m2.xx) + assert.is.equal(m1.xy, m2.xy) + assert.is.equal(m1.yx, m2.yx) + assert.is.equal(m1.yy, m2.yy) + assert.is.equal(m1.x0, m2.x0) + assert.is.equal(m1.y0, m2.y0) + + m1.x0 = 42 + assert.is_not.equal(m1.x0, m2.x0) + end) + end) + + describe("equals", function() + it("Same matrix", function() + local m = cairo.Matrix.create_rotate(1) + assert.is_true(matrix.equals(m, m)) + end) + + it("Different matrix equals", function() + local m1 = cairo.Matrix.create_rotate(1) + local m2 = cairo.Matrix.create_rotate(1) + assert.is_true(matrix.equals(m1, m2)) + end) + + it("Different matrix unequal", function() + local m1 = cairo.Matrix() + local m2 = cairo.Matrix() + m1:init(1, 2, 3, 4, 5, 6) + m2:init(1, 2, 3, 4, 5, 0) + assert.is_false(matrix.equals(m1, m2)) + end) + end) + + describe("transform_rectangle", function() + local function round(n) + return math.floor(n + 0.5) + end + local function test(m, x, y, width, height, + expected_x, expected_y, expected_width, expected_height) + local actual_x, actual_y, actual_width, actual_height = + matrix.transform_rectangle(m, x, y, width, height) + assert.is.equal(round(actual_x), expected_x) + assert.is.equal(round(actual_y), expected_y) + assert.is.equal(round(actual_width), expected_width) + assert.is.equal(round(actual_height), expected_height) + -- Stupid rounding issues... + assert.is_true(math.abs(round(actual_x) - actual_x) < 0.00000001) + assert.is_true(math.abs(round(actual_y) - actual_y) < 0.00000001) + assert.is_true(math.abs(round(actual_width) - actual_width) < 0.00000001) + assert.is_true(math.abs(round(actual_height) - actual_height) < 0.00000001) + end + it("Identity matrix", function() + test(cairo.Matrix.create_identity(), 1, 2, 3, 4, 1, 2, 3, 4) + end) + + it("Rotate 180", function() + test(cairo.Matrix.create_rotate(math.pi), + 1, 2, 3, 4, -4, -6, 3, 4) + end) + + it("Rotate 90", function() + test(cairo.Matrix.create_rotate(math.pi / 2), + 1, 2, 3, 4, -6, 1, 4, 3) + end) + end) +end) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80