mirror of
https://github.com/koreader/koreader
synced 2024-10-31 21:20:20 +00:00
8e4516b824
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.
341 lines
6.7 KiB
Lua
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
|