34

For any arbitrary parametric smooth curve, how can I make the length of a car remain unchanged?

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

\pstVerb
{
    tx@Derive begin
    /EvalVariable { 2 index (t) eq { (1) } { (0) } ifelse 4 -1 roll exch 6 2 roll } def
    end 
}


\def\x{t}
\def\y{2*cos(t)}

\def\xx{Derive(1,\x)}
\def\yy{Derive(1,\y)}
\def\rr{sqrt((\xx)^2+(\yy)^2)}

\def\Nx{-\yy/\rr}
\def\Ny{\xx/\rr}

\def\R{.2}
\def\xP{\x+\R*\Nx}
\def\yP{\y+\R*\Ny}

\begin{document}

\multido{\rx=.0+.3,\ry=.4+.3}{41}{%
\begin{pspicture}[algebraic](-.2,-2.2)(\dimexpr\psPiFour cm+.2cm,2.2)
    \psparametricplot[linecolor=gray,plotpoints=100]{0}{TwoPi 2 mul}{\x|\y}
    \curvepnodes[plotpoints=2]{\rx}{\ry}{\xP|\yP}{P}
    \pspolygon*[linecolor=gray](P0)(P1)([offset=-.5]{P0}P1)([offset=.5]{P1}P0)
    \qdisk(P0){\R}\qdisk(P1){\R}
    \psparametricplot[linecolor=red,linewidth=2pt,plotpoints=100]{0}{\rx}{\xP|\yP}
\end{pspicture}}

\end{document}

enter image description here

Commenting Herbert's solution

It is too long as comment. Herbert's solution apparently keeps the length of curve segment bounded by the wheels constant rather than the length of the car. The following animation shows it.

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

\pstVerb
{
    tx@Derive begin
    /EvalVariable { 2 index (t) eq { (1) } { (0) } ifelse 4 -1 roll exch 6 2 roll } def
    end 
}


\def\x{t}
\def\y{2*cos(t)}

\def\xx{Derive(1,\x)}
\def\yy{Derive(1,\y)}
\def\rr{sqrt((\xx)^2+(\yy)^2)}

\def\Nx{-\yy/\rr}
\def\Ny{\xx/\rr}

\def\R{.2}
\def\xP{\x+\R*\Nx}
\def\yP{\y+\R*\Ny}

\begin{document}

\multido{\iA=1+1,\iB=10+1}{40}{%
\begin{pspicture}[algebraic](-.2,-2.2)(\dimexpr\psPiFour cm+.2cm,2.2)
    \psparametricplot[linecolor=gray,plotpoints=100]{0}{TwoPi 2 mul}{ \x | \y }
    \pscurvepoints[plotpoints=50]{0}{TwoPi 2 mul}{ \xP | \yP }{P}
    \pspolylineticks[ticksize=0 0,metricInitValue=1,Os=1,Ds=.3]{P}{ ds }{1}{50}%
    \pspolygon*[linecolor=gray](PTick\iA)(PTick\iB)([offset=-.05]{PTick\iA}PTick\iB)([offset=.05]{PTick\iB}PTick\iA)
    \qdisk(PTick\iA){\R}\qdisk(PTick\iB){\R}
\end{pspicture}%
}

\end{document}

