19

I would like to draw this figure :
enter image description here
Hence, I resort to TikZ. But, it seems to be very difficult drawing arrows precisely in TikZ with the precise locations (x,y). I have achieved this:
enter image description here
Here is the MWE:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\filldraw[color=gray] (1,0) rectangle (10,5);
\draw[->, line width=2pt, color=red] (2,1) -- (3,1);
\draw[->, line width=2pt, color=red] (2,2) -- (3,2.5);
\draw[->, line width=2pt, color=red] (3,2) -- (30:3);
\end{tikzpicture}
\end{document} 

Now, my question is

How to generate arrows of random length (between 2cm and 3cm) and random inclination (if possible of random colors too like red, yellow, violet, etc.) without crossing in TikZ. Is there any function to generate Random Numbers

Note:

If not Tikz, pstricks or MetaPost will also do. For now, Asmptote will not work because there seems an error in the execution in my system.

9
  • I appreciate you accepted my answer, but may suggest you to wait a little longer before accepting an answer? I am sure some other answers will show up. May 22, 2014 at 15:16
  • @GonzaloMedina Can you please edit your answer as the picture in my question. The spacing between arrows is much less. How to improve that? May 22, 2014 at 15:24
  • 1
    I am afraid I don't completely understand. What do you want to change? May 22, 2014 at 15:25
  • Please see the figure "I want to draw this:". In that, the spacing between the arrows is much. How to achieve this spacing? May 22, 2014 at 15:27
  • I'll have to think about this. In my current answer the starting point for each arrow as well as the ending point are randomly chosen so, in its present form, it's not easy to control the spacing. May 22, 2014 at 15:33

6 Answers 6

17

Updated version (without crossing):

A possibility with random colors, widths, directions, lengths; the image was produced using

\RandArrow

\def\Columns{10}
\RandArrow[80]

enter image description here

The code:

\documentclass{article} 
\usepackage{tikz}
\usetikzlibrary{calc}

\pgfdeclarelayer{background}
\pgfsetlayers{background,main}

\def\maxArrow{30}
\def\Columns{6}

