7

I have a right triangle △ABC with its right angle at C, and the altitude drawn to the hypotenuse. I have code that should draw two right-angle marks at C and at the foot of the altitude. The similar commands display slightly different right-angle marks. The one at the foot of the altitude is correct. The one at C is not perpendicular to side BC. I have drawn a green line segment to illustrate this.

\documentclass{amsart}
\usepackage{amsmath}
\usepackage{amsfonts}

\usepackage{tikz}
\usetikzlibrary{calc,intersections}

\usepackage{mathtools}

\begin{document}

\begin{tikzpicture}

%A right triangle is drawn.
\path (0,0) coordinate (A) ({(1/4)*16},0) coordinate (B) ({(1/4)*12},{(1/4)*(4*sqrt(3))}) coordinate (C);
\draw (A) -- (B) -- (C) -- cycle;

%The vertices are labeled.
\node[anchor=north, inner sep=0, font=\footnotesize] at ($(A) +(0,-0.15)$){$A$};
\node[anchor=north, inner sep=0, font=\footnotesize] at ($(B) +(0,-0.15)$){$B$};
\node[anchor=south, inner sep=0, font=\footnotesize] at ($(C) +(0,0.15)$){$C$};

%A right-angle mark is drawn at C.
\coordinate (U) at ($(C)!5mm!45:(A)$);
\draw ($(A)!(U)!(C)$) -- (U) -- ($(B)!(U)!(C)$);
\draw[green] let \p1=($(B)-(C)$), \n1={atan(\y1/\x1)} in (U) -- ($(U) +({\n1+180}:0.5)$);
\draw[fill=green] ($(A)!(U)!(C)$) circle (1pt);


%The foot of the altitude is labeled F.
\coordinate (F) at ({(1/4)*(12)},0);
\draw[dashed] (F) -- (C);

%A right-angle mark is drawn at F.
\coordinate (V) at ($(F)!3.25mm!-45:(A)$);
\draw ($(A)!(V)!(B)$) -- (V) -- ($(C)!(V)!(F)$);

\end{tikzpicture}
\end{document}

enter image description here

Heiko Oberdiek
  • 271,626

3 Answers3

9

As pointed out by Mark Wibrow in this answer the definition of \pgfpointnormalised can be corrected to obtain better precision.

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc, spy}

% use the Mark Wibrow's correction
\makeatletter
\def\pgfpointnormalised#1{%
  \pgf@process{#1}%
  \pgfmathatantwo{\the\pgf@y}{\the\pgf@x}%
  \let\pgf@tmp=\pgfmathresult%
  \pgfmathcos@{\pgf@tmp}\pgf@x=\pgfmathresult pt\relax%
  \pgfmathsin@{\pgf@tmp}\pgf@y=\pgfmathresult pt\relax%
}
\makeatother

\begin{document}

\begin{tikzpicture}[spy using outlines={circle, magnification=7, size=17mm, connect spies}]

%A right triangle is drawn.
\path (0,0) coordinate (A) ({(1/4)*16},0) coordinate (B) ({(1/4)*12},{(1/4)*(4*sqrt(3))}) coordinate (C);
\draw (A) -- (B) -- (C) -- cycle;

%The vertices are labeled.
\node[anchor=north, inner sep=0, font=\footnotesize] at ($(A) +(0,-0.15)$){$A$};
\node[anchor=north, inner sep=0, font=\footnotesize] at ($(B) +(0,-0.15)$){$B$};
\node[anchor=south, inner sep=0, font=\footnotesize] at ($(C) +(0,0.15)$){$C$};

%A right-angle mark is drawn at C.
\coordinate (U) at ($(C)!5mm!45:(A)$);
\draw ($(A)!(U)!(C)$) -- (U) -- ($(B)!(U)!(C)$);
\draw[green] let \p1=($(B)-(C)$), \n1={atan(\y1/\x1)} in (U) -- ($(U) +({\n1+180}:0.5)$);
\draw[fill=green] ($(A)!(U)!(C)$) circle (1pt);


%The foot of the altitude is labeled F.
\coordinate (F) at ({(1/4)*(12)},0);
\draw[dashed] (F) -- (C);

%A right-angle mark is drawn at F.
\coordinate (V) at ($(F)!3.25mm!-45:(A)$);
\draw ($(A)!(V)!(B)$) -- (V) -- ($(C)!(V)!(F)$);

\spy[red] on (2.7,1.5) in node at (1,1.7);

\end{tikzpicture}
\end{document}

enter image description here

Kpym
  • 23,002
  • 1
    I will wait for a response to the " feature request" by Rokas. I can issue commands to draw the right-angle mark that does not use the calc package. Thanks for the web site. Now I know that this is a flaw in pgf. – A gal named Desire Oct 15 '16 at 20:44
  • I will wait also! that error should be fixed as soon as possible, because projection modifiers are often used – Black Mild Sep 29 '18 at 13:49
  • 1
    This has been fixed upstream: https://sourceforge.net/p/pgf/git/ci/65b8261c63c6f4adfca60106528e2d1bf1e2cb60/ – Henri Menke Feb 28 '19 at 08:03
