0

My code isn't working! I've commented out my problems with /**'s.
I have a OutOfMemory exception when I close the pendulum form as I am passing an IntPtr handle and it becomes to large.
I also have a problem making the pendulum swing and loose velocity each time. Is their a problem in my theory or have I done something stupid.
Below is the code to my pendulum class:

using System.Drawing; //new
using System.Windows.Forms; //new

  class Pendulum
{
    int length = 50;
    double angle = Math.PI /2;
    double aAcc = -9.81;
    double aVel = 0;
    double gravity = 0.1;
    double mass = 0.2;
    Timer timer;

    public Pendulum(int frmWidth, int frmHeight, IntPtr handle)
    {
        timer = new Timer() { Interval = 30 };

        timer.Tick += delegate(object sender, EventArgs e)
        {
            int originX = frmWidth / 2;
            int originY = 0;
            int bobX; // = frmWidth / 2;
            int bobY; // = (int)length;

            //to be relative to origin we go:
            bobX = originX + (int)(Math.Sin(angle) * length);
            bobY = originY +  (int)(Math.Cos(angle) * length);

            aAcc = -9.81 / length * Math.Sin(angle);

            aVel += aAcc * gravity * mass;
            angle += aVel * gravity; //angle += aVel;
            //aVel = aVel -0.09; /** SPEED UP DRAMATICALLY! SPINS WEIRDLY! **/
            DrawPendulum(originX,originY,bobX,bobY,frmWidth,frmHeight, handle);

        };

        timer.Start();
    }

    public void DrawPendulum(int originX, int originY, int bobX, int bobY, int frmWidth, int frmHeight, IntPtr handle)
    {
        using(Bitmap dblBuffer = new Bitmap(frmWidth, frmHeight))
        using(Graphics g = Graphics.FromImage(dblBuffer))
        using(Graphics f = Graphics.FromHwnd(handle))
        {

        g.DrawLine(Pens.Black, originX, originY, bobX, bobY);
        g.FillEllipse(Brushes.Blue, bobX - 8, bobY, 20, 20); //-8 for tidyness!

        f.Clear(Color.White);
        f.DrawImage(dblBuffer, new Point(0, 0));

        }
    }
}

 public partial class frmPendulum : Form
{
    Pendulum p;
    public frmPendulum()
    {
        frmLogin frm = new frmLogin();
        frm.Close();

        int frmWidth = this.Width;
        int frmHeight = this.Height;
        IntPtr Handle = this.Handle;

         p = new Pendulum(frmWidth, frmHeight, Handle);

        InitializeComponent();
    }

    private void frmPendulum_Load(object sender, EventArgs e)
    {

    }

  public void TimerDispose()
        {
            timer.Dispose();
        }
    }

public partial class frmLogin : Form
    {
        public frmLogin()
    {
        InitializeComponent();
    }

    private void frmLogin_Load(object sender, EventArgs e)
    {

    }

    private void btnSubmit_Click(object sender, EventArgs e)
    {
        frmPendulum f = new frmPendulum();
        f.Show();
        //this.Hide();
    }
}

EDIT: To try and dispose the timer I've made Pendulum p global so that it can be used to run the new method I've put inside the pendulum class below:

    public void TimerDispose()
    {
        timer.Dispose();
    }

This also meant making the timer global. However the OutOfMemory exception still occurs. All code is its current state above.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Corey Ford
  • 178
  • 1
  • 13
  • 1
    Why are you using an `IntPtr` at all? What are you trying to draw the pendulum onto? I very much doubt that it's a problem of it getting "too large" - it's just that it'll become invalid when the original handle is closed elsewhere. – Jon Skeet Oct 05 '13 at 08:30
  • I guess you have to dispose the bitmap, the graphics and maybe other resources. Otherwise this will easily lead to oom – MichaC Oct 05 '13 at 08:31
  • @Ela Thanks, but the error occurs before the form has a chance to dispose the bitmap and graphics. I've edited my code above to show what I've done. – Corey Ford Oct 05 '13 at 12:18
  • @JonSkeet I am trying to draw the pendulum onto the form frmPendulum. The specific error is and out of memory exception. As you can probably tell, I'm not an expert with Handles. – Corey Ford Oct 05 '13 at 12:20

1 Answers1

0

What a mess....

Handle the Paint() event of frmPendulum() and pass its e.Graphics to DrawPendulum().
frmPendulum should have its own Timer that simply refreshes itself.

In Pendulum, draw directly to the passed Graphics. Get rid of the Bitmap and Handle code. The variables should all be at class level so they can be accessed in DrawPendulum(). Pendulum should not draw itself in the Tick() event.

So it should look more like this:

public partial class frmPendulum : Form
{

    private Timer timer;
    private Pendulum p = null;

    public frmPendulum()
    {
        InitializeComponent();

        this.Shown += new EventHandler(frmPendulum_Shown);
        this.Paint += new PaintEventHandler(frmPendulum_Paint);
    }

    void frmPendulum_Shown(object sender, EventArgs e)
    {
        p = new Pendulum(this.ClientRectangle.Width, this.ClientRectangle.Height);
        timer = new Timer() { Interval = 100 };
        timer.Tick += delegate(object s2, EventArgs e2)
        {
            this.Refresh();
        };
        timer.Start();
    }

    void frmPendulum_Paint(object sender, PaintEventArgs e)
    {
        if (p != null)
        {
            p.DrawPendulum(e.Graphics);
        }
    }

}

public class Pendulum
{

    int length = 50;
    double angle = Math.PI / 2;
    double aAcc = -9.81;
    double aVel = 0;
    double gravity = 0.1;
    double mass = 0.2;

    int originX = 0;
    int originY = 0;
    int bobX; // = frmWidth / 2;
    int bobY; // = (int)length;

    Timer timer;

    public Pendulum(int frmWidth, int frmHeight)
    {
        timer = new Timer() { Interval = 50 };

        timer.Tick += delegate(object sender, EventArgs e)
        {
            originX = frmWidth / 2;
            originY = 0;

            //to be relative to origin we go:
            bobX = originX + (int)(Math.Sin(angle) * length);
            bobY = originY + (int)(Math.Cos(angle) * length);

            aAcc = -9.81 / length * Math.Sin(angle);

            aVel += aAcc * gravity * mass;
            angle += aVel * gravity; 
        };

        timer.Start();
    }

    public void DrawPendulum(Graphics G)
    {
        G.DrawLine(Pens.Black, originX, originY, bobX, bobY);
        G.FillEllipse(Brushes.Blue, bobX - 8, bobY, 20, 20); //-8 for tidyness!
    }

}

enter image description here

Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
  • Fantastic, you are a Legend! I apologies for the mess, I'm not really used to this drawing style code. Just to clarify the drawing has to be done inside of the form and pass its own graphics to the pendulum class. – Corey Ford Oct 07 '13 at 18:43
  • There are other ways to do it...but this seemed to best fit what you were trying to achieve. ~I think~ – Idle_Mind Oct 07 '13 at 20:04
  • Just one more question about your code. When you add `if (p != null) { p.DrawPendulum(e.Graphics); }` Why is the if statement used instead of just p.DrawPendulum... – Corey Ford Oct 09 '13 at 20:04
  • Because the Paint() event could occur BEFORE "p" has been instantiated. In my example, I didn't create it until the Shown() event. This means the form has already been displayed and therefore the Paint() event had already occurred. Without that check, it would have crashed with a null reference exception. – Idle_Mind Oct 09 '13 at 20:09