Drag

Mouse Interaction 'The Hard Way'

toneburst's picture

Little demo of mouse-interaction without using the new QC4 Interaction patch. I need a few extra options the Interaction patch didn't allow, so ended-up attempting to do the whole thing in JS, with a simple Iterator to render the billboards.

Here's the JS code, if anyone's interested:

/*
   toneburst 2010
*/
 
///////////////
// Functions //
///////////////
 
// Init points array
// In: number of points, scale on x, scale on y, offset x, offset y
// Out: array of point objects
function initPoints(numpnts, scalex, scaley, offsetx, offsety) {
   // Init vars
   pnts = new Array(numpnts);
   var spacingx = scalex / (numpnts - 1);
   var spacingy = scaley / (numpnts - 1);
   // Loop through creating points objects and adding to array
   for(i = 0; i < numpnts; i++) {
      var p = new Object();
      p.x = spacingx * i + offsetx;
      p.y = spacingy * i + offsety;
      p.focus = false;
      pnts[i] = p;
   }
   return pnts;
}
 
// Distance between two 2D points
// In: points objects in form p.x, p.y
// Out: number
// For mouse-click hit-testing
function distance(p0, p1) {
   xd = p1.x - p0.x; // x-distance
   yd = p1.y - p0.y; // y-distance
   return Math.sqrt(xd*xd + yd*yd);
}
 
// Function to clamp input value to cmin > cmax range
function clamp(val, cmin, cmax) {
   return Math.max(cmin, Math.min(cmax, val));
}
 
// Function to handle dragging of points, with constraints
// In: points array (point x y z focus), mouse-position, point index, clamp-limit
// Out: point object
function dragPoint(pnts, mspos, pindex, clmplimit, dminx, dmaxx, dminy, dmaxy) {
   // Get working point
   p = pnts[pindex];
   // Get previous point x-position
   if(pindex == 0) {
   // If first point
      prevx = dminx - clmplimit;
   } else {
      prevx = pnts[pindex - 1].x;
   }
   // Get next point x-value
   if (pindex == pnts.length - 1) {
   // If last point
      nxtx = dmaxx + clmplimit;
   } else {
      nxtx = pnts[pindex + 1].x
   }
   // Clamp x with minimum distance
   p.x = clamp(mspos.x, prevx + clmplimit, nxtx - clmplimit);
   // Clamp y
   p.y = clamp(mspos.y, dminy, dmaxy);
   // Return point object
   return p;
}
 
///////////////
// Main Loop //
///////////////
 
// Vars for main program loop
var clickCoord = new Array(0, 0);
var oldMouseButton = false;
var points = new Array();
var dragIndex = -1;
 
// The Loop
function (__structure PointsOut)
         main
      (__number MouseX, __number MouseY, __boolean MouseButton,
       __index NumPoints, __boolean InitPoints, __number InitScaleX, __number InitOffsetX, __number InitScaleY, __number InitOffsetY,
       __number DragMinX, __number DragMaxX, __number DragMinY, __number DragMaxY, __number DragMinDistanceX,
       __boolean PinStart, __boolean PinEnd,
       __structure StartColor, __structure EndColor,
       __boolean LoadPoints, __structure PointsIn)
{
   if(!_testMode) {
 
      mousePos = new Object();
      mousePos.x = MouseX; mousePos.y = MouseY;
 
      // Set initial X and Y positions
      if(!points[0] || InitPoints) {
         points = initPoints(NumPoints, InitScaleX, InitScaleY, InitOffsetX, InitOffsetY);
      }
 
      // Load points
      if(LoadPoints) {
         if(PointsIn[0]) {
            for(h = 0; h < PointsIn.length; h++) {
               p = new Object();
               p.x = PointsIn[h].x; p.y = PointsIn[h].y; p.focus = false;
               points[h] = p;
            }
         }
      }
 
      // Start drag on mousedown
      if(MouseButton > oldMouseButton) {
         clickCoord = Array(MouseX, MouseY);
         for(i = 0; i < points.length; i++) {
            if(distance(mousePos, points[i]) < 0.01) {
               dragIndex = i;
               if(dragIndex == 0 && PinStart) { dragIndex = -1; break; }
               else if(dragIndex == points.length - 1 && PinEnd) { dragIndex = -1; break; }
               else { points[dragIndex].focus = true; break; }
            }
         }
 
      // End drag on mouseup
      } else if(MouseButton < oldMouseButton && dragIndex >= 0) {
         points[dragIndex].focus = false;
         dragIndex = -1;
      }
 
      // Do drag
      if(dragIndex >= 0) {
         // Call function to set point position with constraints
         points[dragIndex] = dragPoint(points, mousePos, dragIndex, DragMinDistanceX, DragMinX, DragMaxX, DragMinY, DragMaxY);
      }
 
      // Update mousebutton state for next frame
      oldMouseButton = MouseButton;
 
      // Init output object
      var result = new Object();
 
      // Set output values
      result.PointsOut = points;
 
      // Return result object
      return result;
   }
}

Dunno if this is going to be of any direct use to anyone.

a|x