summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2024-12-09 21:18:16 +0000
committerKarl Berry <karl@freefriends.org>2024-12-09 21:18:16 +0000
commitbd01b8f1508b38fabf50da4ad8fbac6497697ba5 (patch)
tree515068e80d87595ecb606d7dbb3d4003b00407ed
parente4c71587aa7865ccd8d4c83a4daf3d1b04c6da6c (diff)
luatikz (9dec24)
git-svn-id: svn://tug.org/texlive/trunk@73087 c570f23f-e606-0410-a88d-b1316a301751
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/README.txt9
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/bezier2.lua11
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/bezier3a.lua12
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/bezier3b.lua32
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/blinea.lua23
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/circle.lua12
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/curvea.lua14
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/curveb.lua16
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/ellipse.lua14
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/ellipseArc.lua18
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/immute1.lua4
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/immute2.lua6
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/intro.lua8
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/linea.lua7
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/lineb.lua9
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/linec.lua7
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/lined.lua15
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/nodea.lua12
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/nodeb.lua18
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/plota.lua14
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/polylinea.lua11
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/radon1.lua119
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/recta.lua21
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/shadea.lua9
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/sierpinski.lua41
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/spiral.lua16
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/stylea.lua19
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/example/superEllipse.lua22
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/luatikz.pdfbin0 -> 112753 bytes
-rw-r--r--Master/texmf-dist/doc/latex/luatikz/luatikz.tex204
-rw-r--r--Master/texmf-dist/tex/latex/luatikz/luatikz.lua1999
-rw-r--r--Master/texmf-dist/tex/latex/luatikz/luatikz.sty5
-rwxr-xr-xMaster/tlpkg/bin/tlpkg-ctan-check2
-rwxr-xr-xMaster/tlpkg/libexec/ctan2tds1
-rw-r--r--Master/tlpkg/tlpsrc/collection-luatex.tlpsrc1
-rw-r--r--Master/tlpkg/tlpsrc/luatikz.tlpsrc0
36 files changed, 2730 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/latex/luatikz/README.txt b/Master/texmf-dist/doc/latex/luatikz/README.txt
new file mode 100644
index 00000000000..14930dc4d36
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/README.txt
@@ -0,0 +1,9 @@
+-----------------
+Luatikz is a graphics library to draw tikz graphics
+using the Lua programming language.
+-----------------
+
+License:
+
+LaTeX Project Public License 1.3c
+MIT License (expat)
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/bezier2.lua b/Master/texmf-dist/doc/latex/luatikz/example/bezier2.lua
new file mode 100644
index 00000000000..55f7766ec06
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/bezier2.lua
@@ -0,0 +1,11 @@
+tikz.within( '*' )
+
+local bz =
+ bezier2{
+ p1 = p{ 0, 0 },
+ pc = p{ 2, 2 },
+ p2 = p{ 4, 0 },
+ }
+
+draw{ line_width=1, bz }
+bz.drawHelpers( 'p' )
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/bezier3a.lua b/Master/texmf-dist/doc/latex/luatikz/example/bezier3a.lua
new file mode 100644
index 00000000000..5c91a4238f8
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/bezier3a.lua
@@ -0,0 +1,12 @@
+tikz.within( '*' )
+
+local bz =
+ bezier3{
+ p1 = p{ 0, 0 },
+ pc1 = p{ 1, 2 },
+ pc2 = p{ 3, 3 },
+ p2 = p{ 4, 0 },
+ }
+
+draw{ line_width=1, bz }
+bz.drawHelpers( 'p' )
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/bezier3b.lua b/Master/texmf-dist/doc/latex/luatikz/example/bezier3b.lua
new file mode 100644
index 00000000000..54a2a982ce5
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/bezier3b.lua
@@ -0,0 +1,32 @@
+tikz.within( '*' )
+
+local bz =
+ bezier3{
+ p1 = p{ 0, 0 },
+ pc1 = p{ 1, 2 },
+ pc2 = p{ 3, 3 },
+ p2 = p{ 4, 0 },
+ }
+
+draw{ line_width = 1, bz }
+
+for t = 0, 1, 0.1
+do
+ local pt = bz.pt( t )
+ draw{
+ fill = black,
+ circle{
+ at = pt,
+ radius = 0.05,
+ }
+ }
+
+ local phit = bz.phit( t ) + math.pi / 2
+ draw{
+ line{
+ p1 = pt,
+ phi = phit,
+ length = 1,
+ }
+ }
+end
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/blinea.lua b/Master/texmf-dist/doc/latex/luatikz/example/blinea.lua
new file mode 100644
index 00000000000..8a49476e511
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/blinea.lua
@@ -0,0 +1,23 @@
+tikz.within( '*' )
+
+local l1 = bline{ p{ 0, 0 }, p{ 2, 3 }, bend_right = 45 }
+local l2 = bline{ p{ 0, 3 }, p{ 2, 1 }, bend_left = 30 }
+
+draw{ draw=red, l1 }
+draw{ draw=blue, l2 }
+put{
+ node{
+ at = l1.pc,
+ anchor = south,
+ rotate = l1.line.phi * 180 / math.pi,
+ text = 'red',
+ }
+}
+put{
+ node{
+ at = l2.pc,
+ anchor = south,
+ rotate = l2.line.phi * 180 / math.pi,
+ text = 'blue',
+ }
+}
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/circle.lua b/Master/texmf-dist/doc/latex/luatikz/example/circle.lua
new file mode 100644
index 00000000000..ba85c04e5ad
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/circle.lua
@@ -0,0 +1,12 @@
+tikz.within( '*' )
+
+draw{
+ circle{
+ at = p{ 0, 0 },
+ radius = 2,
+ },
+ circle{
+ at = p{ 2, 1 },
+ radius = 1,
+ }
+}
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/curvea.lua b/Master/texmf-dist/doc/latex/luatikz/example/curvea.lua
new file mode 100644
index 00000000000..b1d6b85506b
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/curvea.lua
@@ -0,0 +1,14 @@
+tikz.within( '*' )
+
+local c =
+ curve{
+ points =
+ {
+ p{ 0, 0 },
+ p{ 2, 2 },
+ p{ 4, 0 },
+ },
+ }
+
+draw{ line_width=1, c }
+c.drawHelpers( 'p' )
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/curveb.lua b/Master/texmf-dist/doc/latex/luatikz/example/curveb.lua
new file mode 100644
index 00000000000..194127837a7
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/curveb.lua
@@ -0,0 +1,16 @@
+tikz.within( '*' )
+
+local c =
+ curve{
+ points =
+ {
+ p{ 0, 0 },
+ p{ 2, 2 },
+ p{ 4, 0 },
+ },
+ cycle = true,
+ tension = 1.0,
+ }
+
+draw{ line_width=1, c }
+c.drawHelpers( 'p' )
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/ellipse.lua b/Master/texmf-dist/doc/latex/luatikz/example/ellipse.lua
new file mode 100644
index 00000000000..c648e813203
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/ellipse.lua
@@ -0,0 +1,14 @@
+tikz.within( '*' )
+
+draw{
+ ellipse{
+ at = p{ 0, 0 },
+ xradius = 3,
+ yradius = 2,
+ },
+ ellipse{
+ at = p{ 2, 1 },
+ xradius = 1.5,
+ yradius = 1,
+ }
+}
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/ellipseArc.lua b/Master/texmf-dist/doc/latex/luatikz/example/ellipseArc.lua
new file mode 100644
index 00000000000..97536d3836d
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/ellipseArc.lua
@@ -0,0 +1,18 @@
+tikz.within( '*' )
+
+draw{
+ ellipseArc{
+ at = p{ 0, 0 },
+ from = 0,
+ to = 90,
+ xradius = 3,
+ yradius = 2,
+ },
+ ellipseArc{
+ at = p{ 2, 1 },
+ xradius = 1.5,
+ yradius = 1,
+ from = -45,
+ to = 45,
+ }
+}
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/immute1.lua b/Master/texmf-dist/doc/latex/luatikz/example/immute1.lua
new file mode 100644
index 00000000000..39b0e08a420
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/immute1.lua
@@ -0,0 +1,4 @@
+tikz.within( '*' )
+
+local p1 = p{ 1, 1 }
+p1.x = p1.x + 2
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/immute2.lua b/Master/texmf-dist/doc/latex/luatikz/example/immute2.lua
new file mode 100644
index 00000000000..3c69f4b6b52
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/immute2.lua
@@ -0,0 +1,6 @@
+tikz.within( '*' )
+
+local p1 = p{ 1, 1 }
+p1 = p{ p1.x + 2, p1.y }
+-- or
+p1 = p1 + p{ 2, 0 }
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/intro.lua b/Master/texmf-dist/doc/latex/luatikz/example/intro.lua
new file mode 100644
index 00000000000..9752fd464ea
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/intro.lua
@@ -0,0 +1,8 @@
+tikz.within( '*' )
+
+local p1 = p{ 1, 1 }
+local p2 = p{ 6, 2 }
+local l1 = line{ p1, p2 }
+
+draw{ l1 }
+draw{ line{ p{ 2, 2 }, p{ 3, 3 } } }
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/linea.lua b/Master/texmf-dist/doc/latex/luatikz/example/linea.lua
new file mode 100644
index 00000000000..fcc4a78989e
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/linea.lua
@@ -0,0 +1,7 @@
+tikz.within( '*' )
+
+local l1 = line{ p{ 0, 0 }, p{ 2, 3 } }
+local l2 = line{ p1 = p{ 0, 3 }, p2 = p{ 2, 1 } }
+
+draw{ l1 }
+draw{ l2 }
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/lineb.lua b/Master/texmf-dist/doc/latex/luatikz/example/lineb.lua
new file mode 100644
index 00000000000..0faf9049674
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/lineb.lua
@@ -0,0 +1,9 @@
+tikz.within( '*' )
+
+local l1 = line{ p{ 0, 0 }, p{ 2, 3 } }
+local l2 = line{ p1 = p{ 0, 3 }, p2 = p{ 2, 1 } }
+local pi = l1.intersectLine( l2 )
+
+draw{ l1 }
+draw{ l2 }
+draw{ circle{ at = pi, radius = 0.1 } }
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/linec.lua b/Master/texmf-dist/doc/latex/luatikz/example/linec.lua
new file mode 100644
index 00000000000..115def104b3
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/linec.lua
@@ -0,0 +1,7 @@
+tikz.within( '*' )
+
+draw{ line{
+ p1 = p{ 0, 0 },
+ phi = -math.pi / 4,
+ length = 2,
+} }
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/lined.lua b/Master/texmf-dist/doc/latex/luatikz/example/lined.lua
new file mode 100644
index 00000000000..38bc0eaa081
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/lined.lua
@@ -0,0 +1,15 @@
+tikz.within( '*' )
+
+local l = line{ p{ 0, 0 }, p{ 5, 0 } }
+draw{ l }
+
+for i = 1, 10
+do
+ l =
+ line{
+ p1 = l.p2,
+ phi = l.phi + math.pi / 5,
+ length = l.length * 2 / 3,
+ }
+ draw{ l }
+end
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/nodea.lua b/Master/texmf-dist/doc/latex/luatikz/example/nodea.lua
new file mode 100644
index 00000000000..4d34f2c0991
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/nodea.lua
@@ -0,0 +1,12 @@
+tikz.within( '*' )
+
+put{
+ node{
+ at = p{ 0, 0 },
+ text = [[$a^2 + b^2 = c^2$]],
+ },
+ node{
+ at = p{ 2, 1 },
+ text = [[$sin(\phi) = \frac{b}{a}$]],
+ }
+}
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/nodeb.lua b/Master/texmf-dist/doc/latex/luatikz/example/nodeb.lua
new file mode 100644
index 00000000000..85d8aeb49e1
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/nodeb.lua
@@ -0,0 +1,18 @@
+tikz.within( '*' )
+
+local r1 =
+ rect{
+ pc = p{ 0, 0 },
+ size = p{ 3, 3 },
+ }
+
+draw{ r1 }
+
+put{
+ node{
+ at = r1.pc,
+ text = [[this is the center of a rect]],
+ text_width = '2cm',
+ rotate = 45,
+ },
+}
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/plota.lua b/Master/texmf-dist/doc/latex/luatikz/example/plota.lua
new file mode 100644
index 00000000000..1a5721082ef
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/plota.lua
@@ -0,0 +1,14 @@
+tikz.within( '*' )
+
+draw{
+ plot{
+ at = p{ 0, 0 },
+ from = 0,
+ to = 5,
+ step = 0.05,
+ func =
+ function( d )
+ return p{ d, math.pow( d - 2.5, 2 ) / 2 }
+ end
+ }
+}
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/polylinea.lua b/Master/texmf-dist/doc/latex/luatikz/example/polylinea.lua
new file mode 100644
index 00000000000..f19954798a6
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/polylinea.lua
@@ -0,0 +1,11 @@
+tikz.within( '*' )
+
+draw{
+ polyline{
+ p{ 0, 0 },
+ p{ 2, 2 },
+ p{ 4, 0 },
+ p{ 3, -1 },
+ 'cycle',
+ }
+}
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/radon1.lua b/Master/texmf-dist/doc/latex/luatikz/example/radon1.lua
new file mode 100644
index 00000000000..166c44c7635
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/radon1.lua
@@ -0,0 +1,119 @@
+tikz.within( '*' )
+
+local cos = math.cos
+local sin = math.sin
+local pi = math.pi
+
+-- granularity of calculation
+local fine = 10
+
+-- factor the projection length is reduced
+local fproj = 0.35
+
+-- strength of the ellipse mediums
+local s1 = 1.00
+local s2 = 1.50
+local s3 = 1.75
+
+local e1 = ellipse{ at=p{ 0.00, 0.00 }, xradius=3.00, yradius=3.00 }
+local e2 = ellipse{ at=p{ 0.40, 1.20 }, xradius=0.85, yradius=0.50 }
+local e3 = ellipse{ at=p{ 0.00, -0.89 }, xradius=1.00, yradius=0.60 }
+
+draw{ fill='black!08!white', line_width=1, e1 }
+draw{ fill='black!16!white', line_width=1, e2 }
+draw{ fill='black!24!white', line_width=1, e3 }
+
+-- list of projection angles
+local listphi =
+{
+ -1/4 * pi,
+ 2/4 * pi,
+ 5/4 * pi,
+}
+
+-- length of projection lines
+local lenmaintop = 5.0
+local lenmainbot = 6.5
+
+for proji, phimain in ipairs( listphi )
+do
+ local lmain =
+ line{
+ p{ cos( phimain ), sin( phimain ) } * -lenmaintop,
+ p{ cos( phimain ), sin( phimain ) } * lenmainbot,
+ }
+ local phinorm = phimain - pi/2
+
+ -- gets a point on the projection.
+ -- dp: distance from projection center
+ -- dv: projection value
+ function getpproj( dp, dv )
+ return(
+ lmain.p2
+ + p{ cos( phinorm ) * dp, sin( phinorm ) * dp }
+ + p{ cos( phimain ) * dv * fproj, sin( phimain ) * dv * fproj }
+ )
+ end
+
+ -- distance of projection line
+ local ddis = 1 / fine
+
+ -- list of all projection points
+ local listp = { }
+
+ for i = -3.5 * fine, 3.5 * fine
+ do
+ local p1i =
+ lmain.p1
+ + p{ cos( phinorm ) * ddis * i, sin( phinorm ) * ddis * i }
+ local li = line{ p1i, p1i + ( lmain.p2 - lmain.p1 ) }
+ local lv = 0
+ local is1 = e1.intersectLine( li )
+
+ if is1 and #is1 > 1
+ then
+ local lis = line{ is1[ 1 ], is1[ 2 ] }
+ lv = lv + lis.length * s1
+ end
+
+ local is2 = e2.intersectLine( li )
+ if is2 and #is2 > 1
+ then
+ local lis = line{ is2[ 1 ], is2[ 2 ] }
+ lv = lv + lis.length * s2
+ end
+
+ local is3 = e3.intersectLine( li )
+ if is3 and #is3 > 1
+ then
+ local lis = line{ is3[ 1 ], is3[ 2 ] }
+ lv = lv + lis.length * s3
+ end
+
+ local pproj = getpproj( ddis * i, lv )
+ if i % ( 0.7 * fine ) == 0
+ then
+ draw{
+ dotted, arrow, line_width=0.5,
+ line{ li.p1, getpproj( ddis * i, -0.20 ) },
+ }
+ draw{
+ dotted, line_width=0.5,
+ line{ li.p1, pproj },
+ }
+ end
+ table.insert( listp, pproj )
+ end
+
+ -- draws the projection screen
+ local lenlproj = 8
+ draw{
+ line{
+ getpproj( lenlproj / 2, 0 ),
+ getpproj( -lenlproj / 2, 0 ),
+ }
+ }
+
+ -- draws the projection curve
+ draw{ line_width=1, polyline{ table.unpack( listp ) } }
+end
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/recta.lua b/Master/texmf-dist/doc/latex/luatikz/example/recta.lua
new file mode 100644
index 00000000000..91c8203ef78
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/recta.lua
@@ -0,0 +1,21 @@
+tikz.within( '*' )
+
+local r1 =
+ rect{
+ pnw = p{ 0, 3 },
+ pse = p{ 3, 0 },
+ }
+
+local r2 =
+ rect{
+ psw = r1.pse,
+ size = p{ 1, 1 },
+ }
+
+local r3 =
+ rect{
+ pc = r1.pc,
+ size = p{ 1.5, 1.5 },
+ }
+
+draw{ r1, r2, r3 }
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/shadea.lua b/Master/texmf-dist/doc/latex/luatikz/example/shadea.lua
new file mode 100644
index 00000000000..31e28dd3146
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/shadea.lua
@@ -0,0 +1,9 @@
+tikz.within( '*' )
+
+shade{
+ ball_color = gray,
+ circle{
+ at = p0,
+ radius = 2,
+ },
+}
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/sierpinski.lua b/Master/texmf-dist/doc/latex/luatikz/example/sierpinski.lua
new file mode 100644
index 00000000000..0b5588bf918
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/sierpinski.lua
@@ -0,0 +1,41 @@
+tikz.within( '*' )
+
+local s60 = math.sin( 60 / 180 * math.pi )
+local c60 = math.cos( 60 / 180 * math.pi )
+
+-- creates a table object having ptop, pleft and pright as points
+-- and a "draw yourself" function
+function equilateralTriangle( ptop, len )
+ return {
+ ptop = ptop,
+ pleft = ptop + p{ -len/2, -len*s60 },
+ pright = ptop + p{ len/2, -len*s60 },
+ draw =
+ function( self )
+ draw{
+ fill = black,
+ draw = none,
+ polyline{ self.ptop, self.pleft, self.pright, 'cycle' }
+ }
+ end
+ }
+end
+
+-- one step into the fractal
+-- ptop: top point of triangle
+-- len: current length
+-- level: current fractal level
+function drawFractal( ptop, len, level )
+ if level == 1
+ then
+ local t = equilateralTriangle( ptop, len )
+ t:draw( )
+ else
+ local ttop = equilateralTriangle( ptop, len / 2 )
+ drawFractal( ttop.ptop, len / 2, level - 1 )
+ drawFractal( ttop.pleft, len / 2, level - 1 )
+ drawFractal( ttop.pright, len / 2, level - 1 )
+ end
+end
+
+drawFractal( p{ 0, 0 }, 8, 6 )
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/spiral.lua b/Master/texmf-dist/doc/latex/luatikz/example/spiral.lua
new file mode 100644
index 00000000000..12c135062ec
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/spiral.lua
@@ -0,0 +1,16 @@
+tikz.within( '*' )
+
+local pi = p{ 0, 0 }
+local pdx = p{ 0.25, 0 }
+local pdy = p{ 0, 0.25 }
+
+for k = 1, 20
+do
+ local sign = 1
+ if k % 2 == 0 then sign = -1 end
+ local pn = pi + sign * k * pdx
+ local pn2 = pn + sign * k * pdy
+ draw{ line{ pi, pn }, line{ pn, pn2 } }
+ pi = pn2
+end
+
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/stylea.lua b/Master/texmf-dist/doc/latex/luatikz/example/stylea.lua
new file mode 100644
index 00000000000..ef3805cdd02
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/stylea.lua
@@ -0,0 +1,19 @@
+tikz.within( '*' )
+
+local l1 = line{ p{ 0, 0 }, p{ 3, 1 } }
+local l2 = line{ p{ 0, -1 }, p{ 3, 0 } }
+local l3 = line{ p{ 0, -2 }, p{ 3, -1 } }
+
+draw{ arrow, l1 }
+draw{ double_arrow, dashed, l2 }
+draw{ color = purple, line_width = 2, dotted, l3 }
+
+put{ node{
+ at = l3.pc,
+ anchor = center,
+ align = center,
+ color = purple,
+ rotate = l3.phi * 180 / math.pi,
+ text = 'label for purple line',
+ text_width = '2cm',
+} }
diff --git a/Master/texmf-dist/doc/latex/luatikz/example/superEllipse.lua b/Master/texmf-dist/doc/latex/luatikz/example/superEllipse.lua
new file mode 100644
index 00000000000..32c629008ca
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/example/superEllipse.lua
@@ -0,0 +1,22 @@
+tikz.within( '*' )
+
+local list = { 1, 1.5, 2, 4, 999 }
+
+for i, v in ipairs( list )
+do
+ local pi = p{ ( i - 1 ) * 2.5, 0 }
+ draw{
+ superEllipse{
+ at = pi,
+ n = v,
+ xradius = 1,
+ yradius = 1,
+ }
+ }
+ put{
+ node{
+ at = pi,
+ text = 'n = ' .. v,
+ }
+ }
+end
diff --git a/Master/texmf-dist/doc/latex/luatikz/luatikz.pdf b/Master/texmf-dist/doc/latex/luatikz/luatikz.pdf
new file mode 100644
index 00000000000..983105ad7fb
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/luatikz.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/luatikz/luatikz.tex b/Master/texmf-dist/doc/latex/luatikz/luatikz.tex
new file mode 100644
index 00000000000..d4bf734b1dd
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/luatikz/luatikz.tex
@@ -0,0 +1,204 @@
+\documentclass[a4paper,11pt]{ltxdoc}
+
+\usepackage[utf8]{inputenc}
+\usepackage{makeidx}\makeindex
+\usepackage{luatextra}
+\usepackage[margin=1in]{geometry}
+
+\usepackage{fancyvrb}
+\fvset{frame=single,numbers=left,numbersep=3pt,tabsize=4}
+
+\usepackage{tikz}
+\usetikzlibrary{math,backgrounds,calc,intersections,arrows.meta,external,decorations.pathreplacing,shadings}
+\directlua{dofile('./luatikz.lua')}
+
+\newcommand{\example}[2]
+{
+\par#2
+\VerbatimInput{example/#1.lua}
+{\centering
+\begin{tikzpicture}[framed,scale=1]
+\directlua{dofile('example/#1.lua')tikz.without()}
+\end{tikzpicture}\par}
+}
+
+\newcommand{\examplecode}[2]
+{
+\bigskip\par\noindent #2
+\VerbatimInput{example/#1.lua}
+}
+
+
+\title{luatikz}
+\author{Axel Kittenberger}
+\def\version{2.12.0}
+\date{luatikz version \version}
+
+\setlength{\parindent}{0pt}
+\setlength{\parskip}{1ex}
+
+\begin{document}
+
+\maketitle
+
+\section{Introduction}
+Luatikz is a graphics library to draw tikz graphics using the Lua programming language.
+
+\example{intro}
+{Following code draws two lines by specifying begin and end points. First using intermediatery variables and the second line within a single codeline:}
+
+Following \TeX{} wrapper can be used to compile the luatikz Lua code.
+\VerbatimInput{example/wrapper.tex}
+
+Luatikz obviously needs \LuaLaTeX{} to compile.
+
+Note that 2D graphics is a vast topic, functionality to luatikz has been and will be added on a per need basis.
+
+\section{Pointer math}
+
+Mathematical operations are possible with points, adding them with each other or multiplying them with a scalar.
+\example{spiral}{This example uses pointer math to create a spiral:}
+
+\newpage
+\section{Immutability}
+
+In luatikz pointers like all objects are immutable. That means a object once created in memory can no longer be changed.
+However the variable holding an object can be changed to another object with different attributes.
+
+\examplecode{immute1}{Thus following example is invalid:}
+\examplecode{immute2}{This on the other hand are two valid methods to change the value of p1}
+
+\newpage
+\section{luatikz objects}
+
+\subsection{Bezier (quatratic)}
+\example{bezier2}{A quadratic bezier curve is defined by it begin, end and a control point:}
+
+\newpage
+\subsection{Bezier (cubic)}
+\example{bezier3a}{A cubic bezier curve is defined by it begin, end and two control points:}
+
+\newpage
+A cubic bezier has the functions pt and phit that return the point and angle ranging from 0..1 on the curve.
+\example{bezier3b}{This example draw 10 normal lines onto the bezier curve:}
+
+\newpage
+\subsection{BLine}
+\example{blinea}{A bline is defined by it's begin and it's end. Additionally to the standard line it is bend}
+
+\newpage
+\subsection{Circle}
+\example{circle}{A circle is defined by it's center (``at'') and the circle ``radius'':}
+
+\newpage
+\subsection{Curve}
+\example{curvea}{A curve is defined by a list of points:}
+
+\newpage
+\example{curveb}{A curve can cycle and has a changeable tension:}
+
+\newpage
+\subsection{Ellipse}
+\example{ellipse}{An ellipse is difined by it's center (``at'') and a x- and yradius:}
+
+\newpage
+\subsection{EllipseArc}
+\example{ellipseArc}{An ellipse arc is a an ellipse limited by it's ``from'' and ``to'' angle:}
+
+\newpage
+\subsection{Line}
+\example{linea}{A line is defined by it's begin and it's end. There are two basic variants to define a line:}
+
+\example{lineb}{You can use intersectLine() to find the intersection of a line with another. If there is none ``nil'' will be returned:}
+
+\newpage
+\example{linec}{A line can also be defined by it's starting point, angle and length:}
+
+\example{lined}{``length'' and ``phi'' are attributes of a line:}
+
+\newpage
+\subsection{Plot}
+\example{plota}{A plot uses a function to determine a series of points on a curve. It takes values going ``from''--``to''. The function is to be a lua function that takes the input scalar and returns a point. The whole curve is offset ``at'' a point:}
+
+%\newpage
+%\subsection{SuperEllipse}
+%\example{superEllipse}{A ``super'' ellipse is just as an ellipse, but additionally has a strength factor ``n'' that ranges from diamond, to ellipse to rectangle:}
+
+\newpage
+\subsection{Polyline}
+\example{polylinea}{A polyline is a list of points to be connected via straight lines. If the string ``cycle'' is given at the end, it cycles:}
+
+\newpage
+\subsection{Rect}
+\example{recta}{Rectangles have various creation options. Also they provide a wide range of attributes of their points ``pnw'', ``pne'', ``psw'', ``pse'', ``pn'', ``pe'', ``ps'', ``pw'', ``pc'', ``height'', and ``width'':
+}
+
+\newpage
+\section{Labels a.k.a Nodes}
+
+Labels are created as nodes. Contrary to all other objects they do not need the ``draw'' command to be printed, but the ``put'' command. This is due any call to ``draw'' is turned into exactly one ``draw'' command to tikz and nodes in tikz are not using the ``draw'' command.
+
+\example{nodea}{In it's simplest form a node is specified by it's position and text. The double square brackets are Lua's way to make string constants that may contain simple blackslashes:}
+
+All constructors options of node are as follows; they correspond to the standard tikz options:
+\begin{itemize}
+ \item above
+ \item anchor
+ \item align
+ \item at
+ \item below
+ \item color
+ \item draw
+ \item left
+ \item minimum\_height
+ \item node\_distance
+ \item name
+ \item right
+ \item rotate
+ \item text
+ \item text\_width
+\end{itemize}
+\newpage
+
+\example{nodeb}{Another node example}
+
+\newpage
+\section{Styling}
+
+Styling is applied to draw commands by specifying style options, luatikz autodetects the difference between objects and styles.
+
+\example{stylea}{An example using arrows and dashes}
+
+\newpage
+\section{Shades}
+
+The ``shade'' command works analogous to classical tikz.
+
+\example{shadea}{For example a ball:}
+
+Recognized shade options are:
+\begin{itemize}
+ \item ball\_color
+ \item left\_color
+ \item lower\_left\_color
+ \item lower\_right\_color
+ \item opacity
+ \item right\_color
+ \item shading
+ \item upper\_left\_color
+ \item upper\_right\_color
+\end{itemize}
+
+\newpage
+\section{Some larger examples}
+
+\subsection{A radon projection}
+\example{radon1}{A radon projection, calculating correct projection curves:}
+
+\newpage
+\subsection{A sierpiński fractal}
+\example{sierpinski}{A sierpiński fractal. This one creates own temporary lua objects:}
+
+\printindex
+
+\end{document}
diff --git a/Master/texmf-dist/tex/latex/luatikz/luatikz.lua b/Master/texmf-dist/tex/latex/luatikz/luatikz.lua
new file mode 100644
index 00000000000..1c18f2f6278
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/luatikz/luatikz.lua
@@ -0,0 +1,1999 @@
+local _version = '2.12.0'
+
+if tikz
+then
+ if tikz.version ~= _version
+ then
+ error( 'version chaos' )
+ end
+ return
+end
+
+tikz = { version = _version }
+
+--==========================================================================--
+-- DEFINES
+--==========================================================================--
+
+-- directions
+tikz.north = 'north'
+tikz.north_east = 'north east'
+tikz.east = 'east'
+tikz.south_east = 'south east'
+tikz.south = 'south'
+tikz.south_west = 'south west'
+tikz.west = 'west'
+tikz.north_west = 'north west'
+
+-- colors
+tikz.black = 'black'
+tikz.blue = 'blue'
+tikz.brown = 'brown'
+tikz.cyan = 'cyan'
+tikz.darkgray = 'darkgray'
+tikz.gray = 'gray'
+tikz.green = 'green'
+tikz.lightgray = 'lightgray'
+tikz.lime = 'lime'
+tikz.magenta = 'magenta'
+tikz.olive = 'olive'
+tikz.orange = 'orange'
+tikz.pink = 'pink'
+tikz.purple = 'purple'
+tikz.red = 'red'
+tikz.teal = 'teal'
+tikz.violet = 'violet'
+tikz.yellow = 'yellow'
+tikz.white = 'white'
+
+-- align
+tikz.center = 'center'
+tikz.left = 'left'
+tikz.right = 'right'
+
+tikz.none = 'none'
+
+-- line caps
+tikz.round = 'round'
+
+--==========================================================================--
+-- HELPERS --
+--==========================================================================--
+
+-- if defined writes output here
+local outfile
+
+--
+-- Prints arguments to tex and to stdout.
+--
+local function tprint( ... )
+ local args = {...}
+ print( table.concat( args ) )
+
+ if tex
+ then
+ tex.print( table.concat( args ) )
+ end
+
+ if outfile
+ then
+ outfile:write( table.concat( args ) )
+ outfile:write( '\n%%% DO NOT EDIT THIS FILE %%%\n' )
+ end
+end
+tikz.tprint = tprint
+
+--
+-- Flattens an object stack.
+--
+-- The content of all numerated unclassed objects
+-- are put into a copy of obj.
+--
+local function flatten( o )
+ local r = { }
+ for _, v in ipairs( o )
+ do
+ if type( v ) == 'table' and getmetatable( v ) == nil
+ then
+ local fv = flatten( v )
+ for _, fvv in ipairs( fv )
+ do
+ table.insert( r, fvv )
+ end
+ for k, vv in pairs( fv )
+ do
+ if type( k ) ~= 'number'
+ then
+ r[ k ] = vv
+ end
+ end
+ else
+ table.insert( r, v )
+ end
+ end
+ -- copies (and overwrites) all named attributes
+ for k, v in pairs( o )
+ do
+ if type( k ) ~= 'number'
+ then
+ r[ k ] = v
+ end
+ end
+ return r
+end
+
+--
+-- A key marking an object having a zString function.
+--
+local _zString = { }
+
+--
+-- Converts everything to a tikz string.
+--
+local function zString( v )
+ if type( v ) == 'table' and v[ _zString ]
+ then
+ return v[ _zString ]( v )
+ end
+ return tostring( v )
+end
+
+--
+-- Creates an immutable class.
+--
+local function immutable( definer )
+ local def = { }
+ local lazy = { }
+ local proto = { }
+ def.lazy = lazy
+ def.proto = proto
+ definer( def ) -- the callee fills in data
+ local k = { } -- key the hidden table
+
+ local mt = { }
+ mt.__index =
+ function( self, key )
+ if key == 'id' then return def.id end
+
+ -- first check if native value
+ local v = self[ k ][ key ]
+ if v then return v end
+
+ -- then if a lazy function
+ local f = lazy[ key ]
+ if f
+ then
+ local v = f( self )
+ self[ k ][ key ] = v -- cache
+ return v
+ end
+
+ -- then if a proto value
+ v = proto[ key ]
+ if v
+ then
+ if type( v ) == 'function'
+ then
+ return(
+ function(...)
+ return v( self, table.unpack( {...} ) )
+ end
+ )
+ else
+ return v
+ end
+ end
+
+ -- otherwise
+ -- error( 'invalid property' )
+ end
+
+ mt.__newindex =
+ function( )
+ error( 'this is an immutable' )
+ end
+
+ if def.add
+ then
+ mt.__add = def.add
+ end
+
+ if def.concat
+ then
+ mt.__concat = def.concat
+ end
+
+ if def.div
+ then
+ mt.__div = def.div
+ end
+
+ if def.mul
+ then
+ mt.__mul = def.mul
+ end
+
+ if def.sub
+ then
+ mt.__sub = def.sub
+ end
+
+ if def.tostring
+ then
+ mt.__tostring = def.tostring
+ end
+
+ return function(...)
+ local args = {...}
+ local nt = def.constructor( table.unpack( args ) )
+ local o = { [ k ] = nt, [ _zString ] = def.zString }
+ setmetatable( o, mt )
+ return o
+ end
+end
+tikz.immutable = immutable
+
+--
+-- writes out direct options
+--
+local function _opts( s, _opts, haveOpts )
+ if not _opts
+ then
+ return haveOpts
+ end
+
+ for k, v in pairs( _opts )
+ do
+ if not haveOpts
+ then
+ table.insert( s, '[' )
+ else
+ table.insert( s, ',' )
+ end
+ haveOpts = true
+ if type( k ) == 'number'
+ then
+ table.insert( s, v )
+ else
+ table.insert( s, k..'='..v )
+ end
+ end
+ return haveOpts
+end
+
+-------------------------------------------------------------------------------
+-- Point.
+-------------------------------------------------------------------------------
+tikz.p =
+immutable( function( def )
+ def.id = 'point'
+
+ def.add =
+ function( left, right )
+ if right.id == 'line'
+ or right.id == 'bezier3'
+ then
+ return right + left
+ end
+
+ return tikz.p{ left.x + right.x, left.y + right.y }
+ end
+
+ def.concat =
+ function( left, right )
+ return zString( left )..zString( right )
+ end
+
+ -- constructor
+ def.constructor =
+ function( args )
+ return { x = args[ 1 ], y = args[ 2 ] }
+ end
+
+ -- divisor
+ def.div =
+ function( left, right )
+ return tikz.p{ left.x / right, left.y / right }
+ end
+
+ def.mul =
+ function( left, right )
+ if type( right ) == 'number'
+ then
+ return tikz.p{ left.x * right, left.y * right }
+ elseif type( left ) == 'number'
+ then
+ return tikz.p{ left * right.x, left * right.y }
+ elseif type( right ) == 'table'
+ then
+ if right.id == 'point'
+ then
+ return tikz.p{ left.x * right.x, left.y * right.y }
+ end
+ end
+ error( 'invalid operation' )
+ end
+
+ def.sub =
+ function( left, right )
+ return tikz.p{ left.x - right.x, left.y - right.y }
+ end
+
+ def.tostring =
+ function( self )
+ return 'p{ '..self.x..', '..self.y..' }'
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ return '('..self.x..','..self.y..')'
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- A full circle.
+-------------------------------------------------------------------------------
+tikz.circle =
+immutable( function( def )
+ def.id = 'circle'
+
+ -- constructor
+ def.constructor =
+ function( args )
+ return {
+ at = args.at,
+ radius = args.radius,
+ }
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ return zString( self.at )..' circle ('.. self.radius .. ')'
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- A quadratic bezier curve.
+-------------------------------------------------------------------------------
+tikz.bezier2 =
+immutable( function( def )
+ def.id = 'bezier2'
+
+ -- constructor
+ def.constructor =
+ function( args )
+ local p1 = args.p1
+ local pc = args.pc
+ local p2 = args.p2
+
+ return {
+ p1 = p1,
+ pc = pc,
+ p2 = p2,
+ }
+ end
+
+ -- draws helping information
+ def.proto.drawHelpers =
+ function( self, prefix )
+ tikz.draw{ draw = 'red', tikz.line{ self.p1, self.pc } }
+ tikz.draw{ draw = 'blue', tikz.line{ self.pc, self.p2 } }
+ if prefix ~= nil
+ then
+ tikz.put{ tikz.node{
+ at = self.p1,
+ anchor = 'north west',
+ text = prefix .. '1',
+ } }
+ tikz.put{ tikz.node{
+ at = self.pc,
+ anchor = 'north west',
+ text = prefix .. 'c',
+ } }
+ tikz.put{ tikz.node{
+ at = self.p2,
+ anchor = 'north west',
+ text = prefix .. '2',
+ } }
+ end
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ local s = { }
+ table.insert( s, zString( self.p1 ) )
+ table.insert( s, '.. controls' )
+ table.insert( s, zString( self.pc ) )
+ table.insert( s, '..' )
+ table.insert( s, zString( self.p2 ) )
+ return table.concat( s, ' ' )
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- A cubic bezier curve.
+-------------------------------------------------------------------------------
+tikz.bezier3 =
+immutable( function( def )
+ def.id = 'bezier3'
+
+ def.add =
+ function( left, right )
+ if right.id == 'point'
+ then
+ return tikz.bezier3{
+ p1 = left.p1 + right,
+ pc1 = left.pc1 + right,
+ pc2 = left.pc2 + right,
+ p2 = left.p2 + right,
+ }
+ end
+ error( 'unknown operation' )
+ end
+
+ -- constructor
+ def.constructor =
+ function( args )
+ local p1 = args.p1
+ local pc1 = args.pc1
+ local pc2 = args.pc2
+ local p2 = args.p2
+
+ return {
+ p1 = p1,
+ pc1 = pc1,
+ pc2 = pc2,
+ p2 = p2,
+ }
+ end
+
+ -- draws helping information
+ def.proto.drawHelpers =
+ function( self, prefix )
+ tikz.draw{ draw = 'red', tikz.line{ self.p1, self.pc1 } }
+ tikz.draw{ draw = 'yellow', tikz.line{ self.pc1, self.pc2 } }
+ tikz.draw{ draw = 'blue', tikz.line{ self.pc2, self.p2 } }
+ if prefix ~= nil
+ then
+ tikz.put{ tikz.node{
+ at = self.p1,
+ anchor = 'north west',
+ text = prefix .. '1',
+ } }
+ tikz.put{ tikz.node{
+ at = self.pc1,
+ anchor = 'north west',
+ text = prefix .. 'c1',
+ } }
+ tikz.put{ tikz.node{
+ at = self.pc2,
+ anchor = 'north west',
+ text = prefix .. 'c2',
+ } }
+ tikz.put{ tikz.node{
+ at = self.p2,
+ anchor = 'north west',
+ text = prefix .. '2',
+ } }
+ end
+ end
+
+ def.mul =
+ function( left, right )
+ if type( right ) == 'number'
+ then
+ return tikz.bezier3 {
+ p1 = left.p1 * right,
+ pc1 = left.pc1 * right,
+ pc2 = left.pc2 * right,
+ p2 = left.p2 * right,
+ }
+ elseif type( left ) == 'number'
+ then
+ return tikz.bezier3 {
+ p1 = right.p1 * left,
+ pc1 = right.pc1 * left,
+ pc2 = right.pc2 * left,
+ p2 = right.p2 * left,
+ }
+ else
+ error( 'invalid operation' )
+ end
+ end
+
+ -- point in center
+ def.lazy.pc =
+ function( self )
+ return self.pt( 0.5 )
+ end
+
+ -- gets angle at t (0-1)
+ def.proto.phit =
+ function( self, t )
+ local p1 = self.p1
+ local pc1 = self.pc1
+ local pc2 = self.pc2
+ local p2 = self.p2
+
+ local u = 1 - t
+ local uu3 = 3 * u * u
+ local ut6 = 6 * u * t
+ local tt3 = 3 * t * t
+
+ return math.atan2(
+ -uu3 * p1.y + uu3 * pc1.y - ut6 * pc1.y + ut6 * pc2.y - tt3 * pc2.y + tt3 * p2.y,
+ -uu3 * p1.x + uu3 * pc1.x - ut6 * pc1.x + ut6 * pc2.x - tt3 * pc2.x + tt3 * p2.x
+ )
+ end
+
+ -- reverts p1 and p2 and pc1 and pc2 respectively
+ def.lazy.revert =
+ function( self )
+ return tikz.bezier3{
+ p1 = self.p2,
+ pc1 = self.pc2,
+ pc2 = self.pc1,
+ p2 = self.p1,
+ }
+ end
+
+ -- gets point at t (0-1)
+ def.proto.pt =
+ function( self, t )
+ if type( t ) ~= 'number' or t < 0 or t > 1
+ then
+ error( 'invalid pt' )
+ end
+
+ local p1 = self.p1
+ local pc1 = self.pc1
+ local pc2 = self.pc2
+ local p2 = self.p2
+ local t1 = 1 - t
+
+ return(
+ tikz.p{
+ t1^3*p1.x + 3*t*t1^2*pc1.x + 3 *t^2*t1*pc2.x + t^3*p2.x,
+ t1^3*p1.y + 3*t*t1^2*pc1.y + 3 *t^2*t1*pc2.y + t^3*p2.y
+ }
+ )
+ end
+
+ def.sub =
+ function( left, right )
+ if right.id == 'point'
+ then
+ return tikz.bezier3{
+ p1 = left.p1 - right,
+ pc1 = left.pc1 - right,
+ pc2 = left.pc2 - right,
+ p2 = left.p2 - right,
+ }
+ end
+ error( 'unknown operation' )
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ local s = { }
+ table.insert( s, zString( self.p1 ) )
+ table.insert( s, '.. controls' )
+ table.insert( s, zString( self.pc1 ) )
+ table.insert( s, 'and' )
+ table.insert( s, zString( self.pc2 ) )
+ table.insert( s, '..' )
+ table.insert( s, zString( self.p2 ) )
+ return table.concat( s, ' ' )
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- A bend line
+-------------------------------------------------------------------------------
+tikz.bline =
+immutable( function( def )
+ def.id = 'bline'
+
+ -- moves the line.
+ def.add =
+ function( left, right )
+ if right.id == 'point'
+ then
+ return tikz.line{ p1 = left.p1 + right, p2 = left.p2 + right }
+ end
+ error( 'unknown operation' )
+ end
+
+ -- constructor
+ --
+ -- either
+ -- { p1, p2 }, (a list )
+ -- { p1 = ..., p2 = ... },
+ -- { pc = ..., phi = ..., length = ... },
+ def.constructor =
+ function( args )
+ local p1
+ local p2
+
+ if #args > 1
+ then
+ -- list
+ p1 = args[ 1 ]
+ p2 = args[ 2 ]
+ elseif type( args.p1 ) ~= 'nil' and type( args.p2 ) ~= 'nil'
+ then
+ p1 = args.p1
+ p2 = args.p2
+ elseif args.phi ~= nil
+ then
+ local phi = args.phi
+ if args.pc ~= nil
+ then
+ if args.p1 ~= nil
+ or args.p2 ~= nil
+ then
+ error( 'invalid line options', 2 )
+ end
+ local pc = args.pc
+ local len05 = args.length / 2
+ p1 = pc + tikz.p{ math.cos( phi ), math.sin( phi ) } * len05
+ p2 = pc - tikz.p{ math.cos( phi ), math.sin( phi ) } * len05
+ elseif type( args.p1 ) ~= 'nil'
+ then
+ if args.pc ~= nil
+ or args.p2 ~= nil
+ then
+ error( 'invalid line options', 2 )
+ end
+ p1 = args.p1
+ p2 = p1 + tikz.p{ math.cos( phi ), math.sin( phi ) } * args.length
+ else
+ error( 'invalid line options', 2 )
+ end
+ else
+ error( 'invalid line options', 2 )
+ end
+
+ return {
+ bend_left = args.bend_left,
+ bend_right = args.bend_right,
+ p1 = p1,
+ p2 = p2,
+ }
+ end
+
+ -- converts to a cubic bezier (that should look the same)
+ def.lazy.bezier3 =
+ function( self )
+ local phi
+ if self.bend_right ~= nil then
+ phi = self.bend_right * math.pi / 180
+ else
+ phi = -self.bend_left * math.pi / 180
+ end
+ local line = self.line
+ local phil = line.phi
+ local len = line.length * 0.3915
+ local lt1 =
+ tikz.line{
+ p1 = line.p1,
+ phi = phil-phi,
+ length = len
+ }
+ local lt2 =
+ tikz.line{
+ p1 = line.p2,
+ phi = phil+phi+math.pi,
+ length = len
+ }
+
+ return tikz.bezier3{
+ p1 = line.p1,
+ pc1 = lt1.p2,
+ pc2 = lt2.p2,
+ p2 = line.p2,
+ }
+ end
+
+ -- converts to a straight line.
+ def.lazy.line =
+ function( self )
+ return tikz.line{
+ p1 = self.p1,
+ p2 = self.p2,
+ }
+ end
+
+ -- point in center
+ def.lazy.pc =
+ function( self )
+ return self.bezier3.pc
+ end
+
+ -- gets point at t (0-1).
+ def.proto.pt =
+ function( self, t )
+ return self.bezier3.pt( t )
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ local s = { }
+ table.insert( s, zString( self.p1 ) )
+ table.insert( s, 'to' )
+ if self.bend_left ~= nil
+ then
+ table.insert( s, '[bend left='..self.bend_left..']' )
+ end
+ if self.bend_right ~= nil
+ then
+ table.insert( s, '[bend right='..self.bend_right..']' )
+ end
+ table.insert( s, zString( self.p2 ) )
+ return table.concat( s, ' ' )
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- A curve is defined by a list of points.
+-------------------------------------------------------------------------------
+curve =
+immutable( function( def )
+ def.id = 'curve'
+
+ -- constructor
+ def.constructor =
+ function( args )
+ return {
+ points = args.points,
+ tension = args.tension,
+ cycle = args.cycle,
+ }
+ end
+
+ -- draws helping information
+ def.proto.drawHelpers =
+ function( self, prefix )
+ local points = self.points
+ for i, pi in ipairs( points )
+ do
+ tikz.draw{
+ fill = 'black',
+ tikz.circle{
+ at = pi,
+ radius = 0.05,
+ }
+ }
+ if prefix ~= nil
+ then
+ tikz.put{ tikz.node{
+ at = pi,
+ anchor = 'north west',
+ text = prefix .. i,
+ } }
+ end
+ end
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ local s = { 'plot' }
+ table.insert( s, '[' )
+ if self.cycle
+ then
+ table.insert( s, 'smooth cycle' )
+ else
+ table.insert( s, 'smooth' )
+ end
+ if self.tension then
+ table.insert( s, ',tension='..self.tension )
+ end
+ table.insert( s, ']' )
+ local points = self.points
+ table.insert( s, 'coordinates' )
+ table.insert( s, '{' )
+ for _, p in ipairs( points )
+ do
+ table.insert( s, zString( p ) )
+ end
+ table.insert( s, '}' )
+ return table.concat( s, ' ' )
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- A full ellipse.
+-------------------------------------------------------------------------------
+tikz.ellipse =
+immutable( function( def )
+ def.id = 'ellipse'
+
+ -- constructor
+ def.constructor =
+ function( args )
+ local at = args.at
+ local xradius = args.xradius
+ local yradius = args.yradius
+
+ return {
+ at = at,
+ xradius = xradius,
+ yradius = yradius,
+ }
+ end
+
+ -- intersect with a line.
+ def.proto.intersectLine =
+ function( self, line )
+ local at = self.at
+ local rx = self.xradius
+ local ry = self.yradius
+ local p1 = line.p1
+ local p2 = line.p2
+
+ local x0 = p1.x
+ local y0 = p1.y
+ local x1 = p2.x
+ local y1 = p2.y
+ local cx = at.x
+ local cy = at.y
+
+ x0 = x0 - cx
+ y0 = y0 - cy
+ x1 = x1 - cx
+ y1 = y1 - cy
+
+ local A = ((x1 - x0) * (x1 - x0)) / rx / rx + ((y1 - y0) * (y1 - y0)) / ry / ry
+ local B = (2 * x0 * (x1 - x0)) / rx / rx + (2 * y0 * (y1 - y0)) / ry / ry
+ local C = (x0 * x0) / rx / rx + (y0 * y0) / ry / ry - 1
+ local D = B * B - 4 * A * C
+
+ local tv = { }
+ if D == 0
+ then
+ table.insert( tv, -B / 2 / A )
+ else
+ table.insert( tv, (-B + math.sqrt(D)) / 2 / A )
+ table.insert( tv, (-B - math.sqrt(D)) / 2 / A )
+ end
+
+ local r = { }
+ for _, t in ipairs( tv )
+ do
+ if t >= 0 and t <= 1
+ then
+ table.insert(
+ r,
+ p1 + tikz.p{ t * line.length * math.cos( line.phi ), t * line.length * math.sin( line.phi ) }
+ )
+ end
+ end
+
+ if #r == 0
+ then
+ return
+ elseif #r == 1
+ then
+ return r[ 1 ]
+ else
+ return r
+ end
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ s = { }
+ table.insert( s, zString( self.at ) )
+ table.insert( s, 'ellipse' )
+ table.insert( s, '[' )
+ table.insert( s, 'x radius =' )
+ table.insert( s, zString( self.xradius ) )
+ table.insert( s, ', y radius =' )
+ table.insert( s, zString( self.yradius ) )
+ table.insert( s, ']' )
+ return table.concat( s, ' ' )
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- An ellipse part.
+-------------------------------------------------------------------------------
+tikz.ellipseArc =
+immutable( function( def )
+ def.id = 'ellipseArc'
+
+ -- constructor
+ def.constructor =
+ function( args )
+ return {
+ at = args.at,
+ from = args.from,
+ to = args.to,
+ xradius = args.xradius,
+ yradius = args.yradius,
+ }
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ s = { }
+ table.insert( s, 'plot' )
+ table.insert( s, '[domain =' )
+ table.insert( s, self.from )
+ table.insert( s, ':' )
+ table.insert( s, self.to )
+ table.insert( s, ',variable = \\phi,smooth]' )
+ table.insert( s,
+ '({ '..zString(self.at.x)..'+'..zString(self.xradius)..'*cos(\\phi)},'
+ ..'{ '..zString(self.at.y)..'+'..zString(self.yradius)..'*sin(\\phi)})'
+ )
+ return table.concat( s, ' ' )
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- Single line (segment).
+-------------------------------------------------------------------------------
+tikz.line =
+immutable( function( def )
+ def.id = 'line'
+
+ -- moves the line.
+ def.add =
+ function( left, right )
+ if right.id == 'point'
+ then
+ return tikz.line{
+ p1 = left.p1 + right,
+ p2 = left.p2 + right,
+ }
+ end
+ error( 'unknown operation' )
+ end
+
+ -- multiplicator
+ def.mul =
+ function( left, right )
+ if type( left ) == 'number'
+ then
+ return tikz.line{
+ p1 = right.p1 * left,
+ p2 = right.p2 * left,
+ }
+ elseif type( right ) == 'number'
+ then
+ return tikz.line{
+ p1 = left.p1 * right,
+ p2 = left.p2 * right,
+ }
+ end
+ error( 'unknown operation' )
+ end
+
+ -- constructor
+ --
+ -- either
+ -- { p1, p2 }, (a list )
+ -- { p1 = ..., p2 = ... },
+ -- { pc = ..., phi = ..., length = ... },
+ def.constructor =
+ function( args )
+ local p1
+ local p2
+
+ if #args > 1
+ then
+ -- list
+ p1 = args[ 1 ]
+ p2 = args[ 2 ]
+ elseif type( args.p1 ) ~= 'nil' and type( args.p2 ) ~= 'nil'
+ then
+ p1 = args.p1
+ p2 = args.p2
+ elseif args.phi ~= nil
+ then
+ local phi = args.phi
+ if args.pc ~= nil
+ then
+ if args.p1 ~= nil
+ or args.p2 ~= nil
+ then
+ error( 'invalid line options', 2 )
+ end
+ local pc = args.pc
+ local len05 = args.length / 2
+ p1 = pc + tikz.p{ math.cos( phi ), math.sin( phi ) } * len05
+ p2 = pc - tikz.p{ math.cos( phi ), math.sin( phi ) } * len05
+ elseif type( args.p1 ) ~= 'nil'
+ then
+ if args.pc ~= nil
+ or args.p2 ~= nil
+ then
+ error( 'invalid line options', 2 )
+ end
+ p1 = args.p1
+ p2 = p1 + tikz.p{ math.cos( phi ), math.sin( phi ) } * args.length
+ else
+ error( 'invalid line options', 2 )
+ end
+ else
+ error( 'invalid line options', 2 )
+ end
+
+ return {
+ p1 = p1,
+ p2 = p2,
+ }
+ end
+
+ -- divisor
+ def.div =
+ function( left, right )
+ if type( right ) == 'number'
+ then
+ return tikz.line{
+ p1 = left.p1 / right,
+ p2 = left.p2 / right,
+ }
+ end
+ error( 'unknown operation' )
+ end
+
+ -- length of line.
+ def.lazy.length =
+ function( self )
+ local p1 = self.p1
+ local p2 = self.p2
+ local dx = p2.x - p1.x
+ local dy = p2.y - p1.y
+ local result = math.sqrt( dx*dx + dy*dy )
+ return result
+ end
+
+ -- center of line.
+ def.lazy.pc =
+ function( self )
+ local p1 = self.p1
+ local p2 = self.p2
+ local result = tikz.p{ ( p1.x + p2.x ) / 2, ( p1.y + p2.y ) / 2 }
+ return result
+ end
+
+ -- angle of line.
+ def.lazy.phi =
+ function( self )
+ local p1 = self.p1
+ local p2 = self.p2
+ local result = math.atan2( p2.y - p1.y, p2.x - p1.x )
+ return result
+ end
+
+ -- gets point at t (0-1).
+ def.proto.pt =
+ function( self, t )
+ if type( t ) ~= 'number' or t < 0 or t > 1
+ then
+ error( 'invalid pt' )
+ end
+
+ local p1 = self.p1
+ local p2 = self.p2
+ local phi = self.phi
+ local lt = self.length * t
+
+ return(
+ tikz.p{
+ p1.x + lt * math.cos( phi ),
+ p1.y + lt * math.sin( phi ),
+ }
+ )
+ end
+
+ -- intersects the line with another.
+ def.proto.intersectLine =
+ function( self, line )
+ local p1 = self.p1
+ local p2 = self.p2
+ local p3 = line.p1
+ local p4 = line.p2
+
+ if p1.x == p3.x and p1.y == p3.y then return p1 end
+ if p1.x == p4.x and p1.y == p4.y then return p1 end
+ if p2.x == p3.x and p2.y == p3.y then return p2 end
+ if p2.x == p4.x and p2.y == p4.y then return p2 end
+
+ local den = ( p1.x - p2.x )*( p3.y - p4.y ) - ( p1.y - p2.y )*( p3.x - p4.x );
+ if den == 0
+ then
+ -- doesn't intersect
+ return
+ end
+
+ local a = p1.x*p2.y - p1.y*p2.x;
+ local b = p3.x*p4.y - p3.y*p4.x;
+ local x = ( a*( p3.x - p4.x ) - ( p1.x - p2.x )*b ) / den;
+ local y = ( a*( p3.y - p4.y ) - ( p1.y - p2.y )*b ) / den;
+
+ if
+ (
+ ( x >= p1.x and x <= p2.x or x <= p1.x and x >= p2.x )
+ and ( x >= p3.x and x <= p4.x or x <= p3.x and x >= p4.x )
+ )
+ or
+ (
+ ( y >= p1.y and y <= p2.y or y <= p1.y and y >= p2.y )
+ and ( y >= p3.y and y <= p4.y or y <= p3.y and y >= p4.y )
+ )
+ then
+ return tikz.p{ x, y }
+ end
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ local s = { }
+ table.insert( s, zString( self.p1 ) )
+ table.insert( s, '--' )
+ table.insert( s, zString( self.p2 ) )
+ return table.concat( s, ' ' )
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- Function plot.
+-------------------------------------------------------------------------------
+tikz.plot =
+immutable( function( def )
+ def.id = 'plot'
+
+ -- constructor
+ def.constructor =
+ function( args )
+ return {
+ at = args.at,
+ from = args.from,
+ to = args.to,
+ step = args.step,
+ tension = args.tension,
+ func = args.func,
+ }
+ end
+
+ def.lazy.points =
+ function( self )
+ local from = self.from
+ local to = self.to
+ local step = self.step
+ if step == nil
+ then
+ step = ( to - from ) / 100
+ end
+ local points = { }
+ local d
+ local at = self.at or tikz.p0
+ for d = from, to, step
+ do
+ table.insert( points, at + self.func( d ) )
+ end
+ return points
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ local s = { 'plot' }
+ table.insert( s, '[' )
+ table.insert( s, 'smooth' )
+ if self.tension
+ then
+ table.insert( s, ',tension='..self.tension )
+ end
+ table.insert( s, ']' )
+
+ local points = self.points
+
+ table.insert( s, 'coordinates' )
+ table.insert( s, '{' )
+ for _, p in ipairs( points )
+ do
+ table.insert( s, zString( p ) )
+ end
+ table.insert( s, '}' )
+ return table.concat( s, ' ' )
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- A superEllipse
+-------------------------------------------------------------------------------
+tikz.superEllipse =
+immutable( function( def )
+ def.id = 'superEllipse'
+
+ -- constructor
+ def.constructor =
+ function( args )
+ local at = args.at
+ local n = args.n
+ local xradius = args.xradius
+ local yradius = args.yradius
+
+ return {
+ at = at,
+ n = n,
+ xradius = xradius,
+ yradius = yradius,
+ }
+ end
+
+ -- returns p on degree (0-360)
+ def.proto.pdeg =
+ function( self, t )
+ if type( t ) ~= 'number' or t < 0 or t > 360
+ then
+ error( 'invalid pdeg' )
+ end
+
+ local at = self.at
+ local n = self.n
+ local xo = self.xradius * math.abs( math.cos( t * math.pi / 180 ) )^(2/n)
+ local yo = self.yradius * math.abs( math.sin( t * math.pi / 180 ) )^(2/n)
+
+ if t <= 90
+ then
+ return tikz.p{ at.x + xo, at.y + yo }
+ elseif t <= 180
+ then
+ return tikz.p{ at.x - xo, at.y + yo }
+ elseif t <= 270
+ then
+ return tikz.p{ at.x - xo, at.y - yo }
+ else
+ return tikz.p{ at.x + xo, at.y - yo }
+ end
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ local s = { }
+ local at = self.at
+ local xa = at.x
+ local ya = at.y
+ local n = self.n
+ local xr = self.xradius
+ local yr = self.yradius
+ table.insert( s, 'plot[domain=0:90,variable=\\t,smooth]' )
+ table.insert( s, '({'..xr..'*cos(\\t)^(2/'..n..')+'..xa..'},{'..yr..'*sin(\\t)^(2/'..n..')+'..ya..'})' )
+ table.insert( s, '--' )
+ table.insert( s, 'plot[domain=90:0,variable=\\t,smooth]' )
+ table.insert( s, '({-'..xr..'*cos(\\t)^(2/'..n..')+'..xa..'},{'..yr..'*sin(\\t)^(2/'..n..')+'..ya..'})' )
+ table.insert( s, '--' )
+ table.insert( s, 'plot[domain=0:90,variable=\\t,smooth]' )
+ table.insert( s, '({-'..xr..'*cos(\\t)^(2/'..n..')+'..xa..'},{-'..yr..'*sin(\\t)^(2/'..n..')+'..ya..'})' )
+ table.insert( s, '--' )
+ table.insert( s, 'plot[domain=90:0,variable=\\t,smooth]' )
+ table.insert( s, '({'..xr..'*cos(\\t)^(2/'..n..')+'..xa..'},{-'..yr..'*sin(\\t)^(2/'..n..')+'..ya..'})' )
+ table.insert( s, '-- cycle' )
+ return table.concat( s, ' ' )
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- A reference to a named node.
+-------------------------------------------------------------------------------
+local optionsNamed =
+{
+ ref = false,
+ xshift = 'xshift',
+ yshift = 'yshift',
+}
+
+tikz.named =
+immutable( function( def )
+ def.id = 'named'
+
+ -- constructor
+ def.constructor =
+ function( args )
+ local s = { }
+ _doOptions( s, args, optionsNamed, false, 2 )
+ return {
+ opts = table.concat( s, ' ' ),
+ ref = args.ref,
+ }
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ local s = { }
+ table.insert( s, '(' )
+ if self.opts
+ then
+ table.insert( s, self.opts )
+ end
+ table.insert( s, self.ref )
+ table.insert( s, ')' )
+ return table.concat( s )
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- Multiple connected lines.
+-------------------------------------------------------------------------------
+tikz.polyline =
+immutable( function( def )
+ def.id = 'polyline'
+
+ -- constructor
+ def.constructor =
+ function( args )
+ local o = { }
+ for _, v in ipairs( args )
+ do
+ table.insert( o, v )
+ end
+ return o
+ end
+
+ -- converts to a tikz string.
+ def.zString =
+ function( self )
+ local s = { '' }
+ for k, v in ipairs( self )
+ do
+ if k > 1 then table.insert( s, '--' ) end
+ table.insert( s, zString( v ) )
+ end
+ return table.concat( s, ' ' )
+ end
+end )
+
+-------------------------------------------------------------------------------
+-- A rectangle
+-------------------------------------------------------------------------------
+tikz.rect =
+immutable( function( def )
+ def.id = 'rect'
+
+ -- constructor
+ def.constructor =
+ function( args )
+ local psw
+ local pne
+
+ if args.pnw ~= nil
+ then
+ if args.pse ~= nil
+ then
+ psw = tikz.p{ args.pnw.x, args.pse.y }
+ pne = tikz.p{ args.pse.x, args.pnw.y }
+ elseif args.size ~= nil
+ then
+ psw = tikz.p{ args.pnw.x, args.pnw.y - args.size.y }
+ pne = tikz.p{ args.pnw.x + args.size.x, args.pnw.y }
+ else
+ error( 'invalid rect options', 2 )
+ end
+ elseif args.psw ~= nil
+ then
+ psw = args.psw
+ if args.pne ~= nil
+ then
+ pne = args.pne
+ elseif args.size ~= nil
+ then
+ pne = psw + args.size
+ else
+ error( 'invalid rect options', 2 )
+ end
+ elseif args.pse ~= nil
+ then
+ if args.size ~= nil
+ then
+ psw = tikz.p{ args.pse.x - args.size.x, args.pse.y }
+ pne = tikz.p{ args.pse.x, args.pse.y + args.size.y }
+ else
+ error( 'invalid rect options', 2 )
+ end
+ elseif args.pc ~= nil
+ then
+ if args.size ~= nil
+ then
+ psw = tikz.p{ args.pc.x - args.size.x / 2, args.pc.y - args.size.y / 2 }
+ pne = tikz.p{ args.pc.x + args.size.x / 2, args.pc.y + args.size.y / 2 }
+ else
+ error( 'invalid rect options', 2 )
+ end
+ else
+ error( 'invalid rect options', 2 )
+ end
+
+ return {
+ psw = psw,
+ pne = pne,
+ }
+ end
+
+ def.zString =
+ function( self )
+ local s = { }
+ table.insert( s, zString( self.psw ) )
+ table.insert( s, 'rectangle' )
+ table.insert( s, zString( self.pne ) )
+ return table.concat( s, ' ' )
+ end
+
+ def.lazy.e =
+ function( self )
+ return self.pne.x
+ end
+
+ def.lazy.height =
+ function( self )
+ return self.pne.y - self.psw.y
+ end
+
+ def.lazy.n =
+ function( self )
+ return self.pne.y
+ end
+
+ def.lazy.pc =
+ function( self )
+ return tikz.p{ self.psw.x + self.width / 2, self.psw.y + self.height / 2 }
+ end
+
+ def.lazy.pe =
+ function( self )
+ return tikz.p{ self.pne.x, self.psw.y + self.height / 2 }
+ end
+
+ def.lazy.pn =
+ function( self )
+ return tikz.p{ self.psw.x + self.width / 2, self.pne.y }
+ end
+
+ def.lazy.pnw =
+ function( self )
+ return tikz.p{ self.psw.x, self.pne.y }
+ end
+
+ def.lazy.ps =
+ function( self )
+ return tikz.p{ self.psw.x + self.width / 2, self.psw.y }
+ end
+
+ def.lazy.pse =
+ function( self )
+ return tikz.p{ self.pne.x, self.psw.y }
+ end
+
+ def.lazy.pw =
+ function( self )
+ return tikz.p{ self.psw.x, self.psw.y + self.height / 2 }
+ end
+
+ def.lazy.s =
+ function( self )
+ return self.psw.y
+ end
+
+ def.lazy.size =
+ function( self )
+ return tikz.p{ self.width, self.height }
+ end
+
+ def.lazy.w =
+ function( self )
+ return self.psw.x
+ end
+
+ def.lazy.width =
+ function( self )
+ return self.pne.x - self.psw.x
+ end
+end )
+
+
+--==========================================================================--
+-- STYLES --
+--==========================================================================--
+tikz.style =
+immutable( function( def )
+ def.id = 'style'
+
+ -- constructor
+ def.constructor =
+ function( name )
+ return { name=name }
+ end
+end )
+
+tikz.above = tikz.style( 'above' )
+tikz.arrow = tikz.style( '-{Latex}' )
+tikz.below = tikz.style( 'below' )
+tikz.dashed = tikz.style( 'dashed' )
+tikz.dotted = tikz.style( 'dotted' )
+tikz.double_arrow = tikz.style( '{Latex}-{Latex}' )
+tikz.even_odd_rule = tikz.style( 'even odd rule' )
+tikz.midway = tikz.style( 'midway' )
+
+--==========================================================================--
+-- OUTPUT ---
+--==========================================================================--
+
+--
+-- Writes out a named option.
+--
+-- ~s: string to build
+-- ~args: arguments
+-- ~name: name of option
+-- ~haveOpts: true if had options
+-- ~rename: if defined rename options on output
+--
+local function optionNamed( s, args, name, haveOpts, rename )
+
+ local v = args[ name ]
+
+ if not v
+ then
+ return haveOpts
+ end
+
+ if not rename
+ then
+ return haveOpts
+ end
+
+ if not haveOpts
+ then
+ table.insert( s, '[' )
+ else
+ table.insert( s, ',' )
+ end
+
+ if v == true
+ then
+ table.insert( s, rename )
+ else
+ table.insert( s, rename..'='..v )
+ end
+
+ return true
+end
+
+--
+-- Writes out an unnamed option.
+--
+-- ~s: string to build
+-- ~name: name of option
+-- ~haveOpts: true if had options
+--
+local function unnamedOption( s, name, haveOpts )
+ if not haveOpts
+ then
+ table.insert( s, '[' )
+ else
+ table.insert( s, ',' )
+ end
+ table.insert( s, name )
+ return true
+end
+
+--
+-- Custom key compare.
+--
+-- keys to put on top
+local topKeys =
+{
+ node_distance = true,
+}
+
+local function kcompare( ka, kb )
+ local ta = topKeys[ ka ]
+ local tb = topKeys[ kb ]
+ if ta and not tb then return true end
+ if not ta and tb then return false end
+ return ka < kb
+end
+
+--
+-- A helper to parse arguments to pass as drawing options to hand to tikz.
+--
+-- s: string to build
+-- args: the arguments to parse.
+-- opts: named options to pass on
+-- haveOpts: true [ has already been built
+-- level: call level for error throwing
+--
+local function _doOptions( s, args, opts, haveOpts, level )
+ args = flatten( args )
+
+ for _, v in ipairs( args )
+ do
+ if v.id == 'style'
+ then
+ haveOpts = unnamedOption( s, v.name, haveOpts )
+ end
+ end
+
+ local nkeys = { }
+ for key in pairs( args )
+ do
+ if type( key ) ~= 'number'
+ then
+ table.insert( nkeys, key )
+ end
+ end
+ table.sort( nkeys, kcompare )
+
+ for _, k in ipairs( nkeys )
+ do
+ local ok = opts[ k ]
+ if ok
+ then
+ haveOpts = optionNamed( s, args, k, haveOpts, ok )
+ elseif ok == nil
+ then
+ error( 'unknown option '..k, level + 1 )
+ end
+ end
+
+ haveOpts = _opts( s, args._opts, haveOpts )
+
+ if haveOpts
+ then
+ table.insert( s, ']' )
+ end
+end
+
+-------------------------------------------------------------------------------
+-- Drawing shapes.
+-------------------------------------------------------------------------------
+local optionsDraw =
+{
+ decorate = 'decorate',
+ decoration = 'decoration',
+ dash_pattern = 'dash pattern',
+ color = 'color',
+ draw = 'draw',
+ fill = 'fill',
+ line_cap = 'line cap',
+ line_width = 'line width',
+ opacity = 'opacity',
+ pattern = 'pattern',
+ rotate = 'rotate',
+ transform_canvas = 'transform canvas',
+ xshift = 'xshift',
+ yshift = 'yshift',
+ _butt_cap = '-Butt Cap',
+ butt_cap_ = 'Butt Cap-',
+ _round_cap = '-{Round Cap[]}',
+ round_cap_ = '{Round Cap[]}-',
+ _round_cap_ = '{Round Cap[]}-{Round Cap[]}',
+}
+
+--
+-- Puts out a bounding box command
+--
+tikz.boundingbox =
+function( args )
+ tprint( '\\pgfresetboundingbox' )
+ local s = { '\\path [use as bounding box] ' }
+ for _, v in ipairs( args )
+ do
+ if v.id ~= 'style'
+ then
+ table.insert( s, zString( v ) )
+ end
+ end
+ table.insert( s, ';' )
+ tprint( table.unpack( s ) )
+end
+
+--
+-- Puts out a draw command.
+--
+tikz.draw =
+function( args )
+ local s = { '\\draw ' }
+ _doOptions( s, args, optionsDraw, false, 2 )
+
+ for _, v in ipairs( args )
+ do
+ if v.id ~= 'style'
+ then
+ table.insert( s, zString( v ) )
+ end
+ end
+
+ table.insert( s, ';' )
+ tprint( table.unpack( s ) )
+end
+
+--
+-- Puts out a path command.
+--
+tikz.path =
+function( args )
+ local s = { '\\path' }
+ _doOptions( s, args, optionsDraw, false, 2 )
+
+ for _, v in ipairs( args )
+ do
+ if v.id ~= 'style'
+ then
+ table.insert( s, zString( v ) )
+ end
+ end
+ table.insert( s, ';' )
+ tprint( table.unpack( s ) )
+end
+
+--
+-- Puts out a bounding box.
+--
+tikz.clip =
+function( args )
+ local s = { '\\begin{pgfinterruptboundingbox}', '\\clip' }
+ _doOptions( s, args, optionsDraw, false, 2 )
+
+ for _, v in ipairs( args )
+ do
+ if v.id ~= 'style' then table.insert( s, zString( v ) ) end
+ end
+ table.insert( s, ';' )
+ table.insert( s, '\\end{pgfinterruptboundingbox}' )
+ tprint( table.unpack( s ) )
+end
+
+local shadeOptions =
+{
+ ball_color = 'ball color',
+ left_color = 'left color',
+ lower_left_color = 'lower left',
+ lower_right_color = 'lower right',
+ opacity = 'opacity',
+ right_color = 'right color',
+ shading = 'shading',
+ upper_left_color = 'upper left',
+ upper_right_color = 'upper right',
+}
+
+--
+-- Puts out a shade command.
+--
+tikz.shade =
+function( args )
+ local s = { '\\shade' }
+ _doOptions( s, args, shadeOptions, false, 2 )
+
+ for _, v in ipairs( args )
+ do
+ if v.id ~= 'style' then table.insert( s, zString( v ) ) end
+ end
+ table.insert( s, ';' )
+ tprint( table.unpack( s ) )
+end
+
+--
+-- Puts out z stuff.
+--
+tikz.put =
+function( args )
+ for _, v in ipairs( args )
+ do
+ local s = { }
+ if v.id == 'node'
+ then
+ table.insert( s, '\\' )
+ table.insert( s, zString( v ) )
+ table.insert( s, ';' )
+ else
+ error( 'unknown: ' .. v.id, 1 )
+ end
+ tprint( table.unpack( s ) )
+ end
+end
+
+tikz.declarehorizonalshading =
+function( args )
+ local steps = args.steps
+ local name = args.name
+ local s = { '\\pgfdeclarehorizontalshading{' .. name .. '}{100bp}{' }
+
+ local keys = { }
+ for k, _ in pairs( steps )
+ do
+ table.insert( keys, k )
+ end
+
+ table.sort( keys )
+ for _, k in ipairs( keys )
+ do
+ if _ > 1 then table.insert( s, ';' ) end
+ table.insert( s, 'color(' .. k .. 'bp)=(' .. steps[ k ] .. ')' )
+ end
+ table.insert( s, '}' )
+
+ tprint( table.unpack( s ) )
+end
+
+-------------------------------------------------------------------------------
+-- A node to be defined to tikZ
+-------------------------------------------------------------------------------
+local optionsNode =
+{
+ above = 'above',
+ anchor = 'anchor',
+ align = 'align',
+ at = false,
+ below = 'below',
+ color = 'color',
+ draw = 'draw',
+ left = 'left',
+ minimum_height = 'minimum height',
+ node_distance = 'node distance',
+ name = false,
+ right = 'right',
+ rotate = 'rotate',
+ text = false,
+ text_width = 'text width',
+}
+
+tikz.node =
+immutable( function( def )
+ def.id = 'node';
+
+ def.zString =
+ function( self )
+ local s = { 'node' }
+
+ table.insert( s, self.opts )
+ if self.name
+ then
+ table.insert( s, ' (' )
+ table.insert( s, self.name )
+ table.insert( s, ')' )
+ end
+
+ if self.at
+ then
+ table.insert( s, ' at ' )
+ table.insert( s, zString( self.at ) )
+ end
+
+ if self.text
+ then
+ table.insert( s, ' {' )
+ local text = string.gsub( self.text, '\n', '' )
+ table.insert( s, text )
+ table.insert( s, '}' )
+ end
+
+ return table.concat( s )
+ end
+
+ -- constructor
+ def.constructor =
+ function( args )
+ local s = { }
+ _doOptions( s, args, optionsNode, false, 2 )
+ return {
+ at = args.at,
+ name = args.name,
+ opts = table.concat( s, ' ' ),
+ text = args.text
+ }
+ end
+end )
+
+tikz.outfile =
+function( filename )
+ outfile = io.open( filename, 'w' )
+ outfile:write( '%%% DO NOT EDIT THIS FILE %%%\n' )
+end
+
+--
+-- Hacking help.
+--
+function _direct( args )
+ tprint( table.unpack( args ) )
+end
+
+--
+-- Checks version metch.
+--
+tikz.checkVersion =
+function( v )
+ if v ~= tikz.version
+ then
+ error( 'version mismatch', 1 )
+ end
+end
+
+--
+-- Creates a color.
+--
+tikz.colorRGB =
+function( red, green, blue )
+ return '{rgb,255:red,'..red..'; green,'..green..'; blue,'..blue..'}'
+end
+
+--
+-- Makes a tikz scope.
+-- Argument must be a function to call.
+--
+tikz.scope =
+function( args )
+ args = flatten( args )
+ if #args ~= 1 or type( args[ 1 ] ) ~= 'function'
+ then
+ error( 'scope needs a function parameter' )
+ end
+ _direct{ [[\begin{scope}]] }
+ args[ 1 ]( )
+ _direct{ [[\end{scope}]] }
+end
+
+--==========================================================================--
+-- SHORTCUTS
+--==========================================================================--
+tikz.p0 = tikz.p{ 0, 0 }
+
+
+--==========================================================================--
+-- Running code within tikz environment.
+--==========================================================================--
+
+local env_nesting = 0
+local org_env
+tikz.within =
+function( version )
+ if version ~= '*' and version ~= tikz.version
+ then
+ error( 'version mismatch', 1 )
+ end
+
+ env_nesting = env_nesting + 1
+ if env_nesting > 1
+ then
+ return
+ end
+
+ org_env = _ENV
+ local nenv = { }
+ local mt = { }
+ mt.__index =
+ function( _, key )
+ local zk = tikz[ key ]
+ if zk ~= nil
+ then
+ return zk
+ else
+ return org_env[ key ]
+ end
+ end
+ setmetatable( nenv, mt )
+ local func = debug.getinfo( 2 ).func
+ debug.setupvalue( func, 1, nenv )
+end
+
+tikz.without =
+function( )
+ env_nesting = env_nesting - 1
+ if env_nesting > 0 then return end
+ local func = debug.getinfo( 2 ).func
+ debug.setupvalue( func, 1, org_env )
+end
diff --git a/Master/texmf-dist/tex/latex/luatikz/luatikz.sty b/Master/texmf-dist/tex/latex/luatikz/luatikz.sty
new file mode 100644
index 00000000000..5847ab888a1
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/luatikz/luatikz.sty
@@ -0,0 +1,5 @@
+\ProvidesPackage{luatikz}[2024/12/04 2.12.0 Luatikz]
+%This package is just wrapper to load the luatikz code.
+\RequirePackage{luacode}
+\directlua{require('luatikz')}
+\endinput
diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check
index e764f309828..dc132182c92 100755
--- a/Master/tlpkg/bin/tlpkg-ctan-check
+++ b/Master/tlpkg/bin/tlpkg-ctan-check
@@ -545,7 +545,7 @@ my @TLP_working = qw(
luamesh luamml luamodulartables luamplib luanumint luaoptions luaotfload
luapackageloader luaplot luaprogtable luapstricks
luaquotes luarandom
- luaset luasseq luatex85 luatexbase luatexja luatexko luatextra
+ luaset luasseq luatex85 luatexbase luatexja luatexko luatextra luatikz
luatodonotes luatruthtable luavlna luaxml
lutabulartools luwiantype lwarp lxfonts ly1 lyluatex
macrolist macros2e macroswap mafr magaz magicnum magicwatermark magra
diff --git a/Master/tlpkg/libexec/ctan2tds b/Master/tlpkg/libexec/ctan2tds
index 8862dc13a39..fc845661efe 100755
--- a/Master/tlpkg/libexec/ctan2tds
+++ b/Master/tlpkg/libexec/ctan2tds
@@ -2374,6 +2374,7 @@ $standardtex
'luaprogtable','\.lua|' . $standardtex,
'luapstricks', '\.lua|' . $standardtex,
'luatexko', '\.lua|' . $standardtex,
+ 'luatikz', '\.lua|' . $standardtex,
'luatodonotes','\.lua|' . $standardtex,
'luavlna', 'luavlna.*\.lua|luavlna\.tex|' . $standardtex,
'luaxml', '\.lua|' . $standardtex,
diff --git a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
index 88f3edaa7b8..9e1c720cb8a 100644
--- a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
+++ b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
@@ -76,6 +76,7 @@ depend luatex85
depend luatexbase
depend luatexko
depend luatextra
+depend luatikz
depend luatruthtable
depend luavlna
depend luaxml
diff --git a/Master/tlpkg/tlpsrc/luatikz.tlpsrc b/Master/tlpkg/tlpsrc/luatikz.tlpsrc
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/Master/tlpkg/tlpsrc/luatikz.tlpsrc