Skip to content

Commit

Permalink
feat: Add untested parts of the roughjs port
Browse files Browse the repository at this point in the history
These are not needed (yet) in our framebox use, but let's have
them in an "untested" folder. This will help to get started if we
later decide to extract the port as a separate Lua rocks.
  • Loading branch information
Omikhleia authored and Didier Willis committed Jan 14, 2024
1 parent 15fcfd8 commit ae14353
Show file tree
Hide file tree
Showing 9 changed files with 701 additions and 16 deletions.
21 changes: 20 additions & 1 deletion ptable.sile-dev-1.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,27 @@ build = {
["sile.packages.parbox"] = "packages/parbox/init.lua",
["sile.packages.ptable"] = "packages/ptable/init.lua",
["sile.packages.framebox"] = "packages/framebox/init.lua",
["sile.packages.framebox.graphics.prng"] = "packages/framebox/graphics/prng.lua",
["sile.packages.framebox.graphics.renderer"] = "packages/framebox/graphics/renderer.lua",
["sile.packages.framebox.graphics.rough"] = "packages/framebox/graphics/rough.lua",
["prng-prigarin"] = "prng-prigarin/init.lua",
["rough-lua.rough.jsshims"] = "rough-lua/rough/jsshims.lua",
["rough-lua.rough.generator"] = "rough-lua/rough/generator.lua",
["rough-lua.rough.renderer"] = "rough-lua/rough/renderer.lua",
["rough-lua.rough.fillers.hachure-filler"] = "rough-lua/rough/fillers/hachure-filler.lua",
["rough-lua.rough.fillers.zigzag-filler"] = "rough-lua/rough/fillers/zigzag-filler.lua",
["rough-lua.rough.fillers.zigzag-line-filler"] = "rough-lua/rough/fillers/zigzag-line-filler.lua",
["rough-lua.rough.fillers.dot-filler"] = "rough-lua/rough/fillers/dot-filler.lua",
["rough-lua.rough.fillers.dashed-filler"] = "rough-lua/rough/fillers/dashed-filler.lua",
["rough-lua.rough.fillers.scan-line-hachure"] = "rough-lua/rough/fillers/scan-line-hachure.lua",
["rough-lua.rough.fillers.hatch-filler"] = "rough-lua/rough/fillers/hatch-filler.lua",
["rough-lua.rough.fillers.hachure-fill"] = "rough-lua/rough/fillers/hachure-fill.lua",
["rough-lua.rough.fillers.filler"] = "rough-lua/rough/fillers/filler.lua",
["rough-lua.rough.geometry"] = "rough-lua/rough/geometry.lua",
["rough-lua.untested.path-data-parser"] = "rough-lua/untested/path-data-parser/init.lua",
["rough-lua.untested.path-data-parser.parser"] = "rough-lua/untested/path-data-parser/parser.lua",
["rough-lua.untested.path-data-parser.normalize"] = "rough-lua/untested/path-data-parser/normalize.lua",
["rough-lua.untested.path-data-parser.absolutize"] = "rough-lua/untested/path-data-parser/absolutize.lua",
["rough-lua.untested.points-on-curve"] = "rough-lua/untested/points-on-curve/init.lua",
["rough-lua.untested.points-on-curve.curve-to-bezier"] = "rough-lua/untested/points-on-curve/curve-to-bezier.lua",
}
}
11 changes: 3 additions & 8 deletions rough-lua/rough/generator.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,9 @@ local line, rectangle,
renderer.arc, renderer.curve, renderer.linearPath,
renderer.svgPath,
renderer.patternFillArc, renderer.patternFillPolygons, renderer.solidFillPolygon
-- PORTING NOTE:
-- I ported the module but haven't tested it for now
-- local curveToBezier = require("rough-lua.points-on-curve.curve-to-bezier").curveToBezier
-- local pointsOnPath = require("rough-lua.points-on-curve").pointsOnPath
-- local pointsOnBezierCurves = require("rough-lua.points-on-curve").pointsOnBezierCurves
local pointsOnPath = function () error("Not implemented") end
local curveToBezier = function () error("Not implemented") end
local pointsOnBezierCurves = function () error("Not implemented") end
local curveToBezier = require("rough-lua.untested.points-on-curve.curve-to-bezier").curveToBezier
local pointsOnPath = require("rough-lua.untested.points-on-curve").pointsOnPath
local pointsOnBezierCurves = require("rough-lua.untested.points-on-curve").pointsOnBezierCurves