\newcommand\randomarrow{
  \pgfmathsetseed{\pdfuniformdeviate 10000000}
  \edef\R{\pdfuniformdeviate 255}
  \edef\G{\pdfuniformdeviate 255}
  \edef\B{\pdfuniformdeviate 255}
  \xdefinecolor{mycolor}{RGB}{\R,\G,\B}
  \tikz\draw[->,line width=2pt*rnd+1pt,color=mycolor] 
    (rnd,rnd) -- ++(rnd*360:rnd+0.2);
}
\newcommand\RandArrow[1][30]{%
  \def\maxArrow{#1}  
  \begin{tikzpicture}
  \foreach [count=\i] \val in {1,...,\maxArrow} 
  { 
    \path  
      let \n{row}={int(mod(\i -1, \Columns))}, 
            \n{col}={ int( ( \i - 1 ) / (-\Columns) ) } 
      in 
         (\n{row}, \n{col}) rectangle  +(1,1) 
         +(0.5, 0.5) node{\randomarrow};
  }
  \begin{pgfonlayer}{background}
  \draw[orange!70!black,line width=1pt,fill=yellow!15]
    (current bounding box.north west)
      rectangle
    (current bounding box.south east);
  \end{pgfonlayer}
  \end{tikzpicture}%
}

\begin{document}

\RandArrow

\def\Columns{10}
\RandArrow[80]

\end{document}

The idea to avoid crossing arrows is to have a grid and place each arrow in one of the squares of the grid.

  1. \maxArrows allows to specify the number of arrows (initially set to 30).

  2. \Columns controls the number of rows of the grid (initially set to 6).

  3. \randomarrow draws an arrow; the width, length, color and direction are chosen randomly using rnd; the length will only be (in the worst case) 0.2cm larger than the width of the square; this is to prevent arrows from having zero length.

  4. The main command is \RandArrow with an optional argument allowing to decide the number of arrows to be drawn; the default value is 30.

  5. As suggested by Paul Gaborit in his answer, \pgfmathsetseed{\pdfuniformdeviate 10000000} was used in the definition of \randomarrow to change the seed used by the pseudo-random generator at each compilation.

Introducing some randomness in the grid gives better results:

\documentclass{article} 
\usepackage{tikz}
\usetikzlibrary{calc}

\pgfdeclarelayer{background}
\pgfsetlayers{background,main}

\def\maxArrow{30}
\def\Columns{6}

\newcommand\randomarrow{
  \pgfmathsetseed{\pdfuniformdeviate 10000000}
  \edef\R{\pdfuniformdeviate 255}
  \edef\G{\pdfuniformdeviate 255}
  \edef\B{\pdfuniformdeviate 255}
  \xdefinecolor{mycolor}{RGB}{\R,\G,\B}
  \tikz\draw[->,line width=2pt*rnd+1pt,color=mycolor] 
    (rnd,rnd) -- ++(rnd*360:rnd+0.1);
}
\newcommand\RandArrow[1][30]{%
  \pgfmathsetseed{\pdfuniformdeviate 10000000}
  \def\maxArrow{#1}  
  \begin{tikzpicture}
  \foreach [count=\i] \val in {1,...,\maxArrow} 
  { 
    \path  
      let \n{row}={ int(mod(\i -1, \Columns))}, 
            \n{col}={ int( ( \i - 1 ) / (-\Columns) ) } 
      in 
         (\n{row}, \n{col}) rectangle  +({random(2,3)},rand) 
         node[near start] {\randomarrow};
  }
  \begin{pgfonlayer}{background}
  \draw[orange!70!black,line width=1pt,fill=yellow!15]
    (current bounding box.north west)
      rectangle
    (current bounding box.south east);
  \end{pgfonlayer}
  \end{tikzpicture}%
}

\begin{document}

\RandArrow

\def\Columns{10}
\RandArrow[80]

\end{document}

enter image description here

11
  • 1
    Just to be sure. Is there any difference between using \pgfmathparse{random()} \pgfmathsetmacro\xpos{\width*\pgfmathresult} or just \width*rnd instead of \xpos?
    – Ignasi
    May 22, 2014 at 15:39
  • @Ignasi since I didn't use any arguments to random(), they should be equivalent. May 22, 2014 at 15:43
  • @Ignasi now I edited my answer using arguments for some of the random and now rnd and random(2,3) behave differently. May 22, 2014 at 15:58
  • Gonzalo I wanted to be sure because I didn't understand why was so much typping needed ;-)
    – Ignasi
    May 22, 2014 at 16:25
  • Please see the edited question @GonzaloMedina May 22, 2014 at 16:25
11

Edit: random non crossing arrows

Here is a very simple solution (using a grid placement) with random colors, random lengths and random directions.

enter image description here

\documentclass[tikz]{standalone}
\pgfmathsetseed{\pdfuniformdeviate 10000000}
\begin{document}
\begin{tikzpicture}
  \fill[black] (0.5,0.5) rectangle (10.5,10.5);
  \foreach \x in {1,...,10} {
    \foreach \y in {1,...,10} {
      % random HSB color definition
      \pgfmathsetmacro\hue{rnd}
      \definecolor{mycolor}{rgb:hsb}{\hue,1,1}
      % random direction and length
      \pgfmathsetmacro\angle{rnd*360}
      \pgfmathsetmacro\halflen{rnd*.3+0.2}
      % arrow
      \draw[line width=2pt,-stealth,color=mycolor]
        (\x,\y) +(\angle:-\halflen) -- +(\angle:\halflen);
    }
  }
\end{tikzpicture}
\end{document} 

First version: random arrows

Here is a possible solution.

The rnd function generates a pseudo-random number between 0 and 1. Thus:

  • (rnd*4+3,rnd*4+3) gives a random coordinate between (3,3) and (7,7).
  • ++(rnd*360:rnd*2+1) moves the previous coordinate in any direction (rnd*360) with a length between 1cm and 3cm.

The random function used with two arguments x and y generates a random integer between x and y. Thus:

  • \pgfmathtruncatemacro\numcolor{random(0,3)} sets \numcolor with a random integer between 0 and 3.

To change the seed used by pseudo-random generator at each compilation, you may use (with pdflatex) \pgfmathsetseed{\pdfuniformdeviate 10000000} in your peamble.

enter image description here

\documentclass[tikz]{standalone}
\pgfmathsetseed{\pdfuniformdeviate 10000000}
\tikzset{
  my arrow/.style={-latex,line width=2},
  my arrow 0/.style={my arrow,color=red},
  my arrow 1/.style={my arrow,color=violet},
  my arrow 2/.style={my arrow,color=yellow},
  my arrow 3/.style={my arrow,color=black},
}
\begin{document}
\begin{tikzpicture}
  \filldraw[color=gray!50] (0,0) rectangle (10,10);
  \foreach \num in {1,...,20} {
    \pgfmathtruncatemacro\numcolor{random(0,3)}
    \draw[my arrow \numcolor] (rnd*4+3,rnd*4+3) -- ++(rnd*360:rnd*2+1);
  }
\end{tikzpicture}
\end{document} 
0
9

Random and not grid-aligned arrows, using the code from tikz: Distribute evenly and randomly circles:

\documentclass{article}
\usepackage{tikz}

\begin{document}
\def\xlist{4}
\def\ylist{4}

\newcommand{\fillrandomly}[4]{
    \pgfmathsetmacro\diameter{#3*2}
    \draw (0-#3,0-#3) rectangle (#1+#3,#2+#3);
    \foreach \i in {1,...,#4}{
        \pgfmathsetmacro\x{rnd*#1}
        \pgfmathsetmacro\y{rnd*#2}
        \xdef\collision{0}
        \foreach \element [count=\i] in \xlist{
            \pgfmathtruncatemacro\j{\i-1}
            \pgfmathsetmacro\checkdistance{ sqrt( ({\xlist}[\j]-(\x))^2 + ({\ylist}[\j]-(\y))^2 ) }
            \ifdim\checkdistance pt<\diameter pt
                \xdef\collision{1}
                \breakforeach
            \fi
        }
        \ifnum\collision=0
            \xdef\xlist{\xlist,\x}
            \xdef\ylist{\ylist,\y}
            \pgfmathsetmacro\randomvalue{rnd*360}
            \draw [red, line width=4pt, -latex] (\x,\y) +(\randomvalue:-#3) -- +(\randomvalue:#3);
        \fi 

    }
}

\begin{tikzpicture}
\pgfmathsetseed{2}
\fillrandomly{7.5}{5}{0.5}{100}

\end{tikzpicture}
\end{document}
2
  • Where is the random length? :-) May 22, 2014 at 19:01
  • @MoneyOrientedProgrammer: Adding random length, thickness, colour, arrowhead, curvature, etc. is trivial, so it does not need to be explicitly stated in the answer.
    – Jake
    May 22, 2014 at 19:03
8
\documentclass{article}

\usepackage{pstricks-add,multido}
\begin{document}

\begin{pspicture}(-6,-6)(6,6)
\pstVerb{ realtime srand }% For different output of every run
\psframe[linecolor=black!15](-5,-5)(5,5)
\multido{\iRow=-4+1}{9}{%
  \multido{\iCol=-4+1}{9}{%
    \rput{!Rand 360 mul}(\iRow,\iCol){%
      \pscustom{%
        \arrows{->}
        \code{Rand Rand Rand setrgbcolor 
           0 Rand 32 mul dup neg 0 exch ArrowB moveto 
           lineto Rand 2 mul setlinewidth stroke}}}}}
\end{pspicture}
\end{document} 

enter image description here

1
  • Please see the edited question @Herbert May 22, 2014 at 16:26
7

A try with MetaPost, using its handy uniformdeviate() random function, which generates a random number between 0 and the number specified in argument. The generated vectors are of random-generated origin, direction and length (between 2 cm and 3 cm), they can't be too close to each other thanks to a bb macro which puts a given vector into a "bounding box" and checks with the intersectiontimes function if they don't intersect, and they are of random colors in the RGB specification.

u := 1cm; % unit length
nb_vectors := 20; wd = .5; 
boolean intersect; pair random_origin; color random_color; path candidate, random_vector[], bb_candidate, bb[];
%
vardef bb(expr p) =
  save orig, extr; pair orig, extr; orig = point 0 of p; extr = point 1 of p;
  pair v,w; v = wd*unitvector(extr-orig)*u; w = v rotated 90;
  (orig - v + w) -- (extr + v + w) -- (extr + v - w) -- (orig - v - w) -- cycle
enddef;
%
beginfig(1);
  pickup pencircle scaled 2bp;
  i = 1; 
  forever: 
    exitunless (i <=  nb_vectors);
    intersect := false;
    random_length := u*(2+uniformdeviate(1));
    random_angle := uniformdeviate(359);
    random_origin := u*(uniformdeviate(wd*nb_vectors), uniformdeviate(wd*nb_vectors));
    random_color := (uniformdeviate(1), uniformdeviate(1), uniformdeviate(1));
    candidate := random_origin -- (random_origin + random_length*dir(random_angle));
    j := 1;
    forever:
      exitif (j = i) or (intersect = true);
      if bb(random_vector[j]) intersectiontimes bb(candidate) <>  (-1, -1):
        intersect := true;
      fi;
      j := j+1;
    endfor;
    if intersect = false: 
      random_vector[i] = candidate;
      drawarrow candidate withcolor random_color;
      i := i+1;
    fi;
  endfor;
endfig;
end.

Here one of the possible result:

enter image description here

4
  • I tried to reuse your code for representation of forces but I don't be able to add labels because a conflit with bb[] definition. Did you know a way to fix that?
    – Jorge
    Jan 31, 2015 at 16:54
  • Not sure if it is what you want, but I've got no conflict of any sort if I place freelabel(btex $v$ etex, point .5 of candidate, point .5 of candidate + dir (random_angle + 90)); just after drawarrow candidate withcolor random_color; (the freelabel macro is part of the Metafun format). Jan 31, 2015 at 19:48
  • In Context don't work.! Declared variable conflicts with previous vardef. <to be read again> ; <*> ...idate, random_vector[], bb_candidate, bb[];
    – Jorge
    Jan 31, 2015 at 22:18
  • I create a new question to [context] guys tex.stackexchange.com/questions/225864/…
    – Jorge
    Jan 31, 2015 at 22:39
4

Modifying the existing PSTricks answer to be more compact, perhaps!

\documentclass[pstricks,border=12pt]{standalone}   

\pstVerb
{
  realtime srand
  /setRGBcolor /setrgbcolor load def
  /setgray { pop tx@Dict begin Rand Rand Rand end setRGBcolor } def
  /Get {8 Rand mul 4 sub} bind def
}

\begin{document}
\begin{pspicture}[arrows=->](-4,-4)(4,4)
\psframe*[linecolor=black!15](-4,-4)(4,4)
\psLoop{100}{\psline(!Get Get)(!Get Get)}
\end{pspicture}
\end{document}

enter image description here

Animated version

\documentclass[pstricks,border=12pt]{standalone}   

\pstVerb
{
  realtime srand
  /setRGBcolor /setrgbcolor load def
  /setgray { pop tx@Dict begin Rand Rand Rand end setRGBcolor } def
    /Get {8 Rand mul 4 sub} bind def
}

\begin{document}
\psLoop{25}{%
\begin{pspicture}[arrows=->,linewidth=2pt](-4,-4)(4,4)
\psframe*[linecolor=black!15](-4,-4)(4,4)
\psLoop{25}{\psline(!Get Get)(!Get Get)}
\end{pspicture}}
\end{document}

enter image description here

4
  • Epilepsy warning! May 22, 2014 at 16:15
  • Please see my edited question May 22, 2014 at 16:27
  • @MoneyOrientedProgrammer: How can you stop the arrows from crossing?
    – Jake
    May 22, 2014 at 19:04
  • 1
    @Jake: By increasing the frame rate so nobody can see they are actually crossing one over others. :-) May 22, 2014 at 19:08

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.