enter image description here

  • when moving atop the summit the bottom of the fixed length car is going to hit against the pavement. Or, you need a rather tiny car (ratio of length of car to radius of curvature matters here). –  May 07 '14 at 16:29
  • 1
    you could have the body of the car a fixed size rectangle centered mid-way between the wheels, then draw springs from the wheel to the body of the car. The wheels will thus be allowed to be at various distances, but the car itself will keep fixed shape. At least, I would like to see such an animation. –  May 07 '14 at 16:36
  • if you have a curve (f(t),g(t)), and point t0, and wheel radius r, and intra wheel distance d. Then for t>t0, consider (f(t)-r g'(t),g(t)+r f'(t)), which is a point on the normal at local distance r from the curve (if the curvature of the curve is less than the curvature of the wheel in case both have the same sign), express that this point (the center of the potential front wheel) it is at distance d of (f(t0)-r g'(t0), g(t0)+r f'(t0)) (the center of the rear wheel), and solve for t... does not look very practical. An approximate scheme would be more apt. –  May 07 '14 at 16:54
  • In my previous comment it is assumed that the curve is already parametrized by arc-length: f'(t)^2+g'(t)^2=1, else the formula becomes (even more) complicated. –  May 07 '14 at 16:59
  • I am very rusty on these things. Let k be the algebraic curvature: in your example this is -2cos(t)/(1+4sin^2(t))^{3/2} (see wikipedia). Let r be the wheel radius, and d the inter-wheel distance. Assuming r<<d<<1/|k|, it seems that to first approximation, if t corresponds to the rear wheel, then t'=t+h corresponds to the front wheel with h = d*(1+k * r + k^2 * d^2/24)/||T(t)|| where ||T(t)||=sqrt(f'^2+g'^2) is the speed vector length, in your example sqrt(1+4*(sin(t))^2). –  May 07 '14 at 18:01
  • this is a first order approach which should be valid for any parametrized curve as long as r<<d<<1/|k| is true. However I can not check as I am not proficient enough in pstricks to test it in your case. –  May 07 '14 at 18:08
  • 1
    I just posted a MP solution to this problem in my answer to this question. – Thruston May 07 '14 at 18:08
  • @Thruston +1 didn't know there had been so much activity already on this topic! are your math formulas similar to the one in my comment? –  May 07 '14 at 18:12
  • @jfbu - yes probably, but as ever MP provides geometrical functions that avoid the need for explicit calculations. I just constructed the path of the axle parallel to the track and got MP to calculate the intersection of this track with the length of the car. – Thruston May 07 '14 at 18:50
  • 1
    Note that my earlier Asymptote answer to the same question Thruston linked to keeps the length of the car constant, using essentially the same approach. – Charles Staats May 07 '14 at 21:41
  • 2
    I'm sorry, but I consider "This question has not received enough attention." to be a bit rude considering the amount of attention it has had and am consequently deleting my answer. – Andrew Stacey May 10 '14 at 19:19
  • @IamwhoIsayIam Sorry but you may please revise the phrase "FROM EXPERTS" as those who answered are giants in tikz. I will be really grateful if you do so. –  May 11 '14 at 02:04
  • 1
    @HarishKumar: More precisely, I meant attention from other PSTricks experts who can fix the shortcoming in the existing PSTricks answers. :-) TikZ, Metapost, and Asymptote answers are not the focal point for me but just for comparison purpose. I appreciate those answers, of course. – kiss my armpit May 11 '14 at 05:27
  • @Andrew Stacey: While I agree that the OP's initial bounty post had ill-considered wording, I would appreciate it if you would re-post your answer, since I had hoped to look at it in greater detail. – Charles Staats May 12 '14 at 20:35
  • @CharlesStaats My answer currently has 2 undelete votes on it. It needs one more to undelete it. I suspect that there'll be someone in chat who will happily supply the 3rd vote. (I won't delete it again - I've made my point - but neither will I undelete it myself.) – Andrew Stacey May 13 '14 at 07:09
  • @MoneyOrientedProgrammer: Have you tried making your own PSTricks answer based on the ideas contained in the MetaPost, Asymptote and TikZ answers? The basic requirement for most of them is the ability to construct the intersection points of two paths (a circle centered on the rear wheel, and a path parallel to the track) without drawing either path; can PSTricks do this? – Charles Staats May 14 '14 at 14:03
  • @CharlesStaats Now PSTricks can do this :) – Christoph May 19 '14 at 12:20

6 Answers6

27

A bit slow to compile and a bit inaccurate in places.

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{decorations}
\makeatletter
\pgfdeclaredecoration{cart}{start}{
  \state{start}[width=0pt, next state=move,
    persistent precomputation={
      \ifx\pgfdecorationcartdistance\pgfutil@empty%
      \else%
        \pgfmathparse{\pgfdecorationcartdistance/\pgfdecoratedpathlength}%
        \let\pgfdecorationcarttime=\pgfmathresult%
      \fi%
    }]{}
  \state{move}[width=\pgfdecorationcarttime*\pgfdecoratedpathlength,
    next state=pre-calculate]{}
  \state{pre-calculate}[width=1pt, next state=calculate]{
    \pgfcoordinate{cart-start}{\pgfpointorigin}%
  } 
  \state{calculate}[width=1pt, 
    persistent postcomputation={
      \pgfpointdiff{\pgfpointanchor{cart-start}{center}}%
        {\pgfpointanchor{cart-end}{center}}%
      \pgfmathveclen{\the\pgf@x}{\the\pgf@y}%
      \ifdim\pgfmathresult pt>\pgfdecorationcartlength\relax
        \def\pgf@decorate@next@state{final}
      \fi
    }
  ]{ \pgfcoordinate{cart-end}{\pgfpointorigin} }
  \state{final}{%
    \pgftransformreset
    \pgftransformshift{\pgfpointanchor{cart-start}{center}}%
    \pgfmathanglebetweenpoints{\pgfpointanchor{cart-start}{center}}{\pgfpointanchor{cart-end}{center}}%
    \pgftransformrotate{\pgfmathresult}% 
    \path pic [transform shape] {cart};
  }
}
\begin{document}

