17

I'd like to draw a rectilinear path by starting at a particular location, and then prescribing increments in the horizontal and vertical directions.

I am currently doing something like this

\documentclass{standalone}
\usepackage{tikz}

\begin{document}
\begin{tikzpicture}
    \draw [ultra thick] (20,12) -- (18,12);
    \draw [ultra thick] (18,12) -- (18,10);
    \draw [ultra thick] (18,10) -- (16,10);
    \draw [ultra thick] (16,10) -- (16,8);
    \draw [ultra thick] (16,8) -- (12,8);
    \draw [ultra thick] (12,8) -- (12,10);
    \draw [ultra thick] (12,10) -- (8,10);
    \draw [ultra thick] (8,10) -- (8,12);
    \draw [ultra thick] (8,12) -- (6,12);
    \draw [ultra thick] (6,12) -- (6,14);
    \draw [ultra thick] (6,14) -- (4,14);
    % etc ...

\end{tikzpicture}
\end{document}

enter image description here

But this requires that I locate each point, when what is much easier (for my particular case) is to just indicate the horizontal or vertical distance from one point to the next.

I have tried something like this

\documentclass{standalone}
\usepackage{tikz}

\begin{document}

\begin{tikzpicture}
   \node (A) at (20,12) {};
   \node (B) at (18,12) {};
   \node (C) at (18,10) {};
   \node (D) at (16,10) {};

   \draw (A) |- ++ (-2,0)  -| (B);
   \draw (B) |- ++ (0, -2) -| (C);
   \draw (C) |- ++ (-2, 0) -| (D);
   % etc
\end{tikzpicture}
\end{document}

It looks like the ++ (-2,0) bit seems to be on the right track, but naming the nodes is tedious, and this doesn't produce what I have in mind.

Is there some way to do what I have in mind with simple notation? Something like

Start at (20,12)
Go left 2 units
Go down 2 units
Go left 2 units
% etc

Aka, Etch-a-Sketch style?

3 Answers 3

22

\draw (20,12) -- ++(2,0) -- ++(0,2) -- ++(-3,0) -- ++(45:3);

Use ++ before each new incremental coordinate to make it relative to the last one and put the pencil there.

Here's a complete example:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\tikz\draw (20,12) -- ++(2,0) -- ++(0,2) -- ++(-3,0) -- ++(30:3) {[rounded corners=10pt]-- ++(5,0) -- ++(0,-6)} -- ++(-7,0) -- cycle;
\end{document}

enter image description here

Of course, combining this with the -| or |- path operators can simplify the code even further; the following two pieces of code produce the same result:

\tikz\draw (20,12) -- ++(2,0) -- ++(0,2) -- ++(3,0) -- ++(0,1) -- ++(1,0) -- ++(0,-3) -- ++(2,0);\par\bigskip

and

\tikz\draw (20,12) -| ++(2,2) -| ++(3,1) -- ++(1,0) |- ++(2,-3);

I don't think that defining commands in this case adds anything; in fact, I think it reduces the functionality of the existing syntax (which is already simple). The example demonstrates that you can use, for example, polar coordinates and modify (up to TikZ limitations) the path attributes midways; even if the current question doesn't require this, it's a good thing to have the possibility to do those kind of modification if they are required.

5
  • Thanks - this is in fact exactly what I wanted. And it doesn't have the problem that segments aren't meeting up exactly at the corners. Perfect! Commented Sep 26, 2015 at 21:57
  • 1
    @Donna You're welcome. Please see my updated answer. Commented Sep 26, 2015 at 23:25
  • Thanks! So it looks like there are a couple of new features here, at least one of which I was wondering about. It looks like you can change the line style midstream, which is very handy, and exactly something I wanted to do. I want to fill the resulting rectilinear polygons, so always need closed polygons, but only want portions of the border to be, say, ultra thick. So I'll try your trick above for getting rounded corners midway. But what does the (30:3) notation do? And cycle? Commented Sep 27, 2015 at 11:14
  • Trying this out, I see that the (30:3) indicates an angle (30 degrees), and cycle means "return to the beginning" (or so it seems). Turns out, though, it is not possible to change the line style midway. Oh well. Commented Sep 27, 2015 at 11:50
  • @Donna Yes, (<angle>:<distance>) has the meaning you guesses, and yes, line width (as well as color and other attributes) cannot be changed midway for a single path due to TikZ limitations. Commented Sep 27, 2015 at 14:56
25

Not sure it is what is required, but here is a rectilinear decoration which sort of achieves the required effect:

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{decorations}

