# TikZ: drawing a cart on the top of a loop

I am trying to draw a cart on top of a loop. I have a system where a cart rolls down an incline and encounters a loop. I then want to look at cart when it reaches the apex of the loop.

The problem I am having is how to draw it's wheels touching the loop so that they are tangent to the loop.

Here is an image of the bigger picture.

Now here is what I have so far for the just the interaction of the cart and the top of the loop.

\documentclass[tikz]{standalone}
\usetikzlibrary{intersections}

\begin{document}
\begin{tikzpicture}
\draw[name path = circ] (0, 0) circle[radius = 0.05];

\path[name path = line] (-0.1, .04) -- +(0.2, 0);
\path[name intersections = {of = circ and line}];

\coordinate (P1) at (intersection-1);
\coordinate (P1) at (intersection-2);
\end{tikzpicture}
\end{document}


I put in a path that intersects the loop and labelled the points. I did this as the line where the cart wheels should be tangent to the loop. Therefore, if everything is based on these points, we can adjust the path for desired distance from the top that will give the image the best look.

-
Just to check: by "loop" you mean loop in the sense of roller coaster loop, right? (Your drawing does not convey that very well since roller coaster loops are not typically circles.) – Charles Staats May 6 '14 at 15:10
@CharlesStaats Yes. – dustin May 6 '14 at 15:14
if wheels are tangential, the cart bends too or stays rectangular? – percusse May 6 '14 at 15:42

## 5 Answers

Drawing the wheels is not difficult, since their center is in the radius of the loop. The problem is to draw the rest of the car. In is current form (in your drawing), it looks like a block on top of the wheels. How would it roll? How are the wheels attached to the body?

I think a more appropiate way to draw the block on top of the center of the wheels, instead of on top of the wheels. Using this approach, the following code does the trick. If you want the block on top of the wheels, as in your figure, simply uncomment the marked lines. However the result is uglier, IMHO.

## Code:

(Updated to add a translucent opacity to the rectangle, so that the wheels are partially occluded. Thanks to Charles Staats for the suggestion)

\def\loopradius{4cm}
\def\wheelradius{5mm}

\usetikzlibrary{intersections,calc}

\begin{tikzpicture}[thick]
\coordinate (center) at (0,0);
\draw[name path = circ] (center) circle[radius = \loopradius];

\path[name path = line] ($(-10, 0.87*\loopradius)$) -- +(20, 0);
\path[name intersections = {of = circ and line}];

\coordinate (P1) at (intersection-1);
\coordinate (P2) at (intersection-2);

\coordinate (C1) at ($(P1)!\wheelradius!(center)$);
\coordinate (C2) at ($(P2)!\wheelradius!(center)$);

\foreach \p in {1,2} {
\draw[red] (C\p) circle (\wheelradius);
}
%\coordinate (C1) at ($(P1)!2*\wheelradius!(center)$);
%\coordinate (C2) at ($(P2)!2*\wheelradius!(center)$);

\draw[red, fill=white, fill opacity=0.85]
let \p1 = ($(C2)-(C1)$),
\n1 = {veclen(\p1)}
in
(C2) rectangle +($(\n1, -0.4*\n1)$)
node[midway] {M};

\end{tikzpicture}


## Result:

-
If I understand correctly, this code cannot accept the width of the cart as a parameter. – Charles Staats May 6 '14 at 15:56
@CharlesStaats you are right. This is because I respected the original approach of the OP, in which the position of the wheels are found as the intersection of the "loop" with a horizontal line at a "customizable" height. – JLDiaz May 6 '14 at 15:58
How did you determine 2*\wheelradius? From the dimension I am using, I had to change it 1.75. I also changed my circle radius to .7 and wheel radius to .05. I had a typo on circle radius in the OP. – dustin May 6 '14 at 16:05
What I mean is if I use a 2, the wheels aren't tangent to the circle. By changing it to a 1.75, they are so I am wandering how you came up with the 2. I just randomly picked 1.5 and it worked out. – dustin May 6 '14 at 16:28
Aesthetic suggestion: Fill in the rectangle to hide a quarter of each wheel. (Or else add spokes.) – Charles Staats May 6 '14 at 16:35

Here's a version that uses the computational capacities of Asymptote to good advantage. The code is robust in that if you change the distance between the wheels and/or the radius of the wheels, it will still work correctly.

