--
-- 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
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
-------------------------------------------------------------------------------
-- 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
-- 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
}
-- 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'
-- 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
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
-------------------------------------------------------------------------------
-- 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
-- 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 )
-- 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 )
-- 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',
}
-- 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 )
-- 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 )
--
-- 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
--
-- 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
--
-- 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',
}
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
--==========================================================================--
-- 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