% Define a new decoration named 'rectilinear' that converts smooth paths into stair-step patterns.
% The decoration uses four states, with the following transitions:
% - "start"      -> "draw above" 
% - "draw above" -> "draw below" or "final"  
% - "draw below" -> "draw above" or "final"
% - "final"     
% The decoration also uses two nodes to store the position of the start and end of each stair-step:
% - @1: Start point of segement.
% - @2: End point of segment.
\pgfdeclaredecoration{rectilinear}{start}{%
  % Initial state: Set up the starting point and store it
  \state{start}[%
    % Set "width" to half of the given segment length, because we step through two states per segments.
    width=\pgfdecorationsegmentlength/2,
    % Start with "draw above".  
    next state=draw above]{%
    % Move to start of segment
    \pgfpathmoveto{\pgfpointorigin}%
    % Store the origin as a node named @1.
    \pgfcoordinate{@1}{\pgfpointorigin}%
  }
  
  % State for drawing vertical-then-horizontal segments (creates upper stairs)
  \state{draw above}[width=\pgfdecorationsegmentlength/2, 
    next state=draw below]{%
    % Store end point of segment
    \pgfcoordinate{@2}{(\pgfpointorigin)}%
    % Reset any transformations
    \pgftransformreset%
    % Get coordinates of the start point
    \pgfpointanchor{@1}{center}%
    \pgfgetlastxy\a\b%
    % Get coordinates of the end point
    \pgfpointanchor{@2}{center}%
    \pgfgetlastxy\c\d%
    % Add horizontal line segment. In "pgfqpoint", "q" stands for "quick", meaning coordinates are used as-is without transformation.
    \pgfpathlineto{\pgfqpoint{\a}{\d}}%
    % Add vertical line segment
    \pgfpathlineto{\pgfqpoint{\c}{\d}}%
    % Make "@1" and alias for the @2 node, effectively renaming the node so that it becomes the start node for the next step.
    \pgfnodealias{@1}{@2}%
  }
  
  % State for drawing horizontal-then-vertical segments (creates lower stairs)
  \state{draw below}[width=\pgfdecorationsegmentlength/2, 
    next state=draw above]{%
    % Store end point of segment
    \pgfcoordinate{@2}{\pgfpointorigin}%
    % Reset any transformations
    \pgftransformreset%
    % Get coordinates of start point
    \pgfpointanchor{@1}{center}\pgfgetlastxy\a\b%
    % Get coordinates of end point
    \pgfpointanchor{@2}{center}\pgfgetlastxy\c\d%
    % Add vertical line segment
    \pgfpathlineto{\pgfqpoint{\c}{\b}}%
    % Add horizontal line segment
    \pgfpathlineto{\pgfqpoint{\c}{\d}}%
    % Update start point for next segment
    \pgfnodealias{@1}{@2}%
  }
  
  % Final state: Handle the last segment of the path
  \state{final}{%
    % Reset any transformations
    \pgftransformreset%
    % Get last stored point
    \pgfpointanchor{@1}{center}\pgfgetlastxy\a\b%
    % Get very end of path
    \pgfpointdecoratedpathlast\pgfgetlastxy\c\d%
    % Add final horizontal line segment
    \pgfpathlineto{\pgfqpoint{\a}{\d}}%
    % Add final vertical line segment
    \pgfpathlineto{\pgfqpoint{\c}{\d}}%
  }
}

% Define a TikZ style that applies the rectilinear decoration.
\tikzset{rectilinear/.style={
  decoration={rectilinear, #1}, decorate
}}

\begin{document}
\begin{tikzpicture}[very thick, line join=round, line cap=round]
  % Example 1: Two connected line segments.
  \draw [gray, postaction={rectilinear, draw=red}]
    (0,4) -- ++(30:2) -- ++(300:3);
    
  % Example 2: Circle
  \draw [gray, postaction={rectilinear, draw=green!50!black}]
    (0,2) circle [radius=1];
    
  % Example 3: Complex path with custom segment length ("segment length=1.25cm") for larger stair-steps
  \draw  [gray, postaction={rectilinear={segment length=1.25cm}, draw=blue}]
    (0,0) -- (3,1) arc (90:-90:1) .. controls ++(180:1) and ++(225:1) .. cycle;
\end{tikzpicture}
\end{document}

enter image description here

6

Here four macro \Start, \Goleft, \Godown and \Goup to creat the desired path.

Code

\documentclass[border=5mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}

\tikzset{every node/.style={inner sep=0pt,minimum size=0pt}}

\newcommand{\Start}[1]{\node (A) at #1 {};}
\newcommand{\Goleft}[1]{\draw($(A)+( -0.5\pgflinewidth ,0.5\pgflinewidth)$)--+(#1,0)node(A){};}
\newcommand{\Godown}[1]{\draw($(A)+(-0.5\pgflinewidth,0)$)--+(0,-#1)node(A){};}
\newcommand{\Goup}[1]{\draw($(A)+(-0.5\pgflinewidth,0)$)--+(0,#1)node(A){};}

\begin{document}

\begin{tikzpicture}
   \Start{(20,12)}
   \Goleft{2}
   \Godown{2}
   \Goleft{3}
   \Godown{1}
   \Goleft{4}
   \Goup{3}
\end{tikzpicture}

\end{document}

Output

enter image description here

5
  • Thanks - that helps alot. i'd like to also adjust the line width, which did by adding `[line width=8pt] for example. The problem, though is that the lines don't match up, and so the line edges are visible at the corners. it seems like I need to add something like a small filled square at each corner. Commented Sep 26, 2015 at 21:10
  • Just add line width=1pt (or what satisfy you) in the front of tikzpicture environment like this \begin{tikzpikture}[line width=1pt] Commented Sep 26, 2015 at 21:11
  • Yes, I did that. But it still seemed to suffer from this problem that the corner segments don't match up. The solution from @GonzaloMedina doesn't have this problem. Commented Sep 26, 2015 at 22:02
  • My bad. I fixed a bug, and this solution works just as the one below. Thanks! Commented Sep 26, 2015 at 22:12
  • Nice implementation. Funny that you chose to call Goleft the macro which actually go... right! ^^ Commented Jan 12, 2021 at 21:39

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.