Friday, December 2, 2016

graphs in OpenOffice Draw


Automating Graphs in Draw

In my past engineering R&D work, I created graphs to make it easier to pick a value or
see the results of a design based on certain parameters. Engineers and Researchers like to use linear or exponential graph displays. Below are examples of drawing and displaying graphs of data sets.

Building a Linear Graph page

The first thing I worked on was getting a graph page that would fit a regular 8 ½ x 11 inch page of paper and create it in the Openoffice draw program. So I used my DrwLine routine to create horizontal and vertical lines to great the equivalent of large square of graph paper (1cm by 1cm squares). Since the smallest values of x or y on the draw space represents 1/100 of a mm; I choose to draw lines that are 1000 units apart in both vertical and horizontal directions This generates a grid with one square cm units. I started the grid with x=1000 to leave some left margin and y=5190 to more or less center the graph sheet on the page. This leaves some room at the top for future titles

The code below creates the image you see above

Sub DrawCartGraph
  'draw out a linear block 19 x 19 squares ' (1000 by 1000 draw units)
  Drw = ThisComponent
  Page = Drw.DrawPages(0) 'select draw page
  ' Draw top horizontal line
  x= DrwLine(1000,1000,19600,0000)
  ' top left is 1000, 5190
  ' bottom right is 19600,24190
  ' useable graph x space is 19000
  ' useable graph y space is 19000
  ' DRAW Horizontal lines for the cartesian grid, ' with a top margin of 5190
  for y= 5190 to 25000 step 1000
    DrwLine(1000, Y, 19000, 0)'was 17000
  next y
  ' DRAW Vertical lines for Cartesian grid, start x at left margin =1000
  for x=1000 to 20000 step 1000
    DrwLine(X,5190,0,19000) 'was 15000
  next x
end sub

Populating a cartesian graph solution in a Vector Drawing system

In a traditional graph plot in quadrant I of the Cartesian plane, the lower left-hand corner of the graph would be (0,0), but in our vector graphics design example,  the bottom left-hand corner of our graph paper (1000,24190).

The x-axis works the same (as the x value increases the points plot further to the right with larger positive values), but the y-axis is starting with a maximum value in the vector graphics system for a minimum value (zero). So we need to subtract values from the maximum vector drawing system as we get larger y values to grow in altitude. In this example, the zero y-axis is a value represented by a vector y value of 19000.

Let's work a practical example without the need of scaling, which means that each square on our vector graphics graph paper is 1000 by 1000 in vector display units. We'll let every graph square centimeter equal one unit.

Let's graph the equation y=1/3x2 and we will limit the range of x values from 0 to 7.5

The table on the right contains data values for x and y to be graphed. We'll limited x range from 0 to 7.5 in x, because values beyond 7.5 would be greater than 19 units (we'll reserve scaling for a more advanced future example).

For each solution of the graph, we multiply X by 1000 to get the X value and multiply the Y value by 1000 and subtract it from the max table Y display value (in this case 19000). Below is the code to draws the cartesian grid and then populates the graph with points.

