Articles Archive
Articles Search
Director Wiki
 

Drawing Dynamic Charts & Graphs, Part 1

May 8, 2002
by Will Turnage

Dear Will,

Is there a way to create dynamic graphs for Shockwave movies. The data that is imported is based on XML data. Could you please help?

Thank You,
angsana

 

Angsana,

Yes, you can use imaging Lingo to create many different types of dynamic charts and graphs. Over the next few weeks, you'll learn how to make several different types of charts and graphs, but this week, we'll start with a basic line graph. In the Shockwave movie below, click on the 'draw lines' button.

To use this behavior in your movie, download the sample movie and copy the behavior called draw line graph behavior. When you drag this behavior onto a bitmap in your movie, this dialog box will appear.

The dialog box contains every detail about how your line graph is displayed. First, you should decide what the overall background color of your graph will be. Next, you define the total height and width of your graph. The next few properties deal with the grid lines that also appear in the background. You can pick the color and thickness of the lines as well as the horizontal and vertical spacing for each of the grid lines. Next, you specify details about the line that will represent your data such as its thickness, its color, and the number of increments drawn for each segment. This last property determines the smoothness of the animation of the line. A lower number will draw faster, but the animation won't be as smooth. A higher number will draw more smoothly, but will also take longer to draw. Finally, you need to specify some information about each data point. If you want, the behavior will draw a circle at each one of your data points. If you choose to have this circle drawn, then you can specify the size and color of the circle.

Once you have set up how your graph will look, then you're ready to draw. To draw the graph, all you need to do is to send the behavior a list of points. This list of points can be hard coded into your movie, they can be downloaded off the Internet, they can be imported from an XML document, etc. Depending on your data source, you will probably have to do some quick calculations in order to convert your raw data into a list of points. Because of the many varieties of data sources, that step isn't going to be covered in this column, but it's pretty straightforward. Once you have your list of points, then you're ready to roll.

In the sample movie shown above, the list of points are calculated randomly, like this:

  pointList = []
  repeat with i = 1 to 10
    pointList.append (point (40 * (i - 1), random (200)))
  end repeat
  sendAllSprites (#initPoints, pointList)

This code starts by creating an empty list. Next, it repeats a loop 10 times, and for each iteration, it creates a new point and adds it to your list of points. In this case, the locH of this point is equal to an exact multiple of 40, and the locV is completely random. When you're done creating the list of points, then you send it out to all the sprites on the Stage.

When the draw line graph behavior receives this list, it executes this code:

on initPoints me, pointList
  pPointList = pointList
  me.beginDrawingPoints ()
end

on beginDrawingPoints me
  pCurrentPoint = 1
  pCurrentIncrement = 1
  timeout ("drawLineGraph").new (-1, #drawLineGraph, me)
end

The code starts by storing the list of points into a local property and then calling the beginDrawingPoints handler. In that handler, you initialize two variables used to draw the line. The first variable, pCurrentPoint, is used to determine which point you are currently drawing from. The second variable, pCurrentIncrement, is used to determine how much of the partial line to draw from point to point. Finally, you create a timeout object that will draw the line for you. Notice that the length for this timeout object is set to -1. When you set the length of a timeout object to -1, then Director will execute this handler over and over as fast as it can.

So now, your behavior is going to execute the drawLineGraph handler as quickly as possible.

on drawLineGraph me

  tempImage = pBaseImage.duplicate ()
  if pCurrentPoint > 1 then
    repeat with i = 1 to (pCurrentPoint - 1)
      tempImage.draw (pPointList[i], pPointList[i+1], [#shapeType: #line, #lineSize: pLineThickness, #color: pLineColor])
    end repeat
  end if
  

You start the drawLineGraph handler by creating a local variable called tempImage which will contain the image of your graph. In this case, tempImage is a duplicate of pBaseImage, which is an image object containing the background and the grid (this image object is built in the beginSprite handler). Next, you check to see if you're past the first point in the graph. If you are, then you go ahead and draw all the line segments which you have already drawn. Note that each line is drawn using the thickness and color that you specified in your original getPropertyDescriptionList dialog box.

Next, you are going to draw the small line fragment connecting the current point with the next point in your data list.

  destinationPoint = pPointList[pCurrentPoint] + (pCurrentIncrement * ((pPointList[pCurrentPoint + 1] - pPointList[pCurrentPoint]) / pLineIncrements))
  tempImage.draw (pPointList[pCurrentPoint], destinationPoint, [#shapeType: #line, #lineSize: pLineThickness, #color: pLineColor])

You start by calculating the point for the end of this line fragment. This is done by taking the difference between the current point in the graph and the point that comes directly after it. You then divide this difference by the total number of line segments. Then you take this number and multiply it by the current segment you are on, and then add this number to the first point. This will give you a point that is a fraction of the distance along the line between the two points in your graph. Once you have this point, then you draw the line fragment between these two points. The next step is to draw the circles on your graph.

  if pCirclesFlag then
    repeat with i = 1 to pCurrentPoint
      tempImage.fill ( (pPointList[i] - point (pCircleRadius,pCircleRadius)), (pPointList[i] + point (pCircleRadius,pCircleRadius)), [#shapeType: #oval, #lineSize: 0, #color: pCircleColor])
    end repeat
  end if

This code first checks to see if you have specified that you want the circles to be drawn. If you do, then it loops through all of the points that you have passed, and it draws a circle at each one of those points using the radius and color that you specified originally. Once that is done, then you need to increment all of your counting variables for the next time drawLineGraph is called.

  pCurrentIncrement = (pCurrentIncrement mod pLineIncrements) + 1
  if pCurrentIncrement = 1 then
    pCurrentPoint = pCurrentPoint + 1
    if pCurrentPoint >= pPointList.count then
      if pCirclesFlag then
        tempImage.fill ((pPointList[pCurrentPoint] - point (pCircleRadius, pCircleRadius)), (pPointList[pCurrentPoint] + point (pCircleRadius, pCircleRadius)), [#shapeType: #oval, #lineSize: 0, #color: pCircleColor])
      end if
      timeout ("drawLineGraph").forget ()
    end if
  end if

  sprite (me.spriteNum).member.image = tempImage

end

You start by incrementing the current increment by 1, but if it goes over the total number of line increments, then it is reset to 1. If the currentIncrement is now 1 because it has cycled through all of its increments, then the current point also needs to be incremented by 1. If that happens, then you need to check if you're at the end of your graph. If you are at the end of your graph, then you check to see if you should draw a final circle. Once the circle is drawn, then, since you are at the end of the graph, you forget the timeout object. And in the final step of this behavior, you set the image of the current sprite equal to this new image which you have just created.

And that's all there is to it. Next time, you'll learn how to dynamically create a pie chart, and after that you'll learn how to create animated bar charts. If you have any other types of charts you'd like to learn how to make, just send a note along to the Handyman and we'll see what we can come up with.

A sample Director 8 movie is available for download in ZIP or SIT format.

All colorized Lingo code samples have been processed by Dave Mennenoh's brilliant HTMLingo Xtra, available from his site at http://www.crackconspiracy.com/~davem/

Will Turnage is a multimedia programmer based in New York City. In addition to his weekly role as Director Online's Multimedia Handyman, he is also the Technology Director for Sony Music's Client Side Technologies Group. You can read more about his work at http://will.turnage.com/.

Copyright 1997-2017, Director Online. Article content copyright by respective authors.