summaryrefslogtreecommitdiff
path: root/awesome/lib/gears/geometry.lua
diff options
context:
space:
mode:
Diffstat (limited to 'awesome/lib/gears/geometry.lua')
-rw-r--r--awesome/lib/gears/geometry.lua240
1 files changed, 240 insertions, 0 deletions
diff --git a/awesome/lib/gears/geometry.lua b/awesome/lib/gears/geometry.lua
new file mode 100644
index 0000000..a429abd
--- /dev/null
+++ b/awesome/lib/gears/geometry.lua
@@ -0,0 +1,240 @@
+---------------------------------------------------------------------------
+--
+-- Helper functions used to compute geometries.
+--
+-- When this module refer to a geometry table, this assume a table with at least
+-- an *x*, *y*, *width* and *height* keys and numeric values.
+--
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @module gears.geometry
+---------------------------------------------------------------------------
+local math = math
+
+local gears = {geometry = {rectangle = {} } }
+
+--- Get the square distance between a rectangle and a point.
+-- @tparam table geom A rectangle
+-- @tparam number geom.x The horizontal coordinate
+-- @tparam number geom.y The vertical coordinate
+-- @tparam number geom.width The rectangle width
+-- @tparam number geom.height The rectangle height
+-- @tparam number x X coordinate of point
+-- @tparam number y Y coordinate of point
+-- @treturn number The squared distance of the rectangle to the provided point
+function gears.geometry.rectangle.get_square_distance(geom, x, y)
+ local dist_x, dist_y = 0, 0
+ if x < geom.x then
+ dist_x = geom.x - x
+ elseif x >= geom.x + geom.width then
+ dist_x = x - geom.x - geom.width + 1
+ end
+ if y < geom.y then
+ dist_y = geom.y - y
+ elseif y >= geom.y + geom.height then
+ dist_y = y - geom.y - geom.height + 1
+ end
+ return dist_x * dist_x + dist_y * dist_y
+end
+
+--- Return the closest rectangle from `list` for a given point.
+-- @tparam table list A list of geometry tables.
+-- @tparam number x The x coordinate
+-- @tparam number y The y coordinate
+-- @return The key from the closest geometry.
+function gears.geometry.rectangle.get_closest_by_coord(list, x, y)
+ local dist = math.huge
+ local ret = nil
+
+ for k, v in pairs(list) do
+ local d = gears.geometry.rectangle.get_square_distance(v, x, y)
+ if d < dist then
+ ret, dist = k, d
+ end
+ end
+
+ return ret
+end
+
+--- Return the rectangle containing the [x, y] point.
+--
+-- Note that if multiple element from the geometry list contains the point, the
+-- returned result is nondeterministic.
+--
+-- @tparam table list A list of geometry tables.
+-- @tparam number x The x coordinate
+-- @tparam number y The y coordinate
+-- @return The key from the closest geometry. In case no result is found, *nil*
+-- is returned.
+function gears.geometry.rectangle.get_by_coord(list, x, y)
+ for k, geometry in pairs(list) do
+ if x >= geometry.x and x < geometry.x + geometry.width
+ and y >= geometry.y and y < geometry.y + geometry.height then
+ return k
+ end
+ end
+end
+
+--- Return true whether rectangle B is in the right direction
+-- compared to rectangle A.
+-- @param dir The direction.
+-- @param gA The geometric specification for rectangle A.
+-- @param gB The geometric specification for rectangle B.
+-- @return True if B is in the direction of A.
+local function is_in_direction(dir, gA, gB)
+ if dir == "up" then
+ return gA.y > gB.y
+ elseif dir == "down" then
+ return gA.y < gB.y
+ elseif dir == "left" then
+ return gA.x > gB.x
+ elseif dir == "right" then
+ return gA.x < gB.x
+ end
+ return false
+end
+
+--- Calculate distance between two points.
+-- i.e: if we want to move to the right, we will take the right border
+-- of the currently focused screen and the left side of the checked screen.
+-- @param dir The direction.
+-- @param _gA The first rectangle.
+-- @param _gB The second rectangle.
+-- @return The distance between the screens.
+local function calculate_distance(dir, _gA, _gB)
+ local gAx = _gA.x
+ local gAy = _gA.y
+ local gBx = _gB.x
+ local gBy = _gB.y
+
+ if dir == "up" then
+ gBy = _gB.y + _gB.height
+ elseif dir == "down" then
+ gAy = _gA.y + _gA.height
+ elseif dir == "left" then
+ gBx = _gB.x + _gB.width
+ elseif dir == "right" then
+ gAx = _gA.x + _gA.width
+ end
+
+ return math.sqrt(math.pow(gBx - gAx, 2) + math.pow(gBy - gAy, 2))
+end
+
+--- Get the nearest rectangle in the given direction. Every rectangle is specified as a table
+-- with *x*, *y*, *width*, *height* keys, the same as client or screen geometries.
+-- @tparam string dir The direction, can be either *up*, *down*, *left* or *right*.
+-- @tparam table recttbl A table of rectangle specifications.
+-- @tparam table cur The current rectangle.
+-- @return The index for the rectangle in recttbl closer to cur in the given direction. nil if none found.
+function gears.geometry.rectangle.get_in_direction(dir, recttbl, cur)
+ local dist, dist_min
+ local target = nil
+
+ -- We check each object
+ for i, rect in pairs(recttbl) do
+ -- Check geometry to see if object is located in the right direction.
+ if is_in_direction(dir, cur, rect) then
+ -- Calculate distance between current and checked object.
+ dist = calculate_distance(dir, cur, rect)
+
+ -- If distance is shorter then keep the object.
+ if not target or dist < dist_min then
+ target = i
+ dist_min = dist
+ end
+ end
+ end
+ return target
+end
+
+--- Check if an area intersect another area.
+-- @param a The area.
+-- @param b The other area.
+-- @return True if they intersect, false otherwise.
+local function area_intersect_area(a, b)
+ return (b.x < a.x + a.width
+ and b.x + b.width > a.x
+ and b.y < a.y + a.height
+ and b.y + b.height > a.y)
+end
+
+--- Get the intersect area between a and b.
+-- @tparam table a The area.
+-- @tparam number a.x The horizontal coordinate
+-- @tparam number a.y The vertical coordinate
+-- @tparam number a.width The rectangle width
+-- @tparam number a.height The rectangle height
+-- @tparam table b The other area.
+-- @tparam number b.x The horizontal coordinate
+-- @tparam number b.y The vertical coordinate
+-- @tparam number b.width The rectangle width
+-- @tparam number b.height The rectangle height
+-- @treturn table The intersect area.
+function gears.geometry.rectangle.get_intersection(a, b)
+ local g = {}
+ g.x = math.max(a.x, b.x)
+ g.y = math.max(a.y, b.y)
+ g.width = math.min(a.x + a.width, b.x + b.width) - g.x
+ g.height = math.min(a.y + a.height, b.y + b.height) - g.y
+ return g
+end
+
+--- Remove an area from a list, splitting the space between several area that
+-- can overlap.
+-- @tparam table areas Table of areas.
+-- @tparam table elem Area to remove.
+-- @tparam number elem.x The horizontal coordinate
+-- @tparam number elem.y The vertical coordinate
+-- @tparam number elem.width The rectangle width
+-- @tparam number elem.height The rectangle height
+-- @return The new area list.
+function gears.geometry.rectangle.area_remove(areas, elem)
+ for i = #areas, 1, -1 do
+ -- Check if the 'elem' intersect
+ if area_intersect_area(areas[i], elem) then
+ -- It does? remove it
+ local r = table.remove(areas, i)
+ local inter = gears.geometry.rectangle.get_intersection(r, elem)
+
+ if inter.x > r.x then
+ table.insert(areas, {
+ x = r.x,
+ y = r.y,
+ width = inter.x - r.x,
+ height = r.height
+ })
+ end
+
+ if inter.y > r.y then
+ table.insert(areas, {
+ x = r.x,
+ y = r.y,
+ width = r.width,
+ height = inter.y - r.y
+ })
+ end
+
+ if inter.x + inter.width < r.x + r.width then
+ table.insert(areas, {
+ x = inter.x + inter.width,
+ y = r.y,
+ width = (r.x + r.width) - (inter.x + inter.width),
+ height = r.height
+ })
+ end
+
+ if inter.y + inter.height < r.y + r.height then
+ table.insert(areas, {
+ x = r.x,
+ y = inter.y + inter.height,
+ width = r.width,
+ height = (r.y + r.height) - (inter.y + inter.height)
+ })
+ end
+ end
+ end
+
+ return areas
+end
+
+return gears.geometry