|
|
|
@ -1,22 +1,34 @@
|
|
|
|
|
local Math = require("optmath")
|
|
|
|
|
local DEBUG = require("dbg")
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
--[[--
|
|
|
|
|
2D Geometry utilities
|
|
|
|
|
|
|
|
|
|
all of these apply to full rectangles { x = ..., y = ..., w = ..., h = ... }
|
|
|
|
|
All of these apply to full rectangles:
|
|
|
|
|
|
|
|
|
|
local Geom = require("ui/geometry")
|
|
|
|
|
Geom:new{ x = 1, y = 0, w = 100, h = 200, }
|
|
|
|
|
|
|
|
|
|
Some behaviour is defined for points:
|
|
|
|
|
|
|
|
|
|
Geom:new{ x = 0, y = 0, }
|
|
|
|
|
|
|
|
|
|
Some behaviour is defined for dimensions:
|
|
|
|
|
|
|
|
|
|
some behaviour is defined for points { x = ..., y = ... }
|
|
|
|
|
some behaviour is defined for dimensions { w = ..., h = ... }
|
|
|
|
|
Geom:new{ w = 600, h = 800, }
|
|
|
|
|
|
|
|
|
|
just use it on simple tables that have x, y and/or w, h
|
|
|
|
|
Just use it on simple tables that have x, y and/or w, h
|
|
|
|
|
or define your own types using this as a metatable
|
|
|
|
|
]]--
|
|
|
|
|
|
|
|
|
|
]]
|
|
|
|
|
|
|
|
|
|
local Math = require("optmath")
|
|
|
|
|
|
|
|
|
|
--[[--
|
|
|
|
|
@table Geom
|
|
|
|
|
]]
|
|
|
|
|
local Geom = {
|
|
|
|
|
x = 0,
|
|
|
|
|
y = 0,
|
|
|
|
|
w = 0,
|
|
|
|
|
h = 0
|
|
|
|
|
h = 0,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Geom:new(o)
|
|
|
|
@ -26,6 +38,10 @@ function Geom:new(o)
|
|
|
|
|
return o
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[--
|
|
|
|
|
Make a deep copy of itself.
|
|
|
|
|
@treturn Geom
|
|
|
|
|
]]
|
|
|
|
|
function Geom:copy()
|
|
|
|
|
local n = Geom:new()
|
|
|
|
|
n.x = self.x
|
|
|
|
@ -39,48 +55,60 @@ function Geom:__tostring()
|
|
|
|
|
return self.w.."x"..self.h.."+"..self.x.."+"..self.y
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
offset rectangle or point by relative values
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
Offset rectangle or point by relative values
|
|
|
|
|
@int dx x delta
|
|
|
|
|
@int dy y delta
|
|
|
|
|
]]
|
|
|
|
|
function Geom:offsetBy(dx, dy)
|
|
|
|
|
self.x = self.x + dx
|
|
|
|
|
self.y = self.y + dy
|
|
|
|
|
return self
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
offset rectangle or point to certain coordinates
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
Offsets rectangle or point to certain coordinates
|
|
|
|
|
@int x new x
|
|
|
|
|
@int y new y
|
|
|
|
|
]]
|
|
|
|
|
function Geom:offsetTo(x, y)
|
|
|
|
|
self.x = x
|
|
|
|
|
self.y = y
|
|
|
|
|
return self
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
scale rectangle (grow to bottom and to the right) or dimension
|
|
|
|
|
--[[--
|
|
|
|
|
Scales rectangle (grow to bottom and to the right) or dimension
|
|
|
|
|
|
|
|
|
|
if a single factor is given, it is applied to both width and height
|
|
|
|
|
]]--
|
|
|
|
|
If a single factor is given, it is applied to both width and height
|
|
|
|
|
|
|
|
|
|
@int zx scale for x axis
|
|
|
|
|
@int zy scale for y axis
|
|
|
|
|
]]
|
|
|
|
|
function Geom:scaleBy(zx, zy)
|
|
|
|
|
self.w = Math.round(self.w * zx)
|
|
|
|
|
self.h = Math.round(self.h * (zy or zx))
|
|
|
|
|
return self
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
this method also takes care of x and y
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
This method also takes care of x and y on top of @{Geom:scaleBy}
|
|
|
|
|
|
|
|
|
|
@int zx scale for x axis
|
|
|
|
|
@int zy scale for y axis
|
|
|
|
|
]]
|
|
|
|
|
function Geom:transformByScale(zx, zy)
|
|
|
|
|
self.x = Math.round(self.x * zx)
|
|
|
|
|
self.y = Math.round(self.y * (zx or zy))
|
|
|
|
|
self:scaleBy(zx, zy)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
return size of geom
|
|
|
|
|
]]--
|
|
|
|
|
function Geom:sizeof()
|
|
|
|
|
--[[--
|
|
|
|
|
Returns area of itself.
|
|
|
|
|
|
|
|
|
|
@treturn int
|
|
|
|
|
]]
|
|
|
|
|
function Geom:area()
|
|
|
|
|
if not self.w or not self.h then
|
|
|
|
|
return 0
|
|
|
|
|
else
|
|
|
|
@ -88,25 +116,31 @@ function Geom:sizeof()
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
enlarges or shrinks dimensions or rectangles
|
|
|
|
|
--[[--
|
|
|
|
|
Enlarges or shrinks dimensions or rectangles
|
|
|
|
|
|
|
|
|
|
note that for rectangles the offset stays the same
|
|
|
|
|
]]--
|
|
|
|
|
Note that for rectangles the offset stays the same
|
|
|
|
|
|
|
|
|
|
@int dw width delta
|
|
|
|
|
@int dh height delta
|
|
|
|
|
]]
|
|
|
|
|
function Geom:changeSizeBy(dw, dh)
|
|
|
|
|
self.w = self.w + dw
|
|
|
|
|
self.h = self.h + dh
|
|
|
|
|
return self
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
return the outer rectangle that contains both us and a given rectangle
|
|
|
|
|
--[[--
|
|
|
|
|
Returns a new outer rectangle that contains both us and a given rectangle
|
|
|
|
|
|
|
|
|
|
works for rectangles, dimensions and points
|
|
|
|
|
]]--
|
|
|
|
|
Works for rectangles, dimensions and points
|
|
|
|
|
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
@treturn Geom
|
|
|
|
|
]]
|
|
|
|
|
function Geom:combine(rect_b)
|
|
|
|
|
local combined = self:copy()
|
|
|
|
|
if not rect_b or rect_b:sizeof() == 0 then return combined end
|
|
|
|
|
if not rect_b or rect_b:area() == 0 then return combined end
|
|
|
|
|
if combined.x > rect_b.x then
|
|
|
|
|
combined.x = rect_b.x
|
|
|
|
|
end
|
|
|
|
@ -126,11 +160,13 @@ function Geom:combine(rect_b)
|
|
|
|
|
return combined
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
returns a rectangle for the part that we and a given rectangle share
|
|
|
|
|
--[[--
|
|
|
|
|
Returns a new rectangle for the part that we and a given rectangle share
|
|
|
|
|
|
|
|
|
|
TODO: what happens if there is no rectangle shared? currently behaviour is undefined.
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
@treturn Geom
|
|
|
|
|
]]--
|
|
|
|
|
-- TODO: what happens if there is no rectangle shared? currently behaviour is undefined.
|
|
|
|
|
function Geom:intersect(rect_b)
|
|
|
|
|
-- make a copy of self
|
|
|
|
|
local intersected = self:copy()
|
|
|
|
@ -153,9 +189,11 @@ function Geom:intersect(rect_b)
|
|
|
|
|
return intersected
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
return true if self does not share any area with rect_b
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
Return true if self does not share any area with rect_b
|
|
|
|
|
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
]]
|
|
|
|
|
function Geom:notIntersectWith(rect_b)
|
|
|
|
|
if (self.x >= (rect_b.x + rect_b.w))
|
|
|
|
|
or (self.y >= (rect_b.y + rect_b.h))
|
|
|
|
@ -166,28 +204,33 @@ function Geom:notIntersectWith(rect_b)
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
return true if self geom shares area with rect_b
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
Return true if self geom shares area with rect_b
|
|
|
|
|
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
]]
|
|
|
|
|
function Geom:intersectWith(rect_b)
|
|
|
|
|
return not self:notIntersectWith(rect_b)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
set size of dimension or rectangle to size of given dimension/rectangle
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
Set size of dimension or rectangle to size of given dimension/rectangle
|
|
|
|
|
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
]]
|
|
|
|
|
function Geom:setSizeTo(rect_b)
|
|
|
|
|
self.w = rect_b.w
|
|
|
|
|
self.h = rect_b.h
|
|
|
|
|
return self
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
check whether rect_b is within current rectangle
|
|
|
|
|
--[[--
|
|
|
|
|
Check whether rect_b is within current rectangle
|
|
|
|
|
|
|
|
|
|
works for dimensions, too
|
|
|
|
|
for points, it is basically an equality check
|
|
|
|
|
]]--
|
|
|
|
|
Works for dimensions, too. For points, it is basically an equality check
|
|
|
|
|
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
]]
|
|
|
|
|
function Geom:contains(rect_b)
|
|
|
|
|
if self.x <= rect_b.x
|
|
|
|
|
and self.y <= rect_b.y
|
|
|
|
@ -199,11 +242,13 @@ function Geom:contains(rect_b)
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
check for equality
|
|
|
|
|
--[[--
|
|
|
|
|
Check for equality
|
|
|
|
|
|
|
|
|
|
works for rectangles, points, dimensions
|
|
|
|
|
]]--
|
|
|
|
|
Works for rectangles, points, dimensions
|
|
|
|
|
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
]]
|
|
|
|
|
function Geom:__eq(rect_b)
|
|
|
|
|
if self.x == rect_b.x
|
|
|
|
|
and self.y == rect_b.y
|
|
|
|
@ -214,9 +259,11 @@ function Geom:__eq(rect_b)
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
check size of dimension/rectangle for equality
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
Check size of dimension/rectangle for equality
|
|
|
|
|
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
]]
|
|
|
|
|
function Geom:equalSize(rect_b)
|
|
|
|
|
if self.w == rect_b.w
|
|
|
|
|
and self.h == rect_b.h
|
|
|
|
@ -226,9 +273,11 @@ function Geom:equalSize(rect_b)
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
check if our size is smaller than the size of the given dimension/rectangle
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
Check if our size is smaller than the size of the given dimension/rectangle
|
|
|
|
|
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
]]
|
|
|
|
|
function Geom:__lt(rect_b)
|
|
|
|
|
DEBUG("lt:",self,rect_b)
|
|
|
|
|
if self.w < rect_b.w and self.h < rect_b.h then
|
|
|
|
@ -239,9 +288,10 @@ DEBUG("lt-")
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
check if our size is smaller or equal the size of the given dimension/rectangle
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
Check if our size is smaller or equal the size of the given dimension/rectangle
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
]]
|
|
|
|
|
function Geom:__le(rect_b)
|
|
|
|
|
if self.w <= rect_b.w and self.h <= rect_b.h then
|
|
|
|
|
return true
|
|
|
|
@ -249,13 +299,17 @@ function Geom:__le(rect_b)
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
offset the current rectangle by dx, dy while fitting it into the space
|
|
|
|
|
--[[--
|
|
|
|
|
Offset the current rectangle by dx, dy while fitting it into the space
|
|
|
|
|
of a given rectangle
|
|
|
|
|
|
|
|
|
|
this can also be called with dx=0 and dy=0, which will fit the current
|
|
|
|
|
This can also be called with dx=0 and dy=0, which will fit the current
|
|
|
|
|
rectangle into the given rectangle
|
|
|
|
|
]]--
|
|
|
|
|
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
@int dx
|
|
|
|
|
@int dy
|
|
|
|
|
]]
|
|
|
|
|
function Geom:offsetWithin(rect_b, dx, dy)
|
|
|
|
|
-- check size constraints and shrink us when we're too big
|
|
|
|
|
if self.w > rect_b.w then
|
|
|
|
@ -282,9 +336,13 @@ function Geom:offsetWithin(rect_b, dx, dy)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
center the current rectangle at position x and y of a given rectangle
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
Center the current rectangle at position x and y of a given rectangle
|
|
|
|
|
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
@int dx
|
|
|
|
|
@int dy
|
|
|
|
|
]]
|
|
|
|
|
function Geom:centerWithin(rect_b, x, y)
|
|
|
|
|
-- check size constraints and shrink us when we're too big
|
|
|
|
|
if self.w > rect_b.w then
|
|
|
|
@ -316,16 +374,21 @@ function Geom:shrinkInside(rect_b, dx, dy)
|
|
|
|
|
return self:intersect(rect_b)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
return the Euclidean distance between two geoms
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
Return the Euclidean distance between two geoms
|
|
|
|
|
|
|
|
|
|
@tparam Geom rect_b
|
|
|
|
|
]]
|
|
|
|
|
function Geom:distance(geom)
|
|
|
|
|
return math.sqrt(math.pow(self.x - geom.x, 2) + math.pow(self.y - geom.y, 2))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
return the midpoint of two geoms
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
Return the midpoint of two geoms
|
|
|
|
|
|
|
|
|
|
@tparam Geom geom
|
|
|
|
|
@treturn Geom
|
|
|
|
|
]]
|
|
|
|
|
function Geom:midpoint(geom)
|
|
|
|
|
return Geom:new{
|
|
|
|
|
x = Math.round((self.x + geom.x) / 2),
|
|
|
|
@ -334,9 +397,10 @@ function Geom:midpoint(geom)
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
return center point in this geom
|
|
|
|
|
]]--
|
|
|
|
|
--[[--
|
|
|
|
|
Return center point in this geom
|
|
|
|
|
@treturn Geom
|
|
|
|
|
]]
|
|
|
|
|
function Geom:center()
|
|
|
|
|
return Geom:new{
|
|
|
|
|
x = self.x + Math.round(self.w / 2),
|
|
|
|
|