\pgfkeys{/pgf/decoration/.cd,
  cart length/.store in=\pgfdecorationcartlength,
  cart height/.store in=\pgfdecorationcartheight,
  cart time/.store in=\pgfdecorationcarttime,
  cart distance/.store in=\pgfdecorationcartdistance,
  cart wheel radius/.store in=\pgfdecorationcartwheelradius,
  cart length=0.375cm,
  cart height=0.25cm,
  cart time=0.5,
  cart distance=,
  cart wheel radius=0.0625cm,
}

\tikzset{cart/.pic={
    \fill [gray]  (0cm, \pgfdecorationcartwheelradius) 
      rectangle (\pgfdecorationcartlength, \pgfdecorationcartheight);
    \fill [black] (0cm, \pgfdecorationcartwheelradius)
      circle [radius=\pgfdecorationcartwheelradius];
    \fill [black] (\pgfdecorationcartlength, \pgfdecorationcartwheelradius) 
      circle [radius=\pgfdecorationcartwheelradius];
}}

\foreach \p in {0,...,49}{%
\begin{tikzpicture}
\useasboundingbox (-1, -2) rectangle (6, 3);
  \draw [postaction={decoration={cart, cart time=\p/50}, decorate},
    postaction={decoration={cart, cart time=\p/50, reverse path}, decorate}]
    (0,0) .. controls ++(90:1) and ++(240:2) .. (3,2)
    .. controls ++(60:2) and ++(90:2) .. (5,0) .. controls ++(270:2)
    and ++(270:2) .. cycle;
\end{tikzpicture}
}
\end{document}

enter image description here

Mark Wibrow
  • 70,437
  • 1
    These cars defy gravity! good work :) –  May 08 '14 at 08:24
  • One spot of inaccuracy appears to be when both cars are rounding the sharp curve on the left. But you I had to pause the animation there to see it. – Charles Staats May 09 '14 at 02:09
  • @CharlesStaats It is pulling a wheelie (which is more spectacular the nearer the position/time gets to 1). Or it is because it is at the end of the path and the decoration does not recycle the path. – Mark Wibrow May 09 '14 at 04:35
  • 1
    ooh this is very neat! :) I'm dreaming about these cars in a Möbius strip. :) – Paulo Cereda May 10 '14 at 19:50
20

Here's an Asymptote solution, adapted from my answer here:

unitsize(5cm);
import graph;
import animation;
real wheelradius = 0.1, wheeldistance = 1.0;

pair torusknot(real t) {
  int p = 3, q=5;
  real r = cos(q*t) + 2;
  return (r*cos(p*t), r*sin(p*t));
}

path loop = graph(torusknot, 0, 2pi, operator..) & cycle;

//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 ..) & cycle;


void drawcart(pair trailingwheel, pair leadingwheel = trailingwheel + (wheeldistance, 0)) {
  draw(trailingwheel -- leadingwheel, gray);
  filldraw(circle(c=trailingwheel, r=wheelradius));
  filldraw(circle(c=leadingwheel, r=wheelradius));
}

//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(loop);

animation A;

int n = 200;

real length = arclength(wheelpath);

for (int i = 0; i < n; ++i) {
  save();
  drawcart(i*length/n);
  A.add();
  restore();
}

A.movie(delay=300);

The result:

15

Here is a method which is only approximate. There is an underlying fact which is neat: the path followed by the center of the wheels has the same centers of curvatures as the original path. Thus to find the location of the front wheel, one may proceed approximately like this

  1. consider a point M=(f(t),g(t)) on the path, its unit tangent vector, and the normal vector.
  2. The center of the rear wheel is at the distance r along the normal vector.
  3. compute the algebraic curvature k, hence the center of curvature which is at algebraic distance 1/k from M on the normal line,
  4. and find then point which is simultaneously at distance d of the center of the rear wheel and at distance |1/k-r| from the center of curvature, and has positive coordinate along the tangent vector: this gives the approximate location of the center of the front wheel.

For this to work, d must be small compared to the radius of curvatures, and r small compared to d. Here are two implementations (where the condition that r is small compared to d is not satisfied):

cartwheel on torus knot on rollercoaster

code for torus knot:

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

\def\p {3}
\def\q {5}
\def\x{(2+cos(\q*t))*cos(\p*t)}
\def\y{(2+cos(\q*t))*sin(\p*t)}

 % derivative vector
\def\xx {(-sin(\q*t)*\q*cos(\p*t)-(2+cos(\q*t))*sin(\p*t)*\p)}
\def\yy {(-sin(\q*t)*\q*sin(\p*t)+(2+cos(\q*t))*cos(\p*t)*\p)}

\def\rr{sqrt(4*\p^2+4*\p^2*cos(\q*t)+\p^2*cos(\q*t)^2+\q^2-cos(\q*t)^2*\q^2)}

\def\Tx {\xx/\rr}  % unit tangent vector
\def\Ty {\yy/\rr}   
\def\Nx {-\yy/\rr} % normal vector= tangent rotated +90 degrees 
\def\Ny {\xx/\rr}

\def\R{.2}         % wheel radius

\def\xP{\x+\R*\Nx} % wheel center
\def\yP{\y+\R*\Ny}

\def\gg{(\p*(2*cos(\q*t)*\q^2+4*\p^2+4*\p^2*cos(\q*t)-cos(\q*t)^2*\q^2+\p^2*cos(\q*t)^2+2*\q^2)/\rr^3)}% algebraic curvature (thank you maple)

\def\GG{\gg/(1-\R*\gg)}% algebraic curvature of wheel center path

\def\D{.4}   % intra-wheel distance

% approximate location of front wheel center (exact when path is a circle)
\def\xQ {\x + (\R+\GG*(\D)^2/2)*\Nx + \D*sqrt(1-(\GG*\D/2)^2)*\Tx }
\def\yQ {\y + (\R+\GG*(\D)^2/2)*\Ny + \D*sqrt(1-(\GG*\D/2)^2)*\Ty }

% location of centers of curvatures (for testing)
% \def\xG {\x + \Nx/\gg }
% \def\yG {\y + \Ny/\gg }


\begin{document}

\multido{\rx=.0+.1}{63}{%
\begin{pspicture}[algebraic](-3,-3)(3,3)
    \psparametricplot[linecolor=gray,plotpoints=300]{0}{TwoPi}{\x|\y}
    \curvepnode {\rx}{\xP|\yP}{P} % is this efficient way to set one node?
    \curvepnode {\rx}{\xQ|\yQ}{Q} %  (I don't know pstricks!) 
    \pspolygon*[linecolor=gray](P)(Q)([offset=-\D]{P}Q)([offset=\D]{Q}P)
    \qdisk(P){\R}\qdisk(Q){\R}
\end{pspicture}}

\end{document}

and for roller coaster:

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


\def\x{t}
\def\y{2*cos(t)}

\def\xx{1}         % derivative vector
\def\yy{-2*sin(t)}
\def\rr{sqrt(1+4*(sin(t))^2)}

\def\Tx {\xx/\rr}  % unit tangent vector
\def\Ty {\yy/\rr}   
\def\Nx {-\yy/\rr} % normal vector= tangent rotated +90 degrees 
\def\Ny {\xx/\rr}

\def\R{.2}         % wheel radius

\def\xP{\x+\R*\Nx} % wheel center
\def\yP{\y+\R*\Ny}

\def\gg{(-2*cos(t)/(\rr)^3)}% algebraic curvature

\def\GG{\gg/(1-\R*\gg)}   % algebraic curvature of wheel center path

\def\D{.4}   % intra-wheel distance

% approximate location of front wheel center (exact when path is a circle)
\def\xQ {\x + (\R+\GG*(\D)^2/2)*\Nx + \D*sqrt(1-(\GG*\D/2)^2)*\Tx }
\def\yQ {\y + (\R+\GG*(\D)^2/2)*\Ny + \D*sqrt(1-(\GG*\D/2)^2)*\Ty }

% location of centers of curvatures (for testing)
% \def\xG {\x + \Nx/\gg }
% \def\yG {\y + \Ny/\gg }


\begin{document}

\multido{\rx=.0+.3}{40}{%
\begin{pspicture}[algebraic](-.2,-2.2)(\dimexpr\psPiFour cm+.2cm,2.2)
    \psparametricplot[linecolor=gray,plotpoints=100]{0}{TwoPi 2 mul}{\x|\y}
    \curvepnode {\rx}{\xP|\yP}{P} % is this efficient way to set one node?
    \curvepnode {\rx}{\xQ|\yQ}{Q} %  (I don't know pstricks!) 
    \pspolygon*[linecolor=gray](P)(Q)([offset=-\D]{P}Q)([offset=\D]{Q}P)
    \qdisk(P){\R}\qdisk(Q){\R}
\end{pspicture}}

\end{document}
  • Your approximation produces some defects. – kiss my armpit May 08 '14 at 15:00
  • 1
    @IamwhoIsayIam yes, the car is too big compared to the radius of curvature, at some places. On the other hand, one may say it creates a realistic effect of the car bouncing off the road... –  May 08 '14 at 17:02
  • 2
    Bouncing off the road, or in some cases, rearing up for a climb. – Charles Staats May 08 '14 at 19:41
  • @CharlesStaats reminds me of my first car, needed full gas at the bottom of such climbs... –  May 08 '14 at 19:45
  • Use Derive in my updated question to save more keystrokes. – kiss my armpit May 08 '14 at 21:05
  • 1
    @IamwhoIsayIam yes, the Derive from your updated question and this answer to another question is very neat. Nice bounty; if somebody reads this: my approximate method is exact when the path is a circle; this means that it is not so much the size of the car and wheels versus curvature which counts for accuracy, but the sizes versus rate of change of curvature, although I have not tried to make this quantitative. –  May 12 '14 at 17:25
14
\documentclass{beamer}
%\url{http://tex.stackexchange.com/q/175874/86}
\usepackage{tikz}
\usetikzlibrary{%
  % Intersections is needed to work out where the front of the car will be
  intersections,%
  % Hobby is just to get a track that doesn't have a ``nice'' function
  hobby,%
  % Calc is to make it easy to draw the car, and to make the clipping path easy to compute
  calc,%
  % Decorations makes it easy to locate the car on the track
  decorations.markings%
}

% Converts the ``beamer@slideinframe'' to a LaTeX counter to make it easier to animate
\makeatletter
\def\c@slideinframe{\beamer@slideinframe}
\makeatother
\begin{document}
% The track appears to be 125mm long, give or take the length of the car, so we animate over that length
\begin{frame}<1-125>
\begin{tikzpicture}
% Set the position of the car dependent on the slide number
\pgfmathsetmacro\xpos{\the\value{slideinframe}}
% Length of the car
\def\clen{10mm}
% Radius of car wheel
\def\crad{5pt}
% We're going to use this path a few times, so we save it for easy restoration.
% This also sets the bounding box and marks the position of the back wheel of the car.
\path[
  use as bounding box,
  use Hobby shortcut,
  save Hobby path={track},
  decoration={
    markings,
    mark=at position {\xpos mm} with {\coordinate (bwheel);}
  },
  decorate
]
([out angle=70]0,0) .. (2,2) .. (4,-2) .. (6,0) .. ([in angle=120]8,-2);
\begin{scope}
% Now we draw the track.
% The actual track is offset from the track path by the radius of the car wheels.
% Since an offset path is unlikely to be a bezier curve, we can't draw it directly.
% So we cheat: we draw a thick path of the right width but clip it against the original path to only draw one side of it.
% Then we overlay with a narrower white path (this is how the ``double'' key works, except that we only want one of the sides not both, hence the clip).
% The clip path consists of the track path and a lower box, large enough to encompass the thickened track.
\clip (8,-2) |- ($(current bounding box.south)+(0,-2*\crad)$) -| (0,0) [restore and use Hobby path={track}{}];
\draw[restore and use Hobby path={track}{disjoint}, name path global=track,line width=2*\crad+1pt];
\end{scope}
\draw[restore and use Hobby path={track}{disjoint}, name path global=track,line width=2*\crad,white];
% This path is a circle of radius the length of the car.
% We use this to intersect with the track path to find out where the front wheel will be.
\path[name path=base] (bwheel) circle[radius=\clen];
% The intersections library gives us one or two intersections.
% We want the front one, so we order the intersections by the track path and then take the last one.
\path [name intersections={of=track and base,total=\carint,sort by=track}]  coordinate (fwheel) at (intersection-\carint);
% We draw the wheels.
\draw (bwheel) circle[radius=5pt] (fwheel) circle[radius=5pt];
% And lastly the car, using the calc library to draw the sides perpendicular to the base.
\draw[fill=white] (bwheel) -- (fwheel) -- ($(fwheel)!15pt!-90:(bwheel)$) -- ($(bwheel)!15pt!90:(fwheel)$) -- cycle ;
\end{tikzpicture}
\end{frame}
\end{document}

TikZ car riding on a track

Andrew Stacey
  • 153,724
  • 43
  • 389
  • 751
13

Ok, here you go :) This code works similar to the Asymptote, Metapost and TikZ answers. First a path parallel to the original track path is created, the rear wheel is advanced with constant velocity and the position of the front wheel is calculated as intersection of a circle centered around the rear wheel with the parallel track path.

The code is based on the pst-intersect package. The largest part is required to create a path which is parallel to an other path:

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-plot,pst-node,pst-intersect}
\def\x{t}
\def\y{2*cos(t)}

\makeatletter
\pstVerb{ \pst@intersectdict
/ExtrudePath {
  /@myshift exch def
  [ exch
  {
    { aload pop } forall
    4 copy VecSub
    2 copy tx@Dict begin Pyth end dup 3 1 roll div 3 1 roll div exch % normalized
    @myshift VecScale
    -90 matrix rotate dtransform 2 copy 8 -2 roll VecAdd 6 2 roll VecAdd
    [ 5 1 roll ] ArrayToPointArray
    counttomark 1 roll
  } forall ]
} bind def
/CleanupPath {
  [ exch
  5 dict begin
  dup length dup /N exch def 1 gt {
    /i 1 def
    dup 0 get /A exch def
    {
      dup i get /B exch def
      A B IntersectLines pop /tA exch def pop pop
      {
        i N 1 sub eq { exit } if
        /i i 1 add def
        dup i get A exch IntersectLines pop 
        dup 0 get tA 0 get lt { %(skip current line) == 
          /tA exch def /B exch def pop
        } { % (use current line) ==
          pop pop pop /i i 1 sub def exit
        } ifelse
      } loop
      A tA LoadLineIntersectionPoints dup
      [ A 0 get 3 -1 roll ] counttomark 1 roll
      /A [ 3 -1 roll B 1 get ] def
      /i i 1 add def
      i N eq { A counttomark 1 roll exit } if
    } loop 
    pop ]
  } if
  end
} bind def
/GetCurvePointAtLength {
  3 dict begin 
    /l_to exch def
    /L 0 def
    /segm 0 def
    PreparePath [ exch aload pop counttomark -1 2 { 1 roll } for ]
    dup 0 get L 3 -1 roll 
    {
      dup { aload pop } forall
      tx@Dict begin Pyth2 end 
      dup L add /L exch def
      L l_to gt { 4 2 roll pop pop exit } { /segm segm 1 add def pop pop } ifelse
    } forall
    L l_to sub exch div neg 1 add dup segm add 3 1 roll [ exch ] 
    LoadLineIntersectionPoints aload pop 
  end
} bind def
end
}%
\def\psGetCurveCoorAtLength#1#2{%
  \pst@intersectdict
  currentdict /\PIT@name{#1} known not {
    (You haven't defined the curve or path '#1') ==
  } if
  \PIT@name{#1} #2 \pst@number\psxunit\space mul 
  GetCurvePointAtLength 3 -1 roll pop
  \tx@UserCoor
  end
}%
\def\pssaveparallelpath#1#2#3{%
  \pstVerb{ \pst@intersectdict
    /\PIT@name{#1} load PreparePath #3 \pst@number\psxunit mul
    ExtrudePath
    CleanupPath 
    [ exch dup dup length 1 sub get 0 get aload pop /movetype 4 -1 roll
      { 1 get aload pop /linetype } forall counttomark -3 roll ]
    /\PIT@name{#2} exch def
    end }%
}
\def\psGetFrontWheelCoor#1#2#3{%
  \pst@intersectdict
    \PIT@name{#1} /\PIT@name{#2} get #3 \pst@number\psxunit\space mul 
  GetCurvePointAtLength pop pop /t_val exch def
  \PIT@name{#1} /\PIT@name{#2@t} get
  { dup t_val gt { exit }{ pop } ifelse } forall
  \PIT@name{#1} /\PIT@name{#2} get
  PreparePath dup length 1 sub 
  3 -1 roll dup dup
  cvi sub 4 1 roll
  cvi sub get
  PointArrayToArray
  tx@FuncDict begin 2 dict begin
    dup length 2 idiv 1 sub /BezierType exch def /Points exch def GetBezierCoor
  end end end
  \tx@UserCoor
}
\makeatother
\def\Car{%
  \ncline[linestyle=none]{RearWheel}{FrontWheel}
  \ncput[nrot=:U]{\psline[linearc=0.05,fillstyle=solid,fillcolor=red](0,0)(0.5,0)(0.45,0.25)(0.1,0.25)(-0.1,0.4)(-0.45,0.4)(-0.45,0)(0,0)}
  \pscircle[fillstyle=solid, fillcolor=black](FrontWheel){\WheelRadius}
  \pstracecurve[fillstyle=solid,fillcolor=black]{RearWheel}}
\begin{document}

\multido{\r=0+0.45}{95}{%
\def\CarLength{0.4}
\def\WheelRadius{0.2}
\begin{pspicture}[algebraic](-.2,-2.2)(\dimexpr\psPiFour cm+.2cm,2.2)
  \pssavepath[linestyle=none]{A}{\psparametricplot[plotpoints=100]{0}{TwoPi 2 mul}{\x|\y}}
  \pstracecurve[linecolor=gray]{A}
  \pssaveparallelpath{A}{B1}{\WheelRadius}%
  \pssaveparallelpath{A}{B2}{-\WheelRadius}%
  \pssavepath[linestyle=none]{B}{%
    \pstracecurve{B1}
    \psparametricplot[plotpoints=10]{Pi -0.5 mul}{Pi 0.5 mul}{12.56637+0.2*cos(t)|2-0.2*sin(t)}
    \pstracecurve[tstart=99,tstop=0]{B2}
    \psparametricplot[plotpoints=10]{Pi 0.5 mul}{Pi 1.5 mul}{0.2*cos(t)|2-0.2*sin(t)}}%
  \pnode(!\psGetCurveCoorAtLength{B}{\r}){RearWheel}
  \pssavepath[linestyle=none]{RearWheel}{\pscircle(RearWheel){\WheelRadius}}
  \pssavepath[linestyle=none]{CircIsect}{\pscircle(RearWheel){\CarLength}}
  \psintersect[name=I]{B}{CircIsect}
  \pnode(!\psGetFrontWheelCoor{I}{B}{\r}){FrontWheel}
  \Car
\end{pspicture}}
\end{document}

enter image description here

In your case I should mention, that the code doesn't yet account for all possible or impossible path windings :)

Christoph
  • 16,593
  • 1
  • 23
  • 47
11
\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pstricks-add}
\begin{document}

\multido{\iA=1+1,\iB=4+1}{40}{%
\begin{pspicture}[algebraic](-.2,-2.2)(\dimexpr\psPiFour cm+.2cm,2.2)
    \psparametricplot[linecolor=gray,plotpoints=200]{0}{TwoPi 2 mul}{ t | 2*cos(t) }
    \pscurvepoints[plotpoints=100]{0}{4 Pi mul}{ t | 2*cos(t) }{P}
    \pspolylineticks[ticksize=0 0,metricInitValue=1,Os=1,Ds=.2]{P}{ ds }{1}{100}%
    \pspolygon*[linecolor=gray](PTick\iA)(PTick\iB)([offset=-.5]{PTick\iA}PTick\iB)([offset=.5]{PTick\iB}PTick\iA)
    \qdisk(PTick\iA){.1}\qdisk(PTick\iB){.1}
\end{pspicture}%
}

\end{document}

enter image description here