mirror of
https://github.com/appgurueu/modlib.git
synced 2024-11-25 16:53:46 +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
|
end
|
||||||
|
|
||||||
function texmod:flip(flip_axis --[["x" or "y"]])
|
function texmod:flip(flip_axis --[["x" or "y"]])
|
||||||
assert(flip_axis == "x" or flip_axis == "y")
|
return self:transform(assert(
|
||||||
return new{
|
(flip_axis == "x" and "fx")
|
||||||
type = "transform",
|
or (flip_axis == "y" and "fy")
|
||||||
base = self,
|
or (not flip_axis and "i")))
|
||||||
flip_axis = flip_axis
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function texmod:rotate(deg)
|
function texmod:rotate(deg)
|
||||||
|
assert(deg % 90 == 0)
|
||||||
deg = deg % 360
|
deg = deg % 360
|
||||||
assert(deg % 90 == 0, "only multiples of 90° supported")
|
return self:transform(("r%d"):format(deg))
|
||||||
return new{
|
|
||||||
type = "transform",
|
|
||||||
base = self,
|
|
||||||
rotation_deg = deg
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- First flip, then rotate counterclockwise
|
-- D4 group transformations (see https://proofwiki.org/wiki/Definition:Dihedral_Group_D4),
|
||||||
function texmod:transform(flip_axis, rotation_deg)
|
-- represented using indices into a table of matrices
|
||||||
assert(flip_axis == nil or flip_axis == "x" or flip_axis == "y")
|
-- TODO (...) try to come up with a more elegant solution
|
||||||
rotation_deg = (rotation_deg or 0) % 360
|
do
|
||||||
assert(rotation_deg % 90 == 0, "only multiples of 90° supported")
|
-- 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{
|
return new{
|
||||||
type = "transform",
|
type = "transform",
|
||||||
base = self,
|
base = base,
|
||||||
rotation_deg = rotation_deg ~= 0 and rotation_deg or nil,
|
idx = idx,
|
||||||
flip_axis = flip_axis
|
-- 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
|
end
|
||||||
|
|
||||||
function texmod:verticalframe(framecount, frame)
|
function texmod:verticalframe(framecount, frame)
|
||||||
|
@ -79,34 +79,25 @@ function pr.invert(r)
|
|||||||
end
|
end
|
||||||
|
|
||||||
do
|
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)
|
function pr.transform(r)
|
||||||
-- Note: While it isn't documented, `[transform` is indeed case-insensitive.
|
|
||||||
if r:match_charset"[iI]" then
|
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
|
end
|
||||||
local flip_axis
|
|
||||||
if r:match_charset"[fF]" then
|
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
|
end
|
||||||
local rot_deg
|
|
||||||
if r:match_charset"[rR]" then
|
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
|
end
|
||||||
if flip_axis or rot_deg then
|
-- return nothing, we're done
|
||||||
return flip_axis, rot_deg
|
|
||||||
end
|
|
||||||
local transform = assert(transforms[r:int()], "out of range")
|
|
||||||
return transform.flip_axis, transform.rotation_deg
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -51,28 +51,7 @@ function pw:invert(w)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function pw:transform(w)
|
function pw:transform(w)
|
||||||
local rot_deg, flip_axis = self.rotation_deg or 0, self.flip_axis
|
w.int(self.idx)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function pw:verticalframe(w)
|
function pw:verticalframe(w)
|
||||||
|
Loading…
Reference in New Issue
Block a user