## Introduction

This simple program draws hypotrochoids, the patterns the toy spirograph makes.

As we all know, to draw a circle using a radius and an angle, we use the formula:

```
x=radius * cos(angle);
y=radius * sin(angle);
```

So if we draw points at `x,y`

as we change the angle from `0 `

to `2PI`

, we will draw a circle. This is equivalent pinning a `string `

to a point, tying a pencil to the far end then carefully moving the pen in a circle with the `string `

taut. Or instead of `string`

, we could pin something solid to the table. What if we pinned a second rod to the end of the first, stuck a pen through a hole in the 2^{nd} rod then moved the 1^{st} circle at one speed when we moved the second circle faster, all the while keeping pen on paper. You would be adding two rotating circles together. The math is:

```
x=R1 * cos(speed1*A1) + R2 * cos(speed2*A2);
y=R1 * sin(speed1*A1) + R2 * sin(speed2*A2);
```

To add a third circle, just add a third term. Next, we don't always want to neatly line up all the rods in a row straight up. To let each rod start at any angle, add a phase to the angle:

```
x=R1 * cos(speed1*A1 + phase1);
y=R1 * sin(speed1*A1 + phase1);
```

This little program lets you enter as many wheels as you want in a grid, and draw the pattern.

## Points of Interest

Here is the main drawing loop. It draws short line segments from each calculated point. To make sure the ends connect, it saves the location of the first point calculated and joins it after the main loop is completed.

```
private void DrawHypotrochoid(PaintEventArgs e)
{
Graphics g = e.Graphics;
if ( dataGridViewParms.RowCount > 0 )
{
int i;
int max;
// current angle we are calculating with,
// goes all the way around one circle
double angle;
double step;
// how big do we step around the circle.
// smaller steps make a smoother drawing
double twoPI;
double rx, ry; // where the next point is, floating point
double scale; // arbitrary scaling factor chosen by user
// cx and cy are used to move the drawing to the center of the panel
int x, y, cx, cy;
int fromX=0, fromY=0; // where we drew the previous point
// save the first point so we can neatly connect the
// last segment of the curve up.
int finalX = 0, finalY = 0;
// when calculating the first point we don't draw it, we just save it.
Boolean firstLine = true;
Pen pen=new Pen(mainColour);
// to hold the parms from the grid
double[] radius = new double[dataGridViewParms.RowCount];
// don't want to convert them in an inner loop!
double[] speed = new double[dataGridViewParms.RowCount];
double[] phase = new double[dataGridViewParms.RowCount];
// clear the panel
g.FillRectangle(Brushes.White, 0, 0, panelDraw.Width, panelDraw.Height);
Cursor.Current = Cursors.WaitCursor;
// get the parms from the grid into doubles
max = dataGridViewParms.RowCount - 1; // don't process the blank row at the end of the grid
for ( i=0; i < max; i++ )
{
radius[i]=double.Parse(dataGridViewParms.Rows[i].Cells[0].Value as string);
speed[i] = double.Parse(dataGridViewParms.Rows[i].Cells[1].Value as string);
phase[i] = double.Parse(dataGridViewParms.Rows[i].Cells[2].Value as string);
}
pen.Width = int.Parse(textBoxLineWidth.Text);
scale = double.Parse(textBoxScale.Text);
twoPI=Math.PI * 2.0;
// twoPI radians = 360 degrees = one circle
step = twoPI / double.Parse(textBoxSteps.Text);
cx = panelDraw.Width / 2;
cy = panelDraw.Height / 2;
// loop around one full circle
for (angle = 0.0; angle < twoPI; angle += step)
{
rx = 0.0;
ry = 0.0;
// each line in the grid is like adding a wheel to a spirograph toy,
// we just add up where each "wheel" move the point
for (i = 0; i < max; i++)
{
rx += radius[i] * Math.Cos(speed[i] * (angle + phase[i]));
ry += radius[i] * Math.Sin(speed[i] * (angle + phase[i]));
}
// convert our double to int int that fits nicely on our panel
rx *= scale;
ry *= scale;
x = (int)rx + cx;
y = (int)ry + cy;
if (firstLine)
{
// save this point
finalX=fromX = x;
finalY=fromY = y;
firstLine = false;
}
else
{
// draw from the last point to the new point
g.DrawLine(pen, fromX, fromY, x, y);
fromX = x;
fromY = y;
}
}
// and draw the very last connecting segment
g.DrawLine(pen, fromX, fromY, finalX, finalY);
Cursor.Current = Cursors.Default;
}
}
```

## History

- Initial version