From 421b47819f21160c3662c40f7da028f15b726577 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Sun, 16 Apr 2023 03:01:18 +0000 Subject: CTAN sync 202304160301 --- graphics/pstricks/contrib/luapstricks/README.md | 32 +- .../luapstricks/luapstricks-plugin-pstmarble.lua | 662 +++++++++++++++++++++ .../pstricks/contrib/luapstricks/luapstricks.lua | 67 ++- 3 files changed, 725 insertions(+), 36 deletions(-) create mode 100644 graphics/pstricks/contrib/luapstricks/luapstricks-plugin-pstmarble.lua (limited to 'graphics') diff --git a/graphics/pstricks/contrib/luapstricks/README.md b/graphics/pstricks/contrib/luapstricks/README.md index 9e2f2a5397..5ebcc04883 100644 --- a/graphics/pstricks/contrib/luapstricks/README.md +++ b/graphics/pstricks/contrib/luapstricks/README.md @@ -30,34 +30,4 @@ Also many thanks to Herbert Voß for adapting PSTricks and related packages to w ## Demos -Documentation of PSTricks packages, created by `lualatex` and `luapstricks.lua`: - -- [pst-user](https://hvoss.org/PSTexa/pst-user.pdf) Main (historical) documentation without overlay stuff (is now in pst-ovl) -- [pstricks-add](https://hvoss.org/PSTexa/pstricks-add-doc.pdf) -- [pst-3d](https://hvoss.org/PSTexa/pst-3d-doc.pdf) -- [pst-3dplot](https://hvoss.org/PSTexa/pst-3dplot-doc.pdf) -- [pst-am](https://hvoss.org/PSTexa/pst-am-doc.pdf) -- [pst-antiprism](https://hvoss.org/PSTexa/pst-antiprism-doc.pdf) -- [pst-barcode](https://hvoss.org/PSTexa/pst-barcode-doc.pdf) -- [pst-bezier](https://hvoss.org/PSTexa/pst-bezier-doc.pdf) -- [pst-cie](https://hvoss.org/PSTexa/pst-cie-doc.pdf) -- [pst-circ](https://hvoss.org/PSTexa/pst-circ-doc.pdf) -- [pst-coil](https://hvoss.org/PSTexa/pst-coil-doc.pdf) -- [pst-dart](https://hvoss.org/PSTexa/pst-dart-doc.pdf) -- [pst-eucl](https://hvoss.org/PSTexa/pst-eucl-doc.pdf) -- [pst-func](https://hvoss.org/PSTexa/pst-func-doc.pdf) -- [pst-knot](https://hvoss.org/PSTexa/pst-knot-doc.pdf) -- [pst-node](https://hvoss.org/PSTexa/pst-node-doc.pdf) -- [pst-ovl](https://hvoss.org/PSTexa/pst-ovl-doc.pdf) -- [pst-platon](https://hvoss.org/PSTexa/pst-platon-doc.pdf) -- [pst-plot](https://hvoss.org/PSTexa/pst-plot-doc.pdf) -- [pst-poly](https://hvoss.org/PSTexa/pst-poly-doc.pdf) -- [pst-solarsystem](https://hvoss.org/PSTexa/pst-solarsystem-doc.pdf) -- [pst-soroban](https://hvoss.org/PSTexa/pst-soroban-doc.pdf) -- [pst-spinner](https://hvoss.org/PSTexa/pst-spinner-doc.pdf) -- [pst-stru](https://hvoss.org/PSTexa/pst-stru-doc.pdf) -- [pst-tools](https://hvoss.org/PSTexa/pst-tools-doc.pdf) -- [pst-tree](https://hvoss.org/PSTexa/pst-tree-doc.pdf) -- [pst-turtle](https://hvoss.org/PSTexa/pst-turtle-doc.pdf) -- [pst-vehicle](https://hvoss.org/PSTexa/pst-vehicle-doc.pdf) -- [pst-venn](https://hvoss.org/PSTexa/pst-venn-doc.pdf) +Documentation of PSTricks packages, created by `lualatex` and `luapstricks.lua`, can be found at . diff --git a/graphics/pstricks/contrib/luapstricks/luapstricks-plugin-pstmarble.lua b/graphics/pstricks/contrib/luapstricks/luapstricks-plugin-pstmarble.lua new file mode 100644 index 0000000000..d1213a4586 --- /dev/null +++ b/graphics/pstricks/contrib/luapstricks/luapstricks-plugin-pstmarble.lua @@ -0,0 +1,662 @@ +---- luapstricks-plugin-pstmarble.lua +-- Copyright 2021--2023 Marcel Krüger +-- Converted from PostScript in pst-marble.pro version 1.6 +-- pst-marble.pro: Copyright 2018--2019 Aubrey Jaffer +-- +-- This work may be distributed and/or modified under the +-- conditions of the LaTeX Project Public License, either version 1.3 +-- of this license or (at your option) any later version. +-- The latest version of this license is in +-- http://www.latex-project.org/lppl.txt +-- and version 1.3 or later is part of all distributions of LaTeX +-- version 2005/12/01 or later. +-- +-- This work has the LPPL maintenance status `maintained'. +-- +-- The Current Maintainer of this work is M. Krüger +-- +-- This work consists of the files luapstricks.lua and luapstricks-plugin-pstmarble.lua + +local loader, version, plugininterface = ... +assert(loader == 'luapstricks' and version == 0) + +local push = plugininterface.push +local pop = plugininterface.pop +local pop_array = plugininterface.pop_array +local pop_num = plugininterface.pop_num +local pop_proc = plugininterface.pop_proc +local exec = plugininterface.exec + +local newtable = lua.newtable +local abs = math.abs +local exp = math.exp +local cos = math.cos +local sin = math.sin +local rad = math.rad +local max = math.max +local deg = math.deg +local atan = math.atan + +local function spread(px, py, cx, cy, rad2) + local pc2 = (px - cx)^2 + (py - cy)^2 -- (p-c)^2 + local a = (rad2 / pc2 + 1)^0.5 + return (px - cx) * a + cx, (py - cy) * a + cy +end + +local e_inv = math.exp(-1) +local function rake_deformation(px, py, dx, dy, rs, V, tU, Linv) + local a = 0 + for i = 1, #rs do + local r = rs[i] + local bx, by = dy * r, -dx * r + a = a + exp(-abs((px - bx) * dy - (py - by) * dx) * Linv) * tU + end + return px + dx * a, py + dy * a +end + +local function stir_deformation(px, py, cx, cy, rs, th, Linv, oversample) + local dx, dy = (px - cx), (py - cy) + local dist = (dx^2 + dy^2)^.5 + if dist <= 1e-6 then return px, py end + + local a = 0 + for i = 1, #rs do + local r = rs[i] + local positive = r > 0 + if not positive then r = -r end + local delta = exp(-abs(dist - r) * (Linv / r)) * th + if positive then + a = a - delta + else + a = a + delta + end + end + if oversample > 0 then + a = -a + end + a = rad(a) + local cos_a, sin_a = cos(a), sin(a) + return cos_a * dx - sin_a * dy + cx, sin_a * dx + cos_a * dy + cy +end + +local function symmod(x, m) + local x = x % m + if 2 * x >= m then + x = x - m + end + return x +end + +-- Common code to compute inverse of non-linear deformation +local function g1(mdls, a, mf, af, major, pw, freq) + local tmp = mdls / 2 + if a < 0 then tmp = -tmp end + + local tmp2 + if mf > 0 then + tmp2 = 1 - max(1 - abs(af / 180), 0)^pw + else + tmp2 = abs(af / 180)^pw + end + local g0 = tmp * tmp2 +--[[ + %% one iteration of Newton-Raphson + %% g_1=g_0-(g_0-a+(m/2)*sin(g_0*f))/(1+pi*m*f/360*cos(g_0*f)) +]] + local gf = rad(g0 * freq) + return g0 - (g0 - a + major * sin(gf)) / (1 + mf/2 * cos(gf)) +end + +local function jiggle_deformation(px, py, dx, dy, freq, ofst, trv, major, minor, mf, mdls, pw) + local a = symmod(py * dx + px * dy + ofst, mdls) + local af = a * freq + local x, y + if mf ~= 0 then + --[[ + % find the minor axis displacement from the major axis value + % where it was moved from. + ]] + local g = g1(mdls, a, mf, af, major, pw, freq) + x, y = g - a, cos(rad(g * freq)) * minor + else + local ang = rad(af) + x, y = sin(ang), -cos(ang) + -- x, y = x * major, y * minor + end + return trv[1] * x + trv[3] * y + px, trv[2] * x + trv[4] * y + py +end + +local function wriggle_deformation(px, py, cx, cy, freq, major, minor, mf, mdls, pw) + local rd = ((px - cx)^2 + (py - cy)^2)^.5 + if rd <= 1e-6 then return px, py end + + local a = symmod(rd, mdls) + local af = a * freq + + -- x, y are radial and angular displacements from cx,cy + -- The naming is used to demonstrate the similarity with jiggle. + local x, y + if mf ~= 0 then + local g = g1(mdls, a, mf, af, major, pw, freq) + x, y = g - a, cos(rad(g * freq)) * minor + else + local ang = rad(af) + x, y = sin(ang) * major, -cos(ang) * minor + end + rd = rd + x + local ang = rad(y) + atan(px - cx, py - cy) + + return sin(ang) * rd + cx, cos(ang) * rd + cy +end + +local function stylus_deformation(px, py, bx, by, ex, ey, L, tU, steps, nx, ny, step_x, step_y) + for _ = 1, steps do + local dxB, dyB = bx - px, by - py + local dxE, dyE = ex - px, ey - py + local r = (dxB^2 + dyB^2)^.5 + local denr = r / L + if 0 < denr and denr < 6 then + local s = (dxE^2 + dyE^2)^.5 + local txB = dxB * nx + dyB * ny + local txE = dxE * nx + dyE * ny + local ty = dxB * ny - dyB * nx + denr = 2*L*r * exp(denr) + local dens = 2*L*s * exp(s / L) + local ty2 = ty^2 + local inx = (L*r - ty2) * tU / denr + + (L*s - ty2) * tU / dens + local iny = txB * ty * tU / denr + + txE * ty * tU / dens + px = px + inx * nx + iny * ny + py = py + inx * ny - iny * nx + end + bx, by = ex, ey + ex, ey = ex + step_x, ey + step_y + end + return px, py +end + +-- An irrotational vortex. circ is circulation; t is time in seconds +local m4o3 = -4/3 +local function vortex_deformation(px, py, cx, cy, circ, t, nuterm) + local pc2 = (px - cx)^2 + (py - cy)^2 + if pc2 < 1e-6 then return px, py end + local a = rad((nuterm + (pc2 * t)^.75)^m4o3 * circ) + px, py = px - cx, py - cy + local cos_a, sin_a = cos(a), sin(a) + return cos_a * px - sin_a * py + cx, sin_a * px + cos_a * py + cy +end + +-- We don't actually gain much from moving this one to Lua, but it's more consistent +local function offset_deformation(px, py, dx, dy) + return px + dx, py + dy +end + +local function do_turn(px, py, cx, cy, trv) + px, py = px - cx, py - cy + return trv[1] * px + trv[3] * py + trv[5], trv[2] * px + trv[4] * py + trv[6] +end + +local function ct_handler(handler) + return function(px, py, args, count) + push(px) + push(py) + + for j = 1, count do + push(args[j]) + end + exec(handler) + py = pop_num() + px = pop_num() + return px, py + end +end + +local function ct_dispatch_fallback(fallback) + local handler = ct_handler(fallback) + return function(ct, px, py, args, count) + push{kind = 'name', value = 'ct'} + push{kind = 'name', value = ct} + exec'def' + + return handler(px, py, args, count) + end +end + +local ct_handlers = { + offset = function(px, py, args, count) + assert(count == 2) + return offset_deformation(px, py, args[1], args[2]) + end, + -- offset = ct_handler'offset-deformation', + turn = function(px, py, args, count) + assert(count == 3) + return do_turn(px, py, args[1], args[2], args[3].value) + end, + -- turn = ct_handler'do-turn', + jiggle = function(px, py, args, count) + assert(count == 10) + return jiggle_deformation(px, py, args[1], args[2], args[3], args[4], args[5].value, args[6], args[7], args[8], args[9], args[10]) + end, + -- jiggle = ct_handler'jiggle-deformation', + rake = function(px, py, args, count) + assert(count == 6) + return rake_deformation(px, py, args[1], args[2], args[3].value, args[4], args[5], args[6]) + end, + -- rake = ct_handler'rake-deformation', + vortex = function(px, py, args, count) + assert(count == 4) + exec'nuterm' + local nuterm = pop_num() + return vortex_deformation(px, py, args[1], args[2], args[3], args[4], nuterm) + end, + -- vortex = ct_handler'vortex-deformation', + stir = function(px, py, args, count) + assert(count == 5) + exec'oversample' + local oversample = pop_num() + return stir_deformation(px, py, args[1], args[2], args[3].value, args[4], args[5], oversample) + end, + -- stir = ct_handler'stir-deformation', + wriggle = function(px, py, args, count) + assert(count == 8) + return wriggle_deformation(px, py, args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]) + end, + -- wriggle = ct_handler'wriggle-deformation', + stylus = function(px, py, args, count) + assert(count == 11) + return stylus_deformation(px, py, args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11]) + end, + -- stylus = ct_handler'stylus-deformation', +} +local function ct_dispatch(fallback) + fallback = ct_dispatch_fallback(fallback) + return function(ct, px, py, args, count) + local handler = ct_handlers[ct] + if handler then + return handler(px, py, args, count) + else + return fallback(ct, px, py, args, count) + end + end +end + +local function sharpen(x) + x = x - 0.5 + if abs(x) >= 1e-8 then + x = x / abs(x)^.66 * .63 + end + return x + 0.5 +end + +local function Vmap2(v1, v2, func) + local result = newtable(#v2, 0) + for i=1, #v1 do + result[i] = func(v1[i], v2[i]) + end + return result +end + +local function paper_shading(rgb, pwr, paper) + return Vmap2(rgb, paper, function(c1, c2) + if c2 >= c1 then + local a = 1 - c1/c2 + if a >= 1e-30 then + a = a^pwr + end + return (1 - a) * c2 + else + local a = (1 - c1) / (1 - c2) + if a >= 1e-30 then + a = a^(2-pwr) + end + return 1 - a * (1 - c2) + end + end) +end + +local function actions2rgb(fallback) + local dispatch = ct_dispatch(fallback) + return function(px, py, actions, acnt, paper) + local cdx = acnt + for cdx = acnt, 1, -1 do + local action = actions[cdx] + local kind = action.kind + if kind == 'executable' then + action = action.value + kind = action.kind + end + assert(kind == 'array') + action = action.value + local count = #action + local ct = action[count].value + if ct == 'drop' then + assert(count == 8) + local cx, cy = action[1], action[2] + local rad2 = action[3] + local bgc, rgb = action[4].value, action[5].value + local sr2, gc = action[6], action[7] + + local a2 = (px - cx)^2 + (py - cy)^2 + local disc = a2 < 1e-10 and 0 or 1 - rad2 / a2 + if disc <= 0 then + if gc ~= 0 then + rgb = paper_shading(rgb, exp(a2 * sr2) * gc, paper) + end + if disc > -0.001 then + local a = sharpen((-disc)^.5) + rgb = Vmap2(rgb, bgc, function(v1, v2) return v1 * a + v2 * (1-a) end) + end + return rgb + else + local a = disc^.5 + px, py = (px - cx) * a + cx, (py - cy) * a + cy + end + else + px, py = dispatch(ct, px, py, action, count - 1) + end + end + end +end + +-- At this point, fx and fy contain the raster coordinates; +-- [ r g b ] is on top of the stack. +local function do_shadings(rgb, fx, fy, shadings, paper) + local scnt = #shadings + if scnt > 0 then + for idx = 1, scnt do + local shading = shadings[idx] + local kind = shading.kind + if kind == 'executable' then + shading = shading.value + kind = shading.kind + end + assert(kind == 'array') + shading = shading.value + local count = #shading + local ct = shading[count].value + if ct == 'jiggle-shade' then + assert(count == 9) + local dx, dy = shading[1], shading[2] + local freq, ofst = shading[3], shading[4] + local major, mf = shading[5], shading[6] + local mdls, pw = shading[7], shading[8] + + local a = symmod(fy * dx + fx * dy + ofst, mdls) + local af = a * freq + + if mf ~= 0 then + local g = g1(mdls, a, mf, af, major, pw, freq) + rgb = paper_shading(rgb, max(cos(rad(g * freq)) * mf + 1, 0), paper) + end + elseif ct == 'wriggle-shade' then + assert(count == 9) + local cx, cy = shading[1], shading[2] + local freq, ofst = shading[3], shading[4] + local major, mf = shading[5], shading[6] + local mdls, pw = shading[7], shading[8] + + local a = symmod(((fx - cx)^2 + (fy - cy)^2)^.5 + ofst, mdls) + local af = a * freq + + if mf ~= 0 then + local g = g1(mdls, a, mf, af, major, pw, freq) + rgb = paper_shading(rgb, max(cos(rad(g * freq)) * mf + 1, 0), paper) + end + else + print(string.format('unrecognized shade token %s', ct)) + end + end + end + + return rgb +end + +local function do_raster(dispatch) + local actions2rgb = actions2rgb(dispatch) + return function(lox, hix, loy, hiy, oversample, actions, acnt, shadings, paper, scl, background) + local sampleover = 1 / oversample + local width = (hix - lox) // sampleover + local height = (hiy - loy) // sampleover + local raster = newtable(width * height, 0) + + local factor = sampleover / scl + + for y = 0, height do + local fy = loy / scl + y * factor + for x = 0, width do + local fx = lox / scl + x * factor + + local rgb = actions2rgb(fx, fy, actions, acnt, paper) or background + rgb = do_shadings(rgb, fx, fy, shadings, paper) + raster[1 + y*(width+1) + x] = {kind = 'array', value = rgb} + end + end + return raster, width + 1, height + 1 + end +end + +return { + spread = function() + local rad2 = pop_num() -- rad^2 + local cy = pop_num() + local cx = pop_num() + local py = pop_num() + local px = pop_num() + px, py = spread(px, py, cx, cy, rad2) + push(px) + push(py) + end, + ['.actions2rgb'] = function() -- px py actions acnt paper fallback .composite-map exec + local _, fallback = pop_proc() + local actions2rgb = actions2rgb(fallback) + push(function() + local paper = pop_array().value + local acnt = pop_num() + local actions = pop_array().value + + local py = pop_num() + local px = pop_num() + + local rgb = actions2rgb(px, py, actions, acnt, paper) + if rgb then + push{kind = 'array', value = rgb} + push(true) + else + push(false) + end + end) + end, + ['.paper-shading'] = function() -- rgb pwr paper + local paper = pop_array().value + local pwr = pop_num() + local rgb = pop_array().value + rgb = paper_shading(rgb, pwr, paper) + push{kind = 'array', value = rgb} + end, + ['.composite-map'] = function() -- acnt idx actions fallback .composite-map exec + local _, fallback = pop_proc() + local dispatch = ct_dispatch(fallback) + push(function() + local actions = pop_array().value + local idx = pop_num() + local acnt = pop_num() + + local py = pop_num() + local px = pop_num() + + for i = idx + 1, acnt - 1 do + local action = actions[i+1] + local kind = action.kind + if kind == 'executable' then + action = action.value + kind = action.kind + end + assert(kind == 'array') + action = action.value + local count = #action + local ct = action[count].value + if ct == 'drop' then + assert(count == 8) + px, py = spread(px, py, action[1], action[2], action[3]) + else + px, py = dispatch(ct, px, py, action, count - 1) + end + end + push(px) + push(py) + end) + end, + ['stir-deformation'] = function() + local Linv = pop_num() + local th = pop_num() + local rs = pop_array().value + local cy = pop_num() + local cx = pop_num() + local py = pop_num() + local px = pop_num() + + exec'oversample' + local oversample = pop_num() + px, py = stir_deformation(px, py, cx, cy, rs, th, Linv, oversample) + push(px) + push(py) + end, + ['rake-deformation'] = function() + local Linv = pop_num() + local tU = pop_num() + local V = pop_num() + local rs = pop_array().value + local dy = pop_num() + local dx = pop_num() + local py = pop_num() + local px = pop_num() + px, py = rake_deformation(px, py, dx, dy, rs, V, tU, Linv) + push(px) + push(py) + end, + ['jiggle-deformation'] = function() + local pw = pop_num() + local mdls = pop_num() + local mf = pop_num() + local minor = pop_num() + local major = pop_num() + local trv = pop_array().value + local ofst = pop_num() + local freq = pop_num() + local dy = pop_num() + local dx = pop_num() + local py = pop_num() + local px = pop_num() + px, py = jiggle_deformation(px, py, dx, dy, freq, ofst, trv, major, minor, mf, mdls, pw) + push(px) + push(py) + end, + ['wriggle-deformation'] = function() + local pw = pop_num() + local mdls = pop_num() + local mf = pop_num() + local minor = pop_num() + local major = pop_num() + local freq = pop_num() + local cy = pop_num() + local cx = pop_num() + local py = pop_num() + local px = pop_num() + px, py = wriggle_deformation(px, py, cx, cy, freq, major, minor, mf, mdls, pw) + push(px) + push(py) + end, + ['stylus-deformation'] = function() + local step_y = pop_num() + local step_x = pop_num() + local ny = pop_num() + local nx = pop_num() + local steps = pop_num() + local tU = pop_num() + local L = pop_num() + local ey = pop_num() + local ex = pop_num() + local by = pop_num() + local bx = pop_num() + local py = pop_num() + local px = pop_num() + px, py = stylus_deformation(px, py, bx, by, ex, ey, L, tU, steps, nx, ny, step_x, step_y) + push(px) + push(py) + end, + ['.vortex-deformation'] = function() + local nuterm = pop_num() + local t = pop_num() + local circ = pop_num() + local cy = pop_num() + local cx = pop_num() + local py = pop_num() + local px = pop_num() + + px, py = vortex_deformation(px, py, cx, cy, circ, t, nuterm) + push(px) + push(py) + end, + ['offset-deformation'] = function() + local dy = pop_num() + local dx = pop_num() + local py = pop_num() + local px = pop_num() + + px, py = offset_deformation(px, py, dx, dy) + push(px) + push(py) + end, + ['do-turn'] = function() + local trv = pop_array().value + local cy = pop_num() + local cx = pop_num() + local py = pop_num() + local px = pop_num() + + px, py = do_turn(px, py, cx, cy, trv) + push(px) + push(py) + end, + ['.Minsky-circle'] = function() + local eps = pop_num() + local y = pop_num() + local x = pop_num() + x = x - eps * y + y = y + eps * x + push(x) + push(y) + end, + ['.do-shadings'] = function() + local paper = pop_array().value + local shadings = pop_array().value + local fy = pop_num() + local fx = pop_num() + local rgb = pop_array().value + rgb = do_shadings(rgb, fx, fy, shadings, paper) + push{kind = 'array', value = rgb} + end, + ['.do-raster'] = function() + local _, fallback = pop_proc() + local do_raster = do_raster(fallback) + push(function() + local background = pop_array().value + local scl = pop_num() + local paper = pop_array().value + local shadings = pop_array().value + local acnt = pop_num() + local actions = pop_array().value + local oversample = pop_num() + local hiy = pop_num() + local loy = pop_num() + local hix = pop_num() + local lox = pop_num() + + local raster, width, height = do_raster(lox, hix, loy, hiy, oversample, actions, acnt, shadings, paper, scl, background) + push{kind = 'array', value = raster} + push(width) + push(height) + end) + end, +}, 0 diff --git a/graphics/pstricks/contrib/luapstricks/luapstricks.lua b/graphics/pstricks/contrib/luapstricks/luapstricks.lua index ee88e48993..9797073d2d 100644 --- a/graphics/pstricks/contrib/luapstricks/luapstricks.lua +++ b/graphics/pstricks/contrib/luapstricks/luapstricks.lua @@ -1,5 +1,5 @@ ---- luapstricks.lua --- Copyright 2021--2022 Marcel Krüger +-- Copyright 2021--2023 Marcel Krüger -- -- This work may be distributed and/or modified under the -- conditions of the LaTeX Project Public License, either version 1.3 @@ -13,13 +13,13 @@ -- -- The Current Maintainer of this work is M. Krüger -- --- This work consists of the file luapstricks.lua. +-- This work consists of the files luapstricks.lua and luapstricks-plugin-pstmarble.lua if luatexbase then luatexbase.provides_module { name = 'luapstricks', - version = 'v0.8', - date = '2023-02-17', + version = 'v0.9', + date = '2023-04-14', description = 'PSTricks backend for LuaLaTeX', } end @@ -917,6 +917,17 @@ local function num_to_base(num, base, ...) return num_to_base(remaining, base, digit, ...) end +local plugin_interface = { + push = push, + pop = pop, + pop_num = pop_num, + pop_dict = pop_dict, + pop_array = pop_array, + pop_key = pop_key, + pop_proc = pop_proc, + exec = nil, -- execute_tok, -- filled in later +} + local systemdict local function generic_show(str, ax, ay) local state = graphics_stack[#graphics_stack] @@ -3846,7 +3857,52 @@ systemdict = {kind = 'dict', value = { {kind = 'name', value = 'yacute'}, {kind = 'name', value = 'thorn'}, {kind = 'name', value = 'ydieresis'}, - }} + }}, + + -- In internal interface to allow package specific commands to be defined in separate file. + -- This does not provide a stable interface for external extensions + ['.loadplugin'] = function() + local name = pop_key() + local found = kpse.find_file(string.format('luapstricks-plugin-%s', name), 'lua') + if not found then + return push(false) + end + local loader = assert(loadfile(found)) + local plugin, version = loader('luapstricks', 0, plugin_interface) + push{kind = 'dict', value = plugin} + push(version) + push(true) + end, + + ['.build-image'] = function() + local y = pop_num() + local x = pop_num() + local image = pop_array().value + for i = 1, x*y do + local rgb = image[i].value + image[i] = string.pack('BBB', (rgb[1] * 255 + .5) // 1, (rgb[2] * 255 + .5) // 1, (rgb[3] * 255 + .5) // 1) + end + local i = img.scan { + stream = table.concat(image), + attr = string.format("/Type /XObject /Subtype /Image /Width %i /Height %i /BitsPerComponent 8 /ColorSpace /DeviceRGB", x, y), + notype = true, + nobbox = true, + bbox = {0, 0, 65781.76, 65781.76} + } + push(function() + flush_delayed() + local state = graphics_stack[#graphics_stack] + register_point(state, 0, 0) + register_point(state, 1, 1) + vf.push() + local n = node.new'hlist' + n.dir = 'TLT' + n.head = img.node(i) + vf.node(node.direct.todirect(n)) + node.free(n) + vf.pop() + end) + end, }} systemdict.value.systemdict = systemdict dictionary_stack = {systemdict, globaldict, userdict, userdict.value.TeXDict} @@ -4040,6 +4096,7 @@ function execute_tok(tok, suppress_proc) return push(tok) end end +plugin_interface.exec = execute_tok function execute_ps(tokens) for i=1, #tokens do -- cgit v1.2.3