src/d3/Plot.ts

   1/**

   2 * # Plot class for rendering data series

   3 * 

   4 */

   5

   6/** */

   7

   8import { Data, NumDomain }  from 'hsdatab';

   9import { log as gLog }      from 'hsutil';   const log = gLog('d3.Plot');

  10import { GraphCfg, PlotFn } from './ConfigTypes';

  11import { PlotFnDef }        from './ConfigTypes';

  12import { PlotCfg }          from './ConfigTypes';

  13import { UnitVp }           from './ConfigTypes';

  14import { GraphComponent }   from './GraphComponent'

  15

  16/** import and register 'bubble' plot */

  17import { bubble }           from './bubble'

  18

  19const DEF_RADIUS:number = 5;

  20

  21export class Plot extends GraphComponent {

  22    /*------------ Static implementation----- */

  23    /** a map of plot types to corresponding plot functions. New plot types are added via a call to `register`. */

  24    protected static plotFnMap: any = {};

  25

  26    /** registers a new plot type with corresponding function. */

  27    public static register(key:string, fn:PlotFnDef) {

  28        this.plotFnMap[key] = fn;

  29        log.info(`registered plot type '${key}'`);

  30    }

  31

  32    /*------------ Instance implementation----- */

  33    private desc: PlotCfg;

  34    private series:PlotFn[] = [];

  35

  36    constructor(cfg:GraphCfg) {

  37        super(cfg);

  38        this.desc = {

  39            cfg: cfg,

  40            margin: { left:0, top:0, right:0, bottom:0},

  41            plotBase: cfg.baseSVG.append('svg').classed('plotSVG', true)

  42        };

  43        const margin = this.desc.margin;

  44        this.desc.plotBase.append('rect').classed('plotRect', true);

  45        Plot.register('bubble', bubble);

  46    }

  47

  48    setBorders(left:UnitVp, top:UnitVp, right:UnitVp, bottom:UnitVp) {

  49        const margin = this.desc.margin;

  50        margin.left     = left;

  51        margin.right    = right;

  52        margin.top      = top;

  53        margin.bottom   = bottom;

  54    }

  55

  56    /**

  57     * adds a series to the plot.

  58     * @param type type of plot to use, e.g. 'bubble' or 'scatter'

  59     * @param params the column name of the parameters used to plot the series

  60     */

  61    addSeries(type:string, ...params:string[]) {

  62        const fn = Plot.plotFnMap[type];

  63        const seriesKey = `${type} ${params.join(' ')}`;

  64        this.series[seriesKey] = [type].concat(params);

  65        if (fn) {

  66            this.series.push((data:Data) => fn(data, this.desc, ...params));

  67            log.info(`added series ${this.series.length} as '${seriesKey}'`);

  68        } else {

  69            log.error(`unknown plot type ${type}; available types are:\n   '${Object.keys(Plot.plotFnMap).join("'\n   '")}'`);

  70        }

  71    }

  72

  73    /**

  74     * renders the plot area and all added series using `data`

  75     * @param data 

  76     */

  77    render(data:Data) {

  78        this.renderPlotArea();

  79        this.series.forEach((s:PlotFn) => s(data));

  80    }

  81

  82    private renderPlotArea() {

  83        const margin = this.desc.margin;

  84        const plotArea = this.desc.cfg.defaults.Plot.area;

  85        this.desc.plotBase.select('.plotRect')

  86            .attr('x', margin.left)

  87            .attr('y', margin.top)

  88            .attr('width', this.desc.cfg.viewPort.width - margin.left - margin.right)

  89            .attr('height',this.desc.cfg.viewPort.height - margin.top - margin.bottom)

  90            .attr('rx', plotArea.rx)

  91            .attr('ry', plotArea.ry)

  92            .attr('stroke', plotArea.stroke.color)

  93            .attr('stroke-width', plotArea.stroke.width)

  94            .attr('stroke-opacity', plotArea.stroke.opacity)

  95            .attr('fill', plotArea.fill.color)

  96            .attr('fill-opacity', plotArea.fill.opacity);

  97    }

  98}