Fanning Software Consulting

Drawing a Rubberband Box

QUESTION: I'm trying to draw a rubberband selection box in object graphics. I'm using a polyline object for the box. I've created an instance of everything in the scene except for the polyline box, because I thought instancing would be fast. But this thing is just dog slow. Do you know any way to speed it up?

ANSWER: You are probably doing everything right, but you have chosen to render the scene in hardware. That is a mistake. I don't know why this is, exactly, but instance rendering is done much, much faster in software. I think because IDL has been optimized for this operation.

I've written an example program, named ZoomBox, that demonstrates one way to draw a rubberband box in object graphics. You can drag a box in the full image window, and that portion of the image will be zoomed into a second window.

By default, the program runs with software rendering turned on. You can run the program like this:

   IDL> Zoombox

To see the difference in rendering speed, try running a second instance of the program with the hardware renderer turned on:

   IDL> Zoombox, /Hardware_Render

I think you will notice a profound difference.

The type of renderer is selected with the Renderer keyword to the Widget_Draw creation routine for the main graphics window.

drawID = Widget_Draw(drawbase, XSize=xsize, YSize=ysize, Retain=0, $
   Button_Events=1, Event_Pro='ZOOMBOX_DRAW_EVENTS', Graphics_Level=2, $
   Expose_Events=1, Renderer=1-Keyword_Set(hardware))

In this example, the box is drawn in normalized coordiates as a polyline object. Here is the code for a Down event in the draw widget window. The info structure is a structure that is used to hold program information needed in the event handlers. The essential steps here are to: (1) set the static or unmoving corner of the rubberband box, (2) turn motion events on for the draw widget, (3) create the initial instance of the box (as a polyline object), (4) create an instance of everything that will not be moving in the scene, and (5) add the box model to the view so it can be rendered.

   'DOWN': BEGIN

         ; Set the static corners of the box to current
         ; cursor location.

      info.xs = event.x
      info.ys = event.y

         ; Change the event handler for the draw widget and turn MOTION
         ; events ON.

      Widget_Control, event.id, Draw_Motion_Events=1

         ; Initialize the polygon object.

      info.theBox = Obj_New('IDLgrPolyline', Replicate(info.xs, 5) / info.xsize, $
         Replicate(info.ys, 5) / info.ysize, Color=info.boxColor)
      info.boxModel = Obj_New('IDLgrModel')
      info.boxModel->Add, info.theBox

         ; Create an instance of the current view.

      info.theWindow->Draw, info.theView, /Create_Instance
      info.theModel->SetProperty, Hide=0
      info.theView->SetProperty, Transparent=0

         ; Add the boxModel to the view so it can be displayed.

      info.theView->Add, info.boxModel

      ENDCASE

The essential actions of the motion event are to (1) erase the last box drawn, (2) find the new dynamic corner of the box, (3) re-configure the coordinates of the box, and (4) re-draw the box in its new position. Here is the motion part of the event handler code:

   'MOTION': BEGIN

      ; Erase the old zoom box.

    info.theWindow->Draw, info.theView, /Draw_Instance

      ; Get the dynamic corner of the box.

    info.xd = 0 > event.x < (info.xsize-1)
    info.yd = 0 > event.y < (info.ysize-1)

      ; Re-configure the box coordinates.

   theBoxData = FltArr(2,5)
   theBoxData[0,*] = [info.xs, info.xd, info.xd, info.xs, info.xs] / Float(info.xsize)
   theBoxData[1,*] = [info.ys, info.ys, info.yd, info.yd, info.ys] / Float(info.ysize)

      ; Draw the new zoom box.

   info.theBox->SetProperty, Data=theBoxData
   info.theWindow->Draw, info.theView, /Draw_Instance
   ENDCASE

Everything else is, as they say, details.

Google
 
Web Coyote's Guide to IDL Programming