From c28b26a4f42493dc14d266fdce6f375efd9aaed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Fri, 7 Jul 2017 11:49:37 -0400 Subject: [PATCH 01/20] lint in drawing/ --- src/components/drawing/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index e234c5d2a68..b700861a548 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -69,8 +69,8 @@ drawing.setRect = function(s, x, y, w, h) { */ drawing.translatePoint = function(d, sel, xa, ya) { // put xp and yp into d if pixel scaling is already done - var x = d.xp || xa.c2p(d.x), - y = d.yp || ya.c2p(d.y); + var x = d.xp = xa.c2p(d.x); + var y = d.yp = ya.c2p(d.y); if(isNumeric(x) && isNumeric(y) && sel.node()) { // for multiline text this works better @@ -86,10 +86,10 @@ drawing.translatePoint = function(d, sel, xa, ya) { return true; }; -drawing.translatePoints = function(s, xa, ya, trace) { +drawing.translatePoints = function(s, xa, ya) { s.each(function(d) { var sel = d3.select(this); - drawing.translatePoint(d, sel, xa, ya, trace); + drawing.translatePoint(d, sel, xa, ya); }); }; From 8f72d1fc1c840468ff7fc4982cdd98d6ced53092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Fri, 7 Jul 2017 11:56:23 -0400 Subject: [PATCH 02/20] implement cliponaxis - keep track of which subplots have 1 or more `cliponaxis: false` traces + if so, don't clip the subplot layer + instead, clip all trace module layers except for 'scatterlayer' + when `cliponaxis: false`, clip all lines and errorbar groups - use ax.isWithinRange and Drawing.hideOutsideRangePoint to hide markers and text node that are out of range. --- src/components/drawing/index.js | 7 +++++++ src/components/errorbars/plot.js | 10 ++++++++-- src/plot_api/subroutines.js | 23 +++++++++++++++++++++-- src/plots/cartesian/constants.js | 18 +++++++++++++++++- src/plots/cartesian/index.js | 25 ++++++------------------- src/plots/cartesian/set_convert.js | 18 +++++++++++++++--- src/plots/plots.js | 19 +++++++++++++++++++ src/traces/scatter/attributes.js | 12 ++++++++++++ src/traces/scatter/defaults.js | 2 ++ src/traces/scatter/plot.js | 30 +++++++++++++++++++++++++----- 10 files changed, 132 insertions(+), 32 deletions(-) diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index b700861a548..498693ded8a 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -93,6 +93,13 @@ drawing.translatePoints = function(s, xa, ya) { }); }; +drawing.hideOutsideRangePoint = function(d, sel, xa, ya) { + sel.attr( + 'visibility', + xa.isPtWithinRange(d) && ya.isPtWithinRange(d) ? null : 'hidden' + ); +}; + drawing.getPx = function(s, styleAttr) { // helper to pull out a px value from a style that may contain px units // s is a d3 selection (will pull from the first one) diff --git a/src/components/errorbars/plot.js b/src/components/errorbars/plot.js index 84bc05504bf..74627ffa934 100644 --- a/src/components/errorbars/plot.js +++ b/src/components/errorbars/plot.js @@ -12,13 +12,14 @@ var d3 = require('d3'); var isNumeric = require('fast-isnumeric'); +var Drawing = require('../drawing'); var subTypes = require('../../traces/scatter/subtypes'); module.exports = function plot(traces, plotinfo, transitionOpts) { var isNew; - var xa = plotinfo.xaxis, - ya = plotinfo.yaxis; + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; var hasAnimation = transitionOpts && transitionOpts.duration > 0; @@ -60,6 +61,11 @@ module.exports = function plot(traces, plotinfo, transitionOpts) { .style('opacity', 1); } + errorbars.call( + Drawing.setClipUrl, + plotinfo._hasClipOnAxisFalse ? plotinfo.clipId : null + ); + errorbars.each(function(d) { var errorbar = d3.select(this); var coords = errorCoords(d, xa, ya); diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 64d1663910d..ef09f4a8788 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -20,6 +20,7 @@ var Drawing = require('../components/drawing'); var Titles = require('../components/titles'); var ModeBar = require('../components/modebar'); var initInteractions = require('../plots/cartesian/graph_interact'); +var cartesianConstants = require('../plots/cartesian/constants'); exports.layoutStyles = function(gd) { return Lib.syncOrAsync([Plots.doAutoMargin, exports.lsInner], gd); @@ -164,9 +165,27 @@ exports.lsInner = function(gd) { 'height': ya._length }); - plotinfo.plot.call(Drawing.setTranslate, xa._offset, ya._offset); - plotinfo.plot.call(Drawing.setClipUrl, plotinfo.clipId); + + var plotClipId; + var layerClipId; + + if(plotinfo._hasClipOnAxisFalse) { + plotClipId = null; + layerClipId = plotinfo.clipId; + } else { + plotClipId = plotinfo.clipId; + layerClipId = null; + } + + plotinfo.plot.call(Drawing.setClipUrl, plotClipId); + + for(i = 0; i < cartesianConstants.layers.length; i++) { + var layer = cartesianConstants.layers[i]; + if(layer !== 'scatterlayer') { + plotinfo.plot.selectAll('g.' + layer).call(Drawing.setClipUrl, layerClipId); + } + } var xlw = Drawing.crispRound(gd, xa.linewidth, 1), ylw = Drawing.crispRound(gd, ya.linewidth, 1), diff --git a/src/plots/cartesian/constants.js b/src/plots/cartesian/constants.js index d72f7c2fd7b..f502bcee5f5 100644 --- a/src/plots/cartesian/constants.js +++ b/src/plots/cartesian/constants.js @@ -49,5 +49,21 @@ module.exports = { // last resort axis ranges for x and y axes if we have no data DFLTRANGEX: [-1, 6], - DFLTRANGEY: [-1, 4] + DFLTRANGEY: [-1, 4], + + // Layers to keep plot types in the right order. + // from back to front: + // 1. heatmaps, 2D histos and contour maps + // 2. bars / 1D histos + // 3. errorbars for bars and scatter + // 4. scatter + // 5. box plots + layers: [ + 'imagelayer', + 'maplayer', + 'barlayer', + 'carpetlayer', + 'boxlayer', + 'scatterlayer' + ] }; diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 8d86648e4be..7548a608251 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -294,24 +294,8 @@ function makeSubplotData(gd) { } function makeSubplotLayer(plotinfo) { - var plotgroup = plotinfo.plotgroup, - id = plotinfo.id; - - // Layers to keep plot types in the right order. - // from back to front: - // 1. heatmaps, 2D histos and contour maps - // 2. bars / 1D histos - // 3. errorbars for bars and scatter - // 4. scatter - // 5. box plots - function joinPlotLayers(parent) { - joinLayer(parent, 'g', 'imagelayer'); - joinLayer(parent, 'g', 'maplayer'); - joinLayer(parent, 'g', 'barlayer'); - joinLayer(parent, 'g', 'carpetlayer'); - joinLayer(parent, 'g', 'boxlayer'); - joinLayer(parent, 'g', 'scatterlayer'); - } + var plotgroup = plotinfo.plotgroup; + var id = plotinfo.id; if(!plotinfo.mainplot) { var backLayer = joinLayer(plotgroup, 'g', 'layer-subplot'); @@ -354,7 +338,10 @@ function makeSubplotLayer(plotinfo) { } // common attributes for all subplots, overlays or not - plotinfo.plot.call(joinPlotLayers); + + for(var i = 0; i < constants.layers.length; i++) { + joinLayer(plotinfo.plot, 'g', constants.layers[i]); + } plotinfo.xlines .style('fill', 'none') diff --git a/src/plots/cartesian/set_convert.js b/src/plots/cartesian/set_convert.js index 5b885f80503..c72492a4d0e 100644 --- a/src/plots/cartesian/set_convert.js +++ b/src/plots/cartesian/set_convert.js @@ -58,6 +58,8 @@ function fromLog(v) { module.exports = function setConvert(ax, fullLayout) { fullLayout = fullLayout || {}; + var axLetter = (ax._id || 'x').charAt(0); + // clipMult: how many axis lengths past the edge do we render? // for panning, 1-2 would suffice, but for zooming more is nice. // also, clipping can affect the direction of lines off the edge... @@ -277,7 +279,6 @@ module.exports = function setConvert(ax, fullLayout) { ax.cleanRange = function(rangeAttr) { if(!rangeAttr) rangeAttr = 'range'; var range = Lib.nestedProperty(ax, rangeAttr).get(), - axLetter = (ax._id || 'x').charAt(0), i, dflt; if(ax.type === 'date') dflt = Lib.dfltRange(ax.calendar); @@ -341,8 +342,7 @@ module.exports = function setConvert(ax, fullLayout) { // set scaling to pixels ax.setScale = function(usePrivateRange) { - var gs = fullLayout._size, - axLetter = ax._id.charAt(0); + var gs = fullLayout._size; // TODO cleaner way to handle this case if(!ax._categories) ax._categories = []; @@ -435,6 +435,18 @@ module.exports = function setConvert(ax, fullLayout) { ); }; + if(axLetter === 'x') { + ax.isPtWithinRange = function(d) { + var x = d.x; + return x >= ax.range[0] && x <= ax.range[1]; + }; + } else { + ax.isPtWithinRange = function(d) { + var y = d.y; + return y >= ax.range[0] && y <= ax.range[1]; + }; + } + // for autoranging: arrays of objects: // {val: axis value, pad: pixel padding} // on the low and high sides diff --git a/src/plots/plots.js b/src/plots/plots.js index 38416678886..1a95bad876b 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -656,6 +656,25 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa plotinfo.xaxis = Plotly.Axes.getFromId(mockGd, id, 'x'); plotinfo.yaxis = Plotly.Axes.getFromId(mockGd, id, 'y'); + + // By default, we clip at the subplot level, + // but if one trace on a given subplot has *cliponaxis* set to false, + // we need to clip at the trace module layer level; + // find this out here, once of for all. + plotinfo._hasClipOnAxisFalse = false; + + for(var j = 0; j < newFullData.length; j++) { + var trace = newFullData[j]; + + if( + trace.xaxis === plotinfo.xaxis._id && + trace.yaxis === plotinfo.yaxis._id && + trace.cliponaxis === false + ) { + plotinfo._hasClipOnAxisFalse = true; + break; + } + } } }; diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index 726bb94bfef..2076f1012b7 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -168,6 +168,7 @@ module.exports = { ].join(' ') } }, + connectgaps: { valType: 'boolean', dflt: false, @@ -178,6 +179,17 @@ module.exports = { 'in the provided data arrays are connected.' ].join(' ') }, + cliponaxis: { + valType: 'boolean', + dflt: true, + role: 'info', + editType: 'doplot', + description: [ + 'Determines whether or not markers and text nodes', + 'are clipped about the subplot axes.' + ].join(' ') + }, + fill: { valType: 'enumerated', values: ['none', 'tozeroy', 'tozerox', 'tonexty', 'tonextx', 'toself', 'tonext'], diff --git a/src/traces/scatter/defaults.js b/src/traces/scatter/defaults.js index e7ed99fdda2..9699469615f 100644 --- a/src/traces/scatter/defaults.js +++ b/src/traces/scatter/defaults.js @@ -75,4 +75,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'y'}); errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'x', inherit: 'y'}); + + coerce('cliponaxis'); }; diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js index 0a877aee4de..cdcd47cf2fe 100644 --- a/src/traces/scatter/plot.js +++ b/src/traces/scatter/plot.js @@ -46,7 +46,7 @@ module.exports = function plot(gd, plotinfo, cdscatter, transitionOpts, makeOnCo // the z-order of fill layers is correct. linkTraces(gd, plotinfo, cdscatter); - createFills(gd, scatterlayer); + createFills(gd, scatterlayer, plotinfo); // Sort the traces, once created, so that the ordering is preserved even when traces // are shown and hidden. This is needed since we're not just wiping everything out @@ -100,7 +100,7 @@ module.exports = function plot(gd, plotinfo, cdscatter, transitionOpts, makeOnCo scatterlayer.selectAll('path:not([d])').remove(); }; -function createFills(gd, scatterlayer) { +function createFills(gd, scatterlayer, plotinfo) { var trace; scatterlayer.selectAll('g.trace').each(function(d) { @@ -138,6 +138,10 @@ function createFills(gd, scatterlayer) { tr.selectAll('.js-fill.js-tozero').remove(); trace._ownFill = null; } + + if(plotinfo._hasClipOnAxisFalse) { + tr.selectAll('.js-fill').call(Drawing.setClipUrl, plotinfo.clipId); + } }); } @@ -324,6 +328,10 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition .call(Drawing.lineGroupStyle) .each(makeUpdate(true)); + if(plotinfo._hasClipOnAxisFalse) { + Drawing.setClipUrl(lineJoin, plotinfo.clipId); + } + if(segments.length) { if(ownFillEl3) { if(pt0 && pt1) { @@ -400,7 +408,8 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition var trace = d[0].trace, s = d3.select(this), showMarkers = subTypes.hasMarkers(trace), - showText = subTypes.hasText(trace); + showText = subTypes.hasText(trace), + hasClipOnAxisFalse = trace.cliponaxis === false; var keyFunc = getKeyFunc(trace), markerFilter = hideFilter, @@ -426,7 +435,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition if(hasTransition) { enter .call(Drawing.pointStyle, trace, gd) - .call(Drawing.translatePoints, xa, ya, trace) + .call(Drawing.translatePoints, xa, ya) .style('opacity', 0) .transition() .style('opacity', 1); @@ -445,6 +454,10 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition if(hasNode) { Drawing.singlePointStyle(d, sel, trace, markerScale, lineScale, gd); + if(hasClipOnAxisFalse) { + Drawing.hideOutsideRangePoint(d, sel, xa, ya); + } + if(trace.customdata) { el.classed('plotly-customdata', d.data !== null && d.data !== undefined); } @@ -475,7 +488,14 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition var g = d3.select(this); var sel = transition(g.select('text')); hasNode = Drawing.translatePoint(d, sel, xa, ya); - if(!hasNode) g.remove(); + + if(hasNode) { + if(hasClipOnAxisFalse) { + Drawing.hideOutsideRangePoint(d, g, xa, ya); + } + } else { + g.remove(); + } }); join.selectAll('text') From 1909034b9ab23fc5b18c51f39aeb269209ef61a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Fri, 7 Jul 2017 11:56:47 -0400 Subject: [PATCH 03/20] hide outside range points on drag and transitions --- src/components/drawing/index.js | 11 +++++++++++ src/plots/cartesian/dragbox.js | 25 ++++++++++++++----------- src/plots/cartesian/transition_axes.js | 20 ++++++++++++-------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index 498693ded8a..1aa1a488e95 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -100,6 +100,17 @@ drawing.hideOutsideRangePoint = function(d, sel, xa, ya) { ); }; +drawing.hideOutsideRangePoints = function(points, subplot) { + if(!subplot._hasClipOnAxisFalse) return; + + var xa = subplot.xaxis; + var ya = subplot.yaxis; + + points.each(function(d) { + drawing.hideOutsideRangePoint(d, d3.select(this), xa, ya); + }); +}; + drawing.getPx = function(s, styleAttr) { // helper to pull out a px value from a style that may contain px units // s is a d3 selection (will pull from the first one) diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index 603e88d2f78..fa93d51e5b4 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -747,19 +747,22 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { .call(Drawing.setTranslate, clipDx, clipDy) .call(Drawing.setScale, xScaleFactor2, yScaleFactor2); + var scatterPoints = subplot.plot.select('.scatterlayer').selectAll('.points'); + subplot.plot .call(Drawing.setTranslate, plotDx, plotDy) - .call(Drawing.setScale, 1 / xScaleFactor2, 1 / yScaleFactor2) - - // This is specifically directed at scatter traces, applying an inverse - // scale to individual points to counteract the scale of the trace - // as a whole: - .select('.scatterlayer').selectAll('.points').selectAll('.point') - .call(Drawing.setPointGroupScale, xScaleFactor2, yScaleFactor2); - - subplot.plot.select('.scatterlayer') - .selectAll('.points').selectAll('.textpoint') - .call(Drawing.setTextPointsScale, xScaleFactor2, yScaleFactor2); + .call(Drawing.setScale, 1 / xScaleFactor2, 1 / yScaleFactor2); + + // This is specifically directed at scatter traces, applying an inverse + // scale to individual points to counteract the scale of the trace + // as a whole: + scatterPoints.selectAll('.point') + .call(Drawing.setPointGroupScale, xScaleFactor2, yScaleFactor2) + .call(Drawing.hideOutsideRangePoints, subplot); + + scatterPoints.selectAll('.textpoint') + .call(Drawing.setTextPointsScale, xScaleFactor2, yScaleFactor2) + .call(Drawing.hideOutsideRangePoints, subplot); } } diff --git a/src/plots/cartesian/transition_axes.js b/src/plots/cartesian/transition_axes.js index 44344a35132..37557d6cc5f 100644 --- a/src/plots/cartesian/transition_axes.js +++ b/src/plots/cartesian/transition_axes.js @@ -143,16 +143,20 @@ module.exports = function transitionAxes(gd, newLayout, transitionOpts, makeOnCo subplot.plot .call(Drawing.setTranslate, xa2._offset, ya2._offset) - .call(Drawing.setScale, 1, 1) + .call(Drawing.setScale, 1, 1); - // This is specifically directed at scatter traces, applying an inverse - // scale to individual points to counteract the scale of the trace - // as a whole: - .select('.scatterlayer').selectAll('.points').selectAll('.point') - .call(Drawing.setPointGroupScale, 1, 1); + var scatterPoints = subplot.plot.select('.scatterlayer').selectAll('.points'); + + // This is specifically directed at scatter traces, applying an inverse + // scale to individual points to counteract the scale of the trace + // as a whole: + scatterPoints.selectAll('.point') + .call(Drawing.setPointGroupScale, 1, 1) + .call(Drawing.hideOutsideRangePoints, subplot); - subplot.plot.select('.scatterlayer').selectAll('.points').selectAll('.textpoint') - .call(Drawing.setTextPointsScale, 1, 1); + scatterPoints.selectAll('.textpoint') + .call(Drawing.setTextPointsScale, 1, 1) + .call(Drawing.hideOutsideRangePoints, subplot); } function updateSubplot(subplot, progress) { From 4b5c4bb7f407e48a25a4a48c4ea951c8aa9008dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Fri, 7 Jul 2017 11:59:33 -0400 Subject: [PATCH 04/20] implement `cliponaxis: false` for scatterternary - similar to the cartesian case, keep track of subplots that have 1 or more `cliponaxis: false` traces - override setConvert's ax.isPtWithinRange - add relative clip path def (to appropriately clip lines group) --- src/plots/ternary/index.js | 1 + src/plots/ternary/ternary.js | 55 ++++++++++++++++++++++--- src/traces/scatterternary/attributes.js | 1 + src/traces/scatterternary/defaults.js | 2 + src/traces/scatterternary/plot.js | 4 +- 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/plots/ternary/index.js b/src/plots/ternary/index.js index 2b24ed0938a..a915b23e6c3 100644 --- a/src/plots/ternary/index.js +++ b/src/plots/ternary/index.js @@ -67,6 +67,7 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) if(!newFullLayout[oldTernaryKey] && !!oldTernary) { oldTernary.plotContainer.remove(); oldTernary.clipDef.remove(); + oldTernary.clipDefRelative.remove(); } } diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index 4670167ca18..944a8e1c433 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -46,9 +46,19 @@ proto.init = function(fullLayout) { }; proto.plot = function(ternaryCalcData, fullLayout) { - var _this = this, - ternaryLayout = fullLayout[_this.id], - graphSize = fullLayout._size; + var _this = this; + var ternaryLayout = fullLayout[_this.id]; + var graphSize = fullLayout._size; + + _this._hasClipOnAxisFalse = false; + for(var i = 0; i < ternaryCalcData.length; i++) { + var trace = ternaryCalcData[i][0].trace; + + if(trace.cliponaxis === false) { + _this._hasClipOnAxisFalse = true; + break; + } + } _this.adjustLayout(ternaryLayout, graphSize); @@ -66,12 +76,19 @@ proto.makeFramework = function() { .classed('clips', true); // clippath for this ternary subplot - var clipId = 'clip' + _this.layoutId + _this.id; + var clipId = _this.clipId = 'clip' + _this.layoutId + _this.id; _this.clipDef = defGroup.selectAll('#' + clipId) .data([0]); _this.clipDef.enter().append('clipPath').attr('id', clipId) .append('path').attr('d', 'M0,0Z'); + // 'relative' clippath (i.e. no translation) for this ternary subplot + var clipIdRelative = _this.clipIdRelative = 'clip-relative' + _this.layoutId + _this.id; + _this.clipDefRelative = defGroup.selectAll('#' + clipIdRelative) + .data([0]); + _this.clipDefRelative.enter().append('clipPath').attr('id', clipIdRelative) + .append('path').attr('d', 'M0,0Z'); + // container for everything in this ternary subplot _this.plotContainer = _this.container.selectAll('g.' + _this.id) .data([0]); @@ -120,7 +137,7 @@ proto.makeFramework = function() { .attr('class', function(d) { return 'grid ' + d; }) .each(function(d) { _this.layers[d] = d3.select(this); }); - _this.plotContainer.selectAll('.backplot,.frontplot,.grids') + _this.plotContainer.selectAll('.backplot,.grids') .call(Drawing.setClipUrl, clipId); }; @@ -175,6 +192,16 @@ proto.adjustLayout = function(ternaryLayout, graphSize) { }; setConvert(_this.xaxis, _this.graphDiv._fullLayout); _this.xaxis.setScale(); + _this.xaxis.isPtWithinRange = function(d) { + return ( + d.a >= _this.aaxis.range[0] && + d.a <= _this.aaxis.range[1] && + d.b >= _this.baxis.range[1] && + d.b <= _this.baxis.range[0] && + d.c >= _this.caxis.range[1] && + d.c <= _this.caxis.range[0] + ); + }; _this.yaxis = { type: 'linear', @@ -187,6 +214,7 @@ proto.adjustLayout = function(ternaryLayout, graphSize) { }; setConvert(_this.yaxis, _this.graphDiv._fullLayout); _this.yaxis.setScale(); + _this.yaxis.isPtWithinRange = function() { return true; }; // set up the modified axes for tick drawing var yDomain0 = _this.yaxis.domain[0]; @@ -257,6 +285,9 @@ proto.adjustLayout = function(ternaryLayout, graphSize) { _this.clipDef.select('path').attr('d', triangleClip); _this.layers.plotbg.select('path').attr('d', triangleClip); + var triangleClipRelative = 'M0,' + h + 'h' + w + 'l-' + (w / 2) + ',-' + h + 'Z'; + _this.clipDefRelative.select('path').attr('d', triangleClipRelative); + var plotTransform = 'translate(' + x0 + ',' + y0 + ')'; _this.plotContainer.selectAll('.scatterlayer,.maplayer') .attr('transform', plotTransform); @@ -302,6 +333,9 @@ proto.adjustLayout = function(ternaryLayout, graphSize) { if(!_this.graphDiv._context.staticPlot) { _this.initInteractions(); } + + _this.plotContainer.select('.frontplot') + .call(Drawing.setClipUrl, _this._hasClipOnAxisFalse ? null : _this.clipId); }; proto.drawAxes = function(doTitles) { @@ -589,6 +623,17 @@ proto.initInteractions = function() { _this.drawAxes(false); _this.plotContainer.selectAll('.crisp').classed('crisp', false); + + if(_this._hasClipOnAxisFalse) { + var scatterPoints = _this.plotContainer + .select('.scatterlayer').selectAll('.points'); + + scatterPoints.selectAll('.point') + .call(Drawing.hideOutsideRangePoints, _this); + + scatterPoints.selectAll('.textpoint') + .call(Drawing.hideOutsideRangePoints, _this); + } } function dragDone(dragged, numClicks) { diff --git a/src/traces/scatterternary/attributes.js b/src/traces/scatterternary/attributes.js index c6d3b709760..4b5b73bca6f 100644 --- a/src/traces/scatterternary/attributes.js +++ b/src/traces/scatterternary/attributes.js @@ -95,6 +95,7 @@ module.exports = { smoothing: scatterLineAttrs.smoothing }, connectgaps: scatterAttrs.connectgaps, + cliponaxis: scatterAttrs.cliponaxis, fill: extendFlat({}, scatterAttrs.fill, { values: ['none', 'toself', 'tonext'], description: [ diff --git a/src/traces/scatterternary/defaults.js b/src/traces/scatterternary/defaults.js index 5a3b8dd3e4f..9a1ca4f4fcd 100644 --- a/src/traces/scatterternary/defaults.js +++ b/src/traces/scatterternary/defaults.js @@ -99,4 +99,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout dfltHoverOn.push('fills'); } coerce('hoveron', dfltHoverOn.join('+') || 'points'); + + coerce('cliponaxis'); }; diff --git a/src/traces/scatterternary/plot.js b/src/traces/scatterternary/plot.js index 0ecfaa25601..3a583025a60 100644 --- a/src/traces/scatterternary/plot.js +++ b/src/traces/scatterternary/plot.js @@ -22,7 +22,9 @@ module.exports = function plot(ternary, moduleCalcData) { var plotinfo = { xaxis: ternary.xaxis, yaxis: ternary.yaxis, - plot: plotContainer + plot: plotContainer, + clipId: ternary.clipIdRelative, + _hasClipOnAxisFalse: ternary._hasClipOnAxisFalse }; // add ref to ternary subplot object in fullData traces From d46cae7e7332e385c3a9a498136344f8a071d02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Fri, 7 Jul 2017 12:01:57 -0400 Subject: [PATCH 05/20] merge custom shared assertions into single assets/ module --- test/jasmine/assets/assert_dims.js | 18 ------------------ .../{assert_style.js => custom_assertions.js} | 17 ++++++++++++++++- test/jasmine/tests/transform_filter_test.js | 6 ++++-- test/jasmine/tests/transform_groupby_test.js | 6 ++++-- test/jasmine/tests/transform_multi_test.js | 5 +++-- 5 files changed, 27 insertions(+), 25 deletions(-) delete mode 100644 test/jasmine/assets/assert_dims.js rename test/jasmine/assets/{assert_style.js => custom_assertions.js} (65%) diff --git a/test/jasmine/assets/assert_dims.js b/test/jasmine/assets/assert_dims.js deleted file mode 100644 index 2db7f297b4f..00000000000 --- a/test/jasmine/assets/assert_dims.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -var d3 = require('d3'); - -module.exports = function assertDims(dims) { - var traces = d3.selectAll('.trace'); - - expect(traces.size()) - .toEqual(dims.length, 'to have correct number of traces'); - - traces.each(function(_, i) { - var trace = d3.select(this); - var points = trace.selectAll('.point'); - - expect(points.size()) - .toEqual(dims[i], 'to have correct number of pts in trace ' + i); - }); -}; diff --git a/test/jasmine/assets/assert_style.js b/test/jasmine/assets/custom_assertions.js similarity index 65% rename from test/jasmine/assets/assert_style.js rename to test/jasmine/assets/custom_assertions.js index c6684da041e..7c84f215c77 100644 --- a/test/jasmine/assets/assert_style.js +++ b/test/jasmine/assets/custom_assertions.js @@ -2,7 +2,22 @@ var d3 = require('d3'); -module.exports = function assertStyle(dims, color, opacity) { +exports.assertDims = function(dims) { + var traces = d3.selectAll('.trace'); + + expect(traces.size()) + .toEqual(dims.length, 'to have correct number of traces'); + + traces.each(function(_, i) { + var trace = d3.select(this); + var points = trace.selectAll('.point'); + + expect(points.size()) + .toEqual(dims[i], 'to have correct number of pts in trace ' + i); + }); +}; + +exports.assertStyle = function(dims, color, opacity) { var N = dims.reduce(function(a, b) { return a + b; }); diff --git a/test/jasmine/tests/transform_filter_test.js b/test/jasmine/tests/transform_filter_test.js index d5bfe093c35..716c30bf6ca 100644 --- a/test/jasmine/tests/transform_filter_test.js +++ b/test/jasmine/tests/transform_filter_test.js @@ -6,9 +6,11 @@ var Lib = require('@src/lib'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); -var assertDims = require('../assets/assert_dims'); -var assertStyle = require('../assets/assert_style'); var customMatchers = require('../assets/custom_matchers'); +var customAssertions = require('../assets/custom_assertions'); + +var assertDims = customAssertions.assertDims; +var assertStyle = customAssertions.assertStyle; describe('filter transforms defaults:', function() { diff --git a/test/jasmine/tests/transform_groupby_test.js b/test/jasmine/tests/transform_groupby_test.js index 049a2856b5f..46ec7a3c1fb 100644 --- a/test/jasmine/tests/transform_groupby_test.js +++ b/test/jasmine/tests/transform_groupby_test.js @@ -3,8 +3,10 @@ var Lib = require('@src/lib'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); -var assertDims = require('../assets/assert_dims'); -var assertStyle = require('../assets/assert_style'); +var customAssertions = require('../assets/custom_assertions'); + +var assertDims = customAssertions.assertDims; +var assertStyle = customAssertions.assertStyle; describe('groupby', function() { diff --git a/test/jasmine/tests/transform_multi_test.js b/test/jasmine/tests/transform_multi_test.js index f10541b8e80..681c39f3737 100644 --- a/test/jasmine/tests/transform_multi_test.js +++ b/test/jasmine/tests/transform_multi_test.js @@ -6,9 +6,10 @@ var Lib = require('@src/lib'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); -var assertDims = require('../assets/assert_dims'); -var assertStyle = require('../assets/assert_style'); +var customAssertions = require('../assets/custom_assertions'); +var assertDims = customAssertions.assertDims; +var assertStyle = customAssertions.assertStyle; describe('general transforms:', function() { 'use strict'; From 7228d5d4fc8556c942bb4524e0ea837120a63da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Fri, 7 Jul 2017 12:02:44 -0400 Subject: [PATCH 06/20] add cliponaxis false mock --- test/image/baselines/cliponaxis_false.png | Bin 0 -> 37319 bytes test/image/mocks/cliponaxis_false.json | 54 ++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 test/image/baselines/cliponaxis_false.png create mode 100644 test/image/mocks/cliponaxis_false.json diff --git a/test/image/baselines/cliponaxis_false.png b/test/image/baselines/cliponaxis_false.png new file mode 100644 index 0000000000000000000000000000000000000000..aaa7ecb5d2df1b81253b089caf1945dc39753948 GIT binary patch literal 37319 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 literal 0 HcmV?d00001 diff --git a/test/image/mocks/cliponaxis_false.json b/test/image/mocks/cliponaxis_false.json new file mode 100644 index 00000000000..1053fc82087 --- /dev/null +++ b/test/image/mocks/cliponaxis_false.json @@ -0,0 +1,54 @@ +{ + "data": [ + { + "x": [1, 2, 3, 4], + "y": [0, 2, 3, 5], + "fill": "tozeroy", + "cliponaxis": false + }, + { + "x": [1, 2, 3, 4 ], + "y": [3, 5, 1, 7], + "fill": "tonexty", + "cliponaxis": false + }, + { + "mode": "markers+text", + "x": [1, 1, 2, 3, 3, 2], + "y": [2, 1, 1, 1, 2, 2], + "marker": { + "size": [50, 20, 40, 25, 15, 40], + "opacity": 1 + }, + "text": "not
clipped", + "textposition": ["left", "left", "bottom", "right", "right", "top"], + "error_y": { + "array": [0.1, 0.2, 0.3, 0.1, 0.3, 0.2], + "arrayminus": [0.1, 0.2, 0.1, 0.1, 0.1, 0.2] + }, + "error_x": { + "array": [0.1, 0.2, 0.3, 0.1, 0.1, 0.2], + "arrayminus": [0.1, 0.2, 0.1, 0.1, 0.1, 0.2] + }, + "cliponaxis": false + + } + ], + "layout": { + "xaxis": { + "range": [1, 3], + "showline": true, + "linewidth": 2, + "mirror": "all" + }, + "yaxis": { + "range": [1, 2], + "showline": true, + "linewidth": 2, + "mirror": "all" + }, + "showlegend": false, + "dragmode": "pan", + "hovermode": "closest" + } +} From fe1f8893a9b4384be2de2078df0e8d2d2e1a316b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Fri, 7 Jul 2017 12:03:08 -0400 Subject: [PATCH 07/20] showcase new cliponaxis logic in ternary_markers --- test/image/baselines/ternary_markers.png | Bin 44884 -> 47485 bytes test/image/mocks/ternary_markers.json | 22 ++++++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/test/image/baselines/ternary_markers.png b/test/image/baselines/ternary_markers.png index f131b3266f58e6ac6c0476c1bc5704f99f7bc7bc..9288c20458b4bf4defacaa248477c562e946912a 100644 GIT binary patch literal 47485 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 VxE{_47bAc_87W1{s#ivV{{vIl-J}2j literal 44884 zcmeFZWn7ir)-|q3OGtN!(hXA5QX(y}kuK@(PU#YqZV*MfLmJ$K2uOE>Al)7Rb$Rad zob&#F@8|c^^KIYWn{};gt-0nLbBwWexU!-QCORqly?ggCWnW6ZzIP9y>E6Bj9jM6A zzj%_`-MdG9PgYV~&0TMM&P|7)dm3dw%OC%t0i4ZsyV)p~_H$~o`Ll}eq>UAk&BlAe zLj^;Uczt8v=Lra;;PXiM1k6VHSH7m+k7;&33Wo}N`Cbh+hc|K$dYkR7iwvY>(9g}3 zx7965-A7mUN5Id%hr}RuKN|g$aYxZm%33-2kAMIBr+avMEvWx|1Nz5QLW7Q?KY!L1 z|LaT1`-c?&8sqPu{i+6kq@cu(BJ)4PFtoi(+WY&Ie+}&a87X<+($iCb|KAS+!_hT) zqx{#X&sf{u!A3^<+ExB%is&3mYl8oo97DDR_@mTJJ*=32zXTW#jeX%i&lS6`@Q`C^ z?D*Kx{=Z&A-u(jWKQE4MuH}zb_W4UvP~Ly0XwcE;_Mhh}i;W=2yYmP)laoVB_0Oci z(meRDQU7;o{qH)^05`0oQ%&GLwZBv?t?t=mjtGsmv3R*RCUTN)gpHU@Bbr4J4a z(Urwm5zbyv=K3hHS6dt~`dS|)XmxN|kjv6GvFkTWe`$F#RG_EeY;hGme{)y0MQy3U z7;F7T_ry058#=|xMIhhQ3}CF}-a|UZHR!mq4L*A_`LKHT`J)p{b@;EAj1w;J8#;2< z=g07L*-quAutvSXLsRGwTSD3F_mrVEezyMrIN^6Q!~4&kVWe=smApK5eSQ^Ac1Cdu zU!CeJH`qj_Yx3+p0DmU71HL&XA`umffd5ODI{)3-ast`8(}-)>oi`pORvr12N|^?M z_n8YfzrgdZI;sijnMD0;-6D3}lbMn`;)}h+pWbI#%bUW^H0VS>W9M~Sx_^lB8BYY{ zxu0xec47PpXCipDk~BtUzrP{NlFo};G+`GUyJzt(L|Epu_>FRdH>0!p4S`NNH|@sO zkCS`WGft>q2K72V{4%p2NSCF{HY$={3cOg}Q?QhiaJ=&LQOf8HDQU#97bG^K<%^8u zenrS0#V%s&Uzs0aeEji(-}R7XzQrevI*~KTtE0+OD&x05ACz?e&xL)-wj}KQ6C2Nr zP3pF(VYOvko`id`t7K-f;SLp@peOVVUT9nUyx!?s^Nuv0$cNT8){k+=JTuizwEIOb zP;CV&d*8X;+_`3pwaoUU3DT2J6a}-C4%B<^5NednAk?K#l{^0?{v?;g=G#<7Sn^{3 z=8+l`3|*k|ZG!CHZyPThO3HwQhjg7|v{g;8iFYvKsH_&^guW!{5=#ODW8Yp)#eFV^`Gac9!`yhwx}V${d^*h6$B*{9tg@xG(&z6l!gf4 z5Hw(I4zakM!%{EsB{XWD2erScAtB}SyE6*zjZKcxWYsA@?YW0j>_nCYzY^( z&UWnI(rT!q#H5DY&dub#$$QR-C3HOHRJ}b)MWMyleYW8J8P>rZKWXEIWt>?Di|}$v z*(W{SJzxa!eFMd}s7%y*5Z~g|U*EYM*ln%0jMd0}c`Q@Q60VifcjMDHRsYj*t( zp9retD5zQD5#i}EUr}L(%H3xM>B~y$Lx0X|u|8<$9lqdUY>we5kdD53c1;=7)i*W} z&Ai@|bp}_uTYge!*J>S6y3?Xy-#V$d|8_>NCyPF*7gx{0WSVHlhpd#IiE*WL}gt1^qOH&l8uy|tFFj#qi_mQI*esSi}TAW>5bbJw|H7QI^_PDQ}GHKBu=O6MMO2h zvx}Y*WTUOl(Di+$hK?)sMv2w;r8F21faH zGyWBhJOUuU?79&?p&#sAoTnJ7_g!wKHb0qLss|Ni2Lf@ozXYeI5uBPD!8&phq~cf9 z`LC>&(`Re^?joI%N|)D)Ccq+)ZH^a5jkAg$Sx%wZW7<{ zw%b>y5yu=%kcQ^9hKb;!(+k4%N5S;pU8BbJ{b*nwMKHE6Zs4%n^4&BN1-R`lUqmKV zI;QdSX%^c?=Hl(?ue0fO#wTpy;Cn0WKBkka;lDHO*ODMu;; z-vvvBZY~4*0oUL0Pjc=du_%i@ZG0f3B+-STSfE+^^!l288Pr_oy-D<}XN6V)PmFq? z7GRVfG%G$%9bq6?nQ_m(`q<7hpND=mMyA&N=Ipqd8#r4GD$*Ez^sfe+hs?u^%FuI? zrNMJ7bf%b~o+1PB^UoV~jZmw*xg@k*QMUuqCc2-}X?7w~X@psuql9(<-7ts+d;*Qr zVL#Muy$?ai6*_Fs^VQ^Vk-SE?)8VIA%<5*?yGZ8xCi)|ZEiOc*(hAy(M9@f0RIbMk zsrn-jvh6RgF<0;X3Epe53`y)4ReY8TZ!PJBGxKo_desNVep~|I!hb0U%A$ErY(8 z8_oJ_rQ@w}LMlHs!Iq2np|JBt7l=7ieheL@Olv89zS zTIR(reYqq?U22DSYA`nPDI5g7CPQb)uEoqWZoUc)Q)%I$LBL3q=7V=Z_vZM-|FpLF?86^DFgGe%7j?8O(V`HbjB?|$iv z?Q&n;_cN?0wR^%C4yC)GJiUjHO}%<6U=6L_CRFuX1Pmp?!$=S%jkGxYDBpC{b!Q)h z_$DR4^92ha@u?qCln1_U{6tT3WvlAP0$6G+-5XK4--=M=hL2maa+S z(mBxSw|p;M({gpN)QAi%s^m7BEkFXMio`T`K6f~(<&Nzp_1{d!*$w6aS^yLCgvv)+ z6x1zf;et74aGk+S>>cGDcp^){t4KMlj z&QRgez2lu}-gXpoqcjP4CGF5{Bl}9XBseX`U#l^!1c>PRy?=6LyuzD; zA4|OdK`7CBCR0N5XhQVdW!Jrat%p$5vka|DCGVAo$LHOvq?d)UR~hh6I5#X}5s#tV zTdo5lB2mySDjl3xG3thQuxG4_+R1l)q+tm7B7U6-XK#Y_Ip>l&c@a`pEw*sDE9o`{W-QJ?) zs+AXfPaLKlxMqFN&ewAHn;TEM^D~r|G|NDR*~;*W0n_KmX$z83T4ls)9UzJI_LzOW z&3HclzQqH2^68h&s{l_rS)>u2^hDS#yFn2ugx?xZX{D(#RfyS|< zJ`m3e<+S$*52O{OK3ry+PybS7kIT67F5mJ8T?1M1gi})rh;L96LFd`W_YR8nK!lky z@~)OAEy=v5+2SQwq}&vkRxp_=X%`I7Ozgk)S^3V zt#T2l`a%3PhTup}PEC=HB2)dVQoIAxzmNv+jIaFC06wjN>T^nd<5kU92v2zkqlqly zhWc!koqf=MgYUS50+wWl0PPYI;_fV__{44A+K*!XjP_)?^l@s9h@=7X$ZHQLk`eqY zuy){U|EB90o^XJGtS9P;`&eBGpXO-y_lL00p zx%mChk>>TxbCxVY@wn4v(m#`Ch`yV@(8rh()T?!Q|8fJ<;QVXNL^(Bq^;t$}4w09P z93;yV-?5CSZNBS4v!47$cJu`U_J8|&F2dbR3${_P+wy)(L@n9GeHQwDmO0|TjNR*$<8CcH_U^p1Xmva1kNp3`4hesJZPWopDgks z$E4E1^fI{3xW4ur5wWkmIy`m=Qm$6M|KSwg^>=U;FgT%Ef-%@Rc{Yw8ULv4=KNi*C z?xq#d6ZAS@>cZT48?!r!0zQ$D$!sFW`abRlGk`H@(r6jly|{9 z;yDjfT8+Nz-^CmTm_h3jgUc7Z`Zj{DFJ*L$P0Ne^LV};uXB8iAHCtTq68lN5#(k$q zpa;kifhOb+ch5PPS`~WnLem3SDT={p_O9s;Uul)Y2w{7k=3@vhrydU~{ymAg;3UeW z^K{AINB;V-{=g}AfC!X>kolVt=W&92I$Jv zC5Wc%dcjd7+}Uz7cEe1FQ+t)_ek)^J6B}o$jnATkGN3Y3jt`|OF4d?H0Gp@=$ieK4 zwyT33-;TA9jV3}(AQCGm4BT9*V|Q300- zGDOXbVS+#!<9HYq0FF&Q)bSs#lrQ{NlFZZ*P$yYwejmE=%c}l5nGH6uT^JQ74yB5h zR$xvR!Vk8yz~7X#0ovd#?o?^QH<%I(uq<)u*t>HoDt(|Qd|azg&u=E(`Fu>J-atfZ z#>^O6gP%}-)EV9m1#8X_03nUv9SM$W&j5-ojJW|-z>)TwU+gJB!k=g=*mQ2`0Upkl zeV~Lm*ANN@-IvY#cK%g@`IC~}mzPx5f74q7*oRcP)Elq_gtYrdVT|XWR4AU6>i5ws z&-*Mg)b0pGy}=71rr0~t7Jt-T9$rEY=(mqM;eLF8C|Pyw3pmhgUfNhRa2B-&@0acW z-{U>m-`fr1-&lc=wx;|~nqQ!o&(1U>@Sd*ERB_mw^g?XJB8UGx2*RsAI+tZYj!t2# z^N=T>C*tMhl|JbVg`ziE4jjIQPgB^S{@@_Q#i^K2I1x)9?}c#DM!u2!=^(BL&>o&C zTYDkW2SdB8`NhdH9qhKYp%D|oH&yjd&DR0&+z@aVYOx-FBQ zFBj*Z8~>?3XT_Fw>H60+HNiB)1Ev6cGY~*nUMZ#w>h4SE2RtY%{r(w^&*5>3PL3YG z%Tx5dG*HUK zWJ%9ysoU=^tB0{ZjQkuOuT8?H-xv&lT5gLO&l`(@@o|k>dd1*JXnX#d6!?M!G-F~O z51<6|1VjXBK(`E7{TtUrFbRXD9bDZhk^yX^9zXSCdr@)T*jbQLoCd6ZZJk59>4 z8-F)Bsd;>o8Wj(Z%$DiiTXkA?4jGz%_@1@s;q$s1X~IR#t25DhS8i0OD%`}`q$bNl zT`LEq>F;GHdE|vzPXDaNcdrZ2yt*==cJZGwN%&vuH zl+KMI#_OWfVInfAZ2wAMy8?8%piD%QCiLYkDD5mRr_17LkOO>^?E{Y6be3@pXcAsZ z8H}`g+5f&KxCls0ELE95QmH6LX}PRciSN_`{dDzt6PlyxAnD!uV256|`uhFJ3SZgy zqB=wzdLl=c3jEt6UqUnH8ikww;z`X^C^x5ZcF6(gz(oCO zBZAp>urN2eD4SEFYob`4&svKIrlIO}av6PXudt0|JF=`w5Eb?qN?5=2!RpmIHQ5b%?MgX7(7Gp8*i~x$TXMa!Rwi_arE<_zOWO;K!AD<7p z89?rXP#iC>sc9cp_Y+Tl#$na*9$yLX(fX+CZ4Q#98lxRSQ}@lla?%3dhVy{^QEjtI zrMn?rqsD=${d@d!EU?kzoP*@FNGz`n2I9xU!lR|`c6U}*h!WZ*FLcVX8keQpY{C-y zPFe^2Baa#hbRBZV`V?Uh2JY`5-t%ny@@Y`t=BWjPFZ0-5wy_kwW-!1yc+#BB9 zZxlQ+FVf6&U|$U0D+Cf--5mvN)2AqWg95tP~Kj`TVGt?-2u9S1tcYa z_UwcCSmj{3Y=Lewt`_edKTi7!eGZfrpWpsMSbIfS2T?uazj|6_tUYLA|)*h5TWiMPgn@;Na;oi8S_R0MH!z;>zas*`WeSvVu(i zo>=&83roOEiBF&83qbt8t8${lXWoth?AlV|lQaSb1Z+y-fb)lvJ)JV{x7W13zIJ1? zO!pwlt*sCOHYdkU`}oRr{x65S6m`RLgCAzUAi>@Sso!u=-(a z%vX2$ZaG_-$T|cHW=rtMLRU*-Vk8C*gHg4Q%<3;Pe>`%#Iqfzj7yc6qXs{l{^SzSB z_}O9QA}zS2mpV#pO2B1J(eejrm$yzvaBgi47(UZ`*vRDlx*&%?o2=f`=X3RNjrR{^ z6pc2M@267o{T0rs6(J=%>>c^izr6rRAUo6`=+|ro;XLqtv38ihY}^FYlgrrjKpQ`{ zuARlJjcGsmc&d_cpH?Gv{@v-CI|kQn!N8Q2yO~>RjmX%n=dmF*MKnI1Hon;eBm}{8 zC^5YW+x6I2?_}@dR1X|Fr_G166D`-X0w5%z{0f}@*YVZ>3MlJ{S(~|EY}Q=ARIpZY zKYoh8yowAM6~GW`pVBi0g!>WFvxVL19L{*oHV_5Ua!dF55yGskS*Fj9U))jds8m?@ zB0A`O^HjZc0ub%d!Qy=y2}+vHHw@O~6icgmkFacQBVzl~F(XPamLs(1qPMPoUi2k5 z*wbpTCFAdz4Af5&L50H!7yx*_@Lv5D`X2S}M%1KH54^|pI=~%B;mR=`J3O!GhgBjZ z*GvAdp7A7OCJ$O0J9k6k#iGedfE#D3r(89Ovt{;g_*7F(G@ucY5&1{gc<( zac>%4dRL7XJ($=WJC4xBys}lKr=}UpDd1&u$uOTrNeqn+YT`8|?F|c5)D>i^X+xf$5Z$ z>WF_23O#I%Oqg%l@e8}-=80mhA^yH;`v+IbWGLoSDPYRhew=~Ec=apc-z;vf4)#LB zED@48Kec^qXlyglbQUAUW$Xgs4bcuCbqoJ2dx4KT8p4+J*(=%YaMul#ax zbV@E6$UF^7JZudLdz*oQKAz%FE8SqXSaI9GcYYBLT5sF0dEVO`D~vBVo7}gb)osu! zJvA+X!E7CruV^*giqsfNMg%&I66%yDsBpc>3r-I}76)bJ5@ModBF{iMAf)|eSI;M6 z^%^^RbL^wTf^H3|9fIAHb)u{lcth1;tC`(2zpr1ZCXS24z&o1V3%qD<5ph>%$))y9 zk%2AKMT(Rrzw;a|#}9Sui=5X$88%fSw^TXmg9vZ*4JH8RVT|*@g`)Y<|K(x~xhT?} z%FW7(ugecJ3gMCL-mwz z7G8m7*8>QfqDt?e?SXcT{75j;lxi#>TH$%~V>n4S2^W7Z=(hexK>shU{+JLI6Z z@Wa}xw}h~D#KEn~4ra=FpEHr&Ilp&6T79Di6bLu?59ffUh7(sj>~|twY_P6%MaqyD zNJk|9un*bNpbA)~6Ld)ed6k93cADnG$+zz}6}{z?A6yMU){#-5Dt{Val#VI+Ktw3C>Hj&h_dSM)_Y+=!VQ1)4|Dutpf zdA_N+XkPPrp3Wa!^8*iKiL^y0sIyah0*(WZYP%Sif&@}WW~AbGyy8T+<9)G&nG~E>o%mvl@rvmOD6$S-T_7BO;4eKV}07z-EDv7i7;6 zi+v}WH-0WVOdy^2sGH&h@xSi>Y9M|tge`MGg9NE#K{HC5;wnwKcVUc&E22c}KO<`U zyP#D_)&^&$0X{_qmb$*}T7R5+Q;|cYE?TkDrou6;Mo&297oAJ^r}Lo!-7u_&a>v~? zD!y93D0ZXC9((b^;KoW{#pA3)i?8J98hUkyVz3w{H?>o)nS5CFDzJt)hc?D8yzA6u(> z%B6%04?V)#X3l8Ql1jNrun@UZ>6chn21ZrF9{>kv3Gy~ zmom?^_+*Q3=`gtxP0#0;&BV}p!8;SG6!9RnCXAYBgO5{4kgRj^V{QL!RS4bA%-ONP z>Ojf^q(c(dt`d><@OQlSy9g4Vom$=6E$FtAvWI5EqC(!FOAb*?zAvoHmhNkr3Wf&2kpOOUZiu#5Y67(hKUP#(R5U2}kYky?XoBVV$ zm&!#^frG;^`2`k#6$9tRI#8MV620|Gx1{en@%SiX&ZP^T@Iz|ZJn7Om@9rx}#C zKLPo9{#l+`HyEE20RXoU?Ep89COos-ctbF+etU~;gUt;ZecdNs<705bkJb82*M=n~zxs$TXy?m>0FYz+6mZJYM zJ~ZG=uyVKagMiFHq7+7xYNV8?aQ>~iI+!N6zW%ZPKtq}1MV7D(09zl;*;L4uQ7c2W zYv~%@&KBlES5Zu)W{>FIPye)cIt* z<96v_M*arWqp;?m4?LZfy=Sfe%2A8}hNiwwvH{p5;$eEDJLbGoVsC=9px}tFP@>NM zV{FP5Pch|q+NfxXeLro|+10NP_wA{v7BOw*d6A>iBj_-NhN^SE^j;Z(;qM`Rd;}DlcQiBLqdXh+Kqi5DEArW& zcZd0M$xWiV&mqSV@C2xIDSfqSoDUFEJi?-4>*}!!tD;bg9}PY(EKWJ{JU{=c<8b%(lz+8v#dM%_>Mw?F29dL=&;>bd zogT`k)?n-+9cC*;D)rCRDBfJ0XG-S;Uc9G=DQ4_WoP74erd;~j&{y!Glc20rPfD`q zfn;cKdsq8?#a*Ph3Q4puR%{EG641>d*ewK24C(^@_7o5e%3}0jyPtC&0AdLcypnHb z{t=`#j{R)If_;XG=6bP*rI2cZ!8I4pci$!){= z^Fhj=N2jj~;BNgo4V2VMtB+vOOlvA7uiyNO>-2X4JbF#(#cy*)1C_s$LCco`!{IPr zAd{^X@$q}lVq;puLi2)}RSrUAB#riRRtofsn7B-xxe8gGgtmdFs}RN(%AUrf_Nh^# z`?RQ#UzCRqb2`yG^W>Nq=}`w#iSw>5@}W*vP()!=ei~BsZ&|$ z_*YceRosQKGkrXHFXKw$D2$2O$=O0Ezovg4366K|bLLzer8+w+V)u#At0~rmB z2Hv77!B+8&StIOdz5i{`ip8n?sDj6Wl25aq`~d0-aQ ztr%Jo&H6c-BH7gVEmQ^8pp9x0F#eAJtEnyd>%+Gvm3A%WMsdc#E`$Xr^HD(yg5E2bl-ew{Amexw{M4cy%9m;*y3`5ta6QpdHR1vC;0B zsk5uF88mF*O79Q!E8QFS!UQkaE6}&heG${45ZMxd!X>^tqq= z=TQ_pi=BgAb{6e&li%hUFQxzs#%nVZdG$D!W4_4EdzkF>8VNZnl}oVJN0bL6pn^12fLw#btYZcinPDl1G3iR z+PYbQnSdWHp?9BC)UQ~x@r|vUvUbWJHIzOwkTjnGheMj_@pdv$8}-G=g~v*}Ieun7 z8!d><9R0);A=Bi3flknuD9_1qkdw+EDE~E=z`|yKH&4Q@Sc_kv{fG5yhhQ1WZ1peq zC!0Jed4UJ2Z^AlC$x|vfjoHjK>?oPfZU)66P@O^nJ) zU)oBI@x~q3THkW=TF=p*IY#=ivsDbfnGhaI?N~JcX*3UHR8oZ=Wbn}#38KIO^Sc_c z7)r%JGUKWx>c_r{gJ6r&S*0~hH4~wbBQXjfqco*77aOEK3)%Q*`U(Pzf ze7cSMmbjASq2GtGvG0^~d#xApL-mt@Ycj|H5VFK+UceH);FZDT`BZ^=34(~7mcF_J zBSDmAdw-lT^X$>O>@SnW46l0kwUFN$ZY#(QF6;Nng_mM`pQy57HXjb&0l7?A%kKs) zCgV8tEL|Tc{HZKu*SCK#U-hFvb-GvyEY?ZCfQUzf86;7YS>D5U z2vB#Nkx^p({5c&mWc5o}I8&lluGHtL9y6PK>OEEb7?840rtGmOjymI3zO02>(M(Zr zF4C)&XiD4K@%#NDmEQ6wEhFvfCkD#}rC{|5Zw^v)%?zXN+y)%utVA;1Jx`wGl`pI#THgBgE_HS*T`u59hGFPErrRbDWY88g!Db{(hh45JP)JLHH&q=8{KZ_u$D0MQa z+vN`{NM^wJGuQjE$vO=H6EUclxiA71jS(F<>`@nd#xQbCieK7R)j#h`nE##S{WNQ) zcK!Wz{YKK%3U2ii5sgCK=6E5clg_@o4g$y(WuxsIuq-uOVu@V3fF78UL4i(C9y2yq z-`niU{ix{nnyTs2Et;LmS{-k&9l#$8(7o{~Tuccdjk4|^j#+t+mBto(^OjGj)p5^7Jj@=7qzsbNo@0;J4r&I!r4`sGRm(w=j`5*$v~1|M0~g+$(sZPoRBcLV6*9QbiADzsjsl}+w3;{yBb z4lr0R;ONVfaX&JuIU3vI)2LV`!odj_4vooE?EewJQcxxeY{uv)C|A|VT2-fv5mxx@ zn=wYg=mx)%|CnnwK2PN-G#)3O1ls(6`C~pYP{&sfP9L{HHZoDWQs4#W4e@iO{N7>l zz@LjbC#Ot7m@xT-vvQPVp@R>{1uOr>z?sa8uO4%bl7T{V=Jlb9{i1 zHnO?sIh}^5ajvoQXA@3^*sFsPvwdL~wwC&?hRBK4H;u~=YU{bfc`)0c3t5E_#*VNs zc!aN9=Fd-G2ValIQxn$)DwmK4X$a1PZ;v+ygq?Rp>3UD*6b%RPYwj!wwyR;V4t z-fd4*Qh8r^v`|aD8C6=E@N!!qCi7TkB&5c`%!*z+GCVp?^a#cVd$k)mz6q#A=8p$r z^Roggq5Qz}UH@D#Z{i;Mr_005qA>9)(EUuR+@uvB!_pDDDJ4yVdZAx9ep+ z1mD6tufE#><-+!}&<9w0GxM!euicizx*lrcPhtu`jXj7p=QW7Znd*yUwF)%MdW7fy?Eumzt35y&Q$bL)Y3}wTIzSl+V}hqRKgxiu*;;M z#6bw88BR6*NGm!2Z56~q$OS@jNh73w9Im-afYJ^wSisNe;XruZf5#w?Lv}?-@WZob zmCx+-4tM51J?aNZ;Ru3Xs_SgM*ptf|VlJqCG`2X*lgnxmN@NoXTzVCU+8p|sh|Jl9 zoaJhP_{@`nr;q!a{bj++6NyY#{tFeJDcMb3RK0`Lr{fbak{Je9Qfpe44 zqa{ihYm|1Bc=2uo!R+cOgQzDi6HFYAVy*S(j@QKD7L?^5T#vw zPW9XsJ$iHeU690K{s-*y^zRGWu7OZ^KVaQ>y$YTc5lC#At9_PZ#(wFc?k-M>iyd5xp6n#AS~=4o{c{4!C1G@7U`((9_~(DYE*fYN9Rfhtw7=f9Q+ZsTlcdQQ z1w3_dHkP%B=y3R{kIX8%%{U7ia_|kfk7dTDBmQ8p@IXos!?Ncm_8&)#HAYQQsS z@A%BtuXwl}X}%#+CRReBc2KjJe7z?VY4;*wiY9mTTuQy(C)5YH0@O`@YlQy1G z1)J5N+q*buRQ@@y1#G+j-k&3`DJ45Ivv?5<3$2y9?nnGibWC$#q0^c;WsDjd_Wc&*EA2dVuGGbs#UGKKnXNX4 z68`N4*vHBYZkEhHv{1dj1EkuX<#>`{g2Ii@TdU39C&(zVnUcWRH?nG84>bN4*HWDy z=>s+_+poZC^?{6H<>Ay7#`yj>0`NM7NysP}{@A#+AdJ+s@V+?Y0sIh$HU>G(py$CMfn>uPa^~3LqJdp4v1BD<47FU}GL=NvfaIK3!hwbH+Mp*d z*;7HFQV)Z;vP&(mQbpG+^?!LeeM0^@f=Lr4CR6gyvef%d`FuBaofa%@LBWqK8Vm~b zfK9z8DI=9<{nf}cyi+az!wIHHY3c&7-1Vn!Z;{dO0c!d`cWkmP1N1fQ{0qG4PE|jj zxbUq$MhFUAdea==D}e@F&N51uD=Q&2;QEH~;qB{9YC1Z?D<8#d#|D7&EmH%D_Rj7~ zohwm5=1lp#LYNWhvy#FE(p@dkFM%#bV^E9>K}lp(`bw+*g@*2!KC)f~*Y?XCFs7^{`(XiUgpd}9X~Sf}3c ziGqll9Mj^L9K%DRkc;`wCA~HwnGO=f?ohBD>QL~GIdc%B7ib&IR!cFc{G=#J_V6Y9bCL1Zs4) zc<8pm@eU-WL>i@`Mi;rhypm=~6V_mT4DWudL2YAWtqeR# zAHLK14ihndovJeBl)Vb!y*#&I7|{O~gIrTYGA$V~sN3TDkh7wKVMS&1=?>Y5d6IA5 z(4WvU=xTv+>QWVWOaf$`iVy$I!Dvtn`Rs(6U?`P zmGRuTlNa2EejWaM5*eT%2LSv@vkb6e5niEMU%Fu{W;I3~bb2xmrOQs&6X0W1v{pp~ z=DX>)q9uh_Yl;)d=999k!WlGb;1M3+cJwH}HOWYi?9r-6kG{|J%NkBD4uDB0AP)p5 zc2pojA;F{dmIx*oAXmjII)0SWFbSJiY|HtQ#)+1<5R<4RVDuRo6Lb z#Al0~+$J@xKT|$xPqjJ<-)lU*>e>CC7;c#$e`T0Gy!Qao&v1dEZA}|w_D>AuixZ;n z*>s;DZ=7dr=2HrJ1{U!>P=N*55E=Ou^QSy!{joucz^ai8Z)ke8%}rR;2;FurO`pO* zgBK670Piihv>geqFfkAB~Q%)hChS?gz^R-inufK6O~9 zAwJe`OSx?mqm z*Vf)qIMCxRp>Y$fT=rA{JfbX3d4#;rd+<^^LTyPWb;6Nsm>uXhY-`S`Di|z@LZw9ws*{>UR9q}B53&@!& zQfE0wxIOD8N$K``IlX+c>%*79ed9eGV$N(bQIiq56h2kz9gr42-;i@40OR^%Z1jvh z_hQVosH{r)85GvKqt!a5}sI1USYxqT!M@O+Hvu`1o% znQo+T-`iJ&QtjTKS%<7&jiPZUX1~n2HU}2T=PoP2{O6Qd+SPM^ZUyuL8jgOHORS=! z>EL!^IyoMt3?Fp)#@?|qESBHp#i9a!IW9QS8eBuYE?O`7j{7Jyn;<05fO7(lBSbWaQJFcT_}Fn zRb6M58Q6X^l;h&0YwiI&;n~o%m{L$f#P8Rj{?w_)EgeXKWZN;^_;v>&-$M+dQXLte z!O=4p5$|I}`WtpF?_r`)m;Gcap@{lUR#*n)&MCNupkIGjde;6 zvs=72q38S#%4IPJXW=XT-_h;BuTE4B&jOLdfqgY+c3L$j3MJ;^!Wcf?@7Ngh(i38| z6M>Oq!tr-R-0QYggnwL&tLQ1`qCTuTW&`@-pQ9AN681lm)z3f7f3F>iIE1V7-8Cky zZa3RC`W0h{7r2m8I*&RkpG!qR!0MN?zd3>QR<2o4i{+~oaG@UR%u%)6=-eJ;26eAe z8dMxIpW0z>4Bc;-b6oVBIWs(}^?5|kAC^mZ<7=6=*=T~chhqH6zXm#FwIu8AjPC>6 zvWG^7$`xInS*gizE0*cCwu3vWhsj(AcrLGD|HgFaiL@FIvKIhy`Mntkj6-xJ18MvA z+k)N*m_kJ~63FyC?}}ey{Jj^|*}|ClnOehrQ1@F1U~_)Np7>te*j-<%3f6z)U#S!2 z?bAmlR3IbpLrl@UA-MY28Ue65!L=T(q6*qf==zmYE4FD2Rk8bw#oS^|n)st$-HTyb z++Z3FL^le4CfEyIuGXv9`phtZaQ9CH=|9|n9|0KDYeJAgk@?7Asnd?k<9R|QKmhu| zTnmZ8r=4EF0|}VD&e5RO%^m=bSJJ98aizg|hi<+L zAzv6bW>0p5oBT{LREczKX!S=vzpeGntIsDOMR?(2YE6XEllp znjD};SJ(#_j0N&X~6P@VRH5zoh7^dt=lBvUE8GT;H2&avC1o$#$@DmAd zw@#>(+<-V?hm>QSR;JSQQaoM2j|5yCB&wpr2w(V} zZNUJ397=;4gabNVio|H5|D@NX)4n@!s1O^69!F5kCT!h@F1v@7?Q)yizW{gkHqLKg zW$G|==-gLvb336CHg=QaBSJHb!UC^vf2 zI$!2URT`fiwT5~0jYD^gQul}6J_5-giON2F8N&!ZqBWrE18!-v>9-=)ZNCAuOtd2- z`BZ*n9_`#n%NHU~w+l-3XRhd>wEg#u^To6PDpA-XF}TD3r6N}RB08h?5nBrP6T98? zdiN7)?~X=bQrn*13j%WJ_c}Tab{Q}BwgqNWASe06Hy;BcCUcA_*D}BIxwPR$U3doq zur|kW=HVUITe#g>zUHQ_EekWA20s#mA}FZcmnBajV}E$;K`!V*cyw_;HO8nZ8etl= zB}L5wYhllrs6}!cT)CD1Kd#<6tkO4Z8=abJ;^dl)$+k7s%65}&+mmfiwr$%p8Ix_> zwV!_9_r7}{`|sA#de;5iIIk1eor%+Gef?Fl0~smnB5ZC2=r%K~T;5F0!~~nwaOmr{ zrVS8ya>VGD*QbrV!IO`{7`F5O>@+Dmz==0I(9t3yuyAA9o6(6sMV#Y6@MRb^oOFUYicrhj7R_)Csx!~aZEz- z2BNi(BBUHBLc%k+psZ=Zw+!mV>yq>bA9{vkX~S{vf?>`l?ds%8g=0xj*ND8h0P8a> zOtUb|9KbEP6?eV|mf1HbdKTy19{+FoZ$1_sZw&>N=X@LXlb{hdizY*}l4PaUqz^1B z9_Y!?N5%IOwTM0>(dyD}Yo(xT?Dbr0*~$gzW(NcZuFGDhb|Bpwclkgp+#^YLx?Bm>et%Zf9dQa8%wMN+WgqdnwS^3 zD9hwKxDLr?d;|ivqkp3ioEnWCg$Y8|d(o(OpSCqS-WtDzqPh5;Z?E}_7HhYpk2@#1 zZl_&9tLry+nkc*3iyzeOUnNI!#s^cr3^YEd+y)jK?LUA@_~2;<$0$L*A(iSTweR(NSusN}wYYrrB( zuw`1h;Xwq`=n~S4+F3-Ot#?{lJ!88GbBRe@6L-3m&YAg?vaY@}5if}^UNk2O@Hjej z{fn)33p(%1l<_{7m>@s}{B`?L4rR9NiLsJgY$gRIS7~n#t@TZvB5ntMyTb#ezOoVv%b9yNaFH&V@n?`2tVC7_V+va+-)g;_!`u} zSQSml&iAwqcTmJDbqP4Ve`4f+_+Cf0bw6xhT)?xG8sL9}1YB7c|Hq@j1$Z=4Ep-6n z&3Bi|lgk1ZY5W8%F4x;9)~^o_AIzP7mV-j_zwM`;L*~EeWB{fC9P%5PfADuda`Rs- zf%aY^gYZ~?7;yFux9(P-ksw7BN^{BY29qx-f<8PVnc4r~fKC~OKj>%>%Aa zVEdfe$KcS5gl0F9Wm$Bdyw*^I%paifw(GMQ_M%JKphyw*kog1o-gEdgsqg9w!p57w zD2Er_?exeN+ULv7+<$Mmfz)h(oA{hJ-Tcr{-mD5Rx2m=1G6ANG<>hk7&rty9DF%c| zlWia&%LN1y5HaTF`!RxNE1Rd=wjzE?b^%VqL~_mi`e{sd5j&~HIUL_&jk?+bM2$gu z6&Dbt7SKXW0WVq&jQ&)fO<+{#x}uy^~CeqxHWC zb|}d8A^+g$4+B<)2Rf;#m&`++cLfy}sT3ExiU}RHeu-v{@&9NQ ztUTpLYQR8*vXIbt>iKUw3GaE+<0zeG@ozXl%T<1`*5kCIB4B_i6dVXHMY^r^-Oh73 zo}fvfIV~KL+jZcuUw#Jcz<~LuShEIgfc2)OKg?B0iDfjY_Rn$zMa21l;7dTXv+S?iZZQQR zrRxB9S>t#`DIo1Yovwx&l)Uz9mqW_)KF1&f3qSuW~gJyNPS)1bAV7C8$!01 zm+Bv8V}?F>+w2eHCRG+;kSLfC1U{d47;xBq$RNX6?8!vZ!zRLqtH8N2OpI}kSw234y)*F*`vmV?E&b>*(vpobXLUJ#2K>ru!iQQP&a?bP3Itk};c!g5V2$zFXcp!tNak8FuP7qP ziZXNy_D!Hw3;e+pQG>?gJ}{R9KMI*@7=vasF=>JFE9s2qGR>0Oh6d@=JEeCXCMpS) z?NV*=SKwD+tpXA~VY0@Z(on$|LXv_zd%^TbYs7V*2cY%^QHbUA>)^s4>MWS_(<9~T zirE*$J6VyR@zwZfyf85}%aW@nrF`huc);*3rAkHxfGg;36q}hURgv)lZd-j~076~v zn5C3TorPRV>rK4G>jsyuOvRq=K*tH5mk?-oGk+T^( zIDvBf+!Encry7Ao5sOsUkO%3>cO!u* zzGXH1mOKxOu5c#c2?DwY^l&i|79!L&73f=ojafy@rDrtNdfMcM z5|}20cFTUkPJ-Or+f9&%%lsdtZ~|a^FKegN$QSqxJ&b5A*vF7vbja5%Z*aH@SR^+b zw%~uWl_PL}9?zUJs`WeP|IjXH%YSMoFaLFp1|+8hN+8FwDm885C>24YtL22R58PN4F=DUYV^ZtZ^g2(#Ie(W-BThD)L4PZFn@Of+%kLN>n0K8hR4+o(? z1|$>k>rCl+c)&dnpO3V-)YsMer@TMv%jwiXywf6^t`CupArkoNndVw$*c9dN^x(a+ zlAE)R7*QmGSvQq3Q+u1sA1gq>YbR4hBdz{5RU@6O+8`@h(+*>3BJ~M{`XCT?M+j&f zAhoJodK#{L3dE^4<;&$t@3B4nQ3f<3B7G+SmTnXPF3N+k6+p`bh(B5w|GArF%dml; zHUrbI`m{%dX8Shnl;70kFDgGnu0h!$cSVmkHAP_|0K7D9r5aVWlXwhBvFWR)D#zL) zV$rihCJcj7hvR9!bHHBq=YzW789xaGCqq2|q6M9AZ@uvUfRJ&NRT* zjEs-07iVy;@H@K6W?g?dvo9=BX5Qs&03!}LesABqKD8fPb@^kG5x3L-7QNH0C_(3Ck(|Dutfu&;aErx zpf^|QQpJbaLLd-8TFM-h@S)%(Fd+_N&(SHws_B~PGiRQ&Kz`|;i&{cMMXss(a>5{e zqr%SEX%Ns-reM%;n*XDg{cuauTBIp-gEgJM`MB*Xkm0GCM}}8Gx|{`O8>3&Ri@!YG z;ylLYPZz{1;_L_e;NQIjzJQGO0*H*)WpZuGKUT^A052N3jrS#P#Ny&gvZ!|u8agCe zeh4fvEXE1YhLS9clu_P*1=}E!^lr`LoUAcLEst@kAI;#r!=(p`f>=US80muBVPp%u z?ZWw<)-u&YMh2zX=`y&pnfziKE}zvp)j|RJF%D&?GW8+2U!%To29-0{esyc22*i$K;}dI3sFyGd$fXX`Fbl` zuFj&Iw&!lfP`U{aVtg%FlNQg}zPTo*5~{)Fk(q$~hu0PC`XxI~RlUPj+0lLZ zJm(^=5Xmw~j)LdkT-yC)dk+1T+QCEx2@MZQ!o_!XEi9%hd2oC=;29V}6 zjC8>VrGBAn8))+;t)!+*ze_Ad*ewn9DHKri0!3BQ27-2VT6@-{(Aj~&dUoD@LFY4$;QICUi!V>Y@ zFS}{Bc63qG?iUr2;o(Cy%$N>5cjaiqU_W|Xk?~>Hj}i-%7MjNgESluoQ}9+K+ss!~ zvJlotNVbMcu1yu1Fv=_7M)}aZ1mtrOg=){J$T+=vTzZ$@)Pv`1C(^hL!KDQFA=EbK z5LM3-VTNT0nW#iGf2%XP@QgMmCUp2{ag#jK0k62hNVdb{c2EnAGOkS=v35&Hg&*-g z&ekrD-MzPNGC8{iCwE$u2Oomxhvgq?rDRSrJ9`PHH{NuIl6cnD=jhK_`Uej!ThpfU z+&I!{4qjy45ca;GsM}jUWds|7FCnSbSX0BQspsfSOw^lYez&q-yA7h;K37vY+&(yBngb>LX5B1;RTIZqt*$Zhubd|SQ;tVqlK zBo9h7MWGa&KV4P}wTQ_sC%so495wMHU>Jao9M!#Lc{Am=)$KbG+J?8}TO42;q}yKIL@RfBxMm zYx2lsN=kQ>9gD(HDgs+GrizuoZoo^73*W{Akk1;_S#0 z!=uAHsG__ju38vfPWUgRHw;QyM#`3*84Q@G%Pgo#YFcAEE!3bt zoA?>a;gttAx_$FH1MBboo3BQ7xyMxGO(x)jV!!hKK!5%pF95JKHCdAu%b{;@S$;gg zjOsD|rL>*U3D|tgBg{}ZkHZliZF4EM6i%v`RqMfg3r8D7O`LGYnlNQLd?G9(36+e(Z1UkGo_1yU`&mp)z=U49zQs?x!FL z-ObWSTgPhAiAp?p<$lZE=|S2b%|blz!g=njb*Jlc)B&O*n@_a-n=`-3qw8ne@+V&$ z4oHsXOJO}ei)WN0tjC+s_tjaNHFHQri(8rkr%CF4A)e0$NR2}T@r<^Fv!(Lo;Tr#e zA6g)?6#a5;pCH^6-h%zr&wprKcjQk^YWd5}waz_$_s{VwvQS1Mn?bbc89kAu5;E)vZ<1 zeEXdN>C$E`+nkhM{1%}>;UL`hffrU_yLFmR4W8Ahw$Ss?KTyU{1mTTJgI|NI1B=fN z|0hccdE9VJt!9<8^3C}o*6hTJ(Xrj_p4Ok=3~1V!M$^KJK0Ecc7HV7;<%L^zUSzs= zz(cY=({_J?kL?Z_ftxIJ6RI~?Sw|X}5hH`8y(6XpZy*I$;=ek1`I=&N>H8RCY?zYZ z(MubYfZBDSje{EIDA%p^%CRdhw-@qXoVg!8;6XSh>Qsf)3+KJ_DnF(4TZ>gj%F25R zWRkd4cijQA3Nq|rT&h8>C7<`6!fc5;Bp&~Zz-*iS_SgBh8_D0hY^0=+>sBkiUcs`t z8b#5QwPs$MY(K&-s^56!s+?J^`hnBCyBwyv*J6HbBA#6s{P}A}e{B4IjqMYK5SlD? zs!T=~6$(=`2WPQWR?GuNa8wq0(hrI6maw}$+l2j{?vY1)hc>Gw>B|io-qmzUpLTgx zr1X|TA|v|_!xsW1gOxGFQ3VBc{7{^~ZG%{y;8@*{h%Yy%8$sU_mBmu6mW_Ui5$9dp zcO1V%JN|I`6SD>#9T8Mk_r1LQYP&Zv?cd$8PPVPf*MXDC=Wtm=7$0MlvMcwK;H}GqzO4D3l|D_?zyZ z;?CK78pZTHE69HLzKZ&$)LeMP&{qWpLekJ)>+i64$aOih+S#231FrD%*m$4^RLuh( zCUE3Ck^_%^2H*09j1bN@yNC!7rdzAwIg4wc%(;=&TeVMK69TUO)7poQtG2Ym|FxDXoM(2~3#}ZnH~yZZhiiYTzjorQ&o|U ztEW-)71kqJwrRn@MiY}9R|$8y$zPiFRG+LYo|cA3A$|4(i=JkZm01}o!Tb3MRXa@Z z#VQHPqoAN(c1?8k%(GrQ!8#ODa#v13NTmYErm(%0hdqy&!Q%3#t9ydTLiF+|6}!4) zR{1g^+NUF#do&oe1o1PFbs{YW7y-K1CEyXv= za$ksG9=1;>x{OE{JwGZk)@-7N z%eg^s`K}tHOM8eCGA;_$Ti3!q-hdhO$GqAh0_GJDZVM(ep#U<(+dmhGXz;9DxgPN1 zGsPeMf9vV*wB_@vh566lG;p`M&|L)XC*H{Qt&CNHZh3*@cpCmuuDdjbb6)kjj5Out z8*it&dVfbNV-jg>2dL!#I*j(8lv=!a?9R6BjD5R*LMT;cI|Jq%q1@5x_aeIN9-pKW zR1^9jTlbP9Ydo-xP38ZTKS2j5cTF@V9iC}K0H!8c$2)d(~UE`k7$P^xPfh0!L`xh@U zTr-6jx0di;7ga?#m5V*v-l3@m6PHCVH?t_iO$U41Zr~LK?9#V!G}t?gRJjHa4}XnuJHD>?p@CfCU%;_Zf6NIRs+8T18l^SAHmL^IjCXdO;({e!AEtB_jK) z?)zJrdTTrSry(V(dJpAq@rKbgD*8w9OeEEhI)fAdmu?0OV6-rkaOMyBj}In8*mm*m zqwUJJ&W=bqL1?>0Sw5Kb508qX#khfV_D%#07(!3pImvJuZq_TG4zj3}FDk=s!a2j7 z&&}r`+Nn*&#k}ekE^UMfy|^`SkacF$N4L9mkt@qQ)!pbJvAOy#LMV_88VzB{?F5NE zT3j&Q=(OhDt1O<_iIu%uo14u0oMTyQs-1{R{x;*I>Vh+&>ISMBXRl;DB5`q9#(pZ!WcE#b==n&j~sCW<et#Kb|u6sHhpF$8?D;si}5W_=+9i?bA)5G%Q zez<*So?N+pD4=TpdG3VpE|PyGBPR9%FhdcSe(3nnHDE0m07GaDK&(;tgf?bGa2S}- zZyfGHVhBr}(#(v9Y#GmRlGbw)=dbdV^9R3TXD)~4l~c=q=08NFHh!xk`#Ny^05Mys zBJS)ObUbaV6`+yJQLdnHKKhj`nTzDu+Epn55Yx?t=nzO9#N*QORARYW1Rramy|9AZ z8%js%el^k|goRr?*_#n&88s=;rSO=LVo2ebF)iL0>Ij_0z#M$+dhSqvR@3mo6r3py zQYUG0r!K%0$kWiS(WmWu(iIpjLu(2i5&x9KBEKr`&1bX z?rnlLTifH_?D-)|9!MKbiAF~SUL5l-E?z;d#m_`0G=_THP$~l#87s1^=`2V6vyPhD zPONfaeYN4*?&h?*yBbr8-X9k9r1CZ(EFiPIydIGmTA5WZeIGGdFAJPCDZiXQs`n_I zCMF^91az~PKLx~vd#bB^ghs%$15Fi_7bT-%$3L;|~$yJqc%d`7IldyYzjM0WBMfTkJEZL1tFom}2J5#JEO zLCs9NS4VRe>Fu^4wN6;I-DE}2d`a4qJ*YHuWxZ2nI@(q9^mQ))QhZg2qBO3OT$nIK z6cs_Hq+pr*5_yy@09NLB-DZ1>k&YYILo0NorB1Y?Z<6ry-^kPtLtI5VU)e088&LB| zAx7cs$c;-ZBm80-I}C^W6FATg+?P18!bl4%_NVI*-KDQ+DtI`nS)5tbkY?N0Qc)1* zx|V%l2qL^-@AZCem7n!lwY{^jK0e%Jfy$Na#J5ymzKAi8)*MMuH)g6)*jR1@a5A8> z(?-=)%8Yw~7*=R|ZR%2XUa6Q7HN()cSI(9*+-cjFE2rQ)p5!`Jz+o-iUZMXIIaKO~ z3Q{SE7&w13AZeEtL??wS)~pkUMnDe}P;jhYqNBC43aqu*z2QSB*W8YO&#jQwOHkJ9 ziHe9C*tKX9HZEy@R~OjDW^a0SyL%zU|H6w-8K8__`_h_hA6OP{U>#0%{4g>y^1vQG zJK~tb%(a(zVUXN*;88UbtoP6Nb4c5MmBP(8QSx?2C!u5K)EUuWMliCNkDa2OIz`|sidv&F94sbX0N*Ni2`(!sC-BmjMtv z$VX+oM16aBTGFh&L{3FWVkP%z+pceZU4Wdc4@yinCI?esg%XwF`$w5OrGKsDko*bn zbX>DtM9sOp)>hqBP#!+B3$3%aPyr}6rb3M{Vdng>*3=pltw?+2O$;ufk5kj z^I-auF*rZz1HFjh2pTNoD$-5pv_;KSO=1&A|H0vH27!cOy7o>a%#T*T#fHuOlYB{o zz~yw)RyZ<@b-zC`VzE)ffh?bXX2<#drPf@P;v5XI>0}#Tr`;K6AbhdJ@scsWB_71S z(S>CEwL$|nklg_0E6e~;$@QA3BQpYUJ+1o2n8t^ICknVzOYdzqHi$(!DG(-w20#MR z&Sq}_-DG-YX+nOA7oLs7fEH+i8Q5P06FGvcYvrT4MSbEA%EVu%) z_iE7hddr{daz%gb8NuQ|0Okk{sce4?jOXKD*Uc)`yfDWxqtafWU>!5|mdC!*Of+iS zzDGL$q;w?_$xB_p(){`Ac>QHn(?5e9ZV*B$@Z;^*#_=HflUBR!Ysa9l8xKF1 zTPg$;kYMi2ES2k7p;4*HNuc_dqT-4xPk)24{93yZ}_^lK`a{57kkNlcYTR>h>c zV~zDLACwndSs_7rS;~?SGK0)EN1*@;>fPV&*XwE$`uZqe@%g{gT(kY`pD&oS+C-j; z^Xzc&7gvF$S8q5ZDNGmLoB3ugii|_yR8uO5d4B+5I&;K%80WAvi0dXrru)GevF^_8 zw(N5GK2o!VB&$V_FKO5e+Y29>%mp@#-intZ{nFk-{G6Ga^R?+5bhNsIrZi4mo|yCZ zb+yfF;1N zrV(}3j9wfJ;Z19G1&x5`uFZnEoFdYLk`+veddK0*mh@aFsoc(JMwfLX_X{rC`Ac2 z-0v%+VaR|$IK21C zjG%BK#wv^OWpFR#_}5b)Nazjv`DKNhQXP3F)rC)I(~ReAn_MpVZmY3*q<>YgR|G1+ z*^fcFheDnF4CZc^!Q}#ZT?n6C;1VV+iP<82qy=Zz^k#&m)+e*pF5X?koIKgxgdJwO zIaFfa%#2ytQ{f2H%!T88v|7e~o-4N5dqJZpA!J@!#_#$9QB$RDlvXd8)Z=&%AVWxD z7gUHjH;W)HPCeX^qwc>%k-Z)4|8-_`R;hnet5d>|JfEW-WWLbf6)YPbpPcKQDoZ%M zbzzJ=NV842ZA=5|Yh~>ZX8NyaB?VSMJ5LKc*{^_pVLKS3(PCfqLghI$bR+XI3$swz zUHJQy=e>+);-u212&PW>@P4cJFQz}c8ViT*%iqGQq2-|2!bqu2IKB7c#5Oh#2tx z!^S>H0E4Oft++Dfx0j{t|3tZBVB4d&LHM8m5H!3&nw~tJr$t;G9gThRTnb!!m0G=E z=YE-le1u3aIXSu9u;w(3Wv5774dEdXgL4}9ll9)TJAczui#nOuI-Rfa zfdd}`V4{ppRV&A3{^Oe@a|7z0fai_?Q5I6V|M;xaC)MHcZilqBr9FlZ0*(SmQCS_~ z#OcamhjZ)J4(r{W0KYS9_x0oaW|6F}Ujh3$*<&V3rYG(5^C3Q05a(;1=u?;!$dIq; zu&=yvxso77Bh?!Iq^f1`V?oHNqUWL|ODyX?h?EZ;@+IZh8;l(G>W@5=jW!~ko(erJ z^)LC~=Z4yRhT6U$)}1t|#fwnP=hu`QNFGL5m$q?mXV;g7CmdnVg@)(Nwj~Qo=|~Lf ziIHxIGYrFOwYeL0ga95!T7dEUPmmcJrQAphfWYM=q6KK;?P+bqmuDT4_09|w(nnMTzSY`7hwUdRLUx$y zKz8=S>wjdTx=$U^aR!0(?K+-3z@3I|>p!0JAj!bKY}C_+qMAc?BCeaEY|QiuA%5IP<&)wS8OPc{Uc ztORoK`>1hp65E7hxZW@+Xj+e`9UQP44$;}w1eLcNp3b@C{Hq(1(eaZ7$#S4kmo{yh z;)aQ9HrTuQi+6Yoy%Vh!d`fID0OSrRq(|qynqmL2-COP`rA6Sl7Jy;md&wrc8ukoj zvV8v`Ck(N4{Fuf%a5^c*Vw3RL7t43$=d(n7tgWi%TxTyzn6-O^Ul8MY5F^G))6`p` z=HJ;@lz79 z#$Fk;wk*Bgz0H?!gK5bHo4&R2uSP4H%2XYO2!-xvLSM|B%1cALfx$w4-$N)>xth*T zRdGU~ytQ^bp+*kUNu&Oj{rJlQuHe*UoL8ia3Q#Y zX$T;?^oGv79;`PFTQ2PR|3BP+a(MSy*VoWVDdxcrRcqGJp43EK*kigDikLERbcIGB zY)Rz#^LlqI-Cfm=hPp^2kP1tuD+)6?TbVv5<(-F^r_ z7eg@^Mh}uoP1Ikf%95EJnvIReoIPV~xpQ|GC;kWrejXanm0!iKemu|n-p1B?=1+O8 z5(?*=^->jBsb%N!|B+N2Ns&;WB{Z4TIlRzD-P&Drpj(%R4z7gA59y1yna#>g% z**$S5=1ib%#+P@D_7#MCzC~iGYjy1g3kJgGr!`gb)LF=uxEyZ#>7!_Q5Li=I< zw01&<*X{P_7OUG;1VYw5HdKR(9X=1R^q9#R4c5DHiRc zdp=;(2M}#3XPHp6flzs`#=~%}o zXGe|^qJ0;dl!cLtB=qOVS9DieNk8| z(4AaZ-8qPQ)&)0~1WMOBE%(f3TnI6+6b6fy>TbCcsoh+gz~n(#*DHAXK4PZ8{!y5~ z`;&rWiru4#IJ&DRI|+46*iaB-D9+s9$Jw@M@XTMt*;<->|=7g{`DzlJSg_y)J} zV6EoEK2V*pQXmK60+>bKl@div&NgJ&vQTpxO`Dq9H3E;+i7WNUsNf>bsVT0hXmrx{ zNyU}Yh&6-oZ|V(gI}ZZ;VZ{7K3lZut5m3wRA(`S2vcO3Ft^;o<_DJM_Vj4?Tcho8cdHgvC*6q4R|qux@*6F&)#dL#KA0khk(UJnQzRVa(U5e zWBr{oLUO^{U_KycQF(Wr8H_AD=;LT&P5G4s5tY#y0w|eCU|Y)QE%X&7ZNEg!tj?eK zfgn%Abbrh-adfF0X%R{I_Aa(ERkj+Pk(1(Z;!KGP0%^zf|8Vc<_GGtor;HN6a-ze{ zM?IwG3Rrj7MMVZSt|Ga&z%{hPXDz+$Po11FCx=@l#X(91F7x-mM_Lf^xpA@@ZAd+y z%xMXHRw7`&;Q>2XB(Tlu3K4zus{D{Ooy-eGWaQ|=FmnnjDxgUVCB@<95%eGz<(ZrdXFC-AKyD4I2}sIRC&5Wie~L4DkzWddKcHw>+*ap z%a`D%ki^yRF*_T$6$S2f6Js&Z@f^)q?Skp~k9c1(WsHgPte@sXv`rctkDm|h9=m|{ zfcFc^%Qt z@%hlCcU3qN72&$BF9Z&g{?5E+46I%Y%|3<;P8C|(R`Plrw0GrKKnXC$JV&Nb(T!j< z(UUc#%G?fHg_n=P6-EkY@|5U(Ld4?_FR)df1uYU%@=BC&0)W)v#*W}aZY zu;`*(a2CoaJ=R;DU7xR{PQp-}(X3X~j-@nrs-&B4!k3Tfoodr zFam=tinAtd)d$??MYKz-OsnD>68Q-0s2Vq`A_`bc|#uH)T5;d5(hMzMj4G% zd-S#&S3|-=dqR9W^0#J7)?U#0Lz*Xe+nmJ~fJ=VffNd75)$0kl)#*vBh_RUwbr|Mj zbAvF#?=ys6X?GxSNUj=Ms`-5Jv`33v%ty(Eoxk(5g`;O_4315??BJGE48yRNZ0HLF zbU1yY-&ukDtiE{vfe9psAwpT;TYd$A? zt(}Q1hbh8@UBU%lQ!dt#>V=B*^w@*L&|v!SwS8}Dq;Hbm#=8+vk@@(lVcWAdTj`k2}6XL=Fit{y?gPt&x+s*n5)uI>4o z#YHTt5H4xdf1C1)3K){BPF-+pr0jB0wBUNHa1reK(Z$kzI-TIMI%bKMQA~n+J2{5l z@43RzY(i8!LRjr)Wpd8f2~?4$1O&6iiJUTJwMOkq?|fABLM6W!jvv^!?A2zV8kh&v z+0edLZEpw4QU_8}D^>rp)ZcYHGPHsq_fdG(ccxHW0=$e=%LmL(wVHxw+>R(fkOgxQ z?^bKrrfWYN=HSUr!}07N*^I4)_fjZeV$k>B|v?Divj{i6U z#ObJIC9J{IoL_t~>H8Sn#cW#I<|Ta$Pr2QnP=3#rmCVR{0yLvcRu|Ql_0OM$P*3a$ zD76mBfJR>2URb!+;2Z2QB9&_%=X-WifJ33&3qvY7ypNqL>s(Uel9lIuTFQkWw0TFI zXCTj<40ubZcpmHHYR>^DwH3aZKqjlf zSd_?3fJ9YmbcVxTC<~;+TZYck4x5+DX%7w&9|t_8Ou(6q;O3;p=1(X@woaZIqJa+J%668$jgmwnMlPO>g8z#sXTr{D0w7Qf^ zBNL`nt+e0}=APA;y%F=SKtAU4C_B1RUm>)ar=WOlj!CT+%e9EKs#>d$^00$-V4A^{ zYo;+=db)c^7uWPnGH005@v7c#K&rX*;7*T>62I-~Q3z1H{EwCK}!*sn6%iU`Y z+0B6yE4-#0Yb@0RpqT#h-tpANBBiuh@=CODmQ-WP?Ow{;zRO){JG{3 z?AJc{NBa^5&GR*%fSr89#1fY}bJ15)@}iA{L%8D2?MQv7v!PTwJ>tAvyT?H-7Ml&Y z@b=%&HaHT%DrH+KxmJ~*&&UnoFkN{uQX>IQFEg}Ccr5ZJr-dwHzH=Z9#+un=|2b+f z8{U)CQ!6@N5x7qRYYUCQeT)y z>`g2KY1&<02}1vv{>A_t#MhUAx5*pD)s!l_@YVw%Go;6>^$-mdn8#=;k#UTJ^MTH+ z2GrJYNf{hpa2;PRK5s6Vl#{ZOR(1>zlQ!_cyZRlZs20l?_|1}WYRhIIRtX}FM%?kR zz&n|D%66n>AfA`0R@uFrkorqxlvAqNOLEu)>el1Dz-I5E;+FATa!!HFyyKeE`)DLL zaa;(QHO$UeyW_RbdG%*h-{eDTKcFnS1G!kQ$mSg&z)d(VM&>b!^$sOQBmjt3q}7(D z@eKyRTi?bto5Nb-gO7a}c%uZ8+9Vf%5*ANQ;-bzYKIT^+`FNiA>k@^fmRxk);d}$J zgpi8fAXDMmUpk4tIh=c40|VNq!N?XKXji|K1K{^4FMWZ_fS9cCF$j@~nQLti^N5V! zrwbp(hAc0y)#}aPF$Cgs*tcF~q2W=3nu8OXVNacNQ~+SCUMe z2sCSi1g?}^Rzn&+d8Y+m}7@24YBxNeL&0?bcb*d3MAr8p4<*o$9(*3Y_68?p2^ATTnXJ zw+hw-aXS+D>4U5W)|;sclJgi3+-jPiHG#U&rc!tR2SP^ol62RvsG#7xW9x%li8y*j zMi^0`iPQfzn>I=d8^Vukw+1y5u=oW+g4S72h?y#0>mey&V2D?#3%(Yj^^Wd7&EZsc zI(%_`kb6K#YlHnhZphmyiBE{!(_X~2eFl-4aDT&{93ft2YYa=1u z&45-^SuFww?#Qrdh;%~_25g*p03}>KTIDefE2LYkzp8?qN>YXd4AayByFQf)eM$rn zsLYcnxS~qbjvCa3rXFP)76-~lPBVKQvL74r8;bC046-P z*eeCi*9=Z2uhxarbEz{?{BJm!5JrW)(dy<&%413z3MP*mh96+8z*=oz7Xf|Wda&!) z?kZ8ZbHT@V)f&f9Rt`|h3xPL8E>|prwG2mNYi%(hwFM}ibRhco7yFSotH7wRbTE!f zJq?r-9~MV9PQ||#K?X@3ie6U#^_!uGYm7sXLbB_SgVl_5blw ztJ^?Ol>OVw@Xz9wZg zcZ*>4B*3Y=9On0_1OL#zBAoB}kY69W^JMymJ-n5Q`)v#MN>dIJp#00J`0X-4ga7WN zfEP4H2yFIzWeZzcEabk*59dS6$N{{!2p-ItM+uDd7iLehZBvXj96q~+a$!p90+A-p zN2Fe92?2TJ%o^1bn(a~3mE$7u!1y#|!=pfp#q_P)U@j{P7wF4l6f)}jaq9;2Y^r9FU=Bf2T!Wj~b zQhos?dQsGxCfU+ODZCc|VZU1p;5u}KwxtTR<=BC`xD?rJYN`*U_sx}M1w4-#xU;B;=Dgn=@` zZr^@Kvii*~pbZ0~R*y9_xg_Tbf)(e;w_z7Tjoj4(=ad1w@VKJmpO3-?BqU(DZn1;p z{W~RG)C~Z$8F14h(?@BvJ9&S`KUiD>2Q-ft*bZ0wvLjCm?J z`5En>`Qab5b&^5QF{TR?bn1wT#(Z1e%e|Renl?iO8D~f1Xy5=>I;5<27C@5#_qhiE zzIYrEaq7ulUzZ7ZD=?i^G2K_@v!N#o^0ocRw-yMJ3%Glq^^R?_w>uV5yOgQV`xnPH zchv{8_xgGnz&`A;q#O4ec1aU_TF?7;Qr&6*Vz!{69Vjv+JNh*;STs2G5C4G;!X@PVyI+D7`Xvxqundjd`-6P%si#~=Rt=YOJs^LMXl zkBoaeW-^($qL3;Xs)h@nd)B&8dvLApV@QyoCM zB&88Vx@!QXL!?v$2|-#Kl!oVA-+#SdpU)2;?{AoU?wNDXUVH7e_VL(pj87LR`)BON2z7&y=6+#d23k1zgMk=n4`ldCN|| z$iR1lP3P}t@S{vedT{93#=-t!^sTgYt352mk;UN&9*+?jIQHnzKsj+5z`AA?P}|^m zL>TkZWA|gT#Bk&bKL&2AUz?xD^E4J0SsZC!{WYXOGH7ccK)jF!8p6LG28?qk13b?} z)sMvgTr;Z=D1`<6GoKfGzqFXtAZ+D0u0jjgSb z5)F~mC*&}4HlrPv04mxScbGoT9)@#*dU=*s)n#TIOVgcQ{w2Z|0BJ-5=-NQEoL#eu zM&`D19X2b6ZGc(t0t+H&@rkVlp5(D#5GW|5>}r2+NFxM~H(-|Sm6}TSwZW2(_L8VH z2!gcumrQ5()EMddJ-Rde1f6&8Cw`<3$JIWpI10&x63P`*eA~-|p*Hc-KHtg$OuI7z zI*z=PcCX5BVps*ZOpFFysg5J8^z_21$!WrqX33bBX7kI8~q{{hhvddrUIS_sqNy_;;4a*eLJvDjUnXOS~eBd=D@s*;gzr=j_Tm5_sm&p#U@%s!R|IqR^ z7FdJZ&?Z1urFGj4ri?}eP!jN5%ZQ3N>O%9Ltdj0?mY02L)s;R!YQ?SqdllA;UA+4U zn4`GN_rxn`T~7`>1G(BptZhW25@MeK-D_K%el`}LZFf-`D0mjwgJgn@fP*`YK5vZL@Ow# zPA17+<)kp*|J1;m-0aFeQRBb}ZvxsCz-;SQTTiPR=m`6ry;XfWhDw1Iog6Be*ptV9 z^;o{kzkYI2!6NDbp{4A7LjNd`KxIP~ksLXZ_|&2uGn8R7hKi^aYNMexv)}AY2Skp; zqlovPJA2m>rqVo4Dy)8~NNi{FTg3F1oNk`qyxhQAqRUI}Uc1TO#5)Y~lmnX{sfgoR zQeWGHx{XeviTc6w@>g-uQ2?oR1Jhys8w}Ia=09 ziV~Bf(#GEHZH;Hk&38-*U!2}&FmYDrSK)yi@G!wKFew+)y}W#RM4dIX;Qr z+u{7kCNms1-8P~4N?VvuI?4)-_2!v!i8^uXEqOMXxLj#bl>ckEY{`<@$edwyc(7;S z{cyHUW3y2NX+PNKb7Ys?53-!5T<1nxXvJI#57G>IO{=+-Bh4*vsR7b>bjQG8Jb}=x zV2u`7bZ*qG%RF3GaGE^UJ^pg=_FK2ivBVm{Mi7se`l&3vwisyEWu>hD=x7Ifx_=$h zc|2t^*6iVa*vest?y;)7cxa2t;L^LfzzWm#@*z!s9^hX5pv5<_?RC56lWNw4X`JRp zqp|7JlmxWK#7`h531@FTdEx)qpK(&~;rm=u|1c?u4HnnbY*p7gxhCcAqa1?%i#Y(5 z(9O@3U{xZlM@J3x1HO<7f8mf{di%g7t;USxgbvSW0sDe+rOrpw@lt?ht)m0K!3o$~B$=;K*LE0~!tQ4O^e3 z-l#ocgnM_||2mabHXWZBuo|xxh5T>K4INJ|!9gy<1eQnnK@W?F z9Uj)}0+8zSx5=`n5ybZ9sFW%WO15(b ztUgR&n>Az8FcwM)R0i++XF@swesWxRVrVffRW4TyJ+H^Ow?VTP1?UN-KvDX_zZR&< zPs)As)l5`Bu(Q#_(uJ!L-A#F0Q8zyk4NxD=4W4#OS#dX3=lkxd43pgR7s3-O(@*GS z8qyJ0?GXwSI#KKIY701=X)oBCA9u+<7>8HzAKqAp30!V0IcNS($QVr>3S{=FYla1W zDKAG9dbPWhA4e=kt{ORJI`pLN`*8=)#xLgn2pJene@@vi$|SV%+~VYe`P{`p?lJ~Y zO}dp+8beoCh#um}cQe*?f#Rsp;44{Paa`ADDfHxK#3C{%VxZZ(jS?7kdgBZ<)t8rp zt14KquztovLG>JP(qC^`jbEBYz3VzZB>JzAY(?~yIlu6siB|FUXZ1z%7(V5C_gahf zS_9{h)BdPs_STLo1;TDeg+JuRy4mXjq8ISU*)Pq=tGhcdR}7_w;_>c5(lq~htFy4GNpdP zT5~Tt=cbEpEt_P}mlO-FyB+o19=nMVCTd`er5E(rDOWr@a1E1mxbkfbb#PHQZ!oGPusq-*%9mCB_U6DxPMyin<&?5{n|^tmQNkl1c+q@ zT51Bu8#7jf5vi3|>A?6`9tMoj9alIVJIs#iPZd0^pRZXTYjs>-6q&ugZ=Q5z?(2{a z5wZ!W+$j08WE+tNt?MCjRgbdzyX`x+TMNluT82?>WoZ+s8gnl}6=b&iW!0!sBz%@@ zlp?&`Y|q1CRTrUTkM>?WrKDN<#=kv6D0Zp*OO)<_+o?)88s*?`N9kGFt#}gXjTEc_HMdb@l4uuMfbtv zpONlJpD8#+y85a+9p@F+C8dYE!v#ckJP3a#iJAdQF~RjG@8C;=E+)8(QUJ)g=-Fo! z$>1^XAr>+@EG&tD?SOul@=ydE(6eH-f93;+oQc345#?Tzmj6php7Zc2DwD@Bcbm9y z2UJG_+j!OFXMuC*&aGuCV5zxeug}YUm&-h<)8I+9W!;!LzP&h&`j=+9+S6e0{>+){ z-~na?C2EM2sc7pS59}~kgMelnctP}@{;*hlaO-T~#)-?;-KUZg(#&)){RFW9(N}^# zgrEm36y><5rt|Sg;4`lM`@0c{Yc!i0J28# z%`nGpm0Jabs==tf`!Xx;FZ&0C_ql-o)A*3@i)*{kKwLNQn*4#$Wic#h zY2cqZw}MC{k{~LdUmYEF2g|!bEBUbkPYmI;s8H_L3OR=BHdfzqZo?4FMvfZhn6dAP z&o7yPEo~4lI3g|5XP>ShGLA`wR)>OxfTCdTjam);M4k0z*$@-&MEV*ZZo!V0u=YN$ z>n>*ZX`d7R#RXD7cnrhT`RaP`+DDgPPPlO4%8Z=U15H)^hp!HPv$wU~aXPJ~%fJ&0 zdFH34NOMYp?SIMjhN_~?R<-{eDAPJ{J`F zv6K^a!l3@6x;e5($3+4ZpQgu+m%Qo1@r`huAxoG;&gJP$iCO#4#iGD{o-Q{5si3x3 zGmVUmgFqUN(8iNQ0!ZB_Uk-Kin1|!2dqQB!ZQw*vQWWMJrW-y+fJu&kldNiv73q}y zn7IWMjhGUm6}OukuLzf)f%SWoW%JI@qF^g-c?pH?2JI1sfm2nHdR%PE4p>YpPRClE z_C8(05=RFr#kN6QhPT@~Gh4f659}&vJ3{l5A}DGmp})yvb4&WXq8$7Zz*JLkx(ZN4 z`Tk5U0qjbU`7{%30uFN5(>(io`^23m zOi|0xl^d_i7Yows-S%53pA}DoQnUC1Y=OJvI}o1&fBhx^0EvPlF2Lu%E93V3Gs9!i z{^}zD=Ylns%Dxw9RXKfMrds>DOPbM2Aej_pFFj-MhpR}v+9N;v+8-3cb7r;*nRVX5 z(puA)*jmEq7rd0*;^X7Dm(EPT2F&vU-#0zO0!^4OTpA>C%aau(AYz82vE&F;SE&F3O% zNd?FVIk{d+KDXPn_@bR5>^E`~ig+?Yf(fN-jNaqwhy+{X_yIUj+t{!m{18hJ+ie_1b8&=8>8dfUz zjDq~h*w1r|8EUt2&A(T{G8zLUxjCerC*Y9cOQq9T){~k0lk)T|_YmLwPl*6km473C z0wSVkl#^MHvb^HsHut|*cnX0-O&)xD#QZyN>&)#tWy|VsuI1J=z8y@u5}9Vn8U}#K zlyQe2Dd+`P!-xHD<)J5*mclaRH1Op-#NpGL1!XO^mm zNh&4*y+4AB!msy)8JWL)`&C`YXPY|zgFXgDS0|mhkv@XNx~e_-5thOXIDGBQqg5`) zSE>^Npc}{UUEq$L8{bVj02Ki^%fT7f$&PY!ewiMR4FQhk4L!g>#z-

}qRpI1rLxD}SKQZjyddxc|Bih^u6G#I=!Ve%TUDb_e+JA??U9y&({(j;c=BQvF2M0`kWUNJarm~bK8 zkUo8)M)8r@p1B}6;~GUfxp(1_wx;tsrsk-+to%z9^`2HE<2*u^$rwx}+-4K>?eM89 zkr#H;IEga2S$k4Rk)}N;$0KF6>wl}iv@(u;-`g>u3lc2;Kns`LO?tbvad;dfh)5N6 z=UhG3zV2VK-b(puA=!29*U#vFDH?T6OAg(%k4J@DB(!41WsZ-tBw&T-?SX8^TFjYm z4XAtYlO}K1mg$GiogZFh^{53EFDFe|3eg@?_NK%5w6>hjV)@9SC61YQc|xp}3DKei z%kAR2v#*H)K<2=W_R6-r1s}Ex%%jRlV%>ShQwR@x}zFf(?&qREQtD>yj=IpL9 zTUL(ztlu#|A<-jyc&rb+^J8tOgO?SYOtOY+GBKuD+j6%7N_8yKBj2Ho#DXqG?zAgkR}B^jfg-uy{-O zqfI}wegag%OPXbgJC4K#sbtKiaq-NqE#KOpWv1`4I>LSwB;l4)rE~QNn#B8r!~zMs zLO~;0;SE3%0R^ZV$^l*&GYi;d&5?kDRL1Jy+DJFv;2B`fzh`G7Cj6 zMrrG;WfG`0jg+Y>%8Z=RJXyjWNpUZZ<;Nl{{62!7ac*&QF+Y5us2-!xhr)2>hP-yp zb~H9)hbHm0VEmqHK5)zm6cLEY{38FNi+OzGR8gn~vV(U>>ZvjJ8?=&Xr~%h_y*M*$ z%0FL!=q;*i_<+{~Gg-W~+9=r|0#cAZz;1bh#-buQn3aR@;w*SlA_}hR zihFN_iKxK&HYVA*P(x(rXNg8t*a4q3>n%)7R9U0{BtJT>r0J}KqjeL1Au}s$?rj{0 z-NZKj8(K7r?Zi6@3=u_Je>p+y)u$3k11Y?UC2b0=Sws|&TwooE$WtP-B@5Y~xl;gI zO7SEPQe`5P6}bW$zawa=*iT%M9n>^arxlxTLUHZ~^o6>fHS)HC>cSm1`fDVDWv!T zWCG*8tBFU&VPhMppndK4A8$e`WWP^mnJQuUm|_)$Vun zt}5Jag=>E;<}MK-_N3>2H&y?G31y(lSPiNsbGyoPufx-elEpB@cQF2gGa=IM0+r&U zp3S10Z$@gK7SjMS_J$>gAqCaqa<($ey?NsE^SH-(Y!~3C(b1t={3F1 z^UDXJ&aaGBD(zEv(aXe%>qldsnkR$#IVR94>b!=c9v7^_jPD zZFyCxQlmUyOSU}+DxN$KJu3x!0LnDR_AhQSGKzyFdBM*}8xIaEbH>V7;l7JEhcdh# zslc3x4?26z57#6sJjufZbh9%>i8_Hdo{XF(9#N{EZ`^Q$F$-dJYTCLYBVGnd%xWnk zjEh~28~zmJ8sq{|MZ+sJm@-W*eur<7nI7lQ#A-|BJw_7KxOcxULZNXN?Njwi=&!^c z#N<8kz&JGaV!1Z5q}+tEa_34KC+RR(S-Bs2QR=$))sQALTwPFGND>Iuh2HAo#5AUU zU>Kw&JgL|u(6;}2WrkXm-iHybnrmqXBw#o5d}s$VOm}*MQc#~()b4Ji621ZMIF2V* zs-R{^y6gffiC}6)u4fUMC$yY84QUt;3|LwdGRgWJXfiWN=%|09Ln*$y3Pm)6qIcYX z*E0W98cjV7oz?xf`Y5WP!Gi5av0mizbOvr dzJb#OCv>3B*vD}dxp4vf$w@0ql}Z>r{U3c46HovE diff --git a/test/image/mocks/ternary_markers.json b/test/image/mocks/ternary_markers.json index 369460167a7..c7f7ed2196f 100644 --- a/test/image/mocks/ternary_markers.json +++ b/test/image/mocks/ternary_markers.json @@ -2,7 +2,7 @@ "data": [ { "type": "scatterternary", - "mode": "markers", + "mode": "markers+lines+text", "a": [ 75, 70, @@ -55,6 +55,22 @@ "point 10", "point 11" ], + "textposition": [ + "left", + "right", + "top right", + "top", + "bottom right", + "left", + "top", + "left", + "right", + "bottom", + "top" + ], + "textfont": { + "color": "#DB7365" + }, "marker": { "symbol": 100, "color": "#DB7365", @@ -62,7 +78,8 @@ "line": { "width": 2 } - } + }, + "cliponaxis": false } ], "layout": { @@ -83,6 +100,7 @@ "tickangle": 0 }, "baxis": { + "min": 20, "titlefont": { "size": 20 }, From 89ee5338e2487ad3474bad57a9a4b6b3461849d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Fri, 7 Jul 2017 12:03:24 -0400 Subject: [PATCH 08/20] add scatter and scatterternary cliponaxis tests --- test/jasmine/assets/custom_assertions.js | 27 +++++ test/jasmine/tests/scatter_test.js | 137 ++++++++++++++++++++++ test/jasmine/tests/scatterternary_test.js | 109 +++++++++++++++++ 3 files changed, 273 insertions(+) diff --git a/test/jasmine/assets/custom_assertions.js b/test/jasmine/assets/custom_assertions.js index 7c84f215c77..315bff917be 100644 --- a/test/jasmine/assets/custom_assertions.js +++ b/test/jasmine/assets/custom_assertions.js @@ -46,3 +46,30 @@ exports.assertStyle = function(dims, color, opacity) { }); }); }; + +exports.assertClip = function(sel, isClipped, size, msg) { + expect(sel.size()).toBe(size, msg + ' clip path (selection size)'); + + sel.each(function(d, i) { + var clipPath = d3.select(this).attr('clip-path'); + + if(isClipped) { + expect(String(clipPath).substr(0, 4)) + .toBe('url(', msg + ' clip path ' + '(item ' + i + ')'); + } else { + expect(clipPath) + .toBe(null, msg + ' clip path ' + '(item ' + i + ')'); + } + }); + +}; + +exports.assertNodeVisibility = function(sel, expectation, msg) { + expect(sel.size()) + .toBe(expectation.length, msg + ' visibility (selection size)'); + + sel.each(function(d, i) { + expect(d3.select(this).attr('visibility')) + .toBe(expectation[i], msg + ' visibility ' + '(item ' + i + ')'); + }); +}; diff --git a/test/jasmine/tests/scatter_test.js b/test/jasmine/tests/scatter_test.js index 658285ec965..2808e9bb6b8 100644 --- a/test/jasmine/tests/scatter_test.js +++ b/test/jasmine/tests/scatter_test.js @@ -8,8 +8,12 @@ var Plotly = require('@lib/index'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var customMatchers = require('../assets/custom_matchers'); +var customAssertions = require('../assets/custom_assertions'); var fail = require('../assets/fail_test'); +var assertClip = customAssertions.assertClip; +var assertNodeVisibility = customAssertions.assertNodeVisibility; + describe('Test scatter', function() { 'use strict'; @@ -670,3 +674,136 @@ describe('scatter hoverPoints', function() { .then(done); }); }); + +describe('Test scatter *clipnaxis*', function() { + afterEach(destroyGraphDiv); + + it('should show/hide point/text/errorbars in clipped and non-clipped layers', function(done) { + var gd = createGraphDiv(); + var fig = Lib.extendDeep({}, require('@mocks/cliponaxis_false.json')); + var xRange0 = fig.layout.xaxis.range.slice(); + var yRange0 = fig.layout.yaxis.range.slice(); + + // only show *cliponaxis: false* trace + fig.data = [fig.data[2]]; + + // add lines + fig.data[0].mode = 'markers+lines+text'; + + function _assert(layerClips, nodeVisibilities, errorBarClips, lineClips) { + var subplotLayer = d3.select('.plot'); + var scatterLayer = subplotLayer.select('.scatterlayer'); + + assertClip(subplotLayer, layerClips[0], 1, 'subplot layer'); + assertClip(subplotLayer.select('.barlayer'), layerClips[1], 1, 'bar layer'); + assertClip(scatterLayer, layerClips[2], 1, 'scatter layer'); + + assertNodeVisibility( + scatterLayer.selectAll('.point'), + nodeVisibilities, + 'scatter points' + ); + assertNodeVisibility( + scatterLayer.selectAll('.textpoint'), + nodeVisibilities, + 'scatter text points' + ); + + assertClip( + scatterLayer.selectAll('.errorbar'), + errorBarClips[0], errorBarClips[1], + 'error bars' + ); + assertClip( + scatterLayer.selectAll('.js-line'), + lineClips[0], lineClips[1], + 'line clips' + ); + } + + Plotly.plot(gd, fig) + .then(function() { + _assert( + [false, true, false], + [null, null, null, null, null, null], + [true, 6], + [true, 1] + ); + return Plotly.restyle(gd, 'visible', false); + }) + .then(function() { + _assert( + [true, false, false], + [], + [false, 0], + [false, 0] + ); + return Plotly.restyle(gd, {visible: true, cliponaxis: null}); + }) + .then(function() { + _assert( + [true, false, false], + [null, null, null, null, null, null], + [false, 6], + [false, 1] + ); + return Plotly.restyle(gd, 'visible', 'legendonly'); + }) + .then(function() { + _assert( + [true, false, false], + [], + [false, 0], + [false, 0] + ); + return Plotly.restyle(gd, 'visible', true); + }) + .then(function() { + _assert( + [true, false, false], + [null, null, null, null, null, null], + [false, 6], + [false, 1] + ); + return Plotly.restyle(gd, 'cliponaxis', false); + }) + .then(function() { + _assert( + [false, true, false], + [null, null, null, null, null, null], + [true, 6], + [true, 1] + ); + return Plotly.relayout(gd, 'xaxis.range', [0, 1]); + }) + .then(function() { + _assert( + [false, true, false], + [null, null, 'hidden', 'hidden', 'hidden', 'hidden'], + [true, 6], + [true, 1] + ); + return Plotly.relayout(gd, 'yaxis.range', [0, 1]); + }) + .then(function() { + _assert( + [false, true, false], + ['hidden', null, 'hidden', 'hidden', 'hidden', 'hidden'], + [true, 6], + [true, 1] + ); + return Plotly.relayout(gd, {'xaxis.range': xRange0, 'yaxis.range': yRange0}); + }) + .then(function() { + _assert( + [false, true, false], + [null, null, null, null, null, null], + [true, 6], + [true, 1] + ); + }) + .catch(fail) + .then(done); + }); + +}); diff --git a/test/jasmine/tests/scatterternary_test.js b/test/jasmine/tests/scatterternary_test.js index 34c5e764370..903f9cdf21e 100644 --- a/test/jasmine/tests/scatterternary_test.js +++ b/test/jasmine/tests/scatterternary_test.js @@ -6,8 +6,12 @@ var ScatterTernary = require('@src/traces/scatterternary'); var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); +var fail = require('../assets/fail_test'); var customMatchers = require('../assets/custom_matchers'); +var customAssertions = require('../assets/custom_assertions'); +var assertClip = customAssertions.assertClip; +var assertNodeVisibility = customAssertions.assertNodeVisibility; describe('scatterternary defaults', function() { 'use strict'; @@ -374,3 +378,108 @@ describe('scatterternary hover', function() { }); }); + +describe('Test scatterternary *cliponaxis*', function() { + afterEach(destroyGraphDiv); + + it('should show/hide point/text/errorbars in clipped and non-clipped layers', function(done) { + var gd = createGraphDiv(); + var fig = Lib.extendDeep({}, require('@mocks/ternary_markers.json')); + + function _assert(layerClips, nodeVisibilities, lineClips) { + var frontLayer = d3.select('.frontplot'); + var scatterLayer = d3.select('.scatterlayer'); + + assertClip(frontLayer, layerClips[0], 1, 'front layer'); + assertClip(scatterLayer, layerClips[1], 1, 'scatter layer'); + + assertNodeVisibility( + scatterLayer.selectAll('.point'), + nodeVisibilities, + 'scatter points' + ); + assertNodeVisibility( + scatterLayer.selectAll('.textpoint'), + nodeVisibilities, + 'scatter text points' + ); + + assertClip( + scatterLayer.selectAll('.js-line'), + lineClips[0], lineClips[1], + 'line clips' + ); + } + + Plotly.plot(gd, fig) + .then(function() { + _assert( + [false, false], + [null, 'hidden', null, null, null, null, null, null, 'hidden', 'hidden', 'hidden'], + [true, 1] + ); + return Plotly.restyle(gd, 'visible', 'legendonly'); + }) + .then(function() { + _assert( + [false, false], + [], + [false, 0] + ); + return Plotly.restyle(gd, {visible: true, cliponaxis: null}); + }) + .then(function() { + _assert( + [true, false], + [null, null, null, null, null, null, null, null, null, null, null], + [false, 1] + ); + return Plotly.restyle(gd, 'cliponaxis', false); + }) + .then(function() { + _assert( + [false, false], + [null, 'hidden', null, null, null, null, null, null, 'hidden', 'hidden', 'hidden'], + [true, 1] + ); + return Plotly.relayout(gd, 'ternary.aaxis.min', 20); + }) + .then(function() { + _assert( + [false, false], + [null, 'hidden', null, 'hidden', 'hidden', 'hidden', null, 'hidden', 'hidden', 'hidden', 'hidden'], + [true, 1] + ); + return Plotly.relayout(gd, 'ternary.baxis.min', 40); + }) + .then(function() { + _assert( + [false, false], + ['hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden', null, 'hidden', 'hidden', 'hidden', 'hidden'], + [true, 1] + ); + return Plotly.relayout(gd, 'ternary.caxis.min', 30); + }) + .then(function() { + _assert( + [false, false], + ['hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden'], + [true, 1] + ); + return Plotly.relayout(gd, { + 'ternary.aaxis.min': null, + 'ternary.baxis.min': null, + 'ternary.caxis.min': null + }); + }) + .then(function() { + _assert( + [false, false], + [null, null, null, null, null, null, null, null, null, null, null], + [true, 1] + ); + }) + .catch(fail) + .then(done); + }); +}); From e4d788933d731760327cc7a2eb539fb44af8ffea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Fri, 7 Jul 2017 17:03:48 -0400 Subject: [PATCH 09/20] fix panning for ternary lines under `cliponaxis: false` --- src/plots/ternary/ternary.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index 944a8e1c433..3ddd7acffc9 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -292,6 +292,8 @@ proto.adjustLayout = function(ternaryLayout, graphSize) { _this.plotContainer.selectAll('.scatterlayer,.maplayer') .attr('transform', plotTransform); + _this.clipDefRelative.attr('transform', null); + // TODO: shift axes to accommodate linewidth*sin(30) tick mark angle var bTransform = 'translate(' + x0 + ',' + (y0 + h) + ')'; @@ -616,6 +618,9 @@ proto.initInteractions = function() { _this.plotContainer.selectAll('.scatterlayer,.maplayer') .attr('transform', plotTransform); + var plotTransform2 = 'translate(' + -dx + ',' + -dy + ')'; + _this.clipDefRelative.attr('transform', plotTransform2) + // move the ticks _this.aaxis.range = [mins.a, _this.sum - mins.b - mins.c]; _this.baxis.range = [_this.sum - mins.a - mins.c, mins.b]; From 091473bdc906439f6092eb1b80228b20be1485b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Mon, 10 Jul 2017 13:57:01 -0400 Subject: [PATCH 10/20] fix lint --- src/plots/ternary/ternary.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index 3ddd7acffc9..0d4431f7a1a 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -619,7 +619,7 @@ proto.initInteractions = function() { .attr('transform', plotTransform); var plotTransform2 = 'translate(' + -dx + ',' + -dy + ')'; - _this.clipDefRelative.attr('transform', plotTransform2) + _this.clipDefRelative.attr('transform', plotTransform2); // move the ticks _this.aaxis.range = [mins.a, _this.sum - mins.b - mins.c]; From 9becb7cc1bd6508f2d73f5ec231642763c1fba36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 11 Jul 2017 09:58:34 -0400 Subject: [PATCH 11/20] hide points using `display: 'none'` instead of visibility attr - `display: 'none'` completely removes the elements from the rendering pipeline as is likely faster is all browsers. --- src/components/drawing/index.js | 4 ++-- test/jasmine/assets/custom_assertions.js | 8 ++++---- test/jasmine/tests/scatter_test.js | 16 ++++++++-------- test/jasmine/tests/scatterternary_test.js | 22 +++++++++++----------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index 1aa1a488e95..bc46de2c0b6 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -95,8 +95,8 @@ drawing.translatePoints = function(s, xa, ya) { drawing.hideOutsideRangePoint = function(d, sel, xa, ya) { sel.attr( - 'visibility', - xa.isPtWithinRange(d) && ya.isPtWithinRange(d) ? null : 'hidden' + 'display', + xa.isPtWithinRange(d) && ya.isPtWithinRange(d) ? null : 'none' ); }; diff --git a/test/jasmine/assets/custom_assertions.js b/test/jasmine/assets/custom_assertions.js index 315bff917be..756c0b8f290 100644 --- a/test/jasmine/assets/custom_assertions.js +++ b/test/jasmine/assets/custom_assertions.js @@ -64,12 +64,12 @@ exports.assertClip = function(sel, isClipped, size, msg) { }; -exports.assertNodeVisibility = function(sel, expectation, msg) { +exports.assertNodeDisplay = function(sel, expectation, msg) { expect(sel.size()) - .toBe(expectation.length, msg + ' visibility (selection size)'); + .toBe(expectation.length, msg + ' display (selection size)'); sel.each(function(d, i) { - expect(d3.select(this).attr('visibility')) - .toBe(expectation[i], msg + ' visibility ' + '(item ' + i + ')'); + expect(d3.select(this).attr('display')) + .toBe(expectation[i], msg + ' display ' + '(item ' + i + ')'); }); }; diff --git a/test/jasmine/tests/scatter_test.js b/test/jasmine/tests/scatter_test.js index 2808e9bb6b8..7dfd8572452 100644 --- a/test/jasmine/tests/scatter_test.js +++ b/test/jasmine/tests/scatter_test.js @@ -12,7 +12,7 @@ var customAssertions = require('../assets/custom_assertions'); var fail = require('../assets/fail_test'); var assertClip = customAssertions.assertClip; -var assertNodeVisibility = customAssertions.assertNodeVisibility; +var assertNodeDisplay = customAssertions.assertNodeDisplay; describe('Test scatter', function() { 'use strict'; @@ -690,7 +690,7 @@ describe('Test scatter *clipnaxis*', function() { // add lines fig.data[0].mode = 'markers+lines+text'; - function _assert(layerClips, nodeVisibilities, errorBarClips, lineClips) { + function _assert(layerClips, nodeDisplays, errorBarClips, lineClips) { var subplotLayer = d3.select('.plot'); var scatterLayer = subplotLayer.select('.scatterlayer'); @@ -698,14 +698,14 @@ describe('Test scatter *clipnaxis*', function() { assertClip(subplotLayer.select('.barlayer'), layerClips[1], 1, 'bar layer'); assertClip(scatterLayer, layerClips[2], 1, 'scatter layer'); - assertNodeVisibility( + assertNodeDisplay( scatterLayer.selectAll('.point'), - nodeVisibilities, + nodeDisplays, 'scatter points' ); - assertNodeVisibility( + assertNodeDisplay( scatterLayer.selectAll('.textpoint'), - nodeVisibilities, + nodeDisplays, 'scatter text points' ); @@ -779,7 +779,7 @@ describe('Test scatter *clipnaxis*', function() { .then(function() { _assert( [false, true, false], - [null, null, 'hidden', 'hidden', 'hidden', 'hidden'], + [null, null, 'none', 'none', 'none', 'none'], [true, 6], [true, 1] ); @@ -788,7 +788,7 @@ describe('Test scatter *clipnaxis*', function() { .then(function() { _assert( [false, true, false], - ['hidden', null, 'hidden', 'hidden', 'hidden', 'hidden'], + ['none', null, 'none', 'none', 'none', 'none'], [true, 6], [true, 1] ); diff --git a/test/jasmine/tests/scatterternary_test.js b/test/jasmine/tests/scatterternary_test.js index 903f9cdf21e..da49b10a206 100644 --- a/test/jasmine/tests/scatterternary_test.js +++ b/test/jasmine/tests/scatterternary_test.js @@ -11,7 +11,7 @@ var customMatchers = require('../assets/custom_matchers'); var customAssertions = require('../assets/custom_assertions'); var assertClip = customAssertions.assertClip; -var assertNodeVisibility = customAssertions.assertNodeVisibility; +var assertNodeDisplay = customAssertions.assertNodeDisplay; describe('scatterternary defaults', function() { 'use strict'; @@ -386,21 +386,21 @@ describe('Test scatterternary *cliponaxis*', function() { var gd = createGraphDiv(); var fig = Lib.extendDeep({}, require('@mocks/ternary_markers.json')); - function _assert(layerClips, nodeVisibilities, lineClips) { + function _assert(layerClips, nodeDisplays, lineClips) { var frontLayer = d3.select('.frontplot'); var scatterLayer = d3.select('.scatterlayer'); assertClip(frontLayer, layerClips[0], 1, 'front layer'); assertClip(scatterLayer, layerClips[1], 1, 'scatter layer'); - assertNodeVisibility( + assertNodeDisplay( scatterLayer.selectAll('.point'), - nodeVisibilities, + nodeDisplays, 'scatter points' ); - assertNodeVisibility( + assertNodeDisplay( scatterLayer.selectAll('.textpoint'), - nodeVisibilities, + nodeDisplays, 'scatter text points' ); @@ -415,7 +415,7 @@ describe('Test scatterternary *cliponaxis*', function() { .then(function() { _assert( [false, false], - [null, 'hidden', null, null, null, null, null, null, 'hidden', 'hidden', 'hidden'], + [null, 'none', null, null, null, null, null, null, 'none', 'none', 'none'], [true, 1] ); return Plotly.restyle(gd, 'visible', 'legendonly'); @@ -439,7 +439,7 @@ describe('Test scatterternary *cliponaxis*', function() { .then(function() { _assert( [false, false], - [null, 'hidden', null, null, null, null, null, null, 'hidden', 'hidden', 'hidden'], + [null, 'none', null, null, null, null, null, null, 'none', 'none', 'none'], [true, 1] ); return Plotly.relayout(gd, 'ternary.aaxis.min', 20); @@ -447,7 +447,7 @@ describe('Test scatterternary *cliponaxis*', function() { .then(function() { _assert( [false, false], - [null, 'hidden', null, 'hidden', 'hidden', 'hidden', null, 'hidden', 'hidden', 'hidden', 'hidden'], + [null, 'none', null, 'none', 'none', 'none', null, 'none', 'none', 'none', 'none'], [true, 1] ); return Plotly.relayout(gd, 'ternary.baxis.min', 40); @@ -455,7 +455,7 @@ describe('Test scatterternary *cliponaxis*', function() { .then(function() { _assert( [false, false], - ['hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden', null, 'hidden', 'hidden', 'hidden', 'hidden'], + ['none', 'none', 'none', 'none', 'none', 'none', null, 'none', 'none', 'none', 'none'], [true, 1] ); return Plotly.relayout(gd, 'ternary.caxis.min', 30); @@ -463,7 +463,7 @@ describe('Test scatterternary *cliponaxis*', function() { .then(function() { _assert( [false, false], - ['hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden', 'hidden'], + ['none', 'none', 'none', 'none', 'none', 'none', 'none', 'none', 'none', 'none', 'none'], [true, 1] ); return Plotly.relayout(gd, { From 62ab845e7f97359220ef9ce9a1f57e3cbbb4a794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 11 Jul 2017 10:14:39 -0400 Subject: [PATCH 12/20] remove obsolete code from old d.{xp|yp} optimization attempt --- src/components/drawing/index.js | 5 ++--- src/traces/scatter/plot.js | 7 ++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index bc46de2c0b6..344cfeebe75 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -68,9 +68,8 @@ drawing.setRect = function(s, x, y, w, h) { * false if selection could not get translated */ drawing.translatePoint = function(d, sel, xa, ya) { - // put xp and yp into d if pixel scaling is already done - var x = d.xp = xa.c2p(d.x); - var y = d.yp = ya.c2p(d.y); + var x = xa.c2p(d.x); + var y = ya.c2p(d.y); if(isNumeric(x) && isNumeric(y) && sel.node()) { // for multiline text this works better diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js index cdcd47cf2fe..4010df2d35c 100644 --- a/src/traces/scatter/plot.js +++ b/src/traces/scatter/plot.js @@ -501,13 +501,10 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition join.selectAll('text') .call(Drawing.textPointStyle, trace, gd) .each(function(d) { - // This just *has* to be totally custom becuase of SVG text positioning :( // It's obviously copied from translatePoint; we just can't use that - // - // put xp and yp into d if pixel scaling is already done - var x = d.xp || xa.c2p(d.x), - y = d.yp || ya.c2p(d.y); + var x = xa.c2p(d.x); + var y = ya.c2p(d.y); d3.select(this).selectAll('tspan.line').each(function() { transition(d3.select(this)).attr({x: x, y: y}); From 0496144bcdff6c31e7c5a81952f8bc76d048d5cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 11 Jul 2017 11:26:59 -0400 Subject: [PATCH 13/20] stash layer clipId value (null or same as clipId) - to DRY up Drawing.setClipUrl calls downstream, where `if(plotinfo._hasClipOnAxisFalse)` statements are removed. --- src/components/errorbars/plot.js | 5 +---- src/plot_api/subroutines.js | 6 +++++- src/traces/scatter/plot.js | 8 ++------ src/traces/scatterternary/plot.js | 3 +-- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/components/errorbars/plot.js b/src/components/errorbars/plot.js index 74627ffa934..227fe4a99ca 100644 --- a/src/components/errorbars/plot.js +++ b/src/components/errorbars/plot.js @@ -61,10 +61,7 @@ module.exports = function plot(traces, plotinfo, transitionOpts) { .style('opacity', 1); } - errorbars.call( - Drawing.setClipUrl, - plotinfo._hasClipOnAxisFalse ? plotinfo.clipId : null - ); + Drawing.setClipUrl(errorbars, plotinfo.layerClipId); errorbars.each(function(d) { var errorbar = d3.select(this); diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index ef09f4a8788..8e6d5c71251 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -178,7 +178,7 @@ exports.lsInner = function(gd) { layerClipId = null; } - plotinfo.plot.call(Drawing.setClipUrl, plotClipId); + Drawing.setClipUrl(plotinfo.plot, plotClipId); for(i = 0; i < cartesianConstants.layers.length; i++) { var layer = cartesianConstants.layers[i]; @@ -187,6 +187,10 @@ exports.lsInner = function(gd) { } } + // stash layer clipId value (null or same as clipId) + // to DRY up Drawing.setClipUrl calls downstream + plotinfo.layerClipId = layerClipId; + var xlw = Drawing.crispRound(gd, xa.linewidth, 1), ylw = Drawing.crispRound(gd, ya.linewidth, 1), xp = gs.p + ylw, diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js index 4010df2d35c..2155cc03e96 100644 --- a/src/traces/scatter/plot.js +++ b/src/traces/scatter/plot.js @@ -139,9 +139,7 @@ function createFills(gd, scatterlayer, plotinfo) { trace._ownFill = null; } - if(plotinfo._hasClipOnAxisFalse) { - tr.selectAll('.js-fill').call(Drawing.setClipUrl, plotinfo.clipId); - } + tr.selectAll('.js-fill').call(Drawing.setClipUrl, plotinfo.layerClipId); }); } @@ -328,9 +326,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition .call(Drawing.lineGroupStyle) .each(makeUpdate(true)); - if(plotinfo._hasClipOnAxisFalse) { - Drawing.setClipUrl(lineJoin, plotinfo.clipId); - } + Drawing.setClipUrl(lineJoin, plotinfo.layerClipId); if(segments.length) { if(ownFillEl3) { diff --git a/src/traces/scatterternary/plot.js b/src/traces/scatterternary/plot.js index 3a583025a60..8e4096d9fc4 100644 --- a/src/traces/scatterternary/plot.js +++ b/src/traces/scatterternary/plot.js @@ -23,8 +23,7 @@ module.exports = function plot(ternary, moduleCalcData) { xaxis: ternary.xaxis, yaxis: ternary.yaxis, plot: plotContainer, - clipId: ternary.clipIdRelative, - _hasClipOnAxisFalse: ternary._hasClipOnAxisFalse + layerClipId: ternary._hasClipOnAxisFalse ? ternary.clipIdRelative : null }; // add ref to ternary subplot object in fullData traces From 7b62b73d9cc89c14c0a1167076ddeeb71b135eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 11 Jul 2017 17:22:42 -0400 Subject: [PATCH 14/20] handle cliponaxis false/true traces coexisting on same subplot --- src/traces/scatter/plot.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js index 2155cc03e96..eec2d39e05b 100644 --- a/src/traces/scatter/plot.js +++ b/src/traces/scatter/plot.js @@ -404,8 +404,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition var trace = d[0].trace, s = d3.select(this), showMarkers = subTypes.hasMarkers(trace), - showText = subTypes.hasText(trace), - hasClipOnAxisFalse = trace.cliponaxis === false; + showText = subTypes.hasText(trace); var keyFunc = getKeyFunc(trace), markerFilter = hideFilter, @@ -450,7 +449,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition if(hasNode) { Drawing.singlePointStyle(d, sel, trace, markerScale, lineScale, gd); - if(hasClipOnAxisFalse) { + if(plotinfo.layerClipId) { Drawing.hideOutsideRangePoint(d, sel, xa, ya); } @@ -486,7 +485,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition hasNode = Drawing.translatePoint(d, sel, xa, ya); if(hasNode) { - if(hasClipOnAxisFalse) { + if(plotinfo.layerClipId) { Drawing.hideOutsideRangePoint(d, g, xa, ya); } } else { @@ -525,6 +524,13 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition .each(makePoints); join.exit().remove(); + + // lastly, clip points groups of `cliponaxis !== false` traces + // on `plotinfo._hasClipOnAxisFalse === true` subplots + join.each(function(d) { + var hasClipOnAxisFalse = d[0].trace.cliponaxis === false; + Drawing.setClipUrl(d3.select(this), hasClipOnAxisFalse ? null : plotinfo.layerClipId); + }); } function selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll) { From 02b9fbc10b253656be0d49f4ecd41e017e2d466a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 11 Jul 2017 17:24:26 -0400 Subject: [PATCH 15/20] rename 'layers' cartesian constant -> 'traceLayerClasses' - which is more meaningul - other misc lint --- src/plot_api/subroutines.js | 4 ++-- src/plots/cartesian/constants.js | 4 ++-- src/plots/cartesian/index.js | 4 ++-- test/jasmine/tests/scatter_test.js | 5 ++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 8e6d5c71251..05ffc99a260 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -180,8 +180,8 @@ exports.lsInner = function(gd) { Drawing.setClipUrl(plotinfo.plot, plotClipId); - for(i = 0; i < cartesianConstants.layers.length; i++) { - var layer = cartesianConstants.layers[i]; + for(i = 0; i < cartesianConstants.traceLayerClasses.length; i++) { + var layer = cartesianConstants.traceLayerClasses[i]; if(layer !== 'scatterlayer') { plotinfo.plot.selectAll('g.' + layer).call(Drawing.setClipUrl, layerClipId); } diff --git a/src/plots/cartesian/constants.js b/src/plots/cartesian/constants.js index f502bcee5f5..e9a424a5ffc 100644 --- a/src/plots/cartesian/constants.js +++ b/src/plots/cartesian/constants.js @@ -51,14 +51,14 @@ module.exports = { DFLTRANGEX: [-1, 6], DFLTRANGEY: [-1, 4], - // Layers to keep plot types in the right order. + // Layers to keep trace types in the right order. // from back to front: // 1. heatmaps, 2D histos and contour maps // 2. bars / 1D histos // 3. errorbars for bars and scatter // 4. scatter // 5. box plots - layers: [ + traceLayerClasses: [ 'imagelayer', 'maplayer', 'barlayer', diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 7548a608251..0005ca1760e 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -339,8 +339,8 @@ function makeSubplotLayer(plotinfo) { // common attributes for all subplots, overlays or not - for(var i = 0; i < constants.layers.length; i++) { - joinLayer(plotinfo.plot, 'g', constants.layers[i]); + for(var i = 0; i < constants.traceLayerClasses.length; i++) { + joinLayer(plotinfo.plot, 'g', constants.traceLayerClasses[i]); } plotinfo.xlines diff --git a/test/jasmine/tests/scatter_test.js b/test/jasmine/tests/scatter_test.js index 7dfd8572452..ec42e45a2e4 100644 --- a/test/jasmine/tests/scatter_test.js +++ b/test/jasmine/tests/scatter_test.js @@ -675,7 +675,7 @@ describe('scatter hoverPoints', function() { }); }); -describe('Test scatter *clipnaxis*', function() { +describe('Test scatter *clipnaxis*:', function() { afterEach(destroyGraphDiv); it('should show/hide point/text/errorbars in clipped and non-clipped layers', function(done) { @@ -684,7 +684,7 @@ describe('Test scatter *clipnaxis*', function() { var xRange0 = fig.layout.xaxis.range.slice(); var yRange0 = fig.layout.yaxis.range.slice(); - // only show *cliponaxis: false* trace + // only show 1 *cliponaxis: false* trace fig.data = [fig.data[2]]; // add lines @@ -805,5 +805,4 @@ describe('Test scatter *clipnaxis*', function() { .catch(fail) .then(done); }); - }); From 1c85de675ada7c46b688e49034612439a4178dc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 11 Jul 2017 17:25:57 -0400 Subject: [PATCH 16/20] add 'layer' axis attribute - has 2 possible values: 'above traces' and 'below traces' for now, but opens the door for other values down the road. --- src/plots/cartesian/constants.js | 7 ++- src/plots/cartesian/index.js | 51 ++++++++++++---- src/plots/cartesian/layout_attributes.js | 12 ++++ src/plots/cartesian/position_defaults.js | 2 + src/plots/plots.js | 25 +++++--- test/jasmine/tests/cartesian_test.js | 75 ++++++++++++++++++++++++ test/jasmine/tests/plots_test.js | 10 +++- 7 files changed, 162 insertions(+), 20 deletions(-) diff --git a/src/plots/cartesian/constants.js b/src/plots/cartesian/constants.js index e9a424a5ffc..80c6f48af50 100644 --- a/src/plots/cartesian/constants.js +++ b/src/plots/cartesian/constants.js @@ -65,5 +65,10 @@ module.exports = { 'carpetlayer', 'boxlayer', 'scatterlayer' - ] + ], + + layerValue2layerClass: { + 'above traces': 'above', + 'below traces': 'below' + } }; diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 0005ca1760e..1b7ae929c9b 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -296,6 +296,8 @@ function makeSubplotData(gd) { function makeSubplotLayer(plotinfo) { var plotgroup = plotinfo.plotgroup; var id = plotinfo.id; + var xLayer = constants.layerValue2layerClass[plotinfo.xaxis.layer]; + var yLayer = constants.layerValue2layerClass[plotinfo.yaxis.layer]; if(!plotinfo.mainplot) { var backLayer = joinLayer(plotgroup, 'g', 'layer-subplot'); @@ -308,19 +310,36 @@ function makeSubplotLayer(plotinfo) { plotinfo.zerolinelayer = joinLayer(plotgroup, 'g', 'zerolinelayer'); plotinfo.overzero = joinLayer(plotgroup, 'g', 'overzero'); + joinLayer(plotgroup, 'path', 'xlines-below'); + joinLayer(plotgroup, 'path', 'ylines-below'); + plotinfo.overlinesBelow = joinLayer(plotgroup, 'g', 'overlines-below'); + + joinLayer(plotgroup, 'g', 'xaxislayer-below'); + joinLayer(plotgroup, 'g', 'yaxislayer-below'); + plotinfo.overaxesBelow = joinLayer(plotgroup, 'g', 'overaxes-below'); + plotinfo.plot = joinLayer(plotgroup, 'g', 'plot'); plotinfo.overplot = joinLayer(plotgroup, 'g', 'overplot'); - plotinfo.xlines = joinLayer(plotgroup, 'path', 'xlines'); - plotinfo.ylines = joinLayer(plotgroup, 'path', 'ylines'); - plotinfo.overlines = joinLayer(plotgroup, 'g', 'overlines'); + joinLayer(plotgroup, 'path', 'xlines-above'); + joinLayer(plotgroup, 'path', 'ylines-above'); + plotinfo.overlinesAbove = joinLayer(plotgroup, 'g', 'overlines-above'); + + joinLayer(plotgroup, 'g', 'xaxislayer-above'); + joinLayer(plotgroup, 'g', 'yaxislayer-above'); + plotinfo.overaxesAbove = joinLayer(plotgroup, 'g', 'overaxes-above'); - plotinfo.xaxislayer = joinLayer(plotgroup, 'g', 'xaxislayer'); - plotinfo.yaxislayer = joinLayer(plotgroup, 'g', 'yaxislayer'); - plotinfo.overaxes = joinLayer(plotgroup, 'g', 'overaxes'); + // set refs to correct layers as determined by 'axis.layer' + plotinfo.xlines = plotgroup.select('.xlines-' + xLayer); + plotinfo.ylines = plotgroup.select('.ylines-' + yLayer); + plotinfo.xaxislayer = plotgroup.select('.xaxislayer-' + xLayer); + plotinfo.yaxislayer = plotgroup.select('.yaxislayer-' + yLayer); } else { var mainplotinfo = plotinfo.mainplotinfo; + var mainplotgroup = mainplotinfo.plotgroup; + var xId = id + '-x'; + var yId = id + '-y'; // now make the components of overlaid subplots // overlays don't have backgrounds, and append all @@ -330,11 +349,23 @@ function makeSubplotLayer(plotinfo) { plotinfo.gridlayer = joinLayer(mainplotinfo.overgrid, 'g', id); plotinfo.zerolinelayer = joinLayer(mainplotinfo.overzero, 'g', id); + joinLayer(mainplotinfo.overlinesBelow, 'path', xId); + joinLayer(mainplotinfo.overlinesBelow, 'path', yId); + joinLayer(mainplotinfo.overaxesBelow, 'g', xId); + joinLayer(mainplotinfo.overaxesBelow, 'g', yId); + plotinfo.plot = joinLayer(mainplotinfo.overplot, 'g', id); - plotinfo.xlines = joinLayer(mainplotinfo.overlines, 'path', id + '-x'); - plotinfo.ylines = joinLayer(mainplotinfo.overlines, 'path', id + '-y'); - plotinfo.xaxislayer = joinLayer(mainplotinfo.overaxes, 'g', id + '-x'); - plotinfo.yaxislayer = joinLayer(mainplotinfo.overaxes, 'g', id + '-y'); + + joinLayer(mainplotinfo.overlinesAbove, 'path', xId); + joinLayer(mainplotinfo.overlinesAbove, 'path', yId); + joinLayer(mainplotinfo.overaxesAbove, 'g', xId); + joinLayer(mainplotinfo.overaxesAbove, 'g', yId); + + // set refs to correct layers as determined by 'abovetraces' + plotinfo.xlines = mainplotgroup.select('.overlines-' + xLayer).select('.' + xId); + plotinfo.ylines = mainplotgroup.select('.overlines-' + yLayer).select('.' + yId); + plotinfo.xaxislayer = mainplotgroup.select('.overaxes-' + xLayer).select('.' + xId); + plotinfo.yaxislayer = mainplotgroup.select('.overaxes-' + yLayer).select('.' + yId); } // common attributes for all subplots, overlays or not diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js index ef75a5e91c5..62d5038a7fa 100644 --- a/src/plots/cartesian/layout_attributes.js +++ b/src/plots/cartesian/layout_attributes.js @@ -573,6 +573,18 @@ module.exports = { 'If *false*, this axis does not overlay any same-letter axes.' ].join(' ') }, + layer: { + valType: 'enumerated', + values: ['above traces', 'below traces'], + dflt: 'above traces', + role: 'info', + description: [ + 'Sets the layer on which this axis is displayed.', + 'If *above traces*, this axis is displayed above all the subplot\'s traces', + 'If *below traces*, this axis is displayed below all the subplot\'s traces,', + 'but above the grid lines.' + ].join(' ') + }, domain: { valType: 'info_array', role: 'info', diff --git a/src/plots/cartesian/position_defaults.js b/src/plots/cartesian/position_defaults.js index 94ea5ed4e73..ba797fbe038 100644 --- a/src/plots/cartesian/position_defaults.js +++ b/src/plots/cartesian/position_defaults.js @@ -59,5 +59,7 @@ module.exports = function handlePositionDefaults(containerIn, containerOut, coer Lib.noneOrAll(containerIn.domain, containerOut.domain, [0, 1]); } + coerce('layer'); + return containerOut; }; diff --git a/src/plots/plots.js b/src/plots/plots.js index 1a95bad876b..ab54d91ec7b 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -638,9 +638,11 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa var ids = Plotly.Axes.getSubplots(mockGd); for(var i = 0; i < ids.length; i++) { - var id = ids[i], - oldSubplot = oldSubplots[id], - plotinfo; + var id = ids[i]; + var oldSubplot = oldSubplots[id]; + var xaxis = Plotly.Axes.getFromId(mockGd, id, 'x'); + var yaxis = Plotly.Axes.getFromId(mockGd, id, 'y'); + var plotinfo; if(oldSubplot) { plotinfo = newSubplots[id] = oldSubplot; @@ -648,14 +650,23 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa if(plotinfo._scene2d) { plotinfo._scene2d.updateRefs(newFullLayout); } - } - else { + + if(plotinfo.xaxis.layer !== xaxis.layer) { + plotinfo.xlines.attr('d', null); + plotinfo.xaxislayer.selectAll('*').remove(); + } + + if(plotinfo.yaxis.layer !== yaxis.layer) { + plotinfo.ylines.attr('d', null); + plotinfo.yaxislayer.selectAll('*').remove(); + } + } else { plotinfo = newSubplots[id] = {}; plotinfo.id = id; } - plotinfo.xaxis = Plotly.Axes.getFromId(mockGd, id, 'x'); - plotinfo.yaxis = Plotly.Axes.getFromId(mockGd, id, 'y'); + plotinfo.xaxis = xaxis; + plotinfo.yaxis = yaxis; // By default, we clip at the subplot level, // but if one trace on a given subplot has *cliponaxis* set to false, diff --git a/test/jasmine/tests/cartesian_test.js b/test/jasmine/tests/cartesian_test.js index f700e378ed6..324d3789f87 100644 --- a/test/jasmine/tests/cartesian_test.js +++ b/test/jasmine/tests/cartesian_test.js @@ -403,4 +403,79 @@ describe('subplot creation / deletion:', function() { .catch(failTest) .then(done); }); + + it('should clear obsolete content out of axis layers when relayout\'ing *layer*', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/overlaying-axis-lines.json')); + + function assertPathDatum(sel, expected, msg) { + expect(sel.attr('d') === null ? false : true).toBe(expected, msg); + } + + function assertChildrenCnt(sel, expected, msg) { + expect(sel.selectAll('*').size()).toBe(expected, msg); + } + + function _assert(xBelow, yBelow, xAbove, yAbove) { + var g = d3.select('.subplot.xy'); + + assertPathDatum(g.select('.xlines-below'), xBelow[0], 'xlines below'); + assertChildrenCnt(g.select('.xaxislayer-below'), xBelow[1], 'xaxislayer below'); + + assertPathDatum(g.select('.ylines-below'), yBelow[0], 'ylines below'); + assertChildrenCnt(g.select('.yaxislayer-below'), yBelow[1], 'yaxislayer below'); + + assertPathDatum(g.select('.xlines-above'), xAbove[0], 'xlines above'); + assertChildrenCnt(g.select('.xaxislayer-above'), xAbove[1], 'xaxislayer above'); + + assertPathDatum(g.select('.ylines-above'), yAbove[0], 'ylines above'); + assertChildrenCnt(g.select('.yaxislayer-above'), yAbove[1], 'yaxislayer above'); + } + + Plotly.plot(gd, fig).then(function() { + _assert( + [false, 0], + [false, 0], + [true, 10], + [true, 10] + ); + return Plotly.relayout(gd, 'xaxis.layer', 'below traces'); + }) + .then(function() { + _assert( + [true, 10], + [false, 0], + [false, 0], + [true, 10] + ); + return Plotly.relayout(gd, 'yaxis.layer', 'below traces'); + }) + .then(function() { + _assert( + [true, 10], + [true, 10], + [false, 0], + [false, 0] + ); + return Plotly.relayout(gd, { 'xaxis.layer': null, 'yaxis.layer': null }); + }) + .then(function() { + _assert( + [false, 0], + [false, 0], + [true, 10], + [true, 10] + ); + return Plotly.relayout(gd, { 'xaxis.layer': 'below traces', 'yaxis.layer': 'below traces' }); + }) + .then(function() { + _assert( + [true, 10], + [true, 10], + [false, 0], + [false, 0] + ); + }) + .catch(failTest) + .then(done); + }); }); diff --git a/test/jasmine/tests/plots_test.js b/test/jasmine/tests/plots_test.js index b333989aeae..b95b6482db0 100644 --- a/test/jasmine/tests/plots_test.js +++ b/test/jasmine/tests/plots_test.js @@ -1,5 +1,6 @@ var Plotly = require('@lib/index'); var Plots = require('@src/plots/plots'); +var Lib = require('@src/lib'); var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); @@ -38,13 +39,18 @@ describe('Test Plots', function() { var oldFullLayout = { _plots: { xy: { plot: {} } }, - xaxis: { c2p: function() {} }, - yaxis: { _m: 20 }, + xaxis: { c2p: function() {}, layer: 'above traces' }, + yaxis: { _m: 20, layer: 'above traces' }, scene: { _scene: {} }, annotations: [{ _min: 10, }, { _max: 20 }], someFunc: function() {} }; + Lib.extendFlat(oldFullLayout._plots.xy, { + xaxis: oldFullLayout.xaxis, + yaxis: oldFullLayout.yaxis + }); + var newData = [{ type: 'scatter3d', z: [1, 2, 3, 4] From e44aa4d5d313574e630d56cda3bc9b8a97af4da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Tue, 11 Jul 2017 17:28:45 -0400 Subject: [PATCH 17/20] improve cliponaxis_false mock - show off `(x|y)axis.layer` - lock down behavior for `cliponaxis: true` traces on hasClipOnAxisFalse subplot - add annotations for more descriptive mock --- test/image/baselines/cliponaxis_false.png | Bin 37319 -> 50612 bytes test/image/mocks/cliponaxis_false.json | 67 ++++++++++++++++++++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/test/image/baselines/cliponaxis_false.png b/test/image/baselines/cliponaxis_false.png index aaa7ecb5d2df1b81253b089caf1945dc39753948..43f2dce84235734c4a0c5dd5cc23950ff44e5dc6 100644 GIT binary patch literal 50612 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 literal 37319 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 diff --git a/test/image/mocks/cliponaxis_false.json b/test/image/mocks/cliponaxis_false.json index 1053fc82087..22b38fd91c0 100644 --- a/test/image/mocks/cliponaxis_false.json +++ b/test/image/mocks/cliponaxis_false.json @@ -21,7 +21,7 @@ "opacity": 1 }, "text": "not
clipped", - "textposition": ["left", "left", "bottom", "right", "right", "top"], + "textposition": ["left", "left", "bottom", "bottom left", "top left", "top"], "error_y": { "array": [0.1, 0.2, 0.3, 0.1, 0.3, 0.2], "arrayminus": [0.1, 0.2, 0.1, 0.1, 0.1, 0.2] @@ -32,23 +32,82 @@ }, "cliponaxis": false + }, + { + "mode": "markers", + "x": [1, 1, 2, 3, 3, 2], + "y": [2, 1, 1, 1, 2, 2], + "marker": { + "size": [15, 20, 40, 25, 50, 40], + "opacity": 1 + }, + "cliponaxis": false, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "mode": "markers+text", + "x": [1, 1.5, 2.5, 3, 2.5, 1.5], + "y": [1.5, 1, 1, 1.5, 2, 2], + "marker": { + "size": 30 + }, + "text": "clipped
should not see this!", + "textposition": ["left", "bottom", "bottom", "right", "top", "top"], + "cliponaxis": true } ], "layout": { "xaxis": { + "domain": [0, 0.48], "range": [1, 3], "showline": true, "linewidth": 2, - "mirror": "all" + "mirror": "all", + "layer": "below traces" }, "yaxis": { "range": [1, 2], "showline": true, "linewidth": 2, - "mirror": "all" + "mirror": "all", + "layer": "below traces" + }, + "xaxis2": { + "anchor": "y2", + "range": [1, 3], + "domain": [0.52, 1], + "showline": true, + "linewidth": 2, + "mirror": "all", + "layer": "above traces" + }, + "yaxis2": { + "anchor": "x2", + "range": [1, 2], + "showline": true, + "linewidth": 2, + "mirror": "all", + "side": "right", + "layer": "above traces" }, + "annotations": [{ + "showarrow": false, + "xref": "paper", "yref": "paper", + "x": 0.24, "y": 0.5, + "xanchor": "center", + "text": "axis layer
below traces" + }, { + "showarrow": false, + "xref": "paper", "yref": "paper", + "x": 0.74, "y": 0.5, + "xanchor": "center", + "text": "axis layer
above traces" + }], "showlegend": false, "dragmode": "pan", - "hovermode": "closest" + "hovermode": "closest", + "height": 500, + "width": 800 } } From e84701f75ac30e093dfcb73341a3ec472b8643da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Wed, 12 Jul 2017 16:40:31 -0400 Subject: [PATCH 18/20] replace strict-d3 IIFE with browserify 'require' transform - which replaces all `require('d3')` with `require('./strict-d3')` - so that strict-d3.js can more easily be used across all our tools - activate when bundling plotly.js in jasmine tests, `npm run watch` and `npm run cibuild` - :hocho: - diff --git a/tasks/cibundle.js b/tasks/cibundle.js index cd5ff768a4c..6f6e13d90f0 100644 --- a/tasks/cibundle.js +++ b/tasks/cibundle.js @@ -16,6 +16,7 @@ var _bundle = require('./util/browserify_wrapper'); _bundle(constants.pathToPlotlyIndex, constants.pathToPlotlyBuild, { standalone: 'Plotly', pathToMinBundle: constants.pathToPlotlyDistMin, + debug: true }); // Browserify the geo assets diff --git a/tasks/util/browserify_wrapper.js b/tasks/util/browserify_wrapper.js index 0e13fa01874..7050706eb93 100644 --- a/tasks/util/browserify_wrapper.js +++ b/tasks/util/browserify_wrapper.js @@ -7,6 +7,7 @@ var UglifyJS = require('uglify-js'); var constants = require('./constants'); var compressAttributes = require('./compress_attributes'); var patchMinified = require('./patch_minified'); +var strictD3 = require('./strict_d3'); /** Convenience browserify wrapper * @@ -32,16 +33,20 @@ module.exports = function _bundle(pathToIndex, pathToBundle, opts) { opts = opts || {}; // do we output a minified file? - var pathToMinBundle = opts.pathToMinBundle, - outputMinified = !!pathToMinBundle && !opts.debug; + var pathToMinBundle = opts.pathToMinBundle; + var outputMinified = !!pathToMinBundle; var browserifyOpts = {}; browserifyOpts.standalone = opts.standalone; browserifyOpts.debug = opts.debug; browserifyOpts.transform = outputMinified ? [compressAttributes] : []; - var b = browserify(pathToIndex, browserifyOpts), - bundleWriteStream = fs.createWriteStream(pathToBundle); + if(opts.debug) { + browserifyOpts.transform.push(strictD3); + } + + var b = browserify(pathToIndex, browserifyOpts); + var bundleWriteStream = fs.createWriteStream(pathToBundle); bundleWriteStream.on('finish', function() { logger(pathToBundle); diff --git a/tasks/util/constants.js b/tasks/util/constants.js index 9c8b1aaf828..7cffc50cfe0 100644 --- a/tasks/util/constants.js +++ b/tasks/util/constants.js @@ -58,6 +58,7 @@ module.exports = { pathToTestDashboardBundle: path.join(pathToBuild, 'test_dashboard-bundle.js'), pathToImageViewerBundle: path.join(pathToBuild, 'image_viewer-bundle.js'), + pathToImageTest: pathToImageTest, pathToTestImageMocks: path.join(pathToImageTest, 'mocks/'), pathToTestImageBaselines: path.join(pathToImageTest, 'baselines/'), pathToTestImages: path.join(pathToBuild, 'test_images/'), diff --git a/tasks/util/strict_d3.js b/tasks/util/strict_d3.js new file mode 100644 index 00000000000..1e51ab8912b --- /dev/null +++ b/tasks/util/strict_d3.js @@ -0,0 +1,26 @@ +var path = require('path'); +var transformTools = require('browserify-transform-tools'); +var constants = require('./constants'); + +var pathToStrictD3Module = path.join( + constants.pathToImageTest, + 'strict-d3.js' +); + +/** + * Transform `require('d3')` expressions to `require(/path/to/strict-d3.js)` + */ + +module.exports = transformTools.makeRequireTransform('requireTransform', + { evaluateArguments: true, jsFilesOnly: true }, + function(args, opts, cb) { + var pathIn = args[0]; + var pathOut; + + if(pathIn === 'd3' && opts.file !== pathToStrictD3Module) { + pathOut = 'require(\'' + pathToStrictD3Module + '\')'; + } + + if(pathOut) return cb(null, pathOut); + else return cb(); + }); diff --git a/tasks/util/watchified_bundle.js b/tasks/util/watchified_bundle.js index e2ab22068ee..864716dca6f 100644 --- a/tasks/util/watchified_bundle.js +++ b/tasks/util/watchified_bundle.js @@ -7,6 +7,7 @@ var prettySize = require('prettysize'); var constants = require('./constants'); var common = require('./common'); var compressAttributes = require('./compress_attributes'); +var strictD3 = require('./strict_d3'); /** * Make a plotly.js browserify bundle function watched by watchify. @@ -22,7 +23,7 @@ module.exports = function makeWatchifiedBundle(onFirstBundleCallback) { var b = browserify(constants.pathToPlotlyIndex, { debug: true, standalone: 'Plotly', - transform: [compressAttributes], + transform: [strictD3, compressAttributes], cache: {}, packageCache: {}, plugin: [watchify] diff --git a/test/image/index.html b/test/image/index.html index 9907839a5d5..efa5c6d2edc 100644 --- a/test/image/index.html +++ b/test/image/index.html @@ -5,7 +5,6 @@ - diff --git a/test/image/strict-d3.js b/test/image/strict-d3.js index d54336e3dfb..a28a4eb033b 100644 --- a/test/image/strict-d3.js +++ b/test/image/strict-d3.js @@ -2,77 +2,45 @@ * strict-d3: wrap selection.style to prohibit specific incorrect style values * that are known to cause problems in IE (at least IE9) */ +'use strict'; -/* global Plotly:false */ -(function() { - 'use strict'; +var d3 = require('d3'); +var isNumeric = require('fast-isnumeric'); - var selProto = Plotly.d3.selection.prototype; +var selProto = d3.selection.prototype; +var originalSelStyle = selProto.style; - var originalSelStyle = selProto.style; +selProto.style = function() { + var sel = this; + var obj = arguments[0]; - selProto.style = function() { - var sel = this, - obj = arguments[0]; - - if(sel.size()) { - if(typeof obj === 'string') { - checkVal(sel, obj, arguments[1]); - } - else { - Object.keys(obj).forEach(function(key) { checkVal(sel, key, obj[key]); }); - } - } - - return originalSelStyle.apply(sel, arguments); - }; - - function checkVal(sel, key, val) { - if(typeof val === 'string') { - // in case of multipart styles (stroke-dasharray, margins, etc) - // test each part separately - val.split(/[, ]/g).forEach(function(valPart) { - var pxSplit = valPart.length - 2; - if(valPart.substr(pxSplit) === 'px' && !isNumeric(valPart.substr(0, pxSplit))) { - throw new Error('d3 selection.style called with value: ' + val); - } - }); - } - - // Microsoft browsers incl. "Edge" don't support CSS transform on SVG elements - if(key === 'transform' && sel.node() instanceof SVGElement) { - throw new Error('d3 selection.style called on an SVG element with key: ' + key); + if(sel.size()) { + if(typeof obj === 'string') { + checkStyleVal(sel, obj, arguments[1]); + } else { + Object.keys(obj).forEach(function(key) { checkStyleVal(sel, key, obj[key]); }); } } - // below ripped from fast-isnumeric so I don't need to build this file - - function allBlankCharCodes(str) { - var l = str.length, - a; - for(var i = 0; i < l; i++) { - a = str.charCodeAt(i); - if((a < 9 || a > 13) && (a !== 32) && (a !== 133) && (a !== 160) && - (a !== 5760) && (a !== 6158) && (a < 8192 || a > 8205) && - (a !== 8232) && (a !== 8233) && (a !== 8239) && (a !== 8287) && - (a !== 8288) && (a !== 12288) && (a !== 65279)) { - return false; + return originalSelStyle.apply(sel, arguments); +}; + +function checkStyleVal(sel, key, val) { + if(typeof val === 'string') { + // in case of multipart styles (stroke-dasharray, margins, etc) + // test each part separately + val.split(/[, ]/g).forEach(function(valPart) { + var pxSplit = valPart.length - 2; + if(valPart.substr(pxSplit) === 'px' && !isNumeric(valPart.substr(0, pxSplit))) { + throw new Error('d3 selection.style called with value: ' + val); } - } - return true; + }); } - function isNumeric(n) { - var type = typeof n; - if(type === 'string') { - var original = n; - n = +n; - // whitespace strings cast to zero - filter them out - if(n === 0 && allBlankCharCodes(original)) return false; - } - else if(type !== 'number') return false; - - return n - n < 1; + // Microsoft browsers incl. "Edge" don't support CSS transform on SVG elements + if(key === 'transform' && sel.node() instanceof SVGElement) { + throw new Error('d3 selection.style called on an SVG element with key: ' + key); } +} -})(); +module.exports = d3; diff --git a/test/jasmine/karma.conf.js b/test/jasmine/karma.conf.js index 8f8af91fbf7..43408f66367 100644 --- a/test/jasmine/karma.conf.js +++ b/test/jasmine/karma.conf.js @@ -95,6 +95,7 @@ if(isFullSuite) { } var pathToShortcutPath = path.join(__dirname, '..', '..', 'tasks', 'util', 'shortcut_paths.js'); +var pathToStrictD3 = path.join(__dirname, '..', '..', 'tasks', 'util', 'strict_d3.js'); var pathToMain = path.join(__dirname, '..', '..', 'lib', 'index.js'); var pathToJQuery = path.join(__dirname, 'assets', 'jquery-1.8.3.min.js'); var pathToIE9mock = path.join(__dirname, 'assets', 'ie9_mock.js'); @@ -193,7 +194,7 @@ func.defaultConfig = { }, browserify: { - transform: [pathToShortcutPath], + transform: [pathToStrictD3, pathToShortcutPath], extensions: ['.js'], watch: !argv.nowatch, debug: true From e234827dac252743bb68329487cabaee80dab953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Wed, 12 Jul 2017 16:44:17 -0400 Subject: [PATCH 19/20] fixes #1873 - apply transform on child - instead of on element itself - see https://codepen.io/etpinard/pen/eRbxrp for more info on the problem - lock down this rule by adding condition in strict-d3.js --- src/plots/cartesian/dragbox.js | 2 +- src/plots/cartesian/transition_axes.js | 4 ++-- src/plots/ternary/ternary.js | 4 ++-- test/image/strict-d3.js | 24 ++++++++++++++++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index fa93d51e5b4..c1e2b4cda46 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -743,7 +743,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { var plotDx = xa2._offset - clipDx / xScaleFactor2, plotDy = ya2._offset - clipDy / yScaleFactor2; - fullLayout._defs.selectAll('#' + subplot.clipId) + fullLayout._defs.select('#' + subplot.clipId + '> rect') .call(Drawing.setTranslate, clipDx, clipDy) .call(Drawing.setScale, xScaleFactor2, yScaleFactor2); diff --git a/src/plots/cartesian/transition_axes.js b/src/plots/cartesian/transition_axes.js index 37557d6cc5f..2994b07efe9 100644 --- a/src/plots/cartesian/transition_axes.js +++ b/src/plots/cartesian/transition_axes.js @@ -137,7 +137,7 @@ module.exports = function transitionAxes(gd, newLayout, transitionOpts, makeOnCo var xa2 = subplot.xaxis; var ya2 = subplot.yaxis; - fullLayout._defs.selectAll('#' + subplot.clipId) + fullLayout._defs.select('#' + subplot.clipId + '> rect') .call(Drawing.setTranslate, 0, 0) .call(Drawing.setScale, 1, 1); @@ -221,7 +221,7 @@ module.exports = function transitionAxes(gd, newLayout, transitionOpts, makeOnCo var plotDx = xa2._offset - fracDx, plotDy = ya2._offset - fracDy; - fullLayout._defs.selectAll('#' + subplot.clipId) + fullLayout._defs.select('#' + subplot.clipId + '> rect') .call(Drawing.setTranslate, clipDx, clipDy) .call(Drawing.setScale, 1 / xScaleFactor, 1 / yScaleFactor); diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index 0d4431f7a1a..c5840d5e9ec 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -292,7 +292,7 @@ proto.adjustLayout = function(ternaryLayout, graphSize) { _this.plotContainer.selectAll('.scatterlayer,.maplayer') .attr('transform', plotTransform); - _this.clipDefRelative.attr('transform', null); + _this.clipDefRelative.select('path').attr('transform', null); // TODO: shift axes to accommodate linewidth*sin(30) tick mark angle @@ -619,7 +619,7 @@ proto.initInteractions = function() { .attr('transform', plotTransform); var plotTransform2 = 'translate(' + -dx + ',' + -dy + ')'; - _this.clipDefRelative.attr('transform', plotTransform2); + _this.clipDefRelative.select('path').attr('transform', plotTransform2); // move the ticks _this.aaxis.range = [mins.a, _this.sum - mins.b - mins.c]; diff --git a/test/image/strict-d3.js b/test/image/strict-d3.js index a28a4eb033b..fec9f4cbec0 100644 --- a/test/image/strict-d3.js +++ b/test/image/strict-d3.js @@ -8,8 +8,24 @@ var d3 = require('d3'); var isNumeric = require('fast-isnumeric'); var selProto = d3.selection.prototype; +var originalSelAttr = selProto.attr; var originalSelStyle = selProto.style; +selProto.attr = function() { + var sel = this; + var obj = arguments[0]; + + if(sel.size()) { + if(typeof obj === 'string') { + checkAttrVal(sel, obj, arguments[1]); + } else { + Object.keys(obj).forEach(function(key) { checkAttrVal(sel, key, obj[key]); }); + } + } + + return originalSelAttr.apply(sel, arguments); +}; + selProto.style = function() { var sel = this; var obj = arguments[0]; @@ -25,6 +41,14 @@ selProto.style = function() { return originalSelStyle.apply(sel, arguments); }; +function checkAttrVal(sel, key) { + // setting the transform attribute on a does not + // work in Chrome, IE and Edge + if(sel.node().nodeName === 'clipPath' && key === 'transform') { + throw new Error('d3 selection.attr called with key \'transform\' on a clipPath node'); + } +} + function checkStyleVal(sel, key, val) { if(typeof val === 'string') { // in case of multipart styles (stroke-dasharray, margins, etc) From 6f494c692f70c8d2714d1c4457b9dbbd5da2eb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Etienne=20T=C3=A9treault-Pinard?= Date: Wed, 12 Jul 2017 18:04:15 -0400 Subject: [PATCH 20/20] talk about 'cliponaxis' in axis 'layer' description and vice-versa --- src/plots/cartesian/layout_attributes.js | 4 +++- src/traces/scatter/attributes.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js index 62d5038a7fa..90855d7452a 100644 --- a/src/plots/cartesian/layout_attributes.js +++ b/src/plots/cartesian/layout_attributes.js @@ -582,7 +582,9 @@ module.exports = { 'Sets the layer on which this axis is displayed.', 'If *above traces*, this axis is displayed above all the subplot\'s traces', 'If *below traces*, this axis is displayed below all the subplot\'s traces,', - 'but above the grid lines.' + 'but above the grid lines.', + 'Useful when used together with scatter-like traces with `cliponaxis`', + 'set to *false* to show markers and/or text nodes above this axis.' ].join(' ') }, domain: { diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index 2076f1012b7..f48092247b3 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -186,7 +186,9 @@ module.exports = { editType: 'doplot', description: [ 'Determines whether or not markers and text nodes', - 'are clipped about the subplot axes.' + 'are clipped about the subplot axes.', + 'To show markers and text nodes above axis lines and tick labels,', + 'make sure to set `xaxis.layer` and `yaxis.layer` to *below traces*.' ].join(' ') },