1

I'm a big fan of TikZ and I've used it a lot to make technical diagrams, but I've recently started gaining interest in a more artistic usage, namely drawing maps.

As far as I can see, there's not really a package for drawing the type of map I'd like to have, so I started out with something simple: drawing a symbol for a mountain. Here's what I came up with:

\newcommand\mountain[2][1.] {%
\begin{scope}{scale=#1}
\draw[decorate,decoration={random steps,segment length=3pt,amplitude=1pt,pre length=1pt,post length=1pt}] (#2) ++ (-.5,-0.25) --++ (.5,.5) --++ (.5,-.5);
\end{scope}
}

The result looks like this, which I'm fairly happy with: mountain

Here are a few trees:

\newcommand\leaftree[2][1.] {%
  \begin{scope}{scale=#1}
    \pgfmathparse{0.2+.3*rnd}    
    \pgfmathsetmacro\stem{\pgfmathresult}
    \pgfmathparse{0.2+.3*rnd}
    \pgfmathsetmacro\height{\pgfmathresult}
    \pgfmathparse{0.2+.3*rnd}
    \pgfmathsetmacro\width{\pgfmathresult}
    \draw[very thick] (#2) --++ (0,\stem);
    \draw[decorate,decoration={random steps,segment length=2pt,amplitude=.3pt,pre length=.1pt,post length=1pt}] ($(#2)+(0,\stem)+(0,\height)$) ellipse ({\width} and {\height});
  \end{scope}
}

\newcommand\conifer[2][1.] {%
  \begin{scope}{scale=#1}
    \pgfmathparse{0.2+.1*rnd}    
    \pgfmathsetmacro\base{\pgfmathresult}    
    \pgfmathparse{0.3+.5*rnd}    
    \pgfmathsetmacro\top{\pgfmathresult}
    \pgfmathparse{20+30*rnd}    
    \pgfmathsetmacro\branchangle{\pgfmathresult}    
    \pgfmathparse{0.03+.03*rnd}    
    \pgfmathsetmacro\branchdist{\pgfmathresult}        
    \foreach\x in {0,\branchdist,...,\top}{
      \pgfmathparse{0.1+.5*\x+0.03*rnd};
      \draw ($(#2)+(0,\base)+(0,\top)-(0,\x)$) --++(-\branchangle:\pgfmathresult);
      \draw ($(#2)+(0,\base)+(0,\top)-(0,\x)$) --++(\branchangle:-\pgfmathresult);
    }
    \draw[very thick,shorten >=.3pt] (#2) --++(0,\base) --++ (0,\top);
  \end{scope}
}

trees

Similarly, I envision to have symbols cities, etc. and use the random steps key with various settings to draw coastlines, rivers, and more.

However, one crucial aspect is the ability to fill areas with these objects. For a mountain range, for example, I'd like to just define the outer area and have it randomly filled with mountains of different size.

I've looked around at different questions asking for randomly filling areas with symbols (mainly circles, for example this one: Filling specified area by random dots in TikZ), but I found it very hard to adapt either of these solutions from the simple-shaped areas they are intended to fill to possibly complicated shapes without cutting individual symbols in half. I'd like to have a solution for randomly filling areas such that

  • Symbols are either displayed whole or not at all
  • The density and size of the symbols can vary
  • The symbols never overlap
  • The patterns can be easily adapted to skew the randomness in a certain direction, for example displaying larger mountains in the middle of the mountain ridge, or displaying higher density of trees towards the middle of the forest.

I've played around for a few hours but couldn't come up with a solution that was even close to satisfactory, so I'm coming here for some ideas and guidance.

Is there some package that can already do what I'm looking for that I didn't find, and this was just an exercise in drawing with TikZ for me? Or do you think this is a viable route to go? How would one go about randomly filling an area with these symbols?

EDIT:

Since it has been requested, here one of my attempts, based off of this answer: Custom and built in TikZ fill patterns

\documentclass{standalone}
\usepackage{tikz}

\usetikzlibrary{patterns}
\usetikzlibrary{decorations.pathmorphing}

\makeatletter
\newlength{\maxmountainsize}
\newlength{\minmountainsize}
\newlength{\mountainspread}
\tikzset{maxmountainsize/.code={\setlength{\maxmountainsize}{#1}},
         minmountainsize/.code={\setlength{\minmountainsize}{#1}},
         mountainspread/.code={\setlength{\mountainspread}{#1}}}
\tikzset{maxmountainsize=.2cm,
         minmountainsize=.3cm,
         mountainspread=1cm}
\pgfdeclarepatternformonly[\mountainspread,\maxmountainsize]
  {mountainrange}
  {\pgfpointorigin}
  {\pgfqpoint{\mountainspread}{\mountainspread}}
  {\pgfqpoint{\mountainspread}{\mountainspread}}
  {
    \pgfmathsetmacro\thismountainsize{rnd*(\maxmountainsize-\minmountainsize)+\minmountainsize}
    \draw[decorate,decoration={random steps,segment length=3pt,amplitude=1pt,pre length=1pt,post length=1pt}] (0,0) --++ (\thismountainsize pt,\thismountainsize pt) --++ (\thismountainsize pt,-\thismountainsize pt);    
  }
  \makeatother

\begin{document}

\begin{tikzpicture}
  \draw[fill=red,pattern=mountainrange] (0,0) to [bend right] (10,0) to[bend right] (0,0);  
\end{tikzpicture}%
\end{document}

fill pattern

My problems here are:

  • The mountains are not randomized - every one is the same
  • The mountain positions are not randomized - is this even possible with pattern?
  • The mountains are cut off where the drawing area ends.
  • There is no way to steer that larger mountains should be displayed in the center as opposed to at the edges of the area.

One other possible solution is shown here (tikz: Distribute evenly and randomly circles), but this is not expressed as a fill pattern, and can thus not easily be restricted to a certain area - and I fear that by using clipping paths to overcome this limitation, I will always end up with the symbols themselves being clipped.

  • May I advertize to look at tex.stackexchange.com/a/483739/121799, where some random paths are used to randomly fill an area? And may I ask your to provide us with a complete example that starts with \documentclass and ends with \end{document} and a concrete target (mountains or trees or ... )? – user121799 Apr 14 at 13:56
2

You can check if a random point is inside the contour by counting the number of intersections of a line from outside to the point with the contour. This allows you to draw a mountain only if its starting point is inside the contour. This avoids the outside part being clipped away.

\documentclass[tikz,border=3.14mm]{standalone}

\usetikzlibrary{decorations.pathmorphing,intersections,calc}

\newlength{\maxmountainsize}
\newlength{\minmountainsize}
\newlength{\mountainspread}
\tikzset{maxmountainsize/.code={\setlength{\maxmountainsize}{#1}},
         minmountainsize/.code={\setlength{\minmountainsize}{#1}},
         mountainspread/.code={\setlength{\mountainspread}{#1}}}
\tikzset{maxmountainsize=.4cm,
         minmountainsize=.2cm,
         mountainspread=1cm}

\begin{document}

\begin{tikzpicture}
  \begin{scope}[local bounding box=my shape]
  \draw[name path global=boundary] (0,0) to [bend right] (10,0) to[bend right] (0,0); 
  \end{scope}
  \begin{scope}[shift={(my shape.south west)},
   x={($(my shape.south east)-(my shape.south west)$)},
   y={($(my shape.north west)-(my shape.south west)$)}]
   \pgfmathsetseed{12}
  \foreach \X in {1,...,20} 
  {\pgfmathsetmacro{\myX}{rnd}
  \pgfmathsetmacro{\myY}{rnd}
  \path (\myX,\myY) coordinate (aux);
  \path[overlay,name path=test] ([yshift=1cm]my shape.north) --(aux);
  \pgfmathsetmacro\thismountainsize{rnd*(\maxmountainsize-\minmountainsize)+\minmountainsize}
  \typeout{(\myX,\myY):\thismountainsize}
  \path[name intersections={of=boundary and test,total=\t}]
   \ifodd\t
     [draw,decorate,decoration={random steps,segment length=3pt,amplitude=1pt,
     pre length=1pt,post length=1pt}] 
     (\myX,\myY) --++ (\thismountainsize pt,\thismountainsize pt) --++ (\thismountainsize pt,-\thismountainsize pt);    
   \fi   ;
    }
 \end{scope}    
\end{tikzpicture}%
\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.