Animate 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 Tick 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 Tick event handler then calls the following DrawSystem method to draw the picture.

// Draw everything.
private void DrawSystem()
{

    Gr.Clear(this.BackColor);
    Gr.SmoothingMode = 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. That point has X coordinate given by the variable X and Y coordinate that places it in the middle of the piston vertically.

The trick to finding the rod’s second end point is to notice that it must lie on the wheel and that it must lie on the dashed circle centered at the end of the piston as shown in the picture on the right. The rod’s second 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 zero, one, or two places. in this example the circles usually intersect in two places, as shown in the previous picture. That means there are two possible configurations for the system, one with the rod attached to the upper half of the wheel and one with it attached to the bottom half. When the piston is pulling back, the DrawSystem method uses the second 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.



Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in algorithms, animation, drawing, graphics, multimedia and tagged , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *