diff --git a/worldeditadditions/utils/strings/tochars.lua b/worldeditadditions/utils/strings/tochars.lua index 7398868..72fd6bb 100644 --- a/worldeditadditions/utils/strings/tochars.lua +++ b/worldeditadditions/utils/strings/tochars.lua @@ -5,7 +5,7 @@ -- @returns table A sequence table containing the substrings function worldeditadditions.tochars(text,sort,rem_dups) --function tochars(text,s,d) - t, set = {}, {} + local t, set = {}, {} if rem_dups then text:gsub(".",function(c) set[c] = true end) for k,v in pairs(set) do table.insert(t,k) end @@ -17,3 +17,13 @@ function worldeditadditions.tochars(text,sort,rem_dups) return t end + +--- Split into set of characters. +-- @param text string The string to iterate over +-- @returns table A sequence set table containing the substrings +function worldeditadditions.tocharset(text) +-- function tocharset(text) + local t = {} + text:gsub(".",function(c) t[c] = true end) + return t +end diff --git a/worldeditadditions/utils/vector.lua b/worldeditadditions/utils/vector.lua index a22aa98..4c767b4 100644 --- a/worldeditadditions/utils/vector.lua +++ b/worldeditadditions/utils/vector.lua @@ -5,7 +5,7 @@ function worldeditadditions.vector.tostring(v) return "(" .. v.x ..", " .. v.y ..", " .. v.z ..")" end --- Calculates the length squared of the given vector. +--- Calculates the length squared of the given vector. -- @param v Vector The vector to operate on -- @return number The length of the given vector squared function worldeditadditions.vector.lengthsquared(v) @@ -104,3 +104,19 @@ end function worldeditadditions.vector.mean(pos1, pos2) return vector.new((pos1.x + pos2.x)/2, (pos1.y + pos2.y)/2, (pos1.z + pos2.z)/2) end + +--- Returns a vector of the min values of 2 positions. +-- @param pos1 Vector pos1 of the defined region. +-- @param pos2 Vector pos2 of the defined region. +-- @return Vector Min values from input vectors. +function worldeditadditions.vector.min(pos1, pos2) + return vector.new(math.min(pos1.x + pos2.x), math.min(pos1.y + pos2.y), math.min(pos1.z + pos2.z)) +end + +--- Returns a vector of the max values of 2 positions. +-- @param pos1 Vector pos1 of the defined region. +-- @param pos2 Vector pos2 of the defined region. +-- @return Vector Max values from input vectors. +function worldeditadditions.vector.max(pos1, pos2) + return vector.new(math.max(pos1.x + pos2.x), math.max(pos1.y + pos2.y), math.max(pos1.z + pos2.z)) +end diff --git a/worldeditadditions_commands/commands/selectors/smake.lua b/worldeditadditions_commands/commands/selectors/smake.lua new file mode 100644 index 0000000..5b7743a --- /dev/null +++ b/worldeditadditions_commands/commands/selectors/smake.lua @@ -0,0 +1,135 @@ +-- ███████ ███ ███ █████ ██ ██ ███████ +-- ██ ████ ████ ██ ██ ██ ██ ██ +-- ███████ ██ ████ ██ ███████ █████ █████ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ███████ ██ ██ ██ ██ ██ ██ ███████ +local wea = worldeditadditions +worldedit.register_command("smake", { + params = " [ []]", + description = "Make one or more axes of the current selection odd, even, or equal to another.", + privs = { worldedit = true }, + require_pos = 2, + parse = function(params_text) + -- Split params_text, check for missing arguments and fill in empty spots + local parts = wea.split(params_text, "%s+", false) + if #parts < 2 then + return false, "Error: Not enough arguments. Expected \" [ []]\"." + else + for i=3,4 do if not parts[i] then parts[i] = false end end + end + + -- Initialze local variables and sets + local oper, mode, targ, base = unpack(parts) + local operSet, modeSet = wea.makeset {"equal", "odd", "even"}, wea.makeset {"grow", "shrink", "avg"} + + -- Main Logic + -- Check base if base is present and if so valid. + if base then + if base:match("[xyz]") then -- ensure correct base syntax + base = base:match("[xyz]") + else + return false, "Error: Invalid base \""..base.."\". Expected \"x\", \"y\" or \"z\"." + end + end + + -- Resolve target then mode (in that order incase mode is target). + if not targ then -- If no target set to default (xyz) + targ = "xyz" + elseif targ:match("[xyz]+") then -- ensure correct target syntax + targ = table.concat(wea.tochars(targ:match("[xyz]+"),true,true)) + else + return false, "Error: Invalid \""..targ.."\". Expected \"x\" and or \"y\" and or \"z\"." + end + + if mode == "average" then -- If mode is average set to avg + mode = "avg" + elseif mode:match("[xyz]+") then -- If target is actually base set vars to correct values. + base, targ, mode = targ:sub(1,1), table.concat(wea.tochars(mode:match("[xyz]+"),true,true)), false + elseif not modeSet[mode] and not base then -- If mode is invalid and base isn't present throw error + return false, "Error: Invalid \""..mode.."\". Expected \"grow\", \"shrink\", or \"average\"/\"avg\"." + end + + if base then + if oper ~= "equal" then base = false -- If operation isn't equalize we don't need + elseif targ:match(base) then -- Else check that base is not in target and return error if it is + return false, "Error: ("..base..") cannot be included in ("..targ..")." + end + end + + -- Check if operator is valid + if not operSet[oper] then + return false, "Error: Invalid operator \""..oper.."\". Expected \"odd\", \"even\" or \"equal\"." + end + + if false then + return false, ": " .. oper .. ", : " .. tostring(mode) .. ", : " .. tostring(targ) .. ", : " .. tostring(base) + end + return true, oper, mode, targ, base + end, + func = function(name, oper, mode, targ, base) + local p1, p2, eval = vector.new(worldedit.pos1[name]), vector.new(worldedit.pos2[name]), function(int) return int or 0 end + local delta, targ, _m = vector.subtract(p2,p1), wea.tocharset(targ), 0 -- local delta equation: Vd(a) = V2(a) - V1(a) + + -- set _m to the max, min or mean of the target axes depending on mode + if mode == "avg" then + for k,v in pairs(targ) do _m = _m + math.abs(delta[k]) end + _m = _m / #targ + elseif mode == "grow" then + for k,v in pairs(targ) do if math.abs(delta[k]) > _m then _m = math.abs(delta[k]) end end + else + for k,v in pairs(targ) do if math.abs(delta[k]) < _m then _m = math.abs(delta[k]) end end + end + + if oper == "even" then + eval = function(int) + local tmp, abs, neg = int / 2, math.abs(int), int < 0 + if math.floor(tmp) ~= tmp then + if mode == "avg" then + if int > _m then int = abs - 1 + else int = abs + 1 end + elseif mode == "shrink" and abs > 1 then int = abs - 1 + else int = abs + 1 end + end + if neg then int = int * -1 end + return int + end + elseif oper == "odd" then + eval = function(int) + local tmp, abs, neg = int / 2, math.abs(int), int < 0 + if math.floor(tmp) ~= tmp then + if mode == "avg" then + if int > _m then int = abs - 1 + else int = abs + 1 end + elseif mode == "shrink" and abs > 1 then int = abs - 1 + else int = abs + 1 end + end + if neg then int = int * -1 end + return int + end + elseif oper == "fac" then + -- Future feature to add compatability with //maze + -- //smake factor avg xz 5 + -- //smake fac grow 3 + -- Equasion: round(delta[] / factor) * factor + else -- Case: oper == "equal" + return false, "Case \"equal\" not handled." + end + + -- for k,v in pairs(targ) do delta[k] = eval(delta[k]) end + + --- There is a test: + local brk = "" + for k,v in pairs(targ) do + brk = brk..k..": "..delta[k]..", " + delta[k] = eval(delta[k]) + brk = brk..k..": "..delta[k]..", " + end + if true then return false, brk end + -- //multi //fp set1 589 2 -82 //fp set2 615 2 -53 + -- //smake even shrink + + worldedit.pos2[name] = vector.add(p1,delta) + worldedit.mark_pos2(name) + return true, "position 2 set to " .. minetest.pos_to_string(p2) + end +}) diff --git a/worldeditadditions_commands/init.lua b/worldeditadditions_commands/init.lua index e1b9eb0..a03833d 100644 --- a/worldeditadditions_commands/init.lua +++ b/worldeditadditions_commands/init.lua @@ -46,6 +46,7 @@ dofile(we_c.modpath.."/commands/selectors/scentre.lua") dofile(we_c.modpath.."/commands/selectors/scloud.lua") dofile(we_c.modpath.."/commands/selectors/scol.lua") dofile(we_c.modpath.."/commands/selectors/scube.lua") +dofile(we_c.modpath.."/commands/selectors/smake.lua") dofile(we_c.modpath.."/commands/selectors/spop.lua") dofile(we_c.modpath.."/commands/selectors/spush.lua") dofile(we_c.modpath.."/commands/selectors/srect.lua")