29

I'm trying to draw a resultant force diagram like so:

\documentclass{article}
\usepackage{tikz}
\begin{document}
    \begin{tikzpicture}
    \draw (0,0) -- (90:2.7) -- ++ (-13:12) 
        node[shape=rectangle,pos=0.6,sloped,above,draw,pin=270:$mgcos \theta$] 
        {5kg} -- ++(0:-11.6);
    \draw[dashed] (0,2.7) -- (13:11.6);
    \draw (2,2.7) arc (13:0:2) ;
    \draw (3.2,2.4) node {$\sin \alpha$};
    \end{tikzpicture}
\end{document}

But my pin refuses to have an angle of 270 (?) relative to the 5kg weight, and appears slanted to the side. How can I force it to have an angle perpendicular to the slope? I think it's colliding with the bottom of the triangle, as 280 degrees snaps it across to the other side.

This is how it appears now:

enter image description here

I've added the line I'm after in red, and the angle it should make with the slope. This would make the pin line perpendicular with the slope. However the pin line is not at the right angle! It should be perpendicular to the slope.

What can I do to get it behave?


Edit the First

this is what I went with eventually, using Jake's answer, with extra pins.

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\draw (0,0) -- (90:2.7) -- ++ (-13:12) 
        node[shape=rectangle,pos=0.6,sloped,above,draw,
        pin={[inner sep=0pt,style=<-,
            label={[inner sep=0pt]270:$mg\cos(\theta)$}]270:{}},
        pin={[inner sep=0pt,
            label={[inner sep=0pt]180:$F_{fric}-F_A\sin(\beta)$}]180:{}},
        pin={[inner sep=0pt,
            label={[inner sep=0pt]90:$F_{N}-F_A\cos(\beta)$}]90:{}},
        pin={[inner sep=0pt,
            label={[inner sep=0pt]0:$mg\sin(\theta)$}]0:{}},
        pin={[inner sep=0pt,
            label={[inner sep=0pt]220:$F_A$}]220:{}
    }]
    {5kg} -- ++(0:-11.7);
