2
0
mirror of https://github.com/koreader/koreader synced 2024-10-31 21:20:20 +00:00
koreader/frontend/ui/geometry.lua
chrox 8e4516b824 add regional zoom mode in pdf/djvu page
In regional zoom mode double tap will zoom to the tapped
region(paragraph or column, etc., detected optically via libk2pdfopt).
As the first demo, this feature is only turned on in flipping mode by
tapping the top-left corner of the screen. Eventually we may incorporate
this feature in "free" zoom mode.
2014-01-02 11:14:26 +08:00

341 lines
6.7 KiB
Lua

local DEBUG = require("dbg")
--[[
2D Geometry utilities
all of these apply to full rectangles { x = ..., y = ..., w = ..., h = ... }
some behaviour is defined for points { x = ..., y = ... }
some behaviour is defined for dimensions { 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 Geom = {
x = 0,
y = 0,
w = 0,
h = 0
}
function Geom:new(o)
local o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
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
]]--
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
]]--
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
if a single factor is given, it is applied to both width and height
]]--
function Geom:scaleBy(zx, zy)
self.w = self.w * zx
self.h = self.h * (zy or zx)
return self
end
--[[
this method also takes care of x and y
]]--
function Geom:transformByScale(zx, zy)
self.x = self.x * zx
self.y = self.y * (zx or zy)
self:scaleBy(zx, zy)
end
--[[
return size of geom
]]--
function Geom:sizeof()
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
]]--
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
works for rectangles, dimensions and points
]]--
function Geom:combine(rect_b)
local combined = self:copy()
if not rect_b or rect_b:sizeof() == 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 rectangle for the part that we and a given rectangle share
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
]]--
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
--[[
set size of dimension or rectangle to size of given dimension/rectangle
]]--
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
]]--
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
]]--
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
]]--
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
]]--
function Geom:__lt(rect_b)
DEBUG("lt:",self,rect_b)
if self.w < rect_b.w and self.h < rect_b.h then
DEBUG("lt+")
return true
end
DEBUG("lt-")
return false
end
--[[
check if our size is smaller or equal the size of the given dimension/rectangle
]]--
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
]]--
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
]]--
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
]]--
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
]]--
function Geom:midpoint(geom)
return Geom:new{
x = (self.x + geom.x) / 2,
y = (self.y + geom.y) / 2,
w = 0, h = 0,
}
end
--[[
return center point in this geom
]]--
function Geom:center()
return Geom:new{
x = self.x + self.w / 2,
y = self.y + self.h / 2,
w = 0, h = 0,
}
end
return Geom