\documentclass[margin=10pt,convert]{standalone}
\usepackage{asypictureB}
\begin{document}
\begin{asypicture}{name=cart_on_loop}
settings.outformat="pdf";
unitsize(1cm);
import graph;
path loop =  (-3,0) --- (-1,0) .. (0,4){left} .. (1,0) --- (3,0);
real wheelradius = 0.05, wheeldistance = 0.5, cartheight = 0.3;

//Where will a wheel center be when it's tangent to the loop at path time t?
pair wheelcenter(real t) {
return point(loop, t) + wheelradius*(rotate(90)*dir(loop,t));
}
//This path is for computation, not drawing:
path wheelpath = graph(wheelcenter, 0, length(loop), operator ..);

picture cart = currentpicture.copy();
draw(cart, circle(c=(0,0), r=wheelradius));
draw(cart, circle(c=(wheeldistance, 0), r=wheelradius));
draw(cart, box((0, wheelradius), (wheeldistance,  wheelradius+cartheight)));
label(cart, "$M$", align=0.2N, position=(wheeldistance/2, wheelradius));

void drawcart(pair trailingwheel, pair leadingwheel = trailingwheel + (wheeldistance, 0)) {
transform T = identity();
pair vector = leadingwheel - trailingwheel;
real angle = degrees(atan2(vector.y, vector.x));
T = rotate(angle)*T;
T = scale(length(vector) / wheeldistance)*T;
T = shift(trailingwheel)*T;
add(T*cart);
}

//t is specified in arclength
void drawcart(real t) {
pair trailingwheel = arcpoint(wheelpath, t);
pair estimateleading = arcpoint(wheelpath, t + wheeldistance);
path samedist = circle(c=trailingwheel, r=wheeldistance);
pair[] intersections = intersectionpoints(samedist, wheelpath);
pair leadingwheel = intersections[0];
for (pair candidate : intersections) {
if (length(candidate - estimateleading) < length(leadingwheel - estimateleading))
leadingwheel = candidate;
}
drawcart(trailingwheel, leadingwheel);
}

//Draw the loop:
draw(subpath(loop, 0, length(loop)/2));
draw(subpath(loop, length(loop)/2, length(loop)), white + linewidth(2pt) + squarecap);
draw(subpath(loop, length(loop)/2, length(loop)));

/* Find the times when the center of the wheel has x = wheeldistance/2.
* There will be three such times (entering loop, at the top, leaving loop),
* indexed by 0, 1, 2. We want the middle one.
*/
real patht = times(wheelpath, wheeldistance/2)[1];
real t = arclength(subpath(wheelpath,0,patht));
drawcart(t);

//Draw a couple more carts at random places
drawcart(0.2);
drawcart(3.3);

\end{asypicture}
\end{document}


-
This is great but I am not really versed in using asymptote. – dustin May 6 '14 at 15:42
I need to learn asymptote... – Ingo May 7 '14 at 19:42

Just for fun with PSTricks.

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-node}

\begin{document}
\foreach \i in {0,15,...,345}{%
\begin{pspicture}(-2,-2)(2,2)
\pscircle{2}
\pnodes{A}(!1.9 \i\space 30 add PtoC)(!1.9 \i\space PtoC)
\pspolygon*[linecolor=gray](A0)(A1)([offset=.5]{A0}A1)([offset=-.5]{A1}A0)
\qdisk(A0){.1}\qdisk(A1){.1}
\end{pspicture}}
\end{document}


## Frozen animation

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-node}

\begin{document}
\begin{pspicture}(-2,-2)(2,2)
\pscircle{2}
\pnodes{A}(!1.9 105 PtoC)(!1.9 75 PtoC)
\pspolygon*[linecolor=gray](A0)(A1)([offset=.5]{A0}A1)([offset=-.5]{A1}A0)
\qdisk(A0){.1}\qdisk(A1){.1}
\end{pspicture}
\end{document}


## TikZ version

\documentclass[tikz,border=12pt]{standalone}
\usetikzlibrary{calc}
\begin{document}
\foreach \i in {0,15,...,345}{%
\begin{tikzpicture}
\draw (0,0) circle (2);
\coordinate (A0) at (\i+30:1.9);
\coordinate (A1) at (\i:1.9);
\fill [gray] (A0) -- (A1) -- ($(A1)!.5!90:(A0)$) -- ($(A0)!.5!-90:(A1)$) -- cycle;
\fill (A0) circle (.1) (A1) circle (.1);
\end{tikzpicture}}
\end{document}

