Files
koreader/frontend/ui/geometry.lua
Qingping Hou 0c49b915de refactor: add touch zone subsystem to inputcontainer
Touch zone decouples screen size from gesture event registration.

The win here is each individual widget does not need to update
gesture range on screen rotate/resize anymore.

Another advantage is we now have a centralized ordered array to handle
all registered touch event listeners, makes it much easier to resolve
gesture range conflicts between multiple widgets.

This patch also includes the following changes:

* migrate readerpaging to use readerui's touch zone
* migrate readerfooter to use readerui's touch zone
* move inverse read direction setting to touch menu's setting tab
* moved kobolight widget from readerview into readerui
* various dead code cleanups and comments
2016-12-10 16:06:51 -08:00

408 lines
8.3 KiB
Lua

--[[--
2D Geometry utilities
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:
Geom:new{ w = 600, h = 800, }
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,
}
function Geom:new(o)
if not o then o = {} end
setmetatable(o, self)
self.__index = self
return o
end
--[[--
Make a deep copy of itself.
@treturn Geom
]]
function Geom:copy()
local n = Geom:new()
n.x = self.x
n.y = self.y
n.w = self.w
n.h = self.h
return n
end
function Geom:__tostring()
return self.w.."x"..self.h.."+"..self.x.."+"..self.y
end
--[[--
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
--[[--
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
--[[--
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
@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 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
--[[--
Returns area of itself.
@treturn int
]]
function Geom:area()
if not self.w or not self.h then
return 0
else
return self.w * self.h
end
end
--[[--
Enlarges or shrinks dimensions or rectangles
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
--[[--
Returns a new outer rectangle that contains both us and a given rectangle
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:area() == 0 then return combined end
if combined.x > rect_b.x then
combined.x = rect_b.x
end
if combined.y > rect_b.y then
combined.y = rect_b.y
end
if self.x + self.w > rect_b.x + rect_b.w then
combined.w = self.x + self.w - combined.x
else
combined.w = rect_b.x + rect_b.w - combined.x
end
if self.y + self.h > rect_b.y + rect_b.h then
combined.h = self.y + self.h - combined.y
else
combined.h = rect_b.y + rect_b.h - combined.y
end
return combined
end
--[[--
Returns a new rectangle for the part that we and a given rectangle share
@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()
if self.x < rect_b.x then
intersected.x = rect_b.x
end
if self.y < rect_b.y then
intersected.y = rect_b.y
end
if self.x + self.w < rect_b.x + rect_b.w then
intersected.w = self.x + self.w - intersected.x
else
intersected.w = rect_b.x + rect_b.w - intersected.x
end
if self.y + self.h < rect_b.y + rect_b.h then
intersected.h = self.y + self.h - intersected.y
else
intersected.h = rect_b.y + rect_b.h - intersected.y
end
return intersected
end
--[[--
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))
or (rect_b.x >= (self.x + self.w))
or (rect_b.y >= (self.y + self.h)) then
return true
end
return false
end
--[[--
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
@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
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
and self.x + self.w >= rect_b.x + rect_b.w
and self.y + self.h >= rect_b.y + rect_b.h
then
return true
end
return false
end
--[[--
Check for equality
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
and self:equalSize(rect_b)
then
return true
end
return false
end
--[[--
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 then
return true
end
return false
end
--[[--
Check if our size is smaller than the size of the given dimension/rectangle
@tparam Geom rect_b
]]
function Geom:__lt(rect_b)
if self.w < rect_b.w and self.h < rect_b.h then
return true
end
return false
end
--[[--
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
end
return false
end
--[[--
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
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
self.w = rect_b.w
end
if self.h > rect_b.h then
self.h = rect_b.h
end
-- offset
self.x = self.x + dx
self.y = self.y + dy
-- check offsets
if self.x < rect_b.x then
self.x = rect_b.x
end
if self.y < rect_b.y then
self.y = rect_b.y
end
if self.x + self.w > rect_b.x + rect_b.w then
self.x = rect_b.x + rect_b.w - self.w
end
if self.y + self.h > rect_b.y + rect_b.h then
self.y = rect_b.y + rect_b.h - self.h
end
end
--[[--
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
self.w = rect_b.w
end
if self.h > rect_b.h then
self.h = rect_b.h
end
-- place to center
self.x = x - self.w/2
self.y = y - self.h/2
-- check boundary
if self.x < rect_b.x then
self.x = rect_b.x
end
if self.y < rect_b.y then
self.y = rect_b.y
end
if self.x + self.w > rect_b.x + rect_b.w then
self.x = rect_b.x + rect_b.w - self.w
end
if self.y + self.h > rect_b.y + rect_b.h then
self.y = rect_b.y + rect_b.h - self.h
end
end
function Geom:shrinkInside(rect_b, dx, dy)
self:offsetBy(dx, dy)
return self:intersect(rect_b)
end
--[[--
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
@tparam Geom geom
@treturn Geom
]]
function Geom:midpoint(geom)
return Geom:new{
x = Math.round((self.x + geom.x) / 2),
y = Math.round((self.y + geom.y) / 2),
w = 0, h = 0,
}
end
--[[--
Return center point in this geom
@treturn Geom
]]
function Geom:center()
return Geom:new{
x = self.x + Math.round(self.w / 2),
y = self.y + Math.round(self.h / 2),
w = 0, h = 0,
}
end
return Geom