import * as d3 from 'd3'; import { Renderer } from './Renderer'; class AxesRenderer extends Renderer { _axesVisible: boolean; _gridVisible: boolean; _axisColor: string; _gridColor: string; _ticks: number; _ticksize: number; _labelpadding: number; _gridPattern: number[]; constructor () { super(); this._axesVisible = true; this._gridVisible = true; this._axisColor = '#bbbbbb'; this._gridColor = '#dddddd'; this._ticks = 10; this._ticksize = 8; this._labelpadding = 4; this._gridPattern = [5, 5]; } axesVisible (visible: boolean): this { this._axesVisible = visible; return this; } gridVisible (visible: boolean): this { this._gridVisible = visible; return this; } render(context: CanvasRenderingContext2D, x: d3.ScaleLinear, y: d3.ScaleLinear): void { // constants const x0 = align(x(0)); const y0 = align(y(0)); const xrange = x.range().map(align); const yrange = y.range().map(align); const ratio = Math.abs(xrange[1] / yrange[1]); const tickoffset = this._ticksize / 2; const xticks = x.ticks(this._ticks * ratio); const yticks = y.ticks(this._ticks); context.save(); context.beginPath(); if (this._gridVisible) { // x-grid yticks.forEach(tick => { if (tick !== 0 || !this._axesVisible) { const ty = align(y(tick)); context.moveTo(xrange[0], ty); context.lineTo(xrange[1], ty); } }); context.setLineDash(this._gridPattern); context.lineDashOffset = -x0; context.strokeStyle = this._gridColor; context.stroke(); // y-grid context.beginPath(); xticks.forEach(tick => { if (tick !== 0 || !this._axesVisible) { const tx = align(x(tick)); context.moveTo(tx, yrange[0]); context.lineTo(tx, yrange[1]); } }); context.lineDashOffset = -y0; context.stroke(); } if (this._axesVisible) { // x-axis context.beginPath(); context.moveTo(xrange[0], y0); context.lineTo(xrange[1], y0); // x-ticks xticks.forEach(tick => { if (tick !== 0) { const tx = align(x(tick)); context.moveTo(tx, y0 - tickoffset); context.lineTo(tx, y0 + tickoffset); } }); // y-axis context.moveTo(x0, yrange[0]); context.lineTo(x0, yrange[1]); // y-ticks yticks.forEach(tick => { if (tick !== 0) { const ty = align(y(tick)); context.moveTo(x0 - tickoffset, ty); context.lineTo(x0 + tickoffset, ty); } }); // draw lines context.setLineDash([]); context.strokeStyle = this._axisColor; context.stroke(); // labels context.font = '10px sans-serif'; context.textAlign = 'center'; context.textBaseline = 'top'; context.fillStyle = this._axisColor; xticks.forEach(tick => { if (tick !== 0) { const tx = align(x(tick)); context.fillText(tick.toString(), tx, y0 + tickoffset + this._labelpadding); } }); context.textAlign = 'left'; context.textBaseline = 'middle'; yticks.forEach(tick => { if (tick !== 0) { const ty = align(y(tick)); context.fillText(tick.toString(), x0 + tickoffset + this._labelpadding, ty); } }); } context.restore(); } } function align (value: number): number { return Math.round(value) + 0.5; } export { AxesRenderer }