diff --git a/.gitignore b/.gitignore index 8e0ef9e5fd..508f24a457 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ build/ test/tests.js test/tests.js.map plottable.js +plottable.js.map plottable.min.js # Typescript diff --git a/src/plots/barPlot.ts b/src/plots/barPlot.ts index f5fd4b8e17..ad69d8432d 100644 --- a/src/plots/barPlot.ts +++ b/src/plots/barPlot.ts @@ -398,7 +398,7 @@ export class Bar extends XYPlot { /** * Returns the PlotEntity nearest to the query point according to the following algorithm: * - If the query point is inside a bar, returns the PlotEntity for that bar. - * - Otherwise, gets the nearest PlotEntity by the primary direction (X for vertical, Y for horizontal), + * - Otherwise, gets the nearest PlotEntity by the positional direction (X for vertical, Y for horizontal), * breaking ties with the secondary direction. * Returns undefined if no PlotEntity can be found. * @@ -406,72 +406,73 @@ export class Bar extends XYPlot { * @returns {PlotEntity} The nearest PlotEntity, or undefined if no PlotEntity can be found. */ public entityNearest(queryPoint: Point): IPlotEntity { + return this._computeBarPixelThickness.doLocked(() => { + const queryPtPrimary = this._isVertical ? queryPoint.x : queryPoint.y; + const queryPtSecondary = this._isVertical ? queryPoint.y : queryPoint.x; + + // SVGRects are positioned with sub-pixel accuracy (the default unit + // for the x, y, height & width attributes), but user selections (e.g. via + // mouse events) usually have pixel accuracy. We add a tolerance of 0.5 pixels. + const tolerance = 0.5; + + // PERF: precompute all these values to prevent recomputing for each entity + const chartBounds = this.bounds(); + const chartWidth = chartBounds.bottomRight.x - chartBounds.topLeft.x; + const chartHeight = chartBounds.bottomRight.y - chartBounds.topLeft.y; + const xRange = { min: 0, max: chartWidth }; + const yRange = { min: 0, max: chartHeight }; + const originalPositionProjector = Plot._scaledAccessor(this.length()); + const scaledBaseline = this.length().scale.scale(this.baselineValue()); + const plotPointFactory = (datum: any, index: number, dataset: Dataset, rect: IEntityBounds) => { + return this._pixelPointBar(originalPositionProjector(datum, index, dataset), scaledBaseline, rect); + }; + + let minPrimaryDist = Infinity; + let minSecondaryDist = Infinity; + let closest: ILightweightPlotEntity; + this._getEntityStore().entities().forEach((entity: ILightweightPlotEntity) => { + const barBBox = this._entityBounds(entity); + if (!Utils.DOM.intersectsBBox(xRange, yRange, barBBox)) { + return; + } - const queryPtPrimary = this._isVertical ? queryPoint.x : queryPoint.y; - const queryPtSecondary = this._isVertical ? queryPoint.y : queryPoint.x; - - // SVGRects are positioned with sub-pixel accuracy (the default unit - // for the x, y, height & width attributes), but user selections (e.g. via - // mouse events) usually have pixel accuracy. We add a tolerance of 0.5 pixels. - const tolerance = 0.5; - - // PERF: precompute all these values to prevent recomputing for each entity - const chartBounds = this.bounds(); - const chartWidth = chartBounds.bottomRight.x - chartBounds.topLeft.x; - const chartHeight = chartBounds.bottomRight.y - chartBounds.topLeft.y; - const xRange = { min: 0, max: chartWidth }; - const yRange = { min: 0, max: chartHeight }; - const originalPositionProjector = (this._isVertical ? Plot._scaledAccessor(this.y()) : Plot._scaledAccessor(this.x())); - const scaledBaseline = (> (this._isVertical ? this.y().scale : this.x().scale)).scale(this.baselineValue()); - const plotPointFactory = (datum: any, index: number, dataset: Dataset, rect: IEntityBounds) => { - return this._pixelPointBar(originalPositionProjector(datum, index, dataset), scaledBaseline, rect); - }; - - let minPrimaryDist = Infinity; - let minSecondaryDist = Infinity; - let closest: ILightweightPlotEntity; - this._getEntityStore().entities().forEach((entity: ILightweightPlotEntity) => { - const barBBox = this._entityBounds(entity); - if (!Utils.DOM.intersectsBBox(xRange, yRange, barBBox)) { - return; - } + let primaryDist = 0; + let secondaryDist = 0; + + // if we're inside a bar, distance in both directions should stay 0 + if (!Utils.DOM.intersectsBBox(queryPoint.x, queryPoint.y, barBBox, tolerance)) { + const plotPt = plotPointFactory(entity.datum, entity.index, entity.dataset, barBBox); + const plotPtPrimary = this._isVertical ? plotPt.x : plotPt.y; + primaryDist = Math.abs(queryPtPrimary - plotPtPrimary); + + // compute this bar's min and max along the secondary axis + const barMinSecondary = this._isVertical ? barBBox.y : barBBox.x; + const barMaxSecondary = barMinSecondary + (this._isVertical ? barBBox.height : barBBox.width); + + if (queryPtSecondary >= barMinSecondary - tolerance && queryPtSecondary <= barMaxSecondary + tolerance) { + // if we're within a bar's secondary axis span, it is closest in that direction + secondaryDist = 0; + } else { + const plotPtSecondary = this._isVertical ? plotPt.y : plotPt.x; + secondaryDist = Math.abs(queryPtSecondary - plotPtSecondary); + } + } - let primaryDist = 0; - let secondaryDist = 0; - - // if we're inside a bar, distance in both directions should stay 0 - if (!Utils.DOM.intersectsBBox(queryPoint.x, queryPoint.y, barBBox, tolerance)) { - const plotPt = plotPointFactory(entity.datum, entity.index, entity.dataset, barBBox); - const plotPtPrimary = this._isVertical ? plotPt.x : plotPt.y; - primaryDist = Math.abs(queryPtPrimary - plotPtPrimary); - - // compute this bar's min and max along the secondary axis - const barMinSecondary = this._isVertical ? barBBox.y : barBBox.x; - const barMaxSecondary = barMinSecondary + (this._isVertical ? barBBox.height : barBBox.width); - - if (queryPtSecondary >= barMinSecondary - tolerance && queryPtSecondary <= barMaxSecondary + tolerance) { - // if we're within a bar's secondary axis span, it is closest in that direction - secondaryDist = 0; - } else { - const plotPtSecondary = this._isVertical ? plotPt.y : plotPt.x; - secondaryDist = Math.abs(queryPtSecondary - plotPtSecondary); + // if we find a closer bar, record its distance and start new closest lists + if (primaryDist < minPrimaryDist + || primaryDist === minPrimaryDist && secondaryDist < minSecondaryDist) { + closest = entity; + minPrimaryDist = primaryDist; + minSecondaryDist = secondaryDist; } - } + }); - // if we find a closer bar, record its distance and start new closest lists - if (primaryDist < minPrimaryDist - || primaryDist === minPrimaryDist && secondaryDist < minSecondaryDist) { - closest = entity; - minPrimaryDist = primaryDist; - minSecondaryDist = secondaryDist; + if (closest !== undefined) { + return this._lightweightPlotEntityToPlotEntity(closest); + } else { + return undefined; } }); - - if (closest !== undefined) { - return this._lightweightPlotEntityToPlotEntity(closest); - } else { - return undefined; - } } /** diff --git a/webpackConfig/build.js b/webpackConfig/build.js index 590f9b8d54..83b7a48b38 100644 --- a/webpackConfig/build.js +++ b/webpackConfig/build.js @@ -46,6 +46,7 @@ Licensed under MIT (https://github.com/palantir/plottable/blob/master/LICENSE)`; * a dependency on a module named "plottable" (both in AMD and CommonJS). */ module.exports = { + devtool: "source-map", entry: "./build/src/index.js", output: { filename: "plottable.js",