Mouse movement stair-stepped?
by Luke D · in Torque Game Builder · 04/29/2005 (5:59 pm) · 11 replies
I've been playing a bit with T2D and the first thing I wanted to do was to be able to click and 'draw' a line by dropping a sprite at every returned position from mouseMove/mouseDragged. Unfortunately the result is bizarre, especially on diagonal lines, producing a 'stair-stepped' pattern (zig-zagging back and forth) instead of a set of points directly in the path of the mouse. If I pick every other point it looks better but it still isn't 100% right.
Is this a bug? Or am I not interpreting the intended use/purpose of the coordinated returned by these callbacks?
Is this a bug? Or am I not interpreting the intended use/purpose of the coordinated returned by these callbacks?
#2
It happens to me too... Most notable when moving the mouse quickly:

Seems that first, the X movement triggers the event and then the Y movement triggers it.
Only updating on every other call seemed to fix the problem for me though.
04/29/2005 (7:18 pm)
Hmm, I did a little testing, assuming your code is similar to this:function sceneWindow2D::onMouseDragged( %this, %mod, %worldPos, %mouseClicks )
{
%drawsprite = new fxStaticSprite2d() { scenegraph = t2dSceneGraph; };
%drawsprite.setGroup(1);
%drawsprite.setLayer(1);
%drawsprite.setPosition( %worldPos );
%drawsprite.setSize( "1" );
%drawsprite.setImageMap("ggLogoImageMap");
}It happens to me too... Most notable when moving the mouse quickly:

