# Stacked kinematic graphs with Asymptote

Update 2: Current versions of the code are now available at my GitHub repository.

Update: The code below is not as encapsulated as I would like, but it’s still pretty usable. The different versions reflect different amounts of refactoring, but I hope that they give you enough to work with. If you have a question about how to modify one of my examples to product your own favorite version of a stacked graph, please leave a comment, and I’ll try to create one within a reasonable time.

I’ve been using Asymptote to create graphs like the stacked kinematic graphs below.  I’ll post more complicated graphs in the future, but here’s a simple one with axes only.  I like Asymptote’s ability to use consistent units in the graphs so that the t-axis is synchronized.  Although it takes more work to create a version in Asymptote than in a graphics package, I can immediately differentiate it to different problems just by changing a line here and there.

Old version with x/v/at graphs: kinematic_stack.pdf kinematic_stack.png

New version (from refactored code) with s/v/at graphs: kinematic_stack_pos_vel_acc.pdf kinematic_stack_pos_vel_acc.png (N.B. IB Physics uses the convention that position is given by the variable s, so I try to use it with students, although we also use x).

```// Asymptote code for kinematic_stack_pos_vel_acc.asy
import graph;

pen axis_p = linewidth(1.4)+black;
pen grid_p = linewidth(1.0)+gray(0.2);

real hticks = 5;
real vMin_ticks = -5;
real vMax_ticks = 5;

void kingraph(picture pic, Label vL=&quot;&quot;, real vMin=vMin_ticks, real vMax=vMax_ticks, Label hL=&quot;\$t\$&quot;, real hMin=0, real hMax=hticks) {
scale(pic, Linear, Linear);
xlimits(pic, hMin, hMax);
ylimits(pic, vMin, vMax);
xaxis(pic, hL, YZero, axis_p, Arrow(6));
yaxis(pic, vL, XZero, axis_p, Arrow(6));
}

picture pos_pic;
kingraph(pos_pic, &quot;\$s\$&quot;);

picture vel_pic;
kingraph(vel_pic, &quot;\$v\$&quot;);

picture acc_pic;
kingraph(acc_pic, &quot;\$a\$&quot;);

//xequals(pos_pic,3,Dotted);
//xequals(vel_pic,3,Dotted);
//xequals(acc_pic,3,Dotted);

// boring code for stacking the graphs.  The only interesting part is the htick/vtick settings, which can be used to change the size of the horizontal and vertical units of the graphs.
void stack(picture pics[]) {
real margin=2mm;
real htick = .8cm;
real vtick = .4cm;
frame[] frames = new frame[pics.length];
for(int i=0; i&lt;pics.length; ++i) {     unitsize(pics[i], htick, vtick);     frames[i] = pics[i].fit();     if (i&gt;0) {
frames[i] = shift(0,min(frames[i-1]).y-max(frames[i]).y-margin)*frames[i];
}
}
}

stack(new picture[] {pos_pic, vel_pic, acc_pic});
```

With numbered ticks: kinematic_stack_pos_vel_acc_nticks.pdf kinematic_stack_pos_vel_acc_nticks.png

