# Simulating hand-drawn lines

I am working on a project that will have some vector graphics, perhaps using TikZ (or a similar package). All of the graphics consists of lines, in shades of gray. However, the lines from TikZ appear too clean.

Is there any way to make the lines appear as if they were produced by a graphite pencil?

-
Tried the same a couple of month ago, but never came up with a Latex solution. Found a simple code example using google, so maybe it might help: stevehanov.ca/blog/index.php?id=33 –  Christian Dec 25 '11 at 20:50

I'm posting this just because you mentioned specifically the lines.

I modified the bent decoration to make it look like more of a hand drawing. It truly has problems and you can't use it on curves for now... well you can but the result is unexpected at the least.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,decorations.pathmorphing,patterns}

\makeatletter

\pgfdeclaredecoration{penciline}{initial}{
\state{initial}[width=+\pgfdecoratedinputsegmentremainingdistance,auto corner on length=1mm,]{
\pgfpathcurveto%
{% From
\pgfqpoint{\pgfdecoratedinputsegmentremainingdistance}
{\pgfdecorationsegmentamplitude}
}
{%  Control 1
\pgfmathrand
{\pgfqpoint{-\pgfdecorationsegmentaspect\pgfdecoratedinputsegmentremainingdistance}%
{\pgfmathresult\pgfdecorationsegmentamplitude}
}
}
{%TO
}
}
\state{final}{}
}
\makeatother
\begin{document}
\begin{tikzpicture}[decoration=penciline]
\draw[decorate,style=help lines] (-2,-2) grid[step=1cm] (4,4);
\draw[decorate,thick] (0,0) -- (0,3) -- (3,3);
\draw[decorate,ultra thick,blue] (3,3)  arc (0:-90:2cm); %% This is supposed to be an arc!!
\draw[decorate,thick,pattern=north east lines] (-0.4cm,-0.8cm) rectangle (1.2,-2);
\node[decorate,draw,inner sep=0.5cm,fill=yellow,circle] (a) at (2,0) {}; %% That's not even an ellipse !!
\node[decorate,draw,inner sep=0.3cm,fill=red] (b) at (2,-2) {};
\draw[decorate] (b) to[in=-45,out=45] (a); %% This was supposed to be an edge!!
\node[decorate,draw,minimum height=2cm,minimum width=1cm] (c) at (-1.5,0) {};
\draw[decorate,->,dashed] (-0.5cm,-0.5cm) -- (-0.5cm,3.5cm)  -| (c.north);
\end{tikzpicture}
\end{document}


Here is the output:

I'm currently studying the markings decorations to see how one can move along a path without giving explicit coordinates on the curve. That would hopefully make it possible to use it on arcs. Note that the background grid is also decorated :)

-
Is there a way to "fix" the lines once they are drawn? If using this with overlays in beamer slides, the lines change on every slide that is generated. –  user38931 Dec 5 '13 at 12:42
@user38931 You can fix the seed of the random number generator. See example here tex.stackexchange.com/questions/144621/… to start with and replace the seed with a fixed number. I think you can take it from there. –  percusse Dec 5 '13 at 13:49
Beautiful!!! Any updates on how to use it on arcs? –  Sergio Parreiras Aug 16 at 20:01
@SergioParreiras I'll start this weekend sorry for the delay. –  percusse Sep 16 at 1:38
@percusse: Please never apologize to me. I will never be able to repay all the stuff I learned with your posts here. –  Sergio Parreiras Sep 16 at 15:04

Only for the fun, I took Marc's suggestion and I create a new decoration free hand with fixed parameters, a new style free hand and a new macro freedraw

\documentclass[border=1cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing}
\begin{document}