-
Impressive translation! – Charles Staats May 6 '14 at 18:37

Here is a version in Metapost. I've defined a draw_car routine that takes an arbitrary path, a starting point along that path, the radius of the wheels, the length of the car, and a label.

The approach is explained in the comments. If you try to draw the car past the end of the path you'll get a paths don't intersect error.

prologues := 3;
outputtemplate := "%j%c.eps";

vardef draw_car(expr p, n, r, len, name) =
% p : an arbitrary path
% n : a point on p (ie "point n of p" should exist)
% r : the radius of the car's wheels
% len : the length of the car's body
save a, b, q, t; path q; pair a, b; transform t;
% construct a path q, parallel to the given path p starting at point n of p
q = for i = n step 0.1 until length(p)+eps:
point i of p + ((0,r) rotated angle direction i of p) ..
endfor
point infinity of p + ((0,r) rotated angle direction infinity of p);
% a = center of back wheel, b = center of front wheel
a = point 0 of q;
b = q intersectionpoint fullcircle scaled 2 len shifted a;
% draw the wheels
fill fullcircle scaled 2r shifted a;
fill fullcircle scaled 2r shifted b;
% draw the body of the truck with a label, suitably transformed
t = identity rotated angle (b-a) shifted a;
draw unitsquare xscaled len yscaled 13/21 len transformed t;
picture M; M = image(label(name,(1/2len,1/3len))); draw M transformed t;
enddef;

beginfig(1);
path p;
p = origin .. (3cm,-2cm) {dir -45} .. (8cm,1cm) {up};
draw p withcolor .687 red;
draw_car(p, 0, 1mm, 1cm, "A");
draw_car(p, 0.6, 1mm, 1cm, "B");
draw_car(p, 1.2, 1mm, 1cm, "C");
draw_car(p, 1.8, 1mm, 1cm, "D");
endfig;
end.


Given this approach, we can decorate the car in whatever way we like by varying the bit that draws the body. For example, we could add these "go faster" stripes:

by adding

% draw some go faster stripes
draw ((-2,1/4 len) -- (-2,13/21 len)) transformed t;
draw ((-4,1/3 len) -- (-4,13/21 len)) transformed t;
draw ((-6,1/2 len) -- (-6,13/21 len)) transformed t;


after defining the transformation.

We can also have a go at the OP diagram:

beginfig(2);
h = 4.4cm;
r = 2cm;
path track; track = (0,h) {dir -50} .. { right } (3r, 0) --
(4.9r, 0) ..  {up}    (6r,r) ..  {left} (5r,2r) ..
{down} (4r,r) ..  {right} (5.1r, 0) -- (6.4r,0);

draw track withpen pencircle scaled .7pt withcolor .67 red;
% label the start
draw         (2 left -- 2 right) shifted (-5,0);
draw         (2 left -- 2 right) shifted (-5,h);
drawdblarrow ( origin -- (0,h)) shifted (-5,0);
label(btex $h$ etex, (0,1/2h));
% label the loop
drawarrow ( origin -- (0,r)) shifted  (5r,r);
label(btex $r$ etex, (5r+5,3/2r));
fill fullcircle scaled 2pt shifted (5r,r);
% draw the car at the start and then near the top of the loop
draw_car(track,0,2bp,1cm,"M");
draw_car(track,3.4,2bp,1cm,"M");
endfig;

-
This is essentially the same approach that I took. I see that you start the wheel path at the rear wheel to avoid ambiguous intersections, which is simpler and cleaner than what I did. – Charles Staats May 7 '14 at 20:58

The geometry is straightforward back-of-the-envelope stuff. Don't really need the math library but the code looks a bit cleaner.

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{math}
\begin{document}
\begin{tikzpicture}
\tikzmath{
\R = 2;   % Loop radius
\r = 1/8; % Wheel radius
\w = 1;   % Cart width
\h = 1/2; % Cart height
%
% Calculate the angle from the vertical
% of the radius of the loop that passes
% through the center of the wheels.
\a = asin((\w/2)/(\R-\r));
% Calculate the vertical position of the cart wheels;
\S = (\R-\r)*cos(\a);
}

\draw circle [radius=\R];
\draw (-\w/2,\S-\h-\r) rectangle ++(\w, \h);
\node at (0, \S-\h/2-\r) {$M$};
\draw [fill=white] (-\w/2,\S) circle [radius=\r] (\w/2,\S) circle [radius=\r];

\end{tikzpicture}
\end{document}


-