```// Asymptote code for kinematic_stack_pos_vel_acc_nticks.asy
import graph;

pen axis_p = linewidth(1.4)+black;
pen tick_p = linewidth(1.0)+gray(0.2)+fontsize(8);
pen ticklabel_p = tick_p;
int hticks = 5;
int vMin_ticks = -5;
int vMax_ticks = 5;
real[] hTicks_a = sequence(1, hticks);
real[] vTicks_a = sequence(vMin_ticks, vMax_ticks);
ticks hTicks = Ticks(format=Label(&quot;\$%.4g\$&quot;, align=E, p=ticklabel_p), Ticks=hTicks_a, Size=1mm, pTick=tick_p);
ticks vTicks = Ticks(format=Label(&quot;\$%.4g\$&quot;, align=W, p=ticklabel_p), Ticks=vTicks_a, Size=1mm, pTick=tick_p);
real axis_extra = 0.7; // extend the axis just a bit past the last tick mark

axis VZero(bool extend=true) {
return new void(picture pic, axisT axis) {
axis.type = 0; // Value
axis.value = pic.scale.x.T(pic.scale.x.scale.logarithmic ? 1 : 0); // I'm good with Linear 0
axis.position = 1; // relative position of axis label
axis.side = left;
axis.align = 5*E;
axis.extend = extend;
};
}
axis VZero = VZero();

axis HZero(bool extend=true) {
return new void(picture pic, axisT axis) {
axis.type = 0; // Value
axis.value = pic.scale.y.T(pic.scale.y.scale.logarithmic ? 1 : 0); // I'm good with Linear 0
axis.position = 0.5; // relative position of axis label
axis.side = right;
axis.align = W;
axis.extend = extend;
};
}
axis HZero = HZero();

void kingraph(picture pic, Label vL=&quot;&quot;, real vMin=vMin_ticks-axis_extra, real vMax=vMax_ticks+axis_extra, Label hL=&quot;\$t\$ [sec]&quot;, real hMin=0, real hMax=hticks+axis_extra) {
scale(pic, Linear, Linear);
xlimits(pic, hMin, hMax);
ylimits(pic, vMin, vMax);
xaxis(pic=pic, L=hL, axis=VZero(false), p=axis_p, ticks=hTicks, arrow=Arrow(6), above=false);
yaxis(pic=pic, L=vL, axis=HZero(false), p=axis_p, ticks=vTicks, arrow=Arrow(6), above=false);
}

picture pos_pic;
kingraph(pos_pic, &quot;\$s\$ [m]&quot;);

picture vel_pic;
kingraph(vel_pic, &quot;\$v\$ [m/sec]&quot;);

picture acc_pic;
kingraph(acc_pic, &quot;\$a\$ [m/sec/sec]&quot;);

//xequals(pos_pic,3,Dotted);
//xequals(vel_pic,3,Dotted);
//xequals(acc_pic,3,Dotted);

// boring code for stacking the graphs.  The only interesting part is the htick/vtick settings, which can be used to change the size of the horizontal and vertical units of the graphs.
void stack(picture pics[]) {
real margin=2mm;
real htick = .8cm;
real vtick = .4cm;
frame[] frames = new frame[pics.length];
for(int i=0; i&lt;pics.length; ++i) {
unitsize(pics[i], htick, vtick);
frames[i] = pics[i].fit();
if (i&gt;0) {
frames[i] = shift(0,min(frames[i-1]).y-max(frames[i]).y-margin)*frames[i];
}
}
}

stack(new picture[] {pos_pic, vel_pic, acc_pic});
```

With unnumbered ticks: kinematic_stack_pos_vel_acc_ticks.pdfkinematic_stack_pos_vel_acc_ticks.png

