Home Manual Reference Source Repository

src/shapes/trace-dots.js

import BaseShape from './base-shape';


/**
 * A shape to display dots in a trace visualization (mean / range).
 *
 * [example usage](./examples/layer-trace.html)
 */
export default class TraceDots extends BaseShape {
  getClassName() { return 'trace-dots'; }

  _getAccessorList() {
    return { x: 0, mean: 0, range: 0 };
  }

  _getDefaults() {
    return {
      meanRadius: 3,
      rangeRadius: 3,
      meanColor: '#232323',
      rangeColor: 'steelblue'
    };
  }

  render(renderingContext) {
    if (this.$el) { return this.$el; }
    // container
    this.$el = document.createElementNS(this.ns, 'g');
    // draw mean dot
    this.$mean = document.createElementNS(this.ns, 'circle');
    this.$mean.setAttributeNS(null, 'r', this.params.meanRadius);
    this.$mean.setAttributeNS(null, 'stroke', this.params.meanColor);
    this.$mean.setAttributeNS(null, 'fill', 'transparent');
    this.$mean.classList.add('mean');
    // range dots (0 => top, 1 => bottom)
    this.$max = document.createElementNS(this.ns, 'circle');
    this.$max.setAttributeNS(null, 'r', this.params.meanRadius);
    this.$max.setAttributeNS(null, 'stroke', this.params.rangeColor);
    this.$max.setAttributeNS(null, 'fill', 'transparent');
    this.$max.classList.add('max');

    this.$min = document.createElementNS(this.ns, 'circle');
    this.$min.setAttributeNS(null, 'r', this.params.meanRadius);
    this.$min.setAttributeNS(null, 'stroke', this.params.rangeColor);
    this.$min.setAttributeNS(null, 'fill', 'transparent');
    this.$min.classList.add('min');

    this.$el.appendChild(this.$mean);
    this.$el.appendChild(this.$max);
    this.$el.appendChild(this.$min);

    return this.$el;
  }

  // @TODO use accessors
  update(renderingContext, datum) {
    const mean = this.mean(datum);
    const range = this.range(datum);
    const x = this.x(datum);
    // y positions
    const meanPos = `${renderingContext.valueToPixel(mean)}`;
    this.$mean.setAttributeNS(null, 'transform', `translate(0, ${meanPos})`);

    const halfRange = range / 2;
    const max = renderingContext.valueToPixel(mean + halfRange);
    const min = renderingContext.valueToPixel(mean - halfRange);
    const xPos = renderingContext.timeToPixel(x);

    this.$max.setAttributeNS(null, 'transform', `translate(0, ${max})`);
    this.$min.setAttributeNS(null, 'transform', `translate(0, ${min})`);
    this.$el.setAttributeNS(null, 'transform', `translate(${xPos}, 0)`);
  }

  inArea(renderingContext, datum, x1, y1, x2, y2) {
    const x = renderingContext.timeToPixel(this.x(datum));
    const mean = renderingContext.valueToPixel(this.mean(datum));
    const range = renderingContext.valueToPixel(this.range(datum));
    const min = mean - (range / 2);
    const max = mean + (range / 2);

    if (x > x1 && x < x2 && (min > y1 || max < y2)) {
      return true;
    }

    return false;
  }
}