\draw[dashed] (0,2.7) -- (13:11.7);
\draw (2,2.7) arc (13:0:2) ;
\draw (6.9,0.7) arc (235:205:1) ;
\draw (6.8,0.9) node {$\beta$} ;
\draw (1.8,2.45) node {$\alpha$};
\end{tikzpicture}
13
  • It is at 270, but you sloped applied also so that is why it is where it is. Feb 9, 2012 at 2:11
  • 3
    Slightly offline from the topic. Shouldn't you write $mgcos \theta$' as $mg\cos\theta$` ?
    – user11232
    Feb 9, 2012 at 2:21
  • @PeterGrill I don't think so, I've been playing with the angle and it seems to snap as you approach 280 from below. For instance 180 places it at nearly parallel to the slope I've drawn, but not quite. Feb 9, 2012 at 2:22
  • Try removing sloped and see what that it is at 270, and yes 180 should be parallel to the slope. Feb 9, 2012 at 2:23
  • @PeterGrill I've removed sloped but now the box and the pin are not parallel to the slope. Which is what I expected to happen, and not what I want. Feb 9, 2012 at 2:28

5 Answers 5

19

To disable the snapping of the pin node, you can redefine the following internal macro (taken from https://tex.stackexchange.com/a/85669/2552):

\makeatletter
\def\tikz@auto@anchor{%
    \pgfmathtruncatemacro\angle{atan2(\pgf@x,\pgf@y)-90}
    \edef\tikz@anchor{\angle}%
}
\makeatother

Full code:

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

\makeatletter
\def\tikz@auto@anchor{%
    \pgfmathtruncatemacro\angle{atan2(\pgf@x,\pgf@y)-90}
    \edef\tikz@anchor{\angle}%
}
\makeatother

\begin{document}
    \begin{tikzpicture}
    \draw (0,0) -- (90:2.7) -- ++ (-13:12) 
        node[
            shape=rectangle,
            pos=0.6,
            sloped,
            above,
            draw,
            pin=270:$mg\cos(\theta)$
        ] 
        {5kg} -- ++(0:-11.6);
    \draw[dashed] (0,2.7) -- (13:11.6);
    \draw (2,2.7) arc (0:-13:2) ;
    \draw (3.2,2.4) node {$\sin \alpha$};
    \end{tikzpicture}
\end{document}

If you don't want to redefine the macro, you could construct a pin that honours the angle using a combination of an empty pin and a label

To do this, you need an empty pin label with an inner sep of 0. Then the pin line ends up exactly where you want it. Since only having a pin line without a pin label would be a bit pointless, we can fake the label using a label in the options of the empty pin. So pin={[inner sep=0pt, label={[inner sep=0pt]270:$mg\cos(\theta)$}]270:{}} works beautifully in this case. You could wrap this into a style so it's easier to handle.

Edit: It turns out (from TikZ node pin angle in 2D plot) that the label part also has a snapping behaviour that can be undesirable. To place the label text properly in line with the pin line, a "normal" node with the correct anchor instead of a label node has to be used. Here's a new style, precise pin=<angle>:<label text> that will place a pin with the proper angle and correctly placed label text:

precise pin/.style args={#1:#2}{
    pin={[
        inner sep=0pt,
        label={[
            append after command={
                node [
                    inner sep=0pt,
                    at=(\tikzlastnode.#1),
                    anchor=#1+180
                ] {#2}
            }
        ]center:{}}
    ]#1:{}}
}

\documentclass{standalone}
\usepackage{tikz}

\tikzset{
    precise pin/.style args={#1:#2}{
        pin={[
            inner sep=0pt,
            label={[
             append after command={
                    node [
                        inner sep=0pt,
                        at=(\tikzlastnode.#1),
                        anchor=#1+180
                    ] {#2}
                }
            ]center:{}}
        ]#1:{}}
    }
}
\begin{document}
    \begin{tikzpicture}
    \draw (0,0) -- (90:2.7) -- ++ (-13:12) 
        node[
            shape=rectangle,
            pos=0.6,
            sloped,
            above,
            draw,
            precise pin=270:$mg\cos(\theta)$
        ] 
        {5kg} -- ++(0:-11.6);
    \draw[dashed] (0,2.7) -- (13:11.6);
    \draw (2,2.7) arc (13:0:2) ;
    \draw (3.2,2.4) node {$\sin \alpha$};
    \end{tikzpicture}
\end{document}
7
  • 2
    ???!!!???!!! So you use the first pin effectively as a coordinate (your trick ensures that all of the anchors are in the same place, I guess) and then create a new node at that point with the real text. "Oh what a tangled web we weave when first we practice TikZ.". Sneaky solution. Feb 9, 2012 at 13:49
  • So you label the pin, not use the pin as a label. as @AndrewStacey says, sneaky. Feb 9, 2012 at 14:13
  • I went with this one, as it's the one that seemed easiest to do, and it's the one I went with. Feb 10, 2012 at 11:56
  • @Jake This is not working with TeX Live 2013 (I tried pdflatex and lualatex). Nor is TikZ node pin angle in 2D plot. Error is: I do not know the key '/tikz/ inner sep'
    – Jost
    Sep 10, 2013 at 14:32
  • @Jost: It works fine for me (also using TL2013). It's a bit strange that /tikz/inner sep should be unknown. Could you check what version of TikZ/PGF you're using (from your .log file)?
    – Jake
    Sep 10, 2013 at 14:38
16

My investigations on this have led me to the following conclusion:

For precision positioning, don't use the label or pin options.

And for that reason, I'm heartily recommending wh1t3's solution.

Nonetheless, it can be illuminating to see why the code is being particularly stubborn (and it'll also show why there's the strange "skip" when you vary the angles, which baffled me for a long time). To understand what's happening, you have to understand what happens when you pass a pin option to a node.

  1. TikZ figures out what angle you want the adjunct node to be at, via some magic that involves the specified angle, whether or not it is absolute, and something additional involving the transformed shape key.

  2. TikZ then sets a coordinate at a certain distance from the centre of the original node at that angle. (Note that this is from the centre of the original node.)

  3. It then figures out the most appropriate anchor for the label node. This depends on the angle specified (and I think it depends on the absolute angle, not the relative angle - but I may be wrong there). What is particularly interesting about this is that there is a high probability that the resulting angle will be a corner angle. For example, the angle has to be between 87 and 94 degrees to get the anchor as south.

  4. TikZ then renders the label node, at the given coordinate, using the determined anchor.

  5. For a pin, TikZ then draws a line between the centres of the two nodes, but stopping on the boundaries (exactly as if you had written \draw (a) -- (b);).

Putting all of that together, we see why we get such strange behaviour. For example, change the angle from 86 to 87 results in the anchor of the label flipping from south west to south, whereupon the centre of the node jumps by about half the text width. This leads to the resulting line changing a lot.

Now, for vague "label this node at an angle of 60", this level of automation is what you want - these decisions are mostly right most of the time. But if you want precision engineering, they can get annoying.

Annoyingly, the manual suggests that the major problem (which is the choice of anchor for the label) can be overridden. However, my experiments have failed to produce a successful high-level way of doing this. I have, naturally, come up with an Extreme Hack which solves the problem. The fact that I had to resort to Extreme Hackery led me to my aforementioned conclusion.

What you want is to be able to specify the angle of the label and then have the resulting edge drawn with that angle in mind, not between the centres. There are two ways to accomplish this by subverting the above:

  1. Ensure that the centre of the label node is used as the anchor. Then the centres of the two nodes will line up along the original angle and the resulting edge will be drawn correctly.

  2. Draw the resulting edge not between the centres of the nodes but between the anchors that have been used in positioning the label.

I prefer the second, but couldn't get it to work. So I present a solution based on the first. For this to work, we merely need to override TikZ's choice of anchor. The anchor is computed in a command called \tikz@auto@anchor and this is used to set a macro \tikz@anchor. So we temporarily disable \tikz@auto@anchor and explicitly set \tikz@anchor. A side effect of this is that the label isn't positioned as far away as it used to be so we need to increase the pin distance (because the label is now anchored at its centre instead of on the boundary).

Here's the code:

\documentclass{standalone}
\usepackage{tikz}
\makeatletter
\tikzset{reset label anchor/.code={%
    \let\tikz@auto@anchor=\pgfutil@empty
    \def\tikz@anchor{#1}
  },
  reset label anchor/.default=center
}
\makeatother

\begin{document}
    \begin{tikzpicture}
    \draw (0,0) -- (90:2.7) -- ++ (-13:12) 
        node[shape=rectangle,pos=0.6,sloped,above,draw,pin={[pin edge={thin,black},reset label anchor,pin distance=.7cm]270:$mg\cos\theta$}]
        {5kg} -- ++(0:-11.6);
    \end{tikzpicture}
\end{document}

and the picture:

downward force

And I remind you that although the code looks quite simple, it is Extreme Hackery and should be used only with Extreme Caution. Use wh1t3's solution in Real Life.

(NB I used Ryan Reich's trace-pgfkeys package from How do I debug pgfkeys? a bit in figuring out what was going on here. Very useful.)

8

This is not really a solution, as it does not use the pin option. I am not sure why the pin approach isn't working. For your current example you can use the following workaround using partway modifiers though.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
  \begin{tikzpicture}
    \draw (0,0) -- (90:2.7) -- ++ (-13:12)
        node[shape=rectangle,pos=0.6,sloped,above,draw] (weight)
        {5kg} -- ++(0:-11.6);
    \path[draw] (weight.south) -- ($(weight.south)!3ex!90:(weight.south west)$) node[below] {$mg\cos\theta$};
    \draw[dashed] (0,2.7) -- (13:11.6);
    \draw (2,2.7) arc (13:0:2) ;
    \draw (3.2,2.4) node {$\sin \alpha$};
  \end{tikzpicture}
\end{document}

perpendicular label

As requested, some explanation on the subject of distance modifiers. These are explained in detail in the TikZ manual in section 13.5.4. Also have a look at the surrounding sections regarding partway and projection modifiers as they are equally useful. The general syntax for the distance modifiers we use here is
<coordinate a>!<dimension>!<angle>:<coordinate b>
Here the <angle> is optional. When the <angle> is omitted, the above evaluates to the point that is <dimension> away from a on the line from a to b. When an <angle> is used this point is also rotated by <angle> around a. To see what happens in our case,
First: We move 3ex from weight.south to weight.south west.
Second: We rotate this new point 90 degrees around weight.south

It should be noted that the direction of the line from a to b influences which way the rotation goes. That is, if we had used weight.south east in our example instead of weight.south west the line would have been directed upwards perpendicular to the slope.

Again, read the manual on these topics, it is very well written and these techniques are immensely usefull to construct points based on existing shapes.

5
  • If it works it's a solution; +1. Feb 9, 2012 at 11:11
  • Could you explain how the weighting shenanigans works? There are a few more lines I want to add, and I'd like to know what I'm doing. Feb 9, 2012 at 11:17
  • 1
    @Pureferret: added some additional explanation. Feb 9, 2012 at 11:31
  • Here's my favourite example of the madness that can result from too many of these distance modifiers. tex.stackexchange.com/a/29781/86 Feb 9, 2012 at 11:48
  • My university doesn't have the tikz library on the linux distro it runs, so when I can I'll be playing with this. Feb 9, 2012 at 11:56
4

Problem :

  \tikz \node [circle,fill=blue!50,minimum size=1cm,pin=60:$q_0$,pin=60:$q_{00000}$] {};   

enter image description here

Idea : I try to use a box with a null width but like Jake remarks the next code seems wrong

\documentclass{article}
\usepackage{tikz}

\begin{document}

\begin{tikzpicture}
    \draw (0,0) -- (90:2.7) -- ++ (-13:12)  
          node[pin={270:\hspace{-1cm}\hbox to 0pt{$mg\cos(\theta)$}},
          shape=rectangle,
          sloped,pos=0.6,
          above,draw] {5kg}  -- ++(0:-11.6) ;   
    \draw[dashed] (0,2.7) -- (13:11.6);
    \draw (2,2.7) arc (13:0:2) ;
    \draw (3.2,2.4) node {$\sin \alpha$}; 
\end{tikzpicture}

\end{document}

enter image description here

update  Without pin

\documentclass{standalone}
\usepackage{tikz}
\begin{document} 
\makeatletter%
 \begin{tikzpicture}[%
to path={ 
 \pgfextra{
       \tikz@scan@one@point\pgfutil@firstofone(\tikztostart)\relax
          \pgf@xa=\pgf@x
          \pgf@ya=\pgf@y 
       \tikz@scan@one@point\pgfutil@firstofone(\tikztotarget)\relax
         \pgf@xb=\pgf@x
          \pgf@yb=\pgf@y
          \advance\pgf@xa by-\pgf@xb
         \advance\pgf@ya by-\pgf@yb
          \pgfmathsetmacro{\myangle}{atan2(\pgf@xa,\pgf@ya)} 
          \global\let\myangle\myangle
}
      -- (\tikztotarget) \tikztonodes}]
    \draw (0,0) -- (90:2.7) coordinate (tmp)  ;
    \draw (tmp) to node [sloped,pos=0.6,above,draw] (tmp2) {5kg}  ++ (-13:12) -- ++(0:-11.6);
    \draw[gray] (tmp2.south)-- ++(\myangle+90:.6)node[below,black] {$mg\cos(\theta)$};      
    \draw[dashed] (0,2.7) -- (13:11.6);
    \draw (2,2.7) arc (13:0:2) ;
    \draw (3.2,2.4) node {$\sin \alpha$}; 
    \node{\myangle};
\end{tikzpicture} 

\end{document}  

enter image description here

2
  • This, unfortunately, only works for the special case of an angle of 90 or 270 in the global coordinate system (where the vertical dimension of the box doesn't play a role). The pin in your example is not exactly at a right angle to the box, and if you try 0 or 180 for the angle, it's off even further.
    – Jake
    Feb 9, 2012 at 21:33
  • Yes I agree with you. I never used pin because I don't like this option. I update my answer with another idea without pin. Feb 9, 2012 at 23:49
1

you can calculate the angle of rotation with the commands \pgfextractx, \pgfextracty, \pgfpointanchor and \pgfmathsetmacro

once calculated that angle, you do turn the vectors x and y

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
    \begin{tikzpicture}
    \draw (0,0) -- (12,0)-- (0,3)coordinate(origin) coordinate[pos=0.5](M)-- cycle;
       \begin{scope}[shift={(origin)}]
           \newdimen\qy
           \pgfextracty{\qy}{\pgfpointanchor{M}{center}}
           \newdimen\qx
           \pgfextractx{\qx}{\pgfpointanchor{M}{center}}
            \pgfmathsetmacro{\ANGLE}{atan2(\qx,\qy)}    
               \begin{scope}[rotate=\ANGLE]
                   \draw  (M) rectangle ++(1,1)coordinate(N);
                   \path (M) -- (M-|N) coordinate[pos=0.5](I);
                   \path (M) -- (N)coordinate[pos=0.5](G);
                   \draw[red,-latex,thick] (I) -- ++(0,2)node[right]{N};
                   \draw[red,-latex,thick] (I) -- ++(-1,0)node[above]{T};
                \end{scope}
            \draw[blue,thick,-latex] (G)node[right]{G} --++(0,-2.5)node[right]{P};
        \end{scope}
\end{tikzpicture}

\begin{tikzpicture}
    \draw (0,0) -- (12,0)-- (0,5)coordinate(origin) coordinate[pos=0.7](M)-- cycle;
       \begin{scope}[shift={(origin)}]
           \newdimen\qy
           \pgfextracty{\qy}{\pgfpointanchor{M}{center}}
           \newdimen\qx
           \pgfextractx{\qx}{\pgfpointanchor{M}{center}}
            \pgfmathsetmacro{\ANGLE}{atan2(\qx,\qy)}    
               \begin{scope}[rotate=\ANGLE]
                   \draw  (M) rectangle ++(1,1)coordinate(N);
                   \path (M) -- (M-|N) coordinate[pos=0.5](I);
                   \path (M) -- (N)coordinate[pos=0.5](G);
                   \draw[red,-latex,thick] (I) -- ++(0,2)node[right]{N};
                   \draw[red,-latex,thick] (I) -- ++(-1,0)node[above]{T};
                \end{scope}
            \draw[blue,thick,-latex] (G)node[right]{G} --++(0,-2.5)node[right]{P};
        \end{scope}
\end{tikzpicture}    
     \end{document}

changement de base et repère

You must log in to answer this question.

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