```// Asymptote code for kinematic_stack_pos_vel_acc_ticks.asy
import graph;

pen axis_p = linewidth(1.4)+black;
pen tick_p = linewidth(1.0)+gray(0.2)+fontsize(.01);
pen ticklabel_p = tick_p;
int hticks = 10;
int vMin_ticks = -5;
int vMax_ticks = 5;
real[] hTicks_a = sequence(1, hticks);
real[] vTicks_a = sequence(vMin_ticks, vMax_ticks);
ticks hTicks = Ticks(format=Label(&quot; &quot;, align=E, p=ticklabel_p), Ticks=hTicks_a, Size=1mm, pTick=tick_p);
ticks vTicks = Ticks(format=Label(&quot; &quot;, align=W, p=ticklabel_p), Ticks=vTicks_a, Size=1mm, pTick=tick_p);
real axis_extra = 0.7; // extend the axis just a bit past the last tick mark

axis VZero(bool extend=true) {
return new void(picture pic, axisT axis) {
axis.type = 0; // Value
axis.value = pic.scale.x.T(pic.scale.x.scale.logarithmic ? 1 : 0); // I'm good with Linear 0
axis.position = 1; // relative position of axis label
axis.side = left;
axis.align = .01*S;
axis.extend = extend;
};
}
axis VZero = VZero();

axis HZero(bool extend=true) {
return new void(picture pic, axisT axis) {
axis.type = 0; // Value
axis.value = pic.scale.y.T(pic.scale.y.scale.logarithmic ? 1 : 0); // I'm good with Linear 0
axis.position = 1; // relative position of axis label
axis.side = right;
axis.align = .01*W;
axis.extend = extend;
};
}
axis HZero = HZero();

void kingraph(picture pic, Label vL=&quot;&quot;, real vMin=vMin_ticks-axis_extra, real vMax=vMax_ticks+axis_extra, Label hL=&quot;\$t\$&quot;, real hMin=0, real hMax=hticks+axis_extra) {
scale(pic, Linear, Linear);
xlimits(pic, hMin, hMax);
ylimits(pic, vMin, vMax);
xaxis(pic=pic, L=hL, axis=VZero(false), p=axis_p, ticks=hTicks, arrow=Arrow(6), above=false);
yaxis(pic=pic, L=vL, axis=HZero(false), p=axis_p, ticks=vTicks, arrow=Arrow(6), above=false);
}

picture pos_pic;
kingraph(pos_pic, &quot;\$s\$&quot;);

picture vel_pic;
kingraph(vel_pic, &quot;\$v\$&quot;);

picture acc_pic;
kingraph(acc_pic, &quot;\$a\$&quot;);

//xequals(pos_pic,3,Dotted);
//xequals(vel_pic,3,Dotted);
//xequals(acc_pic,3,Dotted);

// boring code for stacking the graphs.  The only interesting part is the htick/vtick settings, which can be used to change the size of the horizontal and vertical units of the graphs.
void stack(picture pics[]) {
real margin=2mm;
real htick = .4cm;
real vtick = .4cm;
frame[] frames = new frame[pics.length];
for(int i=0; i&lt;pics.length; ++i) {
unitsize(pics[i], htick, vtick);
frames[i] = pics[i].fit();
if (i&gt;0) {
frames[i] = shift(0,min(frames[i-1]).y-max(frames[i]).y-margin)*frames[i];
}
}
}

stack(new picture[] {pos_pic, vel_pic, acc_pic});
```

