commit 24912c357ecf62d98663f3f5ad74dcc1910115ae Author: Leslie Krause Date: Sat May 18 18:52:00 2019 -0400 BUILD 01 - initial alpha version diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..d78e80b --- /dev/null +++ b/README.txt @@ -0,0 +1,67 @@ +Scarlet Mod v1.0 +By Leslie Krause + +Scarlet is a thin-wrapper library for Minetest that provides a logical, uniform system of +of measurement for formspecs. + +Repository +---------------------- + +Browse source code: + https://bitbucket.org/sorcerykid/scarlet + +Download archive: + https://bitbucket.org/sorcerykid/scarlet/get/master.zip + https://bitbucket.org/sorcerykid/scarlet/get/master.tar.gz + +Revision History +---------------------- + +Version 1.0b (18-May-2019) + - initial alpha version + +Compatability +---------------------- + +Minetest 0.4.15+ required + +Dependencies +---------------------- + +ActiveFormspecs Mod (optional) + https://bitbucket.org/sorcerykid/formspecs + +Installation +---------------------- + + 1) Unzip the archive into the mods directory of your game + 2) Rename the scarlet-master directory to "scarlet" + 3) Add "scarlet" as a dependency for any mods using the API + + +Source Code License +---------------------- + +The MIT License (MIT) + +Copyright (c) 2018, Leslie Krause (leslie@searstower.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +For more details: +https://opensource.org/licenses/MIT + diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..1e17b67 --- /dev/null +++ b/init.lua @@ -0,0 +1,527 @@ +-------------------------------------------------------- +-- Minetest :: Scarlet Mod (scarlet) +-- +-- See README.txt for licensing and release notes. +-- Copyright (c) 2019, Leslie E. Krause +-------------------------------------------------------- + +ScarletDef = function ( screen_dpi, gui_scaling, has_padding ) + + local image_size = 0.55 * ( screen_dpi or 72 ) * ( gui_scaling or 1 ) + + -- calculate the dot pitch as images per pixel and cells per pixel + -- using the "black magic" algorithm from guiFormSpecMenu.cpp + local dot_pitch = { + -- imgsize (images per pixel) + ix = 1 / image_size, + iy = 1 / image_size, + -- spacing (cells per pixel) + x = 1 / ( image_size * 5 / 4 ), + y = 1 / ( image_size * 15 / 13 ) + } + + -- padding width and height in cell units + -- original formula: image_size * 3.0 / 8 + local padding_width = has_padding and 0 or 3 / 10 + local padding_height = has_padding and 0 or 13 / 40 + + -- nominal button height in cell units + -- NB: this is 2x the value of m_button_height used internally! + -- original formula: image_size * 15.0 / 13 * 0.35 + local button_height = 0.7 + + -- cell margin width and height in cell units + -- original formula: vx * spacing.x - ( spacing.x - imgsize.x ) + local cell_margin_width = 1 / 5 + local cell_margin_height = 2 / 15 + + --------------------------- + -- unit conversion objects + --------------------------- + + local units = ( function ( ) + -- cell size measurements + local factors = { + i = { x = 4 / 5, y = 13 / 15 }, -- imgsize + c = { x = 1, y = 1 }, -- spacing (unity) + b = { y = button_height }, -- 2 x m_button_height + } + local function get_x( v, u ) + return not factors[ u ] and math.floor( v ) * dot_pitch.x or v * factors[ u ].x + end + local function get_y( v, u ) + return not factors[ u ] and math.floor( v ) * dot_pitch.y or v * factors[ u ].y + end + return { get_x = get_x, get_y = get_y } + end )( ) + + local iunits = ( function ( ) + -- image size measurements + local factors = { + i = { x = 1, y = 1 }, -- imgsize (unity) + c = { x = 5 / 4, y = 15 / 13 }, -- spacing + b = { y = button_height * 15 / 13 }, -- 2 x m_button_height + } + local function get_x( v, u ) + return not factors[ u ] and math.floor( v ) * dot_pitch.ix or v * factors[ u ].x + end + local function get_y( v, u ) + return not factors[ u ] and math.floor( v ) * dot_pitch.iy or v * factors[ u ].y + end + return { get_x = get_x, get_y = get_y } + end )( ) + + ----------------------- + -- translation helpers + ----------------------- + + local function element( name, params ) + return name .. "[" .. table.concat( params, ";" ) .. "]" + end + + local function get_pos( params, pos_units, offsets ) + local replace = "%0.3f,%0.3f" + local pos_x, u1, pos_y, u2 = string.match( params[ 1 ], "^(-?[0-9.]+)([icp]?),(-?[0-9.]+)([icpb]?)$" ) + + if not pos_x then return end + + return string.format( replace, + pos_units.get_x( pos_x, u1 ) + offsets[ 1 ], + pos_units.get_y( pos_y, u2 ) + offsets[ 2 ] + ) + end + + local function get_pos_and_dim_x( params, pos_units, dim_units, offsets ) + local replace = "%0.3f,%0.3f;%0.3f,0.000" + local pos_x, u1, pos_y, u2 = string.match( params[ 1 ], "^(-?[0-9.]+)([icp]?),(-?[0-9.]+)([icpb]?)$" ) + local dim_x, u3 = string.match( params[ 2 ], "^([0-9.]+)([iscp]?)$" ) + + if not pos_x or not dim_x then return end + + if u3 == "s" then + u3 = "i" + offsets[ 3 ] = offsets[ 3 ] + ( dim_x % 1 == 0 and dim_x - 1 or math.floor( dim_x ) ) * cell_margin_width + end + + return string.format( replace, + pos_units.get_x( pos_x, u1 ) + offsets[ 1 ], + pos_units.get_y( pos_y, u2 ) + offsets[ 2 ], + dim_units.get_x( dim_x, u3 ) + offsets[ 3 ] + ) + end + + local function get_pos_and_dim( params, pos_units, dim_units, offsets ) + local replace = "%0.3f,%0.3f;%0.3f,%0.3f" + local pos_x, u1, pos_y, u2 = string.match( params[ 1 ], "^(-?[0-9.]+)([icp]?),(-?[0-9.]+)([icpb]?)$" ) + local dim_x, u3, dim_y, u4 = string.match( params[ 2 ], "^([0-9.]+)([iscp]?),([0-9.]+)([iscpb]?)$" ) + + if not pos_x or not dim_x then return end + + if u3 == "s" then + u3 = "i" + offsets[ 3 ] = offsets[ 3 ] + ( dim_x % 1 == 0 and dim_x - 1 or math.floor( dim_x ) ) * cell_margin_width + end + if u4 == "s" then + u4 = "i" + offsets[ 4 ] = offsets[ 4 ] + ( dim_y % 1 == 0 and dim_y - 1 or math.floor( dim_y ) ) * cell_margin_height + end + + return string.format( replace, + pos_units.get_x( pos_x, u1 ) + offsets[ 1 ], + pos_units.get_y( pos_y, u2 ) + offsets[ 2 ], + dim_units.get_x( dim_x, u3 ) + offsets[ 3 ], + dim_units.get_y( dim_y, u4 ) + offsets[ 4 ] + ) + end + + ------------------------------ + -- generic element subclasses + ------------------------------ + + local function ElemPos( pos_units, length ) + return function ( name, params ) + local pos = get_pos( params, pos_units, { + -padding_width, + -padding_height + } ) + assert( pos and #params == length, "Cannot parse formspec element " .. name .. "[]" ) + return element( name, { pos, unpack( params, 2 ) } ) + end + end + + local function ElemPosAndDim( pos_units, dim_units, length, is_unlimited ) + return function ( name, params ) + local pos_and_dim = get_pos_and_dim( params, pos_units, dim_units, { + -padding_width, + -padding_height, + 0, + 0 + } ) + assert( pos_and_dim and ( #params == length or is_unlimited and #params > length ), "Cannot parse formspec element " .. name .. "[]" ) + return element( name, { pos_and_dim, unpack( params, 3 ) } ) + end + end + + local function ElemPosAndDimX( pos_units, dim_units, length, is_unlimited ) + return function ( name, params ) + local pos_and_dim_x = get_pos_and_dim_x( params, pos_units, dim_units, { + -padding_width, + -padding_height, + 0 + } ) + assert( pos_and_dim_x and ( #params == length or is_unlimited and #params > length ), "Cannot parse formspec element " .. name .. "[]" ) + return element( name, { pos_and_dim_x, unpack( params, 3 ) } ) + end + end + + ------------------------------ + -- special element subclasses + ------------------------------ + + local function SizeElement( ) + local pattern = "^(%d+)([iscp]?),(%d+)([iscpb]?)$" + local replace = "%0.3f,%0.3f,true" + + return function ( name, params ) + local dim, count = string.gsub( params[ 1 ], pattern, function ( dim_x, u1, dim_y, u2 ) + return string.format( replace, + units.get_x( dim_x, u1 ), + units.get_y( dim_y, u2 ) + ) + end ) + assert( count == 1, "Cannot parse formspec element size[]" ) + return element( "size", { dim } ) + end + end + + local function ListElement( ) + local pattern = "^(%d+)([icp]?),(%d+)([icpb]?)$" + local replace = "%0.3f,%0.3f" + + return function ( name, params ) + local pos, count = string.gsub( params[ 3 ], pattern, function ( pos_x, u1, pos_y, u2 ) + return string.format( replace, + units.get_x( pos_x, u1 ) - padding_width, + units.get_y( pos_y, u2 ) - padding_height + ) + end ) + assert( count == 1, "Cannot parse formspec element list[]" ) + return element( "list", { params[ 1 ], params[ 2 ], pos, unpack( params, 4 ) } ) + end + end + + -- container[,] + + local function ContainerElement( ) + return function ( name, params ) + local pos = get_pos( params, units, { 0, 0 } ) + assert( pos and #params == 1, "Cannot parse formspec element container[]" ) + return element( "container", { pos } ) + end + end + + -- background[,;] + + local function BackgroundElement( ) + local pattern = "^(-?%d+),(-?%d+)$" + local replace = "%d,%d;0,0" + + return function ( name, params ) + local dim, count = string.gsub( params[ 1 ], pattern, function ( pos_x, pos_y ) + return string.format( replace, -pos_x, -pos_y ) + end ) + assert( count == 1, "Cannot parse formspec element background[]" ) + return element( "background", { dim, params[ 2 ], "true" } ) + end + end + + -- bgimage[,;,;] + + local function BgImageElement( ) + -- original formula: vx * spacing.x - ( spacing.x - imgsize.x ) / 2 + local pos_offset_x = 0.5 * 1 / 5 + local pos_offset_y = 0.5 * 2 / 15 + + return function ( name, params ) + local pos_and_dim = get_pos_and_dim( params, units, units, { + -padding_width + pos_offset_x, + -padding_height + pos_offset_y, + 0, + 0 + } ) + assert( pos_and_dim and #params == 3, "Cannot parse formspec element bgimage[]" ) + return element( "background", { pos_and_dim, params[ 3 ], "false" } ) + end + end + + -- label[,;