%D \module %D [ file=mp-sketch.mp %D version=2021.05.13 %D title=\CONTEXT\ \METAPOST\ graphics, %D subtitle=Sketch drawing, %D author=Aditya Mahajan, %D date=\currentdate, %D copyright={Aditya Mahajan}] %D This metapost module is inspired by a TeX.SE question: %D http://tex.stackexchange.com/q/39296/323 %D %D I thought that it would be fun to implement a similar feature in MetaPost. %D %D To use this package in MetaPost: %D %D \starttyping %D input mp-sketch; %D %D beginfig(1) %D sketchypaths; % Make draw and fill sketchy %D ... %D naturalizepaths; % Restore the value of draw and fill %D ... %D endfig %D \stoptyping %D %D The code is heavily inspired by Hans Hagen's Metafun macros. %D %D The macro \type{sketchypaths} is modeled after \type{visualizepaths} from %D \filename{mp-tool}. def sketchypaths = let draw = sketchdraw ; let fill = sketchfill ; enddef ; %D Check if \filename{mp-tool} is loaded if not known context_tool : let normaldraw = draw; let normalfill = fill; def naturalizepaths = let fill = normalfill ; let draw = normaldraw ; enddef ; fi %D The variable \type{sketch_amount} determines the amount of randomness in the %D drawing numeric sketch_amount; sketch_amount := 0.75bp; %D The variable \type{sketch_passes} determines the number of times the path %D is drawn numeric sketch_passes; sketch_passes := 1; %D Based on \type{randomized}. Assumes p is path: numeric sketch_segments; sketch_segments := 20; %D Length (time) of line segments: numeric sketch_length; sketch_length := 5mm; primarydef p sketchrandomized s = ( if path p : for t = 0 step 1/sketch_segments until 1-1/sketch_segments : ((point (t*arclength(p)) on p) randomshifted s) .. controls ((postcontrol (t*arclength(p)) on p) randomshifted s) and ((precontrol ((t+1/sketch_segments)*arclength(p)) on p) randomshifted s) .. endfor % TODO: beide Ansätze kombinieren. Eckpunkte erhalten! % for t = 0 step sketch_length until arclength p: % (point (arctime t of p) of p) randomshifted s .. % endfor if cycle p : % funktioniert nicht cycle else : ((point (arclength(p)) on p) randomshifted s) %(point (arctime t of p) of p) randomshifted s fi else : p fi ) enddef ; %D The macro \type{sketchdraw} draws the randomized path. The %D \type{expr} ... \type{text} trick is copied from the definition of %D \type{drawarrow} def sketchdraw expr p = do_sketchdraw(p) enddef; def do_sketchdraw(expr p) text t = if (path p) : for i = 1 upto max(1,sketch_passes) : normaldraw p sketchrandomized sketch_amount withtransparency ("multiply", 1/max(1,sketch_passes)) t ; endfor; else : normaldraw p t; fi enddef; %D The macro \type{sketchfill} randomizes the path before filling it. def sketchfill expr p = do_sketchfill(p) enddef ; def do_sketchfill(expr p) text t = if (path p) : for i = 1 upto max(1,sketch_passes) : normalfill p sketchrandomized sketch_amount withtransparency ("multiply", 1/max(1,sketch_passes)) t ; endfor; else : normalfill p t; fi enddef; picture NoisePattern; NoisePattern := image( pickup pencircle xyscaled 0.5bp; numeric pmax ; pmax := 7 ; numeric x ; numeric y ; for i = 1 upto pmax: for j = 1 upto pmax: % try to get more dots in the center x := (i - (pmax/2) * (uniformdeviate 2.5)); % we use a wide pen y := (j - (pmax/2)) * (uniformdeviate 1.25); draw (x, y) withcolor "LineColor" withtransparency ("normal", (uniformdeviate 0.33)); endfor endfor ); numeric brush_length ; brush_length := 20 ; %numeric brush_width ; brush_width := 0.5 ; picture BrushPattern; def makeBrush(expr brush_width) = BrushPattern := image( naturalizepaths; pickup pensquare xyscaled 0.25bp; numeric xmax, ymax, dev ; ymax := 7 ; xmax := round(ymax/2 * brush_width); numeric x ; numeric y ; for i = 1 upto xmax: for j = 1 upto ymax: x := (i - (xmax/2)); y := (j - (ymax/2)); % * (uniformdeviate 1.25); dev := (uniformdeviate 1)-0.5; draw (x-dev, y-(uniformdeviate brush_length))--(x+dev, y+(uniformdeviate brush_length)) withcolor "LineColor" withtransparency ("normal", (uniformdeviate 0.25)); endfor endfor ); enddef; numeric noise_steps ; noise_steps := 150; numeric nrush_steps ; brush_steps := 30; def noisify(expr p) = for i = 0 step 1/noise_steps until length p: draw NoisePattern randomized 1 shifted point i of p ; endfor enddef; def brushify(expr p, w) = makeBrush(w); for i = 0 step 1/brush_steps until length p: draw BrushPattern randomized 1 rotated (90 + angle direction i of p) shifted point i of p ; endfor enddef; endinput; % Modified example from % http://tex.loria.fr/prod-graph/zoonekynd/metapost/metapost.html beginfig(1) pair A,B,C,O; A=(0,0); B=(3cm,0); C=(1cm,2cm); O - 1/2[B,C] = whatever * (B-C) rotated 90; O - 1/2[A,B] = whatever * (A-B) rotated 90; sketchypaths; sketch_amount := 5bp; draw A--B--C--cycle; draw O withpen pencircle scaled 4bp; sketch_amount := 2bp; draw fullcircle scaled 2abs(O-A) shifted O; endfig