[comp.sys.mac.programmer] Easy way to draw the rubber band??

gt4586c@prism.gatech.EDU (WILLETT,THOMAS CARTER) (05/30/91)

i'm trying to draw the nifty rubber band we all know and love, the one that
is a rectangle defined by the mouse location at the time of button down as
the anchor point and the current mouse location (still with button down) as
the other corner.  i've got the mouse location and drawing the rectangle
alright, but it blanks out my drawing as it goes because i'm calling 
EraseRect do blank out the rectangles outline as the mouse moves.  what's
the right way to do it?  thanks for any help


-- 
thomas willett 
Georgia Institute of Technology, Atlanta  
gt4586c@prism.gatech.edu
"Violence is the last refuge of the incompetent." - Salvor Hardin (Foundation)

stevec@Apple.COM (Steve Christensen) (05/31/91)

gt4586c@prism.gatech.EDU (WILLETT,THOMAS CARTER) writes:
>i'm trying to draw the nifty rubber band we all know and love, the one that
>is a rectangle defined by the mouse location at the time of button down as
>the anchor point and the current mouse location (still with button down) as
>the other corner.  i've got the mouse location and drawing the rectangle
>alright, but it blanks out my drawing as it goes because i'm calling 
>EraseRect do blank out the rectangles outline as the mouse moves.  what's
>the right way to do it?  thanks for any help

If you just want to track an empty rectangle around, how about doing something
like this:

void RubberBand(Point startPt, Rect *theRect) {
  Point lastPt,newPt;

  lastPt.v = startPt.v;			// initialize the last mouse location
  lastPt.h = startPt.h;			//  to be the starting mouse location
  SetRect(theRect,0,0,0,0);		// empty rectangle to start

  PenMode(patXor);			// use XOR transfer mode

  while (StillDown()) {
    GetMouse(&newPt);			// get the mouse's current location
    if ((newPt.v != lastPt.v) && (newPt.h != lastPt.h)) {
      lastPt.v = newPt.v;		// it's moved since last time, so
      lastPt.h = newPt.h;		//  update the last location

      FrameRect(&theRect);		// erase the previous rectangle

      if (lastPt.v > startPt.v) {	// make sure the bottom of the
        theRect->top = startPt.v;	//  rectangle is below the top
        theRect->bottom = lastPt.v;
      } else {
        theRect->top = lastPt.v;
        theRect->bottom = startPt.v;
      }
      if (lastPt.h > startPt.h ) {	// ditto with the left and right
        theRect->left = startPt.h;
        theRect->right = lastPt.h;
      } else {
        theRect->left = lastPt.h;
        theRect->right = startPt.h;
      }

      FrameRect(theRect);		// draw the new rectangle
    }
  }

  PenNormal();				// restore the pen mode
}


Note that startPt should be in local coordinates.  This means you've done a
SetPort() to the appropriate port followed by GlobalToLocal(&startPt).
If you'd like a gray rubber band instead of a black one, you could also do
a PenPat(&qd.gray) (MPW) or PenPat(gray) (Think) right after the PenMode call.

steve

-- 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Steve Christensen			Never hit a man with glasses.
  stevec@apple.com			Hit him with a baseball bat.

jcav@quads.uchicago.edu (john cavallino) (05/31/91)

In article <53513@apple.Apple.COM> stevec@Apple.COM (Steve Christensen) writes:
>If you just want to track an empty rectangle around, how about doing something
>like this:

All of the futzing around to create the rectangle from the points can be
eliminated if you use the Quickdraw procedure _Pt2Rect (IM I-175).
See below:

>void RubberBand(Point startPt, Rect *theRect) {
>  Point lastPt,newPt;
>
>  lastPt.v = startPt.v;		// initialize the last mouse location
>  lastPt.h = startPt.h;		//  to be the starting mouse location
>  SetRect(theRect,0,0,0,0);		// empty rectangle to start
>
>  PenMode(patXor);			// use XOR transfer mode
>
>  while (StillDown()) {
>    GetMouse(&newPt);			// get the mouse's current location
>    if ((newPt.v != lastPt.v) && (newPt.h != lastPt.h)) {
>      lastPt.v = newPt.v;		// it's moved since last time, so
>      lastPt.h = newPt.h;		//  update the last location
>
>      FrameRect(&theRect);		// erase the previous rectangle

       Pt2Rect(&startPt,&newPt,&theRect);  // MUCH MUCH simpler

>      FrameRect(theRect);		// draw the new rectangle
>    }
>  }
>
>  PenNormal();				// restore the pen mode
>}

