Draw an animation of a piston driving a wheel in C#

This example draws a piston that powers a rotating wheel. The program uses a timer to move the piston. The following code shows the timer's event handler.

// Move the piston.
private void tmrMovePiston_Tick(
object sender, EventArgs e)
{
X += Dx;

if ((X < Xmin) || (X > Xmax))
{
Dx = -Dx;
X += 2 * Dx;
}

DrawSystem();
picCanvas.Refresh();
}

The event handler simply adds Dx to the X position of the piston. If X goes beyond its allowed bounds, the event handler reverses the sign of Dx to make the piston move in the other direction next time.

The timer's event handler then calls the following DrawSystem method to draw the picture.

// Draw everything.
private void DrawSystem()
{

Gr.Clear(this.BackColor);
Gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

using (Pen custom_pen = new Pen(Color.Blue, 2))
{
// Draw the wheel.
RectangleF wheel_rect = new RectangleF(
Cx - Radius, Cy - Radius,
2 * Radius, 2 * Radius);
Gr.FillEllipse(Brushes.LightBlue, wheel_rect);
custom_pen.Color = Color.Blue;
custom_pen.Width = 2;
Gr.DrawEllipse(custom_pen, wheel_rect);

// Draw the cylinder.
float cylinder_length = L2 + PistonLength + 2 * Gap;
float cylinder_radius = PistonRadius + 2 * Gap;
PointF[] cylinder_lines = new PointF[]
{
new PointF(Ax + cylinder_length, Cy - cylinder_radius / 2 + Gap),
new PointF(Ax + cylinder_length, Cy - cylinder_radius / 2),
new PointF(Ax, Cy - cylinder_radius / 2),
new PointF(Ax, Cy + cylinder_radius / 2),
new PointF(Ax + cylinder_length, Cy + cylinder_radius / 2),
new PointF(Ax + cylinder_length, Cy + cylinder_radius / 2 - Gap),
};
Gr.DrawLines(custom_pen, cylinder_lines);

// Draw the piston.
GraphicsPath piston_path = new GraphicsPath();
PointF[] piston_lines = new PointF[]
{
new PointF(X + PistonLength, Cy - PistonRadius / 2),
new PointF(X, Cy - PistonRadius / 2),
new PointF(X, Cy + PistonRadius / 2),
new PointF(X + PistonLength, Cy + PistonRadius / 2),
new PointF(X + PistonLength, Cy + ArmRadius / 2),
new PointF(X + PistonLength + ArmLength, Cy + ArmRadius / 2),
new PointF(X + PistonLength + ArmLength, Cy - ArmRadius / 2),
new PointF(X + PistonLength, Cy - ArmRadius / 2),
};
piston_path.AddPolygon(piston_lines);
Gr.FillPath(Brushes.LightGreen, piston_path);
custom_pen.Color = Color.Green;
Gr.DrawPath(custom_pen, piston_path);
Gr.DrawLine(custom_pen, X + Gap, Cy - PistonRadius / 2, X + Gap, Cy + PistonRadius / 2);
Gr.DrawLine(custom_pen, X + 2 * Gap, Cy - PistonRadius / 2, X + 2 * Gap, Cy + PistonRadius / 2);

// Find the ends of the linkage.
float linkage_x1 = X + PistonLength + ArmLength;
float linkage_y1 = Cy;
PointF pt1, pt2;
FindCircleCircleIntersections(
linkage_x1, linkage_y1, L1,
Cx, Cy, Radius,
out pt1, out pt2);
float linkage_x2, linkage_y2;
if (Dx > 0)
{
linkage_x2 = pt1.X;
linkage_y2 = pt1.Y;
}
else
{
linkage_x2 = pt2.X;
linkage_y2 = pt2.Y;
}

// Draw the linkage.
custom_pen.Color = Color.Green;
custom_pen.Width = 5;
Gr.DrawLine(custom_pen, linkage_x1, linkage_y1, linkage_x2, linkage_y2);
custom_pen.Color = Color.LightGreen;
custom_pen.Width = 2;
Gr.DrawLine(custom_pen, linkage_x1, linkage_y1, linkage_x2, linkage_y2);

// Draw joints.
Gr.FillEllipse(Brushes.Black, Cx - 4, Cy - 4, 8, 8);
Gr.FillEllipse(Brushes.Green,
linkage_x1 - 4, linkage_y1 - 4, 8, 8);
Gr.FillEllipse(Brushes.Green,
linkage_x2 - 4, linkage_y2 - 4, 8, 8);
}
}

Most of this code is simply drawing. The only real trick is figuring out how the wheel should be rotated. The trick is find the end points of the green rod that connects the piston to the wheel.

The first end point is at the end of the piston.

The trick to finding the second end point is to notice that it must lie on the wheel and it must lie on the dashed circle centered at the end of the piston as shown in the picture on the right. Mathematically that end point lies at the intersection of the dashed circle and the circle defined by the wheel.

The example Determine where two circles intersect in C# explains how to determine where two circles intersect. This example uses the FindCircleCircleIntersections method described by that example.

Note that two circles may intersect in two places, as they do in this example. That means there are two possible configurations for this system. In the second configuration, the second end point is at the bottom of the wheel. When the piston is pulling back, the DrawSystem method uses that solution so the rod's second end point moves all the way around the circle instead of just moving back and forth over the top of the wheel.

The picture on the right shows the complete program. Use the textboxes to experiment with the length of the connecting rod and the wheel's radius.

 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this post.
Comments
  • No comments exist for this post.
Leave a comment

Submitted comments are subject to moderation before being displayed.

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.