diff options
| author | Philipp Gesang <philipp.gesang@alumni.uni-heidelberg.de> | 2012-10-19 21:49:46 +0200 | 
|---|---|---|
| committer | Philipp Gesang <philipp.gesang@alumni.uni-heidelberg.de> | 2012-10-19 21:49:46 +0200 | 
| commit | 26624ddc9f0ffa61e269658de95c91e9a77c08a4 (patch) | |
| tree | 71d2b35ea9d7fb5e6159d0ea5eb05a6e6a3f0692 | |
| parent | 5b3ff130cd5e4a3c29f468b0ac3f9b675096c7da (diff) | |
| download | lualibs-26624ddc9f0ffa61e269658de95c91e9a77c08a4.tar.gz | |
update util-dim
| -rw-r--r-- | lualibs-util-dim.lua | 211 | 
1 files changed, 113 insertions, 98 deletions
diff --git a/lualibs-util-dim.lua b/lualibs-util-dim.lua index da5ab14..d77bd49 100644 --- a/lualibs-util-dim.lua +++ b/lualibs-util-dim.lua @@ -16,14 +16,20 @@ table.</p>  --ldx]]--  local format, match, gsub, type, setmetatable = string.format, string.match, string.gsub, type, setmetatable -local P, S, R, Cc, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.Cc, lpeg.match +local P, S, R, Cc, C, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.Cc, lpeg.C, lpeg.match + +local allocate          = utilities.storage.allocate +local setmetatableindex = table.setmetatableindex + +--this might become another namespace  number = number or { } +local number = number  number.tonumberf = function(n) return match(format("%.20f",n),"(.-0?)0*$") end -- one zero too much but alas  number.tonumberg = function(n) return       format("%.20g",n)              end -local dimenfactors = { +local dimenfactors = allocate {      ["pt"] =             1/65536,      ["in"] = (  100/ 7227)/65536,      ["cm"] = (  254/ 7227)/65536, @@ -80,14 +86,17 @@ local dimenfactors = {  format (string) is implemented using this table.</p>  --ldx]]-- --- was: - -local function todimen(n,unit,fmt) +local function numbertodimen(n,unit,fmt)      if type(n) == 'string' then          return n      else          unit = unit or 'pt' -        return format(fmt or "%s%s",n*dimenfactors[unit],unit) +        if not fmt then +            fmt = "%s%s" +        elseif fmt == true then +            fmt = "%0.5f%s" +        end +        return format(fmt,n*dimenfactors[unit],unit)       -- if fmt then       --     return format(fmt,n*dimenfactors[unit],unit)       -- else @@ -101,21 +110,21 @@ end  --ldx]]--  number.maxdimen     = 1073741823 -number.todimen      = todimen +number.todimen      = numbertodimen  number.dimenfactors = dimenfactors -function number.topoints      (n) return todimen(n,"pt") end -function number.toinches      (n) return todimen(n,"in") end -function number.tocentimeters (n) return todimen(n,"cm") end -function number.tomillimeters (n) return todimen(n,"mm") end -function number.toscaledpoints(n) return todimen(n,"sp") end -function number.toscaledpoints(n) return      n .. "sp"  end -function number.tobasepoints  (n) return todimen(n,"bp") end -function number.topicas       (n) return todimen(n "pc") end -function number.todidots      (n) return todimen(n,"dd") end -function number.tociceros     (n) return todimen(n,"cc") end -function number.tonewdidots   (n) return todimen(n,"nd") end -function number.tonewciceros  (n) return todimen(n,"nc") end +function number.topoints      (n,fmt) return numbertodimen(n,"pt",fmt) end +function number.toinches      (n,fmt) return numbertodimen(n,"in",fmt) end +function number.tocentimeters (n,fmt) return numbertodimen(n,"cm",fmt) end +function number.tomillimeters (n,fmt) return numbertodimen(n,"mm",fmt) end +function number.toscaledpoints(n,fmt) return numbertodimen(n,"sp",fmt) end +function number.toscaledpoints(n)     return            n .. "sp"      end +function number.tobasepoints  (n,fmt) return numbertodimen(n,"bp",fmt) end +function number.topicas       (n,fmt) return numbertodimen(n "pc",fmt) end +function number.todidots      (n,fmt) return numbertodimen(n,"dd",fmt) end +function number.tociceros     (n,fmt) return numbertodimen(n,"cc",fmt) end +function number.tonewdidots   (n,fmt) return numbertodimen(n,"nd",fmt) end +function number.tonewciceros  (n,fmt) return numbertodimen(n,"nc",fmt) end  --[[ldx--  <p>More interesting it to implement a (sort of) dimen datatype, one @@ -132,27 +141,40 @@ local dimenpair = amount/tonumber * (unit^1/dimenfactors + Cc(1)) -- tonumber is  lpeg.patterns.dimenpair = dimenpair +local splitter = amount/tonumber * C(unit^1) + +function number.splitdimen(str) +    return lpegmatch(splitter,str) +end +  --[[ldx--  <p>We use a metatable to intercept errors. When no key is found in  the table with factors, the metatable will be consulted for an  alternative index function.</p>  --ldx]]-- -local mt = { }  setmetatable(dimenfactors,mt) - -mt.__index = function(t,s) +setmetatableindex(dimenfactors, function(t,s)   -- error("wrong dimension: " .. (s or "?")) -- better a message      return false -end +end) -function string:todimen() -    if type(self) == "number" then -        return self -    else -        local value, unit = lpegmatch(dimenpair,self) -        return value/unit -    end -end +--[[ldx-- +<p>We redefine the following function later on, so we comment it +here (which saves us bytecodes.</p> +--ldx]]-- + +-- function string.todimen(str) +--     if type(str) == "number" then +--         return str +--     else +--         local value, unit = lpegmatch(dimenpair,str) +--         return value/unit +--     end +-- end +-- +-- local stringtodimen = string.todimen + +local stringtodimen -- assigned later (commenting saves bytecode)  local amount = S("+-")^0 * R("09")^0 * S(".,")^0 * R("09")^0  local unit   = P("pt") + P("cm") + P("mm") + P("sp") + P("bp") + P("in")  + @@ -160,7 +182,7 @@ local unit   = P("pt") + P("cm") + P("mm") + P("sp") + P("bp") + P("in")  +  local validdimen = amount * unit -lpeg.patterns.validdimen = pattern +lpeg.patterns.validdimen = validdimen  --[[ldx--  <p>This converter accepts calls like:</p> @@ -174,12 +196,6 @@ string.todimen("10pt")  string.todimen("10.0pt")  </typing> -<p>And of course the often more efficient:</p> - -<typing> -somestring:todimen("12.3cm") -</typing> -  <p>With this in place, we can now implement a proper datatype for dimensions, one  that permits us to do this:</p> @@ -197,28 +213,28 @@ local dimensions = { }  <p>The main (and globally) visible representation of a dimen is defined next: it is  a one-element table. The unit that is returned from the match is normally a number  (one of the previously defined factors) but we also accept functions. Later we will -see why.</p> +see why. This function is redefined later.</p>  --ldx]]-- -function dimen(a) -    if a then -        local ta= type(a) -        if ta == "string" then -            local value, unit = lpegmatch(pattern,a) -            if type(unit) == "function" then -                k = value/unit() -            else -                k = value/unit -            end -            a = k -        elseif ta == "table" then -            a = a[1] -        end -        return setmetatable({ a }, dimensions) -    else -        return setmetatable({ 0 }, dimensions) -    end -end +-- function dimen(a) +--     if a then +--         local ta= type(a) +--         if ta == "string" then +--             local value, unit = lpegmatch(pattern,a) +--             if type(unit) == "function" then +--                 k = value/unit() +--             else +--                 k = value/unit +--             end +--             a = k +--         elseif ta == "table" then +--             a = a[1] +--         end +--         return setmetatable({ a }, dimensions) +--     else +--         return setmetatable({ 0 }, dimensions) +--     end +-- end  --[[ldx--  <p>This function return a small hash with a metatable attached. It is @@ -228,35 +244,35 @@ shared some of the code but for reasons of speed we don't.</p>  function dimensions.__add(a, b)      local ta, tb = type(a), type(b) -    if ta == "string" then a = a:todimen() elseif ta == "table" then a = a[1] end -    if tb == "string" then b = b:todimen() elseif tb == "table" then b = b[1] end +    if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end +    if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end      return setmetatable({ a + b }, dimensions)  end  function dimensions.__sub(a, b)      local ta, tb = type(a), type(b) -    if ta == "string" then a = a:todimen() elseif ta == "table" then a = a[1] end -    if tb == "string" then b = b:todimen() elseif tb == "table" then b = b[1] end +    if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end +    if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end      return setmetatable({ a - b }, dimensions)  end  function dimensions.__mul(a, b)      local ta, tb = type(a), type(b) -    if ta == "string" then a = a:todimen() elseif ta == "table" then a = a[1] end -    if tb == "string" then b = b:todimen() elseif tb == "table" then b = b[1] end +    if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end +    if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end      return setmetatable({ a * b }, dimensions)  end  function dimensions.__div(a, b)      local ta, tb = type(a), type(b) -    if ta == "string" then a = a:todimen() elseif ta == "table" then a = a[1] end -    if tb == "string" then b = b:todimen() elseif tb == "table" then b = b[1] end +    if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end +    if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end      return setmetatable({ a / b }, dimensions)  end  function dimensions.__unm(a)      local ta = type(a) -    if ta == "string" then a = a:todimen() elseif ta == "table" then a = a[1] end +    if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end      return setmetatable({ - a }, dimensions)  end @@ -321,23 +337,9 @@ is loaded, the relevant tables that hold the functions needed may not  yet be available.</p>  --ldx]]-- -function dimensions.texify()  -- todo: % -    local fti, fc = fonts and fonts.ids and fonts.ids, font and font.current -    if fti and fc then -        dimenfactors["ex"] = function() return fti[fc()].ex_height end -        dimenfactors["em"] = function() return fti[fc()].quad      end -    else -        dimenfactors["ex"] = 1/65536* 4 --  4pt -        dimenfactors["em"] = 1/65536*10 -- 10pt -    end -end - ---[[ldx-- -<p>In order to set the defaults we call this function now. At some point -the macro package needs to make sure the function is called again.</p> ---ldx]]-- - -dimensions.texify() +   dimenfactors["ex"] =  4 * 1/65536 --   4pt +   dimenfactors["em"] = 10 * 1/65536 --  10pt +-- dimenfactors["%"]  =  4 * 1/65536 -- 400pt/100  --[[ldx--  <p>The previous code is rather efficient (also thanks to <l n='lpeg'/>) but we @@ -389,27 +391,40 @@ function dimen(a)      end  end -function string:todimen() -    if type(self) == "number" then -        return self +function string.todimen(str) -- maybe use tex.sp when available +    if type(str) == "number" then +        return str      else -        local k = known[self] +        local k = known[str]          if not k then -            local value, unit = lpegmatch(dimenpair,self) +            local value, unit = lpegmatch(dimenpair,str)              if value and unit then -                k = value/unit +                k = value/unit -- to be considered: round              else                  k = 0              end -            -- print(self,value,unit) -            known[self] = k +            -- print(str,value,unit) +            known[str] = k          end          return k      end  end +--~ local known = { } + +--~ function string.todimen(str) -- maybe use tex.sp +--~     local k = known[str] +--~     if not k then +--~         k = tex.sp(str) +--~         known[str] = k +--~     end +--~     return k +--~ end + +stringtodimen = string.todimen -- local variable defined earlier +  function number.toscaled(d) -    return format("0.5f",d/2^16) +    return format("%0.5f",d/2^16)  end  --[[ldx-- @@ -421,12 +436,12 @@ probably use a hash instead of a one-element table.</p>  <p>Goodie:s</p>  --ldx]]-- -function number.percent(n) -- will be cleaned up once luatex 0.30 is out -    local hsize = tex.hsize -    if type(hsize) == "string" then -        hsize = hsize:todimen() +function number.percent(n,d) -- will be cleaned up once luatex 0.30 is out +    d = d or tex.hsize +    if type(d) == "string" then +        d = stringtodimen(d)      end -    return (n/100) * hsize +    return (n/100) * d  end  number["%"] = number.percent  | 
