From 16a1ef0f48d8ac3b3905f50de22fea9a76da2f24 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 13 Jun 2015 16:58:22 +0200 Subject: [PATCH 1/3] Add gears.matrix for working with cairo matrices Signed-off-by: Uli Schlachter --- lib/gears/init.lua | 1 + lib/gears/matrix.lua | 59 +++++++++++++++++++++++++++ spec/gears/matrix_spec.lua | 82 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 lib/gears/matrix.lua create mode 100644 spec/gears/matrix_spec.lua diff --git a/lib/gears/init.lua b/lib/gears/init.lua index 696bed7bc..3289483ac 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 000000000..c8ea28607 --- /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 000000000..0f9ef7a60 --- /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 From 19b9bfc46ee851e1d242ee19aebfbbee1d7cf855 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 25 Jul 2015 15:52:53 +0200 Subject: [PATCH 2/3] Move wibox.layout.rect_to_device_geometry to wibox.widget Having two modules named "base" is confusing and "wibox.layout" doesn't contain much useful stuff. This is a first step for removing wibox.layout by moving a function which should only ever be used internally in awesome. Signed-off-by: Uli Schlachter --- lib/wibox/layout/base.lua | 21 ++------------------- lib/wibox/widget/base.lua | 15 +++++++++++++++ lib/wibox/widget/systray.lua | 3 +-- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/lib/wibox/layout/base.lua b/lib/wibox/layout/base.lua index 97e939661..6508602a0 100644 --- a/lib/wibox/layout/base.lua +++ b/lib/wibox/layout/base.lua @@ -5,29 +5,12 @@ -- @classmod wibox.layout.base --------------------------------------------------------------------------- -local pairs = pairs local pcall = pcall local print = print -local min = math.min -local max = math.max +local wbase = require("wibox.widget.base") local base = {} ---- Figure out the geometry in device coordinate space. This gives only tight --- bounds if no rotations by non-multiples of 90° are used. -function base.rect_to_device_geometry(cr, x, y, width, height) - local x1, y1 = cr:user_to_device(x, y) - local x2, y2 = cr:user_to_device(x, y + height) - local x3, y3 = cr:user_to_device(x + width, y + height) - local x4, y4 = cr:user_to_device(x + width, y) - local x = min(x1, x2, x3, x4) - local y = min(y1, y2, y3, y4) - local width = max(x1, x2, x3, x4) - x - local height = max(y1, y2, y3, y4) - y - - return x, y, width, height -end - --- Fit a widget for the given available width and height -- @param widget The widget to fit (this uses widget:fit(width, height)). -- @param width The available width for the widget @@ -73,7 +56,7 @@ function base.draw_widget(wibox, cr, widget, x, y, width, height) end -- Register the widget for input handling - wibox:widget_at(widget, base.rect_to_device_geometry(cr, 0, 0, width, height)) + wibox:widget_at(widget, wbase.rect_to_device_geometry(cr, 0, 0, width, height)) cr:restore() end diff --git a/lib/wibox/widget/base.lua b/lib/wibox/widget/base.lua index f57c01f56..a6ac0a715 100644 --- a/lib/wibox/widget/base.lua +++ b/lib/wibox/widget/base.lua @@ -15,6 +15,21 @@ local table = table local base = {} +--- Figure out the geometry in device coordinate space. This gives only tight +-- bounds if no rotations by non-multiples of 90° are used. +function base.rect_to_device_geometry(cr, x, y, width, height) + local x1, y1 = cr:user_to_device(x, y) + local x2, y2 = cr:user_to_device(x, y + height) + local x3, y3 = cr:user_to_device(x + width, y + height) + local x4, y4 = cr:user_to_device(x + width, y) + 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 + --- Set/get a widget's buttons function base:buttons(_buttons) if _buttons then diff --git a/lib/wibox/widget/systray.lua b/lib/wibox/widget/systray.lua index f2afefced..1f679bb07 100644 --- a/lib/wibox/widget/systray.lua +++ b/lib/wibox/widget/systray.lua @@ -6,7 +6,6 @@ --------------------------------------------------------------------------- local wbase = require("wibox.widget.base") -local lbase = require("wibox.layout.base") local beautiful = require("beautiful") local capi = { awesome = awesome } local setmetatable = setmetatable @@ -20,7 +19,7 @@ local base_size = nil local reverse = false function systray:draw(wibox, cr, width, height) - local x, y, _, _ = lbase.rect_to_device_geometry(cr, 0, 0, width, height) + local x, y, _, _ = wbase.rect_to_device_geometry(cr, 0, 0, width, height) local num_entries = capi.awesome.systray() local bg = beautiful.bg_systray or beautiful.bg_normal or "#000000" local spacing = beautiful.systray_icon_spacing or 0 From 3685077291f48ef3149d33ea1b3a5c31aa52b78e Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 25 Jul 2015 15:59:41 +0200 Subject: [PATCH 3/3] rect_to_device_geometry: Use gears.matrix.transform_rectangle Signed-off-by: Uli Schlachter --- lib/wibox/widget/base.lua | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/wibox/widget/base.lua b/lib/wibox/widget/base.lua index a6ac0a715..cb3e7f584 100644 --- a/lib/wibox/widget/base.lua +++ b/lib/wibox/widget/base.lua @@ -8,6 +8,7 @@ local debug = require("gears.debug") local object = require("gears.object") local cache = require("gears.cache") +local matrix = require("gears.matrix") local setmetatable = setmetatable local pairs = pairs local type = type @@ -18,16 +19,7 @@ local base = {} --- Figure out the geometry in device coordinate space. This gives only tight -- bounds if no rotations by non-multiples of 90° are used. function base.rect_to_device_geometry(cr, x, y, width, height) - local x1, y1 = cr:user_to_device(x, y) - local x2, y2 = cr:user_to_device(x, y + height) - local x3, y3 = cr:user_to_device(x + width, y + height) - local x4, y4 = cr:user_to_device(x + width, y) - 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 + return matrix.transform_rectangle(cr.matrix, x, y, width, height) end --- Set/get a widget's buttons