diff --git a/example/app.js b/example/app.js index aee2fc6..84a51a2 100644 --- a/example/app.js +++ b/example/app.js @@ -10,19 +10,6 @@ class App extends Component { glPlot2d.defineGlPlot2d('gl-plot-2d'); } - componentWillMount() { - // TODO: Unique event names! - document.addEventListener('gl-plot-2d-init-plot-done', () => { - if (this.glPlot2dComponent1.plot) { - this.glPlot2dComponent1.drawPlot(); - } - - if (this.glPlot2dComponent2.plot) { - this.glPlot2dComponent2.drawPlot(); - } - }); - } - makePlot1() { const p1 = glPlot2d.getRandomPositions(1000); const p2 = glPlot2d.getRandomPositions(100); @@ -53,17 +40,18 @@ class App extends Component { } }; + this.name1 = 'plot1'; this.traces1 = [trace1, trace2]; this.debug1 = true; - this.height1 = '300px'; + this.height1 = '200px'; this.width1 = '100%'; - const tickList = glPlot2d.getTicks(this.traces1, 'linear', 1, true); + const tickList = glPlot2d.getTicks(this.traces1, 'linear', 1, true, null); this.plotOptions1 = { pixelRatio: 1, screenBox: null, - dataBox: [tickList.t1[0].x - 0.25, tickList.t2[0].x - 0.25, tickList.t1[tickList.t1.length - 1].x + 0.25, tickList.t2[tickList.t2.length - 1].x + 0.25], + dataBox: [tickList.t1[0].x - 0.25, tickList.t2[0].x - 0.5, tickList.t1[tickList.t1.length - 1].x + 0.25, tickList.t2[tickList.t2.length - 1].x + 0.5], viewBox: null, titleEnabe: false, title: '', @@ -101,6 +89,7 @@ class App extends Component { } props(this.glPlot2dComponent1, { + name: this.name1, traces: this.traces1, debug: this.debug1, height: this.height1, @@ -110,8 +99,8 @@ class App extends Component { } makePlot2() { - const p3 = glPlot2d.getRandomPositions(1000); - const p4 = glPlot2d.getRandomPositions(100); + const p3 = glPlot2d.getRandomPositions(100); + const p4 = glPlot2d.getRandomPositions(1000); const trace3 = { mode: 'line', @@ -139,17 +128,18 @@ class App extends Component { } }; + this.name2 = 'plot2'; this.traces2 = [trace3, trace4]; this.debug2 = true; - this.height2 = '300px'; + this.height2 = '200px'; this.width2 = '100%'; - const tickList = glPlot2d.getTicks(this.traces1, 'log', 1, false); + const tickList = glPlot2d.getTicks(this.traces1, 'log', 1, true, null); this.plotOptions2 = { pixelRatio: 1, screenBox: null, - dataBox: [tickList.t1[0].x - 0.25, tickList.t2[0].x - 0.25, tickList.t1[tickList.t1.length - 1].x + 0.25, tickList.t2[tickList.t2.length - 1].x + 0.25], + dataBox: [tickList.t1[0].x - 0.25, tickList.t2[0].x - 0.5, tickList.t1[tickList.t1.length - 1].x + 0.25, tickList.t2[tickList.t2.length - 1].x + 0.5], viewBox: null, titleEnabe: false, title: '', @@ -187,6 +177,7 @@ class App extends Component { } props(this.glPlot2dComponent2, { + name: this.name2, traces: this.traces2, debug: this.debug2, height: this.height2, @@ -198,36 +189,25 @@ class App extends Component { componentDidMount() { this.makePlot1(); this.makePlot2(); + window.dispatchEvent(new Event('resize')); } render() { - let outerContainer = { + const outerContainer = { width: '100%' }; - let innerContainer = { + const innerContainer = { paddingBottom: '0.5px' }; return (
- { this.glPlot2dComponent1 = glPlot2dComponent1 }} - traces={this.traces1} - debug={this.debug1} - height={this.height1} - width={this.width1} - plotOptions={this.plotOptions1} /> + { this.glPlot2dComponent1 = glPlot2dComponent1 }}/>
- { this.glPlot2dComponent2 = glPlot2dComponent2 }} - traces={this.traces2} - debug={this.debug2} - height={this.height2} - width={this.width2} - plotOptions={this.plotOptions2} /> + { this.glPlot2dComponent2 = glPlot2dComponent2 }} />
); diff --git a/src/GlPlot2dComponent.tsx b/src/GlPlot2dComponent.tsx index 81994ae..7f30b91 100644 --- a/src/GlPlot2dComponent.tsx +++ b/src/GlPlot2dComponent.tsx @@ -35,6 +35,7 @@ export class GlPlot2dComponent extends skate.Component { static get props(): skate.ComponentProps { return { // Custom. + name: skate.prop.string({ attribute: true }), traces: skate.prop.array({ attribute: true, coerce(traces) { @@ -91,10 +92,12 @@ export class GlPlot2dComponent extends skate.Component { * @memberOf GlPlot2dComponent */ public renderedCallback(): void { - if (this.shadowRoot && !this.canvas) { + if (this.shadowRoot && !this.canvas && !this.plot) { this.canvas = this.shadowRoot.querySelector('canvas'); - this.initResize(); + this.initResizeEvents(); this.initPlot(); + this.fitCanvas(); + this.drawPlot(); } } @@ -115,14 +118,14 @@ export class GlPlot2dComponent extends skate.Component { case 'plot-options': if (newValue) { this['plotOptions'] = JSON.parse(newValue); - this['plotOptions'].gl = this.gl; } break; default: break; } - this.plot.update(this['plotOptions']); + this['plotOptions'].gl = this.gl; + this.fitCanvas(); this.drawPlot(); } } @@ -148,13 +151,10 @@ export class GlPlot2dComponent extends skate.Component { * @memberOf GlPlot2dComponent */ public getStyles(): string { - const width = this['width']; - const height = this['height']; - const styles = ` div { - width: ${width}; - height: ${height}; + width: ${this['width']}; + height: ${this['height']}; } canvas { @@ -166,49 +166,46 @@ export class GlPlot2dComponent extends skate.Component { } /** - * Resize function that uses canvas-fit. - * Sets the viewBox to contain the entire containing div. + * Resize fit canvas function that uses canvas-fit. + * Sets the viewBox to contain the entire surrounding div. * * @memberOf GlPlot2dComponent */ - public resize(): void { + public fitCanvas(): void { // Setup fit(). const resize = fit(this.canvas, null, +window.devicePixelRatio); if (this.shadowRoot) { // Get the div around the canvas. - let div = this.shadowRoot.querySelector('div'); + const div = this.shadowRoot.querySelector('div'); if (div) { - let boundingClientRect = div.getBoundingClientRect(); + const boundingClientRect = div.getBoundingClientRect(); + + // Set the viewBox to contain the entire surrounding div. + // TODO: Parameterize the viewBox. this['plotOptions'].viewBox = [50, 1, boundingClientRect.width - 1, boundingClientRect.height - 1]; } } - // Resize after setting up fit(). + // Trigger resize. resize(); } /** - * Helper function that initializes resize canvas logic. + * Helper function that initializes resize events. * Should be only called once on component initialization. * * @memberOf GlPlot2dComponent */ - public initResize(): void { - this.resize(); - + public initResizeEvents(): void { // Debounce the resize call. const debounceResize = debounce(() => { - this.resize(); - - if (this.plot) { - this.plot.update(this['plotOptions']); - this.drawPlot(); - } + this.fitCanvas(); + this.drawPlot(); }, 200); - // Setup resize event listener. + // Setup debounced window resize event listener. window.addEventListener('resize', debounceResize, false); } @@ -238,7 +235,6 @@ export class GlPlot2dComponent extends skate.Component { } this['plotOptions'].gl = this.gl; - this.plot = createPlot(this['plotOptions']); this['traces'].forEach((trace: Trace) => { @@ -250,7 +246,7 @@ export class GlPlot2dComponent extends skate.Component { } }); - skate.emit(this, 'gl-plot-2d-init-plot-done'); + skate.emit(this, `gl-plot-2d-init-plot-done-${this['name']}`); } /** @@ -259,6 +255,9 @@ export class GlPlot2dComponent extends skate.Component { * @memberOf GlPlot2dComponent */ public drawPlot(): void { + // Make sure plot is updated with current plotOptions before drawing. + this.plot.update(this['plotOptions']); + if (this['debug']) { console.time('drawTime'); } @@ -269,7 +268,7 @@ export class GlPlot2dComponent extends skate.Component { console.timeEnd('drawTime'); } - skate.emit(this, 'gl-plot-2d-draw-plot-done'); + skate.emit(this, `gl-plot-2d-draw-plot-done-${this['name']}`); } /** diff --git a/src/GlPlot2dComponentProps.ts b/src/GlPlot2dComponentProps.ts index a046653..c080f1a 100644 --- a/src/GlPlot2dComponentProps.ts +++ b/src/GlPlot2dComponentProps.ts @@ -10,6 +10,7 @@ import { GlPlot2dOptions, Trace } from './'; */ export interface GlPlot2dComponentProps { // Custom. + name: string; traces: Trace[]; debug: boolean; height: string; diff --git a/src/GlPlot2dUtils.ts b/src/GlPlot2dUtils.ts index eb0bab4..2218964 100644 --- a/src/GlPlot2dUtils.ts +++ b/src/GlPlot2dUtils.ts @@ -52,7 +52,38 @@ export function getLogTicks(lo: number, precision: number, nice: boolean): Tick[] { let scale = d3Scale.scaleLog() - .domain([Math.max(1, Math.floor(lo)), Math.ceil(hi)]); + .domain([Math.max(1.0, Math.floor(lo)), Math.ceil(hi)]); + + if (nice) { + scale = scale.nice(); + } + + const ticks = scale.ticks(); + + return ticks.map((tick: number) => new Tick(round(tick, precision))); +} + +/** + * Helper function to make count pow tick marks on domain lo to hi. + * Uses d3-scale to do so. + * Coerces tick number[] to Tick[]. + * + * @export + * @param {number} lo + * @param {number} hi + * @param {number} precision + * @param {boolean} nice + * @param {number} exponent + * @returns {Tick[]} + */ +export function getPowTicks(lo: number, + hi: number, + precision: number, + nice: boolean, + exponent: number): Tick[] { + let scale = d3Scale.scalePow() + .exponent(exponent) + .domain([lo, hi]); if (nice) { scale = scale.nice(); @@ -129,19 +160,21 @@ export function getRandomPositions(count: number): any { /** * Gets ticks by type. - * Supported types are linear and log. + * Supported types are linear, log, and pow. * * @export * @param {Trace[]} traces * @param {string} type * @param {number} precision * @param {boolean} nice + * @param {number} exponent * @returns {TickListPair} */ export function getTicks(traces: Trace[], type: string, precision: number, - nice: boolean): TickListPair { + nice: boolean, + exponent: number): TickListPair { const minMax = getMinMax(traces); let xTicks: Tick[] = []; @@ -155,6 +188,10 @@ export function getTicks(traces: Trace[], xTicks = getLogTicks(minMax.p1.x, minMax.p2.x, precision, nice); yTicks = getLogTicks(minMax.p1.y, minMax.p2.y, precision, nice); } + else if (type === 'pow') { + xTicks = getPowTicks(minMax.p1.x, minMax.p2.x, precision, nice, exponent); + yTicks = getPowTicks(minMax.p1.y, minMax.p2.y, precision, nice, exponent); + } return new TickListPair(xTicks, yTicks); } diff --git a/tslint.json b/tslint.json index fe40571..c128dd5 100644 --- a/tslint.json +++ b/tslint.json @@ -4,6 +4,7 @@ "curly": true, "interface-name": [true, "never-prefix"], "no-console": [true, "log"], + "no-debugger": false, "no-string-literal": false, "object-literal-sort-keys": false, "one-line": [false, "check-catch", "check-finally", "check-else"],