Sub DrawCartGraph
  'draw out a linear block 19 x 19 one cm ' squares (1000 by     '1000, 1 cm by 1 cm)
  Drw = ThisComponent  
  Page = Drw.DrawPages(0)
  x= DrwLine(1000,1000,19600,0000) 'top ' horizontal line
  'top left is 1000,1000
  'bot right is 19600,26000
  '
  'DRAW the Horizontal lines for a Cartesian
  'grid starting at y=1000, to 25000
  for y= 5000 to 25000 step 1000
    DrwLine(1000, Y,19000,0
  next y
  'DRAW Vertical lines for a linear Cartesian
  'grid starting at x=1000 to 20000
  for x=1000 to 20000 step 1000
    DrwLine(X,5190,0,19000)
  next x
end sub

So the first routine 'DrawCartGraph' generated our graph paper as shown in the figure above

To populate the graph I created routine called PopulatePtGrph. In this routine I created an array to hold x values 0, .5, 1.0, …. 7.0 and an array that holds y values that satisfied the equation less our graph maximum Y value.

The x values of 0 through 7 are in an array called xPts and the y value solutions in yPts. Remember 500 represents a 0.5 in x distance and the Y points are subtracted from 19000. (So if x=3 then y=3, which xPts=3000 and yPts =16000). We step through those arrays in subroutine PopulatePtGraph and this routine calls the DrwPt subroutine to draw the points on the grid.

Below is the PopulatePtGrph code
sub PopulatePtGrph
' plot Y=1/3x2 for range of x from 1 ' to 9 NOTE: 1 unit of grid equals 1000 units on graph
' dim xPts(13) as integer
' dim yPts(13) as integer
xPts=array(0,500,1000,1500,2000,2500,3000,3500,4000,4500,5000,5500,6000,6500,7000)
yPts=array(19000,18917,18667,18250,17667,16917,16000,14917,13667,12250,10667, _ 8917,7000,4917,2667, 250)
for x=0 to 14
  z= DrwPt (xPts(x),y(Pts),24,24) 'DrwPt(xPts(x),yPts(x))
next x
end sub


Here is the resulting graph generated in Openoffice draw.

This graph has no scaling
Each Y horizontal line is 1.0 units
Each X vertical grid line is 1.0 units

Designing the graph point

If you've been looking at the code example for graphing
points on a function then you saw the function DrwPt that draws the plot points.
  z= DrwPt (xPts(x),y(Pts),24,24) 


The DrwPt function takes a location on the grid to plot and draws a circle that has a diameter of 205 draw units where one unit is 1/100 of a mm. So the diameter of the dot is roughly 0.2mm. Now in vector drawing, whatever shape you draw is based on an x,y location at the left hand top corner of the imaginary bounding box that surrounds the circle. This means that shifting the x,y point by half the radius in x and y will center the dot over the actual x,y location value. In this code the offset is 51. Here is the code that draws a circle and fills the center of the circle with green.

function DrwPt (Lft as integer,Top as integer)
  Dim DrwShape As Object     'Define DrwShape as an openoffice drawing object
  Dim Point As New com.sun.star.awt.Point
  Dim Size As New com.sun.star.awt.Size
  Dim rslt As integer
'note graph point is a circle with bounding box =24
'offset of x and y of point is -12
  rslt=1
  Point.x = Lft+1000-51    ' the x value must be offset by the left margin and half the radius
  Point.y = Top+5190-51  ' the y value must be offset the bottom less the half radius of point 
  Size.Width = 205   ' the height and width of the point (circle)
  Size.Height 205
  Circle =Drw.createInstance("com.sun.star.drawing.EllipseShape")
  Circle.Position = Point
  oPage.add(Circle)  ' Add the point to the drawing page
  Circle.FillColor = RGB(0,255,0) ' Green fill color to the point
  DrwPt=-1
end function


Building a Horizontal Logarithmic graph

While I've used log-log and Deci-log graph paper in my college and engineering years, I've never tried to automate logarithmic graphing with programs or macros. 

In the previous example, I used a simple equation y=1/3x2 , but I kept the x values small, because an exponential equation graph quickly runs out of space for y values in a regular cartesian graph.

A logarithmic graph scale can easily allow you to graph a much larger range of numbers. For a span of 1 to 10 or the first decade we can convert them to their base 10 logarithm value. Below are the values of one through ten and their values as base 10 logarithm the second row. as

So this ratio can be repeated and used for any decade

log(10)=1, log(20)=1.301, log(30)=1.477, log(40)=1.602, ….. log(100)=2.0
log(100)=2 log(200)=2.301, log(300)=2.477, log(400)=2.602 …..log(1000)=3.0
log(1000)=3 log(2000)=3.301, log(3000)=3.477, …. log(10000)=4.0
log(10000)=4 log(20000)=4.301, log(3000)=4.477, …. log(100000)=5.0
log(100000)=5 log(200000)=5.301
   o o o

To draw a horizontal logarithmic graph grid (vertical grid lines); I converted the logarithm fractional values to whole numbers by multiplying the fractional log values by 100 and rounded the values to the nearest whole number. 

The variable Itm is an array that holds this basic set of log values

Itm=array(0,30,48,60,70,78,84,90,95,100) and places grids based on the relative ratio of the logarithmic progression for any given decade.

The code to draw a single decade logarithmic grid follows below in the code snippet.  We iterate through the log10 graduations from 1 to 10

Itm=array(0,30,48,60,70,78,84,90,95,100)
for i=0 to 9 'print a decade of graduations
  L=Itm(i)
  L=(L*43)+1000
  x=DrwLine(L,5000,0,15000)
next I

The 43 value was my guess of scaling up to four decades to fit on one page and still have a readable graph. The added '1000 is the start of the left page margin of the log graph field.

To work with multiple decades, we added a variable to the calling routine and added an outer loop to draw each decade. Here is the code:

Sub GrphHLogScales (NoOfDecades as integer)
  for j=0 to NoOfDecades-1
    'Inner loop for each of the nine divisions within the log scale decade
    for i=0 to 9 'print a decade of graduations
      L=Itm(i)+j*100
      L=(L*43)+1000 '43 scales up to four decades to fit page
      x=DrwLine(L,5000,0,15000)
    next i
  next j
end sub