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.


         ; 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,, 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


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:


      ; 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

Everything else is, as they say, details.

Web Coyote's Guide to IDL Programming