Category talk:Wren-plot

Revision as of 14:07, 18 July 2022 by PureFox (talk | contribs) (Added source code for new 'Wren-plot' module.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

<lang ecmascript>/* Module "plot.wren" */

import "graphics" for Canvas, Color, Point

/* Axes represents a two dimensional Cartesian coordinate system

  for plotting points or drawing simple charts. The use of
  an 8 x 8 font (the default) is assumed throughout. */

class Axes {

   // Converts a list of Points to a list of coordinate pairs.
   static PointsToPairs(points) { points.map { |p| [p.x, p.y] }.toList }
   // Constructs a new Axes object with:
   // 1. the origin at absolute coordinates (originX, originY);
   // 2. lengths of the x and y axes: lengthX and lengthY in pixels; and
   // 3. ranges of x and y values: rangeX and rangeY.
   // If x-values are non-numeric or discrete, null should be passed for rangeX.
   construct new(originX, originY, lengthX, lengthY, rangeX, rangeY) {
       _ox = originX
       _oy = originY
       _lx = lengthX
       _ly = lengthY
       _rx = rangeX
       _ry = rangeY
   }
   // Self explanatory read-only properties.
   originX { _ox }
   originY { _oy }
   lengthX { _lx }
   lengthY { _ly }
   rangeX  { _rx }
   rangeY  { _ry }
   // Properties for scaling x and y coordinates to the lengths of their axes.
   scaleX  { _lx / (_rx.to - _rx.from) }
   scaleY  { _ly / (_ry.to - _ry.from) }
   // Draws the axes in a given color and line thickness.
   draw(color,thickness) {
       Canvas.line(_ox, _oy, _ox + _lx, _oy, color, thickness)
       Canvas.line(_ox, _oy, _ox, _oy - _ly, color, thickness)
   }
   // Prints 'text' centered at unscaled coordinates (x, y) 
   // relative to the origin in a given color.
   print(x, y, text, color) {
       Canvas.print(text, _ox + x - 4, _oy - y - 4, color)
   }
   // Adds marks to x and y axes at the appropriate scaled coordinates.
   // The marks themselves are drawn in a given color and line thickness.
   // The label is printed in 'lblColor'. 
   label(xMarks, yMarks, color, thickness, lblColor) {
       for (xm in xMarks) {
           var x = (xm - _rx.from) * scaleX
           Canvas.line(_ox + x, _oy, _ox + x, _oy + 10, color, thickness)
           var label = xm.toString
           var v = Canvas.getPrintArea(label)
           Canvas.print(label, _ox + x - v.x/2, _oy + 10 + v.y, lblColor) 
       }
       for (ym in yMarks) {
           var y = (ym  - _ry.from) * scaleY
           Canvas.line(_ox, _oy - y, _ox - 10, _oy - y, color, thickness)
           var label = ym.toString
           var v = Canvas.getPrintArea(label)
           Canvas.print(label, _ox - 14 - v.x, _oy - y - v.y/2, lblColor)           
       }
   }
   // Adds marks to x and y axes at the appropriate scaled coordinates
   // drawn in a given color and line thickness. No labels are drawn.
   mark(xMarks, yMarks, color, thickness) {
       for (xm in xMarks) {
           var x = (xm - _rx.from) * scaleX
           Canvas.line(_ox + x, _oy, _ox + x, _oy + 10, color, thickness)
       }
       for (ym in yMarks) {
           var y = (ym  - _ry.from) * scaleY
           Canvas.line(_ox, _oy - y, _ox - 10, _oy - y, color, thickness)
       }
   }
   // Draws a line between scaled points (x1, y1) and (x2, y2)
   // relative to the origin in a given color and line thickness.
   line(x1, y1, x2, y2, color, thickness) {
       x1 = (x1 - _rx.from) * scaleX
       x2 = (x2 - _rx.from) * scaleX
       y1 = (y1 - _ry.from) * scaleY
       y2 = (y2 - _ry.from) * scaleY
       Canvas.line(_ox + x1, _oy - y1, _ox + x2, _oy - y2, color, thickness)
   }
   // Draws grid lines from scaled coordinates on the x and y axes
   // in a given color and line thickness.
   grid(xCoords, yCoords, color, thickness) {
       for (xc in xCoords) {
           var x = (xc - _rx.from) * scaleX
           Canvas.line(_ox + x, _oy, _ox + x, _oy - _ly, color, thickness)
       }
       for (yc in yCoords) {
           var y = (yc - _ry.from) * scaleY
           Canvas.line(_ox, _oy - y, _ox + _lx, _oy - y, color, thickness)
       }
   }
   // Plots a list of points using 'symbol' centred at scaled coordinates (x, y)
   // relative to the origin in a given color.
   plot(points, color, symbol) {
       if (points.count > 0 && (points[0] is Point)) {
           points = Axes.PointsToPairs(points)
       }
       for (p in points) {
           var px = (p[0] - _rx.from) * scaleX - 4
           var py = (p[1] - _ry.from) * scaleY + 4
           Canvas.print(symbol, _ox + px, _oy - py, color)
       }
   }
   // As the 'plot' method but prints a label 'dist' pixels after 'symbol'
   // in color 'lblColor'.
   plotWithLabels(points, color, symbol, labels, lblColor, dist) {
       if (points.count > 0 && (points[0] is Point)) {
           points = Axes.PointsToPairs(points)
       }
       for (i in 0...points.count) {
           var px = (points[i][0] - _rx.from) * scaleX - 4
           var py = (points[i][1] - _ry.from) * scaleY + 4
           Canvas.print(symbol, _ox + px, _oy - py, color)
           Canvas.print(labels[i], _ox + px + 8 + dist, _oy - py, lblColor)
       }
   }
   // As the 'plot' method but prints the unscaled coordinates of each point
   // 'dist' pixels after 'symbol' in color 'crdColor'.
   plotWithCoords(points, color, symbol, crdColor, dist) {
       if (points.count > 0 && (points[0] is Point)) {
           points = Axes.PointsToPairs(points)
       }
       for (i in 0...points.count) {
           var px = (points[i][0] - _rx.from) * scaleX - 4
           var py = (points[i][1] - _ry.from) * scaleY + 4
           var coords = "(%(points[i][0]),%(points[i][1]))"
           Canvas.print(symbol, _ox + px, _oy - py, color)
           Canvas.print(coords, _ox + px + 8 + dist, _oy - py, crdColor)
       }
   }
   // Draws a line graph between each scaled point in a list of points
   // relative to the origin in a given color and line thickness.
   lineGraph(points, color, thickness) {
       if (points.count > 0 && (points[0] is Point)) {
           points = Axes.PointsToPairs(points)
       }
       var px = (points[0][0] - _rx.from) * scaleX
       var py = (points[0][1] - _ry.from) * scaleY
       for (i in 1...points.count) {
           var qx = (points[i][0] - _rx.from) * scaleX
           var qy = (points[i][1] - _ry.from) * scaleY
           Canvas.line(_ox + px, _oy - py, _ox + qx, _oy - qy, color, thickness)
           px = qx
           py = qy
       }
   }
   // Draws a bar-chart representing 'data' which is a list of pairs, the first element of which
   // is the bar's label and the second is the bar's value.
   // The bars are drawn in order with a width of 'barWidth' using successive colors in the
   // 'barColors' list and with a border color of 'brdColor'.
   // The interval between bars is calculated by the method.
   // If _rx is null, the method prints the labels on the x-axis at the appropriate positions.
   // 'yMarks' are drawn on the y-axis at the appropriate positions in color 'mrkColor'
   // and thickness 'mrkThick'. Labels on both the x and y axes are printed in 'lblColor.'
   barChart(data, barWidth, barColors, brdColor, yMarks, mrkColor, mrkThick, lblColor) {
       var n = data.count
       var interval = (_lx - n * barWidth)/(n + 1)
       var x = _ox + interval
       var cix = 0
       for (d in data) {
           var v = (d[1] - _ry.from) * scaleY
           Canvas.rectfill(x, _oy - v, barWidth, v, barColors[cix])
           Canvas.rect(x, _oy - v, barWidth, v, brdColor)
           if (!_rx) {
               var label = d[0].toString
               var w = Canvas.getPrintArea(label).x
               var h = (barWidth - w)/2
               Canvas.print(label, x + h, _oy + 14, lblColor)
           }
           x = x + barWidth + interval
           cix = (cix + 1) % barColors.count
       }
       draw(mrkColor, mrkThick)
       label([], yMarks, mrkColor, mrkThick, lblColor)
   }
   // Draws a histogram representing 'data' which is a list of pairs, the first element of which
   // is the bar's label and the second is the bar's value.
   // A histogram is essentially a bar-chart which covers the entire x-axis with no intervals
   // between the bars and 'barWidth' is therefore calculated by the method.
   // Otherwise the parameters and remarks are the same as for a bar-chart.
   // If _rx isn't null, 'xmarks' are deduced and drawn on the x-axis at the appropriate intervals.
   histogram(data, barColors, brdColor, yMarks, mrkColor, mrkThick, lblColor) {
       var n = data.count
       var rectWidth = _lx/n
       barChart(data, rectWidth, rectColors, brdColor, yMarks, mrkColor, mrkThick, lblColor)
       if (_rx) { 
            var interval = (_rx.to - _rx.from) / n
            var xMarks = (0..n+1).map { |i| i * interval }.toList
            label(xMarks, [], mrkColor, mrkThick, lblColor)
       }
   }

}</lang>

Return to "Wren-plot" page.