Category talk:Wren-plot
<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>