\pgfdeclaredecoration{free hand}{start}
{
\state{start}[width = +0pt,
next state=step,
persistent precomputation = \pgfdecoratepathhascornerstrue]{}
\state{step}[auto end on length    = 3pt,
auto corner on length = 3pt,
width=+2pt]
{
\pgfpathlineto{
{\pgfpoint{2pt}{0pt}}
{\pgfpoint{rand*0.15pt}{rand*0.15pt}}
}
}
\state{final}
{}
}
\tikzset{free hand/.style={
decorate,
decoration={free hand}
}
}
\def\freedraw#1;{\draw[free hand] #1;}

\begin{tikzpicture}
\freedraw [->](-5,0) -- (5,0);
\freedraw [->](0,-5) -- (0,5);

\freedraw[fill=blue!15,fill opacity=.25]  (-3,-3) rectangle (3,3);
\freedraw[free hand,red,shift={(1,1)},rotate=45,fill=red!25,fill opacity=.5] %
(1,1) -- (1,-1) -- (-1,-1) -- (-1,1) -- (1,1);
\freedraw[color=blue]   plot (\x,{sin(\x r)}) node [free hand,draw] {$\sin(x)$} ;
\end{tikzpicture}
\end{document}


-

This is another attempt to simulate hand drawn curves in Metapost. This is heavily inspired by the MetaFun macros. The main advantage of this approach is that you don't have change your drawing macros at all. Simply input the file and call sketchypaths, and all paths become a bit sketchy.

Save the following as mp-sketch.mp

%D The variable \type{sketch_amount} determines the amount of randomness in the
%D drawing
numeric sketch_amount; sketch_amount := 3bp;

%D The macro \type{sketchdraw} randomized the path before drawing it. The
%D \type{expr} ... \type{text} trick is copied from the definition of
%D \type{drawarrow}

def sketchdraw expr p =
do_sketchdraw(p if (path p): randomized sketch_amount fi)
enddef;

def do_sketchdraw(expr p) text t =
normaldraw p t ;
enddef;

%D The macro \type{sketchfill} randomizes the path before filling it.
path _sketch_path_;

def sketchfill expr p =
_sketch_path_ := p randomized sketch_amount;
do_sketchfill
enddef ;

def do_sketchfill text t =
normalfill _sketch_path_ t ;
enddef ;

%D The macro \type{sketchypaths} is modeled after \type{visualizepaths} from
%D \filename{mp-tool}.

def sketchypaths =
let draw = sketchdraw ;
let fill = sketchfill ;
enddef ;


The main macro is sketchypaths which changes the definition of draw and fill to be sketchy. To recover the normal behavior use the naturalizepahts macro from metafun.

To use these, you also need to load mp-tool.mkiv. I'll show a test file in ConTeXt, which already loads the MetaFun macros.

\startMPinclusions
input mp-sketch;
\stopMPinclusions

\starttext

\startMPpage[offset=3mm]
sketchypaths;

draw (0,0) -- (1cm,0) -- (1cm,1cm);

fill fullsquare scaled 1cm shifted (3cm,3cm) withcolor red;

drawarrow (1cm,1cm) -- (2cm,2cm);

stripe_path_a
(withcolor red)
(draw)
fullcircle xscaled 100 yscaled 40 shifted (0, 2.5cm) withcolor blue;

\stopMPpage

\stoptext


which gives

Since the sketchypaths macro changes the draw and fill commands, it is easy to combine this with existing MetaPost packages. For example, to use boxes.mp:

\startMPdefinitions
input mp-sketch;
input boxes;
\stopMPdefinitions

\starttext
\startMPpage[offset=3mm]
sketchypaths;

boxit.one   (btex One etex);
boxit.two   (btex Two etex);
boxit.three (btex Three etex);

three.w - two.e = two.w - one.e = (1cm,0);
one.c = origin;

drawboxed (one, two, three);

drawarrow one.e -- lft two.w;
drawarrow two.e -- lft three.w;

\stopMPpage
\stoptext


The figure on the top shows the output without sketchypaths and the figure on the bottom shows the output with sketchyparts.