```// Asymptote code for kinematic_stack_pos_vel_acc_grid.asy
import graph;

pen axis_p = linewidth(1.4)+black;
pen grid_p = linewidth(0.8)+gray(0.2);
pen ticklabel_p = fontsize(.01);
int hticks = 10;
int vMin_ticks = -5;
int vMax_ticks = 5;
real[] hTicks_a = sequence(1, hticks);
real[] vTicks_a = sequence(vMin_ticks, vMax_ticks);
real axis_extra = 0.7; // extend the axis just a bit past the last tick mark

axis VZero(bool extend=true) {
return new void(picture pic, axisT axis) {
axis.type = 0; // Value
axis.value = pic.scale.x.T(pic.scale.x.scale.logarithmic ? 1 : 0); // I'm good with Linear 0
axis.position = 1; // relative position of axis label
axis.side = left;
axis.align = 1.5*E;
axis.extend = extend;
};
}
axis VZero = VZero();

axis HZero(bool extend=true) {
return new void(picture pic, axisT axis) {
axis.type = 0; // Value
axis.value = pic.scale.y.T(pic.scale.y.scale.logarithmic ? 1 : 0); // I'm good with Linear 0
axis.position = 1; // relative position of axis label
axis.side = right;
axis.align = W;
axis.extend = extend;
};
}
axis HZero = HZero();

void kingraph(picture pic, Label vL=&quot;&quot;, real vMin=vMin_ticks, real vMax=vMax_ticks, Label hL=&quot;\$t\$&quot;, real hMin=0, real hMax=hticks) {
scale(pic, Linear, Linear);
xlimits(pic, hMin, hMax);
ylimits(pic, vMin, vMax);
ticks hTicks = LeftTicks(format=Label(&quot; &quot;, align=E, p=ticklabel_p), Ticks=hTicks_a, extend=true, pTick=grid_p); // The space clears the labels on the ticks.
ticks vTicks = RightTicks(format=Label(&quot; &quot;, align=W, p=ticklabel_p), Ticks=vTicks_a, extend=true, pTick=grid_p);
xaxis(pic=pic, L=&quot;&quot;, axis=BottomTop, p=grid_p, ticks=hTicks);
yaxis(pic=pic, L=&quot;&quot;, axis=LeftRight, p=grid_p, ticks=vTicks);
xaxis(pic=pic, L=hL, axis=VZero(false), p=axis_p, ticks=NoTicks, arrow=Arrow(6), above=true);
yaxis(pic=pic, L=vL, axis=HZero(false), p=axis_p, ticks=NoTicks, arrow=Arrow(6), above=true);
}

picture pos_pic;
kingraph(pos_pic, &quot;\$s\$&quot;);

picture vel_pic;
kingraph(vel_pic, &quot;\$v\$&quot;);

picture acc_pic;
kingraph(acc_pic, &quot;\$a\$&quot;);

//xequals(pos_pic,3,Dotted);
//xequals(vel_pic,3,Dotted);
//xequals(acc_pic,3,Dotted);

// boring code for stacking the graphs.  The only interesting part is the htick/vtick settings, which can be used to change the size of the horizontal and vertical units of the graphs.
void stack(picture pics[]) {
real margin=0mm;
real htick = .4cm;
real vtick = .4cm;
frame[] frames = new frame[pics.length];
for(int i=0; i&lt;pics.length; ++i) {
unitsize(pics[i], htick, vtick);
frames[i] = pics[i].fit();
if (i&gt;0) {
frames[i] = shift(0,min(frames[i-1]).y-max(frames[i]).y-margin)*frames[i];
}
}
}

stack(new picture[] {pos_pic, vel_pic, acc_pic});
```

Blank position and velocity graphs only: kinematic_stack_pos_vel.pdf kinematic_stack_pos_vel.png

```// Asymptote code for kinematic_stack_pos_vel.asy
import graph;

pen axis_p = linewidth(1.4)+black;
pen grid_p = linewidth(1.0)+gray(0.2);

real hticks = 5;
real vMin_ticks = -5;
real vMax_ticks = 5;

void kingraph(picture pic, Label vL=&quot;&quot;, real vMin=vMin_ticks, real vMax=vMax_ticks, Label hL=&quot;\$t\$&quot;, real hMin=0, real hMax=hticks) {
scale(pic, Linear, Linear);
xlimits(pic, hMin, hMax);
ylimits(pic, vMin, vMax);
xaxis(pic, hL, YZero, axis_p, Arrow(6));
yaxis(pic, vL, XZero, axis_p, Arrow(6));
}

picture pos_pic;
kingraph(pos_pic, &quot;\$s\$&quot;);

picture vel_pic;
kingraph(vel_pic, &quot;\$v\$&quot;);

// boring code for stacking the graphs.  You can change the stack statement at the bottom to choose which graphs to include in what order.  The only interesting part of the stack function is the htick/vtick settings, which can be used to change the size of the horizontal and vertical units of the graphs.
void stack(picture pics[]) {
real margin=2mm;
real htick = .8cm;
real vtick = .4cm;
frame[] frames = new frame[pics.length];
for(int i=0; i&lt;pics.length; ++i) {
unitsize(pics[i], htick, vtick);
frames[i] = pics[i].fit();
if (i&gt;0) {
frames[i] = shift(0,min(frames[i-1]).y-max(frames[i]).y-margin)*frames[i];
}
}
}

stack(new picture[] {pos_pic, vel_pic});
```