Seems that first, the X movement triggers the event and then the Y movement triggers it.
Only updating on every other call seemed to fix the problem for me though.
#3
04/29/2005 (7:54 pm)
Hmm, maybe this code will result in a more accurate 'dotted line of sprites'function sceneWindow2D::onMouseDragged( %this, %mod, %worldPos, %mouseClicks )
{
if($nodraw)
{
$nodraw = false ;
$oldPos = %worldPos; // Set current world position to $oldPosition
}
else
{
%sum = vectorAdd2d( $oldPos, %worldPos ); // Add the old location to the new location
%pos = vectorScale2d( %sum, 0.5 ); // find the midpoint between the current and old position
%linesprite = new fxStaticSprite2d() { scenegraph = t2dSceneGraph; };
%linesprite.setGroup(1);
%linesprite.setLayer(1);
%linesprite.setPosition( %pos );
%linesprite.setSize( "1" );
%linesprite.setImageMap("ggLogoImageMap");
$nodraw = true;
}
}
function sceneWindow2D::onMouseUp( %this, %mod, %worldPos, %mouseClicks )
{
$nodraw = true;
}
#4
04/30/2005 (8:15 am)
If I'm not mistaken, I think this has to do with the errors reported regarding the editor and a few of the gui related classess and their associated headers. I f you compile the engine(using TBE?), you'll likely notice the errors are linked to the container classes. The mouse movements as described above probably are a result of these errors. I could be very wrong about this, however; so don't take me too seriously with this!:)
#5
Gabor, if that is true then hopefully the next release version will clean it up. If not, I guess it calls for diving down into the engine code and start debugging it which is good a reason to get my feet wet in the T2D source as any. :)
04/30/2005 (10:39 am)
Yep that's exactly what I was seeing. And I did the alternating draw/no-draw workaround too but it seemed like those points alone weren't 100% accurately following the line. I used the 'beam' code posted elsewhere to place a sprite between the last X/Y and the new X/Y, rotate it and stretch it to fit like a line/laser beam, and when moving the mouse erratically the resulting line is subtly oddly warped in spots.Gabor, if that is true then hopefully the next release version will clean it up. If not, I guess it calls for diving down into the engine code and start debugging it which is good a reason to get my feet wet in the T2D source as any. :)
#6
04/30/2005 (12:58 pm)
Time permitting, I think I'll see what viable adjustments can be made to the engine to rectify this problem, especially if it's within my capacity to do so!:) Would be great experience nevertheless!:)
#7
When the TGE platform receives a mouse-event, it splits it up into X-component and Y-component events, each of which are processed seperately, like so...
I'm not exactly sure why it was done this way but I can see that the 'event' packet structure only has room for a single float value which in itself isn't a good reason to do it this way although I may not have all the facts here.
You can see the X/Y events being processed in this list which shows global->local-world point conversions. Notice that the X component is updated, leaving the old Y as it was, followed immediately by the Y update and so on resulting in the X/Y components appearing in pairs.
Fixing this is pretty simple but because this is core, I'm not at liberty to change this now but I will speak to Josh/Ben about this issue and see what can be done.
- Melv.
05/01/2005 (3:57 am)
This is a wierd effect so I wrote a little test code and I think I may have found what the problem is.When the TGE platform receives a mouse-event, it splits it up into X-component and Y-component events, each of which are processed seperately, like so...
void GuiCanvas::processMouseMoveEvent(const MouseMoveEvent *event)
{
InputEvent iEvent;
iEvent.deviceType = MouseDeviceType;
iEvent.objType = SI_XAXIS;
iEvent.fValue = event->xPos - cursorPt.x;
iEvent.modifier = event->modifier;
processInputEvent(&iEvent);
iEvent.objType = SI_YAXIS;
iEvent.fValue = event->yPos - cursorPt.y;
processInputEvent(&iEvent);
}At low mouse-speeds, this wouldn't be a problem but when the speed is high, you'll really start to see the seperate X/Y component steps.I'm not exactly sure why it was done this way but I can see that the 'event' packet structure only has room for a single float value which in itself isn't a good reason to do it this way although I may not have all the facts here.
You can see the X/Y events being processed in this list which shows global->local-world point conversions. Notice that the X component is updated, leaving the old Y as it was, followed immediately by the Y update and so on resulting in the X/Y components appearing in pairs.
(27 62)->(27 40)->(-477.440002 -330.851196) (26 62)->(26 40)->(-478.720001 -330.851196) (26 58)->(26 36)->(-478.720001 -336.166077) (24 58)->(24 36)->(-481.279999 -336.166077) (24 56)->(24 34)->(-481.279999 -338.823517) (23 56)->(23 34)->(-482.559998 -338.823517) (23 53)->(23 31)->(-482.559998 -342.809692) (22 53)->(22 31)->(-483.839996 -342.809692) (22 50)->(22 28)->(-483.839996 -346.795837) (21 50)->(21 28)->(-485.119995 -346.795837) (21 48)->(21 26)->(-485.119995 -349.453278)In the above image by Joshua, the higher 'track' of objects is the correct one with the lower one being where the initial X-component is added.
Fixing this is pretty simple but because this is core, I'm not at liberty to change this now but I will speak to Josh/Ben about this issue and see what can be done.
- Melv.
#8
This is a temporary fix but may not be the final solution. I thought I'd post this so you don't have to work around it. Please be aware that this isn't part of the T2D release officially at the moment until if can be looked at in more detail; I'm just trying to help you directly here. :)
Replace the following function in "guiCanvas.cc"...
All the best,
- Melv.
05/02/2005 (3:52 am)
After speaking with Ben, he corrected me slight as the idea of handling these components seperately is related to dealing with devices abstractly so that n-axis devices are supported fully; the downside here being that two events are generated from the two seperated axii.This is a temporary fix but may not be the final solution. I thought I'd post this so you don't have to work around it. Please be aware that this isn't part of the T2D release officially at the moment until if can be looked at in more detail; I'm just trying to help you directly here. :)
Replace the following function in "guiCanvas.cc"...
void GuiCanvas::processMouseMoveEvent(const MouseMoveEvent *event)
{
/*
InputEvent iEvent;
iEvent.deviceType = MouseDeviceType;
iEvent.objType = SI_XAXIS;
iEvent.fValue = event->xPos - cursorPt.x;
iEvent.modifier = event->modifier;
processInputEvent(&iEvent);
iEvent.objType = SI_YAXIS;
iEvent.fValue = event->yPos - cursorPt.y;
processInputEvent(&iEvent);
*/
if( cursorON )
{
//copy the modifier into the new event
mLastEvent.modifier = event->modifier;
Point2F pt(cursorPt.x, cursorPt.y);
pt.x += ( F32(event->xPos - cursorPt.x) * mPixelsPerMickey);
cursorPt.x = getMax(0, getMin((S32)pt.x, mBounds.extent.x - 1));
pt.y += ( F32(event->yPos - cursorPt.y) * mPixelsPerMickey);
cursorPt.y = getMax(0, getMin((S32)pt.y, mBounds.extent.y - 1));
mLastEvent.mousePoint.x = S32(cursorPt.x);
mLastEvent.mousePoint.y = S32(cursorPt.y);
if (mMouseButtonDown)
rootMouseDragged(mLastEvent);
else if (mMouseRightButtonDown)
rootRightMouseDragged(mLastEvent);
else if(mMouseMiddleButtonDown)
rootMiddleMouseDragged(mLastEvent);
else
rootMouseMove(mLastEvent);
}
}All the best,
- Melv.
#9
Luke
05/02/2005 (7:16 pm)
Awesome, thanks a lot Melv. :) And if I haven't said it elsewhere, great job on T2D overall, it has been a lot of fun to dig into.Luke
#10
I just committed the above fix to the internal Torque core repo, so this should go out in Torque 1.4 (and T2D accordingly at that time).
05/03/2005 (3:05 pm)
Melv's fix works great. A slighly better fix is as follows:void GuiCanvas::processMouseMoveEvent(const MouseMoveEvent *event)
{
InputEvent iEvent;
iEvent.deviceType = MouseDeviceType;
iEvent.objType = SI_XAXIS;
iEvent.fValue = event->xPos - cursorPt.x;
iEvent.modifier = event->modifier;
processInputEvent(&iEvent, true);
iEvent.objType = SI_YAXIS;
iEvent.fValue = event->yPos - cursorPt.y;
processInputEvent(&iEvent);
}
bool GuiCanvas::processInputEvent(const InputEvent *event)
{
return(processInputEvent(event, false));
}
bool GuiCanvas::processInputEvent(const InputEvent *event, bool suppressMouse)
{
.
. (irrelevant code omitted from paste)
.
if (moved && !suppressMouse)
{
mLastEvent.mousePoint.x = S32(cursorPt.x);
mLastEvent.mousePoint.y = S32(cursorPt.y);
if (mMouseButtonDown)
rootMouseDragged(mLastEvent);
else if (mMouseRightButtonDown)
rootRightMouseDragged(mLastEvent);
else if(mMouseMiddleButtonDown)
rootMiddleMouseDragged(mLastEvent);
else
rootMouseMove(mLastEvent);
}
.
. (irrelevant code omitted from paste)
.
}
****Also make the appropriate change to the GuiCanvas header.I just committed the above fix to the internal Torque core repo, so this should go out in Torque 1.4 (and T2D accordingly at that time).
#11
However, now I'm getting a strange behavior with sceneWindow2D::onMouseMove(). If I move the mouse horizontally in some locations, I never get this event. If I switch back to my pre-fix build, this problem goes away.
Looking at the source the problem arises from the fact that processInputEvent always surpresses the X movement events and Y only fires the event off if there's actual movement in the Y axis. It took me awhile to settle on the following code fix:
This basically removes the 'moved' boolean and directly checks either X or Y for changes between the current location and the last. It seems to work perfectly, but I didn't delve too far into how cursorPt and lastCursorPt are used/abused elsewhere, so there may be a catch I didn't spot. Since suppressMove is checked first, we still end up doing the comparisons only once each per processMouseMoveEvent call, when 'Y' is being processed.
05/24/2005 (12:43 am)
I recently implemented this fix, and it solves the problem.However, now I'm getting a strange behavior with sceneWindow2D::onMouseMove(). If I move the mouse horizontally in some locations, I never get this event. If I switch back to my pre-fix build, this problem goes away.
Looking at the source the problem arises from the fact that processInputEvent always surpresses the X movement events and Y only fires the event off if there's actual movement in the Y axis. It took me awhile to settle on the following code fix:
if(event->objType == SI_XAXIS || event->objType == SI_YAXIS)
{
Point2I oldpt((S32)cursorPt.x, (S32)cursorPt.y);
Point2F pt(cursorPt.x, cursorPt.y);
if (event->objType == SI_XAXIS)
{
pt.x += (event->fValue * mPixelsPerMickey);
cursorPt.x = getMax(0, getMin((S32)pt.x, mBounds.extent.x - 1));
}
else
{
pt.y += (event->fValue * mPixelsPerMickey);
cursorPt.y = getMax(0, getMin((S32)pt.y, mBounds.extent.y - 1));
}
if (!suppressMouse && (cursorPt.y != lastCursorPt.y || cursorPt.x != lastCursorPt.x))
{
mLastEvent.mousePoint.x = S32(cursorPt.x);
mLastEvent.mousePoint.y = S32(cursorPt.y);
if (mMouseButtonDown)
rootMouseDragged(mLastEvent);
else if (mMouseRightButtonDown)
rootRightMouseDragged(mLastEvent);
else if(mMouseMiddleButtonDown)
rootMiddleMouseDragged(mLastEvent);
else
rootMouseMove(mLastEvent);
}
return true;
}This basically removes the 'moved' boolean and directly checks either X or Y for changes between the current location and the last. It seems to work perfectly, but I didn't delve too far into how cursorPt and lastCursorPt are used/abused elsewhere, so there may be a catch I didn't spot. Since suppressMove is checked first, we still end up doing the comparisons only once each per processMouseMoveEvent call, when 'Y' is being processed.
Torque Owner Charlie Malbaurn
And are you doing this by creating a grid of small pixel sized tiles? That seems like the smartest bet to me
Oh and I thought of one more thing. Since T2d Doesn't measure in pixels, rather then in world coords, wouldn't it do that by nature?