And here is a slightly tweaked version of example 83 of metapost examples

In my biased opinion, this looks more natural than many of the other solutions.

-
It looks like a fact to me rather than a bias. –  percusse Feb 4 '13 at 9:13
@percuße now that you have seen its power, you must come over to the dark side :) –  Aditya Feb 4 '13 at 15:07
Like the blue-light fly zapper, I'm drawn to it. –  percusse Feb 5 '13 at 2:50

In terms of regular (straight) lines, pstricks-add offers

\pslineByHand[<options>](<x1>,<y1>)(<x2>,<y2>)(<x3>,<y3>)...


that draws a straight line with some "jagging." The "jagging" parameters that influence the line drawing are VarStepEpsilon (default is 2) and varsteptol (default is 0.8).

\documentclass{article}
\usepackage{xcolor}% http://ctan.org/pkg/xcolor
\begin{document}
\begin{pspicture}(11,3)
\psset{unit=1cm,linewidth=0.5pt,linecolor=black!70,linejoin=1}
\psline(0,0)(0,2)(2,2)(2,0)(0,0)(2,2)(1,3)(0,2)(2,0)% Regular HOUSE with straight lines
\rput(3,0){\pslineByHand(0,0)(0,2)(2,2)(2,0)(0,0)(2,2)(1,3)(0,2)(2,0)}
\rput(6,0){\pslineByHand[VarStepEpsilon=4](0,0)(0,2)(2,2)(2,0)(0,0)(2,2)(1,3)(0,2)(2,0)}
\rput(9,0){\pslineByHand[varsteptol=0.2](0,0)(0,2)(2,2)(2,0)(0,0)(2,2)(1,3)(0,2)(2,0)}
\end{pspicture}
\end{document}


This requires a latex->dvips->ps2pdf or xelatex compile sequence.

-

Here are two pens with MetaPost (using ConTeXt format), I feel the code is pretty self-explanatory:

\starttext
\startMPenvironment
color lightgray ; lightgray := (.8,.8,.8) ;
color darkgray ; darkgray := (.2,.2,.2) ;
\stopMPenvironment

Pen based on a circle:
\startMPcode
z0 = (0.5cm,1.5cm) ; z1 = (2.5cm,2.5cm) ;
z2 = (6.5cm,0.5cm) ; z3 = (3.0cm,1.5cm) ;

pickup pencircle xscaled 2mm yscaled 4mm rotated 30 ;
draw z0..z1..z2..z3..z0..cycle withcolor lightgray ;
\stopMPcode

\bigskip
Pen based on a diamond:
\startMPcode
z0 = (0.5cm,1.5cm) ; z1 = (2.5cm,2.5cm) ;
z2 = (6.5cm,0.5cm) ; z3 = (3.0cm,1.5cm) ;

pickup pensquare xyscaled 2mm rotated 45 ;
draw z0..z1..z2..z3...z0..cycle withcolor darkgray ;
\stopMPcode
\stoptext


This is the first time I have used MetaPost with ConTeXt, and was happy how to see how easy it is to use.

-

There are lots of nice answers here addressing how to get that "hand drawn" look with the shape of the lines. I'm going to sketch an approach to getting the texture right. I spent a bit of time this afternoon staring at some lines drawn with a 9B pencil on paper and decided that what it looked most like was a brass rubbing. That is, the stroke of the pencil reveals the texture of the paper underneath. This is most like a fading in TikZ/PGF parlance.

The cheap and nasty paper that I buy doesn't have much of a grain to it (I have kids, they do art, I buy about 5 reams every six months). The pattern looks more like noise. So having learnt about Perlin noise recently, I thought that would be an appropriate thing to simulate the paper texture with. I soon decided that a TeX implementation would be daft, so went hunting for a Lua version and found one of the closely related Simplex noise (also due to Perlin; the link goes to a Lua implementation; the author uses strong language both in comments and function names). So I hacked that into a Lua file for LuaTeX and wrote a little demonstration LaTeX file.

I'm calling this an approach because it needs considerable work to be useful. For one thing, even though I'm using Lua to do the heavy processing then it takes time to render the fading. So given that noise isn't usually required to be too random, one should do a lot of caching: both of the original generated numbers and the graphic used for the fading itself. There's also a fair bit about fadings that I don't understand, particularly related to how the fading and the picture match up.

What should happen is that one uses one fading for the whole picture so that strokes laid over each other end up using the same noise and thus work as though they are on top of the same piece of paper. This would also need a little work to do with positioning.

The code isn't too complicated. Here's the LaTeX file:

\documentclass{article}
\usepackage{tikz}

\directlua{
dofile('tikzgraphite.lua')
}

\makeatletter
\tikzset{show bounding box/.code={
\message{^^JBounding box is (\the\pgf@picminx,\the\pgf@picminy) to       (\the\pgf@picmaxx,\the\pgf@picmaxy)^^J}
}
}
\makeatother

\begin{document}
\foreach \i in {0,...,160} \foreach \j in {0,...,40}
\fill[transparent!  \directlua{tex.print(math.floor(50*(Noise2D(\i,\j)+1)))}] (\i pt, \j pt) rectangle (\i + 1 pt, \j + 1 pt);
\begin{tikzpicture}
\tikzset{show bounding box}
\end{tikzpicture}
\end{document}


The show bounding box key is so that I can get an idea of how big to make the fading. I don't want to scale it as I want a good texture, and I don't want to generate it too big as it takes a looooonnnnnggggg time to render.

Here's the lua file:

local Gradients3D = {{1,1,0},{-1,1,0},{1,-1,0},{-1,-1,0},
{1,0,1},{-1,0,1},{1,0,-1},{-1,0,-1},
{0,1,1},{0,-1,1},{0,1,-1},{0,-1,-1}}

local simplex = {
{0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,3,0},
{0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,3,2,0},
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
{1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0},
{1,0,2,3},{1,0,3,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0},
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
{2,0,1,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0},
{2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1},{3,2,1,0}}

local p = {151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180}

for i=1,#p do
p[i-1] = p[i]
p[i] = nil
end

end

local perm = {}

for i=0,255 do
perm[i] = p[i]
perm[i+256] = p[i]
end

function Dot2D(tbl, x, y)
return tbl[1]*x + tbl[2]*y;
end

local Prev2D = {}

-- 2D simplex noise

function Noise2D(xin, yin)
if Prev2D[xin] and Prev2D[xin][yin] then return Prev2D[xin][yin] end

local n0, n1, n2; -- Noise contributions from the three corners
-- Skew the input space to determine which simplex cell we're in
local F2 = 0.5*(math.sqrt(3.0)-1.0);
local s = (xin+yin)*F2; -- Hairy factor for 2D
local i = math.floor(xin+s);
local j = math.floor(yin+s);
local G2 = (3.0-math.sqrt(3.0))/6.0;

local t = (i+j)*G2;
local X0 = i-t; -- Unskew the cell origin back to (x,y) space
local Y0 = j-t;
local x0 = xin-X0; -- The x,y distances from the cell origin
local y0 = yin-Y0;

-- For the 2D case, the simplex shape is an equilateral triangle.
-- Determine which simplex we are in.
local i1, j1; -- Offsets for second (middle) corner of simplex in (i,j) coords
if(x0>y0) then
i1=1
j1=0  -- lower triangle, XY order: (0,0)->(1,0)->(1,1)
else
i1=0
j1=1 -- upper triangle, YX order: (0,0)->(0,1)->(1,1)
end

-- A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
-- a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
-- c = (3-sqrt(3))/6

