Skip to content

Commit

Permalink
For-each loop optimization and memoize normalizeKey (#3469)
Browse files Browse the repository at this point in the history
* For-each loop optimization and memoize normalizeKey

* Fix yarn.lock

* Return -> continue, use full lodash

* Correct typings for memoized function

* Fix line plot indices
  • Loading branch information
philcchen authored and themadcreator committed May 31, 2018
1 parent aad550a commit 3c51210
Show file tree
Hide file tree
Showing 14 changed files with 112 additions and 47 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,12 @@
"dependencies": {
"@types/d3": "^4.13.0",
"@types/is-plain-object": "^0.0.2",
"@types/lodash": "^4.14.109",
"d3": "^4.13.0",
"d3-ease": "^1.0.0",
"d3-shape": "^1.0.0",
"is-plain-object": "^2.0.4",
"lodash": "^4.17.10",
"tslib": "~1.8.0",
"typesettable": "4.1.0"
}
Expand Down
8 changes: 5 additions & 3 deletions src/drawers/rectangleDrawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,17 @@ export const RectangleCanvasDrawStep: CanvasDrawStep = (
data: any[],
projector: AttributeToAppliedProjector) => {
context.save();
data.forEach((datum, index) => {
const dataLen = data.length;
for (let index = 0; index < dataLen; index++ ) {
const datum = data[index];
if (datum == null) {
return;
continue;
}
const attrs = resolveAttributesSubsetWithStyles(projector, RECT_ATTRS, datum, index);
context.beginPath();
context.rect(attrs["x"], attrs["y"], attrs["width"], attrs["height"]);
renderPathWithStyle(context, attrs);
});
}
context.restore();
};

Expand Down
6 changes: 4 additions & 2 deletions src/drawers/svgDrawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,12 @@ export class SVGDrawer implements IDrawer {
this._createAndDestroyDOMElements(data);

let delay = 0;
appliedDrawSteps.forEach((drawStep, i) => {
const drawLength = appliedDrawSteps.length;
for (let i = 0; i < drawLength; i++) {
const drawStep = appliedDrawSteps[i];
Utils.Window.setTimeout(() => this._drawStep(drawStep), delay);
delay += drawStep.animator.totalTime(data.length);
});
}
}

public getVisualPrimitives() {
Expand Down
7 changes: 5 additions & 2 deletions src/plots/barPlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,11 +447,14 @@ export class Bar<X, Y> extends XYPlot<X, Y> {

private _entitiesIntersecting(xValOrRange: number | Range, yValOrRange: number | Range): IPlotEntity[] {
const intersected: IPlotEntity[] = [];
this._getEntityStore().entities().forEach((entity) => {
const entities = this._getEntityStore().entities();
const entitiesLen = entities.length;
for (let i = 0; i < entitiesLen; i++) {
const entity = entities[i];
if (Utils.DOM.intersectsBBox(xValOrRange, yValOrRange, this._entityBounds(entity))) {
intersected.push(this._lightweightPlotEntityToPlotEntity(entity));
}
});
}
return intersected;
}

Expand Down
29 changes: 23 additions & 6 deletions src/plots/linePlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,9 +444,12 @@ export class Line<X> extends XYPlot<X, number> {
let closest: IPlotEntity;

const chartBounds = this.bounds();
this.entities().forEach((entity) => {
const entities = this.entities();
const entityLen = entities.length;
for (let i = 0; i < entityLen; i++) {
const entity = entities[i];
if (!Utils.Math.within(entity.position, chartBounds)) {
return;
continue;
}
const xDist = Math.abs(queryPoint.x - entity.position.x);
const yDist = Math.abs(queryPoint.y - entity.position.y);
Expand All @@ -456,7 +459,7 @@ export class Line<X> extends XYPlot<X, number> {
minXDist = xDist;
minYDist = yDist;
}
});
}

return closest;
}
Expand Down Expand Up @@ -530,7 +533,12 @@ export class Line<X> extends XYPlot<X, number> {
return;
}

let filteredDataIndices = data.map((d, i) => i);
let filteredDataIndices = [];
const dataLen = data.length;
for (let i = 0; i < dataLen; i++) {
filteredDataIndices[i] = i;
}

if (this._croppedRenderingEnabled) {
filteredDataIndices = this._filterCroppedRendering(dataset, filteredDataIndices);
}
Expand All @@ -540,7 +548,15 @@ export class Line<X> extends XYPlot<X, number> {
if (this._collapseDenseVerticalLinesEnabled) {
filteredDataIndices = this._filterDenseLines(dataset, filteredDataIndices);
}
dataToDraw.set(dataset, [filteredDataIndices.map((d, i) => data[d])]);

const filteredData = [];
const filteredIndicesLen = filteredDataIndices.length;
for (let i = 0; i < filteredIndicesLen; i++) {
const index = filteredDataIndices[i];
filteredData[i] = data[index];
}

dataToDraw.set(dataset, [filteredData]);
});

return dataToDraw;
Expand Down Expand Up @@ -690,7 +706,8 @@ export class Line<X> extends XYPlot<X, number> {
const data = dataset.data();
let bucket: Utils.Bucket = null;

for (let ii = 0; ii <= indices.length; ++ii) {
const indicesLength = indices.length;
for (let ii = 0; ii <= indicesLength; ++ii) {
const i = indices[ii];
if (data[i] == null) {
continue;
Expand Down
9 changes: 6 additions & 3 deletions src/plots/piePlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -598,10 +598,13 @@ export class Pie extends Plot {
const writer = new Typesettable.Writer(measurer, context);
const dataset = this.datasets()[0];
const data = this._getDataToDraw().get(dataset);
data.forEach((datum, datumIndex) => {
const dataLen = data.length;
for (let datumIndex = 0; datumIndex < dataLen; datumIndex++) {
const datum = data[datumIndex];

let value = this.sectorValue().accessor(datum, datumIndex, dataset);
if (!Utils.Math.isValidNumber(value)) {
return;
continue;
}
value = this._labelFormatter(value, datum, datumIndex, dataset);
const measurement = measurer.measure(value);
Expand Down Expand Up @@ -647,6 +650,6 @@ export class Pie extends Plot {
xAlign: "center",
yAlign: "center",
}, g.node());
});
}
}
}
9 changes: 6 additions & 3 deletions src/plots/plot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -703,10 +703,13 @@ export class Plot extends Component {
const drawer = this._datasetToDrawer.get(dataset);
let validDatumIndex = 0;

dataset.data().forEach((datum: any, datumIndex: number) => {
const data = dataset.data();
const dataLen = data.length;
for (let datumIndex = 0; datumIndex < dataLen; datumIndex++) {
const datum = data[datumIndex];
const position = this._pixelPoint(datum, datumIndex, dataset);
if (Utils.Math.isNaN(position.x) || Utils.Math.isNaN(position.y)) {
return;
continue;
}

const plot = this;
Expand All @@ -724,7 +727,7 @@ export class Plot extends Component {
validDatumIndex,
});
validDatumIndex++;
});
}
});

return lightweightPlotEntities;
Expand Down
16 changes: 10 additions & 6 deletions src/plots/rectanglePlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,9 +408,12 @@ export class Rectangle<X, Y> extends XYPlot<X, Y> {
const yMin = Math.min.apply(null, yRange);
const yMax = Math.max.apply(null, yRange);
const data = dataToDraw.get(dataset);
data.forEach((datum, datumIndex) => {
const dataLength = data.length;
for (let datumIndex = 0; datumIndex < dataLength; datumIndex++) {
const datum = data[datumIndex];

if (datum == null) {
return;
continue;
}

const label = "" + this.label()(datum, datumIndex, dataset);
Expand All @@ -430,10 +433,10 @@ export class Rectangle<X, Y> extends XYPlot<X, Y> {
const xLabelRange = { min: x, max: x + measurement.width };
const yLabelRange = { min: y, max: y + measurement.height };
if (xLabelRange.min < xMin || xLabelRange.max > xMax || yLabelRange.min < yMin || yLabelRange.max > yMax) {
return;
continue;
}
if (this._overlayLabel(xLabelRange, yLabelRange, datumIndex, datasetIndex, dataToDraw)) {
return;
continue;
}

const color = attrToProjector["fill"](datum, datumIndex, dataset);
Expand All @@ -447,7 +450,7 @@ export class Rectangle<X, Y> extends XYPlot<X, Y> {
yAlign: "center",
}, g.node());
}
});
}
}

private _overlayLabel(labelXRange: Range, labelYRange: Range, datumIndex: number, datasetIndex: number,
Expand All @@ -457,7 +460,8 @@ export class Rectangle<X, Y> extends XYPlot<X, Y> {
for (let i = datasetIndex; i < datasets.length; i++) {
const dataset = datasets[i];
const data = dataToDraw.get(dataset);
for (let j = (i === datasetIndex ? datumIndex + 1 : 0); j < data.length; j++) {
const dataLen = data.length;
for (let j = (i === datasetIndex ? datumIndex + 1 : 0); j < dataLen; j++) {
if (Utils.DOM.intersectsBBox(labelXRange, labelYRange, this._entityBBox(data[j], j, dataset, attrToProjector))) {
return true;
}
Expand Down
9 changes: 6 additions & 3 deletions src/plots/scatterPlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,12 +277,15 @@ export class Scatter<X, Y> extends XYPlot<X, Y> {
const dataToDraw = this._getDataToDraw();
const attrToProjector = this._getAttrToProjector();
this.datasets().forEach((dataset) => {
dataToDraw.get(dataset).forEach((datum, index) => {
const data = dataToDraw.get(dataset);
const dataLen = data.length;
for (let index = 0; index < dataLen; index++) {
const datum = data[index];
if (datum == null) {
return;
continue;
}
this._drawLabel(datum, index, dataset, attrToProjector);
});
}
});
}

Expand Down
7 changes: 5 additions & 2 deletions src/plots/segmentPlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,14 @@ export class Segment<X, Y> extends XYPlot<X, Y> {
private _entitiesIntersecting(xRange: Range, yRange: Range): IPlotEntity[] {
const intersected: IPlotEntity[] = [];
const attrToProjector = this._getAttrToProjector();
this.entities().forEach((entity) => {
const entities = this.entities();
const entitiesLen = entities.length;
for (let i = 0; i < entitiesLen; i++) {
const entity = entities[i];
if (this._lineIntersectsBox(entity, xRange, yRange, attrToProjector)) {
intersected.push(entity);
}
});
}
return intersected;
}

Expand Down
7 changes: 5 additions & 2 deletions src/plots/stackedAreaPlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,12 @@ export class StackedArea<X> extends Area<X> {
private static _domainKeys(datasets: Dataset[], keyAccessor: IAccessor<any>) {
const domainKeys = d3.set();
datasets.forEach((dataset) => {
dataset.data().forEach((datum, index) => {
const data = dataset.data();
const dataLen = data.length;
for (let index = 0; index < dataLen; index++) {
const datum = data[index];
domainKeys.add(keyAccessor(datum, index, dataset));
});
}
});

return domainKeys.values();
Expand Down
7 changes: 5 additions & 2 deletions src/plots/waterfallPlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,10 @@ export class Waterfall<X, Y> extends Bar<X, number> {
let max = Number.MIN_VALUE;
let total = 0;
let hasStarted = false;
dataset.data().forEach((datum, index) => {
const data = dataset.data();
const dataLen = data.length;
for (let index = 0; index < dataLen; index ++) {
const datum = data[index];
const currentValue = this.y().accessor(datum, index, dataset);
const isTotal = this.total().accessor(datum, index, dataset);
if (!isTotal || index === 0) {
Expand Down Expand Up @@ -201,7 +204,7 @@ export class Waterfall<X, Y> extends Bar<X, number> {
min += startTotal;
max += startTotal;
}
});
}
this._extent = [min, max];
}

Expand Down
35 changes: 22 additions & 13 deletions src/utils/stackingUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as d3 from "d3";
import { Dataset } from "../core/dataset";
import { IAccessor } from "../core/interfaces";

import { memoize, MemoizedFunction } from "lodash";
import * as Utils from "./";
import { makeEnum } from "./makeEnum";

Expand Down Expand Up @@ -75,10 +76,14 @@ export function stack(
datasets.reverse();
}

datasets.forEach((dataset) => {
for (const dataset of datasets) {
const keyToStackedDatum = new Utils.Map<string, StackedDatum>();
dataset.data().forEach((datum, index) => {
const key = normalizeKey(keyAccessor(datum, index, dataset));
const data = dataset.data();
const dataLen = data.length;
for (let index = 0; index < dataLen; index++) {
const datum = data[index];
const accessedKey = keyAccessor(datum, index, dataset);
const key = normalizeKey(accessedKey);
const value = +valueAccessor(datum, index, dataset);
let offset: number;
const offsetMap = (value >= 0) ? positiveOffsets : negativeOffsets;
Expand All @@ -92,14 +97,14 @@ export function stack(
keyToStackedDatum.set(key, {
offset: offset,
value: value,
axisValue: keyAccessor(datum, index, dataset),
axisValue: accessedKey,
originalDatum: datum,
originalDataset: dataset,
originalIndex: index,
});
});
}
datasetToKeyToStackedDatum.set(dataset, keyToStackedDatum);
});
}
return datasetToKeyToStackedDatum;
}

Expand All @@ -120,8 +125,9 @@ export function stackedExtents<D>(stackingResult: GenericStackingResult<D>): {
stackingResult.forEach((stack) => {
stack.forEach((datum: GenericStackedDatum<D>, key) => {
// correctly handle negative bar stacks
const maximalValue = Utils.Math.max([datum.offset + datum.value, datum.offset], datum.offset);
const minimalValue = Utils.Math.min([datum.offset + datum.value, datum.offset], datum.offset);
const offsetValue = datum.offset + datum.value;
const maximalValue = Utils.Math.max([offsetValue, datum.offset], datum.offset);
const minimalValue = Utils.Math.min([offsetValue, datum.offset], datum.offset);

const { axisValue } = datum;

Expand Down Expand Up @@ -153,13 +159,16 @@ export function stackedExtents<D>(stackingResult: GenericStackingResult<D>): {
export function stackedExtent(stackingResult: StackingResult, keyAccessor: IAccessor<any>, filter: IAccessor<boolean>) {
const extents: number[] = [];
stackingResult.forEach((stackedDatumMap: Utils.Map<string, StackedDatum>, dataset: Dataset) => {
dataset.data().forEach((datum, index) => {
const data = dataset.data();
const dataLen = data.length;
for (let index = 0; index < dataLen; index++) {
const datum = data[index];
if (filter != null && !filter(datum, index, dataset)) {
return;
continue;
}
const stackedDatum = stackedDatumMap.get(normalizeKey(keyAccessor(datum, index, dataset)));
extents.push(stackedDatum.value + stackedDatum.offset);
});
}
});
const maxStackExtent = Utils.Math.max(extents, 0);
const minStackExtent = Utils.Math.min(extents, 0);
Expand All @@ -173,6 +182,6 @@ export function stackedExtent(stackingResult: StackingResult, keyAccessor: IAcce
* @param {any} key The key to be normalized
* @return {string} The stringified key
*/
export function normalizeKey(key: any) {
export const normalizeKey = memoize((key: any) => {
return String(key);
}
}) as ((key: any) => string) & MemoizedFunction;
Loading

1 comment on commit 3c51210

@blueprint-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For-each loop optimization and memoize normalizeKey (#3469)

Demo: quicktests | fiddle

Please sign in to comment.