5

The calculation of projection involves \pgfpointnormalised{⟨point⟩}, which is documented as follows

This command returns a normalised version of ⟨point⟩, that is, a vector of length 1pt pointing in the direction of ⟨point⟩. If ⟨point⟩ is the 0-vector or extremely short, a vector of length 1pt pointing upwards is returned. This command is not implemented by calculating the length of the vector, but rather by calculating the angle of the vector and then using (something equivalent to) the \pgfpointpolar command. This ensures that the point will really have length 1pt, but it is not guaranteed that the vector will precisely point in the direction of ⟨point⟩ due to the fact that the polar tables are accurate only up to one degree. Normally, this is not a problem.

So this is not a precision issue but an algorithmic feature. In particular, including fpu-engine is not necessary helpful.

One can simply rewrite the calculation by redefining \tikz@cc@after@project, solving the issue once and for all.

\documentclass{amsart}
\usepackage{amsmath}
\usepackage{amsfonts}

\usepackage{tikz}
\usetikzlibrary{calc,intersections}

\usepackage{mathtools}


\makeatletter
\newdimen\pgf@xd
\newdimen\pgf@yd
\def\tikz@cc@after@project#1{%
  \pgf@process{#1}%
  % Ok, now we need to project (xc,yc) on the line (xb,xc) to (x,y)
  \advance\pgf@x by-\pgf@xb%
  \advance\pgf@y by-\pgf@yb%
  \advance\pgf@xc by-\pgf@xb%
  \advance\pgf@yc by-\pgf@yb%
  % Scalar product
  \pgf@xd=\pgf@sys@tonumber{\pgf@xc}\pgf@x%
  \advance\pgf@xd by\pgf@sys@tonumber{\pgf@yc}\pgf@y%
  \pgf@yd=\pgf@sys@tonumber{\pgf@x}\pgf@x%
  \advance\pgf@yd by\pgf@sys@tonumber{\pgf@y}\pgf@y%
  \divide\pgf@xd by\pgf@sys@tonumber{\pgf@yd}
  % and add
  \advance\pgf@xb by\pgf@sys@tonumber{\pgf@xd}\pgf@x%
  \advance\pgf@yb by\pgf@sys@tonumber{\pgf@xd}\pgf@y%
  \tikz@cc@mid@checks%
}
\makeatother

\begin{document}

\begin{tikzpicture}

%A right triangle is drawn.
\path (0,0) coordinate (A) ({(1/4)*16},0) coordinate (B) ({(1/4)*12},{(1/4)*(4*sqrt(3))}) coordinate (C);
\draw (A) -- (B) -- (C) -- cycle;

%The vertices are labeled.
\node[anchor=north, inner sep=0, font=\footnotesize] at ($(A) +(0,-0.15)$){$A$};
\node[anchor=north, inner sep=0, font=\footnotesize] at ($(B) +(0,-0.15)$){$B$};
\node[anchor=south, inner sep=0, font=\footnotesize] at ($(C) +(0,0.15)$){$C$};

%A right-angle mark is drawn at C.
\coordinate (U) at ($(C)!5mm!45:(A)$);
\draw ($(A)!(U)!(C)$) -- (U) -- ($(B)!(U)!(C)$);
\draw[green] let \p1=($(B)-(C)$), \n1={atan(\y1/\x1)} in (U) -- ($(U) +({\n1+180}:0.5)$);
\draw[fill=green] ($(A)!(U)!(C)$) circle (1pt);


%The foot of the altitude is labeled F.
\coordinate (F) at ({(1/4)*(12)},0);
\draw[dashed] (F) -- (C);

%A right-angle mark is drawn at F.
\coordinate (V) at ($(F)!3.25mm!-45:(A)$);
\draw ($(A)!(V)!(B)$) -- (V) -- ($(C)!(V)!(F)$);

\end{tikzpicture}
\end{document}

P.S.

Package tkz-euclide implements orthogonal projection by calculating the intersection of two specific lines. This involves even more arithmetics and they are done by package fp.

After all, I guess it is still a good practice to fix the calc library instead of asking users to abandon it.

Symbol 1
  • 36,855
  • I have used such code to draw right-angle marks hundreds of times. The lengths of the line segments are 3mm or 3.5mm. This is the only time that the calc package did not compile the code correctly. – A gal named Desire Oct 15 '16 at 18:58
  • To draw the right-angle mark at C, I decided to avoid using the calc package. See my comments to Martin. – A gal named Desire Oct 15 '16 at 18:58
  • @AgalnamedDesire I am glad to hear that. Because after all calc is nothing but syntactic sugar. And most of the time I rather do the calculation myself. – Symbol 1 Oct 15 '16 at 19:06
0

Try to reverse the order in coordinate calculation:

\draw ($(C)!(U)!(A)$) -- (U) -- ($(C)!(U)!(B)$);

I played around with your example and found this to work for me. To be honest: I don't know why.