local x1 = x0 - i1 + G2; -- Offsets for middle corner in (x,y) unskewed coords
local y1 = y0 - j1 + G2;
local x2 = x0 - 1.0 + 2.0 * G2; -- Offsets for last corner in (x,y) unskewed coords
local y2 = y0 - 1.0 + 2.0 * G2;

-- Work out the hashed gradient indices of the three simplex corners
local ii = i%256
local jj = j%256
local gi0 = perm[ii+perm[jj]] % 12;
local gi1 = perm[ii+i1+perm[jj+j1]] % 12;
local gi2 = perm[ii+1+perm[jj+1]] % 12;

-- Calculate the contribution from the three corners
local t0 = 0.5 - x0*x0-y0*y0;
if t0<0 then
n0 = 0.0;
else
t0 = t0 * t0
n0 = t0 * t0 * Dot2D(Gradients3D[gi0], x0, y0); -- (x,y) of Gradients3D used for 2D gradient
end

local t1 = 0.5 - x1*x1-y1*y1;
if (t1<0) then
n1 = 0.0;
else
t1 = t1*t1
n1 = t1 * t1 * Dot2D(Gradients3D[gi1], x1, y1);
end

local t2 = 0.5 - x2*x2-y2*y2;
if (t2<0) then
n2 = 0.0;
else
t2 = t2*t2
n2 = t2 * t2 * Dot2D(Gradients3D[gi2], x2, y2);
end

-- Add contributions from each corner to get the final noise value.
-- The result is scaled to return values in the localerval [-1,1].

local retval = 70.0 * (n0 + n1 + n2)

if not Prev2D[xin] then Prev2D[xin] = {} end
Prev2D[xin][yin] = retval

return retval;
end


It is almost entirely just clipped out from the lua implementation linked above. There's no obvious licence information there, but consider it covered under whatever-licence-it-would-be-there.

Here's the result of that code:

-
@cjorssen I hadn't seen it, thanks for bringing to my attention. Something to work on, I deem. –  Loop Space Dec 8 '12 at 20:06

Based on Marcs solution I tried to mimic the ps-tricks results (randomized/imprecise coordinates) shown by Werner using tikz:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing}
\begin{document}
\begin{tikzpicture}[pencildraw/.style={
black!75,
decorate,
decoration={random steps,segment length=2pt,amplitude=0.3pt},
to path={ -- ([xshift=rand*1mm, yshift=rand*1mm] \tikztotarget) \tikztonodes}
}
]
\draw[pencildraw] (0,0) to (0,2) to (2,2) to (2,0) to (0,0) to (2,2) to
(1,3) to (0,2) to (2,0);

\end{tikzpicture}
\end{document}


Maybe someone can improve this, i.e. find a more general way to randomize coordinates, as this solution for example will not randomize all corners of a rectangle automatically.

-

You might want to create a brush (possibly with transparency) and apply it repeatedly on the paths created by the other replies, possibly with some transformations of the brush itself to make it appear even more random. Intensity effects can be achieved with a highly transparent brush and changing "velocity" of the path movement (or changing frequency of applying the brush).

See how a graphite effect is created in this drawing pad, or the airbrush tool in GIMP. I am not an expert in graphics so I cannot give much advice here; my feeling is that you will need to experiment quite a bit to achieve the desired result. (On that note: A sample drawing would help understand your question better.)

You can use marking decorations (Section 30.4 in the PGF manual). There is also an example that draws arrows along a path, so I'm pretty sure you can do the same thing with a vector or bitmap brush. This has been borrowed from the manual:

\begin{tikzpicture}[decoration={markings,
mark=between positions 0 and 1 step 1cm
with { \node [single arrow,fill=red,
single arrow head extend=3pt,transform shape] {};}}]

\draw [help lines] grid (3,2);
\draw [postaction={decorate}] (0,0) -- (3,1) arc (0:180:1.5 and 1);

\end{tikzpicture}


The size of the PDF will not be affected much because TikZ can be instructed to load the brush only once. However, rendering may take quite a while.

-