local RoughGenerator = pl.class({
Expand Down
10 changes: 3 additions & 7 deletions rough-lua/rough/renderer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@ local jsshims = require("rough-lua.rough.jsshims")
local array_concat = jsshims.array_concat
local PRNG = require("prng-prigarin")

-- PORTING NOTE:
-- I ported path-data-parser but haven't tested it for now
-- local pathDataParser = require("rough-lua.path-data-parser")
-- local parsePath, normalize, absolutize = pathDataParser.parsePath, pathDataParser.normalize, pathDataParser.absolutize
local normalize = function () error("Not yet implemented") end
local absolutize = function () error("Not yet implemented") end
local parsePath = function () error("Not yet implemented") end
local pathDataParser = require("rough-lua.untested.path-data-parser")
local parsePath, normalize, absolutize
= pathDataParser.parsePath, pathDataParser.normalize, pathDataParser.absolutize

local getFiller = require("rough-lua.rough.fillers.filler").getFiller

Expand Down
95 changes: 95 additions & 0 deletions rough-lua/untested/path-data-parser/absolutize.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
--
-- License: MIT
-- Copyright (c) 2023, Didier Willis
--
-- This is a straightforward port of the path-data-parser JavaScript library.
-- (https://github.com/pshihn/path-data-parser/)
-- License: MIT
-- Copyright (c) 2019 Preet Shihn
--
local function absolutize(segments)
local cx, cy = 0, 0
local subx, suby = 0, 0
local out = {}
for _, segment in ipairs(segments) do
local key, data = segment.key, segment.data
if key == 'M' then
out[#out + 1] = { key = 'M', data = pl.tablex.copy(data) }
cx, cy = data[1], data[2]
subx, suby = data[1], data[2]
elseif key == 'm' then
cx = cx + data[1]
cy = cy + data[2]
out[#out + 1] = { key = 'M', data = { cx, cy } }
subx, suby = cx, cy
elseif key == 'L' then
out[#out + 1] = { key = 'L', data = pl.tablex.copy(data) }
cx, cy = data[1], data[2]
elseif key == 'l' then
cx = cx + data[1]
cy = cy + data[2]
out[#out + 1] = { key = 'L', data = { cx, cy } }
elseif key == 'C' then
out[#out + 1] = { key = 'C', data = pl.tablex.copy(data) }
cx, cy = data[5], data[6]
elseif key == 'c' then
local newdata = pl.tablex.map(data, function (d, i)
return (i % 2) == 0 and (d + cx) or (d + cy)
end)
out[#out + 1] = { key = 'C', data = newdata }
cx, cy = newdata[5], newdata[6]
elseif key == 'Q' then
out[#out + 1] = { key = 'Q', data = pl.tablex.copy(data) }
cx, cy = data[2], data[3]
elseif key == 'q' then
local newdata = pl.tablex.map(data, function (d, i)
return (i % 2) == 0 and (d + cx) or (d + cy)
end)
out[#out + 1] = { key = 'Q', data = newdata }
cx, cy = newdata[2], newdata[3]
elseif key == 'A' then
out[#out + 1] = { key = 'A', data = pl.tablex.copy(data) }
cx, cy = data[5], data[6]
elseif key == 'a' then
cx = cx + data[5]
cy = cy + data[6]
out[#out + 1] = { key = 'A', data = { data[1], data[2], data[3], data[4], data[5], cx, cy } }
elseif key == 'H' then
out[#out + 1] = { key = 'H', data = pl.tablex.copy(data) }
cx = data[1]
elseif key == 'h' then
cx = cx + data[1]
out[#out + 1] = { key = 'H', data = { cx } }
elseif key == 'V' then
out[#out + 1] = { key = 'V', data = pl.tablex.copy(data) }
cy = data[1]
elseif key == 'v' then
cy = cy + data[1]
out[#out + 1] = { key = 'V', data = { cy } }
elseif key == 'S' then
out[#out + 1] = { key = 'S', data = pl.tablex.copy(data) }
cx, cy = data[2], data[3]
elseif key == 's' then
local newdata = pl.tablex.map(data, function (d, i)
return (i % 2) == 0 and (d + cx) or (d + cy)
end)
out[#out + 1] = { key = 'S', data = newdata }
cx, cy = newdata[2], newdata[3]
elseif key == 'T' then
out[#out + 1] = { key = 'T', data = pl.tablex.copy(data) }
cx, cy = data[1], data[2]
elseif key == 't' then
cx = cx + data[1]
cy = cy + data[2]
out[#out + 1] = { key = 'T', data = { cx, cy } }
elseif key == 'Z' or key == 'z' then
out[#out + 1] = { key = 'Z', data = {} }
cx, cy = subx, suby
end
end
return out
end

return {
absolutize = absolutize,
}
20 changes: 20 additions & 0 deletions rough-lua/untested/path-data-parser/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--
-- License: MIT
-- Copyright (c) 2023, Didier Willis
--
-- This is a straightforward port of the path-data-parser JavaScript library.
-- (https://github.com/pshihn/path-data-parser/)
-- License: MIT
-- Copyright (c) 2019 Preet Shihn
--
local normalize = require("rough-lua.untested.path-data-parser.normalize").normalize
local absolutize = require("rough-lua.untested.path-data-parser.absolutize").absolutize
local parsePath = require("rough-lua.untested.path-data-parser.parser").parsePath
local serialize = require("rough-lua.untested.path-data-parser.parser").serialize

return {
parsePath = parsePath,
serialize = serialize,
absolutize = absolutize,
normalize = normalize,
}
204 changes: 204 additions & 0 deletions rough-lua/untested/path-data-parser/normalize.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
-- License: MIT
-- Copyright (c) 2023, Didier Willis
--
-- This is a straightforward port of the path-data-parser JavaScript library.
-- (https://github.com/pshihn/path-data-parser/)
-- License: MIT
-- Copyright (c) 2019 Preet Shihn
--
local jsshims = require("rough-lua.rough.jsshims")
local array_concat = jsshims.array_concat

local function degToRad (degrees)
return (math.pi * degrees) / 180
end

local function rotate (x, y, angleRad)
local X = x * math.cos(angleRad) - y * math.sin(angleRad)
local Y = x * math.sin(angleRad) + y * math.cos(angleRad)
return { X, Y }
end

local function arcToCubicCurves (x1, y1, x2, y2, r1, r2, angle, largeArcFlag, sweepFlag, recursive)
local angleRad = degToRad(angle)
local params = {}
local f1, f2, cx, cy
if recursive then
f1, f2, cx, cy = recursive[1], recursive[2], recursive[3], recursive[4]
else
x1, y1 = rotate(x1, y1, -angleRad)
x2, y2 = rotate(x2, y2, -angleRad)
local x = (x1 - x2) / 2
local y = (y1 - y2) / 2
local h = (x * x) / (r1 * r1) + (y * y) / (r2 * r2)
if h > 1 then
h = math.sqrt(h)
r1 = h * r1
r2 = h * r2
end
local sign = (largeArcFlag == sweepFlag) and -1 or 1
local r1Pow = r1 * r1
local r2Pow = r2 * r2
local left = r1Pow * r2Pow - r1Pow * y * y - r2Pow * x * x
local right = r1Pow * y * y + r2Pow * x * x
local k = sign * math.sqrt(math.abs(left / right))
cx = k * r1 * y / r2 + (x1 + x2) / 2
cy = k * -r2 * x / r1 + (y1 + y2) / 2
f1 = math.asin(((y1 - cy) / r2))
f2 = math.asin(((y2 - cy) / r2))
if x1 < cx then
f1 = math.pi - f1
end
if x2 < cx then
f2 = math.pi - f2
end
if f1 < 0 then
f1 = math.pi * 2 + f1
end
if f2 < 0 then
f2 = math.pi * 2 + f2
end
if sweepFlag and f1 > f2 then
f1 = f1 - math.pi * 2
end
if not sweepFlag and f2 > f1 then
f2 = f2 - math.pi * 2
end
end
local df = f2 - f1
if math.abs(df) > (math.pi * 120 / 180) then
local f2old = f2
local x2old = x2
local y2old = y2
if sweepFlag and f2 > f1 then
f2 = f1 + (math.pi * 120 / 180) * (1)
else
f2 = f1 + (math.pi * 120 / 180) * (-1)
end
x2 = cx + r1 * math.cos(f2)
y2 = cy + r2 * math.sin(f2)
params = arcToCubicCurves(x2, y2, x2old, y2old, r1, r2, angle, 0, sweepFlag, { f2, f2old, cx, cy })
end
df = f2 - f1
local c1 = math.cos(f1)
local s1 = math.sin(f1)
local c2 = math.cos(f2)
local s2 = math.sin(f2)
local t = math.tan(df / 4)
local hx = 4 / 3 * r1 * t
local hy = 4 / 3 * r2 * t
local m1 = { x1, y1 }
local m2 = { x1 + hx * s1, y1 - hy * c1 }
local m3 = { x2 + hx * s2, y2 - hy * c2 }
local m4 = { x2, y2 }
m2[1] = 2 * m1[1] - m2[1]
m2[2] = 2 * m1[2] - m2[2]
if recursive then
return array_concat({ m2, m3, m4 }, params)
else
params = array_concat({ m2, m3, m4 }, params)
local curves = {}
for i = 1, #params, 3 do
local ro1 = rotate(params[i][1], params[i][2], angleRad)
local ro2 = rotate(params[i + 1][1], params[i + 1][2], angleRad)
local ro3 = rotate(params[i + 2][1], params[i + 2][2], angleRad)
curves[#curves + 1] = { ro1[1], ro1[2], ro2[1], ro2[2], ro3[1], ro3[2] }
end
return curves
end
end

local function normalize(segments)
local out = {}
local lastType = ''
local cx, cy = 0, 0
local subx, suby = 0, 0
local lcx, lcy = 0, 0
for _, segment in ipairs(segments) do
local key, data = segment.key, segment.data
if key == 'M' then
out[#out + 1] = { key = 'M', data = pl.tablex.copy(data) }
cx, cy = data[1], data[2]
subx, suby = data[1], data[2]
elseif key == 'C' then
out[#out + 1] = { key = 'C', data = pl.tablex.copy(data) }
cx, cy = data[5], data[6]
lcx, lcy = data[3], data[4]
elseif key == 'L' then
out[#out + 1] = { key = 'L', data = pl.tablex.copy(data) }
cx, cy = data[1], data[2]
elseif key == 'H' then
cx = data[1]
out[#out + 1] = { key = 'L', data = { cx, cy } }
elseif key == 'V' then
cy = data[1]
out[#out + 1] = { key = 'L', data = { cx, cy } }
elseif key == 'S' then
local cx1, cy1
if lastType == 'C' or lastType == 'S' then
cx1 = cx + (cx - lcx)
cy1 = cy + (cy - lcy)
else
cx1 = cx
cy1 = cy
end
out[#out + 1] = { key = 'C', data = { cx1, cy1, pl.tablex.copy(data) } }
lcx, lcy = data[1], data[2]
cx, cy = data[3], data[4]
elseif key == 'T' then
local x, y = data[1], data[2]
local x1, y1
if lastType == 'Q' or lastType == 'T' then
x1 = cx + (cx - lcx)
y1 = cy + (cy - lcy)
else
x1 = cx
y1 = cy
end
local cx1 = cx + 2 * (x1 - cx) / 3
local cy1 = cy + 2 * (y1 - cy) / 3
local cx2 = x + 2 * (x1 - x) / 3
local cy2 = y + 2 * (y1 - y) / 3
out[#out + 1] = { key = 'C', data = { cx1, cy1, cx2, cy2, x, y } }
lcx, lcy = x1, y1
cx, cy = x, y
elseif key == 'Q' then
local x1, y1, x, y = data[1], data[2], data[3], data[4]
local cx1 = cx + 2 * (x1 - cx) / 3
local cy1 = cy + 2 * (y1 - cy) / 3
local cx2 = x + 2 * (x1 - x) / 3
local cy2 = y + 2 * (y1 - y) / 3
out[#out + 1] = { key = 'C', data = { cx1, cy1, cx2, cy2, x, y } }
lcx, lcy = x1, y1
cx, cy = x, y
elseif key == 'A' then
local r1, r2 = math.abs(data[1]), math.abs(data[2])
local angle = data[3]
local largeArcFlag = data[4]
local sweepFlag = data[5]
local x, y = data[6], data[7]
if r1 == 0 or r2 == 0 then
out[#out + 1] = { key = 'C', data = { cx, cy, x, y, x, y } }
cx, cy = x, y
else
if cx ~= x or cy ~= y then
local curves = arcToCubicCurves(cx, cy, x, y, r1, r2, angle, largeArcFlag, sweepFlag)
for _, curve in ipairs(curves) do
out[#out + 1] = { key = 'C', data = curve }
end
cx, cy = x, y
end
end
elseif key == 'Z' then
out[#out + 1] = { key = 'Z', data = {} }
cx, cy = subx, suby
end
lastType = key
end
return out
end

return {
normalize = normalize,
arcToCubicCurves = arcToCubicCurves,
}
Loading

0 comments on commit ae14353

Please sign in to comment.