Skip to content

Commit f7fc8cb

Browse files
committed
Merge branch 'master' into global-context
2 parents 1517ece + b5b72ff commit f7fc8cb

File tree

113 files changed

+2447
-637
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+2447
-637
lines changed

src/components/drawing/index.js

Lines changed: 165 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var svgTextUtils = require('../../lib/svg_text_utils');
2222
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
2323
var alignment = require('../../constants/alignment');
2424
var LINE_SPACING = alignment.LINE_SPACING;
25+
var DESELECTDIM = require('../../constants/interactions').DESELECTDIM;
2526

2627
var subTypes = require('../../traces/scatter/subtypes');
2728
var makeBubbleSizeFn = require('../../traces/scatter/make_bubble_size_func');
@@ -92,21 +93,27 @@ drawing.translatePoints = function(s, xa, ya) {
9293
});
9394
};
9495

95-
drawing.hideOutsideRangePoint = function(d, sel, xa, ya) {
96+
drawing.hideOutsideRangePoint = function(d, sel, xa, ya, xcalendar, ycalendar) {
9697
sel.attr(
9798
'display',
98-
xa.isPtWithinRange(d) && ya.isPtWithinRange(d) ? null : 'none'
99+
(xa.isPtWithinRange(d, xcalendar) && ya.isPtWithinRange(d, ycalendar)) ? null : 'none'
99100
);
100101
};
101102

102-
drawing.hideOutsideRangePoints = function(points, subplot) {
103+
drawing.hideOutsideRangePoints = function(traceGroups, subplot) {
103104
if(!subplot._hasClipOnAxisFalse) return;
104105

105106
var xa = subplot.xaxis;
106107
var ya = subplot.yaxis;
107108

108-
points.each(function(d) {
109-
drawing.hideOutsideRangePoint(d, d3.select(this), xa, ya);
109+
traceGroups.each(function(d) {
110+
var trace = d[0].trace;
111+
var xcalendar = trace.xcalendar;
112+
var ycalendar = trace.ycalendar;
113+
114+
traceGroups.selectAll('.point,.textpoint').each(function(d) {
115+
drawing.hideOutsideRangePoint(d, d3.select(this), xa, ya, xcalendar, ycalendar);
116+
});
110117
});
111118
};
112119

@@ -247,18 +254,22 @@ drawing.symbolNumber = function(v) {
247254
return Math.floor(Math.max(v, 0));
248255
};
249256

257+
function makePointPath(symbolNumber, r) {
258+
var base = symbolNumber % 100;
259+
return drawing.symbolFuncs[base](r) + (symbolNumber >= 200 ? DOTPATH : '');
260+
}
261+
250262
function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerLine, gd) {
251-
// only scatter & box plots get marker path and opacity
252-
// bars, histograms don't
253263
if(Registry.traceIs(trace, 'symbols')) {
254264
var sizeFn = makeBubbleSizeFn(trace);
255265

256266
sel.attr('d', function(d) {
257267
var r;
258268

259269
// handle multi-trace graph edit case
260-
if(d.ms === 'various' || marker.size === 'various') r = 3;
261-
else {
270+
if(d.ms === 'various' || marker.size === 'various') {
271+
r = 3;
272+
} else {
262273
r = subTypes.isBubble(trace) ?
263274
sizeFn(d.ms) : (marker.size || 6) / 2;
264275
}
@@ -267,21 +278,20 @@ function singlePointStyle(d, sel, trace, markerScale, lineScale, marker, markerL
267278
d.mrc = r;
268279

269280
// turn the symbol into a sanitized number
270-
var x = drawing.symbolNumber(d.mx || marker.symbol) || 0,
271-
xBase = x % 100;
281+
var x = drawing.symbolNumber(d.mx || marker.symbol) || 0;
272282

273283
// save if this marker is open
274284
// because that impacts how to handle colors
275285
d.om = x % 200 >= 100;
276286

277-
return drawing.symbolFuncs[xBase](r) +
278-
(x >= 200 ? DOTPATH : '');
279-
})
280-
.style('opacity', function(d) {
281-
return (d.mo + 1 || marker.opacity + 1) - 1;
287+
return makePointPath(x, r);
282288
});
283289
}
284290

291+
sel.style('opacity', function(d) {
292+
return (d.mo + 1 || marker.opacity + 1) - 1;
293+
});
294+
285295
var perPointGradient = false;
286296

287297
// 'so' is suspected outliers, for box plots
@@ -409,7 +419,6 @@ drawing.singlePointStyle = function(d, sel, trace, markerScale, lineScale, gd) {
409419
var marker = trace.marker;
410420

411421
singlePointStyle(d, sel, trace, markerScale, lineScale, marker, marker.line, gd);
412-
413422
};
414423

415424
drawing.pointStyle = function(s, trace, gd) {
@@ -426,6 +435,84 @@ drawing.pointStyle = function(s, trace, gd) {
426435
});
427436
};
428437

438+
drawing.selectedPointStyle = function(s, trace) {
439+
if(!s.size() || !trace.selectedpoints) return;
440+
441+
var selectedAttrs = trace.selected || {};
442+
var unselectedAttrs = trace.unselected || {};
443+
444+
var marker = trace.marker || {};
445+
var selectedMarker = selectedAttrs.marker || {};
446+
var unselectedMarker = unselectedAttrs.marker || {};
447+
448+
var mo = marker.opacity;
449+
var smo = selectedMarker.opacity;
450+
var usmo = unselectedMarker.opacity;
451+
var smoIsDefined = smo !== undefined;
452+
var usmoIsDefined = usmo !== undefined;
453+
454+
s.each(function(d) {
455+
var pt = d3.select(this);
456+
var dmo = d.mo;
457+
var dmoIsDefined = dmo !== undefined;
458+
var mo2;
459+
460+
if(dmoIsDefined || smoIsDefined || usmoIsDefined) {
461+
if(d.selected) {
462+
if(smoIsDefined) mo2 = smo;
463+
} else {
464+
if(usmoIsDefined) mo2 = usmo;
465+
else mo2 = DESELECTDIM * (dmoIsDefined ? dmo : mo);
466+
}
467+
}
468+
469+
if(mo2 !== undefined) pt.style('opacity', mo2);
470+
});
471+
472+
var smc = selectedMarker.color;
473+
var usmc = unselectedMarker.color;
474+
475+
if(smc || usmc) {
476+
s.each(function(d) {
477+
var pt = d3.select(this);
478+
var mc2;
479+
480+
if(d.selected) {
481+
if(smc) mc2 = smc;
482+
} else {
483+
if(usmc) mc2 = usmc;
484+
}
485+
486+
if(mc2) Color.fill(pt, mc2);
487+
});
488+
}
489+
490+
var sms = selectedMarker.size;
491+
var usms = unselectedMarker.size;
492+
var smsIsDefined = sms !== undefined;
493+
var usmsIsDefined = usms !== undefined;
494+
495+
if(Registry.traceIs(trace, 'symbols') && (smsIsDefined || usmsIsDefined)) {
496+
s.each(function(d) {
497+
var pt = d3.select(this);
498+
var mrc = d.mrc;
499+
var mx = d.mx || marker.symbol || 0;
500+
var mrc2;
501+
502+
if(d.selected) {
503+
mrc2 = (smsIsDefined) ? sms / 2 : mrc;
504+
} else {
505+
mrc2 = (usmsIsDefined) ? usms / 2 : mrc;
506+
}
507+
508+
pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2));
509+
510+
// save for selectedTextStyle
511+
d.mrc2 = mrc2;
512+
});
513+
}
514+
};
515+
429516
drawing.tryColorscale = function(marker, prefix) {
430517
var cont = prefix ? Lib.nestedProperty(marker, prefix).get() : marker,
431518
scl = cont.colorscale,
@@ -439,8 +526,39 @@ drawing.tryColorscale = function(marker, prefix) {
439526
else return Lib.identity;
440527
};
441528

442-
// draw text at points
443529
var TEXTOFFSETSIGN = {start: 1, end: -1, middle: 0, bottom: 1, top: -1};
530+
531+
function textPointPosition(s, textPosition, fontSize, markerRadius) {
532+
var group = d3.select(s.node().parentNode);
533+
534+
var v = textPosition.indexOf('top') !== -1 ?
535+
'top' :
536+
textPosition.indexOf('bottom') !== -1 ? 'bottom' : 'middle';
537+
var h = textPosition.indexOf('left') !== -1 ?
538+
'end' :
539+
textPosition.indexOf('right') !== -1 ? 'start' : 'middle';
540+
541+
// if markers are shown, offset a little more than
542+
// the nominal marker size
543+
// ie 2/1.6 * nominal, bcs some markers are a bit bigger
544+
var r = markerRadius ? markerRadius / 0.8 + 1 : 0;
545+
546+
var numLines = (svgTextUtils.lineCount(s) - 1) * LINE_SPACING + 1;
547+
var dx = TEXTOFFSETSIGN[h] * r;
548+
var dy = fontSize * 0.75 + TEXTOFFSETSIGN[v] * r +
549+
(TEXTOFFSETSIGN[v] - 1) * numLines * fontSize / 2;
550+
551+
// fix the overall text group position
552+
s.attr('text-anchor', h);
553+
group.attr('transform', 'translate(' + dx + ',' + dy + ')');
554+
}
555+
556+
function extracTextFontSize(d, trace) {
557+
var fontSize = d.ts || trace.textfont.size;
558+
return (isNumeric(fontSize) && fontSize > 0) ? fontSize : 0;
559+
}
560+
561+
// draw text at points
444562
drawing.textPointStyle = function(s, trace, gd) {
445563
s.each(function(d) {
446564
var p = d3.select(this);
@@ -451,35 +569,43 @@ drawing.textPointStyle = function(s, trace, gd) {
451569
return;
452570
}
453571

454-
var pos = d.tp || trace.textposition,
455-
v = pos.indexOf('top') !== -1 ? 'top' :
456-
pos.indexOf('bottom') !== -1 ? 'bottom' : 'middle',
457-
h = pos.indexOf('left') !== -1 ? 'end' :
458-
pos.indexOf('right') !== -1 ? 'start' : 'middle',
459-
fontSize = d.ts || trace.textfont.size,
460-
// if markers are shown, offset a little more than
461-
// the nominal marker size
462-
// ie 2/1.6 * nominal, bcs some markers are a bit bigger
463-
r = d.mrc ? (d.mrc / 0.8 + 1) : 0;
464-
465-
fontSize = (isNumeric(fontSize) && fontSize > 0) ? fontSize : 0;
572+
var pos = d.tp || trace.textposition;
573+
var fontSize = extracTextFontSize(d, trace);
466574

467575
p.call(drawing.font,
468576
d.tf || trace.textfont.family,
469577
fontSize,
470578
d.tc || trace.textfont.color)
471-
.attr('text-anchor', h)
472579
.text(text)
473-
.call(svgTextUtils.convertToTspans, gd);
580+
.call(svgTextUtils.convertToTspans, gd)
581+
.call(textPointPosition, pos, fontSize, d.mrc);
582+
});
583+
};
474584

475-
var pgroup = d3.select(this.parentNode);
476-
var numLines = (svgTextUtils.lineCount(p) - 1) * LINE_SPACING + 1;
477-
var dx = TEXTOFFSETSIGN[h] * r;
478-
var dy = fontSize * 0.75 + TEXTOFFSETSIGN[v] * r +
479-
(TEXTOFFSETSIGN[v] - 1) * numLines * fontSize / 2;
585+
drawing.selectedTextStyle = function(s, trace) {
586+
if(!s.size() || !trace.selectedpoints) return;
587+
588+
var selectedAttrs = trace.selected || {};
589+
var unselectedAttrs = trace.unselected || {};
590+
591+
s.each(function(d) {
592+
var tx = d3.select(this);
593+
var tc = d.tc || trace.textfont.color;
594+
var tp = d.tp || trace.textposition;
595+
var fontSize = extracTextFontSize(d, trace);
596+
var stc = (selectedAttrs.textfont || {}).color;
597+
var utc = (unselectedAttrs.textfont || {}).color;
598+
var tc2;
599+
600+
if(d.selected) {
601+
if(stc) tc2 = stc;
602+
} else {
603+
if(utc) tc2 = utc;
604+
else if(!stc) tc2 = Color.addOpacity(tc, DESELECTDIM);
605+
}
480606

481-
// fix the overall text group position
482-
pgroup.attr('transform', 'translate(' + dx + ',' + dy + ')');
607+
if(tc2) Color.fill(tx, tc2);
608+
textPointPosition(tx, tp, fontSize, d.mrc2 || d.mrc);
483609
});
484610
};
485611

src/components/fx/helpers.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,63 @@ exports.quadrature = function quadrature(dx, dy) {
8585
};
8686
};
8787

88+
/** Fill event data point object for hover and selection.
89+
* Invokes _module.eventData if present.
90+
*
91+
* N.B. note that point 'index' corresponds to input data array index
92+
* whereas 'number' is its post-transform version.
93+
*
94+
* If the hovered/selected pt corresponds to an multiple input points
95+
* (e.g. for histogram and transformed traces), 'pointNumbers` and 'pointIndices'
96+
* are include in the event data.
97+
*
98+
* @param {object} pt
99+
* @param {object} trace
100+
* @param {object} cd
101+
* @return {object}
102+
*/
103+
exports.makeEventData = function makeEventData(pt, trace, cd) {
104+
// hover uses 'index', select uses 'pointNumber'
105+
var pointNumber = 'index' in pt ? pt.index : pt.pointNumber;
106+
107+
var out = {
108+
data: trace._input,
109+
fullData: trace,
110+
curveNumber: trace.index,
111+
pointNumber: pointNumber
112+
};
113+
114+
if(trace._indexToPoints) {
115+
var pointIndices = trace._indexToPoints[pointNumber];
116+
117+
if(pointIndices.length === 1) {
118+
out.pointIndex = pointIndices[0];
119+
} else {
120+
out.pointIndices = pointIndices;
121+
}
122+
} else {
123+
out.pointIndex = pointNumber;
124+
}
125+
126+
if(trace._module.eventData) {
127+
out = trace._module.eventData(out, pt, trace, cd, pointNumber);
128+
} else {
129+
if('xVal' in pt) out.x = pt.xVal;
130+
else if('x' in pt) out.x = pt.x;
131+
132+
if('yVal' in pt) out.y = pt.yVal;
133+
else if('y' in pt) out.y = pt.y;
134+
135+
if(pt.xa) out.xaxis = pt.xa;
136+
if(pt.ya) out.yaxis = pt.ya;
137+
if(pt.zLabelVal !== undefined) out.z = pt.zLabelVal;
138+
}
139+
140+
exports.appendArrayPointValue(out, trace, pointNumber);
141+
142+
return out;
143+
};
144+
88145
/** Appends values inside array attributes corresponding to given point number
89146
*
90147
* @param {object} pointData : point data object (gets mutated here)

src/components/fx/hover.js

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -417,26 +417,7 @@ function _hover(gd, evt, subplot, noHoverEvent) {
417417
// other people and send it to the event
418418
for(itemnum = 0; itemnum < hoverData.length; itemnum++) {
419419
var pt = hoverData[itemnum];
420-
421-
var out = {
422-
data: pt.trace._input,
423-
fullData: pt.trace,
424-
curveNumber: pt.trace.index,
425-
pointNumber: pt.index
426-
};
427-
428-
if(pt.trace._module.eventData) out = pt.trace._module.eventData(out, pt);
429-
else {
430-
out.x = pt.xVal;
431-
out.y = pt.yVal;
432-
out.xaxis = pt.xa;
433-
out.yaxis = pt.ya;
434-
435-
if(pt.zLabelVal !== undefined) out.z = pt.zLabelVal;
436-
}
437-
438-
helpers.appendArrayPointValue(out, pt.trace, pt.index);
439-
newhoverdata.push(out);
420+
newhoverdata.push(helpers.makeEventData(pt, pt.trace, pt.cd));
440421
}
441422

442423
gd._hoverdata = newhoverdata;

0 commit comments

Comments
 (0)