Search code examples
c#mouseeventpictureboxmagick.net

.Net Winforms picture box custom drag is twitchy


I am making an application in which I load a bunch of 256x256px chunks of a map and stitch them together with magick.net to then display them in a picture box. I added a feature for slewing the image (and loading new picture chunks as I do) using mouse events on the picture box control like so:

// Set true when mouse should slew the picture in the picture box
private bool isSlewing = false;
// last position of the mouse in map space when slewing
private float lastX = 0;
private float lastY = 0;

private void Pb_MouseMove(object sender, MouseEventArgs e)
{
    // slew while mouse is down.
    if (isSlewing)
    {
        Tuple<float, float> mouseMapCoordinates = MouseToMap(e.X, e.Y);

        Slew(mouseMapCoordinates.Item1 - lastX, mouseMapCoordinates.Item2 - lastY);
        lastX = mouseMapCoordinates.Item1;
        lastY = mouseMapCoordinates.Item2;
    }
}

private void Pb_MouseDown(object sender, MouseEventArgs e)
{
    isSlewing = true;
    Tuple<float, float> mouseMapCoordinates = MouseToMap(e.X, e.Y);

    lastX = mouseMapCoordinates.Item1;
    lastY = mouseMapCoordinates.Item2;
}

private void Pb_MouseUp(object sender, MouseEventArgs e)
{
    isSlewing = false;
}

// Slews map image by dx and dY
public void Slew(float dX, float dY)
{
    X -= dX;
    Y -= dY;
}

// Converts mouse coordinates into map coordinates
private Tuple<float,float> MouseToMap(int mouseX, int mouseY)
{
    // works
}

X and Y are properties that invalidate the picture box by setting a boolean needsUpdate to true. A timer is run and every 200ms it calls the update function which builds the picture based on X and Y . There also is a zoom but that's unimportant for now and works fine.

The issue is that when I click and drag the map it works in principle but every so often the map "jumps" back to a previous mouse position.

I just don't know how to go about trying to find out what's causing it or how to fix it.
I would like the whole program to be a bit more responsive to begin with, it feels sluggish, which is why I introduced the timer to begin with, so it doesn't draw updates on every little change dozens of times per second as I move the mouse a tiny amount.

I think it has to do with some kind of race condition that sets lastX and lastY between one slew and the next with the update drawing things in between. The update function takes a few milliseconds to complete, as it loads the images from disk and composites them and finally draws them back to the picture box.

The whole code is available at github. Map data is not included, but can be downloaded with the script in Assets. For testing I recommend deleting all but one map from the json file there. All map data in total is about 3GB.

I tried putting lock() around the block inside the if statement in Pb_MouseMove, but that didn't help anything.


Solution

  • The issue was with converting the mouse coordinates into map space outside of slew because the map space moves with the viewport. To fix the issue you save the raw mouse position in lastX and lastY, put the raw delta for the mouse coordinate into the slew function and then multiply by a scaling factor. The scaling factor is how many map coordinate units are used per pixel.