mirror of
https://github.com/appgurueu/modlib.git
synced 2024-12-22 13:22:27 +01:00
Fix handling of multiple texmod
transforms
This commit is contained in:
parent
e07d646203
commit
8e5bdc817c
@ -123,35 +123,106 @@ function texmod:invert(channels --[[set with keys "r", "g", "b", "a"]])
|
||||
end
|
||||
|
||||
function texmod:flip(flip_axis --[["x" or "y"]])
|
||||
assert(flip_axis == "x" or flip_axis == "y")
|
||||
return new{
|
||||
type = "transform",
|
||||
base = self,
|
||||
flip_axis = flip_axis
|
||||
}
|
||||
return self:transform(assert(
|
||||
(flip_axis == "x" and "fx")
|
||||
or (flip_axis == "y" and "fy")
|
||||
or (not flip_axis and "i")))
|
||||
end
|
||||
|
||||
function texmod:rotate(deg)
|
||||
assert(deg % 90 == 0)
|
||||
deg = deg % 360
|
||||
assert(deg % 90 == 0, "only multiples of 90° supported")
|
||||
return new{
|
||||
type = "transform",
|
||||
base = self,
|
||||
rotation_deg = deg
|
||||
}
|
||||
return self:transform(("r%d"):format(deg))
|
||||
end
|
||||
|
||||
-- First flip, then rotate counterclockwise
|
||||
function texmod:transform(flip_axis, rotation_deg)
|
||||
assert(flip_axis == nil or flip_axis == "x" or flip_axis == "y")
|
||||
rotation_deg = (rotation_deg or 0) % 360
|
||||
assert(rotation_deg % 90 == 0, "only multiples of 90° supported")
|
||||
return new{
|
||||
type = "transform",
|
||||
base = self,
|
||||
rotation_deg = rotation_deg ~= 0 and rotation_deg or nil,
|
||||
flip_axis = flip_axis
|
||||
-- D4 group transformations (see https://proofwiki.org/wiki/Definition:Dihedral_Group_D4),
|
||||
-- represented using indices into a table of matrices
|
||||
-- TODO (...) try to come up with a more elegant solution
|
||||
do
|
||||
-- Matrix multiplication for composition: First applies a, then b <=> b * a
|
||||
local function mat_2x2_compose(a, b)
|
||||
local a_1_1, a_1_2, a_2_1, a_2_2 = unpack(a)
|
||||
local b_1_1, b_1_2, b_2_1, b_2_2 = unpack(b)
|
||||
return {
|
||||
a_1_1 * b_1_1 + a_2_1 * b_1_2, a_1_2 * b_1_1 + a_2_2 * b_1_2;
|
||||
a_1_1 * b_2_1 + a_2_1 * b_2_2, a_1_2 * b_2_1 + a_2_2 * b_2_2
|
||||
}
|
||||
end
|
||||
local r90 ={
|
||||
0, -1;
|
||||
1, 0
|
||||
}
|
||||
local fx = {
|
||||
-1, 0;
|
||||
0, 1
|
||||
}
|
||||
local fy = {
|
||||
1, 0;
|
||||
0, -1
|
||||
}
|
||||
local r180 = mat_2x2_compose(r90, r90)
|
||||
local r270 = mat_2x2_compose(r180, r90)
|
||||
local fxr90 = mat_2x2_compose(fx, r90)
|
||||
local fyr90 = mat_2x2_compose(fy, r90)
|
||||
local transform_mats = {[0] = {1, 0; 0, 1}, r90, r180, r270, fx, fxr90, fy, fyr90}
|
||||
local transform_idx_by_name = {i = 0, r90 = 1, r180 = 2, r270 = 3, fx = 4, fxr90 = 5, fy = 6, fyr90 = 7}
|
||||
-- Lookup tables for getting the flipped axis / rotation angle
|
||||
local flip_by_idx = {
|
||||
[4] = "x",
|
||||
[5] = "x",
|
||||
[6] = "y",
|
||||
[7] = "y",
|
||||
}
|
||||
local rot_by_idx = {
|
||||
[1] = 90,
|
||||
[2] = 180,
|
||||
[3] = 270,
|
||||
[5] = 90,
|
||||
[7] = 90,
|
||||
}
|
||||
local idx_by_mat_2x2 = {}
|
||||
local function transform_idx(mat)
|
||||
-- note: assumes mat[i] in {-1, 0, 1}
|
||||
return mat[1] + 3*(mat[2] + 3*(mat[3] + 3*mat[4]))
|
||||
end
|
||||
for i = 0, 7 do
|
||||
idx_by_mat_2x2[transform_idx(transform_mats[i])] = i
|
||||
end
|
||||
-- Compute a multiplication table
|
||||
local composition_idx = {}
|
||||
local function ij_idx(i, j)
|
||||
return i*8 + j
|
||||
end
|
||||
for i = 0, 7 do
|
||||
for j = 0, 7 do
|
||||
composition_idx[ij_idx(i, j)] = assert(idx_by_mat_2x2[
|
||||
transform_idx(mat_2x2_compose(transform_mats[i], transform_mats[j]))])
|
||||
end
|
||||
end
|
||||
function texmod:transform(...)
|
||||
if select("#", ...) == 0 then return self end
|
||||
local idx = ...
|
||||
if type(idx) == "string" then
|
||||
idx = assert(transform_idx_by_name[idx:lower()])
|
||||
end
|
||||
local base = self
|
||||
if self.type == "transform" then
|
||||
-- Merge with a `^[transform` base image
|
||||
assert(transform_mats[idx])
|
||||
base = self.base
|
||||
idx = composition_idx[ij_idx(self.idx, idx)]
|
||||
end
|
||||
assert(transform_mats[idx])
|
||||
if idx == 0 then return base end -- identity
|
||||
return new{
|
||||
type = "transform",
|
||||
base = base,
|
||||
idx = idx,
|
||||
-- Redundantly store this information for convenience. Do not modify!
|
||||
flip_axis = flip_by_idx[idx],
|
||||
rotation_deg = rot_by_idx[idx] or 0,
|
||||
}:transform(select(2, ...))
|
||||
end
|
||||
end
|
||||
|
||||
function texmod:verticalframe(framecount, frame)
|
||||
@ -256,4 +327,4 @@ function texmod:lowpart(percent, overlay)
|
||||
}
|
||||
end
|
||||
|
||||
return texmod, metatable
|
||||
return texmod, metatable
|
||||
|
@ -79,34 +79,25 @@ function pr.invert(r)
|
||||
end
|
||||
|
||||
do
|
||||
local transforms = {
|
||||
[0] = {},
|
||||
{rotation_deg = 90},
|
||||
{rotation_deg = 180},
|
||||
{rotation_deg = 270},
|
||||
{flip_axis = "x"},
|
||||
{flip_axis = "x", rotation_deg = 90},
|
||||
{flip_axis = "y"},
|
||||
{flip_axis = "y", rotation_deg = 90},
|
||||
}
|
||||
function pr.transform(r)
|
||||
-- Note: While it isn't documented, `[transform` is indeed case-insensitive.
|
||||
if r:match_charset"[iI]" then
|
||||
return
|
||||
return pr.transform(r)
|
||||
end
|
||||
local idx = r:match_charset"[0-7]"
|
||||
if idx then
|
||||
return tonumber(idx), pr.transform(r)
|
||||
end
|
||||
local flip_axis
|
||||
if r:match_charset"[fF]" then
|
||||
flip_axis = assert(r:match_charset"[xXyY]", "axis expected"):lower()
|
||||
local flip_axis = assert(r:match_charset"[xXyY]", "axis expected")
|
||||
return "f" .. flip_axis, pr.transform(r)
|
||||
end
|
||||
local rot_deg
|
||||
if r:match_charset"[rR]" then
|
||||
rot_deg = r:int()
|
||||
local deg = r:match_str"%d"
|
||||
-- Be strict here: Minetest won't recognize other ways to write these numbers (or other numbers)
|
||||
assert(deg == "90" or deg == "180" or deg == "270")
|
||||
return ("r%d"):format(deg), pr.transform(r)
|
||||
end
|
||||
if flip_axis or rot_deg then
|
||||
return flip_axis, rot_deg
|
||||
end
|
||||
local transform = assert(transforms[r:int()], "out of range")
|
||||
return transform.flip_axis, transform.rotation_deg
|
||||
-- return nothing, we're done
|
||||
end
|
||||
end
|
||||
|
||||
@ -329,4 +320,4 @@ return function(read_char)
|
||||
local res = r:texp()
|
||||
assert(r.eof, "eof expected")
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
@ -51,28 +51,7 @@ function pw:invert(w)
|
||||
end
|
||||
|
||||
function pw:transform(w)
|
||||
local rot_deg, flip_axis = self.rotation_deg or 0, self.flip_axis
|
||||
if rot_deg == 0 and flip_axis == nil then
|
||||
w.str"I"
|
||||
elseif rot_deg == 0 then
|
||||
w.str(assert(({x = "FX", y = "FY"})[flip_axis]))
|
||||
elseif flip_axis == nil then
|
||||
w.str"R"; w.int(self.rotation_deg)
|
||||
elseif rot_deg == 90 then
|
||||
w.str(assert(({x = "FX", y = "FY"})[flip_axis]))
|
||||
w.str"R90"
|
||||
elseif rot_deg == 180 then
|
||||
-- Rotating by 180° is equivalent to flipping both axes;
|
||||
-- if one axis was already flipped, that is undone -
|
||||
-- thus it is equivalent to flipping the other axis
|
||||
w.str(assert(({x = "FY", y = "FX"})[flip_axis]))
|
||||
elseif rot_deg == 270 then
|
||||
-- Rotating by 270° is equivalent to first rotating by 180°, then rotating by 90°;
|
||||
-- first flipping an axis and then rotating by 180°
|
||||
-- is equivalent to flipping the other axis as shown above
|
||||
w.str(assert(({x = "FY", y = "FX"})[flip_axis]))
|
||||
w.str"R90"
|
||||
end
|
||||
w.int(self.idx)
|
||||
end
|
||||
|
||||
function pw:verticalframe(w)
|
||||
@ -172,4 +151,4 @@ return function(self, write_str)
|
||||
w.level = w.level - 1
|
||||
end
|
||||
w.tex(self)
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user