-- 
John Cavallino                      |     EMail: jcav@midway.uchicago.edu
University of Chicago Hospitals     |    USMail: 5841 S. Maryland Ave, Box 145
Office of Facilities Management     |            Chicago, IL  60637
B0 f++ w c+ g+ k s(+) e+ h- pv (qv) | Telephone: 312-702-6900

dickie@schaefer.math.wisc.edu (Garth Dickie) (06/01/91)

In article <53513@apple.Apple.COM> stevec@Apple.COM (Steve Christensen) writes:
>gt4586c@prism.gatech.EDU (WILLETT,THOMAS CARTER) writes:
>>i'm trying to draw the nifty rubber band we all know and love, the one that
>>is a rectangle defined by the mouse location at the time of button down as
>>the anchor point and the current mouse location (still with button down) as
>>the other corner.  i've got the mouse location and drawing the rectangle
>>alright, but it blanks out my drawing as it goes because i'm calling 
>>EraseRect do blank out the rectangles outline as the mouse moves.  what's
>>the right way to do it?  thanks for any help
>
>If you just want to track an empty rectangle around, how about doing something
>like this:
>
> [ code for tracking a rectangle ]

It also pays to play with regions, so that the old rectangle is erased at the
same time the new one is drawn.  It takes a little more time per step, but it
*looks* faster, since there is no flicker.  The Finder now does this, when you
drag an icon, although DragGrayRgn still does it the old way.  So,

    PenPat( gray );
	PenMode( notPatXor );           // so we look black, not white, on desktop

	oldframe = NewRgn();
	newframe = NewRgn();
	insidebox = NewRgn();

	[ whenever the cursor changes ] {

		Pt2Rect( oldwhere, newwhere, &bounds );
		oldwhere = newhere;

        RectRgn( newframe, &bounds );            // compute a region for a one
        InsetRect( &bounds, 1, 1 );              // pixel wide frame inside the
        RectRgn( insidebox, &newrect );          // rectangle.
        XorRgn( insidebox, newframe, newframe );
        XorRgn( newframe, oldframe, oldframe );  // compute the difference
        PaintRgn( oldframe );                    // xor the difference
		
        temp = oldframe;                         // save the current rectangle
        oldframe = newframe;                     // as oldframe.
        newframe = temp;
    }

    PaintRgn( oldframe );                        // erase the rectangle

Somebody was asking about doing color hilighting under the inverted rectangle,
like the Finder appears to do.  The trick is that the Finder does the hiliting
just before it draws over the hilited icon.  So, (as far as I can tell), at
each step, you:

	figure out the new rectangle.
	hilite anything which is now included, but wasn't before.
	draw the difference between the frames.
	dehilite anything now not included, which was before.

-- 

Garth Dickie -- Math Graduate Student? -- dickie@math.wisc.edu

stevec@Apple.COM (Steve Christensen) (06/06/91)

jcav@quads.uchicago.edu (john  cavallino) writes:
>In article <53513@apple.Apple.COM> stevec@Apple.COM (Steve Christensen) writes:
>>[...about doing rubber-banding, to which John writes...]
>
>All of the futzing around to create the rectangle from the points can be
>eliminated if you use the Quickdraw procedure _Pt2Rect (IM I-175).

I knew that it existed, but I must've been suffering a temporary case of
blindness when perusing those pages in Inside Mac, so I did it the hard way.

Also, in a separate message, it was pointed out the check to see if the mouse
moved should be "if ((newPt.v != lastPt.v) || (newPt.h != lastPt.h))" instead
of "&&".  Brain-deadness on my part, I guess...

steve

-- 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Steve Christensen			Never hit a man with glasses.
  stevec@apple.com			Hit him with a baseball bat.