Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions draftlogs/8055_change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Cache parsed marker colors for the length of one point-style pass, so that marker-heavy SVG `scatter` traces draw about one third faster [[#8055](https://github.com/plotly/plotly.js/pull/8055)]
45 changes: 41 additions & 4 deletions src/components/color/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -271,24 +271,61 @@ const contrast = (cstr, lightAmount, darkAmount) => {
}
};

// `stroke` and `fill` run once per data point. Most points of a trace repeat
// one specifier, so a second parse of that specifier wastes the work. A caller
// that loops over points passes a `cache`. Every other caller passes nothing
// and parses each time. The cache belongs to the loop, so a cache that filled
// up never outlives the trace that filled it. Only a string serves as a key,
// because the other specifiers repeat by identity, not by value.
//
// The bound covers one loop. A trace with a distinct color per point writes an
// entry per point, and no later point reads that entry.
const MAX_MEMO_SIZE = 1000;

const computeStyle = (cstr) => {
// One `parse` yields the two values, so a miss costs one parse, not two.
// With a distinct color per point, every lookup is a miss.
const c = parse(cstr);
// Force alpha to 1 in the color, so that the string drops it.
return [formatRgb({ ...c, alpha: 1 }), c.alpha];
};

const styleOf = (cstr, cache) => {
if (cache === undefined || typeof cstr !== 'string') return computeStyle(cstr);

let value = cache.get(cstr);
if (value === undefined) {
value = computeStyle(cstr);
if (cache.size < MAX_MEMO_SIZE) cache.set(cstr, value);
}

return value;
};

/**
* Apply `stroke` and `stroke-opacity` styles to a D3 selection.
*
* @param {Selection} s - D3 selection
* @param {*} cstr - Color specifier
* @param {Map} [cache] - Cache of the styles of specifiers seen before. A caller
* that loops over points passes one cache for the whole loop.
*/
const stroke = (s, cstr) => {
s.style({ stroke: rgb(cstr), 'stroke-opacity': parse(cstr).alpha });
const stroke = (s, cstr, cache) => {
const style = styleOf(cstr, cache);
s.style({ stroke: style[0], 'stroke-opacity': style[1] });
};

/**
* Apply `fill` and `fill-opacity` styles to a D3 selection.
*
* @param {Selection} s - D3 selection
* @param {*} cstr - Color specifier
* @param {Map} [cache] - Cache of the styles of specifiers seen before. A caller
* that loops over points passes one cache for the whole loop.
*/
const fill = (s, cstr) => {
s.style({ fill: rgb(cstr), 'fill-opacity': parse(cstr).alpha });
const fill = (s, cstr, cache) => {
const style = styleOf(cstr, cache);
s.style({ fill: style[0], 'fill-opacity': style[1] });
};

/**
Expand Down
8 changes: 6 additions & 2 deletions src/components/drawing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,8 @@ drawing.pointStyle = function (s, trace, gd, pt) {
if (!s.size()) return;

var fns = drawing.makePointStyleFns(trace);
// The cache dies with this loop, so one trace never slows down the next.
fns.colorCache = new Map();

s.each(function (d) {
drawing.singlePointStyle(d, d3.select(this), trace, fns, gd, pt);
Expand Down Expand Up @@ -1050,11 +1052,13 @@ drawing.singlePointStyle = function (d, sel, trace, fns, gd, pt) {
patternFGOpacity
);
} else {
Lib.isArrayOrTypedArray(fillColor) ? Color.fill(sel, fillColor[d.i]) : Color.fill(sel, fillColor);
Lib.isArrayOrTypedArray(fillColor)
? Color.fill(sel, fillColor[d.i], fns.colorCache)
: Color.fill(sel, fillColor, fns.colorCache);
}

if (lineWidth) {
Color.stroke(sel, lineColor);
Color.stroke(sel, lineColor, fns.colorCache);
}
}
};
Expand Down
1 change: 1 addition & 0 deletions src/traces/scatter/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
var styleFns;
if(showMarkers) {
styleFns = Drawing.makePointStyleFns(trace);
styleFns.colorCache = new Map();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @robertclaus for the improvements.

Similar adjustment could possibly be applied in bar trace after

var styleFns = Drawing.makePointStyleFns(trace);

Also wondering if adjusting other traces code may be needed.

}

join.each(function(d) {
Expand Down
Loading