16

I'm drawing a picture in pgf/tikz in which I define some vectors (coordinates) at the start. The picture changes depending on these initial coordinate positions. eg:\coordinate (A) at (3,4);

During the drawing, I need to normalize these vectors. How do I compute the corresponding vector that is a normalized version of A?

0

4 Answers 4

20

Here's one possibility using the ()!<length>!() syntax:

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

\newcommand\normalize[2][(0,0)]{%
  \draw[red,->] #1 -- ($#1!1cm!#2$);}
\begin{document}

\begin{tikzpicture}[>=latex]
\coordinate (a)  at (1,3);
\coordinate (b)  at (-2,5);
\coordinate (c)  at (-0.5,0-.5);
\draw[->,ultra thin] (0,0) -- (b);
\draw[->,ultra thin] (0,0) -- (a);
\draw[->,ultra thin] (a) -- (b);
\normalize{(a)};
\normalize{(b)};
\normalize{($(b)-(a)$)};
\normalize[(a)]{(b)};
\end{tikzpicture}

\end{document}

\normalize has one mandatory argument, and an optional one. \normalize{(x)} will draw the normalized vector corresponding to (x), from the origin. \normalize[(y)]{(x)} is the path from (y) to (y)!1cm!(x); that is, the path that starts in (y) and goes 1cm in the direction from (y) to (x).

enter image description here

For visualization purposes, the original vectors are drawn thin and their normalized versions are red.

5
  • 3
    Nice. The hardcoded 1cm, however, should better be calculated from the actual x and y norm vector values, which might be different from 1cm.
    – Daniel
    Apr 20, 2013 at 6:56
  • @Daniel But then those would be direction dependent if say, x=1,y=2 is given. So better to keep a uniform length for visualizations. Am I getting it right?
    – percusse
    Apr 20, 2013 at 13:34
  • 2
    @percusse Yeah, but: The norm of (<angle>:<distance>) in any coordinate system is: (<angle>:1). In fact, I have managed to write a simple norm cs: which does exactly that (but obviously intended for (<x>,<y>) coordinates). The problem is, though, to get that to work with coordinates/node’s anchors. I can’t find an easy way to calculate this <angle> without already falling back to the basic TeX coordinate system. Apr 20, 2013 at 14:10
  • @Qrrbrbirlbel Sorry I can't see how that happens. In the case I've given, if the unit circle is an ellipse then it's not (<angle>:1) but actually an angle dependent (<angle>:<r(angle)>). Actually it seems that it's not even nice visually to have dependent lengths for unit vectors.
    – percusse
    Apr 20, 2013 at 14:29
  • What if instead of 1cm, I want 1 of whatever unit my axis cs is using?
    – Eric
    May 29, 2017 at 13:14
8

Here's a solution with a to path. The first argument is the length of the offset. It defaults to 1cm.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\tikzset{offset/.style={to path={%
    -- ($(\tikztostart)!#1cm!(\tikztotarget)$)}},
         offset/.default=1}
\begin{document}
\begin{tikzpicture}
   \draw[help lines] (0,0) grid (3,3);
   \draw[red,->]  (0,0) to[offset=2]   (100,0);
   \draw[blue,->] (0,0) to[offset]     (0,2);
   \draw[->]      (1,1) to[offset=1.5] (1,2);
\end{tikzpicture}
\end{document}

Three offset vectors

0
7

I have decided to create a custom coordinate system, norm cs:. The needed macros and two keys are packaged in the library norm (→ \usetikzlibrary{norm}).

Keys:

  • norm is a to path (similar to the ones already discussed in other answers) that uses the norm cs to create an normalized vector.
  • Norm can be used to change the length of the normalized vector.

    The default is 1 in the current coordinate system. This has the advantage that any change in the coordinate system (the x and y keys can be used to change the coordinate system) the normalized vector is still of the length of 1 (and not 1cm in the paper plane which is used with in other answers).

    The key, though, can also be used to set a fixed length (in the paper plane) which is used then.

Without any change in the coordinate system and with the default Norm=1 we get for Example 1

Example 1 a

With

x={(1.5cm,1cm)},
y={(-1.5cm,1cm)}

we get

Example 1b

instead of (Norm=1cm)

Example 1c

norm cs:

The norm cs accepts as parameter any TikZ <coordinate> and an optional from <another coordinate> (the <another coordinate> is by default (0,0), the origin). This will result in the normalized (according to Norm setting) vector.

Note that this does not draw any lines in any relation to the current point on the path.

\draw (1,2) -- (norm cs: 3,0);

will result in a line from (1,2) to (1,0) and not (2,2) (case A) nor the point that is 1cm from (1,2) to (3,0) (case B). The norm cs will most likely be used with relative coordinates. The following will draw a line from (1,2) to (2,2) (case A):

\draw (1,2) -- ++(norm cs: 3,0);

If one wants to specify the point that is 1cm from (1,2) to (33,0) one needs to use the from parameter

\draw (1,2) -- ++(norm cs: 3,0 from 1,2);

or the norm to path:

\draw (1,2) to[norm] (3,0);

enter image description here

Code

tikzlibrarynorm.code.tex

\tikzset{
    Norm/.code={%
        \pgfmathparse{#1}%
        \edef\qrr@tikz@norm{\pgfmathresult\ifpgfmathunitsdeclared pt\fi}%
        \let\ifqrr@tikz@normcs@unitsdeclared\ifpgfmathunitsdeclared},
    Norm=1,
    norm/.style={to path={-- ++(norm cs: \tikztotarget\space from \tikztostart)}}
}
\newif\ifqrr@tikz@normcs@unitsdeclared
\newdimen\qrr@pgf@normcs@from@x\newdimen\qrr@pgf@normcs@from@y
\tikzdeclarecoordinatesystem{norm}{%
    \let\qrr@next\relax
    \pgfutil@in@{ from }{#1}%
    \ifpgfutil@in@
        \qrr@tikz@normcs@parse@from#1\@qrr@tikz@normcs@parse@from
        \let\qrr@next\qrr@tikz@normcs@calc@hard
    \else
        \qrr@tikz@normcs@parse@from#1 from +0pt,+0pt\@qrr@tikz@normcs@parse@from
    \fi
    \expandafter\qrr@tikz@normcs@parse@@to\expandafter{\qrr@tikz@normcs@parse@to}%
}
\def\qrr@tikz@normcs@calc@easy#1{%
    \pgf@xx\@ne
    \pgf@yy\@ne
    \pgf@xy\z@
    \pgf@yx\z@
    \tikz@scan@one@point\pgfutil@firstofone(#1)\relax
    \pgfmathatantwo{+\pgf@x}{+\pgf@y}%
}
\def\qrr@tikz@normcs@calc@hard#1{%
    \tikz@scan@one@point\pgfutil@firstofone(#1)\relax
    \pgf@xa\pgf@x\pgf@ya\pgf@y
    \advance\pgf@xa-\qrr@pgf@normcs@from@x
    \advance\pgf@ya-\qrr@pgf@normcs@from@y
    \pgfpointnormalised{\pgfqpoint{\pgf@xa}{\pgf@ya}}
    \pgfmathsetlength\pgf@xa{\pgf@yy*\pgf@x-\pgf@yx*\pgf@y}
    \pgfmathsetlength\pgf@ya{-\pgf@xy*\pgf@x+\pgf@xx*\pgf@y}
    \pgfmathatantwo{+\pgf@xa}{+\pgf@ya}
}
\def\qrr@tikz@normcs@calc@unit#1{%
    \tikz@scan@one@point\pgfutil@firstofone(#1)\relax
    \pgf@xa\pgf@x\pgf@ya\pgf@y
    \advance\pgf@xa-\qrr@pgf@normcs@from@x
    \advance\pgf@ya-\qrr@pgf@normcs@from@y
    \pgfpointnormalised{\pgfqpoint{\pgf@xa}{\pgf@ya}}
    \pgfmathatantwo{+\pgf@x}{+\pgf@y}
}
\def\qrr@tikz@normcs@parse@@to#1{%
    \ifqrr@tikz@normcs@unitsdeclared
        \let\qrr@next\qrr@tikz@normcs@calc@unit
    \else
        \ifx\qrr@next\relax
          \pgfutil@in@{cs:}{#1}%
          \ifpgfutil@in@
            \let\qrr@next\qrr@tikz@normcs@calc@hard
          \else
            \pgfutil@in@{intersection }{#1}%
            \ifpgfutil@in@
              \let\qrr@next\qrr@tikz@normcs@calc@hard
            \else
              \pgfutil@in@|{#1}%
              \ifpgfutil@in@
                \let\qrr@next\qrr@tikz@normcs@calc@hard
              \else
                \pgfutil@in@:{#1}%
                \ifpgfutil@in@
                  \let\qrr@next\qrr@tikz@normcs@calc@easy
                \else
                  \pgfutil@in@,{#1}%
                  \ifpgfutil@in@
                    \let\qrr@next\qrr@tikz@normcs@calc@easy
                  \else
                    \let\qrr@next\qrr@tikz@normcs@calc@hard
                  \fi
                \fi
              \fi
            \fi
          \fi
        \fi
    \fi
    \begingroup
        \qrr@next{#1}%
        \pgfmath@smuggleone\pgfmathresult
    \endgroup
    \let\pgf@tempa\pgfmathresult
%   \pgfmathifthenelse{\pgf@tempa<0}{\pgf@tempa+180}{\pgf@tempa}%
%   \let\pgf@tempa\pgfmathresult
    \ifqrr@tikz@normcs@unitsdeclared
        \pgfpointpolar{\pgf@tempa}{\qrr@tikz@norm}%
    \else
        \pgfpointpolarxy{\pgf@tempa}{\qrr@tikz@norm}%
    \fi
}
\def\qrr@tikz@normcs@parse@from#1 from #2\@qrr@tikz@normcs@parse@from{%
    \def\qrr@tikz@normcs@parse@to{#1}%
    \tikz@scan@one@point\pgfutil@firstofone(#2)\relax
    \qrr@pgf@normcs@from@x\pgf@x
    \qrr@pgf@normcs@from@y\pgf@y
}

Example 1

\documentclass[tikz]{standalone}
\usetikzlibrary{backgrounds,norm}
\begin{document}
\begin{tikzpicture}[
    gridded,
%   Norm=1cm,
%   x=-2cm,
%   x={(1.5cm,1cm)},
%   y={(-1.5cm,1cm)}
    ]

\draw (0,0)  coordinate[label=below:$O$] (O) circle (1);
\draw (1,3)  coordinate[label=right:$a$] (a) circle (1);
\draw (-2,5) coordinate[label=left:$b$] (b) circle (1);

\draw (O) -- (b);\draw (O) -- (a);\draw (a) -- (b);

\tikzset{every path/.append style={green,thick}}
\path[->] (O)   edge ++(norm cs: b)
                edge ++(norm cs: a)
                ;
\path[->] (a)   edge[blue] ++(norm cs: b)
                edge[red]  ++(norm cs: a)
                edge[norm] (b) % equal to edge ++(norm cs: b from a)
                edge       ++(norm cs: O from a)
                ;

\path[->] (b)   edge[red]  ++(norm cs: b)
                edge[blue] ++(norm cs: a)
                edge       ++(norm cs: a from b)
                edge       ++(norm cs: O from b)
                ;
\end{tikzpicture}
\end{document}
0
6

Here a way without calc. I used \pgfpointnormalised to get a vector with a width of 1pt.

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

\makeatletter  
  \def\vectornormalised#1#2{%
\begingroup 
   \coordinate (tempa) at (#1);
   \coordinate (tempb) at (#2);
  \pgfpointdiff{\pgfpointanchor{tempa}{center}}%
               {\pgfpointanchor{tempb}{center}}%
  \pgfpointnormalised{} 
  \pgf@xa=28.45274\pgf@x%
  \pgf@ya=28.45274\pgf@y% 
  \draw[red,->] (#1)-- +(\the\pgf@xa,\the\pgf@ya) coordinate (tempc);
\endgroup
}

 \tikzset{norm/.style={to path={%
  \pgfextra{\vectornormalised{\tikztostart}{\tikztotarget}} (tempc) -- (\tikztotarget) \tikztonodes
   }}}
\makeatother 

\begin{document}
\begin{tikzpicture}[>=latex]
\draw[help lines] (0,0) grid (5,3);
\coordinate (a)  at (1,0);
\coordinate (b)  at (5,1);
\coordinate (c)  at (2,3);
\draw[blue,->] (a) to [norm] (b);
\draw[orange,->] (b) to[norm] (c);
\draw[green,->] (a)  to [norm] (c);
\draw (a) circle (1cm);
\end{tikzpicture}
\end{document}

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .