diff --git a/lib/carpet.js b/lib/carpet.js new file mode 100644 index 00000000000..83184629ebe --- /dev/null +++ b/lib/carpet.js @@ -0,0 +1,11 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = require('../src/traces/carpet'); diff --git a/lib/contourcarpet.js b/lib/contourcarpet.js new file mode 100644 index 00000000000..43cb6831955 --- /dev/null +++ b/lib/contourcarpet.js @@ -0,0 +1,11 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = require('../src/traces/contourcarpet'); diff --git a/lib/index.js b/lib/index.js index 150bece3355..c16212d23ba 100644 --- a/lib/index.js +++ b/lib/index.js @@ -36,6 +36,10 @@ Plotly.register([ require('./scattermapbox'), + require('./carpet'), + require('./scattercarpet'), + require('./contourcarpet'), + require('./ohlc'), require('./candlestick') ]); diff --git a/lib/scattercarpet.js b/lib/scattercarpet.js new file mode 100644 index 00000000000..7039812fd7c --- /dev/null +++ b/lib/scattercarpet.js @@ -0,0 +1,11 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = require('../src/traces/scattercarpet'); diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index fa995b0641e..7a5a9bc1fe5 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -678,3 +678,13 @@ drawing.setPointGroupScale = function(selection, x, y) { return scale; }; + +drawing.measureText = function(tester, text, font) { + var dummyText = tester.append('text') + .text(text) + .call(drawing.font, font); + + var bbox = drawing.bBox(dummyText.node()); + dummyText.remove(); + return bbox; +}; diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index beb5f58a1c8..663bbc13e0f 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -510,6 +510,8 @@ function handleClick(g, gd, numClicks) { for(i = 0; i < fullData.length; i++) { allTraces.push(i); + // Allow the legendonly state through for *all* trace types (including + // carpet for which it's overridden with true/false in supplyDefaults) traceVisibility.push('legendonly'); } @@ -540,7 +542,11 @@ function handleClick(g, gd, numClicks) { if(sameAsLast) { traceVisibility = true; } - Plotly.restyle(gd, 'visible', traceVisibility, allTraces); + var visibilityUpdates = []; + for(i = 0; i < fullData.length; i++) { + visibilityUpdates.push(allTraces[i]); + } + Plotly.restyle(gd, 'visible', traceVisibility, visibilityUpdates); } } } diff --git a/src/components/legend/style.js b/src/components/legend/style.js index c3c2101e62e..50b1125dd71 100644 --- a/src/components/legend/style.js +++ b/src/components/legend/style.js @@ -65,6 +65,11 @@ function styleLines(d) { showFill = trace.visible && trace.fill && trace.fill !== 'none', showLine = subTypes.hasLines(trace); + if(trace && trace._module && trace._module.name === 'contourcarpet') { + showLine = trace.contours.showlines; + showFill = trace.contours.coloring === 'fill'; + } + var fill = d3.select(this).select('.legendfill').selectAll('path') .data(showFill ? [d] : []); fill.enter().append('path').classed('js-fill', true); diff --git a/src/lib/ensure_array.js b/src/lib/ensure_array.js new file mode 100644 index 00000000000..222b4dc2aae --- /dev/null +++ b/src/lib/ensure_array.js @@ -0,0 +1,27 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/* + * Ensures an array has the right amount of storage space. If it doesn't + * exist, it creates an array. If it does exist, it returns it if too + * short or truncates it in-place. + * + * The goal is to just reuse memory to avoid a bit of excessive garbage + * collection. + */ +module.exports = function ensureArray(out, n) { + if(!Array.isArray(out)) out = []; + + // If too long, truncate. (If too short, it will grow + // automatically so we don't care about that case) + out.length = n; + + return out; +}; diff --git a/src/lib/index.js b/src/lib/index.js index e1b6475a29b..21ac36e6668 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -19,6 +19,7 @@ lib.isArray = require('./is_array'); lib.mod = require('./mod'); lib.toLogRange = require('./to_log_range'); lib.relinkPrivateKeys = require('./relink_private'); +lib.ensureArray = require('./ensure_array'); var coerceModule = require('./coerce'); lib.valObjects = coerceModule.valObjects; diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 2494c9d76e2..c42722b1ef5 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -1294,6 +1294,7 @@ function _restyle(gd, aobj, _traces) { 'autobinx', 'nbinsx', 'xbins', 'xbins.start', 'xbins.end', 'xbins.size', 'autobiny', 'nbinsy', 'ybins', 'ybins.start', 'ybins.end', 'ybins.size', 'autocontour', 'ncontours', 'contours', 'contours.coloring', + 'contours.operation', 'contours.value', 'contours.type', 'contours.value[0]', 'contours.value[1]', 'error_y', 'error_y.visible', 'error_y.value', 'error_y.type', 'error_y.traceref', 'error_y.array', 'error_y.symmetric', 'error_y.arrayminus', 'error_y.valueminus', 'error_y.tracerefminus', @@ -1311,9 +1312,31 @@ function _restyle(gd, aobj, _traces) { 'line.showscale', 'line.cauto', 'line.autocolorscale', 'line.reversescale', 'marker.line.showscale', 'marker.line.cauto', 'marker.line.autocolorscale', 'marker.line.reversescale', 'xcalendar', 'ycalendar', - 'cumulative', 'cumulative.enabled', 'cumulative.direction', 'cumulative.currentbin' + 'cumulative', 'cumulative.enabled', 'cumulative.direction', 'cumulative.currentbin', + 'a0', 'da', 'b0', 'db', 'atype', 'btype', + 'cheaterslope', 'carpet', 'sum', ]; + var carpetAxisAttributes = [ + 'color', 'smoothing', 'title', 'titlefont', 'titlefont.size', 'titlefont.family', + 'titlefont.color', 'titleoffset', 'type', 'autorange', 'rangemode', 'range', + 'fixedrange', 'cheatertype', 'tickmode', 'nticks', 'tickvals', 'ticktext', + 'ticks', 'mirror', 'ticklen', 'tickwidth', 'tickcolor', 'showticklabels', + 'tickfont', 'tickfont.size', 'tickfont.family', 'tickfont.color', 'tickprefix', + 'showtickprefix', 'ticksuffix', 'showticksuffix', 'showexponent', 'exponentformat', + 'separatethousands', 'tickformat', 'categoryorder', 'categoryarray', 'labelpadding', + 'labelprefix', 'labelsuffix', 'labelfont', 'labelfont.family', 'labelfont.size', + 'labelfont.color', 'showline', 'linecolor', 'linewidth', 'gridcolor', 'gridwidth', + 'showgrid', 'minorgridcount', 'minorgridwidth', 'minorgridcolor', 'startline', + 'startlinecolor', 'startlinewidth', 'endline', 'endlinewidth', 'endlinecolor', + 'tick0', 'dtick', 'arraytick0', 'arraydtick', 'hoverformat', 'tickangle' + ]; + + for(i = 0; i < carpetAxisAttributes.length; i++) { + recalcAttrs.push('aaxis.' + carpetAxisAttributes[i]); + recalcAttrs.push('baxis.' + carpetAxisAttributes[i]); + } + for(i = 0; i < traces.length; i++) { if(Registry.traceIs(fullData[traces[i]], 'box')) { recalcAttrs.push('name'); @@ -1657,9 +1680,15 @@ function _restyle(gd, aobj, _traces) { doextra(axlist.map(rangeAttr), [0, 1], 0); } flags.docalc = true; + + } else if(replotAttrs.indexOf(aiAboveArray) !== -1) { + flags.doplot = true; + + } else if(aiAboveArray.indexOf('aaxis') === 0 || aiAboveArray.indexOf('baxis') === 0) { + flags.doplot = true; + } else if(autorangeAttrs.indexOf(aiAboveArray) !== -1) { + flags.docalcAutorange = true; } - else if(replotAttrs.indexOf(aiAboveArray) !== -1) flags.doplot = true; - else if(autorangeAttrs.indexOf(aiAboveArray) !== -1) flags.docalcAutorange = true; } // do we need to force a recalc? diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index e77bc52831e..7c549e7dfa5 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -1665,6 +1665,8 @@ axes.doTicks = function(gd, axid, skipTitle) { ticksign = ticksign.map(function(v) { return -v; }); } + if(!ax.visible) return; + // remove zero lines, grid lines, and inside ticks if they're within // 1 pixel of the end // The key case here is removing zero lines when the axis bound is zero. diff --git a/src/plots/cartesian/axis_defaults.js b/src/plots/cartesian/axis_defaults.js index c0f0ea8e35a..49691e9400c 100644 --- a/src/plots/cartesian/axis_defaults.js +++ b/src/plots/cartesian/axis_defaults.js @@ -47,6 +47,8 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, return Lib.coerce2(containerIn, containerOut, layoutAttributes, attr, dflt); } + coerce('visible', !options.cheateronly); + var axType = containerOut.type; if(axType === 'date') { diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index d1307a8cbb5..2cbbd08ac57 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -467,6 +467,11 @@ function hover(gd, evt, subplot) { if(!cd || !cd[0] || !cd[0].trace || cd[0].trace.visible !== true) continue; trace = cd[0].trace; + + // Explicitly bail out for these two. I don't know how to otherwise prevent + // the rest of this function from running and failing + if(['carpet', 'contourcarpet'].indexOf(trace._module.name) !== -1) continue; + subplotId = getSubplot(trace); subploti = subplots.indexOf(subplotId); diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 0c1472fff11..0649a155296 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -63,8 +63,11 @@ exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) { // Skip trace if whitelist provided and it's not whitelisted: // if (Array.isArray(traces) && traces.indexOf(i) === -1) continue; if(trace.xaxis + trace.yaxis === subplot) { + // XXX: Should trace carpet dependencies. Only replot all carpet plots if the carpet + // axis has actually changed: + // // If this trace is specifically requested, add it to the list: - if(traces.indexOf(trace.index) !== -1) { + if(traces.indexOf(trace.index) !== -1 || trace.carpet) { // Okay, so example: traces 0, 1, and 2 have fill = tonext. You animate // traces 0 and 2. Trace 1 also needs to be updated, otherwise its fill // is outdated. So this retroactively adds the previous trace if the @@ -300,6 +303,7 @@ function makeSubplotLayer(plotinfo) { joinLayer(parent, 'g', 'imagelayer'); joinLayer(parent, 'g', 'maplayer'); joinLayer(parent, 'g', 'barlayer'); + joinLayer(parent, 'g', 'carpetlayer'); joinLayer(parent, 'g', 'boxlayer'); joinLayer(parent, 'g', 'scatterlayer'); } diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js index f826ca522ba..8b7c2d1c426 100644 --- a/src/plots/cartesian/layout_attributes.js +++ b/src/plots/cartesian/layout_attributes.js @@ -16,6 +16,15 @@ var constants = require('./constants'); module.exports = { + visible: { + valType: 'boolean', + role: 'info', + description: [ + 'A single toggle to hide the axis while preserving interaction like dragging.', + 'Default is true when a cheater plot is present on the axis, otherwise', + 'false' + ].join(' ') + }, color: { valType: 'color', dflt: colorAttrs.defaultLine, diff --git a/src/plots/cartesian/layout_defaults.js b/src/plots/cartesian/layout_defaults.js index 468e685234b..7a78a8adb9e 100644 --- a/src/plots/cartesian/layout_defaults.js +++ b/src/plots/cartesian/layout_defaults.js @@ -29,6 +29,8 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { yaListCartesian = [], xaListGl2d = [], yaListGl2d = [], + xaListCheater = [], + xaListNonCheater = [], outerTicks = {}, noGrids = {}, i; @@ -51,6 +53,21 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { var xaName = axisIds.id2name(trace.xaxis), yaName = axisIds.id2name(trace.yaxis); + // Two things trigger axis visibility: + // 1. is not carpet + // 2. carpet that's not cheater + if(!Registry.traceIs(trace, 'carpet') || (trace.type === 'carpet' && !trace._cheater)) { + if(xaName) Lib.pushUnique(xaListNonCheater, xaName); + } + + // The above check for definitely-not-cheater is not adequate. This + // second list tracks which axes *could* be a cheater so that the + // full condition triggering hiding is: + // *could* be a cheater and *is not definitely visible* + if(trace.type === 'carpet' && trace._cheater) { + if(xaName) Lib.pushUnique(xaListCheater, xaName); + } + // add axes implied by traces if(xaName && listX.indexOf(xaName) === -1) listX.push(xaName); if(yaName && listY.indexOf(yaName) === -1) listY.push(yaName); @@ -168,7 +185,8 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { showGrid: !noGrids[axName], data: fullData, bgColor: bgColor, - calendar: layoutOut.calendar + calendar: layoutOut.calendar, + cheateronly: axLetter === 'x' && (xaListCheater.indexOf(axName) !== -1 && xaListNonCheater.indexOf(axName) === -1) }; handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions, layoutOut); diff --git a/src/plots/gl3d/layout/axis_attributes.js b/src/plots/gl3d/layout/axis_attributes.js index fe1f672b7ba..16f901d56d0 100644 --- a/src/plots/gl3d/layout/axis_attributes.js +++ b/src/plots/gl3d/layout/axis_attributes.js @@ -14,6 +14,7 @@ var extendFlat = require('../../../lib/extend').extendFlat; module.exports = { + visible: axesAttrs.visible, showspikes: { valType: 'boolean', role: 'info', diff --git a/src/plots/plots.js b/src/plots/plots.js index 5c853459536..0e243c4ab69 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -578,6 +578,7 @@ plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayou var query = ( '.hm' + oldUid + ',.contour' + oldUid + + ',.carpet' + oldUid + ',#clip' + oldUid + ',.trace' + oldUid ); @@ -632,6 +633,7 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa }; plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) { + var i, fullTrace, trace; var modules = fullLayout._modules = [], basePlotModules = fullLayout._basePlotModules = [], cnt = 0; @@ -650,9 +652,12 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) { cnt++; } - for(var i = 0; i < dataIn.length; i++) { - var trace = dataIn[i], - fullTrace = plots.supplyTraceDefaults(trace, cnt, fullLayout, i); + var carpetIndex = {}; + var carpetDependents = []; + + for(i = 0; i < dataIn.length; i++) { + trace = dataIn[i]; + fullTrace = plots.supplyTraceDefaults(trace, cnt, fullLayout, i); fullTrace.index = i; fullTrace._input = trace; @@ -689,6 +694,31 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) { pushModule(fullTrace); } + + if(Registry.traceIs(fullTrace, 'carpetAxis')) { + carpetIndex[fullTrace.carpet] = fullTrace; + } + + if(Registry.traceIs(fullTrace, 'carpetDependent')) { + carpetDependents.push(i); + } + } + + for(i = 0; i < carpetDependents.length; i++) { + fullTrace = dataOut[carpetDependents[i]]; + + if(!fullTrace.visible) continue; + + var carpetAxis = carpetIndex[fullTrace.carpet]; + fullTrace._carpet = carpetAxis; + + if(!carpetAxis || !carpetAxis.visible) { + fullTrace.visible = false; + continue; + } + + fullTrace.xaxis = carpetAxis.xaxis; + fullTrace.yaxis = carpetAxis.yaxis; } }; @@ -811,6 +841,11 @@ plots.supplyTraceDefaults = function(traceIn, traceOutIndex, layout, traceInInde // gets overwritten in pie, geo and ternary modules coerce('hoverinfo', (layout._dataLength === 1) ? 'x+y+z+text' : undefined); + if(plots.traceIs(traceOut, 'showLegend')) { + coerce('showlegend'); + coerce('legendgroup'); + } + // TODO add per-base-plot-module trace defaults step if(_module) _module.supplyDefaults(traceIn, traceOut, defaultColor, layout); @@ -823,9 +858,10 @@ plots.supplyTraceDefaults = function(traceIn, traceOutIndex, layout, traceInInde coerceSubplotAttr('gl2d', 'xaxis'); coerceSubplotAttr('gl2d', 'yaxis'); - if(plots.traceIs(traceOut, 'showLegend')) { - coerce('showlegend'); - coerce('legendgroup'); + if(plots.traceIs(traceOut, 'notLegendIsolatable')) { + // This clears out the legendonly state for traces like carpet that + // cannot be isolated in the legend + traceOut.visible = !!traceOut.visible; } plots.supplyTransformDefaults(traceIn, traceOut, layout); diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index 2c53f6f4a17..3e4cf0a3215 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -200,6 +200,7 @@ proto.adjustLayout = function(ternaryLayout, graphSize) { // fictitious angles and domain, but then rotate and translate // it into place at the end var aaxis = _this.aaxis = extendFlat({}, ternaryLayout.aaxis, { + visible: true, range: [amin, sum - bmin - cmin], side: 'left', _counterangle: 30, @@ -220,6 +221,7 @@ proto.adjustLayout = function(ternaryLayout, graphSize) { // baxis goes across the bottom (backward). We can set it up as an x axis // without any enclosing transformation. var baxis = _this.baxis = extendFlat({}, ternaryLayout.baxis, { + visible: true, range: [sum - amin - cmin, bmin], side: 'bottom', _counterangle: 30, @@ -239,6 +241,7 @@ proto.adjustLayout = function(ternaryLayout, graphSize) { // caxis goes down the right side. Set it up as a y axis, with // post-transformation similar to aaxis var caxis = _this.caxis = extendFlat({}, ternaryLayout.caxis, { + visible: true, range: [sum - amin - bmin, cmin], side: 'right', _counterangle: 30, diff --git a/src/traces/carpet/ab_defaults.js b/src/traces/carpet/ab_defaults.js new file mode 100644 index 00000000000..d4208e7444c --- /dev/null +++ b/src/traces/carpet/ab_defaults.js @@ -0,0 +1,66 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var handleAxisDefaults = require('./axis_defaults'); + +module.exports = function handleABDefaults(traceIn, traceOut, fullLayout, coerce, dfltColor) { + var a = coerce('a'); + + if(!a) { + coerce('da'); + coerce('a0'); + } + + var b = coerce('b'); + + if(!b) { + coerce('db'); + coerce('b0'); + } + + mimickAxisDefaults(traceIn, traceOut, fullLayout, dfltColor); + + return; +}; + +function mimickAxisDefaults(traceIn, traceOut, fullLayout, dfltColor) { + var axesList = ['aaxis', 'baxis']; + + axesList.forEach(function(axName) { + var axLetter = axName.charAt(0); + var axIn = traceIn[axName] || {}; + var axOut = {}; + + var defaultOptions = { + tickfont: 'x', + id: axLetter + 'axis', + letter: axLetter, + font: traceOut.font, + name: axName, + data: traceIn[axLetter], + calendar: traceOut.calendar, + dfltColor: dfltColor, + bgColor: fullLayout.paper_bgcolor, + fullLayout: fullLayout + }; + + handleAxisDefaults(axIn, axOut, defaultOptions); + + axOut._categories = axOut._categories || []; + + traceOut[axName] = axOut; + + // so we don't have to repeat autotype unnecessarily, + // copy an autotype back to traceIn + if(!traceIn[axName] && axIn.type !== '-') { + traceIn[axName] = {type: axIn.type}; + } + }); +} diff --git a/src/traces/carpet/array_minmax.js b/src/traces/carpet/array_minmax.js new file mode 100644 index 00000000000..d1b74e94343 --- /dev/null +++ b/src/traces/carpet/array_minmax.js @@ -0,0 +1,43 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = function(a) { + return minMax(a, 0); +}; + +function minMax(a, depth) { + // Limit to ten dimensional datasets. This seems *exceedingly* unlikely to + // ever cause problems or even be a concern. It's include strictly so that + // circular arrays could never cause this to loop. + if(!Array.isArray(a) || depth >= 10) { + return null; + } + + var min = Infinity; + var max = -Infinity; + var n = a.length; + for(var i = 0; i < n; i++) { + var datum = a[i]; + + if(Array.isArray(datum)) { + var result = minMax(datum, depth + 1); + + if(result) { + min = Math.min(result[0], min); + max = Math.max(result[1], max); + } + } else { + min = Math.min(datum, min); + max = Math.max(datum, max); + } + } + + return [min, max]; +} diff --git a/src/traces/carpet/attributes.js b/src/traces/carpet/attributes.js new file mode 100644 index 00000000000..8774f610ae5 --- /dev/null +++ b/src/traces/carpet/attributes.js @@ -0,0 +1,121 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var extendFlat = require('../../lib/extend').extendFlat; +var fontAttrs = require('../../plots/font_attributes'); +var axisAttrs = require('./axis_attributes'); +var colorAttrs = require('../../components/color/attributes'); + +module.exports = { + carpet: { + valType: 'string', + role: 'info', + description: [ + 'An identifier for this carpet, so that `scattercarpet` and', + '`scattercontour` traces can specify a carpet plot on which', + 'they lie' + ].join(' ') + }, + x: { + valType: 'data_array', + description: [ + 'A two dimensional array of x coordinates at each carpet point.', + 'If ommitted, the plot is a cheater plot and the xaxis is hidden', + 'by default.' + ].join(' ') + }, + y: { + valType: 'data_array', + description: 'A two dimensional array of y coordinates at each carpet point.' + }, + a: { + valType: 'data_array', + description: [ + 'An array containing values of the first parameter value' + ].join(' ') + }, + a0: { + valType: 'number', + dflt: 0, + role: 'info', + description: [ + 'Alternate to `a`.', + 'Builds a linear space of a coordinates.', + 'Use with `da`', + 'where `a0` is the starting coordinate and `da` the step.' + ].join(' ') + }, + da: { + valType: 'number', + dflt: 1, + role: 'info', + description: [ + 'Sets the a coordinate step.', + 'See `a0` for more info.' + ].join(' ') + }, + b: { + valType: 'data_array', + description: 'A two dimensional array of y coordinates at each carpet point.' + }, + b0: { + valType: 'number', + dflt: 0, + role: 'info', + description: [ + 'Alternate to `b`.', + 'Builds a linear space of a coordinates.', + 'Use with `db`', + 'where `b0` is the starting coordinate and `db` the step.' + ].join(' ') + }, + db: { + valType: 'number', + dflt: 1, + role: 'info', + description: [ + 'Sets the b coordinate step.', + 'See `b0` for more info.' + ].join(' ') + }, + cheaterslope: { + valType: 'number', + role: 'info', + dflt: 1, + description: [ + 'The shift applied to each successive row of data in creating a cheater plot.', + 'Only used if `x` is been ommitted.' + ].join(' ') + }, + aaxis: extendFlat({}, axisAttrs), + baxis: extendFlat({}, axisAttrs), + font: { + family: extendFlat({}, fontAttrs.family, { + dflt: '"Open Sans", verdana, arial, sans-serif' + }), + size: extendFlat({}, fontAttrs.size, { + dflt: 12 + }), + color: extendFlat({}, fontAttrs.color, { + dflt: colorAttrs.defaultLine + }), + }, + color: { + valType: 'color', + dflt: colorAttrs.defaultLine, + role: 'style', + description: [ + 'Sets default for all colors associated with this axis', + 'all at once: line, font, tick, and grid colors.', + 'Grid color is lightened by blending this with the plot background', + 'Individual pieces can override this.' + ].join(' ') + }, +}; diff --git a/src/traces/carpet/axis_aligned_line.js b/src/traces/carpet/axis_aligned_line.js new file mode 100644 index 00000000000..fad3b70d586 --- /dev/null +++ b/src/traces/carpet/axis_aligned_line.js @@ -0,0 +1,103 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/* This function retrns a set of control points that define a curve aligned along + * either the a or b axis. Exactly one of a or b must be an array defining the range + * spanned. + * + * Honestly this is the most complicated function I've implemente here so far because + * of the way it handles knot insertion and direction/axis-agnostic slices. + */ +module.exports = function(carpet, carpetcd, a, b) { + var idx, tangent, tanIsoIdx, tanIsoPar, segment, refidx; + var p0, p1, v0, v1, start, end, range; + + var axis = Array.isArray(a) ? 'a' : 'b'; + var ax = axis === 'a' ? carpet.aaxis : carpet.baxis; + var smoothing = ax.smoothing; + var toIdx = axis === 'a' ? carpet.a2i : carpet.b2j; + var pt = axis === 'a' ? a : b; + var iso = axis === 'a' ? b : a; + var n = axis === 'a' ? carpetcd.a.length : carpetcd.b.length; + var m = axis === 'a' ? carpetcd.b.length : carpetcd.a.length; + var isoIdx = Math.floor(axis === 'a' ? carpet.b2j(iso) : carpet.a2i(iso)); + + var xy = axis === 'a' ? function(value) { + return carpet.evalxy([], value, isoIdx); + } : function(value) { + return carpet.evalxy([], isoIdx, value); + }; + + if(smoothing) { + tanIsoIdx = Math.max(0, Math.min(m - 2, isoIdx)); + tanIsoPar = isoIdx - tanIsoIdx; + tangent = axis === 'a' ? function(i, ti) { + return carpet.dxydi([], i, tanIsoIdx, ti, tanIsoPar); + } : function(j, tj) { + return carpet.dxydj([], tanIsoIdx, j, tanIsoPar, tj); + }; + } + + var vstart = toIdx(pt[0]); + var vend = toIdx(pt[1]); + + // So that we can make this work in two directions, flip all of the + // math functions if the direction is from higher to lower indices: + // + // Note that the tolerance is directional! + var dir = vstart < vend ? 1 : -1; + var tol = (vend - vstart) * 1e-8; + var dirfloor = dir > 0 ? Math.floor : Math.ceil; + var dirceil = dir > 0 ? Math.ceil : Math.floor; + var dirmin = dir > 0 ? Math.min : Math.max; + var dirmax = dir > 0 ? Math.max : Math.min; + + var idx0 = dirfloor(vstart + tol); + var idx1 = dirceil(vend - tol); + + p0 = xy(vstart); + var segments = [[p0]]; + + for(idx = idx0; idx * dir < idx1 * dir; idx += dir) { + segment = []; + start = dirmax(vstart, idx); + end = dirmin(vend, idx + dir); + range = end - start; + + // In order to figure out which cell we're in for the derivative (remember, + // the derivatives are *not* constant across grid lines), let's just average + // the start and end points. This cuts out just a tiny bit of logic and + // there's really no computational difference: + refidx = Math.max(0, Math.min(n - 2, Math.floor(0.5 * (start + end)))); + + p1 = xy(end); + if(smoothing) { + v0 = tangent(refidx, start - refidx); + v1 = tangent(refidx, end - refidx); + + segment.push([ + p0[0] + v0[0] / 3 * range, + p0[1] + v0[1] / 3 * range + ]); + + segment.push([ + p1[0] - v1[0] / 3 * range, + p1[1] - v1[1] / 3 * range + ]); + } + + segment.push(p1); + + segments.push(segment); + p0 = p1; + } + + return segments; +}; diff --git a/src/traces/carpet/axis_attributes.js b/src/traces/carpet/axis_attributes.js new file mode 100644 index 00000000000..cd689e2772f --- /dev/null +++ b/src/traces/carpet/axis_attributes.js @@ -0,0 +1,445 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var extendFlat = require('../../lib/extend').extendFlat; +var fontAttrs = require('../../plots/font_attributes'); +var colorAttrs = require('../../components/color/attributes'); + +module.exports = { + color: { + valType: 'color', + role: 'style', + description: [ + 'Sets default for all colors associated with this axis', + 'all at once: line, font, tick, and grid colors.', + 'Grid color is lightened by blending this with the plot background', + 'Individual pieces can override this.' + ].join(' ') + }, + smoothing: { + valType: 'number', + dflt: 1, + min: 0, + max: 1.3, + role: 'info' + }, + title: { + valType: 'string', + role: 'info', + description: 'Sets the title of this axis.' + }, + titlefont: extendFlat({}, fontAttrs, { + description: [ + 'Sets this axis\' title font.' + ].join(' ') + }), + titleoffset: { + valType: 'number', + role: 'info', + dflt: 10, + description: [ + 'An additional amount by which to offset the title from the tick', + 'labels, given in pixels' + ].join(' '), + }, + type: { + valType: 'enumerated', + // '-' means we haven't yet run autotype or couldn't find any data + // it gets turned into linear in gd._fullLayout but not copied back + // to gd.data like the others are. + values: ['-', 'linear', 'date', 'category'], + dflt: '-', + role: 'info', + description: [ + 'Sets the axis type.', + 'By default, plotly attempts to determined the axis type', + 'by looking into the data of the traces that referenced', + 'the axis in question.' + ].join(' ') + }, + autorange: { + valType: 'enumerated', + values: [true, false, 'reversed'], + dflt: true, + role: 'style', + description: [ + 'Determines whether or not the range of this axis is', + 'computed in relation to the input data.', + 'See `rangemode` for more info.', + 'If `range` is provided, then `autorange` is set to *false*.' + ].join(' ') + }, + rangemode: { + valType: 'enumerated', + values: ['normal', 'tozero', 'nonnegative'], + dflt: 'normal', + role: 'style', + description: [ + 'If *normal*, the range is computed in relation to the extrema', + 'of the input data.', + 'If *tozero*`, the range extends to 0,', + 'regardless of the input data', + 'If *nonnegative*, the range is non-negative,', + 'regardless of the input data.' + ].join(' ') + }, + range: { + valType: 'info_array', + role: 'info', + items: [ + {valType: 'any'}, + {valType: 'any'} + ], + description: [ + 'Sets the range of this axis.', + 'If the axis `type` is *log*, then you must take the log of your', + 'desired range (e.g. to set the range from 1 to 100,', + 'set the range from 0 to 2).', + 'If the axis `type` is *date*, it should be date strings,', + 'like date data, though Date objects and unix milliseconds', + 'will be accepted and converted to strings.', + 'If the axis `type` is *category*, it should be numbers,', + 'using the scale where each category is assigned a serial', + 'number from zero in the order it appears.' + ].join(' ') + }, + + fixedrange: { + valType: 'boolean', + dflt: false, + role: 'info', + description: [ + 'Determines whether or not this axis is zoom-able.', + 'If true, then zoom is disabled.' + ].join(' ') + }, + cheatertype: { + valType: 'enumerated', + values: ['index', 'value'], + dflt: 'value', + role: 'info' + }, + tickmode: { + valType: 'enumerated', + values: ['linear', 'array'], + dflt: 'array', + role: 'info', + }, + nticks: { + valType: 'integer', + min: 0, + dflt: 0, + role: 'style', + description: [ + 'Specifies the maximum number of ticks for the particular axis.', + 'The actual number of ticks will be chosen automatically to be', + 'less than or equal to `nticks`.', + 'Has an effect only if `tickmode` is set to *auto*.' + ].join(' ') + }, + tickvals: { + valType: 'data_array', + description: [ + 'Sets the values at which ticks on this axis appear.', + 'Only has an effect if `tickmode` is set to *array*.', + 'Used with `ticktext`.' + ].join(' ') + }, + ticktext: { + valType: 'data_array', + description: [ + 'Sets the text displayed at the ticks position via `tickvals`.', + 'Only has an effect if `tickmode` is set to *array*.', + 'Used with `tickvals`.' + ].join(' ') + }, + showticklabels: { + valType: 'enumerated', + values: ['start', 'end', 'both', 'none'], + dflt: 'start', + role: 'style', + description: [ + 'Determines whether axis labels are drawn on the low side,', + 'the high side, both, or neither side of the axis.' + ].join(' ') + }, + tickfont: extendFlat({}, fontAttrs, { + description: 'Sets the tick font.' + }), + tickangle: { + valType: 'angle', + dflt: 'auto', + role: 'style', + description: [ + 'Sets the angle of the tick labels with respect to the horizontal.', + 'For example, a `tickangle` of -90 draws the tick labels', + 'vertically.' + ].join(' ') + }, + tickprefix: { + valType: 'string', + dflt: '', + role: 'style', + description: 'Sets a tick label prefix.' + }, + showtickprefix: { + valType: 'enumerated', + values: ['all', 'first', 'last', 'none'], + dflt: 'all', + role: 'style', + description: [ + 'If *all*, all tick labels are displayed with a prefix.', + 'If *first*, only the first tick is displayed with a prefix.', + 'If *last*, only the last tick is displayed with a suffix.', + 'If *none*, tick prefixes are hidden.' + ].join(' ') + }, + ticksuffix: { + valType: 'string', + dflt: '', + role: 'style', + description: 'Sets a tick label suffix.' + }, + showticksuffix: { + valType: 'enumerated', + values: ['all', 'first', 'last', 'none'], + dflt: 'all', + role: 'style', + description: 'Same as `showtickprefix` but for tick suffixes.' + }, + showexponent: { + valType: 'enumerated', + values: ['all', 'first', 'last', 'none'], + dflt: 'all', + role: 'style', + description: [ + 'If *all*, all exponents are shown besides their significands.', + 'If *first*, only the exponent of the first tick is shown.', + 'If *last*, only the exponent of the last tick is shown.', + 'If *none*, no exponents appear.' + ].join(' ') + }, + exponentformat: { + valType: 'enumerated', + values: ['none', 'e', 'E', 'power', 'SI', 'B'], + dflt: 'B', + role: 'style', + description: [ + 'Determines a formatting rule for the tick exponents.', + 'For example, consider the number 1,000,000,000.', + 'If *none*, it appears as 1,000,000,000.', + 'If *e*, 1e+9.', + 'If *E*, 1E+9.', + 'If *power*, 1x10^9 (with 9 in a super script).', + 'If *SI*, 1G.', + 'If *B*, 1B.' + ].join(' ') + }, + separatethousands: { + valType: 'boolean', + dflt: false, + role: 'style', + description: [ + 'If "true", even 4-digit integers are separated' + ].join(' ') + }, + tickformat: { + valType: 'string', + dflt: '', + role: 'style', + description: [ + 'Sets the tick label formatting rule using d3 formatting mini-languages', + 'which are very similar to those in Python. For numbers, see:', + 'https://github.com/d3/d3-format/blob/master/README.md#locale_format', + 'And for dates see:', + 'https://github.com/d3/d3-time-format/blob/master/README.md#locale_format', + 'We add one item to d3\'s date formatter: *%{n}f* for fractional seconds', + 'with n digits. For example, *2016-10-13 09:15:23.456* with tickformat', + '*%H~%M~%S.%2f* would display *09~15~23.46*' + ].join(' ') + }, + categoryorder: { + valType: 'enumerated', + values: [ + 'trace', 'category ascending', 'category descending', 'array' + /* , 'value ascending', 'value descending'*/ // value ascending / descending to be implemented later + ], + dflt: 'trace', + role: 'info', + description: [ + 'Specifies the ordering logic for the case of categorical variables.', + 'By default, plotly uses *trace*, which specifies the order that is present in the data supplied.', + 'Set `categoryorder` to *category ascending* or *category descending* if order should be determined by', + 'the alphanumerical order of the category names.', + /* 'Set `categoryorder` to *value ascending* or *value descending* if order should be determined by the', + 'numerical order of the values.',*/ // // value ascending / descending to be implemented later + 'Set `categoryorder` to *array* to derive the ordering from the attribute `categoryarray`. If a category', + 'is not found in the `categoryarray` array, the sorting behavior for that attribute will be identical to', + 'the *trace* mode. The unspecified categories will follow the categories in `categoryarray`.' + ].join(' ') + }, + categoryarray: { + valType: 'data_array', + role: 'info', + description: [ + 'Sets the order in which categories on this axis appear.', + 'Only has an effect if `categoryorder` is set to *array*.', + 'Used with `categoryorder`.' + ].join(' ') + }, + labelpadding: { + valType: 'integer', + role: 'style', + dflt: 10, + description: 'Extra padding between label and the axis' + }, + labelprefix: { + valType: 'string', + role: 'style', + description: 'Sets a axis label prefix.' + }, + labelsuffix: { + valType: 'string', + dflt: '', + role: 'style', + description: 'Sets a axis label suffix.' + }, + // lines and grids + showline: { + valType: 'boolean', + dflt: false, + role: 'style', + description: [ + 'Determines whether or not a line bounding this axis is drawn.' + ].join(' ') + }, + linecolor: { + valType: 'color', + dflt: colorAttrs.defaultLine, + role: 'style', + description: 'Sets the axis line color.' + }, + linewidth: { + valType: 'number', + min: 0, + dflt: 1, + role: 'style', + description: 'Sets the width (in px) of the axis line.' + }, + gridcolor: { + valType: 'color', + role: 'style', + description: 'Sets the axis line color.' + }, + gridwidth: { + valType: 'number', + min: 0, + dflt: 1, + role: 'style', + description: 'Sets the width (in px) of the axis line.' + }, + showgrid: { + valType: 'boolean', + role: 'style', + dflt: true, + description: [ + 'Determines whether or not grid lines are drawn.', + 'If *true*, the grid lines are drawn at every tick mark.' + ].join(' ') + }, + minorgridcount: { + valType: 'integer', + min: 0, + dflt: 0, + role: 'info', + description: 'Sets the number of minor grid ticks per major grid tick' + }, + minorgridwidth: { + valType: 'number', + min: 0, + dflt: 1, + role: 'style', + description: 'Sets the width (in px) of the grid lines.' + }, + minorgridcolor: { + valType: 'color', + dflt: colorAttrs.lightLine, + role: 'style', + description: 'Sets the color of the grid lines.' + }, + startline: { + valType: 'boolean', + role: 'style', + description: [ + 'Determines whether or not a line is drawn at along the starting value', + 'of this axis.', + 'If *true*, the start line is drawn on top of the grid lines.' + ].join(' ') + }, + startlinecolor: { + valType: 'color', + role: 'style', + description: 'Sets the line color of the start line.' + }, + startlinewidth: { + valType: 'number', + dflt: 1, + role: 'style', + description: 'Sets the width (in px) of the start line.' + }, + endline: { + valType: 'boolean', + role: 'style', + description: [ + 'Determines whether or not a line is drawn at along the final value', + 'of this axis.', + 'If *true*, the end line is drawn on top of the grid lines.' + ].join(' ') + }, + endlinewidth: { + valType: 'number', + dflt: 1, + role: 'style', + description: 'Sets the width (in px) of the end line.' + }, + endlinecolor: { + valType: 'color', + role: 'style', + description: 'Sets the line color of the end line.' + }, + tick0: { + valType: 'number', + min: 0, + dflt: 0, + role: 'info', + description: 'The starting index of grid lines along the axis' + }, + dtick: { + valType: 'number', + min: 0, + dflt: 1, + role: 'info', + description: 'The stride between grid lines along the axis' + }, + arraytick0: { + valType: 'integer', + min: 0, + dflt: 0, + role: 'info', + description: 'The starting index of grid lines along the axis' + }, + arraydtick: { + valType: 'integer', + min: 1, + dflt: 1, + role: 'info', + description: 'The stride between grid lines along the axis' + }, +}; diff --git a/src/traces/carpet/axis_defaults.js b/src/traces/carpet/axis_defaults.js new file mode 100644 index 00000000000..8d155985d06 --- /dev/null +++ b/src/traces/carpet/axis_defaults.js @@ -0,0 +1,231 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var carpetAttrs = require('./attributes'); + +var addOpacity = require('../../components/color').addOpacity; +var Registry = require('../../registry'); +var Lib = require('../../lib'); +var handleTickValueDefaults = require('../../plots/cartesian/tick_value_defaults'); +var handleTickLabelDefaults = require('../../plots/cartesian/tick_label_defaults'); +var handleCategoryOrderDefaults = require('../../plots/cartesian/category_order_defaults'); +var setConvert = require('../../plots/cartesian/set_convert'); +var orderedCategories = require('../../plots/cartesian/ordered_categories'); +var autoType = require('../../plots/cartesian/axis_autotype'); + +/** + * options: object containing: + * + * letter: 'x' or 'y' + * title: name of the axis (ie 'Colorbar') to go in default title + * name: axis object name (ie 'xaxis') if one should be stored + * font: the default font to inherit + * outerTicks: boolean, should ticks default to outside? + * showGrid: boolean, should gridlines be shown by default? + * noHover: boolean, this axis doesn't support hover effects? + * data: the plot data to use in choosing auto type + * bgColor: the plot background color, to calculate default gridline colors + */ +module.exports = function handleAxisDefaults(containerIn, containerOut, options) { + var letter = options.letter, + font = options.font || {}, + attributes = carpetAttrs[letter + 'axis']; + + options.noHover = true; + + function coerce(attr, dflt) { + return Lib.coerce(containerIn, containerOut, attributes, attr, dflt); + } + + function coerce2(attr, dflt) { + return Lib.coerce2(containerIn, containerOut, attributes, attr, dflt); + } + + // set up some private properties + if(options.name) { + containerOut._name = options.name; + containerOut._id = options.name; + } + + // now figure out type and do some more initialization + var axType = coerce('type'); + if(axType === '-') { + if(options.data) setAutoType(containerOut, options.data); + + if(containerOut.type === '-') { + containerOut.type = 'linear'; + } + else { + // copy autoType back to input axis + // note that if this object didn't exist + // in the input layout, we have to put it in + // this happens in the main supplyDefaults function + axType = containerIn.type = containerOut.type; + } + } + + coerce('smoothing'); + coerce('cheatertype'); + + coerce('showticklabels'); + coerce('labelprefix', letter + ' = '); + coerce('labelsuffix'); + coerce('showtickprefix'); + coerce('showticksuffix'); + + coerce('separatethousands'); + coerce('tickformat'); + coerce('exponentformat'); + coerce('showexponent'); + coerce('categoryorder'); + + coerce('tickmode'); + coerce('tickvals'); + coerce('ticktext'); + coerce('tick0'); + coerce('dtick'); + + if(containerOut.tickmode === 'array') { + coerce('arraytick0'); + coerce('arraydtick'); + } + + coerce('labelpadding'); + + containerOut._hovertitle = letter; + + + if(axType === 'date') { + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults'); + handleCalendarDefaults(containerIn, containerOut, 'calendar', options.calendar); + } + + setConvert(containerOut, options.fullLayout); + + var dfltColor = coerce('color', options.dfltColor); + // if axis.color was provided, use it for fonts too; otherwise, + // inherit from global font color in case that was provided. + var dfltFontColor = (dfltColor === containerIn.color) ? dfltColor : font.color; + + coerce('title'); + Lib.coerceFont(coerce, 'titlefont', { + family: font.family, + size: Math.round(font.size * 1.2), + color: dfltFontColor + }); + + coerce('titleoffset'); + + coerce('tickangle'); + + var autoRange = coerce('autorange', !containerOut.isValidRange(containerIn.range)); + + if(autoRange) coerce('rangemode'); + + coerce('range'); + containerOut.cleanRange(); + + coerce('fixedrange'); + + handleTickValueDefaults(containerIn, containerOut, coerce, axType); + handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options); + handleCategoryOrderDefaults(containerIn, containerOut, coerce); + + var gridColor = coerce2('gridcolor', addOpacity(dfltColor, 0.3)); + var gridWidth = coerce2('gridwidth'); + var showGrid = coerce('showgrid'); + + if(!showGrid) { + delete containerOut.gridcolor; + delete containerOut.gridwidth; + } + + var startLineColor = coerce2('startlinecolor', dfltColor); + var startLineWidth = coerce2('startlinewidth', gridWidth); + var showStartLine = coerce('startline', containerOut.showgrid || !!startLineColor || !!startLineWidth); + + if(!showStartLine) { + delete containerOut.startlinecolor; + delete containerOut.startlinewidth; + } + + var endLineColor = coerce2('endlinecolor', dfltColor); + var endLineWidth = coerce2('endlinewidth', gridWidth); + var showEndLine = coerce('endline', containerOut.showgrid || !!endLineColor || !!endLineWidth); + + if(!showEndLine) { + delete containerOut.endlinecolor; + delete containerOut.endlinewidth; + } + + if(!showGrid) { + delete containerOut.gridcolor; + delete containerOut.gridWidth; + } else { + coerce('minorgridcount'); + coerce('minorgridwidth', gridWidth); + coerce('minorgridcolor', addOpacity(gridColor, 0.06)); + + if(!containerOut.minorgridcount) { + delete containerOut.minorgridwidth; + delete containerOut.minorgridcolor; + } + } + + containerOut._separators = options.fullLayout.separators; + + // fill in categories + containerOut._initialCategories = axType === 'category' ? + orderedCategories(letter, containerOut.categoryorder, containerOut.categoryarray, options.data) : + []; + + if(containerOut.showticklabels === 'none') { + delete containerOut.tickfont; + delete containerOut.tickangle; + delete containerOut.showexponent; + delete containerOut.exponentformat; + delete containerOut.tickformat; + delete containerOut.showticksuffix; + delete containerOut.showtickprefix; + } + + if(!containerOut.showticksuffix) { + delete containerOut.ticksuffix; + } + + if(!containerOut.showtickprefix) { + delete containerOut.tickprefix; + } + + // It needs to be coerced, then something above overrides this deep in the axis code, + // but no, we *actually* want to coerce this. + coerce('tickmode'); + + if(!containerOut.title || (containerOut.title && containerOut.title.length === 0)) { + delete containerOut.titlefont; + delete containerOut.titleoffset; + } + + return containerOut; +}; + +function setAutoType(ax, data) { + // new logic: let people specify any type they want, + // only autotype if type is '-' + if(ax.type !== '-') return; + + var id = ax._id, + axLetter = id.charAt(0); + + var calAttr = axLetter + 'calendar', + calendar = ax[calAttr]; + + ax.type = autoType(data, calendar); +} diff --git a/src/traces/carpet/calc.js b/src/traces/carpet/calc.js new file mode 100644 index 00000000000..4a109eb4964 --- /dev/null +++ b/src/traces/carpet/calc.js @@ -0,0 +1,99 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Axes = require('../../plots/cartesian/axes'); +var cheaterBasis = require('./cheater_basis'); +var arrayMinmax = require('./array_minmax'); +var map2dArray = require('./map_2d_array'); +var calcGridlines = require('./calc_gridlines'); +var calcLabels = require('./calc_labels'); +var calcClipPath = require('./calc_clippath'); +var clean2dArray = require('../heatmap/clean_2d_array'); +var smoothFill2dArray = require('./smooth_fill_2d_array'); + +module.exports = function calc(gd, trace) { + var xa = Axes.getFromId(gd, trace.xaxis || 'x'); + var ya = Axes.getFromId(gd, trace.yaxis || 'y'); + var aax = trace.aaxis; + var bax = trace.baxis; + var a = trace._a = trace.a; + var b = trace._b = trace.b; + + var t = {}; + var x; + var y = trace.y; + + if(trace._cheater) { + var avals = aax.cheatertype === 'index' ? a.length : a; + var bvals = bax.cheatertype === 'index' ? b.length : b; + trace.x = x = cheaterBasis(avals, bvals, trace.cheaterslope); + } else { + x = trace.x; + } + + trace._x = trace.x = x = clean2dArray(x); + trace._y = trace.y = y = clean2dArray(y); + + // Fill in any undefined values with elliptic smoothing. This doesn't take + // into account the spacing of the values. That is, the derivatives should + // be modified to use a and b values. It's not that hard, but this is already + // moderate overkill for just filling in missing values. + smoothFill2dArray(x, a, b); + smoothFill2dArray(y, a, b); + + // create conversion functions that depend on the data + trace.setScale(); + + // Convert cartesian-space x/y coordinates to screen space pixel coordinates: + t.xp = trace.xp = map2dArray(trace.xp, x, xa.c2p); + t.yp = trace.yp = map2dArray(trace.yp, y, ya.c2p); + + // This is a rather expensive scan. Nothing guarantees monotonicity, + // so we need to scan through all data to get proper ranges: + var xrange = arrayMinmax(x); + var yrange = arrayMinmax(y); + + var dx = 0.5 * (xrange[1] - xrange[0]); + var xc = 0.5 * (xrange[1] + xrange[0]); + + var dy = 0.5 * (yrange[1] - yrange[0]); + var yc = 0.5 * (yrange[1] + yrange[0]); + + // Expand the axes to fit the plot, except just grow it by a factor of 1.3 + // because the labels should be taken into account except that's difficult + // hence 1.3. + var grow = 1.3; + xrange = [xc - dx * grow, xc + dx * grow]; + yrange = [yc - dy * grow, yc + dy * grow]; + + Axes.expand(xa, xrange, {padded: true}); + Axes.expand(ya, yrange, {padded: true}); + + // Enumerate the gridlines, both major and minor, and store them on the trace + // object: + calcGridlines(trace, t, 'a', 'b'); + calcGridlines(trace, t, 'b', 'a'); + + // Calculate the text labels for each major gridline and store them on the + // trace object: + calcLabels(trace, aax); + calcLabels(trace, bax); + + // Tabulate points for the four segments that bound the axes so that we can + // map to pixel coordinates in the plot function and create a clip rect: + t.clipsegments = calcClipPath(trace.xctrl, trace.yctrl, aax, bax); + + t.x = x; + t.y = y; + t.a = a; + t.b = b; + + return [t]; +}; diff --git a/src/traces/carpet/calc_clippath.js b/src/traces/carpet/calc_clippath.js new file mode 100644 index 00000000000..e77c2280783 --- /dev/null +++ b/src/traces/carpet/calc_clippath.js @@ -0,0 +1,50 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +module.exports = function makeClipPath(xctrl, yctrl, aax, bax) { + var i, x, y; + var segments = []; + + var asmoothing = !!aax.smoothing; + var bsmoothing = !!bax.smoothing; + var nea1 = xctrl[0].length - 1; + var neb1 = xctrl.length - 1; + + // Along the lower a axis: + for(i = 0, x = [], y = []; i <= nea1; i++) { + x[i] = xctrl[0][i]; + y[i] = yctrl[0][i]; + } + segments.push({x: x, y: y, bicubic: asmoothing}); + + // Along the upper b axis: + for(i = 0, x = [], y = []; i <= neb1; i++) { + x[i] = xctrl[i][nea1]; + y[i] = yctrl[i][nea1]; + } + segments.push({x: x, y: y, bicubic: bsmoothing}); + + // Backwards along the upper a axis: + for(i = nea1, x = [], y = []; i >= 0; i--) { + x[nea1 - i] = xctrl[neb1][i]; + y[nea1 - i] = yctrl[neb1][i]; + } + segments.push({x: x, y: y, bicubic: asmoothing}); + + // Backwards along the lower b axis: + for(i = neb1, x = [], y = []; i >= 0; i--) { + x[neb1 - i] = xctrl[i][0]; + y[neb1 - i] = yctrl[i][0]; + } + segments.push({x: x, y: y, bicubic: bsmoothing}); + + return segments; +}; diff --git a/src/traces/carpet/calc_gridlines.js b/src/traces/carpet/calc_gridlines.js new file mode 100644 index 00000000000..e9434b3a4df --- /dev/null +++ b/src/traces/carpet/calc_gridlines.js @@ -0,0 +1,341 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Axes = require('../../plots/cartesian/axes'); +var extendFlat = require('../../lib/extend').extendFlat; + +module.exports = function calcGridlines(trace, cd, axisLetter, crossAxisLetter) { + var i, j, j0; + var eps, bounds, n1, n2, n, value, v; + var j1, v0, v1, d; + + var data = trace[axisLetter]; + var axis = trace[axisLetter + 'axis']; + + var gridlines = axis._gridlines = []; + var minorgridlines = axis._minorgridlines = []; + var boundarylines = axis._boundarylines = []; + + var crossData = trace[crossAxisLetter]; + var crossAxis = trace[crossAxisLetter + 'axis']; + + if(axis.tickmode === 'array') { + axis.tickvals = []; + for(i = 0; i < data.length; i++) { + axis.tickvals.push(data[i]); + } + } + + var xcp = trace.xctrl; + var ycp = trace.yctrl; + var nea = xcp[0].length; + var neb = xcp.length; + var na = trace.a.length; + var nb = trace.b.length; + + Axes.calcTicks(axis); + + // The default is an empty array that will cause the join to remove the gridline if + // it's just disappeared: + // axis._startline = axis._endline = []; + + // If the cross axis uses bicubic interpolation, then the grid + // lines fall once every three expanded grid row/cols: + var stride = axis.smoothing ? 3 : 1; + + function constructValueGridline(value) { + var i, j, j0, tj, pxy, i0, ti, xy, dxydi0, dxydi1, dxydj0, dxydj1; + var xpoints = []; + var ypoints = []; + var ret = {}; + // Search for the fractional grid index giving this line: + if(axisLetter === 'b') { + // For the position we use just the i-j coordinates: + j = trace.b2j(value); + + // The derivatives for catmull-rom splines are discontinuous across cell + // boundaries though, so we need to provide both the cell and the position + // within the cell separately: + j0 = Math.floor(Math.max(0, Math.min(nb - 2, j))); + tj = j - j0; + + ret.length = nb; + ret.crossLength = na; + + ret.xy = function(i) { + return trace.evalxy([], i, j); + }; + + ret.dxy = function(i0, ti) { + return trace.dxydi([], i0, j0, ti, tj); + }; + + for(i = 0; i < na; i++) { + i0 = Math.min(na - 2, i); + ti = i - i0; + xy = trace.evalxy([], i, j); + + if(crossAxis.smoothing && i > 0) { + // First control point: + dxydi0 = trace.dxydi([], i - 1, j0, 0, tj); + xpoints.push(pxy[0] + dxydi0[0] / 3); + ypoints.push(pxy[1] + dxydi0[1] / 3); + + // Second control point: + dxydi1 = trace.dxydi([], i - 1, j0, 1, tj); + xpoints.push(xy[0] - dxydi1[0] / 3); + ypoints.push(xy[1] - dxydi1[1] / 3); + } + + xpoints.push(xy[0]); + ypoints.push(xy[1]); + + pxy = xy; + } + } else { + i = trace.a2i(value); + i0 = Math.floor(Math.max(0, Math.min(na - 2, i))); + ti = i - i0; + + ret.length = na; + ret.crossLength = nb; + + ret.xy = function(j) { + return trace.evalxy([], i, j); + }; + + ret.dxy = function(j0, tj) { + return trace.dxydj([], i0, j0, ti, tj); + }; + + for(j = 0; j < nb; j++) { + j0 = Math.min(nb - 2, j); + tj = j - j0; + xy = trace.evalxy([], i, j); + + if(crossAxis.smoothing && j > 0) { + // First control point: + dxydj0 = trace.dxydj([], i0, j - 1, ti, 0); + xpoints.push(pxy[0] + dxydj0[0] / 3); + ypoints.push(pxy[1] + dxydj0[1] / 3); + + // Second control point: + dxydj1 = trace.dxydj([], i0, j - 1, ti, 1); + xpoints.push(xy[0] - dxydj1[0] / 3); + ypoints.push(xy[1] - dxydj1[1] / 3); + } + + xpoints.push(xy[0]); + ypoints.push(xy[1]); + + pxy = xy; + } + } + + ret.axisLetter = axisLetter; + ret.axis = axis; + ret.crossAxis = crossAxis; + ret.value = value; + ret.constvar = crossAxisLetter; + ret.index = n; + ret.x = xpoints; + ret.y = ypoints; + ret.smoothing = crossAxis.smoothing; + + return ret; + } + + function constructArrayGridline(idx) { + var j, i0, j0, ti, tj; + var xpoints = []; + var ypoints = []; + var ret = {}; + ret.length = data.length; + ret.crossLength = crossData.length; + + if(axisLetter === 'b') { + j0 = Math.max(0, Math.min(nb - 2, idx)); + tj = Math.min(1, Math.max(0, idx - j0)); + + ret.xy = function(i) { + return trace.evalxy([], i, idx); + }; + + ret.dxy = function(i0, ti) { + return trace.dxydi([], i0, j0, ti, tj); + }; + + // In the tickmode: array case, this operation is a simple + // transfer of data: + for(j = 0; j < nea; j++) { + xpoints[j] = xcp[idx * stride][j]; + ypoints[j] = ycp[idx * stride][j]; + } + } else { + i0 = Math.max(0, Math.min(na - 2, idx)); + ti = Math.min(1, Math.max(0, idx - i0)); + + ret.xy = function(j) { + return trace.evalxy([], idx, j); + }; + + ret.dxy = function(j0, tj) { + return trace.dxydj([], i0, j0, ti, tj); + }; + + // In the tickmode: array case, this operation is a simple + // transfer of data: + for(j = 0; j < neb; j++) { + xpoints[j] = xcp[j][idx * stride]; + ypoints[j] = ycp[j][idx * stride]; + } + } + + ret.axisLetter = axisLetter; + ret.axis = axis; + ret.crossAxis = crossAxis; + ret.value = data[idx]; + ret.constvar = crossAxisLetter; + ret.index = idx; + ret.x = xpoints; + ret.y = ypoints; + ret.smoothing = crossAxis.smoothing; + + return ret; + } + + if(axis.tickmode === 'array') { + // var j0 = axis.startline ? 1 : 0; + // var j1 = data.length - (axis.endline ? 1 : 0); + + eps = 5e-15; + bounds = [ + Math.floor(((data.length - 1) - axis.arraytick0) / axis.arraydtick * (1 + eps)), + Math.ceil((- axis.arraytick0) / axis.arraydtick / (1 + eps)) + ].sort(function(a, b) {return a - b;}); + + // Unpack sorted values so we can be sure to avoid infinite loops if something + // is backwards: + n1 = bounds[0] - 1; + n2 = bounds[1] + 1; + + // If the axes fall along array lines, then this is a much simpler process since + // we already have all the control points we need + for(n = n1; n < n2; n++) { + j = axis.arraytick0 + axis.arraydtick * n; + if(j < 0 || j > data.length - 1) continue; + gridlines.push(extendFlat(constructArrayGridline(j), { + color: axis.gridcolor, + width: axis.gridwidth + })); + } + + for(n = n1; n < n2; n++) { + j0 = axis.arraytick0 + axis.arraydtick * n; + j1 = Math.min(j0 + axis.arraydtick, data.length - 1); + + // TODO: fix the bounds computation so we don't have to do a large range and then throw + // out unneeded numbers + if(j0 < 0 || j0 > data.length - 1) continue; + if(j1 < 0 || j1 > data.length - 1) continue; + + v0 = data[j0]; + v1 = data[j1]; + + for(i = 0; i < axis.minorgridcount; i++) { + d = j1 - j0; + + // TODO: fix the bounds computation so we don't have to do a large range and then throw + // out unneeded numbers + if(d <= 0) continue; + + // XXX: This calculation isn't quite right. Off by one somewhere? + v = v0 + (v1 - v0) * (i + 1) / (axis.minorgridcount + 1) * (axis.arraydtick / d); + + // TODO: fix the bounds computation so we don't have to do a large range and then throw + // out unneeded numbers + if(v < data[0] || v > data[data.length - 1]) continue; + minorgridlines.push(extendFlat(constructValueGridline(v), { + color: axis.minorgridcolor, + width: axis.minorgridwidth + })); + } + } + + if(axis.startline) { + boundarylines.push(extendFlat(constructArrayGridline(0), { + color: axis.startlinecolor, + width: axis.startlinewidth + })); + } + + if(axis.endline) { + boundarylines.push(extendFlat(constructArrayGridline(data.length - 1), { + color: axis.endlinecolor, + width: axis.endlinewidth + })); + } + } else { + // If the lines do not fall along the axes, then we have to interpolate + // the contro points and so some math to figure out where the lines are + // in the first place. + + // Compute the integer boudns of tick0 + n * dtick that fall within the range + // (roughly speaking): + // Give this a nice generous epsilon. We use at as * (1 + eps) in order to make + // inequalities a little tolerant in a more or less correct manner: + eps = 5e-15; + bounds = [ + Math.floor((data[data.length - 1] - axis.tick0) / axis.dtick * (1 + eps)), + Math.ceil((data[0] - axis.tick0) / axis.dtick / (1 + eps)) + ].sort(function(a, b) {return a - b;}); + + // Unpack sorted values so we can be sure to avoid infinite loops if something + // is backwards: + n1 = bounds[0]; + n2 = bounds[1]; + + for(n = n1; n <= n2; n++) { + value = axis.tick0 + axis.dtick * n; + + gridlines.push(extendFlat(constructValueGridline(value), { + color: axis.gridcolor, + width: axis.gridwidth + })); + } + + for(n = n1 - 1; n < n2 + 1; n++) { + value = axis.tick0 + axis.dtick * n; + + for(i = 0; i < axis.minorgridcount; i++) { + v = value + axis.dtick * (i + 1) / (axis.minorgridcount + 1); + if(v < data[0] || v > data[data.length - 1]) continue; + minorgridlines.push(extendFlat(constructValueGridline(v), { + color: axis.minorgridcolor, + width: axis.minorgridwidth + })); + } + } + + if(axis.startline) { + boundarylines.push(extendFlat(constructValueGridline(data[0]), { + color: axis.startlinecolor, + width: axis.startlinewidth + })); + } + + if(axis.endline) { + boundarylines.push(extendFlat(constructValueGridline(data[data.length - 1]), { + color: axis.endlinecolor, + width: axis.endlinewidth + })); + } + } +}; diff --git a/src/traces/carpet/calc_labels.js b/src/traces/carpet/calc_labels.js new file mode 100644 index 00000000000..674e61b7e9f --- /dev/null +++ b/src/traces/carpet/calc_labels.js @@ -0,0 +1,59 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Axes = require('../../plots/cartesian/axes'); +var extendFlat = require('../../lib/extend').extendFlat; + +module.exports = function calcLabels(trace, axis) { + var i, tobj, prefix, suffix, gridline; + + var labels = axis._labels = []; + var gridlines = axis._gridlines; + + for(i = 0; i < gridlines.length; i++) { + gridline = gridlines[i]; + + if(['start', 'both'].indexOf(axis.showticklabels) !== -1) { + tobj = Axes.tickText(axis, gridline.value); + + extendFlat(tobj, { + prefix: prefix, + suffix: suffix, + endAnchor: true, + xy: gridline.xy(0), + dxy: gridline.dxy(0, 0), + axis: gridline.axis, + length: gridline.crossAxis.length, + font: gridline.axis.tickfont, + isFirst: i === 0, + isLast: i === gridlines.length - 1 + }); + + labels.push(tobj); + } + + if(['end', 'both'].indexOf(axis.showticklabels) !== -1) { + tobj = Axes.tickText(axis, gridline.value); + + extendFlat(tobj, { + endAnchor: false, + xy: gridline.xy(gridline.crossLength - 1), + dxy: gridline.dxy(gridline.crossLength - 2, 1), + axis: gridline.axis, + length: gridline.crossAxis.length, + font: gridline.axis.tickfont, + isFirst: i === 0, + isLast: i === gridlines.length - 1 + }); + + labels.push(tobj); + } + } +}; diff --git a/src/traces/carpet/catmull_rom.js b/src/traces/carpet/catmull_rom.js new file mode 100644 index 00000000000..389784934b5 --- /dev/null +++ b/src/traces/carpet/catmull_rom.js @@ -0,0 +1,40 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/* + * Compute the tangent vector according to catmull-rom cubic splines (centripetal, + * I think). That differs from the control point in two ways: + * 1. It is a vector, not a position relative to the point + * 2. the vector is longer than the position relative to p1 by a factor of 3 + * + * Close to the boundaries, we'll use these as *quadratic control points, so that + * to make a nice grid, we'll need to divide the tangent by 2 instead of 3. (The + * math works out this way if you work through the bezier derivatives) + */ +var CatmullRomExp = 0.5; +module.exports = function makeControlPoints(p0, p1, p2, smoothness) { + var d1x = p0[0] - p1[0], + d1y = p0[1] - p1[1], + d2x = p2[0] - p1[0], + d2y = p2[1] - p1[1], + d1a = Math.pow(d1x * d1x + d1y * d1y, CatmullRomExp / 2), + d2a = Math.pow(d2x * d2x + d2y * d2y, CatmullRomExp / 2), + numx = (d2a * d2a * d1x - d1a * d1a * d2x) * smoothness, + numy = (d2a * d2a * d1y - d1a * d1a * d2y) * smoothness, + denom1 = d2a * (d1a + d2a) * 3, + denom2 = d1a * (d1a + d2a) * 3; + return [[ + p1[0] + (denom1 && numx / denom1), + p1[1] + (denom1 && numy / denom1) + ], [ + p1[0] - (denom2 && numx / denom2), + p1[1] - (denom2 && numy / denom2) + ]]; +}; diff --git a/src/traces/carpet/cheater_basis.js b/src/traces/carpet/cheater_basis.js new file mode 100644 index 00000000000..5caeecd15c5 --- /dev/null +++ b/src/traces/carpet/cheater_basis.js @@ -0,0 +1,66 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var isArray = require('../../lib').isArray; + +/* + * Construct a 2D array of cheater values given a, b, and a slope. + * If + */ +module.exports = function(a, b, cheaterslope) { + var i, j, ascal, bscal, aval, bval; + var data = []; + + var na = isArray(a) ? a.length : a; + var nb = isArray(b) ? b.length : b; + var adata = isArray(a) ? a : null; + var bdata = isArray(b) ? b : null; + + // If we're using data, scale it so that for data that's just barely + // not evenly spaced, the switch to value-based indexing is continuous. + // This means evenly spaced data should look the same whether value + // or index cheatertype. + if(adata) { + ascal = (adata.length - 1) / (adata[adata.length - 1] - adata[0]) / (na - 1); + } + + if(bdata) { + bscal = (bdata.length - 1) / (bdata[bdata.length - 1] - bdata[0]) / (nb - 1); + } + + var xval; + var xmin = Infinity; + var xmax = -Infinity; + for(j = 0; j < nb; j++) { + data[j] = []; + bval = bdata ? (bdata[j] - bdata[0]) * bscal : j / (nb - 1); + for(i = 0; i < na; i++) { + aval = adata ? (adata[i] - adata[0]) * ascal : i / (na - 1); + xval = aval - bval * cheaterslope; + xmin = Math.min(xval, xmin); + xmax = Math.max(xval, xmax); + data[j][i] = xval; + } + } + + // Normalize cheater values to the 0-1 range. This comes into play when you have + // multiple cheater plots. After careful consideration, it seems better if cheater + // values are normalized to a consistent range. Otherwise one cheater affects the + // layout of other cheaters on the same axis. + var slope = 1.0 / (xmax - xmin); + var offset = -xmin * slope; + for(j = 0; j < nb; j++) { + for(i = 0; i < na; i++) { + data[j][i] = slope * data[j][i] + offset; + } + } + + return data; +}; diff --git a/src/traces/carpet/compute_control_points.js b/src/traces/carpet/compute_control_points.js new file mode 100644 index 00000000000..b3f7b3f5cd7 --- /dev/null +++ b/src/traces/carpet/compute_control_points.js @@ -0,0 +1,350 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var makeControlPoints = require('./catmull_rom'); +var ensureArray = require('../../lib').ensureArray; + +/* + * Turns a coarse grid into a fine grid with control points. + * + * Here's an ASCII representation: + * + * o ----- o ----- o ----- o + * | | | | + * | | | | + * | | | | + * o ----- o ----- o ----- o + * | | | | + * | | | | + * ^ | | | | + * | o ----- o ----- o ----- o + * b | | | | | + * | | | | | + * | | | | | + * o ----- o ----- o ----- o + * ------> + * a + * + * First of all, note that we want to do this in *cartesian* space. This means + * we might run into problems when there are extreme differences in x/y scaling, + * but the alternative is that the topology of the contours might actually be + * view-dependent, which seems worse. As a fallback, the only parameter that + * actually affects the result is the *aspect ratio*, so that we can at least + * improve the situation a bit without going all the way to screen coordinates. + * + * This function flattens the points + tangents into a slightly denser grid of + * *control points*. The resulting grid looks like this: + * + * 9 +--o-o--+ -o-o--+--o-o--+ + * 8 o o o o o o o o o o + * | | | | + * 7 o o o o o o o o o o + * 6 +--o-o--+ -o-o--+--o-o--+ + * 5 o o o o o o o o o o + * | | | | + * ^ 4 o o o o o o o o o o + * | 3 +--o-o--+ -o-o--+--o-o--+ + * b | 2 o o o o o o o o o o + * | | | | | + * | 1 o o o o o o o o o o + * 0 +--o-o--+ -o-o--+--o-o--+ + * 0 1 2 3 4 5 6 7 8 9 + * ------> + * a + * + * where `o`s represent newly-computed control points. the resulting dimension is + * + * (m - 1) * 3 + 1 + * = 3 * m - 2 + * + * We could simply store the tangents separately, but that's a nightmare to organize + * in two dimensions since we'll be slicing grid lines in both directions and since + * that basically requires very nearly just as much storage as just storing the dense + * grid. + * + * Wow! + */ + + +/* + * Catmull-rom is biased at the boundaries toward the interior and we actually + * can't use catmull-rom to compute the control point closest to (but inside) + * the boundary. + * + * A note on plotly's spline interpolation. It uses the catmull rom control point + * closest to the boundary *as* a quadratic control point. This seems incorrect, + * so I've elected not to follow that. Given control points 0 and 1, regular plotly + * splines give *equivalent* cubic control points: + * + * Input: + * + * boundary + * | | + * p0 p2 p3 --> interior + * 0.0 0.667 1.0 + * | | + * + * Cubic-equivalent of what plotly splines draw:: + * + * boundary + * | | + * p0 p1 p2 p3 --> interior + * 0.0 0.4444 0.8888 1.0 + * | | + * + * What this function fills in: + * + * boundary + * | | + * p0 p1 p2 p3 --> interior + * 0.0 0.333 0.667 1.0 + * | | + * + * Parameters: + * p0: boundary point + * p2: catmull rom point based on computation at p3 + * p3: first grid point + * + * Of course it works whichever way it's oriented; you just need to interpret the + * input/output accordingly. + */ +function inferCubicControlPoint(p0, p2, p3) { + // Extend p1 away from p0 by 50%. This is the equivalent quadratic point that + // would give the same slope as catmull rom at p0. + var p2e0 = -0.5 * p3[0] + 1.5 * p2[0]; + var p2e1 = -0.5 * p3[1] + 1.5 * p2[1]; + + return [ + (2 * p2e0 + p0[0]) / 3, + (2 * p2e1 + p0[1]) / 3, + ]; +} + +module.exports = function computeControlPoints(xe, ye, x, y, asmoothing, bsmoothing) { + var i, j, ie, je, xej, yej, xj, yj, cp, p1; + // At this point, we know these dimensions are correct and representative of + // the whole 2D arrays: + var na = x[0].length; + var nb = x.length; + + // (n)umber of (e)xpanded points: + var nea = asmoothing ? 3 * na - 2 : na; + var neb = bsmoothing ? 3 * nb - 2 : nb; + + xe = ensureArray(xe, neb); + ye = ensureArray(ye, neb); + + for(ie = 0; ie < neb; ie++) { + xe[ie] = ensureArray(xe[ie], nea); + ye[ie] = ensureArray(ye[ie], nea); + } + + // This loop fills in the X'd points: + // + // . . . . + // . . . . + // | | | | + // | | | | + // X ----- X ----- X ----- X + // | | | | + // | | | | + // | | | | + // X ----- X ----- X ----- X + // + // + // ie = (i) (e)xpanded: + for(j = 0, je = 0; j < nb; j++, je += bsmoothing ? 3 : 1) { + xej = xe[je]; + yej = ye[je]; + xj = x[j]; + yj = y[j]; + + // je = (j) (e)xpanded: + for(i = 0, ie = 0; i < na; i++, ie += asmoothing ? 3 : 1) { + xej[ie] = xj[i]; + yej[ie] = yj[i]; + } + } + + if(asmoothing) { + // If there's a-smoothing, this loop fills in the X'd points with catmull-rom + // control points computed along the a-axis: + // . . . . + // . . . . + // | | | | + // | | | | + // o -Y-X- o -X-X- o -X-Y- o + // | | | | + // | | | | + // | | | | + // o -Y-X- o -X-X- o -X-Y- o + // + // i: 0 1 2 3 + // ie: 0 1 3 3 4 5 6 7 8 9 + // + // ------> + // a + // + for(j = 0, je = 0; j < nb; j++, je += bsmoothing ? 3 : 1) { + // Fill in the points marked X for this a-row: + for(i = 1, ie = 3; i < na - 1; i++, ie += 3) { + cp = makeControlPoints( + [x[j][i - 1], y[j][i - 1]], + [x[j][i ], y[j][i]], + [x[j][i + 1], y[j][i + 1]], + asmoothing + ); + + xe[je][ie - 1] = cp[0][0]; + ye[je][ie - 1] = cp[0][1]; + xe[je][ie + 1] = cp[1][0]; + ye[je][ie + 1] = cp[1][1]; + } + + // The very first cubic interpolation point (to the left for i = 1 above) is + // used as a *quadratic* interpolation point by the spline drawing function + // which isn't really correct. But for the sake of consistency, we'll use it + // as such. Since we're using cubic splines, that means we need to shorten the + // tangent by 1/3 and also construct a new cubic spline control point 1/3 from + // the original to the i = 0 point. + p1 = inferCubicControlPoint( + [xe[je][0], ye[je][0]], + [xe[je][2], ye[je][2]], + [xe[je][3], ye[je][3]] + ); + xe[je][1] = p1[0]; + ye[je][1] = p1[1]; + + // Ditto last points, sans explanation: + p1 = inferCubicControlPoint( + [xe[je][nea - 1], ye[je][nea - 1]], + [xe[je][nea - 3], ye[je][nea - 3]], + [xe[je][nea - 4], ye[je][nea - 4]] + ); + xe[je][nea - 2] = p1[0]; + ye[je][nea - 2] = p1[1]; + } + } + + if(bsmoothing) { + // If there's a-smoothing, this loop fills in the X'd points with catmull-rom + // control points computed along the b-axis: + // . . . . + // X X X X X X X X X X + // | | | | + // X X X X X X X X X X + // o -o-o- o -o-o- o -o-o- o + // X X X X X X X X X X + // | | | | + // Y Y Y Y Y Y Y Y Y Y + // o -o-o- o -o-o- o -o-o- o + // + // i: 0 1 2 3 + // ie: 0 1 3 3 4 5 6 7 8 9 + // + // ------> + // a + // + for(ie = 0; ie < nea; ie++) { + for(je = 3; je < neb - 3; je += 3) { + cp = makeControlPoints( + [xe[je - 3][ie], ye[je - 3][ie]], + [xe[je][ie], ye[je][ie]], + [xe[je + 3][ie], ye[je + 3][ie]], + bsmoothing + ); + + xe[je - 1][ie] = cp[0][0]; + ye[je - 1][ie] = cp[0][1]; + xe[je + 1][ie] = cp[1][0]; + ye[je + 1][ie] = cp[1][1]; + } + // Do the same boundary condition magic for these control points marked Y above: + p1 = inferCubicControlPoint( + [xe[0][ie], ye[0][ie]], + [xe[2][ie], ye[2][ie]], + [xe[3][ie], ye[3][ie]] + ); + xe[1][ie] = p1[0]; + ye[1][ie] = p1[1]; + + p1 = inferCubicControlPoint( + [xe[neb - 1][ie], ye[neb - 1][ie]], + [xe[neb - 3][ie], ye[neb - 3][ie]], + [xe[neb - 4][ie], ye[neb - 4][ie]] + ); + xe[neb - 2][ie] = p1[0]; + ye[neb - 2][ie] = p1[1]; + } + } + + if(asmoothing && bsmoothing) { + // Do one more pass, this time recomputing exactly what we just computed. + // It's overdetermined since we're peforming catmull-rom in two directions, + // so we'll just average the overdetermined. These points don't lie along the + // grid lines, so note that only grid lines will follow normal plotly spline + // interpolation. + // + // Unless of course there was no b smoothing. Then these intermediate points + // don't actually exist and this section is bypassed. + // . . . . + // o X X o X X o X X o + // | | | | + // o X X o X X o X X o + // o -o-o- o -o-o- o -o-o- o + // o X X o X X o X X o + // | | | | + // o Y Y o Y Y o Y Y o + // o -o-o- o -o-o- o -o-o- o + // + // i: 0 1 2 3 + // ie: 0 1 3 3 4 5 6 7 8 9 + // + // ------> + // a + // + for(je = 1; je < neb; je += (je + 1) % 3 === 0 ? 2 : 1) { + // Fill in the points marked X for this a-row: + for(ie = 3; ie < nea - 3; ie += 3) { + cp = makeControlPoints( + [xe[je][ie - 3], ye[je][ie - 3]], + [xe[je][ie], ye[je][ie]], + [xe[je][ie + 3], ye[je][ie + 3]], + asmoothing + ); + + xe[je][ie - 1] = 0.5 * (xe[je][ie - 1] + cp[0][0]); + ye[je][ie - 1] = 0.5 * (ye[je][ie - 1] + cp[0][1]); + xe[je][ie + 1] = 0.5 * (xe[je][ie + 1] + cp[1][0]); + ye[je][ie + 1] = 0.5 * (ye[je][ie + 1] + cp[1][1]); + } + + // This case is just slightly different. The computation is the same, + // but having computed this, we'll average with the existing result. + p1 = inferCubicControlPoint( + [xe[je][0], ye[je][0]], + [xe[je][2], ye[je][2]], + [xe[je][3], ye[je][3]] + ); + xe[je][1] = 0.5 * (xe[je][1] + p1[0]); + ye[je][1] = 0.5 * (ye[je][1] + p1[1]); + + p1 = inferCubicControlPoint( + [xe[je][nea - 1], ye[je][nea - 1]], + [xe[je][nea - 3], ye[je][nea - 3]], + [xe[je][nea - 4], ye[je][nea - 4]] + ); + xe[je][nea - 2] = 0.5 * (xe[je][nea - 2] + p1[0]); + ye[je][nea - 2] = 0.5 * (ye[je][nea - 2] + p1[1]); + } + } + + return [xe, ye]; +}; diff --git a/src/traces/carpet/constants.js b/src/traces/carpet/constants.js new file mode 100644 index 00000000000..7c9465e0a00 --- /dev/null +++ b/src/traces/carpet/constants.js @@ -0,0 +1,14 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +module.exports = { + RELATIVE_CULL_TOLERANCE: 1e-6 +}; diff --git a/src/traces/carpet/create_i_derivative_evaluator.js b/src/traces/carpet/create_i_derivative_evaluator.js new file mode 100644 index 00000000000..5faea051eea --- /dev/null +++ b/src/traces/carpet/create_i_derivative_evaluator.js @@ -0,0 +1,150 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/* + * Evaluates the derivative of a list of control point arrays. That is, it expects an array or arrays + * that are expanded relative to the raw data to include the bicubic control points, if applicable. If + * only linear interpolation is desired, then the data points correspond 1-1 along that axis to the + * data itself. Since it's catmull-rom splines in either direction note in particular that the + * derivatives are discontinuous across cell boundaries. That's the reason you need both the *cell* + * and the *point within the cell*. + * + * Also note that the discontinuity of the derivative is in magnitude only. The direction *is* + * continuous across cell boundaries. + * + * For example, to compute the derivative of the xcoordinate halfway betwen the 7 and 8th i-gridpoints + * and the 10th and 11th j-gridpoints given bicubic smoothing in both dimensions, you'd write: + * + * var deriv = createIDerivativeEvaluator([x], 1, 1); + * + * var dxdi = deriv([], 7, 10, 0.5, 0.5); + * // => [0.12345] + * + * Since there'd be a bunch of duplicate computation to compute multiple derivatives, you can double + * this up by providing more arrays: + * + * var deriv = createIDerivativeEvaluator([x, y], 1, 1); + * + * var dxdi = deriv([], 7, 10, 0.5, 0.5); + * // => [0.12345, 0.78910] + * + * NB: It's presumed that at this point all data has been sanitized and is valid numerical data arrays + * of the correct dimension. + */ +module.exports = function(arrays, asmoothing, bsmoothing) { + if(asmoothing && bsmoothing) { + return function(out, i0, j0, u, v) { + if(!out) out = []; + var f0, f1, f2, f3, ak, k; + + // Since it's a grid of control points, the actual indices are * 3: + i0 *= 3; + j0 *= 3; + + // Precompute some numbers: + var u2 = u * u; + var ou = 1 - u; + var ou2 = ou * ou; + var ouu2 = ou * u * 2; + var a = -3 * ou2; + var b = 3 * (ou2 - ouu2); + var c = 3 * (ouu2 - u2); + var d = 3 * u2; + + var v2 = v * v; + var v3 = v2 * v; + var ov = 1 - v; + var ov2 = ov * ov; + var ov3 = ov2 * ov; + + for(k = 0; k < arrays.length; k++) { + ak = arrays[k]; + // Compute the derivatives in the u-direction: + f0 = a * ak[j0 ][i0] + b * ak[j0 ][i0 + 1] + c * ak[j0 ][i0 + 2] + d * ak[j0 ][i0 + 3]; + f1 = a * ak[j0 + 1][i0] + b * ak[j0 + 1][i0 + 1] + c * ak[j0 + 1][i0 + 2] + d * ak[j0 + 1][i0 + 3]; + f2 = a * ak[j0 + 2][i0] + b * ak[j0 + 2][i0 + 1] + c * ak[j0 + 2][i0 + 2] + d * ak[j0 + 2][i0 + 3]; + f3 = a * ak[j0 + 3][i0] + b * ak[j0 + 3][i0 + 1] + c * ak[j0 + 3][i0 + 2] + d * ak[j0 + 3][i0 + 3]; + + // Now just interpolate in the v-direction since it's all separable: + out[k] = ov3 * f0 + 3 * (ov2 * v * f1 + ov * v2 * f2) + v3 * f3; + } + + return out; + }; + } else if(asmoothing) { + // Handle smooth in the a-direction but linear in the b-direction by performing four + // linear interpolations followed by one cubic interpolation of the result + return function(out, i0, j0, u, v) { + if(!out) out = []; + var f0, f1, k, ak; + i0 *= 3; + var u2 = u * u; + var ou = 1 - u; + var ou2 = ou * ou; + var ouu2 = ou * u * 2; + var a = -3 * ou2; + var b = 3 * (ou2 - ouu2); + var c = 3 * (ouu2 - u2); + var d = 3 * u2; + var ov = 1 - v; + for(k = 0; k < arrays.length; k++) { + ak = arrays[k]; + f0 = a * ak[j0 ][i0] + b * ak[j0 ][i0 + 1] + c * ak[j0 ][i0 + 2] + d * ak[j0 ][i0 + 3]; + f1 = a * ak[j0 + 1][i0] + b * ak[j0 + 1][i0 + 1] + c * ak[j0 + 1][i0 + 2] + d * ak[j0 + 1][i0 + 3]; + + out[k] = ov * f0 + v * f1; + } + return out; + }; + } else if(bsmoothing) { + // Same as the above case, except reversed. I've disabled the no-unused vars rule + // so that this function is fully interpolation-agnostic. Otherwise it would need + // to be called differently in different cases. Which wouldn't be the worst, but + /* eslint-disable no-unused-vars */ + return function(out, i0, j0, u, v) { + /* eslint-enable no-unused-vars */ + if(!out) out = []; + var f0, f1, f2, f3, k, ak; + j0 *= 3; + var v2 = v * v; + var v3 = v2 * v; + var ov = 1 - v; + var ov2 = ov * ov; + var ov3 = ov2 * ov; + for(k = 0; k < arrays.length; k++) { + ak = arrays[k]; + f0 = ak[j0][i0 + 1] - ak[j0][i0]; + f1 = ak[j0 + 1][i0 + 1] - ak[j0 + 1][i0]; + f2 = ak[j0 + 2][i0 + 1] - ak[j0 + 2][i0]; + f3 = ak[j0 + 3][i0 + 1] - ak[j0 + 3][i0]; + + out[k] = ov3 * f0 + 3 * (ov2 * v * f1 + ov * v2 * f2) + v3 * f3; + } + return out; + }; + } else { + // Finally, both directions are linear: + /* eslint-disable no-unused-vars */ + return function(out, i0, j0, u, v) { + /* eslint-enable no-unused-vars */ + if(!out) out = []; + var f0, f1, k, ak; + var ov = 1 - v; + for(k = 0; k < arrays.length; k++) { + ak = arrays[k]; + f0 = ak[j0][i0 + 1] - ak[j0][i0]; + f1 = ak[j0 + 1][i0 + 1] - ak[j0 + 1][i0]; + + out[k] = ov * f0 + v * f1; + } + return out; + }; + } +}; diff --git a/src/traces/carpet/create_j_derivative_evaluator.js b/src/traces/carpet/create_j_derivative_evaluator.js new file mode 100644 index 00000000000..f7c6b897740 --- /dev/null +++ b/src/traces/carpet/create_j_derivative_evaluator.js @@ -0,0 +1,126 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = function(arrays, asmoothing, bsmoothing) { + if(asmoothing && bsmoothing) { + return function(out, i0, j0, u, v) { + if(!out) out = []; + var f0, f1, f2, f3, ak, k; + + // Since it's a grid of control points, the actual indices are * 3: + i0 *= 3; + j0 *= 3; + + // Precompute some numbers: + var u2 = u * u; + var u3 = u2 * u; + var ou = 1 - u; + var ou2 = ou * ou; + var ou3 = ou2 * ou; + + var v2 = v * v; + var ov = 1 - v; + var ov2 = ov * ov; + var ovv2 = ov * v * 2; + var a = -3 * ov2; + var b = 3 * (ov2 - ovv2); + var c = 3 * (ovv2 - v2); + var d = 3 * v2; + + for(k = 0; k < arrays.length; k++) { + ak = arrays[k]; + + // Compute the derivatives in the v-direction: + f0 = a * ak[j0][i0] + b * ak[j0 + 1][i0] + c * ak[j0 + 2][i0] + d * ak[j0 + 3][i0]; + f1 = a * ak[j0][i0 + 1] + b * ak[j0 + 1][i0 + 1] + c * ak[j0 + 2][i0 + 1] + d * ak[j0 + 3][i0 + 1]; + f2 = a * ak[j0][i0 + 2] + b * ak[j0 + 1][i0 + 2] + c * ak[j0 + 2][i0 + 2] + d * ak[j0 + 3][i0 + 2]; + f3 = a * ak[j0][i0 + 3] + b * ak[j0 + 1][i0 + 3] + c * ak[j0 + 2][i0 + 3] + d * ak[j0 + 3][i0 + 3]; + + // Now just interpolate in the v-direction since it's all separable: + out[k] = ou3 * f0 + 3 * (ou2 * u * f1 + ou * u2 * f2) + u3 * f3; + } + + return out; + }; + } else if(asmoothing) { + // Handle smooth in the a-direction but linear in the b-direction by performing four + // linear interpolations followed by one cubic interpolation of the result + return function(out, i0, j0, v, u) { + if(!out) out = []; + var f0, f1, f2, f3, k, ak; + i0 *= 3; + var u2 = u * u; + var u3 = u2 * u; + var ou = 1 - u; + var ou2 = ou * ou; + var ou3 = ou2 * ou; + for(k = 0; k < arrays.length; k++) { + ak = arrays[k]; + + f0 = ak[j0 + 1][i0] - ak[j0][i0]; + f1 = ak[j0 + 1][i0 + 1] - ak[j0][i0 + 1]; + f2 = ak[j0 + 1][i0 + 2] - ak[j0][i0 + 2]; + f3 = ak[j0 + 1][i0 + 3] - ak[j0][i0 + 3]; + + out[k] = ou3 * f0 + 3 * (ou2 * u * f1 + ou * u2 * f2) + u3 * f3; + + // mathematically equivalent: + // f0 = ou3 * ak[j0 ][i0] + 3 * (ou2 * u * ak[j0 ][i0 + 1] + ou * u2 * ak[j0 ][i0 + 2]) + u3 * ak[j0 ][i0 + 3]; + // f1 = ou3 * ak[j0 + 1][i0] + 3 * (ou2 * u * ak[j0 + 1][i0 + 1] + ou * u2 * ak[j0 + 1][i0 + 2]) + u3 * ak[j0 + 1][i0 + 3]; + // out[k] = f1 - f0; + } + return out; + }; + } else if(bsmoothing) { + // Same as the above case, except reversed: + /* eslint-disable no-unused-vars */ + return function(out, i0, j0, u, v) { + /* eslint-enable no-unused-vars */ + if(!out) out = []; + var f0, f1, k, ak; + j0 *= 3; + var ou = 1 - u; + var v2 = v * v; + var ov = 1 - v; + var ov2 = ov * ov; + var ovv2 = ov * v * 2; + var a = -3 * ov2; + var b = 3 * (ov2 - ovv2); + var c = 3 * (ovv2 - v2); + var d = 3 * v2; + for(k = 0; k < arrays.length; k++) { + ak = arrays[k]; + f0 = a * ak[j0][i0] + b * ak[j0 + 1][i0] + c * ak[j0 + 2][i0] + d * ak[j0 + 3][i0]; + f1 = a * ak[j0][i0 + 1] + b * ak[j0 + 1][i0 + 1] + c * ak[j0 + 2][i0 + 1] + d * ak[j0 + 3][i0 + 1]; + + out[k] = ou * f0 + u * f1; + } + return out; + }; + } else { + // Finally, both directions are linear: + /* eslint-disable no-unused-vars */ + return function(out, i0, j0, v, u) { + /* eslint-enable no-unused-vars */ + if(!out) out = []; + var f0, f1, k, ak; + var ov = 1 - v; + for(k = 0; k < arrays.length; k++) { + ak = arrays[k]; + f0 = ak[j0 + 1][i0] - ak[j0][i0]; + f1 = ak[j0 + 1][i0 + 1] - ak[j0][i0 + 1]; + + out[k] = ov * f0 + v * f1; + } + return out; + }; + } + +}; diff --git a/src/traces/carpet/create_spline_evaluator.js b/src/traces/carpet/create_spline_evaluator.js new file mode 100644 index 00000000000..b5870ccbdd0 --- /dev/null +++ b/src/traces/carpet/create_spline_evaluator.js @@ -0,0 +1,149 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/* + * Return a function that evaluates a set of linear or bicubic control points. + * This will get evaluated a lot, so we'll at least do a bit of extra work to + * flatten some of the choices. In particular, we'll unroll the linear/bicubic + * combinations and we'll allow computing results in parallel to cut down + * on repeated arithmetic. + * + * Take note that we don't search for the correct range in this function. The + * reason is for consistency due to the corrresponding derivative function. In + * particular, the derivatives aren't continuous across cells, so it's important + * to be able control whether the derivative at a cell boundary is approached + * from one side or the other. + */ +module.exports = function(arrays, na, nb, asmoothing, bsmoothing) { + var imax = na - 2; + var jmax = nb - 2; + + if(asmoothing && bsmoothing) { + return function(out, i, j) { + if(!out) out = []; + var f0, f1, f2, f3, ak, k; + + var i0 = Math.max(0, Math.min(Math.floor(i), imax)); + var j0 = Math.max(0, Math.min(Math.floor(j), jmax)); + var u = Math.max(0, Math.min(1, i - i0)); + var v = Math.max(0, Math.min(1, j - j0)); + + // Since it's a grid of control points, the actual indices are * 3: + i0 *= 3; + j0 *= 3; + + // Precompute some numbers: + var u2 = u * u; + var u3 = u2 * u; + var ou = 1 - u; + var ou2 = ou * ou; + var ou3 = ou2 * ou; + + var v2 = v * v; + var v3 = v2 * v; + var ov = 1 - v; + var ov2 = ov * ov; + var ov3 = ov2 * ov; + + for(k = 0; k < arrays.length; k++) { + ak = arrays[k]; + f0 = ou3 * ak[j0][i0] + 3 * (ou2 * u * ak[j0][i0 + 1] + ou * u2 * ak[j0][i0 + 2]) + u3 * ak[j0][i0 + 3]; + f1 = ou3 * ak[j0 + 1][i0] + 3 * (ou2 * u * ak[j0 + 1][i0 + 1] + ou * u2 * ak[j0 + 1][i0 + 2]) + u3 * ak[j0 + 1][i0 + 3]; + f2 = ou3 * ak[j0 + 2][i0] + 3 * (ou2 * u * ak[j0 + 2][i0 + 1] + ou * u2 * ak[j0 + 2][i0 + 2]) + u3 * ak[j0 + 2][i0 + 3]; + f3 = ou3 * ak[j0 + 3][i0] + 3 * (ou2 * u * ak[j0 + 3][i0 + 1] + ou * u2 * ak[j0 + 3][i0 + 2]) + u3 * ak[j0 + 3][i0 + 3]; + out[k] = ov3 * f0 + 3 * (ov2 * v * f1 + ov * v2 * f2) + v3 * f3; + } + + return out; + }; + } else if(asmoothing) { + // Handle smooth in the a-direction but linear in the b-direction by performing four + // linear interpolations followed by one cubic interpolation of the result + return function(out, i, j) { + if(!out) out = []; + + var i0 = Math.max(0, Math.min(Math.floor(i), imax)); + var j0 = Math.max(0, Math.min(Math.floor(j), jmax)); + var u = Math.max(0, Math.min(1, i - i0)); + var v = Math.max(0, Math.min(1, j - j0)); + + var f0, f1, f2, f3, k, ak; + i0 *= 3; + var u2 = u * u; + var u3 = u2 * u; + var ou = 1 - u; + var ou2 = ou * ou; + var ou3 = ou2 * ou; + var ov = 1 - v; + for(k = 0; k < arrays.length; k++) { + ak = arrays[k]; + f0 = ov * ak[j0][i0] + v * ak[j0 + 1][i0]; + f1 = ov * ak[j0][i0 + 1] + v * ak[j0 + 1][i0 + 1]; + f2 = ov * ak[j0][i0 + 2] + v * ak[j0 + 1][i0 + 1]; + f3 = ov * ak[j0][i0 + 3] + v * ak[j0 + 1][i0 + 1]; + + out[k] = ou3 * f0 + 3 * (ou2 * u * f1 + ou * u2 * f2) + u3 * f3; + } + return out; + }; + } else if(bsmoothing) { + // Same as the above case, except reversed: + return function(out, i, j) { + if(!out) out = []; + + var i0 = Math.max(0, Math.min(Math.floor(i), imax)); + var j0 = Math.max(0, Math.min(Math.floor(j), jmax)); + var u = Math.max(0, Math.min(1, i - i0)); + var v = Math.max(0, Math.min(1, j - j0)); + + var f0, f1, f2, f3, k, ak; + j0 *= 3; + var v2 = v * v; + var v3 = v2 * v; + var ov = 1 - v; + var ov2 = ov * ov; + var ov3 = ov2 * ov; + var ou = 1 - u; + for(k = 0; k < arrays.length; k++) { + ak = arrays[k]; + f0 = ou * ak[j0][i0] + u * ak[j0][i0 + 1]; + f1 = ou * ak[j0 + 1][i0] + u * ak[j0 + 1][i0 + 1]; + f2 = ou * ak[j0 + 2][i0] + u * ak[j0 + 2][i0 + 1]; + f3 = ou * ak[j0 + 3][i0] + u * ak[j0 + 3][i0 + 1]; + + out[k] = ov3 * f0 + 3 * (ov2 * v * f1 + ov * v2 * f2) + v3 * f3; + } + return out; + }; + } else { + // Finally, both directions are linear: + return function(out, i, j) { + if(!out) out = []; + + var i0 = Math.max(0, Math.min(Math.floor(i), imax)); + var j0 = Math.max(0, Math.min(Math.floor(j), jmax)); + var u = Math.max(0, Math.min(1, i - i0)); + var v = Math.max(0, Math.min(1, j - j0)); + + var f0, f1, k, ak; + var ov = 1 - v; + var ou = 1 - u; + for(k = 0; k < arrays.length; k++) { + ak = arrays[k]; + f0 = ou * ak[j0][i0] + u * ak[j0][i0 + 1]; + f1 = ou * ak[j0 + 1][i0] + u * ak[j0 + 1][i0 + 1]; + + out[k] = ov * f0 + v * f1; + } + return out; + }; + } + +}; diff --git a/src/traces/carpet/defaults.js b/src/traces/carpet/defaults.js new file mode 100644 index 00000000000..332117da1c4 --- /dev/null +++ b/src/traces/carpet/defaults.js @@ -0,0 +1,60 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Lib = require('../../lib'); +var handleXYDefaults = require('./xy_defaults'); +var handleABDefaults = require('./ab_defaults'); +var setConvert = require('./set_convert'); +var attributes = require('./attributes'); +var colorAttrs = require('../../components/color/attributes'); + +module.exports = function supplyDefaults(traceIn, traceOut, dfltColor, fullLayout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var defaultColor = coerce('color', colorAttrs.defaultLine); + Lib.coerceFont(coerce, 'font'); + + coerce('carpet'); + + handleABDefaults(traceIn, traceOut, fullLayout, coerce, defaultColor); + + if(!traceOut.a || !traceOut.b) { + traceOut.visible = false; + return; + } + + if(traceOut.a.length < 3) { + traceOut.aaxis.smoothing = 0; + } + + if(traceOut.b.length < 3) { + traceOut.baxis.smoothing = 0; + } + + // NB: the input is x/y arrays. You should know that the *first* dimension of x and y + // corresponds to b and the second to a. This sounds backwards but ends up making sense + // the important part to know is that when you write y[j][i], j goes from 0 to b.length - 1 + // and i goes from 0 to a.length - 1. + var len = handleXYDefaults(traceIn, traceOut, coerce); + + setConvert(traceOut); + + if(traceOut._cheater) { + coerce('cheaterslope'); + } + + if(!len) { + traceOut.visible = false; + return; + } +}; diff --git a/src/traces/carpet/has_columns.js b/src/traces/carpet/has_columns.js new file mode 100644 index 00000000000..66e1ef74c89 --- /dev/null +++ b/src/traces/carpet/has_columns.js @@ -0,0 +1,14 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +module.exports = function(data) { + return Array.isArray(data[0]); +}; diff --git a/src/traces/carpet/index.js b/src/traces/carpet/index.js new file mode 100644 index 00000000000..20fbe7fae90 --- /dev/null +++ b/src/traces/carpet/index.js @@ -0,0 +1,36 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Carpet = {}; + +Carpet.attributes = require('./attributes'); +Carpet.supplyDefaults = require('./defaults'); +Carpet.plot = require('./plot'); +Carpet.calc = require('./calc'); +Carpet.animatable = true; + +Carpet.moduleType = 'trace'; +Carpet.name = 'carpet'; +Carpet.basePlotModule = require('../../plots/cartesian'); +Carpet.categories = ['cartesian', 'carpet', 'carpetAxis', 'notLegendIsolatable']; +Carpet.meta = { + description: [ + 'The data describing carpet axis layout is set in `y` and (optionally)', + 'also `x`. If only `y` is present, `x` the plot is interpreted as a', + 'cheater plot and is filled in using the `y` values.', + + '`x` and `y` may either be 2D arrays matching with each dimension matching', + 'that of `a` and `b`, or they may be 1D arrays with total length equal to', + 'that of `a` and `b`.' + ].join(' ') +}; + +module.exports = Carpet; diff --git a/src/traces/carpet/lookup_carpetid.js b/src/traces/carpet/lookup_carpetid.js new file mode 100644 index 00000000000..574353dba46 --- /dev/null +++ b/src/traces/carpet/lookup_carpetid.js @@ -0,0 +1,34 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/* + * Given a trace, look up the carpet axis by carpet. + */ +module.exports = function(gd, trace) { + var n = gd._fullData.length; + var firstAxis; + for(var i = 0; i < n; i++) { + var maybeCarpet = gd._fullData[i]; + + if(maybeCarpet.index === trace.index) continue; + + if(maybeCarpet.type === 'carpet') { + if(!firstAxis) { + firstAxis = maybeCarpet; + } + + if(maybeCarpet.carpet === trace.carpet) { + return maybeCarpet; + } + } + } + + return firstAxis; +}; diff --git a/src/traces/carpet/makepath.js b/src/traces/carpet/makepath.js new file mode 100644 index 00000000000..4966eab4506 --- /dev/null +++ b/src/traces/carpet/makepath.js @@ -0,0 +1,29 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = function makePath(xp, yp, isBicubic) { + // Prevent d3 errors that would result otherwise: + if(xp.length === 0) return ''; + + var i, path = []; + var stride = isBicubic ? 3 : 1; + for(i = 0; i < xp.length; i += stride) { + path.push(xp[i] + ',' + yp[i]); + + if(isBicubic && i < xp.length - stride) { + path.push('C'); + path.push([ + xp[i + 1] + ',' + yp[i + 1], + xp[i + 2] + ',' + yp[i + 2] + ' ', + ].join(' ')); + } + } + return path.join(isBicubic ? '' : 'L'); +}; diff --git a/src/traces/carpet/map_1d_array.js b/src/traces/carpet/map_1d_array.js new file mode 100644 index 00000000000..907618f10e2 --- /dev/null +++ b/src/traces/carpet/map_1d_array.js @@ -0,0 +1,33 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/* + * Map an array of x or y coordinates (c) to screen-space pixel coordinates (p). + * The output array is optional, but if provided, it will be reused without + * reallocation to the extent possible. + */ +module.exports = function mapArray(out, data, func) { + var i; + + if(!Array.isArray(out)) { + // If not an array, make it an array: + out = []; + } else if(out.length > data.length) { + // If too long, truncate. (If too short, it will grow + // automatically so we don't care about that case) + out = out.slice(0, data.length); + } + + for(i = 0; i < data.length; i++) { + out[i] = func(data[i]); + } + + return out; +}; diff --git a/src/traces/carpet/map_2d_array.js b/src/traces/carpet/map_2d_array.js new file mode 100644 index 00000000000..341f52b8e34 --- /dev/null +++ b/src/traces/carpet/map_2d_array.js @@ -0,0 +1,43 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/* + * Map an array of x or y coordinates (c) to screen-space pixel coordinates (p). + * The output array is optional, but if provided, it will be reused without + * reallocation to the extent possible. + */ +module.exports = function mapArray(out, data, func) { + var i, j; + + if(!Array.isArray(out)) { + // If not an array, make it an array: + out = []; + } else if(out.length > data.length) { + // If too long, truncate. (If too short, it will grow + // automatically so we don't care about that case) + out = out.slice(0, data.length); + } + + for(i = 0; i < data.length; i++) { + if(!Array.isArray(out[i])) { + // If not an array, make it an array: + out[i] = []; + } else if(out[i].length > data.length) { + // If too long, truncate. (If too short, it will grow + // automatically so we don't care about[i] that case) + out[i] = out[i].slice(0, data.length); + } + + for(j = 0; j < data[0].length; j++) { + out[i][j] = func(data[i][j]); + } + } + return out; +}; diff --git a/src/traces/carpet/orient_text.js b/src/traces/carpet/orient_text.js new file mode 100644 index 00000000000..476e3c5c967 --- /dev/null +++ b/src/traces/carpet/orient_text.js @@ -0,0 +1,40 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +module.exports = function orientText(trace, xaxis, yaxis, xy, dxy, refDxy) { + var dx = dxy[0] * trace.dpdx(xaxis); + var dy = dxy[1] * trace.dpdy(yaxis); + var flip = 1; + + var offsetMultiplier = 1.0; + if(refDxy) { + var l1 = Math.sqrt(dxy[0] * dxy[0] + dxy[1] * dxy[1]); + var l2 = Math.sqrt(refDxy[0] * refDxy[0] + refDxy[1] * refDxy[1]); + var dot = (dxy[0] * refDxy[0] + dxy[1] * refDxy[1]) / l1 / l2; + offsetMultiplier = Math.max(0.0, dot); + } + + var angle = Math.atan2(dy, dx) * 180 / Math.PI; + if(angle < -90) { + angle += 180; + flip = -flip; + } else if(angle > 90) { + angle -= 180; + flip = -flip; + } + + return { + angle: angle, + flip: flip, + p: trace.c2p(xy, xaxis, yaxis), + offsetMultplier: offsetMultiplier + }; +}; diff --git a/src/traces/carpet/plot.js b/src/traces/carpet/plot.js new file mode 100644 index 00000000000..9e62b3221c3 --- /dev/null +++ b/src/traces/carpet/plot.js @@ -0,0 +1,228 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = require('d3'); +var Drawing = require('../../components/drawing'); +var map1dArray = require('./map_1d_array'); +var makepath = require('./makepath'); +var orientText = require('./orient_text'); + +module.exports = function plot(gd, plotinfo, cdcarpet) { + for(var i = 0; i < cdcarpet.length; i++) { + plotOne(gd, plotinfo, cdcarpet[i]); + } +}; + +function makeg(el, type, klass) { + var join = el.selectAll(type + '.' + klass).data([0]); + join.enter().append(type).classed(klass, true); + return join; +} + +function plotOne(gd, plotinfo, cd) { + var t = cd[0]; + var trace = cd[0].trace, + xa = plotinfo.xaxis, + ya = plotinfo.yaxis, + aax = trace.aaxis, + bax = trace.baxis, + fullLayout = gd._fullLayout; + // uid = trace.uid, + // id = 'carpet' + uid; + + var gridLayer = plotinfo.plot.selectAll('.carpetlayer'); + var clipLayer = makeg(fullLayout._defs, 'g', 'clips'); + + var axisLayer = makeg(gridLayer, 'g', 'carpet' + trace.uid).classed('trace', true); + var minorLayer = makeg(axisLayer, 'g', 'minorlayer'); + var majorLayer = makeg(axisLayer, 'g', 'majorlayer'); + var boundaryLayer = makeg(axisLayer, 'g', 'boundarylayer'); + var labelLayer = makeg(axisLayer, 'g', 'labellayer'); + + axisLayer.style('opacity', trace.opacity); + + drawGridLines(xa, ya, majorLayer, aax, 'a', aax._gridlines, true); + drawGridLines(xa, ya, majorLayer, bax, 'b', bax._gridlines, true); + drawGridLines(xa, ya, minorLayer, aax, 'a', aax._minorgridlines, true); + drawGridLines(xa, ya, minorLayer, bax, 'b', bax._minorgridlines, true); + + // NB: These are not ommitted if the lines are not active. The joins must be executed + // in order for them to get cleaned up without a full redraw + drawGridLines(xa, ya, boundaryLayer, aax, 'a-boundary', aax._boundarylines); + drawGridLines(xa, ya, boundaryLayer, bax, 'b-boundary', bax._boundarylines); + + var maxAExtent = drawAxisLabels(gd._tester, xa, ya, trace, t, labelLayer, aax._labels, 'a-label'); + var maxBExtent = drawAxisLabels(gd._tester, xa, ya, trace, t, labelLayer, bax._labels, 'b-label'); + + drawAxisTitles(labelLayer, trace, t, xa, ya, maxAExtent, maxBExtent); + + // Swap for debugging in order to draw directly: + // drawClipPath(trace, axisLayer, xa, ya); + drawClipPath(trace, t, clipLayer, xa, ya); +} + +function drawClipPath(trace, t, layer, xaxis, yaxis) { + var seg, xp, yp, i; + // var clip = makeg(layer, 'g', 'carpetclip'); + trace.clipPathId = 'clip' + trace.uid + 'carpet'; + + var clip = layer.select('#' + trace.clipPathId); + + if(!clip.size()) { + clip = layer.append('clipPath') + .classed('carpetclip', true); + } + + var path = makeg(clip, 'path', 'carpetboundary'); + var segments = t.clipsegments; + var segs = []; + + for(i = 0; i < segments.length; i++) { + seg = segments[i]; + xp = map1dArray([], seg.x, xaxis.c2p); + yp = map1dArray([], seg.y, yaxis.c2p); + segs.push(makepath(xp, yp, seg.bicubic)); + } + + // This could be optimized ever so slightly to avoid no-op L segments + // at the corners, but it's so negligible that I don't think it's worth + // the extra complexity + trace.clipPathData = 'M' + segs.join('L') + 'Z'; + clip.attr('id', trace.clipPathId); + path.attr('d', trace.clipPathData); + // .style('stroke-width', 20) + // .style('vector-effect', 'non-scaling-stroke') + // .style('stroke', 'black') + // .style('fill', 'rgba(0, 0, 0, 0.1)'); +} + +function drawGridLines(xaxis, yaxis, layer, axis, axisLetter, gridlines) { + var lineClass = 'const-' + axisLetter + '-lines'; + var gridJoin = layer.selectAll('.' + lineClass).data(gridlines); + + gridJoin.enter().append('path') + .classed(lineClass, true) + .style('vector-effect', 'non-scaling-stroke'); + + gridJoin.each(function(d) { + var gridline = d; + var x = gridline.x; + var y = gridline.y; + + var xp = map1dArray([], x, xaxis.c2p); + var yp = map1dArray([], y, yaxis.c2p); + + var path = 'M' + makepath(xp, yp, gridline.smoothing); + + var el = d3.select(this); + + el.attr('d', path) + .style('stroke-width', gridline.width) + .style('stroke', gridline.color) + .style('fill', 'none'); + }); + + gridJoin.exit().remove(); +} + +function drawAxisLabels(tester, xaxis, yaxis, trace, t, layer, labels, labelClass) { + var labelJoin = layer.selectAll('text.' + labelClass).data(labels); + + labelJoin.enter().append('text') + .classed(labelClass, true); + + var maxExtent = 0; + + labelJoin.each(function(label) { + // Most of the positioning is done in calc_labels. Only the parts that depend upon + // the screen space representation of the x and y axes are here: + var orientation; + if(label.axis.tickangle === 'auto') { + orientation = orientText(trace, xaxis, yaxis, label.xy, label.dxy); + } else { + var angle = (label.axis.tickangle + 180.0) * Math.PI / 180.0; + orientation = orientText(trace, xaxis, yaxis, label.xy, [Math.cos(angle), Math.sin(angle)]); + } + var direction = (label.endAnchor ? -1 : 1) * orientation.flip; + var bbox = Drawing.measureText(tester, label.text, label.font); + + d3.select(this) + .attr('text-anchor', direction > 0 ? 'start' : 'end') + .text(label.text) + .attr('transform', + // Translate to the correct point: + 'translate(' + orientation.p[0] + ',' + orientation.p[1] + ') ' + + // Rotate to line up with grid line tangent: + 'rotate(' + orientation.angle + ')' + + // Adjust the baseline and indentation: + 'translate(' + label.axis.labelpadding * direction + ',' + bbox.height * 0.3 + ')' + ) + .call(Drawing.font, label.font.family, label.font.size, label.font.color); + + maxExtent = Math.max(maxExtent, bbox.width + label.axis.labelpadding); + }); + + labelJoin.exit().remove(); + + return maxExtent; +} + +function drawAxisTitles(layer, trace, t, xa, ya, maxAExtent, maxBExtent) { + var a, b, xy, dxy; + + a = 0.5 * (trace.a[0] + trace.a[trace.a.length - 1]); + b = trace.b[0]; + xy = trace.ab2xy(a, b, true); + dxy = trace.dxyda_rough(a, b); + drawAxisTitle(layer, trace, t, xy, dxy, trace.aaxis, xa, ya, maxAExtent, 'a-title'); + + a = trace.a[0]; + b = 0.5 * (trace.b[0] + trace.b[trace.b.length - 1]); + xy = trace.ab2xy(a, b, true); + dxy = trace.dxydb_rough(a, b); + drawAxisTitle(layer, trace, t, xy, dxy, trace.baxis, xa, ya, maxBExtent, 'b-title'); +} + +function drawAxisTitle(layer, trace, t, xy, dxy, axis, xa, ya, offset, labelClass) { + var data = []; + if(axis.title) data.push(axis.title); + var titleJoin = layer.selectAll('text.' + labelClass).data(data); + + titleJoin.enter().append('text') + .classed(labelClass, true); + + // There's only one, but we'll do it as a join so it's updated nicely: + titleJoin.each(function() { + var orientation = orientText(trace, xa, ya, xy, dxy); + + if(['start', 'both'].indexOf(axis.showticklabels) === -1) { + offset = 0; + } + + // In addition to the size of the labels, add on some extra padding: + offset += axis.titlefont.size + axis.titleoffset; + + + var el = d3.select(this); + + el.text(axis.title || '') + .attr('transform', + 'translate(' + orientation.p[0] + ',' + orientation.p[1] + ') ' + + 'rotate(' + orientation.angle + ') ' + + 'translate(0,' + offset + ')' + ) + .classed('user-select-none', true) + .attr('text-anchor', 'middle') + .call(Drawing.font, axis.titlefont); + }); + + titleJoin.exit().remove(); +} diff --git a/src/traces/carpet/set_convert.js b/src/traces/carpet/set_convert.js new file mode 100644 index 00000000000..be4316420dd --- /dev/null +++ b/src/traces/carpet/set_convert.js @@ -0,0 +1,285 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var constants = require('./constants'); +var search = require('../../lib/search').findBin; +var computeControlPoints = require('./compute_control_points'); +var createSplineEvaluator = require('./create_spline_evaluator'); +var createIDerivativeEvaluator = require('./create_i_derivative_evaluator'); +var createJDerivativeEvaluator = require('./create_j_derivative_evaluator'); + +/* + * Create conversion functions to go from one basis to another. In particular the letter + * abbreviations are: + * + * i: i/j coordinates along the grid. Integer values correspond to data points + * a: real-valued coordinates along the a/b axes + * c: cartesian x-y coordinates + * p: screen-space pixel coordinates + */ +module.exports = function setConvert(trace) { + var a = trace.a; + var b = trace.b; + var na = trace.a.length; + var nb = trace.b.length; + var aax = trace.aaxis; + var bax = trace.baxis; + + // Grab the limits once rather than recomputing the bounds for every point + // independently: + var amin = a[0]; + var amax = a[na - 1]; + var bmin = b[0]; + var bmax = b[nb - 1]; + var arange = a[a.length - 1] - a[0]; + var brange = b[b.length - 1] - b[0]; + + // Compute the tolerance so that points are visible slightly outside the + // defined carpet axis: + var atol = arange * constants.RELATIVE_CULL_TOLERANCE; + var btol = brange * constants.RELATIVE_CULL_TOLERANCE; + + // Expand the limits to include the relative tolerance: + amin -= atol; + amax += atol; + bmin -= btol; + bmax += btol; + + trace.isVisible = function(a, b) { + return a > amin && a < amax && b > bmin && b < bmax; + }; + + trace.isOccluded = function(a, b) { + return a < amin || a > amax || b < bmin || b > bmax; + }; + + // XXX: ONLY PASSTHRU. ONLY. No, ONLY. + aax.c2p = function(v) { return v; }; + bax.c2p = function(v) { return v; }; + + trace.setScale = function() { + var x = trace.x; + var y = trace.y; + + // This is potentially a very expensive step! It does the bulk of the work of constructing + // an expanded basis of control points. Note in particular that it overwrites the existing + // basis without creating a new array since that would potentially thrash the garbage + // collector. + var result = computeControlPoints(trace.xctrl, trace.yctrl, x, y, aax.smoothing, bax.smoothing); + trace.xctrl = result[0]; + trace.yctrl = result[1]; + + // This step is the second step in the process, but it's somewhat simpler. It just unrolls + // some logic since it would be unnecessarily expensive to compute both interpolations + // nearly identically but separately and to include a bunch of linear vs. bicubic logic in + // every single call. + trace.evalxy = createSplineEvaluator([trace.xctrl, trace.yctrl], na, nb, aax.smoothing, bax.smoothing); + + trace.dxydi = createIDerivativeEvaluator([trace.xctrl, trace.yctrl], aax.smoothing, bax.smoothing); + trace.dxydj = createJDerivativeEvaluator([trace.xctrl, trace.yctrl], aax.smoothing, bax.smoothing); + }; + + /* + * Convert from i/j data grid coordinates to a/b values. Note in particular that this + * is *linear* interpolation, even if the data is interpolated bicubically. + */ + trace.i2a = function(i) { + var i0 = Math.max(0, Math.floor(i[0]), na - 2); + var ti = i[0] - i0; + return (1 - ti) * a[i0] + ti * a[i0 + 1]; + }; + + trace.j2b = function(j) { + var j0 = Math.max(0, Math.floor(j[1]), na - 2); + var tj = j[1] - j0; + return (1 - tj) * b[j0] + tj * b[j0 + 1]; + }; + + trace.ij2ab = function(ij) { + return [trace.i2a(ij[0]), trace.j2b(ij[1])]; + }; + + /* + * Convert from a/b coordinates to i/j grid-numbered coordinates. This requires searching + * through the a/b data arrays and assumes they are monotonic, which is presumed to have + * been enforced already. + */ + trace.a2i = function(aval) { + var i0 = Math.max(0, Math.min(search(aval, a), na - 2)); + var a0 = a[i0]; + var a1 = a[i0 + 1]; + return Math.max(0, Math.min(na - 1, i0 + (aval - a0) / (a1 - a0))); + }; + + trace.b2j = function(bval) { + var j0 = Math.max(0, Math.min(search(bval, b), nb - 2)); + var b0 = b[j0]; + var b1 = b[j0 + 1]; + return Math.max(0, Math.min(nb - 1, j0 + (bval - b0) / (b1 - b0))); + }; + + trace.ab2ij = function(ab) { + return [trace.a2i(ab[0]), trace.b2j(ab[1])]; + }; + + /* + * Convert from i/j coordinates to x/y caretesian coordinates. This means either bilinear + * or bicubic spline evaluation, but the hard part is already done at this point. + */ + trace.i2c = function(i, j) { + return trace.evalxy([], i, j); + }; + + trace.ab2xy = function(aval, bval, extrapolate) { + if(!extrapolate && (aval < a[0] || aval > a[na - 1] | bval < b[0] || bval > b[nb - 1])) { + return [false, false]; + } + var i = trace.a2i(aval); + var j = trace.b2j(bval); + + var pt = trace.evalxy([], i, j); + + if(extrapolate) { + // This section uses the boundary derivatives to extrapolate linearly outside + // the defined range. Consider a scatter line with one point inside the carpet + // axis and one point outside. If we don't extrapolate, we can't draw the line + // at all. + var iex = 0; + var jex = 0; + var der = []; + + var i0, ti, j0, tj; + if(aval < a[0]) { + i0 = 0; + ti = 0; + iex = (aval - a[0]) / (a[1] - a[0]); + } else if(aval > a[na - 1]) { + i0 = na - 2; + ti = 1; + iex = (aval - a[na - 1]) / (a[na - 1] - a[na - 2]); + } else { + i0 = Math.max(0, Math.min(na - 2, Math.floor(i))); + ti = i - i0; + } + + if(bval < b[0]) { + j0 = 0; + tj = 0; + jex = (bval - b[0]) / (b[1] - b[0]); + } else if(bval > b[nb - 1]) { + j0 = nb - 2; + tj = 1; + jex = (bval - b[nb - 1]) / (b[nb - 1] - b[nb - 2]); + } else { + j0 = Math.max(0, Math.min(nb - 2, Math.floor(j))); + tj = j - j0; + } + + if(iex) { + trace.dxydi(der, i0, j0, ti, tj); + pt[0] += der[0] * iex; + pt[1] += der[1] * iex; + } + + if(jex) { + trace.dxydj(der, i0, j0, ti, tj); + pt[0] += der[0] * jex; + pt[1] += der[1] * jex; + } + } + + return pt; + }; + + + trace.c2p = function(xy, xa, ya) { + return [xa.c2p(xy[0]), ya.c2p(xy[1])]; + }; + + trace.p2x = function(p, xa, ya) { + return [xa.p2c(p[0]), ya.p2c(p[1])]; + }; + + trace.dadi = function(i /* , u*/) { + // Right now only a piecewise linear a or b basis is permitted since smoother interpolation + // would cause monotonicity problems. As a retult, u is entirely disregarded in this + // computation, though we'll specify it as a parameter for the sake of completeness and + // future-proofing. It would be possible to use monotonic cubic interpolation, for example. + // + // See: https://en.wikipedia.org/wiki/Monotone_cubic_interpolation + + // u = u || 0; + + var i0 = Math.max(0, Math.min(a.length - 2, i)); + + // The step (demoninator) is implicitly 1 since that's the grid spacing. + return a[i0 + 1] - a[i0]; + }; + + trace.dbdj = function(j /* , v*/) { + // See above caveats for dadi which also apply here + var j0 = Math.max(0, Math.min(b.length - 2, j)); + + // The step (demoninator) is implicitly 1 since that's the grid spacing. + return b[j0 + 1] - b[j0]; + }; + + // Takes: grid cell coordinate (i, j) and fractional grid cell coordinates (u, v) + // Returns: (dx/da, dy/db) + // + // NB: separate grid cell + fractional grid cell coordinate format is due to the discontinuous + // derivative, as described better in create_i_derivative_evaluator.js + trace.dxyda = function(i0, j0, u, v) { + var dxydi = trace.dxydi(null, i0, j0, u, v); + var dadi = trace.dadi(i0, u); + + return [dxydi[0] / dadi, dxydi[1] / dadi]; + }; + + trace.dxydb = function(i0, j0, u, v) { + var dxydj = trace.dxydj(null, i0, j0, u, v); + var dbdj = trace.dbdj(j0, v); + + return [dxydj[0] / dbdj, dxydj[1] / dbdj]; + }; + + // Sometimes we don't care about precision and all we really want is decent rough + // directions (as is the case with labels). In that case, we can do a very rough finite + // difference and spare having to worry about precise grid coordinates: + trace.dxyda_rough = function(a, b, reldiff) { + var h = arange * (reldiff || 0.1); + var plus = trace.ab2xy(a + h, b, true); + var minus = trace.ab2xy(a - h, b, true); + + return [ + (plus[0] - minus[0]) * 0.5 / h, + (plus[1] - minus[1]) * 0.5 / h + ]; + }; + + trace.dxydb_rough = function(a, b, reldiff) { + var h = brange * (reldiff || 0.1); + var plus = trace.ab2xy(a, b + h, true); + var minus = trace.ab2xy(a, b - h, true); + + return [ + (plus[0] - minus[0]) * 0.5 / h, + (plus[1] - minus[1]) * 0.5 / h + ]; + }; + + trace.dpdx = function(xa) { + return xa._m; + }; + + trace.dpdy = function(ya) { + return ya._m; + }; +}; diff --git a/src/traces/carpet/smooth_fill_2d_array.js b/src/traces/carpet/smooth_fill_2d_array.js new file mode 100644 index 00000000000..bc3686e96f5 --- /dev/null +++ b/src/traces/carpet/smooth_fill_2d_array.js @@ -0,0 +1,221 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = require('../../lib'); + +/* + * Given a 2D array as well as a basis in either direction, this function fills in the + * 2D array using a combination of smoothing and extrapolation. This is rather important + * for carpet plots since it's used for layout so that we can't simply omit or blank out + * points. We need a reasonable guess so that the interpolation puts points somewhere + * even if we were to somehow represent that the data was missing later on. + * + * input: + * - data: 2D array of arrays + * - a: array such that a.length === data[0].length + * - b: array such that b.length === data.length + */ +module.exports = function smoothFill2dArray(data, a, b) { + var i, j, k; + var ip = []; + var jp = []; + // var neighborCnts = []; + + var ni = data[0].length; + var nj = data.length; + + function avgSurrounding(i, j) { + // As a low-quality start, we can simply average surrounding points (in a not + // non-uniform grid aware manner): + var sum = 0.0; + var val; + var cnt = 0; + if(i > 0 && (val = data[j][i - 1]) !== undefined) { + cnt++; + sum += val; + } + if(i < ni - 1 && (val = data[j][i + 1]) !== undefined) { + cnt++; + sum += val; + } + if(j > 0 && (val = data[j - 1][i]) !== undefined) { + cnt++; + sum += val; + } + if(j < nj - 1 && (val = data[j + 1][i]) !== undefined) { + cnt++; + sum += val; + } + return sum / Math.max(1, cnt); + + } + + // This loop iterates over all cells. Any cells that are null will be noted and those + // are the only points we will loop over and update via laplace's equation. Points with + // any neighbors will receive the average. If there are no neighboring points, then they + // will be set to zero. Also as we go, track the maximum magnitude so that we can scale + // our tolerance accordingly. + var dmax = 0.0; + for(i = 0; i < ni; i++) { + for(j = 0; j < nj; j++) { + if(data[j][i] === undefined) { + ip.push(i); + jp.push(j); + + data[j][i] = avgSurrounding(i, j); + // neighborCnts.push(result.neighbors); + } + dmax = Math.max(dmax, Math.abs(data[j][i])); + } + } + + if(!ip.length) return data; + + // The tolerance doesn't need to be excessive. It's just for display positioning + var dxp, dxm, dap, dam, dbp, dbm, c, d, diff, reldiff, overrelaxation; + var tol = 1e-5; + var resid = 0; + var itermax = 100; + var iter = 0; + var n = ip.length; + do { + resid = 0; + // Normally we'd loop in two dimensions, but not all points are blank and need + // an update, so we instead loop only over the points that were tabulated above + for(k = 0; k < n; k++) { + i = ip[k]; + j = jp[k]; + // neighborCnt = neighborCnts[k]; + + // Track a counter for how many contributions there are. We'll use this counter + // to average at the end, which reduces to laplace's equation with neumann boundary + // conditions on the first derivative (second derivative is zero so that we get + // a nice linear extrapolation at the boundaries). + var boundaryCnt = 0; + var newVal = 0; + + var d0, d1, x0, x1, i0, j0; + if(i === 0) { + // If this lies along the i = 0 boundary, extrapolate from the two points + // to the right of this point. Note that the finite differences take into + // account non-uniform grid spacing: + i0 = Math.min(ni - 1, 2); + x0 = a[i0]; + x1 = a[1]; + d0 = data[j][i0]; + d1 = data[j][1]; + newVal += d1 + (d1 - d0) * (a[0] - x1) / (x1 - x0); + boundaryCnt++; + } else if(i === ni - 1) { + // If along the high i boundary, extrapolate from the two points to the + // left of this point + i0 = Math.max(0, ni - 3); + x0 = a[i0]; + x1 = a[ni - 2]; + d0 = data[j][i0]; + d1 = data[j][ni - 2]; + newVal += d1 + (d1 - d0) * (a[ni - 1] - x1) / (x1 - x0); + boundaryCnt++; + } + + if((i === 0 || i === ni - 1) && (j > 0 && j < nj - 1)) { + // If along the min(i) or max(i) boundaries, also smooth vertically as long + // as we're not in a corner. Note that the finite differences used here + // are also aware of nonuniform grid spacing: + dxp = b[j + 1] - b[j]; + dxm = b[j] - b[j - 1]; + newVal += (dxm * data[j + 1][i] + dxp * data[j - 1][i]) / (dxm + dxp); + boundaryCnt++; + } + + if(j === 0) { + // If along the j = 0 boundary, extrpolate this point from the two points + // above it + j0 = Math.min(nj - 1, 2); + x0 = b[j0]; + x1 = b[1]; + d0 = data[j0][i]; + d1 = data[1][i]; + newVal += d1 + (d1 - d0) * (b[0] - x1) / (x1 - x0); + boundaryCnt++; + } else if(j === nj - 1) { + // Same for the max j boundary from the cells below it: + j0 = Math.max(0, nj - 3); + x0 = b[j0]; + x1 = b[nj - 2]; + d0 = data[j0][i]; + d1 = data[nj - 2][i]; + newVal += d1 + (d1 - d0) * (b[nj - 1] - x1) / (x1 - x0); + boundaryCnt++; + } + + if((j === 0 || j === nj - 1) && (i > 0 && i < ni - 1)) { + // Now average points to the left/right as long as not in a corner: + dxp = a[i + 1] - a[i]; + dxm = a[i] - a[i - 1]; + newVal += (dxm * data[j][i + 1] + dxp * data[j][i - 1]) / (dxm + dxp); + boundaryCnt++; + } + + if(!boundaryCnt) { + // If none of the above conditions were triggered, then this is an interior + // point and we can just do a laplace equation update. As above, these differences + // are aware of nonuniform grid spacing: + dap = a[i + 1] - a[i]; + dam = a[i] - a[i - 1]; + dbp = b[j + 1] - b[j]; + dbm = b[j] - b[j - 1]; + + // These are just some useful constants for the iteration, which is perfectly + // straightforward but a little long to derive from f_xx + f_yy = 0. + c = dap * dam * (dap + dam); + d = dbp * dbm * (dbp + dbm); + + newVal = (c * (dbm * data[j + 1][i] + dbp * data[j - 1][i]) + + d * (dam * data[j][i + 1] + dap * data[j][i - 1])) / + (d * (dam + dap) + c * (dbm + dbp)); + } else { + // If we did have contributions from the boundary conditions, then average + // the result from the various contributions: + newVal /= boundaryCnt; + } + + // Jacobi updates are ridiculously slow to converge, so this approach uses a + // Gauss-seidel iteration which is dramatically faster. + diff = newVal - data[j][i]; + reldiff = diff / dmax; + resid += reldiff * reldiff; + + // Gauss-Seidel-ish iteration, omega chosen based on heuristics and some + // quick tests. + // + // NB: Don't overrelax the boundarie. Otherwise set an overrelaxation factor + // which is a little low but safely optimal-ish: + overrelaxation = boundaryCnt ? 0 : 0.85; + + // If there are four non-null neighbors, then we want a simple average without + // overrelaxation. If all the surrouding points are null, then we want the full + // overrelaxation + // + // Based on experiments, this actually seems to slow down convergence just a bit. + // I'll leave it here for reference in case this needs to be revisited, but + // it seems to work just fine without this. + // if (overrelaxation) overrelaxation *= (4 - neighborCnt) / 4; + + data[j][i] += diff * (1 + overrelaxation); + } + + resid = Math.sqrt(resid); + } while(iter++ < itermax && resid > tol); + + Lib.log('Smoother converged to', resid, 'after', iter, 'iterations'); + + return data; +}; diff --git a/src/traces/carpet/smooth_fill_array.js b/src/traces/carpet/smooth_fill_array.js new file mode 100644 index 00000000000..56abfd11e46 --- /dev/null +++ b/src/traces/carpet/smooth_fill_array.js @@ -0,0 +1,98 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/* + * Fill in a 1D array via linear interpolation. This *is* the basis, so we + * don't have to scale this by some basis as we do for the 2D version. That + * makes this much simpler. Just loop over it and do the best we can to fill + * the array. + */ +module.exports = function smoothFillArray(data) { + var i, i0, i1; + var n = data.length; + + for(i = 0; i < n; i++) { + if(data[i] !== undefined) { + i0 = i; + break; + } + } + + for(i = n - 1; i >= 0; i--) { + if(data[i] !== undefined) { + i1 = i; + break; + } + } + + if(i0 === undefined) { + // Fill with zeros and return early; + for(i = 0; i < n; i++) { + data[i] = 0; + } + + return data; + } else if(i0 === i1) { + // Only one data point so can't extrapolate. Fill with it and return early: + for(i = 0; i < n; i++) { + data[i] = data[i0]; + } + + return data; + } + + var iA = i0; + var iB; + var m, b, dA, dB; + + // Fill in interior data. When we land on an undefined point, + // look ahead until the next defined point and then fill in linearly: + for(i = i0; i < i1; i++) { + if(data[i] === undefined) { + iA = iB = i; + while(iB < i1 && data[iB] === undefined) iB++; + + dA = data[iA - 1]; + dB = data[iB]; + + // Lots of variables, but it's just mx + b: + m = (dB - dA) / (iB - iA + 1); + b = dA + (1 - iA) * m; + + // Note that this *does* increment the outer loop counter. Worried a linter + // might complain, but it's the whole point in this case: + for(i = iA; i < iB; i++) { + data[i] = m * i + b; + } + + i = iA = iB; + } + } + + // Fill in up to the first data point: + if(i0 > 0) { + m = data[i0 + 1] - data[i0]; + b = data[i0]; + for(i = 0; i < i0; i++) { + data[i] = m * (i - i0) + b; + } + } + + // Fill in after the last data point: + if(i1 < n - 1) { + m = data[i1] - data[i1 - 1]; + b = data[i1]; + for(i = i1 + 1; i < n; i++) { + data[i] = m * (i - i1) + b; + } + } + + return data; +}; diff --git a/src/traces/carpet/xy_defaults.js b/src/traces/carpet/xy_defaults.js new file mode 100644 index 00000000000..0cac2836ce1 --- /dev/null +++ b/src/traces/carpet/xy_defaults.js @@ -0,0 +1,36 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var hasColumns = require('./has_columns'); +var convertColumnData = require('../heatmap/convert_column_xyz'); + +module.exports = function handleXYDefaults(traceIn, traceOut, coerce) { + var cols = []; + var x = coerce('x'); + + var needsXTransform = x && !hasColumns(x); + if(needsXTransform) cols.push('x'); + + traceOut._cheater = !x; + + var y = coerce('y'); + + var needsYTransform = y && !hasColumns(y); + if(needsYTransform) cols.push('y'); + + if(!x && !y) return; + + if(cols.length) { + convertColumnData(traceOut, traceOut.aaxis, traceOut.baxis, 'a', 'b', cols); + } + + return true; +}; diff --git a/src/traces/contour/colorbar.js b/src/traces/contour/colorbar.js index 68be46e0234..7410c2250ab 100644 --- a/src/traces/contour/colorbar.js +++ b/src/traces/contour/colorbar.js @@ -22,7 +22,7 @@ module.exports = function colorbar(gd, cd) { gd._fullLayout._infolayer.selectAll('.' + cbId).remove(); - if(trace.showscale === false) { + if(!trace.showscale) { Plots.autoMargin(gd, cbId); return; } diff --git a/src/traces/contour/find_all_paths.js b/src/traces/contour/find_all_paths.js index 273368094e2..ba54143e226 100644 --- a/src/traces/contour/find_all_paths.js +++ b/src/traces/contour/find_all_paths.js @@ -11,34 +11,38 @@ var Lib = require('../../lib'); var constants = require('./constants'); -module.exports = function findAllPaths(pathinfo) { +module.exports = function findAllPaths(pathinfo, xtol, ytol) { var cnt, startLoc, i, pi, j; + // Default just passes these values through as they were before: + xtol = xtol || 0.01; + ytol = ytol || 0.01; + for(i = 0; i < pathinfo.length; i++) { pi = pathinfo[i]; for(j = 0; j < pi.starts.length; j++) { startLoc = pi.starts[j]; - makePath(pi, startLoc, 'edge'); + makePath(pi, startLoc, 'edge', xtol, ytol); } cnt = 0; while(Object.keys(pi.crossings).length && cnt < 10000) { cnt++; startLoc = Object.keys(pi.crossings)[0].split(',').map(Number); - makePath(pi, startLoc); + makePath(pi, startLoc, undefined, xtol, ytol); } if(cnt === 10000) Lib.log('Infinite loop in contour?'); } }; -function equalPts(pt1, pt2) { - return Math.abs(pt1[0] - pt2[0]) < 0.01 && - Math.abs(pt1[1] - pt2[1]) < 0.01; +function equalPts(pt1, pt2, xtol, ytol) { + return Math.abs(pt1[0] - pt2[0]) < xtol && + Math.abs(pt1[1] - pt2[1]) < ytol; } function ptDist(pt1, pt2) { @@ -47,17 +51,17 @@ function ptDist(pt1, pt2) { return Math.sqrt(dx * dx + dy * dy); } -function makePath(pi, loc, edgeflag) { - var startLocStr = loc.join(','), - locStr = startLocStr, - mi = pi.crossings[locStr], - marchStep = startStep(mi, edgeflag, loc), - // start by going backward a half step and finding the crossing point - pts = [getInterpPx(pi, loc, [-marchStep[0], -marchStep[1]])], - startStepStr = marchStep.join(','), - m = pi.z.length, - n = pi.z[0].length, - cnt; +function makePath(pi, loc, edgeflag, xtol, ytol) { + var startLocStr = loc.join(','); + var locStr = startLocStr; + var mi = pi.crossings[locStr]; + var marchStep = startStep(mi, edgeflag, loc); + // start by going backward a half step and finding the crossing point + var pts = [getInterpPx(pi, loc, [-marchStep[0], -marchStep[1]])]; + var startStepStr = marchStep.join(','); + var m = pi.z.length; + var n = pi.z[0].length; + var cnt; // now follow the path for(cnt = 0; cnt < 10000; cnt++) { // just to avoid infinite loops @@ -81,7 +85,7 @@ function makePath(pi, loc, edgeflag) { loc[1] += marchStep[1]; // don't include the same point multiple times - if(equalPts(pts[pts.length - 1], pts[pts.length - 2])) pts.pop(); + if(equalPts(pts[pts.length - 1], pts[pts.length - 2], xtol, ytol)) pts.pop(); locStr = loc.join(','); var atEdge = (marchStep[0] && (loc[0] < 0 || loc[0] > n - 2)) || @@ -97,7 +101,7 @@ function makePath(pi, loc, edgeflag) { if(cnt === 10000) { Lib.log('Infinite loop in contour?'); } - var closedpath = equalPts(pts[0], pts[pts.length - 1]), + var closedpath = equalPts(pts[0], pts[pts.length - 1], xtol, ytol), totaldist = 0, distThresholdFactor = 0.2 * pi.smoothing, alldists = [], @@ -186,7 +190,7 @@ function makePath(pi, loc, edgeflag) { // edge path - does it start where an existing edge path ends, or vice versa? var merged = false; pi.edgepaths.forEach(function(edgepath, edgei) { - if(!merged && equalPts(edgepath[0], pts[pts.length - 1])) { + if(!merged && equalPts(edgepath[0], pts[pts.length - 1], xtol, ytol)) { pts.pop(); merged = true; @@ -194,7 +198,7 @@ function makePath(pi, loc, edgeflag) { var doublemerged = false; pi.edgepaths.forEach(function(edgepath2, edgei2) { if(!doublemerged && equalPts( - edgepath2[edgepath2.length - 1], pts[0])) { + edgepath2[edgepath2.length - 1], pts[0], xtol, ytol)) { doublemerged = true; pts.splice(0, 1); pi.edgepaths.splice(edgei, 1); @@ -214,7 +218,7 @@ function makePath(pi, loc, edgeflag) { } }); pi.edgepaths.forEach(function(edgepath, edgei) { - if(!merged && equalPts(edgepath[edgepath.length - 1], pts[0])) { + if(!merged && equalPts(edgepath[edgepath.length - 1], pts[0], xtol, ytol)) { pts.splice(0, 1); pi.edgepaths[edgei] = edgepath.concat(pts); merged = true; @@ -257,6 +261,7 @@ function getInterpPx(pi, loc, step) { if(step[1]) { var dx = (pi.level - zxy) / (pi.z[locy][locx + 1] - zxy); + return [xa.c2p((1 - dx) * pi.x[locx] + dx * pi.x[locx + 1], true), ya.c2p(pi.y[locy], true)]; } diff --git a/src/traces/contour/make_color_map.js b/src/traces/contour/make_color_map.js index fc54a155705..8c2835455c7 100644 --- a/src/traces/contour/make_color_map.js +++ b/src/traces/contour/make_color_map.js @@ -21,6 +21,11 @@ module.exports = function makeColorMap(trace) { nc = Math.floor((end - start) / cs) + 1, extra = contours.coloring === 'lines' ? 0 : 1; + if(!isFinite(cs)) { + cs = 1; + nc = 1; + } + var scl = trace.colorscale, len = scl.length; diff --git a/src/traces/contour/plot.js b/src/traces/contour/plot.js index 894b53efc7a..da09ada387b 100644 --- a/src/traces/contour/plot.js +++ b/src/traces/contour/plot.js @@ -277,7 +277,8 @@ function makeLines(plotgroup, pathinfo, contours) { .attr('d', function(d) { return Drawing.smoothopen(d, smoothing); }) - .style('stroke-miterlimit', 1); + .style('stroke-miterlimit', 1) + .style('vector-effect', 'non-scaling-stroke'); var closedcontourlines = linegroup.selectAll('path.closedline') .data(function(d) { return d.paths; }); @@ -288,7 +289,8 @@ function makeLines(plotgroup, pathinfo, contours) { .attr('d', function(d) { return Drawing.smoothclosed(d, smoothing); }) - .style('stroke-miterlimit', 1); + .style('stroke-miterlimit', 1) + .style('vector-effect', 'non-scaling-stroke'); } function clipGaps(plotGroup, plotinfo, cd0, perimeter) { diff --git a/src/traces/contour/style_defaults.js b/src/traces/contour/style_defaults.js index cd29ec6ccbe..cf87d2b4c2c 100644 --- a/src/traces/contour/style_defaults.js +++ b/src/traces/contour/style_defaults.js @@ -12,15 +12,15 @@ var colorscaleDefaults = require('../../components/colorscale/defaults'); -module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, layout) { +module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, layout, defaultColor, defaultWidth) { var coloring = coerce('contours.coloring'); var showLines; if(coloring === 'fill') showLines = coerce('contours.showlines'); if(showLines !== false) { - if(coloring !== 'lines') coerce('line.color', '#000'); - coerce('line.width', 0.5); + if(coloring !== 'lines') coerce('line.color', defaultColor || '#000'); + coerce('line.width', defaultWidth === undefined ? 0.5 : defaultWidth); coerce('line.dash'); } diff --git a/src/traces/contourcarpet/attributes.js b/src/traces/contourcarpet/attributes.js new file mode 100644 index 00000000000..f20755a5d6c --- /dev/null +++ b/src/traces/contourcarpet/attributes.js @@ -0,0 +1,223 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var heatmapAttrs = require('../heatmap/attributes'); +var scatterAttrs = require('../scatter/attributes'); +var colorscaleAttrs = require('../../components/colorscale/attributes'); +var colorbarAttrs = require('../../components/colorbar/attributes'); + +var extendFlat = require('../../lib/extend').extendFlat; + +var scatterLineAttrs = scatterAttrs.line; +var constants = require('./constants'); + +module.exports = extendFlat({}, { + carpet: { + valType: 'string', + role: 'info', + description: [ + 'The `carpet` of the carpet axes on which this contour trace lies' + ].join(' ') + }, + z: heatmapAttrs.z, + a: heatmapAttrs.x, + a0: heatmapAttrs.x0, + da: heatmapAttrs.dx, + b: heatmapAttrs.y, + b0: heatmapAttrs.y0, + db: heatmapAttrs.dy, + text: heatmapAttrs.text, + transpose: heatmapAttrs.transpose, + atype: heatmapAttrs.xtype, + btype: heatmapAttrs.ytype, + + mode: { + valType: 'flaglist', + flags: ['lines', 'fill'], + extras: ['none'], + role: 'info', + description: ['The mode.'].join(' ') + }, + + connectgaps: heatmapAttrs.connectgaps, + + fillcolor: { + valType: 'color', + role: 'style', + description: [ + 'Sets the fill color.', + 'Defaults to a half-transparent variant of the line color,', + 'marker color, or marker line color, whichever is available.' + ].join(' ') + }, + + autocontour: { + valType: 'boolean', + dflt: true, + role: 'style', + description: [ + 'Determines whether or not the contour level attributes are', + 'picked by an algorithm.', + 'If *true*, the number of contour levels can be set in `ncontours`.', + 'If *false*, set the contour level attributes in `contours`.' + ].join(' ') + }, + ncontours: { + valType: 'integer', + dflt: 15, + min: 1, + role: 'style', + description: [ + 'Sets the maximum number of contour levels. The actual number', + 'of contours will be chosen automatically to be less than or', + 'equal to the value of `ncontours`.', + 'Has an effect only if `autocontour` is *true* or if', + '`contours.size` is missing.' + ].join(' ') + }, + + contours: { + type: { + valType: 'enumerated', + values: ['levels', 'constraint'], + dflt: 'levels', + role: 'info', + description: [ + 'If `levels`, the data is represented as a contour plot with multiple', + 'levels displayed. If `constraint`, the data is represented as constraints', + 'with the invalid region shaded as specified by the `operation` and', + '`value` parameters.' + ].join(' ') + }, + start: { + valType: 'number', + dflt: null, + role: 'style', + description: [ + 'Sets the starting contour level value.', + 'Must be less than `contours.end`' + ].join(' ') + }, + end: { + valType: 'number', + dflt: null, + role: 'style', + description: [ + 'Sets the end contour level value.', + 'Must be more than `contours.start`' + ].join(' ') + }, + size: { + valType: 'number', + dflt: null, + min: 0, + role: 'style', + description: [ + 'Sets the step between each contour level.', + 'Must be positive.' + ].join(' ') + }, + coloring: { + valType: 'enumerated', + values: ['fill', 'lines', 'none'], + dflt: 'fill', + role: 'style', + description: [ + 'Determines the coloring method showing the contour values.', + 'If *fill*, coloring is done evenly between each contour level', + 'If *lines*, coloring is done on the contour lines.', + 'If *none*, no coloring is applied on this trace.' + ].join(' ') + }, + showlines: { + valType: 'boolean', + dflt: true, + role: 'style', + description: [ + 'Determines whether or not the contour lines are drawn.', + 'Has only an effect if `contours.coloring` is set to *fill*.' + ].join(' ') + }, + operation: { + valType: 'enumerated', + values: [].concat(constants.INEQUALITY_OPS).concat(constants.INTERVAL_OPS).concat(constants.SET_OPS), + role: 'info', + dflt: '=', + description: [ + 'Sets the filter operation.', + + '*=* keeps items equal to `value`', + + '*<* keeps items less than `value`', + '*<=* keeps items less than or equal to `value`', + + '*>* keeps items greater than `value`', + '*>=* keeps items greater than or equal to `value`', + + '*[]* keeps items inside `value[0]` to value[1]` including both bounds`', + '*()* keeps items inside `value[0]` to value[1]` excluding both bounds`', + '*[)* keeps items inside `value[0]` to value[1]` including `value[0]` but excluding `value[1]', + '*(]* keeps items inside `value[0]` to value[1]` excluding `value[0]` but including `value[1]', + + '*][* keeps items outside `value[0]` to value[1]` and equal to both bounds`', + '*)(* keeps items outside `value[0]` to value[1]`', + '*](* keeps items outside `value[0]` to value[1]` and equal to `value[0]`', + '*)[* keeps items outside `value[0]` to value[1]` and equal to `value[1]`' + ].join(' ') + }, + value: { + valType: 'any', + arrayOk: true, + dflt: 0, + role: 'info', + description: [ + 'Sets the value or values by which to filter by.', + + 'Values are expected to be in the same type as the data linked', + 'to *target*.', + + 'When `operation` is set to one of the inequality values', + '(' + constants.INEQUALITY_OPS + ')', + '*value* is expected to be a number or a string.', + + 'When `operation` is set to one of the interval value', + '(' + constants.INTERVAL_OPS + ')', + '*value* is expected to be 2-item array where the first item', + 'is the lower bound and the second item is the upper bound.', + + 'When `operation`, is set to one of the set value', + '(' + constants.SET_OPS + ')', + '*value* is expected to be an array with as many items as', + 'the desired set elements.' + ].join(' ') + } + }, + + line: { + color: extendFlat({}, scatterLineAttrs.color, { + description: [ + 'Sets the color of the contour level.', + 'Has no if `contours.coloring` is set to *lines*.' + ].join(' ') + }), + width: scatterLineAttrs.width, + dash: scatterLineAttrs.dash, + smoothing: extendFlat({}, scatterLineAttrs.smoothing, { + description: [ + 'Sets the amount of smoothing for the contour lines,', + 'where *0* corresponds to no smoothing.' + ].join(' ') + }) + } +}, + colorscaleAttrs, + { autocolorscale: extendFlat({}, colorscaleAttrs.autocolorscale, {dflt: false}) }, + { colorbar: colorbarAttrs } +); diff --git a/src/traces/contourcarpet/calc.js b/src/traces/contourcarpet/calc.js new file mode 100644 index 00000000000..e313bbdbaf4 --- /dev/null +++ b/src/traces/contourcarpet/calc.js @@ -0,0 +1,214 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = require('../../lib'); +var Axes = require('../../plots/cartesian/axes'); +var extendFlat = require('../../lib').extendFlat; +var Registry = require('../../registry'); +var colorscaleCalc = require('../../components/colorscale/calc'); +var hasColumns = require('../heatmap/has_columns'); +var convertColumnData = require('../heatmap/convert_column_xyz'); +var clean2dArray = require('../heatmap/clean_2d_array'); +var maxRowLength = require('../heatmap/max_row_length'); +var interp2d = require('../heatmap/interp2d'); +var findEmpties = require('../heatmap/find_empties'); +var makeBoundArray = require('../heatmap/make_bound_array'); +var supplyDefaults = require('./defaults'); +var lookupCarpet = require('../carpet/lookup_carpetid'); + +// most is the same as heatmap calc, then adjust it +// though a few things inside heatmap calc still look for +// contour maps, because the makeBoundArray calls are too entangled +module.exports = function calc(gd, trace) { + var carpet = trace.carpetTrace = lookupCarpet(gd, trace); + if(!carpet || !carpet.visible || carpet.visible === 'legendonly') return; + + if(!trace.a || !trace.b) { + // Look up the original incoming carpet data: + var carpetdata = gd.data[carpet.index]; + + // Look up the incoming trace data, *except* perform a shallow + // copy so that we're not actually modifying it when we use it + // to supply defaults: + var tracedata = gd.data[trace.index]; + // var tracedata = extendFlat({}, gd.data[trace.index]); + + // If the data is not specified + if(!tracedata.a) tracedata.a = carpetdata.a; + if(!tracedata.b) tracedata.b = carpetdata.b; + + supplyDefaults(tracedata, trace, trace._defaultColor, gd._fullLayout); + } + + var cd = heatmappishCalc(gd, trace), + contours = trace.contours; + + // Autocontour is unset for constraint plots so also autocontour if undefind: + if(trace.autocontour === true) { + var dummyAx = autoContours(trace.zmin, trace.zmax, trace.ncontours); + + contours.size = dummyAx.dtick; + + contours.start = Axes.tickFirst(dummyAx); + dummyAx.range.reverse(); + contours.end = Axes.tickFirst(dummyAx); + + if(contours.start === trace.zmin) contours.start += contours.size; + if(contours.end === trace.zmax) contours.end -= contours.size; + + // if you set a small ncontours, *and* the ends are exactly on zmin/zmax + // there's an edge case where start > end now. Make sure there's at least + // one meaningful contour, put it midway between the crossed values + if(contours.start > contours.end) { + contours.start = contours.end = (contours.start + contours.end) / 2; + } + + // copy auto-contour info back to the source data. + trace._input.contours = extendFlat({}, contours); + } + else { + // sanity checks on manually-supplied start/end/size + var start = contours.start, + end = contours.end, + inputContours = trace._input.contours; + + if(start > end) { + contours.start = inputContours.start = end; + end = contours.end = inputContours.end = start; + start = contours.start; + } + + if(!(contours.size > 0)) { + var sizeOut; + if(start === end) sizeOut = 1; + else sizeOut = autoContours(start, end, trace.ncontours).dtick; + + inputContours.size = contours.size = sizeOut; + } + } + + return cd; +}; + +/* + * autoContours: make a dummy axis object with dtick we can use + * as contours.size, and if needed we can use Axes.tickFirst + * with this axis object to calculate the start and end too + * + * start: the value to start the contours at + * end: the value to end at (must be > start) + * ncontours: max number of contours to make, like roughDTick + * + * returns: an axis object + */ +function autoContours(start, end, ncontours) { + var dummyAx = { + type: 'linear', + range: [start, end] + }; + + Axes.autoTicks( + dummyAx, + (end - start) / (ncontours || 15) + ); + + return dummyAx; +} + +function heatmappishCalc(gd, trace) { + // prepare the raw data + // run makeCalcdata on x and y even for heatmaps, in case of category mappings + var carpet = trace.carpetTrace; + var aax = carpet.aaxis, + bax = carpet.baxis, + isContour = Registry.traceIs(trace, 'contour'), + zsmooth = isContour ? 'best' : trace.zsmooth, + a, + a0, + da, + b, + b0, + db, + z, + i; + + // cancel minimum tick spacings (only applies to bars and boxes) + aax._minDtick = 0; + bax._minDtick = 0; + + if(hasColumns(trace)) convertColumnData(trace, aax, bax, 'a', 'b', ['z']); + + a = trace.a ? aax.makeCalcdata(trace, 'a') : []; + b = trace.b ? bax.makeCalcdata(trace, 'b') : []; + a0 = trace.a0 || 0; + da = trace.da || 1; + b0 = trace.b0 || 0; + db = trace.db || 1; + + z = clean2dArray(trace.z, trace.transpose); + + trace._emptypoints = findEmpties(z); + trace._interpz = interp2d(z, trace._emptypoints, trace._interpz); + + function noZsmooth(msg) { + zsmooth = trace._input.zsmooth = trace.zsmooth = false; + Lib.notifier('cannot fast-zsmooth: ' + msg); + } + + // check whether we really can smooth (ie all boxes are about the same size) + if(zsmooth === 'fast') { + if(aax.type === 'log' || bax.type === 'log') { + noZsmooth('log axis found'); + } + else { + if(a.length) { + var avgda = (a[a.length - 1] - a[0]) / (a.length - 1), + maxErrX = Math.abs(avgda / 100); + for(i = 0; i < a.length - 1; i++) { + if(Math.abs(a[i + 1] - a[i] - avgda) > maxErrX) { + noZsmooth('a scale is not linear'); + break; + } + } + } + if(b.length && zsmooth === 'fast') { + var avgdy = (b[b.length - 1] - b[0]) / (b.length - 1), + maxErrY = Math.abs(avgdy / 100); + for(i = 0; i < b.length - 1; i++) { + if(Math.abs(b[i + 1] - b[i] - avgdy) > maxErrY) { + noZsmooth('b scale is not linear'); + break; + } + } + } + } + } + + // create arrays of brick boundaries, to be used by autorange and heatmap.plot + var xlen = maxRowLength(z), + xIn = trace.xtype === 'scaled' ? '' : a, + xArray = makeBoundArray(trace, xIn, a0, da, xlen, aax), + yIn = trace.ytype === 'scaled' ? '' : b, + yArray = makeBoundArray(trace, yIn, b0, db, z.length, bax); + + var cd0 = { + a: xArray, + b: yArray, + z: z, + //mappedZ: mappedZ + }; + + if(trace.contours.type === 'levels') { + // auto-z and autocolorscale if applicable + colorscaleCalc(trace, z, '', 'z'); + } + + return [cd0]; +} diff --git a/src/traces/contourcarpet/close_boundaries.js b/src/traces/contourcarpet/close_boundaries.js new file mode 100644 index 00000000000..d1dc727ebb0 --- /dev/null +++ b/src/traces/contourcarpet/close_boundaries.js @@ -0,0 +1,68 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = function(pathinfo, operation, perimeter, trace) { + // Abandon all hope, ye who enter here. + var i, v1, v2; + var na = trace.a.length; + var nb = trace.b.length; + var z = trace.z; + + var boundaryMax = -Infinity; + var boundaryMin = Infinity; + + for(i = 0; i < nb; i++) { + boundaryMin = Math.min(boundaryMin, z[i][0]); + boundaryMin = Math.min(boundaryMin, z[i][na - 1]); + boundaryMax = Math.max(boundaryMax, z[i][0]); + boundaryMax = Math.max(boundaryMax, z[i][na - 1]); + } + + for(i = 1; i < na - 1; i++) { + boundaryMin = Math.min(boundaryMin, z[0][i]); + boundaryMin = Math.min(boundaryMin, z[nb - 1][i]); + boundaryMax = Math.max(boundaryMax, z[0][i]); + boundaryMax = Math.max(boundaryMax, z[nb - 1][i]); + } + + switch(operation) { + case '>': + case '>=': + if(trace.contours.value > boundaryMax) { + pathinfo[0].prefixBoundary = true; + } + break; + case '<': + case '<=': + if(trace.contours.value < boundaryMin) { + pathinfo[0].prefixBoundary = true; + } + break; + case '[]': + case '()': + v1 = Math.min.apply(null, trace.contours.value); + v2 = Math.max.apply(null, trace.contours.value); + if(v2 < boundaryMin) { + pathinfo[0].prefixBoundary = true; + } + if(v1 > boundaryMax) { + pathinfo[0].prefixBoundary = true; + } + break; + case '][': + case ')(': + v1 = Math.min.apply(null, trace.contours.value); + v2 = Math.max.apply(null, trace.contours.value); + if(v1 < boundaryMin && v2 > boundaryMax) { + pathinfo[0].prefixBoundary = true; + } + break; + } +}; diff --git a/src/traces/contourcarpet/constants.js b/src/traces/contourcarpet/constants.js new file mode 100644 index 00000000000..123d6f45241 --- /dev/null +++ b/src/traces/contourcarpet/constants.js @@ -0,0 +1,15 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = { + INEQUALITY_OPS: ['=', '<', '>=', '>', '<='], + INTERVAL_OPS: ['[]', '()', '[)', '(]', '][', ')(', '](', ')['], + SET_OPS: ['{}', '}{'] +}; diff --git a/src/traces/contourcarpet/constraint_mapping.js b/src/traces/contourcarpet/constraint_mapping.js new file mode 100644 index 00000000000..9465633859c --- /dev/null +++ b/src/traces/contourcarpet/constraint_mapping.js @@ -0,0 +1,86 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var constants = require('./constants'); +var isNumeric = require('fast-isnumeric'); + +// This syntax conforms to the existing filter transform syntax, but we don't care +// about open vs. closed intervals for simply drawing contours constraints: +module.exports['[]'] = makeRangeSettings('[]'); +module.exports['()'] = makeRangeSettings('()'); +module.exports['[)'] = makeRangeSettings('[)'); +module.exports['(]'] = makeRangeSettings('(]'); + +// Inverted intervals simply flip the sign: +module.exports[']['] = makeRangeSettings(']['); +module.exports[')('] = makeRangeSettings(')('); +module.exports[')['] = makeRangeSettings(')['); +module.exports[']('] = makeRangeSettings(']('); + +module.exports['>'] = makeInequalitySettings('>'); +module.exports['>='] = makeInequalitySettings('>='); +module.exports['<'] = makeInequalitySettings('<'); +module.exports['<='] = makeInequalitySettings('<='); +module.exports['='] = makeInequalitySettings('='); + +// This does not in any way shape or form support calendars. It's adapted from +// transforms/filter.js. +function coerceValue(operation, value) { + var hasArrayValue = Array.isArray(value); + + var coercedValue; + + function coerce(value) { + return isNumeric(value) ? (+value) : null; + } + + if(constants.INEQUALITY_OPS.indexOf(operation) !== -1) { + coercedValue = hasArrayValue ? coerce(value[0]) : coerce(value); + } else if(constants.INTERVAL_OPS.indexOf(operation) !== -1) { + coercedValue = hasArrayValue ? + [coerce(value[0]), coerce(value[1])] : + [coerce(value), coerce(value)]; + } else if(constants.SET_OPS.indexOf(operation) !== -1) { + coercedValue = hasArrayValue ? value.map(coerce) : [coerce(value)]; + } + + return coercedValue; +} + +// Returns a parabola scaled so that the min/max is either +/- 1 and zero at the two values +// provided. The data is mapped by this function when constructing intervals so that it's +// very easy to construct contours as normal. +function makeRangeSettings(operation) { + return function(value) { + value = coerceValue(operation, value); + + // Ensure proper ordering: + var min = Math.min(value[0], value[1]); + var max = Math.max(value[0], value[1]); + + return { + start: min, + end: max, + size: max - min + }; + }; +} + +function makeInequalitySettings(operation) { + return function(value) { + value = coerceValue(operation, value); + + return { + start: value, + end: Infinity, + size: Infinity + }; + }; +} diff --git a/src/traces/contourcarpet/constraint_value_defaults.js b/src/traces/contourcarpet/constraint_value_defaults.js new file mode 100644 index 00000000000..2d19d6211bf --- /dev/null +++ b/src/traces/contourcarpet/constraint_value_defaults.js @@ -0,0 +1,59 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var constraintMapping = require('./constraint_mapping'); +var isNumeric = require('fast-isnumeric'); + +module.exports = function(coerce, contours) { + var zvalue; + var scalarValuedOps = ['=', '<', '<=', '>', '>=']; + + if(scalarValuedOps.indexOf(contours.operation) === -1) { + // Requires an array of two numbers: + coerce('contours.value', [0, 1]); + + if(!Array.isArray(contours.value)) { + if(isNumeric(contours.value)) { + zvalue = parseFloat(contours.value); + contours.value = [zvalue, zvalue + 1]; + } + } else if(contours.value.length > 2) { + contours.value = contours.value.slice(2); + } else if(contours.length === 0) { + contours.value = [0, 1]; + } else if(contours.length < 2) { + zvalue = parseFloat(contours.value[0]); + contours.value = [zvalue, zvalue + 1]; + } else { + contours.value = [ + parseFloat(contours.value[0]), + parseFloat(contours.value[1]) + ]; + } + } else { + // Requires a single scalar: + coerce('contours.value', 0); + + if(!isNumeric(contours.value)) { + if(Array.isArray(contours.value)) { + contours.value = parseFloat(contours.value[0]); + } else { + contours.value = 0; + } + } + } + + var map = constraintMapping[contours.operation](contours.value); + + contours.start = map.start; + contours.end = map.end; + contours.size = map.size; +}; diff --git a/src/traces/contourcarpet/convert_to_constraints.js b/src/traces/contourcarpet/convert_to_constraints.js new file mode 100644 index 00000000000..9105c73bd85 --- /dev/null +++ b/src/traces/contourcarpet/convert_to_constraints.js @@ -0,0 +1,87 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = require('../../lib'); + +// The contour extraction is great, except it totally fails for constraints because we +// need weird range loops and flipped contours instead of the usual format. This function +// does some weird manipulation of the extracted pathinfo data such that it magically +// draws contours correctly *as* constraints. +module.exports = function(pathinfo, operation) { + var i, pi0, pi1; + + var op0 = function(arr) { return arr.reverse(); }; + var op1 = function(arr) { return arr; }; + + switch(operation) { + case '][': + case ')[': + case '](': + case ')(': + var tmp = op0; + op0 = op1; + op1 = tmp; + // It's a nice rule, except this definitely *is* what's intended here. + /* eslint-disable: no-fallthrough */ + case '[]': + case '[)': + case '(]': + case '()': + /* eslint-enable: no-fallthrough */ + if(pathinfo.length !== 2) { + Lib.warn('Contour data invalid for the specified inequality range operation.'); + return; + } + + // In this case there should be exactly two contour levels in pathinfo. We + // simply concatenate the info into one pathinfo and flip all of the data + // in one. This will draw the contour as closed. + pi0 = pathinfo[0]; + pi1 = pathinfo[1]; + + for(i = 0; i < pi0.edgepaths.length; i++) { + pi0.edgepaths[i] = op0(pi0.edgepaths[i]); + } + + for(i = 0; i < pi0.paths.length; i++) { + pi0.paths[i] = op0(pi0.paths[i]); + } + + while(pi1.edgepaths.length) { + pi0.edgepaths.push(op1(pi1.edgepaths.shift())); + } + while(pi1.paths.length) { + pi0.paths.push(op1(pi1.paths.shift())); + } + pathinfo.pop(); + + break; + case '>=': + case '>': + if(pathinfo.length !== 1) { + Lib.warn('Contour data invalid for the specified inequality operation.'); + return; + } + + // In this case there should be exactly two contour levels in pathinfo. We + // simply concatenate the info into one pathinfo and flip all of the data + // in one. This will draw the contour as closed. + pi0 = pathinfo[0]; + + for(i = 0; i < pi0.edgepaths.length; i++) { + pi0.edgepaths[i] = op0(pi0.edgepaths[i]); + } + + for(i = 0; i < pi0.paths.length; i++) { + pi0.paths[i] = op0(pi0.paths[i]); + } + break; + } +}; diff --git a/src/traces/contourcarpet/defaults.js b/src/traces/contourcarpet/defaults.js new file mode 100644 index 00000000000..891a41b714e --- /dev/null +++ b/src/traces/contourcarpet/defaults.js @@ -0,0 +1,152 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Lib = require('../../lib'); + +var handleXYZDefaults = require('../heatmap/xyz_defaults'); +var attributes = require('./attributes'); +var handleStyleDefaults = require('../contour/style_defaults'); +var handleFillColorDefaults = require('../scatter/fillcolor_defaults'); +var plotAttributes = require('../../plots/attributes'); +var supplyConstraintDefaults = require('./constraint_value_defaults'); +var addOpacity = require('../../components/color').addOpacity; + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + coerce('carpet'); + + // If either a or b is not present, then it's not a valid trace *unless* the carpet + // axis has the a or b values we're looking for. So if these are not found, just defer + // that decision until the calc step. + // + // NB: the calc step will modify the original data input by assigning whichever of + // a or b are missing. This is necessary because panning goes right from supplyDefaults + // to plot (skipping calc). That means on subsequent updates, this *will* need to be + // able to find a and b. + // + // The long-term proper fix is that this should perhaps use underscored attributes to + // at least modify the user input to a slightly lesser extent. Fully removing the + // input mutation is challenging. The underscore approach is not currently taken since + // it requires modification to all of the functions below that expect the coerced + // attribute name to match the property name -- except '_a' !== 'a' so that is not + // straightforward. + if(traceIn.a && traceIn.b) { + var contourSize, contourStart, contourEnd, missingEnd, autoContour; + + var len = handleXYZDefaults(traceIn, traceOut, coerce, layout, 'a', 'b'); + + if(!len) { + traceOut.visible = false; + return; + } + + coerce('text'); + coerce('contours.type'); + + var contours = traceOut.contours; + + // Unimplemented: + // coerce('connectgaps', hasColumns(traceOut)); + + if(contours.type === 'constraint') { + coerce('contours.operation'); + + supplyConstraintDefaults(coerce, contours); + + // Override the trace-level showlegend default with a default that takes + // into account whether this is a constraint or level contours: + Lib.coerce(traceIn, traceOut, plotAttributes, 'showlegend', true); + + // Override the above defaults with constraint-aware tweaks: + coerce('contours.coloring', contours.operation === '=' ? 'lines' : 'fill'); + coerce('contours.showlines', true); + + if(contours.operation === '=') { + contours.coloring = 'lines'; + } + handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce); + + // If there's a fill color, use it at full opacity for the line color + var lineDfltColor = traceOut.fillcolor ? addOpacity(traceOut.fillcolor, 1) : defaultColor; + + handleStyleDefaults(traceIn, traceOut, coerce, layout, lineDfltColor, 2); + + if(contours.operation === '=') { + coerce('line.color', defaultColor); + + if(contours.coloring === 'fill') { + contours.coloring = 'lines'; + } + + if(contours.coloring === 'lines') { + delete traceOut.fillcolor; + } + } + + delete traceOut.showscale; + delete traceOut.autocontour; + delete traceOut.autocolorscale; + delete traceOut.colorscale; + delete traceOut.ncontours; + delete traceOut.colorbar; + + if(traceOut.line) { + delete traceOut.line.autocolorscale; + delete traceOut.line.colorscale; + delete traceOut.line.mincolor; + delete traceOut.line.maxcolor; + } + + // TODO: These shouldb e deleted in accordance with toolpanel convention, but + // we can't becuase we require them so that it magically makes the contour + // parts of the code happy: + // delete traceOut.contours.start; + // delete traceOut.contours.end; + // delete traceOut.contours.size; + } else { + // Override the trace-level showlegend default with a default that takes + // into account whether this is a constraint or level contours: + Lib.coerce(traceIn, traceOut, plotAttributes, 'showlegend', false); + + contourStart = Lib.coerce2(traceIn, traceOut, attributes, 'contours.start'); + contourEnd = Lib.coerce2(traceIn, traceOut, attributes, 'contours.end'); + + // normally we only need size if autocontour is off. But contour.calc + // pushes its calculated contour size back to the input trace, so for + // things like restyle that can call supplyDefaults without calc + // after the initial draw, we can just reuse the previous calculation + contourSize = coerce('contours.size'); + coerce('contours.coloring'); + + missingEnd = (contourStart === false) || (contourEnd === false); + + if(missingEnd) { + autoContour = traceOut.autocontour = true; + } else { + autoContour = coerce('autocontour', false); + } + + if(autoContour || !contourSize) { + coerce('ncontours'); + } + + handleStyleDefaults(traceIn, traceOut, coerce, layout); + + delete traceOut.value; + delete traceOut.operation; + } + } else { + traceOut._defaultColor = defaultColor; + } +}; diff --git a/src/traces/contourcarpet/empty_pathinfo.js b/src/traces/contourcarpet/empty_pathinfo.js new file mode 100644 index 00000000000..139b6932adf --- /dev/null +++ b/src/traces/contourcarpet/empty_pathinfo.js @@ -0,0 +1,47 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = require('../../lib'); + +module.exports = function emptyPathinfo(contours, plotinfo, cd0) { + var cs = contours.size; + var pathinfo = []; + + var carpet = cd0.trace.carpetTrace; + + for(var ci = contours.start; ci < contours.end + cs / 10; ci += cs) { + pathinfo.push({ + level: ci, + // all the cells with nontrivial marching index + crossings: {}, + // starting points on the edges of the lattice for each contour + starts: [], + // all unclosed paths (may have less items than starts, + // if a path is closed by rounding) + edgepaths: [], + // all closed paths + paths: [], + // store axes so we can convert to px + xaxis: carpet.aaxis, + yaxis: carpet.baxis, + // full data arrays to use for interpolation + x: cd0.a, + y: cd0.b, + z: cd0.z, + smoothing: cd0.trace.line.smoothing + }); + + if(pathinfo.length > 1000) { + Lib.warn('Too many contours, clipping at 1000', contours); + break; + } + } + return pathinfo; +}; diff --git a/src/traces/contourcarpet/index.js b/src/traces/contourcarpet/index.js new file mode 100644 index 00000000000..9c894f4ae64 --- /dev/null +++ b/src/traces/contourcarpet/index.js @@ -0,0 +1,34 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var ContourCarpet = {}; + +ContourCarpet.attributes = require('./attributes'); +ContourCarpet.supplyDefaults = require('./defaults'); +ContourCarpet.colorbar = require('../contour/colorbar'); +ContourCarpet.calc = require('./calc'); +ContourCarpet.plot = require('./plot'); +ContourCarpet.style = require('./style'); + +ContourCarpet.moduleType = 'trace'; +ContourCarpet.name = 'contourcarpet'; +ContourCarpet.basePlotModule = require('../../plots/cartesian'); +ContourCarpet.categories = ['cartesian', 'carpet', 'contour', 'symbols', 'showLegend', 'hasLines', 'carpetDependent']; +ContourCarpet.meta = { + hrName: 'contour_carpet', + description: [ + 'Plots contours on either the first carpet axis or the', + 'carpet axis with a matching `carpet` attribute. Data `z`', + 'is interpreted as matching that of the corresponding carpet', + 'axis.' + ].join(' ') +}; + +module.exports = ContourCarpet; diff --git a/src/traces/contourcarpet/join_all_paths.js b/src/traces/contourcarpet/join_all_paths.js new file mode 100644 index 00000000000..99b6c317a64 --- /dev/null +++ b/src/traces/contourcarpet/join_all_paths.js @@ -0,0 +1,134 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Drawing = require('../../components/drawing'); +var axisAlignedLine = require('../carpet/axis_aligned_line'); +var Lib = require('../../lib'); +// var map1dArray = require('../carpet/map_1d_array'); +// var makepath = require('../carpet/makepath'); + +module.exports = function joinAllPaths(trace, pi, perimeter, ab2p, carpet, carpetcd, xa, ya) { + var i; + var fullpath = ''; + + var startsleft = pi.edgepaths.map(function(v, i) { return i; }); + var newloop = true; + var endpt, newendpt, cnt, nexti, possiblei, addpath; + + var atol = Math.abs(perimeter[0][0] - perimeter[2][0]) * 1e-4; + var btol = Math.abs(perimeter[0][1] - perimeter[2][1]) * 1e-4; + + function istop(pt) { return Math.abs(pt[1] - perimeter[0][1]) < btol; } + function isbottom(pt) { return Math.abs(pt[1] - perimeter[2][1]) < btol; } + function isleft(pt) { return Math.abs(pt[0] - perimeter[0][0]) < atol; } + function isright(pt) { return Math.abs(pt[0] - perimeter[2][0]) < atol; } + + function pathto(pt0, pt1) { + var i, j, segments, axis; + var path = ''; + + if((istop(pt0) && !isright(pt0)) || (isbottom(pt0) && !isleft(pt0))) { + axis = carpet.aaxis; + segments = axisAlignedLine(carpet, carpetcd, [pt0[0], pt1[0]], 0.5 * (pt0[1] + pt1[1])); + } else { + axis = carpet.baxis; + segments = axisAlignedLine(carpet, carpetcd, 0.5 * (pt0[0] + pt1[0]), [pt0[1], pt1[1]]); + } + + for(i = 1; i < segments.length; i++) { + path += axis.smoothing ? 'C' : 'L'; + for(j = 0; j < segments[i].length; j++) { + var pt = segments[i][j]; + path += [xa.c2p(pt[0]), ya.c2p(pt[1])] + ' '; + } + } + + return path; + } + + i = 0; + endpt = null; + while(startsleft.length) { + var startpt = pi.edgepaths[i][0]; + + if(endpt) { + fullpath += pathto(endpt, startpt); + } + + addpath = Drawing.smoothopen(pi.edgepaths[i].map(ab2p), pi.smoothing); + fullpath += newloop ? addpath : addpath.replace(/^M/, 'L'); + startsleft.splice(startsleft.indexOf(i), 1); + endpt = pi.edgepaths[i][pi.edgepaths[i].length - 1]; + nexti = -1; + + // now loop through sides, moving our endpoint until we find a new start + for(cnt = 0; cnt < 4; cnt++) { // just to prevent infinite loops + if(!endpt) { + Lib.log('Missing end?', i, pi); + break; + } + + if(istop(endpt) && !isright(endpt)) { + newendpt = perimeter[1]; // left top ---> right top + } else if(isleft(endpt)) { + newendpt = perimeter[0]; // left bottom ---> left top + } else if(isbottom(endpt)) { + newendpt = perimeter[3]; // right bottom + } else if(isright(endpt)) { + newendpt = perimeter[2]; // left bottom + } + + for(possiblei = 0; possiblei < pi.edgepaths.length; possiblei++) { + var ptNew = pi.edgepaths[possiblei][0]; + // is ptNew on the (horz. or vert.) segment from endpt to newendpt? + if(Math.abs(endpt[0] - newendpt[0]) < atol) { + if(Math.abs(endpt[0] - ptNew[0]) < atol && (ptNew[1] - endpt[1]) * (newendpt[1] - ptNew[1]) >= 0) { + newendpt = ptNew; + nexti = possiblei; + } + } else if(Math.abs(endpt[1] - newendpt[1]) < btol) { + if(Math.abs(endpt[1] - ptNew[1]) < btol && (ptNew[0] - endpt[0]) * (newendpt[0] - ptNew[0]) >= 0) { + newendpt = ptNew; + nexti = possiblei; + } + } else { + Lib.log('endpt to newendpt is not vert. or horz.', endpt, newendpt, ptNew); + } + } + + if(nexti >= 0) break; + fullpath += pathto(endpt, newendpt); + endpt = newendpt; + } + + if(nexti === pi.edgepaths.length) { + Lib.log('unclosed perimeter path'); + break; + } + + i = nexti; + + // if we closed back on a loop we already included, + // close it and start a new loop + newloop = (startsleft.indexOf(i) === -1); + if(newloop) { + i = startsleft[0]; + fullpath += pathto(endpt, newendpt) + 'Z'; + endpt = null; + } + } + + // finally add the interior paths + for(i = 0; i < pi.paths.length; i++) { + fullpath += Drawing.smoothclosed(pi.paths[i].map(ab2p), pi.smoothing); + } + + return fullpath; +}; diff --git a/src/traces/contourcarpet/map_pathinfo.js b/src/traces/contourcarpet/map_pathinfo.js new file mode 100644 index 00000000000..6dc5300835f --- /dev/null +++ b/src/traces/contourcarpet/map_pathinfo.js @@ -0,0 +1,35 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = function mapPathinfo(pathinfo, map) { + var i, j, k, pi, pedgepaths, ppaths, pedgepath, ppath, path; + + for(i = 0; i < pathinfo.length; i++) { + pi = pathinfo[i]; + pedgepaths = pi.pedgepaths = []; + ppaths = pi.ppaths = []; + for(j = 0; j < pi.edgepaths.length; j++) { + path = pi.edgepaths[j]; + pedgepath = []; + for(k = 0; k < path.length; k++) { + pedgepath[k] = map(path[k]); + } + pedgepaths.push(pedgepath); + } + for(j = 0; j < pi.paths.length; j++) { + path = pi.paths[j]; + ppath = []; + for(k = 0; k < path.length; k++) { + ppath[k] = map(path[k]); + } + ppaths.push(ppath); + } + } +}; diff --git a/src/traces/contourcarpet/plot.js b/src/traces/contourcarpet/plot.js new file mode 100644 index 00000000000..e3e9bc04478 --- /dev/null +++ b/src/traces/contourcarpet/plot.js @@ -0,0 +1,234 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = require('d3'); +var map1dArray = require('../carpet/map_1d_array'); +var makepath = require('../carpet/makepath'); +var Drawing = require('../../components/drawing'); + +var makeCrossings = require('../contour/make_crossings'); +var findAllPaths = require('../contour/find_all_paths'); +var convertToConstraints = require('./convert_to_constraints'); +var joinAllPaths = require('./join_all_paths'); +var emptyPathinfo = require('./empty_pathinfo'); +var mapPathinfo = require('./map_pathinfo'); +var lookupCarpet = require('../carpet/lookup_carpetid'); +var closeBoundaries = require('./close_boundaries'); + +function makeg(el, type, klass) { + var join = el.selectAll(type + '.' + klass).data([0]); + join.enter().append(type).classed(klass, true); + return join; +} + +module.exports = function plot(gd, plotinfo, cdcontours) { + for(var i = 0; i < cdcontours.length; i++) { + plotOne(gd, plotinfo, cdcontours[i]); + } +}; + +function plotOne(gd, plotinfo, cd) { + var trace = cd[0].trace; + + var carpet = trace.carpetTrace = lookupCarpet(gd, trace); + var carpetcd = gd.calcdata[carpet.index][0]; + + if(!carpet.visible || carpet.visible === 'legendonly') return; + + var a = cd[0].a; + var b = cd[0].b; + var contours = trace.contours; + var uid = trace.uid; + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + var fullLayout = gd._fullLayout; + var id = 'contour' + uid; + var pathinfo = emptyPathinfo(contours, plotinfo, cd[0]); + var isConstraint = trace.contours.type === 'constraint'; + + // Map [a, b] (data) --> [i, j] (pixels) + function ab2p(ab) { + var pt = carpet.ab2xy(ab[0], ab[1], true); + return [xa.c2p(pt[0]), ya.c2p(pt[1])]; + } + + if(trace.visible !== true) { + fullLayout._infolayer.selectAll('.cb' + uid).remove(); + return; + } + + // Define the perimeter in a/b coordinates: + var perimeter = [ + [a[0], b[b.length - 1]], + [a[a.length - 1], b[b.length - 1]], + [a[a.length - 1], b[0]], + [a[0], b[0]] + ]; + + // Extract the contour levels: + makeCrossings(pathinfo); + var atol = (a[a.length - 1] - a[0]) * 1e-8; + var btol = (b[b.length - 1] - b[0]) * 1e-8; + findAllPaths(pathinfo, atol, btol); + + // Constraints might need to be draw inverted, which is not something contours + // handle by default since they're assumed fully opaque so that they can be + // drawn overlapping. This function flips the paths as necessary so that they're + // drawn correctly. + // + // TODO: Perhaps this should be generalized and *all* paths should be drawn as + // closed regions so that translucent contour levels would be valid. + // See: https://github.com/plotly/plotly.js/issues/1356 + if(trace.contours.type === 'constraint') { + convertToConstraints(pathinfo, trace.contours.operation); + closeBoundaries(pathinfo, trace.contours.operation, perimeter, trace); + } + + // Map the paths in a/b coordinates to pixel coordinates: + mapPathinfo(pathinfo, ab2p); + + // draw everything + var plotGroup = makeContourGroup(plotinfo, cd, id); + + // Compute the boundary path + var seg, xp, yp, i; + var segs = []; + for(i = carpetcd.clipsegments.length - 1; i >= 0; i--) { + seg = carpetcd.clipsegments[i]; + xp = map1dArray([], seg.x, xa.c2p); + yp = map1dArray([], seg.y, ya.c2p); + xp.reverse(); + yp.reverse(); + segs.push(makepath(xp, yp, seg.bicubic)); + } + + var boundaryPath = 'M' + segs.join('L') + 'Z'; + + // Draw the baseline background fill that fills in the space behind any other + // contour levels: + makeBackground(plotGroup, carpetcd.clipsegments, xa, ya, isConstraint, contours.coloring); + + // Draw the specific contour fills. As a simplification, they're assumed to be + // fully opaque so that it's easy to draw them simply overlapping. The alternative + // would be to flip adjacent paths and draw closed paths for each level instead. + makeFills(trace, plotGroup, xa, ya, pathinfo, perimeter, ab2p, carpet, carpetcd, contours.coloring, boundaryPath); + + // Draw contour lines: + makeLines(plotGroup, pathinfo, contours); + + // Clip the boundary of the plot: + clipBoundary(plotGroup, carpet); +} + +function clipBoundary(plotGroup, carpet) { + plotGroup.attr('clip-path', 'url(#' + carpet.clipPathId + ')'); +} + +function makeContourGroup(plotinfo, cd, id) { + var plotgroup = plotinfo.plot.select('.maplayer') + .selectAll('g.contour.' + id) + .classed('trace', true) + .data(cd); + + plotgroup.enter().append('g') + .classed('contour', true) + .classed(id, true); + + plotgroup.exit().remove(); + + return plotgroup; +} + +function makeLines(plotgroup, pathinfo, contours) { + var smoothing = pathinfo[0].smoothing; + + var linegroup = plotgroup.selectAll('g.contourlevel') + .data(contours.showlines === false ? [] : pathinfo); + linegroup.enter().append('g') + .classed('contourlevel', true); + linegroup.exit().remove(); + + var opencontourlines = linegroup.selectAll('path.openline') + .data(function(d) { return d.pedgepaths; }); + opencontourlines.enter().append('path') + .classed('openline', true); + opencontourlines.exit().remove(); + opencontourlines + .attr('d', function(d) { + return Drawing.smoothopen(d, smoothing); + }) + .style('vector-effect', 'non-scaling-stroke'); + + var closedcontourlines = linegroup.selectAll('path.closedline') + .data(function(d) { return d.ppaths; }); + closedcontourlines.enter().append('path') + .classed('closedline', true); + closedcontourlines.exit().remove(); + closedcontourlines + .attr('d', function(d) { + return Drawing.smoothclosed(d, smoothing); + }) + .style('vector-effect', 'non-scaling-stroke') + .style('stroke-miterlimit', 1); +} + +function makeBackground(plotgroup, clipsegments, xaxis, yaxis, isConstraint, coloring) { + var seg, xp, yp, i; + var bggroup = makeg(plotgroup, 'g', 'contourbg'); + + var bgfill = bggroup.selectAll('path') + .data((coloring === 'fill' && !isConstraint) ? [0] : []); + bgfill.enter().append('path'); + bgfill.exit().remove(); + + var segs = []; + for(i = 0; i < clipsegments.length; i++) { + seg = clipsegments[i]; + xp = map1dArray([], seg.x, xaxis.c2p); + yp = map1dArray([], seg.y, yaxis.c2p); + segs.push(makepath(xp, yp, seg.bicubic)); + } + + bgfill + .attr('d', 'M' + segs.join('L') + 'Z') + .style('stroke', 'none'); +} + +function makeFills(trace, plotgroup, xa, ya, pathinfo, perimeter, ab2p, carpet, carpetcd, coloring, boundaryPath) { + var fillgroup = plotgroup.selectAll('g.contourfill') + .data([0]); + fillgroup.enter().append('g') + .classed('contourfill', true); + + var fillitems = fillgroup.selectAll('path') + .data(coloring === 'fill' ? pathinfo : []); + fillitems.enter().append('path'); + fillitems.exit().remove(); + fillitems.each(function(pi) { + // join all paths for this level together into a single path + // first follow clockwise around the perimeter to close any open paths + // if the whole perimeter is above this level, start with a path + // enclosing the whole thing. With all that, the parity should mean + // that we always fill everything above the contour, nothing below + var fullpath = joinAllPaths(trace, pi, perimeter, ab2p, carpet, carpetcd, xa, ya); + + if(pi.prefixBoundary) { + fullpath = boundaryPath + fullpath; + } + + if(!fullpath) { + d3.select(this).remove(); + } else { + d3.select(this) + .attr('d', fullpath) + .style('stroke', 'none'); + } + }); +} diff --git a/src/traces/contourcarpet/style.js b/src/traces/contourcarpet/style.js new file mode 100644 index 00000000000..eae3c131e9b --- /dev/null +++ b/src/traces/contourcarpet/style.js @@ -0,0 +1,63 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = require('d3'); + +var Drawing = require('../../components/drawing'); +var heatmapStyle = require('../heatmap/style'); + +var makeColorMap = require('../contour/make_color_map'); + +module.exports = function style(gd) { + var contours = d3.select(gd).selectAll('g.contour'); + + contours.style('opacity', function(d) { + return d.trace.opacity; + }); + + contours.each(function(d) { + var c = d3.select(this); + var trace = d.trace; + var contours = trace.contours; + var line = trace.line; + var cs = contours.size || 1; + var start = contours.start; + + if(!isFinite(cs)) { + cs = 0; + } + + c.selectAll('g.contourlevel').each(function() { + d3.select(this).selectAll('path') + .call(Drawing.lineGroupStyle, + line.width, + line.color, + line.dash); + }); + + if(trace.contours.type === 'levels' && trace.contours.coloring !== 'none') { + var colorMap = makeColorMap(trace); + + c.selectAll('g.contourbg path') + .style('fill', colorMap(start - cs / 2)); + + c.selectAll('g.contourfill path') + .style('fill', function(d, i) { + return colorMap(start + (i + 0.5) * cs); + }); + } else { + c.selectAll('g.contourfill path') + .style('fill', trace.fillcolor); + } + }); + + heatmapStyle(gd); +}; diff --git a/src/traces/heatmap/calc.js b/src/traces/heatmap/calc.js index 677209698d6..e77ab3530db 100644 --- a/src/traces/heatmap/calc.js +++ b/src/traces/heatmap/calc.js @@ -16,7 +16,7 @@ var Axes = require('../../plots/cartesian/axes'); var histogram2dCalc = require('../histogram2d/calc'); var colorscaleCalc = require('../../components/colorscale/calc'); var hasColumns = require('./has_columns'); -var convertColumnXYZ = require('./convert_column_xyz'); +var convertColumnData = require('./convert_column_xyz'); var maxRowLength = require('./max_row_length'); var clean2dArray = require('./clean_2d_array'); var interp2d = require('./interp2d'); @@ -57,7 +57,7 @@ module.exports = function calc(gd, trace) { z = binned.z; } else { - if(hasColumns(trace)) convertColumnXYZ(trace, xa, ya); + if(hasColumns(trace)) convertColumnData(trace, xa, ya, 'x', 'y', ['z']); x = trace.x ? xa.makeCalcdata(trace, 'x') : []; y = trace.y ? ya.makeCalcdata(trace, 'y') : []; diff --git a/src/traces/heatmap/convert_column_xyz.js b/src/traces/heatmap/convert_column_xyz.js index 796aca7f43d..eb527b07af8 100644 --- a/src/traces/heatmap/convert_column_xyz.js +++ b/src/traces/heatmap/convert_column_xyz.js @@ -12,49 +12,68 @@ var Lib = require('../../lib'); var BADNUM = require('../../constants/numerical').BADNUM; +module.exports = function convertColumnData(trace, ax1, ax2, var1Name, var2Name, arrayVarNames) { + var1Name = var1Name || 'x'; + var2Name = var2Name || 'y'; + arrayVarNames = arrayVarNames || ['z']; -module.exports = function convertColumnXYZ(trace, xa, ya) { - var xCol = trace.x.slice(), - yCol = trace.y.slice(), - zCol = trace.z, + var col1 = trace[var1Name].slice(), + col2 = trace[var2Name].slice(), textCol = trace.text, - colLen = Math.min(xCol.length, yCol.length, zCol.length), + colLen = Math.min(col1.length, col2.length), hasColumnText = (textCol !== undefined && !Array.isArray(textCol[0])), - xcalendar = trace.xcalendar, - ycalendar = trace.ycalendar; + col1Calendar = trace[var1Name + 'calendar'], + col2Calendar = trace[var2Name + 'calendar']; - var i; + var i, j, arrayVar, newArray, arrayVarName; - if(colLen < xCol.length) xCol = xCol.slice(0, colLen); - if(colLen < yCol.length) yCol = yCol.slice(0, colLen); + for(i = 0; i < arrayVarNames.length; i++) { + arrayVar = trace[arrayVarNames[i]]; + if(arrayVar) colLen = Math.min(colLen, arrayVar.length); + } + + if(colLen < col1.length) col1 = col1.slice(0, colLen); + if(colLen < col2.length) col2 = col2.slice(0, colLen); for(i = 0; i < colLen; i++) { - xCol[i] = xa.d2c(xCol[i], 0, xcalendar); - yCol[i] = ya.d2c(yCol[i], 0, ycalendar); + col1[i] = ax1.d2c(col1[i], 0, col1Calendar); + col2[i] = ax2.d2c(col2[i], 0, col2Calendar); } - var xColdv = Lib.distinctVals(xCol), - x = xColdv.vals, - yColdv = Lib.distinctVals(yCol), - y = yColdv.vals, - z = Lib.init2dArray(y.length, x.length); + var col1dv = Lib.distinctVals(col1), + col1vals = col1dv.vals, + col2dv = Lib.distinctVals(col2), + col2vals = col2dv.vals, + newArrays = []; + + for(i = 0; i < arrayVarNames.length; i++) { + newArrays[i] = Lib.init2dArray(col2vals.length, col1vals.length); + } - var text; + var i1, i2, text; - if(hasColumnText) text = Lib.init2dArray(y.length, x.length); + if(hasColumnText) text = Lib.init2dArray(col2vals.length, col1vals.length); for(i = 0; i < colLen; i++) { - if(xCol[i] !== BADNUM && yCol[i] !== BADNUM) { - var ix = Lib.findBin(xCol[i] + xColdv.minDiff / 2, x); - var iy = Lib.findBin(yCol[i] + yColdv.minDiff / 2, y); + if(col1[i] !== BADNUM && col2[i] !== BADNUM) { + i1 = Lib.findBin(col1[i] + col1dv.minDiff / 2, col1vals); + i2 = Lib.findBin(col2[i] + col2dv.minDiff / 2, col2vals); + + for(j = 0; j < arrayVarNames.length; j++) { + arrayVarName = arrayVarNames[j]; + arrayVar = trace[arrayVarName]; + newArray = newArrays[j]; + newArray[i2][i1] = arrayVar[i]; + } - z[iy][ix] = zCol[i]; - if(hasColumnText) text[iy][ix] = textCol[i]; + if(hasColumnText) text[i2][i1] = textCol[i]; } } - trace.x = x; - trace.y = y; - trace.z = z; + trace[var1Name] = col1vals; + trace[var2Name] = col2vals; + for(j = 0; j < arrayVarNames.length; j++) { + trace[arrayVarNames[j]] = newArrays[j]; + } if(hasColumnText) trace.text = text; }; diff --git a/src/traces/heatmap/xyz_defaults.js b/src/traces/heatmap/xyz_defaults.js index 33d13eb8dd5..435fc9a9eb5 100644 --- a/src/traces/heatmap/xyz_defaults.js +++ b/src/traces/heatmap/xyz_defaults.js @@ -15,22 +15,24 @@ var Registry = require('../../registry'); var hasColumns = require('./has_columns'); -module.exports = function handleXYZDefaults(traceIn, traceOut, coerce, layout) { +module.exports = function handleXYZDefaults(traceIn, traceOut, coerce, layout, xName, yName) { var z = coerce('z'); + xName = xName || 'x'; + yName = yName || 'y'; var x, y; if(z === undefined || !z.length) return 0; if(hasColumns(traceIn)) { - x = coerce('x'); - y = coerce('y'); + x = coerce(xName); + y = coerce(yName); - // column z must be accompanied by 'x' and 'y' arrays + // column z must be accompanied by xName and yName arrays if(!x || !y) return 0; } else { - x = coordDefaults('x', coerce); - y = coordDefaults('y', coerce); + x = coordDefaults(xName, coerce); + y = coordDefaults(yName, coerce); // TODO put z validation elsewhere if(!isValidZ(z)) return 0; @@ -39,7 +41,7 @@ module.exports = function handleXYZDefaults(traceIn, traceOut, coerce, layout) { } var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); - handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); + handleCalendarDefaults(traceIn, traceOut, [xName, yName], layout); return traceOut.z.length; }; diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js index 8e367807a70..99c8c044901 100644 --- a/src/traces/scatter/plot.js +++ b/src/traces/scatter/plot.js @@ -403,11 +403,11 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition textFilter = hideFilter; if(showMarkers) { - markerFilter = trace.marker.maxdisplayed ? visFilter : Lib.identity; + markerFilter = (trace.marker.maxdisplayed || trace._needsCull) ? visFilter : Lib.identity; } if(showText) { - textFilter = trace.marker.maxdisplayed ? visFilter : Lib.identity; + textFilter = (trace.marker.maxdisplayed || trace._needsCull) ? visFilter : Lib.identity; } // marker points diff --git a/src/traces/scattercarpet/attributes.js b/src/traces/scattercarpet/attributes.js new file mode 100644 index 00000000000..6ccd38e7af2 --- /dev/null +++ b/src/traces/scattercarpet/attributes.js @@ -0,0 +1,122 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var scatterAttrs = require('../scatter/attributes'); +var plotAttrs = require('../../plots/attributes'); +var colorAttributes = require('../../components/colorscale/color_attributes'); +var colorbarAttrs = require('../../components/colorbar/attributes'); + +var extendFlat = require('../../lib/extend').extendFlat; + +var scatterMarkerAttrs = scatterAttrs.marker, + scatterLineAttrs = scatterAttrs.line, + scatterMarkerLineAttrs = scatterMarkerAttrs.line; + +module.exports = { + carpet: { + valType: 'string', + role: 'info', + description: [ + 'An identifier for this carpet, so that `scattercarpet` and', + '`scattercontour` traces can specify a carpet plot on which', + 'they lie' + ].join(' ') + }, + a: { + valType: 'data_array', + description: [ + 'Sets the quantity of component `a` in each data point.', + 'If `a`, `b`, and `c` are all provided, they need not be', + 'normalized, only the relative values matter. If only two', + 'arrays are provided they must be normalized to match', + '`ternary.sum`.' + ].join(' ') + }, + b: { + valType: 'data_array', + description: [ + 'Sets the quantity of component `a` in each data point.', + 'If `a`, `b`, and `c` are all provided, they need not be', + 'normalized, only the relative values matter. If only two', + 'arrays are provided they must be normalized to match', + '`ternary.sum`.' + ].join(' ') + }, + sum: { + valType: 'number', + role: 'info', + dflt: 0, + min: 0, + description: [ + 'The number each triplet should sum to,', + 'if only two of `a`, `b`, and `c` are provided.', + 'This overrides `ternary.sum` to normalize this specific', + 'trace, but does not affect the values displayed on the axes.', + '0 (or missing) means to use ternary.sum' + ].join(' ') + }, + mode: extendFlat({}, scatterAttrs.mode, {dflt: 'markers'}), + text: extendFlat({}, scatterAttrs.text, { + description: [ + 'Sets text elements associated with each (a,b,c) point.', + 'If a single string, the same string appears over', + 'all the data points.', + 'If an array of strings, the items are mapped in order to the', + 'the data points in (a,b,c).' + ].join(' ') + }), + line: { + color: scatterLineAttrs.color, + width: scatterLineAttrs.width, + dash: scatterLineAttrs.dash, + shape: extendFlat({}, scatterLineAttrs.shape, + {values: ['linear', 'spline']}), + smoothing: scatterLineAttrs.smoothing + }, + connectgaps: scatterAttrs.connectgaps, + fill: extendFlat({}, scatterAttrs.fill, { + values: ['none', 'toself', 'tonext'], + description: [ + 'Sets the area to fill with a solid color.', + 'Use with `fillcolor` if not *none*.', + 'scatterternary has a subset of the options available to scatter.', + '*toself* connects the endpoints of the trace (or each segment', + 'of the trace if it has gaps) into a closed shape.', + '*tonext* fills the space between two traces if one completely', + 'encloses the other (eg consecutive contour lines), and behaves like', + '*toself* if there is no trace before it. *tonext* should not be', + 'used if one trace does not enclose the other.' + ].join(' ') + }), + fillcolor: scatterAttrs.fillcolor, + marker: extendFlat({}, { + symbol: scatterMarkerAttrs.symbol, + opacity: scatterMarkerAttrs.opacity, + maxdisplayed: scatterMarkerAttrs.maxdisplayed, + size: scatterMarkerAttrs.size, + sizeref: scatterMarkerAttrs.sizeref, + sizemin: scatterMarkerAttrs.sizemin, + sizemode: scatterMarkerAttrs.sizemode, + line: extendFlat({}, + {width: scatterMarkerLineAttrs.width}, + colorAttributes('marker'.line) + ) + }, colorAttributes('marker'), { + showscale: scatterMarkerAttrs.showscale, + colorbar: colorbarAttrs + }), + + textfont: scatterAttrs.textfont, + textposition: scatterAttrs.textposition, + hoverinfo: extendFlat({}, plotAttrs.hoverinfo, { + flags: ['a', 'b', 'c', 'text', 'name'] + }), + hoveron: scatterAttrs.hoveron, +}; diff --git a/src/traces/scattercarpet/calc.js b/src/traces/scattercarpet/calc.js new file mode 100644 index 00000000000..9a81a224ae0 --- /dev/null +++ b/src/traces/scattercarpet/calc.js @@ -0,0 +1,75 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var isNumeric = require('fast-isnumeric'); + +var Axes = require('../../plots/cartesian/axes'); +var Lib = require('../../lib'); + +var subTypes = require('../scatter/subtypes'); +var calcColorscale = require('../scatter/colorscale_calc'); +var lookupCarpet = require('../carpet/lookup_carpetid'); + +module.exports = function calc(gd, trace) { + var carpet = trace.carpetTrace = lookupCarpet(gd, trace); + if(!carpet || !carpet.visible || carpet.visible === 'legendonly') return; + var i; + + // Transfer this over from carpet before plotting since this is a necessary + // condition in order for cartesian to actually plot this trace: + trace.xaxis = carpet.xaxis; + trace.yaxis = carpet.yaxis; + + // make the calcdata array + var serieslen = trace.a.length; + var cd = new Array(serieslen); + var a, b; + var needsCull = false; + for(i = 0; i < serieslen; i++) { + a = trace.a[i]; + b = trace.b[i]; + if(isNumeric(a) && isNumeric(b)) { + var xy = carpet.ab2xy(+a, +b, true); + var visible = carpet.isVisible(+a, +b); + if(!visible) needsCull = true; + cd[i] = {x: xy[0], y: xy[1], a: a, b: b, vis: visible}; + } + else cd[i] = {x: false, y: false}; + } + + trace._needsCull = needsCull; + + cd[0].carpet = carpet; + cd[0].trace = trace; + + // fill in some extras + var marker, s; + if(subTypes.hasMarkers(trace)) { + // Treat size like x or y arrays --- Run d2c + // this needs to go before ppad computation + marker = trace.marker; + s = marker.size; + + if(Array.isArray(s)) { + var ax = {type: 'linear'}; + Axes.setConvert(ax); + s = ax.makeCalcdata(trace.marker, 'size'); + if(s.length > serieslen) s.splice(serieslen, s.length - serieslen); + } + } + + calcColorscale(trace); + + // this has migrated up from arraysToCalcdata as we have a reference to 's' here + if(typeof s !== 'undefined') Lib.mergeArray(s, cd, 'ms'); + + return cd; +}; diff --git a/src/traces/scattercarpet/defaults.js b/src/traces/scattercarpet/defaults.js new file mode 100644 index 00000000000..ed1dfb6c51e --- /dev/null +++ b/src/traces/scattercarpet/defaults.js @@ -0,0 +1,91 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Lib = require('../../lib'); + +var constants = require('../scatter/constants'); +var subTypes = require('../scatter/subtypes'); +var handleMarkerDefaults = require('../scatter/marker_defaults'); +var handleLineDefaults = require('../scatter/line_defaults'); +var handleLineShapeDefaults = require('../scatter/line_shape_defaults'); +var handleTextDefaults = require('../scatter/text_defaults'); +var handleFillColorDefaults = require('../scatter/fillcolor_defaults'); + +var attributes = require('./attributes'); + + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + coerce('carpet'); + + // XXX: Don't hard code this + traceOut.xaxis = 'x'; + traceOut.yaxis = 'y'; + + var a = coerce('a'), + b = coerce('b'), + len; + + len = Math.min(a.length, b.length); + + if(!len) { + traceOut.visible = false; + return; + } + + // cut all data arrays down to same length + if(a && len < a.length) traceOut.a = a.slice(0, len); + if(b && len < b.length) traceOut.b = b.slice(0, len); + + coerce('sum'); + + coerce('text'); + + var defaultMode = len < constants.PTS_LINESONLY ? 'lines+markers' : 'lines'; + coerce('mode', defaultMode); + + if(subTypes.hasLines(traceOut)) { + handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); + handleLineShapeDefaults(traceIn, traceOut, coerce); + coerce('connectgaps'); + } + + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce); + } + + if(subTypes.hasText(traceOut)) { + handleTextDefaults(traceIn, traceOut, layout, coerce); + } + + var dfltHoverOn = []; + + if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) { + coerce('marker.maxdisplayed'); + dfltHoverOn.push('points'); + } + + coerce('fill'); + if(traceOut.fill !== 'none') { + handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce); + if(!subTypes.hasLines(traceOut)) handleLineShapeDefaults(traceIn, traceOut, coerce); + } + + coerce('hoverinfo', (layout._dataLength === 1) ? 'a+b+text' : undefined); + + if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') { + dfltHoverOn.push('fills'); + } + coerce('hoveron', dfltHoverOn.join('+') || 'points'); +}; diff --git a/src/traces/scattercarpet/hover.js b/src/traces/scattercarpet/hover.js new file mode 100644 index 00000000000..980072cb12e --- /dev/null +++ b/src/traces/scattercarpet/hover.js @@ -0,0 +1,75 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var scatterHover = require('../scatter/hover'); + +module.exports = function hoverPoints(pointData, xval, yval, hovermode) { + var scatterPointData = scatterHover(pointData, xval, yval, hovermode); + if(!scatterPointData || scatterPointData[0].index === false) return; + + var newPointData = scatterPointData[0]; + + // if hovering on a fill, we don't show any point data so the label is + // unchanged from what scatter gives us - except that it needs to + // be constrained to the trianglular plot area, not just the rectangular + // area defined by the synthetic x and y axes + // TODO: in some cases the vertical middle of the shape is not within + // the triangular viewport at all, so the label can become disconnected + // from the shape entirely. But calculating what portion of the shape + // is actually visible, as constrained by the diagonal axis lines, is not + // so easy and anyway we lost the information we would have needed to do + // this inside scatterHover. + if(newPointData.index === undefined) { + var yFracUp = 1 - (newPointData.y0 / pointData.ya._length), + xLen = pointData.xa._length, + xMin = xLen * yFracUp / 2, + xMax = xLen - xMin; + newPointData.x0 = Math.max(Math.min(newPointData.x0, xMax), xMin); + newPointData.x1 = Math.max(Math.min(newPointData.x1, xMax), xMin); + return scatterPointData; + } + + var cdi = newPointData.cd[newPointData.index]; + + newPointData.a = cdi.a; + newPointData.b = cdi.b; + + newPointData.xLabelVal = undefined; + newPointData.yLabelVal = undefined; + // TODO: nice formatting, and label by axis title, for a, b, and c? + + var trace = newPointData.trace, + carpet = trace._carpet, + hoverinfo = trace.hoverinfo.split('+'), + text = []; + + function textPart(ax, val) { + text.push(((ax.labelprefix && ax.labelprefix.length > 0) ? ax.labelprefix : (ax._hovertitle + ': ')) + val.toFixed(3) + ax.labelsuffix); + } + + if(hoverinfo.indexOf('all') !== -1) hoverinfo = ['a', 'b']; + if(hoverinfo.indexOf('a') !== -1) textPart(carpet.aaxis, cdi.a); + if(hoverinfo.indexOf('b') !== -1) textPart(carpet.baxis, cdi.b); + + var ij = carpet.ab2ij([cdi.a, cdi.b]); + var i0 = Math.floor(ij[0]); + var ti = ij[0] - i0; + + var j0 = Math.floor(ij[1]); + var tj = ij[1] - j0; + + var xy = carpet.evalxy([], i0, j0, ti, tj); + text.push('y: ' + xy[1].toFixed(3)); + + newPointData.extraText = text.join('
'); + + return scatterPointData; +}; diff --git a/src/traces/scattercarpet/index.js b/src/traces/scattercarpet/index.js new file mode 100644 index 00000000000..a5d84296fd1 --- /dev/null +++ b/src/traces/scattercarpet/index.js @@ -0,0 +1,34 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var ScatterCarpet = {}; + +ScatterCarpet.attributes = require('./attributes'); +ScatterCarpet.supplyDefaults = require('./defaults'); +ScatterCarpet.colorbar = require('../scatter/colorbar'); +ScatterCarpet.calc = require('./calc'); +ScatterCarpet.plot = require('./plot'); +ScatterCarpet.style = require('./style'); +ScatterCarpet.hoverPoints = require('./hover'); +ScatterCarpet.selectPoints = require('./select'); + +ScatterCarpet.moduleType = 'trace'; +ScatterCarpet.name = 'scattercarpet'; +ScatterCarpet.basePlotModule = require('../../plots/cartesian'); +ScatterCarpet.categories = ['carpet', 'symbols', 'markerColorscale', 'showLegend', 'carpetDependent']; +ScatterCarpet.meta = { + hrName: 'scatter_carpet', + description: [ + 'Plots a scatter trace on either the first carpet axis or the', + 'carpet axis with a matching `carpet` attribute.' + ].join(' ') +}; + +module.exports = ScatterCarpet; diff --git a/src/traces/scattercarpet/plot.js b/src/traces/scattercarpet/plot.js new file mode 100644 index 00000000000..ebe356cf28e --- /dev/null +++ b/src/traces/scattercarpet/plot.js @@ -0,0 +1,42 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var scatterPlot = require('../scatter/plot'); +var Axes = require('../../plots/cartesian/axes'); + +module.exports = function plot(gd, plotinfoproxy, data) { + var i, trace, node; + + var carpet = data[0][0].carpet; + + // mimic cartesian plotinfo + var plotinfo = { + xaxis: Axes.getFromId(gd, carpet.xaxis || 'x'), + yaxis: Axes.getFromId(gd, carpet.yaxis || 'y'), + plot: plotinfoproxy.plot + }; + + scatterPlot(plotinfo.graphDiv, plotinfo, data); + + for(i = 0; i < data.length; i++) { + trace = data[i][0].trace; + + // Note: .select is adequate but seems to mutate the node data, + // which is at least a bit suprising and causes problems elsewhere + node = plotinfo.plot.selectAll('g.trace' + trace.uid + ' .js-line'); + + // Note: it would be more efficient if this didn't need to be applied + // separately to all scattercarpet traces, but that would require + // lots of reorganization of scatter traces that is otherwise not + // necessary. That makes this a potential optimization. + node.attr('clip-path', 'url(#clip' + carpet.uid + 'carpet)'); + } +}; diff --git a/src/traces/scattercarpet/select.js b/src/traces/scattercarpet/select.js new file mode 100644 index 00000000000..5682b0e1669 --- /dev/null +++ b/src/traces/scattercarpet/select.js @@ -0,0 +1,33 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var scatterSelect = require('../scatter/select'); + + +module.exports = function selectPoints(searchInfo, polygon) { + var selection = scatterSelect(searchInfo, polygon); + if(!selection) return; + + var cd = searchInfo.cd, + pt, cdi, i; + + for(i = 0; i < selection.length; i++) { + pt = selection[i]; + cdi = cd[pt.pointNumber]; + pt.a = cdi.a; + pt.b = cdi.b; + pt.c = cdi.c; + delete pt.x; + delete pt.y; + } + + return selection; +}; diff --git a/src/traces/scattercarpet/style.js b/src/traces/scattercarpet/style.js new file mode 100644 index 00000000000..8ead87cc97e --- /dev/null +++ b/src/traces/scattercarpet/style.js @@ -0,0 +1,27 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var scatterStyle = require('../scatter/style'); + + +module.exports = function style(gd) { + var modules = gd._fullLayout._modules; + + // we're just going to call scatter style... if we already + // called it, don't need to redo. + // Later though we may want differences, or we may make style + // more specific in its scope, then we can remove this. + for(var i = 0; i < modules.length; i++) { + if(modules[i].name === 'scatter') return; + } + + scatterStyle(gd); +}; diff --git a/test/image/baselines/airfoil.png b/test/image/baselines/airfoil.png new file mode 100644 index 00000000000..5864595e7ab Binary files /dev/null and b/test/image/baselines/airfoil.png differ diff --git a/test/image/baselines/carpet_axis.png b/test/image/baselines/carpet_axis.png new file mode 100644 index 00000000000..e584931c740 Binary files /dev/null and b/test/image/baselines/carpet_axis.png differ diff --git a/test/image/baselines/cheater.png b/test/image/baselines/cheater.png new file mode 100644 index 00000000000..90e53ea91bb Binary files /dev/null and b/test/image/baselines/cheater.png differ diff --git a/test/image/baselines/cheater_constraint_greater_than.png b/test/image/baselines/cheater_constraint_greater_than.png new file mode 100644 index 00000000000..540728e570d Binary files /dev/null and b/test/image/baselines/cheater_constraint_greater_than.png differ diff --git a/test/image/baselines/cheater_constraint_greater_than_with_hill.png b/test/image/baselines/cheater_constraint_greater_than_with_hill.png new file mode 100644 index 00000000000..bc1c3bf3f1d Binary files /dev/null and b/test/image/baselines/cheater_constraint_greater_than_with_hill.png differ diff --git a/test/image/baselines/cheater_constraint_greater_than_with_valley.png b/test/image/baselines/cheater_constraint_greater_than_with_valley.png new file mode 100644 index 00000000000..04756020783 Binary files /dev/null and b/test/image/baselines/cheater_constraint_greater_than_with_valley.png differ diff --git a/test/image/baselines/cheater_constraint_inner_range.png b/test/image/baselines/cheater_constraint_inner_range.png new file mode 100644 index 00000000000..faad63793bb Binary files /dev/null and b/test/image/baselines/cheater_constraint_inner_range.png differ diff --git a/test/image/baselines/cheater_constraint_inner_range_hi_top.png b/test/image/baselines/cheater_constraint_inner_range_hi_top.png new file mode 100644 index 00000000000..8d6b9c7cf39 Binary files /dev/null and b/test/image/baselines/cheater_constraint_inner_range_hi_top.png differ diff --git a/test/image/baselines/cheater_constraint_inner_range_hi_top_with_hill.png b/test/image/baselines/cheater_constraint_inner_range_hi_top_with_hill.png new file mode 100644 index 00000000000..ebfdfa4e581 Binary files /dev/null and b/test/image/baselines/cheater_constraint_inner_range_hi_top_with_hill.png differ diff --git a/test/image/baselines/cheater_constraint_inner_range_hi_top_with_valley.png b/test/image/baselines/cheater_constraint_inner_range_hi_top_with_valley.png new file mode 100644 index 00000000000..6d9cecccd30 Binary files /dev/null and b/test/image/baselines/cheater_constraint_inner_range_hi_top_with_valley.png differ diff --git a/test/image/baselines/cheater_constraint_inner_range_lo_top.png b/test/image/baselines/cheater_constraint_inner_range_lo_top.png new file mode 100644 index 00000000000..3eb1f52edc3 Binary files /dev/null and b/test/image/baselines/cheater_constraint_inner_range_lo_top.png differ diff --git a/test/image/baselines/cheater_constraint_inner_range_lo_top_with_hill.png b/test/image/baselines/cheater_constraint_inner_range_lo_top_with_hill.png new file mode 100644 index 00000000000..240f47d82a2 Binary files /dev/null and b/test/image/baselines/cheater_constraint_inner_range_lo_top_with_hill.png differ diff --git a/test/image/baselines/cheater_constraint_inner_range_lo_top_with_valley.png b/test/image/baselines/cheater_constraint_inner_range_lo_top_with_valley.png new file mode 100644 index 00000000000..c083e840042 Binary files /dev/null and b/test/image/baselines/cheater_constraint_inner_range_lo_top_with_valley.png differ diff --git a/test/image/baselines/cheater_constraint_inner_range_with_hill.png b/test/image/baselines/cheater_constraint_inner_range_with_hill.png new file mode 100644 index 00000000000..a3a226dc778 Binary files /dev/null and b/test/image/baselines/cheater_constraint_inner_range_with_hill.png differ diff --git a/test/image/baselines/cheater_constraint_inner_range_with_valley.png b/test/image/baselines/cheater_constraint_inner_range_with_valley.png new file mode 100644 index 00000000000..6bec9e84ee8 Binary files /dev/null and b/test/image/baselines/cheater_constraint_inner_range_with_valley.png differ diff --git a/test/image/baselines/cheater_constraint_less_than.png b/test/image/baselines/cheater_constraint_less_than.png new file mode 100644 index 00000000000..686239be521 Binary files /dev/null and b/test/image/baselines/cheater_constraint_less_than.png differ diff --git a/test/image/baselines/cheater_constraint_less_than_with_hill.png b/test/image/baselines/cheater_constraint_less_than_with_hill.png new file mode 100644 index 00000000000..e348b2d1f63 Binary files /dev/null and b/test/image/baselines/cheater_constraint_less_than_with_hill.png differ diff --git a/test/image/baselines/cheater_constraint_less_than_with_valley.png b/test/image/baselines/cheater_constraint_less_than_with_valley.png new file mode 100644 index 00000000000..7ce196aa586 Binary files /dev/null and b/test/image/baselines/cheater_constraint_less_than_with_valley.png differ diff --git a/test/image/baselines/cheater_constraint_outer_range.png b/test/image/baselines/cheater_constraint_outer_range.png new file mode 100644 index 00000000000..93a22387005 Binary files /dev/null and b/test/image/baselines/cheater_constraint_outer_range.png differ diff --git a/test/image/baselines/cheater_constraint_outer_range_hi_top.png b/test/image/baselines/cheater_constraint_outer_range_hi_top.png new file mode 100644 index 00000000000..d033c854152 Binary files /dev/null and b/test/image/baselines/cheater_constraint_outer_range_hi_top.png differ diff --git a/test/image/baselines/cheater_constraint_outer_range_hi_top_with_hill.png b/test/image/baselines/cheater_constraint_outer_range_hi_top_with_hill.png new file mode 100644 index 00000000000..041ebc7cae8 Binary files /dev/null and b/test/image/baselines/cheater_constraint_outer_range_hi_top_with_hill.png differ diff --git a/test/image/baselines/cheater_constraint_outer_range_hi_top_with_valley.png b/test/image/baselines/cheater_constraint_outer_range_hi_top_with_valley.png new file mode 100644 index 00000000000..d5e83425a07 Binary files /dev/null and b/test/image/baselines/cheater_constraint_outer_range_hi_top_with_valley.png differ diff --git a/test/image/baselines/cheater_constraint_outer_range_lo_top.png b/test/image/baselines/cheater_constraint_outer_range_lo_top.png new file mode 100644 index 00000000000..04edd3ec5e7 Binary files /dev/null and b/test/image/baselines/cheater_constraint_outer_range_lo_top.png differ diff --git a/test/image/baselines/cheater_constraint_outer_range_lo_top_with_hill.png b/test/image/baselines/cheater_constraint_outer_range_lo_top_with_hill.png new file mode 100644 index 00000000000..67a8416e5f2 Binary files /dev/null and b/test/image/baselines/cheater_constraint_outer_range_lo_top_with_hill.png differ diff --git a/test/image/baselines/cheater_constraint_outer_range_lo_top_with_valley.png b/test/image/baselines/cheater_constraint_outer_range_lo_top_with_valley.png new file mode 100644 index 00000000000..ccca7a93811 Binary files /dev/null and b/test/image/baselines/cheater_constraint_outer_range_lo_top_with_valley.png differ diff --git a/test/image/baselines/cheater_constraint_outer_range_with_hill.png b/test/image/baselines/cheater_constraint_outer_range_with_hill.png new file mode 100644 index 00000000000..7e86ea89787 Binary files /dev/null and b/test/image/baselines/cheater_constraint_outer_range_with_hill.png differ diff --git a/test/image/baselines/cheater_constraint_outer_range_with_valley.png b/test/image/baselines/cheater_constraint_outer_range_with_valley.png new file mode 100644 index 00000000000..923ac649da8 Binary files /dev/null and b/test/image/baselines/cheater_constraint_outer_range_with_valley.png differ diff --git a/test/image/baselines/cheater_constraints.png b/test/image/baselines/cheater_constraints.png new file mode 100644 index 00000000000..07a2b4554e4 Binary files /dev/null and b/test/image/baselines/cheater_constraints.png differ diff --git a/test/image/baselines/cheater_contour.png b/test/image/baselines/cheater_contour.png new file mode 100644 index 00000000000..3ab46900ff7 Binary files /dev/null and b/test/image/baselines/cheater_contour.png differ diff --git a/test/image/baselines/cheater_fully_filled.png b/test/image/baselines/cheater_fully_filled.png new file mode 100644 index 00000000000..1c7f63fd54d Binary files /dev/null and b/test/image/baselines/cheater_fully_filled.png differ diff --git a/test/image/baselines/cheater_smooth.png b/test/image/baselines/cheater_smooth.png new file mode 100644 index 00000000000..ba8e9f4a12d Binary files /dev/null and b/test/image/baselines/cheater_smooth.png differ diff --git a/test/image/baselines/scattercarpet.png b/test/image/baselines/scattercarpet.png new file mode 100644 index 00000000000..b8d5a4aec3c Binary files /dev/null and b/test/image/baselines/scattercarpet.png differ diff --git a/test/image/mocks/airfoil.json b/test/image/mocks/airfoil.json new file mode 100644 index 00000000000..e06a97c466e --- /dev/null +++ b/test/image/mocks/airfoil.json @@ -0,0 +1,563 @@ +{ + "config":{ + "scrollZoom":true + }, + "layout":{ + "yaxis":{ + "zeroline":false, + "range":[ + -1.800, + 1.800 + ], + "showgrid":false + }, + "dragmode":"pan", + "height":700, + "xaxis":{ + "zeroline":false, + "scaleratio":1, + "scaleanchor":"y", + "range":[ + -3.800, + 3.800 + ], + "showgrid":false + }, + "title":"Flow over a Karman-Trefftz airfoil", + "hovermode":"closest", + "margin":{ + "r":60, + "b":40, + "l":40, + "t":80 + }, + "width":900 + }, + "data":[ + { + "a": [1.083, 1.214, 1.344, 1.475, 1.605, 1.736, 1.866, 1.997, 2.128, 2.258, 2.389, 2.519, 2.650, 2.780, 2.911, 3.041, 3.172, 3.303, 3.433, 3.564, 3.694, 3.825, 3.955, 4.086, 4.217, 4.347, 4.478, 4.608, 4.739, 4.869, 5.000], + "b": [ + 0.000, 0.090, 0.180, 0.269, 0.359, 0.449, 0.539, 0.628, 0.718, 0.808, 0.898, 0.987, 1.077, 1.167, 1.257, 1.346, 1.436, 1.526, 1.616, 1.705, 1.795, 1.885, 1.975, 2.064, 2.154, 2.244, 2.334, 2.424, 2.513, 2.603, 2.693, + 2.783, 2.872, 2.962, 3.052, 3.142, 3.231, 3.321, 3.411, 3.501, 3.590, 3.680, 3.770, 3.860, 3.949, 4.039, 4.129, 4.219, 4.308, 4.398, 4.488, 4.578, 4.668, 4.757, 4.847, 4.937, 5.027, 5.116, 5.206, 5.296, 5.386, 5.475, + 5.565, 5.655, 5.745, 5.834, 5.924, 6.014, 6.104, 6.193, 6.283 + ], + "baxis":{ + "startline":false, + "endline":false, + "showticklabels":"none", + "smoothing":0, + "showgrid":false + }, + "y":[ + [ 0.002, 0.020, 0.032, 0.041, 0.048, 0.053, 0.057, 0.060, 0.062, 0.064, 0.066, 0.068, 0.069, 0.070, 0.071, 0.072, 0.072, 0.073, 0.073, 0.074, 0.074, 0.075, 0.075, 0.075, 0.076, 0.076, 0.076, 0.076, 0.077, 0.077, 0.077 ], + [ 0.009, 0.050, 0.083, 0.110, 0.134, 0.156, 0.176, 0.194, 0.211, 0.227, 0.243, 0.258, 0.273, 0.288, 0.302, 0.316, 0.329, 0.343, 0.356, 0.369, 0.382, 0.395, 0.408, 0.421, 0.433, 0.446, 0.458, 0.471, 0.483, 0.496, 0.508 ], + [ 0.020, 0.083, 0.135, 0.181, 0.222, 0.260, 0.295, 0.328, 0.359, 0.390, 0.420, 0.448, 0.476, 0.504, 0.531, 0.558, 0.584, 0.611, 0.637, 0.662, 0.688, 0.713, 0.738, 0.763, 0.788, 0.813, 0.838, 0.862, 0.887, 0.912, 0.936 ], + [ 0.036, 0.119, 0.190, 0.252, 0.310, 0.363, 0.413, 0.460, 0.506, 0.551, 0.594, 0.636, 0.677, 0.717, 0.757, 0.797, 0.836, 0.875, 0.913, 0.951, 0.989, 1.026, 1.064, 1.101, 1.138, 1.175, 1.211, 1.248, 1.284, 1.321, 1.357 ], + [ 0.056, 0.157, 0.245, 0.324, 0.397, 0.465, 0.529, 0.591, 0.650, 0.708, 0.764, 0.819, 0.873, 0.926, 0.979, 1.030, 1.082, 1.132, 1.183, 1.233, 1.282, 1.332, 1.381, 1.430, 1.479, 1.527, 1.576, 1.624, 1.672, 1.720, 1.768 ], + [ 0.079, 0.197, 0.301, 0.396, 0.483, 0.565, 0.643, 0.718, 0.791, 0.861, 0.930, 0.997, 1.063, 1.128, 1.193, 1.256, 1.319, 1.382, 1.444, 1.506, 1.567, 1.628, 1.688, 1.749, 1.809, 1.869, 1.928, 1.988, 2.047, 2.106, 2.165 ], + [ 0.105, 0.238, 0.357, 0.466, 0.567, 0.662, 0.753, 0.841, 0.926, 1.008, 1.089, 1.168, 1.246, 1.323, 1.398, 1.473, 1.548, 1.621, 1.694, 1.767, 1.839, 1.911, 1.983, 2.054, 2.125, 2.196, 2.266, 2.336, 2.406, 2.476, 2.546 ], + [ 0.133, 0.280, 0.413, 0.535, 0.648, 0.756, 0.859, 0.958, 1.055, 1.148, 1.240, 1.331, 1.419, 1.507, 1.594, 1.679, 1.764, 1.849, 1.932, 2.015, 2.098, 2.180, 2.262, 2.343, 2.425, 2.506, 2.586, 2.667, 2.747, 2.827, 2.907 ], + [ 0.162, 0.322, 0.467, 0.601, 0.726, 0.845, 0.959, 1.069, 1.176, 1.281, 1.383, 1.484, 1.583, 1.680, 1.777, 1.873, 1.968, 2.062, 2.155, 2.248, 2.340, 2.432, 2.524, 2.615, 2.706, 2.796, 2.887, 2.977, 3.066, 3.156, 3.245 ], + [ 0.192, 0.363, 0.519, 0.663, 0.799, 0.928, 1.052, 1.173, 1.289, 1.404, 1.516, 1.626, 1.734, 1.841, 1.947, 2.052, 2.156, 2.259, 2.362, 2.464, 2.565, 2.666, 2.766, 2.866, 2.966, 3.065, 3.164, 3.263, 3.362, 3.460, 3.558 ], + [ 0.223, 0.403, 0.568, 0.722, 0.867, 1.005, 1.139, 1.268, 1.393, 1.516, 1.637, 1.755, 1.872, 1.988, 2.102, 2.215, 2.328, 2.439, 2.550, 2.660, 2.769, 2.878, 2.987, 3.095, 3.203, 3.310, 3.417, 3.524, 3.631, 3.737, 3.843 ], + [ 0.252, 0.440, 0.614, 0.776, 0.929, 1.075, 1.216, 1.353, 1.487, 1.617, 1.746, 1.872, 1.996, 2.119, 2.241, 2.362, 2.481, 2.600, 2.718, 2.836, 2.952, 3.069, 3.184, 3.300, 3.415, 3.529, 3.643, 3.757, 3.871, 3.984, 4.098 ], + [ 0.280, 0.475, 0.655, 0.824, 0.984, 1.137, 1.285, 1.429, 1.569, 1.706, 1.841, 1.974, 2.105, 2.235, 2.363, 2.490, 2.616, 2.741, 2.866, 2.989, 3.112, 3.235, 3.357, 3.479, 3.600, 3.721, 3.841, 3.961, 4.081, 4.201, 4.320 ], + [ 0.305, 0.506, 0.692, 0.866, 1.032, 1.191, 1.344, 1.494, 1.639, 1.782, 1.923, 2.061, 2.197, 2.332, 2.466, 2.599, 2.730, 2.861, 2.990, 3.119, 3.248, 3.376, 3.503, 3.630, 3.756, 3.883, 4.008, 4.134, 4.259, 4.384, 4.508 ], + [ 0.329, 0.533, 0.723, 0.901, 1.072, 1.235, 1.393, 1.547, 1.697, 1.844, 1.989, 2.132, 2.273, 2.412, 2.550, 2.687, 2.823, 2.958, 3.092, 3.225, 3.358, 3.490, 3.622, 3.753, 3.884, 4.014, 4.144, 4.274, 4.403, 4.532, 4.661 ], + [ 0.349, 0.556, 0.748, 0.930, 1.103, 1.270, 1.431, 1.588, 1.741, 1.892, 2.040, 2.186, 2.330, 2.473, 2.614, 2.754, 2.893, 3.032, 3.169, 3.306, 3.441, 3.577, 3.712, 3.846, 3.980, 4.114, 4.247, 4.380, 4.512, 4.645, 4.777 ], + [ 0.365, 0.573, 0.767, 0.951, 1.126, 1.294, 1.458, 1.617, 1.772, 1.925, 2.075, 2.223, 2.370, 2.514, 2.658, 2.800, 2.941, 3.082, 3.221, 3.360, 3.498, 3.635, 3.772, 3.909, 4.045, 4.181, 4.316, 4.451, 4.586, 4.720, 4.854 ], + [ 0.378, 0.586, 0.780, 0.964, 1.139, 1.309, 1.473, 1.633, 1.789, 1.943, 2.094, 2.243, 2.390, 2.536, 2.680, 2.824, 2.966, 3.107, 3.248, 3.388, 3.527, 3.665, 3.803, 3.941, 4.078, 4.215, 4.351, 4.487, 4.623, 4.758, 4.894 ], + [ 0.386, 0.593, 0.786, 0.969, 1.144, 1.312, 1.476, 1.635, 1.792, 1.945, 2.096, 2.245, 2.392, 2.537, 2.682, 2.825, 2.967, 3.108, 3.249, 3.388, 3.528, 3.666, 3.804, 3.942, 4.079, 4.215, 4.352, 4.488, 4.624, 4.759, 4.894 ], + [ 0.391, 0.594, 0.785, 0.966, 1.139, 1.305, 1.467, 1.625, 1.780, 1.932, 2.081, 2.229, 2.375, 2.519, 2.662, 2.804, 2.945, 3.085, 3.224, 3.363, 3.501, 3.638, 3.775, 3.911, 4.047, 4.183, 4.318, 4.453, 4.587, 4.722, 4.856 ], + [ 0.390, 0.590, 0.777, 0.954, 1.124, 1.288, 1.447, 1.602, 1.754, 1.903, 2.050, 2.195, 2.339, 2.480, 2.621, 2.761, 2.899, 3.037, 3.174, 3.310, 3.446, 3.581, 3.716, 3.850, 3.983, 4.117, 4.250, 4.383, 4.515, 4.647, 4.779 ], + [ 0.386, 0.580, 0.762, 0.935, 1.100, 1.260, 1.415, 1.566, 1.714, 1.860, 2.003, 2.144, 2.284, 2.422, 2.560, 2.696, 2.831, 2.965, 3.099, 3.232, 3.364, 3.496, 3.627, 3.758, 3.888, 4.018, 4.148, 4.277, 4.407, 4.536, 4.664 ], + [ 0.377, 0.565, 0.741, 0.907, 1.067, 1.221, 1.371, 1.517, 1.660, 1.801, 1.940, 2.076, 2.211, 2.345, 2.478, 2.609, 2.740, 2.870, 2.999, 3.127, 3.255, 3.383, 3.510, 3.636, 3.762, 3.888, 4.013, 4.138, 4.263, 4.388, 4.512 ], + [ 0.364, 0.544, 0.712, 0.872, 1.025, 1.173, 1.317, 1.457, 1.594, 1.728, 1.861, 1.992, 2.121, 2.249, 2.376, 2.502, 2.627, 2.752, 2.875, 2.998, 3.121, 3.243, 3.364, 3.486, 3.606, 3.727, 3.847, 3.967, 4.086, 4.206, 4.325 ], + [ 0.347, 0.518, 0.678, 0.830, 0.975, 1.115, 1.251, 1.384, 1.514, 1.642, 1.768, 1.892, 2.014, 2.136, 2.256, 2.376, 2.494, 2.612, 2.729, 2.846, 2.962, 3.078, 3.193, 3.308, 3.422, 3.536, 3.650, 3.764, 3.877, 3.990, 4.103 ], + [ 0.326, 0.487, 0.638, 0.781, 0.917, 1.049, 1.176, 1.301, 1.423, 1.542, 1.660, 1.777, 1.892, 2.005, 2.118, 2.230, 2.341, 2.452, 2.561, 2.671, 2.780, 2.888, 2.996, 3.103, 3.211, 3.318, 3.424, 3.531, 3.637, 3.743, 3.849 ], + [ 0.302, 0.453, 0.593, 0.725, 0.852, 0.974, 1.092, 1.207, 1.320, 1.431, 1.540, 1.648, 1.754, 1.859, 1.964, 2.067, 2.170, 2.272, 2.374, 2.475, 2.575, 2.675, 2.775, 2.875, 2.974, 3.073, 3.171, 3.270, 3.368, 3.466, 3.564 ], + [ 0.276, 0.414, 0.542, 0.664, 0.779, 0.891, 0.999, 1.104, 1.207, 1.308, 1.408, 1.506, 1.603, 1.699, 1.794, 1.888, 1.982, 2.075, 2.167, 2.259, 2.351, 2.442, 2.533, 2.624, 2.714, 2.804, 2.894, 2.983, 3.073, 3.162, 3.251 ], + [ 0.247, 0.372, 0.488, 0.597, 0.701, 0.801, 0.898, 0.993, 1.085, 1.175, 1.264, 1.352, 1.439, 1.525, 1.610, 1.694, 1.778, 1.861, 1.944, 2.026, 2.108, 2.190, 2.271, 2.352, 2.432, 2.513, 2.593, 2.673, 2.753, 2.833, 2.912 ], + [ 0.216, 0.327, 0.429, 0.526, 0.618, 0.706, 0.791, 0.873, 0.954, 1.034, 1.112, 1.188, 1.264, 1.339, 1.414, 1.487, 1.561, 1.633, 1.705, 1.777, 1.849, 1.920, 1.991, 2.062, 2.132, 2.202, 2.272, 2.342, 2.412, 2.482, 2.551 ], + [ 0.183, 0.280, 0.368, 0.451, 0.530, 0.605, 0.677, 0.748, 0.817, 0.884, 0.950, 1.016, 1.080, 1.144, 1.207, 1.269, 1.331, 1.393, 1.454, 1.515, 1.575, 1.636, 1.696, 1.756, 1.815, 1.875, 1.934, 1.993, 2.052, 2.111, 2.170 ], + [ 0.150, 0.231, 0.305, 0.373, 0.438, 0.500, 0.559, 0.617, 0.673, 0.728, 0.782, 0.835, 0.888, 0.939, 0.991, 1.041, 1.092, 1.142, 1.191, 1.241, 1.290, 1.339, 1.387, 1.436, 1.484, 1.533, 1.581, 1.629, 1.676, 1.724, 1.772 ], + [ 0.117, 0.182, 0.240, 0.293, 0.344, 0.392, 0.437, 0.482, 0.525, 0.567, 0.608, 0.649, 0.689, 0.728, 0.767, 0.806, 0.844, 0.882, 0.920, 0.957, 0.995, 1.032, 1.069, 1.105, 1.142, 1.179, 1.215, 1.252, 1.288, 1.324, 1.360 ], + [ 0.085, 0.132, 0.174, 0.212, 0.248, 0.281, 0.313, 0.344, 0.373, 0.402, 0.430, 0.458, 0.485, 0.512, 0.538, 0.564, 0.590, 0.616, 0.642, 0.667, 0.692, 0.717, 0.742, 0.767, 0.791, 0.816, 0.841, 0.865, 0.890, 0.914, 0.938 ], + [ 0.053, 0.083, 0.108, 0.130, 0.151, 0.169, 0.187, 0.203, 0.219, 0.235, 0.250, 0.264, 0.278, 0.292, 0.306, 0.319, 0.333, 0.346, 0.359, 0.372, 0.385, 0.397, 0.410, 0.423, 0.435, 0.448, 0.460, 0.472, 0.485, 0.497, 0.509 ], + [ 0.023, 0.035, 0.043, 0.049, 0.054, 0.057, 0.060, 0.063, 0.065, 0.066, 0.068, 0.069, 0.070, 0.071, 0.072, 0.072, 0.073, 0.074, 0.074, 0.074, 0.075, 0.075, 0.075, 0.076, 0.076, 0.076, 0.076, 0.077, 0.077, 0.077, 0.077 ], + [ -0.005, -0.012, -0.021, -0.032, -0.043, -0.054, -0.066, -0.077, -0.089, -0.101, -0.114, -0.126, -0.138, -0.150, -0.162, -0.174, -0.186, -0.199, -0.211, -0.223, -0.235, -0.247, -0.259, -0.271, -0.283, -0.295, -0.307, -0.319, -0.331, -0.343, -0.355 ], + [ -0.031, -0.057, -0.084, -0.111, -0.137, -0.164, -0.190, -0.216, -0.242, -0.268, -0.293, -0.318, -0.344, -0.369, -0.394, -0.419, -0.443, -0.468, -0.493, -0.517, -0.542, -0.566, -0.590, -0.615, -0.639, -0.663, -0.687, -0.711, -0.736, -0.760, -0.784 ], + [ -0.055, -0.100, -0.144, -0.187, -0.230, -0.271, -0.312, -0.352, -0.391, -0.431, -0.469, -0.508, -0.546, -0.584, -0.621, -0.659, -0.696, -0.733, -0.770, -0.807, -0.844, -0.880, -0.917, -0.953, -0.989, -1.025, -1.061, -1.097, -1.133, -1.169, -1.205 ], + [ -0.077, -0.141, -0.202, -0.261, -0.319, -0.375, -0.430, -0.483, -0.537, -0.589, -0.641, -0.692, -0.743, -0.793, -0.843, -0.893, -0.942, -0.991, -1.040, -1.089, -1.138, -1.186, -1.234, -1.282, -1.330, -1.378, -1.426, -1.474, -1.521, -1.569, -1.616 ], + [ -0.096, -0.178, -0.256, -0.331, -0.404, -0.474, -0.543, -0.610, -0.676, -0.742, -0.806, -0.870, -0.933, -0.995, -1.057, -1.119, -1.180, -1.241, -1.302, -1.362, -1.422, -1.482, -1.542, -1.601, -1.660, -1.719, -1.779, -1.837, -1.896, -1.955, -2.013 ], + [ -0.113, -0.213, -0.307, -0.397, -0.484, -0.568, -0.651, -0.731, -0.810, -0.887, -0.964, -1.039, -1.114, -1.188, -1.262, -1.335, -1.407, -1.480, -1.551, -1.623, -1.694, -1.765, -1.835, -1.906, -1.976, -2.046, -2.116, -2.186, -2.255, -2.325, -2.394 ], + [ -0.127, -0.244, -0.354, -0.459, -0.560, -0.657, -0.752, -0.845, -0.935, -1.025, -1.113, -1.200, -1.286, -1.371, -1.455, -1.539, -1.623, -1.705, -1.788, -1.870, -1.951, -2.033, -2.114, -2.195, -2.275, -2.355, -2.436, -2.515, -2.595, -2.675, -2.754 ], + [ -0.138, -0.271, -0.396, -0.515, -0.629, -0.739, -0.846, -0.950, -1.052, -1.153, -1.252, -1.350, -1.446, -1.542, -1.637, -1.731, -1.824, -1.917, -2.009, -2.101, -2.193, -2.284, -2.374, -2.465, -2.555, -2.645, -2.735, -2.824, -2.914, -3.003, -3.092 ], + [ -0.145, -0.294, -0.434, -0.566, -0.692, -0.814, -0.932, -1.047, -1.160, -1.271, -1.380, -1.488, -1.594, -1.699, -1.803, -1.907, -2.010, -2.112, -2.213, -2.315, -2.415, -2.515, -2.615, -2.715, -2.814, -2.913, -3.011, -3.110, -3.208, -3.306, -3.404 ], + [ -0.150, -0.314, -0.466, -0.611, -0.748, -0.881, -1.010, -1.135, -1.257, -1.378, -1.496, -1.613, -1.728, -1.842, -1.955, -2.067, -2.179, -2.289, -2.399, -2.509, -2.618, -2.726, -2.834, -2.942, -3.049, -3.156, -3.263, -3.369, -3.476, -3.582, -3.687 ], + [ -0.152, -0.329, -0.494, -0.649, -0.797, -0.940, -1.078, -1.212, -1.344, -1.472, -1.599, -1.724, -1.847, -1.969, -2.090, -2.210, -2.329, -2.447, -2.565, -2.682, -2.798, -2.914, -3.029, -3.144, -3.259, -3.373, -3.487, -3.601, -3.714, -3.828, -3.941 ], + [ -0.151, -0.340, -0.516, -0.682, -0.839, -0.990, -1.137, -1.279, -1.418, -1.554, -1.688, -1.820, -1.951, -2.080, -2.208, -2.334, -2.460, -2.585, -2.709, -2.833, -2.955, -3.078, -3.200, -3.321, -3.442, -3.563, -3.683, -3.803, -3.923, -4.042, -4.162 ], + [ -0.147, -0.347, -0.533, -0.707, -0.873, -1.032, -1.185, -1.335, -1.480, -1.623, -1.763, -1.902, -2.038, -2.173, -2.307, -2.439, -2.570, -2.701, -2.831, -2.960, -3.088, -3.216, -3.343, -3.470, -3.597, -3.723, -3.849, -3.974, -4.099, -4.224, -4.348 ], + [ -0.140, -0.351, -0.544, -0.726, -0.899, -1.064, -1.224, -1.379, -1.530, -1.678, -1.823, -1.967, -2.108, -2.248, -2.386, -2.523, -2.660, -2.795, -2.929, -3.063, -3.196, -3.328, -3.460, -3.591, -3.722, -3.852, -3.982, -4.112, -4.241, -4.371, -4.500 ], + [ -0.132, -0.350, -0.551, -0.739, -0.917, -1.087, -1.251, -1.411, -1.566, -1.719, -1.868, -2.015, -2.161, -2.304, -2.446, -2.587, -2.727, -2.865, -3.003, -3.140, -3.276, -3.412, -3.547, -3.682, -3.816, -3.950, -4.083, -4.216, -4.349, -4.482, -4.614 ], + [ -0.121, -0.346, -0.552, -0.744, -0.927, -1.101, -1.268, -1.431, -1.590, -1.745, -1.897, -2.047, -2.195, -2.341, -2.486, -2.629, -2.771, -2.912, -3.052, -3.192, -3.330, -3.468, -3.606, -3.743, -3.879, -4.015, -4.151, -4.286, -4.421, -4.556, -4.690 ], + [ -0.109, -0.338, -0.548, -0.744, -0.929, -1.105, -1.275, -1.440, -1.600, -1.757, -1.911, -2.062, -2.211, -2.359, -2.505, -2.649, -2.793, -2.935, -3.076, -3.217, -3.357, -3.496, -3.635, -3.773, -3.911, -4.048, -4.185, -4.321, -4.457, -4.593, -4.728 ], + [ -0.096, -0.328, -0.540, -0.737, -0.923, -1.100, -1.271, -1.436, -1.597, -1.754, -1.908, -2.060, -2.209, -2.357, -2.503, -2.648, -2.791, -2.934, -3.075, -3.216, -3.356, -3.495, -3.634, -3.772, -3.910, -4.047, -4.184, -4.320, -4.456, -4.592, -4.728 ], + [ -0.082, -0.315, -0.527, -0.724, -0.910, -1.086, -1.256, -1.420, -1.580, -1.737, -1.890, -2.040, -2.189, -2.336, -2.481, -2.624, -2.767, -2.908, -3.049, -3.188, -3.327, -3.466, -3.603, -3.740, -3.877, -4.013, -4.149, -4.284, -4.419, -4.554, -4.689 ], + [ -0.069, -0.300, -0.511, -0.706, -0.889, -1.063, -1.231, -1.393, -1.551, -1.705, -1.856, -2.004, -2.151, -2.295, -2.438, -2.580, -2.720, -2.859, -2.997, -3.135, -3.271, -3.408, -3.543, -3.678, -3.812, -3.946, -4.080, -4.213, -4.346, -4.479, -4.611 ], + [ -0.055, -0.284, -0.490, -0.682, -0.861, -1.032, -1.196, -1.355, -1.509, -1.659, -1.807, -1.952, -2.095, -2.236, -2.375, -2.513, -2.650, -2.786, -2.921, -3.055, -3.189, -3.322, -3.454, -3.585, -3.717, -3.847, -3.978, -4.108, -4.237, -4.367, -4.496 ], + [ -0.043, -0.265, -0.467, -0.653, -0.827, -0.993, -1.152, -1.305, -1.455, -1.600, -1.743, -1.884, -2.022, -2.158, -2.293, -2.427, -2.559, -2.691, -2.821, -2.951, -3.080, -3.208, -3.336, -3.464, -3.590, -3.717, -3.843, -3.969, -4.094, -4.219, -4.344 ], + [ -0.031, -0.246, -0.440, -0.619, -0.786, -0.946, -1.098, -1.245, -1.389, -1.528, -1.665, -1.800, -1.932, -2.063, -2.192, -2.320, -2.447, -2.573, -2.698, -2.822, -2.946, -3.069, -3.191, -3.313, -3.435, -3.556, -3.677, -3.797, -3.917, -4.037, -4.157 ], + [ -0.021, -0.226, -0.411, -0.581, -0.740, -0.891, -1.036, -1.176, -1.311, -1.444, -1.574, -1.701, -1.827, -1.951, -2.073, -2.195, -2.315, -2.434, -2.553, -2.671, -2.788, -2.904, -3.020, -3.136, -3.251, -3.366, -3.480, -3.594, -3.708, -3.822, -3.935 ], + [ -0.013, -0.206, -0.379, -0.538, -0.688, -0.830, -0.965, -1.096, -1.223, -1.348, -1.469, -1.589, -1.706, -1.822, -1.937, -2.051, -2.164, -2.275, -2.386, -2.497, -2.607, -2.716, -2.824, -2.933, -3.041, -3.148, -3.255, -3.362, -3.469, -3.575, -3.681 ], + [ -0.007, -0.185, -0.345, -0.493, -0.631, -0.762, -0.887, -1.008, -1.126, -1.240, -1.353, -1.463, -1.572, -1.679, -1.785, -1.890, -1.995, -2.098, -2.201, -2.303, -2.404, -2.505, -2.605, -2.706, -2.805, -2.905, -3.004, -3.103, -3.201, -3.299, -3.398 ], + [ -0.002, -0.164, -0.309, -0.443, -0.569, -0.688, -0.802, -0.912, -1.019, -1.123, -1.225, -1.326, -1.425, -1.522, -1.619, -1.714, -1.809, -1.903, -1.996, -2.089, -2.182, -2.273, -2.365, -2.456, -2.547, -2.637, -2.727, -2.817, -2.907, -2.996, -3.086 ], + [ 0.001, -0.143, -0.272, -0.391, -0.503, -0.609, -0.710, -0.808, -0.903, -0.996, -1.088, -1.177, -1.265, -1.352, -1.439, -1.524, -1.608, -1.692, -1.776, -1.859, -1.941, -2.023, -2.105, -2.186, -2.267, -2.348, -2.428, -2.509, -2.589, -2.669, -2.749 ], + [ 0.003, -0.121, -0.233, -0.336, -0.433, -0.525, -0.613, -0.698, -0.781, -0.862, -0.941, -1.019, -1.096, -1.171, -1.246, -1.321, -1.394, -1.468, -1.540, -1.613, -1.684, -1.756, -1.827, -1.898, -1.969, -2.039, -2.109, -2.179, -2.249, -2.319, -2.389 ], + [ 0.003, -0.100, -0.193, -0.279, -0.359, -0.436, -0.510, -0.582, -0.651, -0.719, -0.786, -0.852, -0.916, -0.980, -1.044, -1.107, -1.169, -1.231, -1.292, -1.353, -1.414, -1.474, -1.534, -1.594, -1.654, -1.713, -1.773, -1.832, -1.891, -1.950, -2.009 ], + [ 0.003, -0.078, -0.151, -0.219, -0.283, -0.344, -0.403, -0.460, -0.516, -0.571, -0.624, -0.677, -0.729, -0.781, -0.832, -0.883, -0.933, -0.983, -1.032, -1.082, -1.131, -1.180, -1.228, -1.277, -1.325, -1.373, -1.421, -1.469, -1.517, -1.565, -1.612 ], + [ 0.002, -0.055, -0.108, -0.157, -0.203, -0.248, -0.292, -0.335, -0.376, -0.417, -0.457, -0.497, -0.536, -0.575, -0.613, -0.651, -0.689, -0.727, -0.764, -0.801, -0.838, -0.875, -0.912, -0.949, -0.985, -1.021, -1.058, -1.094, -1.130, -1.166, -1.202 ], + [ 0.001, -0.032, -0.063, -0.093, -0.122, -0.150, -0.178, -0.205, -0.232, -0.259, -0.285, -0.312, -0.337, -0.363, -0.388, -0.414, -0.439, -0.464, -0.489, -0.514, -0.538, -0.563, -0.587, -0.612, -0.636, -0.661, -0.685, -0.709, -0.733, -0.758, -0.782 ], + [ 0.000, -0.007, -0.016, -0.027, -0.038, -0.050, -0.062, -0.074, -0.086, -0.098, -0.111, -0.123, -0.135, -0.148, -0.160, -0.172, -0.184, -0.197, -0.209, -0.221, -0.233, -0.245, -0.258, -0.270, -0.282, -0.294, -0.306, -0.318, -0.330, -0.342, -0.354 ], + [ 0.002, 0.020, 0.032, 0.041, 0.048, 0.053, 0.057, 0.060, 0.062, 0.064, 0.066, 0.068, 0.069, 0.070, 0.071, 0.072, 0.072, 0.073, 0.073, 0.074, 0.074, 0.075, 0.075, 0.075, 0.076, 0.076, 0.076, 0.076, 0.077, 0.077, 0.077 ] + ], + "x":[ + [ 1.933, 1.953, 1.998, 2.059, 2.132, 2.214, 2.304, 2.399, 2.498, 2.602, 2.708, 2.817, 2.929, 3.042, 3.157, 3.273, 3.390, 3.509, 3.628, 3.748, 3.869, 3.991, 4.113, 4.236, 4.359, 4.483, 4.607, 4.732, 4.857, 4.982, 5.107 ], + [ 1.906, 1.932, 1.979, 2.042, 2.116, 2.199, 2.289, 2.385, 2.485, 2.588, 2.694, 2.803, 2.914, 3.027, 3.142, 3.258, 3.375, 3.493, 3.612, 3.732, 3.852, 3.974, 4.095, 4.218, 4.341, 4.464, 4.588, 4.712, 4.836, 4.961, 5.086 ], + [ 1.863, 1.893, 1.943, 2.007, 2.082, 2.166, 2.255, 2.350, 2.449, 2.552, 2.658, 2.766, 2.876, 2.988, 3.101, 3.215, 3.331, 3.448, 3.566, 3.684, 3.804, 3.923, 4.044, 4.165, 4.286, 4.408, 4.530, 4.653, 4.776, 4.899, 5.023 ], + [ 1.803, 1.837, 1.890, 1.955, 2.030, 2.113, 2.202, 2.296, 2.394, 2.495, 2.599, 2.705, 2.813, 2.923, 3.034, 3.147, 3.260, 3.375, 3.490, 3.607, 3.723, 3.841, 3.959, 4.078, 4.197, 4.316, 4.436, 4.556, 4.677, 4.797, 4.918 ], + [ 1.728, 1.766, 1.820, 1.886, 1.961, 2.043, 2.130, 2.222, 2.318, 2.417, 2.518, 2.622, 2.727, 2.834, 2.942, 3.052, 3.162, 3.274, 3.386, 3.499, 3.613, 3.727, 3.842, 3.957, 4.073, 4.189, 4.305, 4.422, 4.539, 4.656, 4.774 ], + [ 1.640, 1.681, 1.736, 1.802, 1.876, 1.956, 2.041, 2.130, 2.223, 2.319, 2.417, 2.517, 2.618, 2.722, 2.826, 2.932, 3.038, 3.146, 3.254, 3.363, 3.472, 3.582, 3.693, 3.804, 3.915, 4.027, 4.139, 4.251, 4.364, 4.477, 4.590 ], + [ 1.539, 1.583, 1.638, 1.703, 1.775, 1.853, 1.935, 2.021, 2.110, 2.201, 2.295, 2.391, 2.488, 2.587, 2.686, 2.787, 2.889, 2.991, 3.095, 3.199, 3.303, 3.408, 3.513, 3.619, 3.725, 3.832, 3.939, 4.046, 4.153, 4.261, 4.369 ], + [ 1.427, 1.472, 1.527, 1.591, 1.660, 1.734, 1.813, 1.895, 1.979, 2.066, 2.155, 2.245, 2.337, 2.430, 2.525, 2.620, 2.716, 2.813, 2.910, 3.008, 3.107, 3.206, 3.305, 3.405, 3.505, 3.606, 3.706, 3.808, 3.909, 4.010, 4.112 ], + [ 1.306, 1.351, 1.405, 1.466, 1.532, 1.602, 1.676, 1.753, 1.832, 1.914, 1.997, 2.081, 2.167, 2.254, 2.342, 2.431, 2.521, 2.611, 2.702, 2.793, 2.885, 2.977, 3.070, 3.163, 3.256, 3.350, 3.444, 3.538, 3.632, 3.727, 3.821 ], + [ 1.175, 1.220, 1.272, 1.329, 1.391, 1.457, 1.526, 1.597, 1.670, 1.746, 1.822, 1.900, 1.979, 2.060, 2.140, 2.222, 2.305, 2.387, 2.471, 2.555, 2.639, 2.724, 2.809, 2.895, 2.980, 3.066, 3.153, 3.239, 3.326, 3.412, 3.499 ], + [ 1.037, 1.080, 1.129, 1.183, 1.240, 1.300, 1.363, 1.428, 1.495, 1.563, 1.633, 1.704, 1.775, 1.848, 1.921, 1.995, 2.069, 2.144, 2.220, 2.296, 2.372, 2.449, 2.525, 2.603, 2.680, 2.758, 2.835, 2.914, 2.992, 3.070, 3.149 ], + [ 0.892, 0.933, 0.979, 1.028, 1.079, 1.134, 1.190, 1.248, 1.308, 1.368, 1.430, 1.493, 1.557, 1.621, 1.686, 1.751, 1.817, 1.884, 1.951, 2.018, 2.085, 2.153, 2.221, 2.289, 2.358, 2.426, 2.495, 2.564, 2.633, 2.703, 2.772 ], + [ 0.742, 0.780, 0.821, 0.865, 0.910, 0.958, 1.007, 1.058, 1.110, 1.162, 1.216, 1.270, 1.325, 1.381, 1.437, 1.493, 1.550, 1.607, 1.665, 1.723, 1.781, 1.839, 1.898, 1.957, 2.016, 2.075, 2.134, 2.194, 2.253, 2.313, 2.372 ], + [ 0.588, 0.622, 0.658, 0.695, 0.735, 0.775, 0.817, 0.859, 0.903, 0.947, 0.992, 1.037, 1.083, 1.129, 1.176, 1.223, 1.270, 1.318, 1.366, 1.414, 1.462, 1.511, 1.559, 1.608, 1.657, 1.706, 1.755, 1.805, 1.854, 1.904, 1.953 ], + [ 0.430, 0.460, 0.490, 0.521, 0.553, 0.586, 0.619, 0.654, 0.688, 0.723, 0.759, 0.795, 0.831, 0.868, 0.905, 0.942, 0.980, 1.017, 1.055, 1.093, 1.131, 1.169, 1.208, 1.246, 1.285, 1.323, 1.362, 1.401, 1.440, 1.479, 1.518 ], + [ 0.270, 0.295, 0.319, 0.343, 0.368, 0.392, 0.417, 0.443, 0.468, 0.494, 0.520, 0.547, 0.573, 0.600, 0.627, 0.654, 0.681, 0.708, 0.735, 0.763, 0.790, 0.818, 0.846, 0.874, 0.901, 0.929, 0.957, 0.985, 1.013, 1.042, 1.070 ], + [ 0.109, 0.128, 0.146, 0.163, 0.179, 0.196, 0.212, 0.228, 0.244, 0.261, 0.277, 0.293, 0.310, 0.326, 0.343, 0.359, 0.376, 0.393, 0.409, 0.426, 0.443, 0.460, 0.477, 0.493, 0.510, 0.527, 0.544, 0.561, 0.578, 0.595, 0.612 ], + [ -0.052, -0.039, -0.028, -0.019, -0.011, -0.003, 0.005, 0.012, 0.018, 0.025, 0.031, 0.038, 0.044, 0.050, 0.056, 0.062, 0.068, 0.074, 0.080, 0.085, 0.091, 0.097, 0.103, 0.109, 0.115, 0.120, 0.126, 0.132, 0.138, 0.144, 0.149 ], + [ -0.213, -0.206, -0.202, -0.201, -0.200, -0.201, -0.203, -0.205, -0.208, -0.211, -0.215, -0.219, -0.223, -0.228, -0.232, -0.237, -0.242, -0.246, -0.251, -0.256, -0.262, -0.267, -0.272, -0.277, -0.283, -0.288, -0.293, -0.299, -0.304, -0.310, -0.315 ], + [ -0.372, -0.371, -0.375, -0.381, -0.389, -0.398, -0.409, -0.421, -0.433, -0.447, -0.460, -0.474, -0.489, -0.504, -0.519, -0.534, -0.549, -0.565, -0.581, -0.597, -0.613, -0.629, -0.645, -0.662, -0.678, -0.695, -0.711, -0.728, -0.744, -0.761, -0.778 ], + [ -0.528, -0.534, -0.544, -0.558, -0.574, -0.593, -0.612, -0.634, -0.656, -0.679, -0.702, -0.726, -0.751, -0.776, -0.802, -0.827, -0.853, -0.880, -0.906, -0.933, -0.960, -0.987, -1.014, -1.041, -1.069, -1.096, -1.124, -1.151, -1.179, -1.207, -1.235 ], + [ -0.680, -0.692, -0.710, -0.731, -0.756, -0.783, -0.811, -0.842, -0.873, -0.906, -0.939, -0.973, -1.008, -1.043, -1.079, -1.115, -1.151, -1.188, -1.225, -1.262, -1.300, -1.337, -1.375, -1.413, -1.451, -1.489, -1.528, -1.566, -1.605, -1.644, -1.683 ], + [ -0.828, -0.846, -0.871, -0.900, -0.932, -0.967, -1.005, -1.044, -1.084, -1.126, -1.169, -1.212, -1.257, -1.302, -1.348, -1.394, -1.440, -1.487, -1.534, -1.582, -1.629, -1.677, -1.726, -1.774, -1.823, -1.872, -1.920, -1.969, -2.019, -2.068, -2.117 ], + [ -0.970, -0.994, -1.025, -1.061, -1.102, -1.145, -1.190, -1.238, -1.287, -1.338, -1.390, -1.443, -1.497, -1.551, -1.606, -1.662, -1.718, -1.775, -1.832, -1.889, -1.947, -2.005, -2.063, -2.122, -2.180, -2.239, -2.298, -2.357, -2.417, -2.476, -2.536 ], + [ -1.106, -1.136, -1.173, -1.216, -1.263, -1.314, -1.367, -1.423, -1.481, -1.540, -1.601, -1.663, -1.725, -1.789, -1.853, -1.918, -1.983, -2.049, -2.116, -2.182, -2.250, -2.317, -2.385, -2.453, -2.521, -2.589, -2.658, -2.727, -2.796, -2.865, -2.934 ], + [ -1.234, -1.269, -1.312, -1.362, -1.416, -1.474, -1.535, -1.598, -1.664, -1.731, -1.800, -1.870, -1.941, -2.013, -2.085, -2.159, -2.233, -2.308, -2.383, -2.459, -2.535, -2.611, -2.688, -2.765, -2.842, -2.920, -2.997, -3.075, -3.153, -3.232, -3.310 ], + [ -1.355, -1.395, -1.443, -1.498, -1.558, -1.623, -1.690, -1.761, -1.834, -1.908, -1.985, -2.062, -2.141, -2.221, -2.302, -2.383, -2.466, -2.549, -2.632, -2.716, -2.800, -2.885, -2.970, -3.055, -3.141, -3.227, -3.313, -3.399, -3.486, -3.573, -3.660 ], + [ -1.467, -1.510, -1.563, -1.623, -1.689, -1.760, -1.834, -1.911, -1.990, -2.072, -2.155, -2.240, -2.326, -2.413, -2.501, -2.590, -2.679, -2.770, -2.861, -2.952, -3.044, -3.136, -3.229, -3.322, -3.415, -3.509, -3.603, -3.697, -3.791, -3.886, -3.981 ], + [ -1.569, -1.616, -1.673, -1.737, -1.808, -1.884, -1.963, -2.046, -2.132, -2.219, -2.309, -2.400, -2.492, -2.586, -2.680, -2.776, -2.872, -2.969, -3.067, -3.165, -3.264, -3.363, -3.463, -3.563, -3.663, -3.764, -3.864, -3.966, -4.067, -4.169, -4.270 ], + [ -1.661, -1.710, -1.771, -1.839, -1.914, -1.994, -2.079, -2.166, -2.257, -2.350, -2.445, -2.541, -2.639, -2.739, -2.839, -2.941, -3.043, -3.146, -3.249, -3.354, -3.458, -3.564, -3.669, -3.775, -3.882, -3.989, -4.096, -4.203, -4.311, -4.418, -4.526 ], + [ -1.742, -1.793, -1.856, -1.928, -2.006, -2.090, -2.178, -2.270, -2.365, -2.463, -2.562, -2.664, -2.767, -2.871, -2.976, -3.082, -3.190, -3.298, -3.406, -3.516, -3.626, -3.736, -3.847, -3.959, -4.070, -4.182, -4.295, -4.407, -4.520, -4.633, -4.747 ], + [ -1.812, -1.864, -1.928, -2.002, -2.083, -2.170, -2.262, -2.357, -2.456, -2.557, -2.660, -2.766, -2.872, -2.981, -3.090, -3.200, -3.312, -3.424, -3.537, -3.651, -3.765, -3.880, -3.995, -4.111, -4.227, -4.343, -4.460, -4.577, -4.694, -4.812, -4.930 ], + [ -1.869, -1.921, -1.987, -2.062, -2.145, -2.234, -2.328, -2.426, -2.528, -2.632, -2.738, -2.846, -2.956, -3.067, -3.180, -3.294, -3.408, -3.524, -3.640, -3.757, -3.874, -3.993, -4.111, -4.230, -4.350, -4.470, -4.590, -4.710, -4.831, -4.952, -5.074 ], + [ -1.913, -1.965, -2.031, -2.107, -2.191, -2.282, -2.377, -2.477, -2.580, -2.686, -2.794, -2.905, -3.017, -3.130, -3.245, -3.361, -3.478, -3.596, -3.714, -3.834, -3.954, -4.074, -4.195, -4.317, -4.439, -4.561, -4.684, -4.807, -4.930, -5.053, -5.177 ], + [ -1.945, -1.995, -2.060, -2.136, -2.221, -2.312, -2.408, -2.509, -2.613, -2.720, -2.829, -2.941, -3.054, -3.169, -3.285, -3.402, -3.521, -3.640, -3.760, -3.880, -4.002, -4.124, -4.246, -4.369, -4.493, -4.616, -4.741, -4.865, -4.990, -5.115, -5.240 ], + [ -1.962, -2.010, -2.074, -2.150, -2.234, -2.325, -2.421, -2.521, -2.626, -2.733, -2.842, -2.954, -3.068, -3.183, -3.299, -3.417, -3.536, -3.655, -3.776, -3.897, -4.019, -4.141, -4.264, -4.387, -4.511, -4.635, -4.760, -4.885, -5.010, -5.136, -5.261 ], + [ -1.964, -2.011, -2.073, -2.147, -2.230, -2.320, -2.415, -2.515, -2.618, -2.725, -2.834, -2.945, -3.057, -3.172, -3.288, -3.405, -3.523, -3.642, -3.762, -3.882, -4.004, -4.125, -4.248, -4.371, -4.494, -4.618, -4.742, -4.866, -4.991, -5.116, -5.241 ], + [ -1.952, -1.996, -2.056, -2.128, -2.209, -2.297, -2.391, -2.489, -2.591, -2.695, -2.803, -2.912, -3.023, -3.136, -3.251, -3.366, -3.483, -3.600, -3.719, -3.838, -3.957, -4.078, -4.198, -4.320, -4.441, -4.564, -4.686, -4.809, -4.932, -5.056, -5.179 ], + [ -1.925, -1.966, -2.024, -2.093, -2.171, -2.257, -2.348, -2.444, -2.543, -2.645, -2.750, -2.857, -2.966, -3.076, -3.188, -3.301, -3.415, -3.530, -3.646, -3.763, -3.880, -3.998, -4.116, -4.235, -4.354, -4.473, -4.593, -4.714, -4.834, -4.955, -5.076 ], + [ -1.884, -1.922, -1.976, -2.042, -2.117, -2.199, -2.287, -2.380, -2.475, -2.574, -2.676, -2.780, -2.885, -2.992, -3.101, -3.210, -3.321, -3.432, -3.545, -3.658, -3.772, -3.886, -4.001, -4.116, -4.232, -4.348, -4.464, -4.581, -4.698, -4.816, -4.933 ], + [ -1.828, -1.862, -1.913, -1.975, -2.046, -2.125, -2.209, -2.297, -2.389, -2.484, -2.581, -2.681, -2.782, -2.885, -2.989, -3.094, -3.201, -3.308, -3.416, -3.524, -3.634, -3.744, -3.854, -3.965, -4.076, -4.188, -4.300, -4.413, -4.525, -4.638, -4.751 ], + [ -1.758, -1.788, -1.835, -1.893, -1.960, -2.034, -2.113, -2.196, -2.284, -2.374, -2.466, -2.561, -2.657, -2.755, -2.854, -2.954, -3.055, -3.157, -3.260, -3.363, -3.467, -3.572, -3.677, -3.783, -3.889, -3.995, -4.102, -4.209, -4.316, -4.424, -4.532 ], + [ -1.674, -1.701, -1.743, -1.796, -1.858, -1.927, -2.001, -2.079, -2.160, -2.245, -2.332, -2.420, -2.511, -2.603, -2.696, -2.790, -2.885, -2.981, -3.078, -3.176, -3.274, -3.372, -3.471, -3.571, -3.671, -3.771, -3.871, -3.972, -4.073, -4.174, -4.276 ], + [ -1.577, -1.599, -1.637, -1.685, -1.742, -1.805, -1.873, -1.945, -2.020, -2.099, -2.179, -2.261, -2.345, -2.431, -2.517, -2.605, -2.693, -2.782, -2.872, -2.963, -3.054, -3.146, -3.238, -3.330, -3.423, -3.516, -3.610, -3.704, -3.798, -3.892, -3.986 ], + [ -1.468, -1.486, -1.518, -1.561, -1.611, -1.668, -1.730, -1.796, -1.865, -1.936, -2.009, -2.085, -2.161, -2.239, -2.319, -2.399, -2.480, -2.562, -2.644, -2.727, -2.811, -2.895, -2.979, -3.064, -3.149, -3.234, -3.320, -3.406, -3.492, -3.579, -3.666 ], + [ -1.347, -1.360, -1.387, -1.424, -1.469, -1.519, -1.574, -1.633, -1.694, -1.758, -1.824, -1.891, -1.960, -2.031, -2.102, -2.174, -2.247, -2.321, -2.395, -2.470, -2.545, -2.621, -2.697, -2.773, -2.850, -2.927, -3.004, -3.082, -3.160, -3.238, -3.316 ], + [ -1.214, -1.223, -1.245, -1.276, -1.314, -1.357, -1.405, -1.456, -1.510, -1.566, -1.624, -1.683, -1.744, -1.806, -1.869, -1.932, -1.997, -2.061, -2.127, -2.193, -2.259, -2.326, -2.393, -2.461, -2.528, -2.596, -2.665, -2.733, -2.802, -2.871, -2.940 ], + [ -1.072, -1.076, -1.092, -1.117, -1.149, -1.185, -1.225, -1.269, -1.314, -1.362, -1.411, -1.462, -1.514, -1.567, -1.621, -1.675, -1.730, -1.786, -1.842, -1.899, -1.956, -2.013, -2.071, -2.129, -2.187, -2.246, -2.304, -2.363, -2.422, -2.481, -2.541 ], + [ -0.920, -0.920, -0.931, -0.949, -0.974, -1.003, -1.036, -1.071, -1.108, -1.147, -1.188, -1.230, -1.272, -1.316, -1.360, -1.405, -1.451, -1.497, -1.543, -1.590, -1.637, -1.685, -1.733, -1.781, -1.829, -1.877, -1.926, -1.975, -2.023, -2.072, -2.122 ], + [ -0.759, -0.755, -0.761, -0.774, -0.792, -0.813, -0.838, -0.865, -0.893, -0.923, -0.955, -0.987, -1.021, -1.055, -1.089, -1.124, -1.160, -1.196, -1.232, -1.269, -1.306, -1.343, -1.381, -1.418, -1.456, -1.494, -1.532, -1.571, -1.609, -1.647, -1.686 ], + [ -0.592, -0.584, -0.585, -0.592, -0.603, -0.617, -0.633, -0.651, -0.671, -0.692, -0.714, -0.737, -0.761, -0.785, -0.810, -0.835, -0.860, -0.886, -0.912, -0.938, -0.965, -0.991, -1.018, -1.045, -1.072, -1.100, -1.127, -1.155, -1.182, -1.210, -1.238 ], + [ -0.418, -0.408, -0.404, -0.404, -0.408, -0.415, -0.423, -0.433, -0.444, -0.456, -0.469, -0.482, -0.495, -0.510, -0.524, -0.539, -0.554, -0.569, -0.585, -0.600, -0.616, -0.632, -0.648, -0.664, -0.680, -0.697, -0.713, -0.730, -0.746, -0.763, -0.780 ], + [ -0.240, -0.227, -0.218, -0.213, -0.211, -0.210, -0.210, -0.211, -0.213, -0.216, -0.219, -0.223, -0.226, -0.230, -0.235, -0.239, -0.244, -0.248, -0.253, -0.258, -0.263, -0.268, -0.273, -0.278, -0.284, -0.289, -0.294, -0.300, -0.305, -0.310, -0.316 ], + [ -0.059, -0.043, -0.031, -0.020, -0.011, -0.003, 0.005, 0.012, 0.019, 0.025, 0.032, 0.038, 0.044, 0.050, 0.056, 0.062, 0.068, 0.074, 0.080, 0.086, 0.092, 0.098, 0.103, 0.109, 0.115, 0.121, 0.127, 0.132, 0.138, 0.144, 0.150 ], + [ 0.124, 0.141, 0.158, 0.173, 0.189, 0.204, 0.220, 0.235, 0.251, 0.266, 0.282, 0.298, 0.314, 0.330, 0.346, 0.363, 0.379, 0.396, 0.412, 0.429, 0.445, 0.462, 0.479, 0.495, 0.512, 0.529, 0.546, 0.563, 0.580, 0.597, 0.614 ], + [ 0.307, 0.325, 0.345, 0.366, 0.387, 0.409, 0.432, 0.456, 0.480, 0.505, 0.530, 0.555, 0.581, 0.607, 0.633, 0.660, 0.686, 0.713, 0.740, 0.767, 0.795, 0.822, 0.849, 0.877, 0.905, 0.932, 0.960, 0.988, 1.016, 1.044, 1.072 ], + [ 0.488, 0.508, 0.530, 0.555, 0.582, 0.611, 0.642, 0.673, 0.706, 0.739, 0.773, 0.808, 0.843, 0.879, 0.915, 0.951, 0.988, 1.025, 1.062, 1.099, 1.137, 1.175, 1.213, 1.251, 1.289, 1.328, 1.366, 1.405, 1.444, 1.482, 1.521 ], + [ 0.666, 0.686, 0.711, 0.740, 0.773, 0.808, 0.845, 0.884, 0.925, 0.967, 1.009, 1.053, 1.097, 1.142, 1.188, 1.234, 1.280, 1.327, 1.375, 1.422, 1.470, 1.518, 1.566, 1.614, 1.663, 1.712, 1.761, 1.810, 1.859, 1.908, 1.957 ], + [ 0.839, 0.859, 0.886, 0.919, 0.956, 0.998, 1.042, 1.088, 1.136, 1.186, 1.237, 1.289, 1.342, 1.396, 1.451, 1.506, 1.562, 1.619, 1.675, 1.733, 1.790, 1.848, 1.906, 1.964, 2.023, 2.081, 2.140, 2.199, 2.258, 2.318, 2.377 ], + [ 1.005, 1.024, 1.053, 1.090, 1.132, 1.179, 1.229, 1.282, 1.338, 1.395, 1.454, 1.514, 1.576, 1.639, 1.702, 1.766, 1.831, 1.896, 1.962, 2.028, 2.095, 2.162, 2.230, 2.297, 2.365, 2.433, 2.502, 2.571, 2.639, 2.708, 2.777 ], + [ 1.163, 1.181, 1.211, 1.251, 1.297, 1.349, 1.406, 1.465, 1.528, 1.592, 1.659, 1.727, 1.796, 1.867, 1.938, 2.011, 2.084, 2.158, 2.232, 2.307, 2.383, 2.459, 2.535, 2.611, 2.688, 2.765, 2.843, 2.920, 2.998, 3.076, 3.154 ], + [ 1.310, 1.327, 1.359, 1.401, 1.452, 1.509, 1.570, 1.636, 1.704, 1.776, 1.849, 1.924, 2.001, 2.079, 2.158, 2.239, 2.320, 2.401, 2.484, 2.567, 2.650, 2.734, 2.819, 2.904, 2.989, 3.074, 3.160, 3.246, 3.332, 3.419, 3.505 ], + [ 1.445, 1.461, 1.494, 1.539, 1.593, 1.654, 1.721, 1.792, 1.867, 1.944, 2.024, 2.106, 2.189, 2.274, 2.360, 2.448, 2.536, 2.625, 2.715, 2.805, 2.896, 2.987, 3.079, 3.172, 3.265, 3.358, 3.451, 3.545, 3.639, 3.733, 3.827 ], + [ 1.567, 1.582, 1.615, 1.663, 1.720, 1.786, 1.857, 1.933, 2.013, 2.096, 2.181, 2.269, 2.359, 2.450, 2.542, 2.636, 2.731, 2.826, 2.923, 3.020, 3.117, 3.216, 3.314, 3.414, 3.513, 3.613, 3.714, 3.814, 3.915, 4.016, 4.118 ], + [ 1.673, 1.687, 1.722, 1.772, 1.832, 1.901, 1.976, 2.057, 2.141, 2.229, 2.320, 2.413, 2.508, 2.605, 2.703, 2.802, 2.903, 3.004, 3.106, 3.209, 3.313, 3.417, 3.522, 3.627, 3.733, 3.839, 3.946, 4.052, 4.160, 4.267, 4.374 ], + [ 1.763, 1.777, 1.813, 1.864, 1.928, 2.000, 2.078, 2.163, 2.252, 2.344, 2.439, 2.537, 2.636, 2.738, 2.841, 2.945, 3.050, 3.157, 3.264, 3.372, 3.481, 3.590, 3.700, 3.811, 3.922, 4.033, 4.145, 4.257, 4.369, 4.482, 4.595 ], + [ 1.836, 1.849, 1.886, 1.940, 2.005, 2.080, 2.162, 2.250, 2.342, 2.438, 2.537, 2.639, 2.742, 2.848, 2.955, 3.063, 3.173, 3.283, 3.395, 3.507, 3.620, 3.734, 3.848, 3.963, 4.078, 4.194, 4.310, 4.427, 4.543, 4.661, 4.778 ], + [ 1.890, 1.904, 1.942, 1.997, 2.065, 2.143, 2.227, 2.318, 2.413, 2.512, 2.614, 2.718, 2.825, 2.934, 3.044, 3.155, 3.268, 3.382, 3.497, 3.613, 3.729, 3.846, 3.964, 4.082, 4.201, 4.320, 4.440, 4.560, 4.680, 4.801, 4.922 ], + [ 1.925, 1.940, 1.979, 2.037, 2.106, 2.186, 2.273, 2.365, 2.463, 2.564, 2.668, 2.775, 2.884, 2.995, 3.108, 3.222, 3.337, 3.453, 3.571, 3.689, 3.808, 3.927, 4.047, 4.168, 4.289, 4.411, 4.533, 4.656, 4.778, 4.901, 5.025 ], + [ 1.940, 1.956, 1.998, 2.057, 2.129, 2.210, 2.298, 2.392, 2.491, 2.594, 2.700, 2.808, 2.919, 3.031, 3.145, 3.261, 3.378, 3.495, 3.614, 3.734, 3.854, 3.976, 4.097, 4.220, 4.342, 4.465, 4.589, 4.713, 4.837, 4.962, 5.087 ], + [ 1.933, 1.953, 1.998, 2.059, 2.132, 2.214, 2.304, 2.399, 2.498, 2.602, 2.708, 2.817, 2.929, 3.042, 3.157, 3.273, 3.390, 3.509, 3.628, 3.748, 3.869, 3.991, 4.113, 4.236, 4.359, 4.483, 4.607, 4.732, 4.857, 4.982, 5.107 ] + ], + "type":"carpet", + "aaxis":{ + "startlinewidth":2, + "startline":true, + "showticklabels":"none", + "endline":true, + "showgrid":false, + "endlinewidth":2, + "smoothing":0 + } + }, + { + "autocolorscale":false, + "zmax":1, + "name":"Pressure", + "colorscale":"Viridis", + "zmin":-8, + "colorbar":{ + "y":0, + "yanchor":"bottom", + "titleside":"right", + "len":0.75, + "title":"Pressure coefficient, cp" + }, + "contours":{ + "start":-1, + "size":0.025, + "end":1.000, + "showlines":false + }, + "line":{ + "smoothing":0 + }, + "z":[ + [ 0.361, 0.300, 0.246, 0.209, 0.182, 0.162, 0.145, 0.132, 0.121, 0.111, 0.103, 0.096, 0.090, 0.085, 0.080, 0.075, 0.072, 0.068, 0.065, 0.062, 0.059, 0.057, 0.055, 0.053, 0.051, 0.049, 0.047, 0.046, 0.044, 0.043, 0.042 ], + [ 0.261, 0.234, 0.199, 0.170, 0.147, 0.129, 0.115, 0.103, 0.093, 0.085, 0.078, 0.072, 0.066, 0.062, 0.058, 0.054, 0.051, 0.048, 0.045, 0.043, 0.041, 0.039, 0.037, 0.036, 0.034, 0.033, 0.031, 0.030, 0.029, 0.028, 0.027 ], + [ 0.180, 0.165, 0.143, 0.123, 0.105, 0.091, 0.080, 0.070, 0.062, 0.055, 0.050, 0.045, 0.041, 0.037, 0.034, 0.031, 0.029, 0.026, 0.024, 0.023, 0.021, 0.020, 0.018, 0.017, 0.016, 0.015, 0.014, 0.014, 0.013, 0.012, 0.012 ], + [ 0.102, 0.095, 0.084, 0.071, 0.059, 0.049, 0.041, 0.034, 0.028, 0.023, 0.019, 0.016, 0.013, 0.010, 0.008, 0.006, 0.005, 0.004, 0.002, 0.001, 0.000, -0.000, -0.001, -0.002, -0.002, -0.003, -0.003, -0.004, -0.004, -0.004, -0.005 ], + [ 0.024, 0.025, 0.021, 0.015, 0.009, 0.003, -0.001, -0.005, -0.008, -0.011, -0.013, -0.015, -0.017, -0.018, -0.019, -0.020, -0.020, -0.021, -0.021, -0.021, -0.021, -0.022, -0.022, -0.022, -0.022, -0.022, -0.022, -0.021, -0.021, -0.021, -0.021 ], + [ -0.055, -0.047, -0.044, -0.043, -0.044, -0.045, -0.046, -0.047, -0.047, -0.048, -0.048, -0.048, -0.048, -0.048, -0.047, -0.047, -0.046, -0.046, -0.045, -0.045, -0.044, -0.043, -0.043, -0.042, -0.042, -0.041, -0.040, -0.040, -0.039, -0.039, -0.038 ], + [ -0.136, -0.121, -0.111, -0.104, -0.099, -0.096, -0.093, -0.091, -0.088, -0.086, -0.084, -0.082, -0.080, -0.079, -0.077, -0.075, -0.073, -0.072, -0.070, -0.069, -0.067, -0.066, -0.065, -0.063, -0.062, -0.061, -0.060, -0.059, -0.058, -0.057, -0.056 ], + [ -0.220, -0.197, -0.180, -0.167, -0.157, -0.149, -0.142, -0.136, -0.131, -0.126, -0.122, -0.118, -0.114, -0.111, -0.107, -0.104, -0.101, -0.099, -0.096, -0.094, -0.091, -0.089, -0.087, -0.085, -0.083, -0.081, -0.079, -0.078, -0.076, -0.075, -0.073 ], + [ -0.307, -0.275, -0.250, -0.231, -0.216, -0.203, -0.192, -0.183, -0.174, -0.167, -0.160, -0.154, -0.149, -0.143, -0.139, -0.134, -0.130, -0.126, -0.122, -0.119, -0.116, -0.113, -0.110, -0.107, -0.104, -0.102, -0.099, -0.097, -0.095, -0.093, -0.091 ], + [ -0.396, -0.355, -0.323, -0.297, -0.276, -0.259, -0.244, -0.230, -0.219, -0.209, -0.200, -0.191, -0.184, -0.177, -0.170, -0.164, -0.159, -0.154, -0.149, -0.144, -0.140, -0.136, -0.132, -0.129, -0.125, -0.122, -0.119, -0.116, -0.114, -0.111, -0.108 ], + [ -0.488, -0.437, -0.397, -0.364, -0.338, -0.315, -0.296, -0.279, -0.264, -0.251, -0.239, -0.229, -0.219, -0.210, -0.202, -0.195, -0.188, -0.181, -0.175, -0.170, -0.164, -0.160, -0.155, -0.151, -0.146, -0.143, -0.139, -0.135, -0.132, -0.129, -0.126 ], + [ -0.581, -0.520, -0.472, -0.432, -0.400, -0.372, -0.348, -0.328, -0.310, -0.294, -0.279, -0.266, -0.254, -0.244, -0.234, -0.225, -0.217, -0.209, -0.202, -0.195, -0.189, -0.183, -0.177, -0.172, -0.167, -0.163, -0.158, -0.154, -0.150, -0.146, -0.143 ], + [ -0.677, -0.605, -0.548, -0.501, -0.462, -0.429, -0.401, -0.377, -0.355, -0.336, -0.319, -0.304, -0.290, -0.277, -0.265, -0.255, -0.245, -0.236, -0.227, -0.220, -0.212, -0.205, -0.199, -0.193, -0.187, -0.182, -0.177, -0.172, -0.168, -0.163, -0.159 ], + [ -0.773, -0.690, -0.624, -0.570, -0.525, -0.487, -0.454, -0.425, -0.400, -0.378, -0.358, -0.340, -0.324, -0.309, -0.296, -0.284, -0.273, -0.262, -0.252, -0.244, -0.235, -0.227, -0.220, -0.213, -0.207, -0.201, -0.195, -0.190, -0.185, -0.180, -0.175 ], + [ -0.870, -0.776, -0.701, -0.639, -0.587, -0.544, -0.506, -0.473, -0.445, -0.419, -0.397, -0.376, -0.358, -0.341, -0.326, -0.312, -0.299, -0.288, -0.277, -0.267, -0.257, -0.248, -0.240, -0.233, -0.225, -0.219, -0.212, -0.206, -0.201, -0.195, -0.190 ], + [ -0.968, -0.862, -0.777, -0.708, -0.649, -0.600, -0.557, -0.520, -0.488, -0.459, -0.434, -0.411, -0.390, -0.372, -0.355, -0.339, -0.325, -0.312, -0.299, -0.288, -0.278, -0.268, -0.259, -0.251, -0.243, -0.235, -0.228, -0.222, -0.215, -0.209, -0.204 ], + [ -1.066, -0.948, -0.853, -0.775, -0.710, -0.655, -0.607, -0.566, -0.530, -0.498, -0.469, -0.444, -0.421, -0.400, -0.381, -0.364, -0.349, -0.334, -0.321, -0.308, -0.297, -0.286, -0.276, -0.267, -0.259, -0.250, -0.243, -0.236, -0.229, -0.222, -0.216 ], + [ -1.164, -1.034, -0.929, -0.842, -0.770, -0.708, -0.656, -0.610, -0.570, -0.535, -0.503, -0.475, -0.450, -0.427, -0.407, -0.388, -0.371, -0.355, -0.340, -0.327, -0.314, -0.303, -0.292, -0.282, -0.273, -0.264, -0.256, -0.248, -0.241, -0.234, -0.227 ], + [ -1.262, -1.119, -1.003, -0.908, -0.828, -0.760, -0.702, -0.652, -0.608, -0.569, -0.535, -0.504, -0.476, -0.452, -0.429, -0.409, -0.390, -0.373, -0.358, -0.343, -0.330, -0.317, -0.306, -0.295, -0.285, -0.276, -0.267, -0.259, -0.251, -0.243, -0.237 ], + [ -1.360, -1.203, -1.076, -0.971, -0.884, -0.810, -0.746, -0.691, -0.643, -0.601, -0.563, -0.530, -0.500, -0.473, -0.449, -0.427, -0.407, -0.389, -0.372, -0.357, -0.343, -0.329, -0.317, -0.306, -0.295, -0.285, -0.276, -0.267, -0.259, -0.251, -0.244 ], + [ -1.457, -1.286, -1.148, -1.033, -0.937, -0.856, -0.787, -0.727, -0.675, -0.629, -0.589, -0.553, -0.521, -0.492, -0.466, -0.443, -0.422, -0.402, -0.384, -0.368, -0.353, -0.339, -0.326, -0.314, -0.303, -0.292, -0.283, -0.274, -0.265, -0.257, -0.249 ], + [ -1.555, -1.369, -1.218, -1.093, -0.988, -0.900, -0.824, -0.759, -0.703, -0.654, -0.610, -0.572, -0.538, -0.507, -0.480, -0.455, -0.432, -0.412, -0.393, -0.376, -0.360, -0.346, -0.332, -0.320, -0.308, -0.297, -0.287, -0.278, -0.269, -0.260, -0.252 ], + [ -1.654, -1.452, -1.287, -1.150, -1.036, -0.939, -0.858, -0.787, -0.727, -0.674, -0.627, -0.587, -0.550, -0.518, -0.489, -0.463, -0.439, -0.418, -0.398, -0.381, -0.364, -0.349, -0.335, -0.322, -0.310, -0.299, -0.289, -0.279, -0.270, -0.261, -0.253 ], + [ -1.755, -1.535, -1.354, -1.204, -1.080, -0.975, -0.886, -0.810, -0.745, -0.689, -0.639, -0.596, -0.558, -0.524, -0.494, -0.467, -0.442, -0.420, -0.400, -0.381, -0.365, -0.349, -0.335, -0.322, -0.309, -0.298, -0.287, -0.277, -0.268, -0.259, -0.251 ], + [ -1.861, -1.619, -1.420, -1.256, -1.119, -1.005, -0.909, -0.827, -0.758, -0.698, -0.646, -0.600, -0.560, -0.525, -0.494, -0.466, -0.440, -0.418, -0.397, -0.378, -0.361, -0.345, -0.331, -0.317, -0.305, -0.294, -0.283, -0.273, -0.264, -0.255, -0.247 ], + [ -1.973, -1.706, -1.485, -1.303, -1.153, -1.029, -0.925, -0.837, -0.763, -0.700, -0.645, -0.598, -0.557, -0.520, -0.488, -0.459, -0.434, -0.410, -0.390, -0.371, -0.353, -0.337, -0.323, -0.310, -0.297, -0.286, -0.275, -0.265, -0.256, -0.248, -0.240 ], + [ -2.095, -1.797, -1.549, -1.346, -1.180, -1.044, -0.932, -0.839, -0.760, -0.694, -0.637, -0.588, -0.546, -0.509, -0.476, -0.447, -0.421, -0.398, -0.377, -0.358, -0.341, -0.326, -0.311, -0.298, -0.286, -0.275, -0.264, -0.255, -0.246, -0.237, -0.230 ], + [ -2.234, -1.893, -1.612, -1.383, -1.199, -1.050, -0.929, -0.830, -0.748, -0.679, -0.621, -0.571, -0.528, -0.490, -0.458, -0.429, -0.403, -0.380, -0.360, -0.341, -0.325, -0.309, -0.296, -0.283, -0.271, -0.260, -0.250, -0.241, -0.232, -0.224, -0.217 ], + [ -2.397, -1.999, -1.672, -1.411, -1.206, -1.044, -0.914, -0.809, -0.724, -0.653, -0.594, -0.544, -0.501, -0.464, -0.432, -0.404, -0.379, -0.357, -0.337, -0.319, -0.303, -0.289, -0.276, -0.264, -0.252, -0.242, -0.233, -0.224, -0.216, -0.208, -0.201 ], + [ -2.597, -2.116, -1.727, -1.427, -1.197, -1.020, -0.883, -0.774, -0.687, -0.616, -0.557, -0.508, -0.466, -0.430, -0.399, -0.372, -0.349, -0.328, -0.309, -0.293, -0.278, -0.264, -0.252, -0.241, -0.230, -0.221, -0.212, -0.204, -0.197, -0.190, -0.183 ], + [ -2.854, -2.246, -1.772, -1.422, -1.166, -0.976, -0.832, -0.722, -0.635, -0.565, -0.508, -0.461, -0.422, -0.388, -0.359, -0.334, -0.313, -0.293, -0.276, -0.261, -0.248, -0.235, -0.224, -0.214, -0.205, -0.196, -0.189, -0.181, -0.175, -0.168, -0.163 ], + [ -3.199, -2.387, -1.795, -1.386, -1.104, -0.904, -0.759, -0.650, -0.566, -0.500, -0.447, -0.404, -0.368, -0.338, -0.312, -0.290, -0.271, -0.254, -0.239, -0.226, -0.214, -0.203, -0.193, -0.185, -0.177, -0.169, -0.162, -0.156, -0.150, -0.145, -0.140 ], + [ -3.683, -2.526, -1.773, -1.303, -1.002, -0.800, -0.660, -0.558, -0.481, -0.422, -0.375, -0.338, -0.307, -0.281, -0.259, -0.240, -0.224, -0.210, -0.197, -0.186, -0.176, -0.168, -0.160, -0.152, -0.146, -0.140, -0.134, -0.129, -0.124, -0.120, -0.116 ], + [ -4.381, -2.615, -1.666, -1.151, -0.849, -0.660, -0.534, -0.445, -0.380, -0.331, -0.293, -0.263, -0.238, -0.218, -0.201, -0.186, -0.173, -0.162, -0.153, -0.144, -0.137, -0.130, -0.124, -0.118, -0.113, -0.108, -0.104, -0.100, -0.097, -0.093, -0.090 ], + [ -5.328, -2.533, -1.422, -0.912, -0.643, -0.484, -0.384, -0.316, -0.267, -0.231, -0.204, -0.182, -0.165, -0.151, -0.139, -0.129, -0.120, -0.113, -0.106, -0.100, -0.095, -0.091, -0.086, -0.083, -0.079, -0.076, -0.073, -0.071, -0.068, -0.066, -0.064 ], + [ -6.040, -2.071, -1.002, -0.589, -0.391, -0.283, -0.218, -0.176, -0.147, -0.126, -0.111, -0.099, -0.089, -0.082, -0.076, -0.070, -0.066, -0.062, -0.059, -0.056, -0.053, -0.051, -0.049, -0.047, -0.045, -0.043, -0.042, -0.040, -0.039, -0.038, -0.037 ], + [ -4.472, -1.137, -0.449, -0.218, -0.121, -0.074, -0.049, -0.035, -0.027, -0.022, -0.018, -0.016, -0.015, -0.014, -0.013, -0.013, -0.012, -0.012, -0.012, -0.012, -0.012, -0.011, -0.011, -0.011, -0.011, -0.011, -0.011, -0.011, -0.011, -0.010, -0.010 ], + [ -1.177, -0.104, 0.094, 0.132, 0.131, 0.121, 0.108, 0.096, 0.086, 0.076, 0.069, 0.062, 0.056, 0.051, 0.046, 0.042, 0.039, 0.036, 0.033, 0.031, 0.028, 0.027, 0.025, 0.023, 0.022, 0.020, 0.019, 0.018, 0.017, 0.016, 0.015 ], + [ 0.491, 0.570, 0.488, 0.403, 0.335, 0.282, 0.242, 0.210, 0.184, 0.163, 0.146, 0.132, 0.120, 0.109, 0.100, 0.093, 0.086, 0.080, 0.075, 0.070, 0.066, 0.062, 0.059, 0.056, 0.053, 0.050, 0.048, 0.046, 0.044, 0.042, 0.040 ], + [ 0.948, 0.855, 0.702, 0.574, 0.476, 0.401, 0.344, 0.300, 0.264, 0.236, 0.212, 0.192, 0.175, 0.161, 0.148, 0.138, 0.128, 0.120, 0.113, 0.106, 0.100, 0.095, 0.090, 0.086, 0.082, 0.078, 0.074, 0.071, 0.068, 0.066, 0.063 ], + [ 0.995, 0.916, 0.781, 0.657, 0.557, 0.478, 0.415, 0.366, 0.325, 0.292, 0.264, 0.241, 0.221, 0.204, 0.189, 0.176, 0.165, 0.155, 0.146, 0.138, 0.130, 0.124, 0.118, 0.112, 0.107, 0.103, 0.099, 0.095, 0.091, 0.088, 0.085 ], + [ 0.926, 0.881, 0.783, 0.681, 0.593, 0.519, 0.458, 0.409, 0.367, 0.333, 0.304, 0.279, 0.257, 0.239, 0.222, 0.208, 0.195, 0.184, 0.174, 0.165, 0.157, 0.149, 0.142, 0.136, 0.130, 0.125, 0.120, 0.116, 0.111, 0.107, 0.104 ], + [ 0.832, 0.812, 0.746, 0.670, 0.597, 0.534, 0.479, 0.433, 0.393, 0.360, 0.331, 0.306, 0.284, 0.265, 0.248, 0.233, 0.220, 0.208, 0.197, 0.188, 0.179, 0.171, 0.163, 0.156, 0.150, 0.144, 0.139, 0.134, 0.129, 0.125, 0.121 ], + [ 0.741, 0.737, 0.695, 0.639, 0.583, 0.530, 0.484, 0.443, 0.407, 0.376, 0.348, 0.324, 0.303, 0.284, 0.268, 0.253, 0.239, 0.227, 0.216, 0.206, 0.197, 0.188, 0.180, 0.173, 0.167, 0.161, 0.155, 0.149, 0.144, 0.140, 0.135 ], + [ 0.661, 0.667, 0.641, 0.602, 0.559, 0.517, 0.478, 0.442, 0.411, 0.383, 0.357, 0.335, 0.315, 0.297, 0.281, 0.266, 0.253, 0.241, 0.230, 0.220, 0.211, 0.202, 0.194, 0.187, 0.180, 0.174, 0.168, 0.162, 0.157, 0.152, 0.148 ], + [ 0.595, 0.605, 0.590, 0.563, 0.530, 0.497, 0.465, 0.435, 0.408, 0.383, 0.360, 0.340, 0.322, 0.305, 0.290, 0.276, 0.263, 0.251, 0.241, 0.231, 0.222, 0.213, 0.205, 0.198, 0.191, 0.185, 0.179, 0.173, 0.168, 0.163, 0.158 ], + [ 0.540, 0.552, 0.544, 0.525, 0.501, 0.475, 0.449, 0.424, 0.400, 0.379, 0.359, 0.340, 0.324, 0.308, 0.294, 0.281, 0.269, 0.258, 0.248, 0.238, 0.229, 0.221, 0.213, 0.206, 0.199, 0.193, 0.187, 0.181, 0.176, 0.171, 0.166 ], + [ 0.495, 0.508, 0.505, 0.491, 0.473, 0.452, 0.431, 0.410, 0.390, 0.372, 0.354, 0.338, 0.322, 0.308, 0.295, 0.283, 0.272, 0.261, 0.252, 0.243, 0.234, 0.226, 0.219, 0.212, 0.205, 0.199, 0.193, 0.188, 0.182, 0.177, 0.173 ], + [ 0.460, 0.472, 0.471, 0.461, 0.447, 0.431, 0.413, 0.396, 0.379, 0.363, 0.347, 0.333, 0.319, 0.306, 0.294, 0.283, 0.273, 0.263, 0.254, 0.245, 0.237, 0.229, 0.222, 0.215, 0.209, 0.203, 0.197, 0.192, 0.187, 0.182, 0.177 ], + [ 0.432, 0.442, 0.442, 0.435, 0.424, 0.411, 0.396, 0.382, 0.367, 0.353, 0.339, 0.326, 0.314, 0.302, 0.291, 0.281, 0.271, 0.262, 0.254, 0.245, 0.238, 0.231, 0.224, 0.217, 0.211, 0.205, 0.200, 0.195, 0.190, 0.185, 0.180 ], + [ 0.410, 0.419, 0.419, 0.413, 0.404, 0.393, 0.380, 0.368, 0.355, 0.342, 0.330, 0.319, 0.308, 0.297, 0.287, 0.277, 0.269, 0.260, 0.252, 0.244, 0.237, 0.230, 0.224, 0.218, 0.212, 0.206, 0.201, 0.196, 0.191, 0.186, 0.182 ], + [ 0.393, 0.400, 0.399, 0.394, 0.386, 0.377, 0.366, 0.355, 0.343, 0.332, 0.321, 0.311, 0.301, 0.291, 0.282, 0.273, 0.265, 0.257, 0.249, 0.242, 0.235, 0.229, 0.223, 0.217, 0.211, 0.206, 0.201, 0.196, 0.191, 0.187, 0.183 ], + [ 0.381, 0.385, 0.383, 0.378, 0.371, 0.362, 0.353, 0.343, 0.332, 0.322, 0.312, 0.303, 0.293, 0.284, 0.276, 0.268, 0.260, 0.253, 0.245, 0.239, 0.232, 0.226, 0.220, 0.215, 0.209, 0.204, 0.199, 0.195, 0.190, 0.186, 0.182 ], + [ 0.371, 0.373, 0.370, 0.365, 0.358, 0.350, 0.341, 0.331, 0.322, 0.313, 0.303, 0.295, 0.286, 0.278, 0.270, 0.262, 0.255, 0.248, 0.241, 0.235, 0.229, 0.223, 0.217, 0.212, 0.207, 0.202, 0.197, 0.193, 0.188, 0.184, 0.180 ], + [ 0.365, 0.364, 0.360, 0.354, 0.347, 0.339, 0.330, 0.321, 0.312, 0.303, 0.295, 0.286, 0.278, 0.271, 0.263, 0.256, 0.249, 0.242, 0.236, 0.230, 0.224, 0.218, 0.213, 0.208, 0.203, 0.199, 0.194, 0.190, 0.186, 0.182, 0.178 ], + [ 0.360, 0.357, 0.352, 0.345, 0.337, 0.329, 0.320, 0.312, 0.303, 0.295, 0.286, 0.278, 0.271, 0.263, 0.256, 0.249, 0.243, 0.236, 0.230, 0.224, 0.219, 0.214, 0.208, 0.204, 0.199, 0.194, 0.190, 0.186, 0.182, 0.178, 0.175 ], + [ 0.357, 0.352, 0.345, 0.337, 0.329, 0.320, 0.312, 0.303, 0.295, 0.286, 0.278, 0.271, 0.263, 0.256, 0.249, 0.242, 0.236, 0.230, 0.224, 0.219, 0.213, 0.208, 0.203, 0.199, 0.194, 0.190, 0.186, 0.182, 0.178, 0.174, 0.171 ], + [ 0.355, 0.347, 0.339, 0.330, 0.321, 0.312, 0.303, 0.295, 0.286, 0.278, 0.270, 0.263, 0.255, 0.248, 0.242, 0.235, 0.229, 0.223, 0.217, 0.212, 0.207, 0.202, 0.197, 0.193, 0.188, 0.184, 0.180, 0.176, 0.173, 0.169, 0.166 ], + [ 0.354, 0.344, 0.334, 0.324, 0.315, 0.305, 0.296, 0.287, 0.278, 0.270, 0.262, 0.255, 0.247, 0.240, 0.234, 0.228, 0.222, 0.216, 0.210, 0.205, 0.200, 0.195, 0.191, 0.186, 0.182, 0.178, 0.174, 0.171, 0.167, 0.164, 0.160 ], + [ 0.353, 0.341, 0.330, 0.319, 0.308, 0.298, 0.288, 0.279, 0.270, 0.262, 0.254, 0.246, 0.239, 0.232, 0.226, 0.220, 0.214, 0.208, 0.203, 0.198, 0.193, 0.188, 0.184, 0.180, 0.176, 0.172, 0.168, 0.164, 0.161, 0.158, 0.154 ], + [ 0.352, 0.339, 0.326, 0.314, 0.302, 0.291, 0.281, 0.271, 0.262, 0.254, 0.246, 0.238, 0.231, 0.224, 0.217, 0.211, 0.205, 0.200, 0.195, 0.190, 0.185, 0.180, 0.176, 0.172, 0.168, 0.164, 0.161, 0.157, 0.154, 0.151, 0.148 ], + [ 0.352, 0.336, 0.322, 0.309, 0.296, 0.285, 0.274, 0.263, 0.254, 0.245, 0.237, 0.229, 0.222, 0.215, 0.208, 0.202, 0.197, 0.191, 0.186, 0.181, 0.176, 0.172, 0.168, 0.164, 0.160, 0.156, 0.153, 0.150, 0.146, 0.143, 0.140 ], + [ 0.351, 0.334, 0.319, 0.304, 0.290, 0.278, 0.266, 0.255, 0.245, 0.236, 0.227, 0.220, 0.212, 0.205, 0.199, 0.193, 0.187, 0.182, 0.177, 0.172, 0.167, 0.163, 0.159, 0.155, 0.151, 0.148, 0.144, 0.141, 0.138, 0.135, 0.132 ], + [ 0.350, 0.332, 0.315, 0.299, 0.284, 0.270, 0.258, 0.246, 0.236, 0.226, 0.217, 0.209, 0.202, 0.195, 0.188, 0.182, 0.177, 0.171, 0.166, 0.162, 0.157, 0.153, 0.149, 0.145, 0.142, 0.138, 0.135, 0.132, 0.129, 0.126, 0.124 ], + [ 0.350, 0.331, 0.312, 0.294, 0.277, 0.262, 0.248, 0.236, 0.225, 0.215, 0.206, 0.198, 0.190, 0.183, 0.177, 0.171, 0.165, 0.160, 0.155, 0.151, 0.146, 0.142, 0.139, 0.135, 0.132, 0.128, 0.125, 0.122, 0.119, 0.117, 0.114 ], + [ 0.351, 0.330, 0.308, 0.288, 0.269, 0.253, 0.238, 0.225, 0.213, 0.203, 0.194, 0.185, 0.178, 0.171, 0.164, 0.159, 0.153, 0.148, 0.143, 0.139, 0.135, 0.131, 0.127, 0.124, 0.120, 0.117, 0.114, 0.112, 0.109, 0.106, 0.104 ], + [ 0.354, 0.331, 0.305, 0.281, 0.260, 0.241, 0.226, 0.212, 0.200, 0.189, 0.180, 0.171, 0.164, 0.157, 0.151, 0.145, 0.140, 0.135, 0.130, 0.126, 0.122, 0.118, 0.115, 0.111, 0.108, 0.106, 0.103, 0.100, 0.098, 0.095, 0.093 ], + [ 0.364, 0.335, 0.302, 0.272, 0.248, 0.228, 0.211, 0.197, 0.184, 0.174, 0.164, 0.156, 0.148, 0.142, 0.135, 0.130, 0.125, 0.120, 0.116, 0.112, 0.108, 0.105, 0.101, 0.098, 0.095, 0.093, 0.090, 0.088, 0.086, 0.084, 0.081 ], + [ 0.388, 0.343, 0.295, 0.259, 0.232, 0.211, 0.193, 0.179, 0.166, 0.156, 0.146, 0.138, 0.131, 0.124, 0.119, 0.113, 0.109, 0.104, 0.100, 0.096, 0.093, 0.090, 0.087, 0.084, 0.082, 0.079, 0.077, 0.075, 0.073, 0.071, 0.069 ], + [ 0.493, 0.342, 0.279, 0.239, 0.211, 0.189, 0.172, 0.157, 0.145, 0.135, 0.126, 0.118, 0.111, 0.105, 0.100, 0.095, 0.091, 0.087, 0.083, 0.080, 0.077, 0.074, 0.071, 0.069, 0.067, 0.065, 0.063, 0.061, 0.059, 0.057, 0.056 ], + [ 0.361, 0.300, 0.246, 0.209, 0.182, 0.162, 0.145, 0.132, 0.121, 0.111, 0.103, 0.096, 0.090, 0.085, 0.080, 0.075, 0.072, 0.068, 0.065, 0.062, 0.059, 0.057, 0.055, 0.053, 0.051, 0.049, 0.047, 0.046, 0.044, 0.043, 0.042 ] + ], + "type":"contourcarpet", + "autocontour":false, + "zauto":false + }, + { + "opacity":0.300, + "showlegend":true, + "name":"Streamlines", + "autocontour":true, + "ncontours":50, + "contours":{ + "coloring":"none" + }, + "line":{ + "color":"white", + "width":1 + }, + "z":[ + [ 0.042, 0.060, 0.076, 0.089, 0.100, 0.110, 0.117, 0.124, 0.128, 0.132, 0.134, 0.136, 0.136, 0.135, 0.134, 0.131, 0.128, 0.125, 0.120, 0.115, 0.110, 0.104, 0.097, 0.090, 0.083, 0.075, 0.066, 0.058, 0.049, 0.039, 0.030 ], + [ 0.042, 0.082, 0.118, 0.149, 0.178, 0.204, 0.228, 0.249, 0.269, 0.287, 0.303, 0.318, 0.332, 0.345, 0.357, 0.368, 0.378, 0.387, 0.395, 0.403, 0.410, 0.417, 0.423, 0.428, 0.433, 0.438, 0.442, 0.445, 0.448, 0.451, 0.454 ], + [ 0.042, 0.104, 0.160, 0.210, 0.256, 0.299, 0.339, 0.375, 0.410, 0.443, 0.473, 0.502, 0.530, 0.556, 0.582, 0.606, 0.629, 0.651, 0.672, 0.693, 0.713, 0.732, 0.751, 0.769, 0.786, 0.803, 0.820, 0.836, 0.851, 0.866, 0.881 ], + [ 0.042, 0.126, 0.202, 0.271, 0.335, 0.394, 0.449, 0.502, 0.551, 0.598, 0.643, 0.686, 0.728, 0.768, 0.806, 0.844, 0.880, 0.915, 0.949, 0.983, 1.016, 1.047, 1.079, 1.109, 1.139, 1.168, 1.197, 1.226, 1.254, 1.281, 1.308 ], + [ 0.042, 0.148, 0.244, 0.332, 0.413, 0.488, 0.559, 0.627, 0.691, 0.753, 0.812, 0.869, 0.924, 0.977, 1.029, 1.080, 1.129, 1.177, 1.224, 1.270, 1.316, 1.360, 1.404, 1.447, 1.489, 1.531, 1.572, 1.613, 1.653, 1.692, 1.732 ], + [ 0.042, 0.170, 0.285, 0.391, 0.489, 0.581, 0.668, 0.750, 0.829, 0.905, 0.978, 1.048, 1.117, 1.183, 1.248, 1.312, 1.374, 1.435, 1.494, 1.553, 1.611, 1.667, 1.723, 1.779, 1.833, 1.887, 1.940, 1.993, 2.045, 2.097, 2.148 ], + [ 0.042, 0.191, 0.325, 0.449, 0.563, 0.671, 0.773, 0.870, 0.963, 1.053, 1.139, 1.223, 1.305, 1.384, 1.462, 1.538, 1.613, 1.686, 1.758, 1.829, 1.898, 1.967, 2.035, 2.102, 2.169, 2.234, 2.299, 2.364, 2.428, 2.491, 2.554 ], + [ 0.042, 0.211, 0.364, 0.505, 0.635, 0.758, 0.875, 0.986, 1.093, 1.196, 1.296, 1.392, 1.487, 1.579, 1.669, 1.757, 1.843, 1.928, 2.012, 2.095, 2.176, 2.257, 2.336, 2.415, 2.493, 2.570, 2.647, 2.722, 2.798, 2.872, 2.946 ], + [ 0.042, 0.231, 0.401, 0.558, 0.704, 0.842, 0.973, 1.097, 1.217, 1.333, 1.445, 1.554, 1.661, 1.764, 1.866, 1.966, 2.064, 2.161, 2.256, 2.350, 2.442, 2.534, 2.625, 2.714, 2.803, 2.891, 2.979, 3.065, 3.152, 3.237, 3.322 ], + [ 0.042, 0.249, 0.437, 0.609, 0.770, 0.921, 1.065, 1.203, 1.335, 1.463, 1.587, 1.708, 1.825, 1.940, 2.053, 2.164, 2.273, 2.381, 2.487, 2.591, 2.695, 2.797, 2.898, 2.998, 3.097, 3.196, 3.293, 3.390, 3.487, 3.582, 3.678 ], + [ 0.042, 0.267, 0.470, 0.656, 0.831, 0.995, 1.152, 1.301, 1.445, 1.584, 1.719, 1.851, 1.980, 2.105, 2.229, 2.350, 2.469, 2.587, 2.703, 2.817, 2.930, 3.043, 3.154, 3.263, 3.373, 3.481, 3.588, 3.695, 3.801, 3.906, 4.010 ], + [ 0.042, 0.283, 0.500, 0.700, 0.887, 1.064, 1.231, 1.392, 1.547, 1.697, 1.842, 1.984, 2.122, 2.258, 2.391, 2.521, 2.650, 2.777, 2.902, 3.026, 3.148, 3.270, 3.390, 3.509, 3.627, 3.744, 3.860, 3.976, 4.090, 4.205, 4.318 ], + [ 0.042, 0.297, 0.528, 0.740, 0.939, 1.126, 1.304, 1.475, 1.639, 1.799, 1.953, 2.104, 2.252, 2.396, 2.538, 2.677, 2.815, 2.950, 3.084, 3.216, 3.347, 3.476, 3.604, 3.732, 3.858, 3.983, 4.108, 4.231, 4.354, 4.476, 4.598 ], + [ 0.042, 0.310, 0.552, 0.776, 0.984, 1.181, 1.369, 1.549, 1.722, 1.890, 2.053, 2.212, 2.367, 2.519, 2.669, 2.816, 2.961, 3.104, 3.246, 3.385, 3.524, 3.660, 3.796, 3.931, 4.064, 4.197, 4.328, 4.459, 4.589, 4.719, 4.847 ], + [ 0.042, 0.321, 0.574, 0.807, 1.024, 1.230, 1.425, 1.613, 1.794, 1.969, 2.139, 2.305, 2.468, 2.627, 2.784, 2.937, 3.089, 3.239, 3.387, 3.533, 3.678, 3.821, 3.963, 4.104, 4.244, 4.383, 4.521, 4.658, 4.794, 4.930, 5.065 ], + [ 0.042, 0.331, 0.592, 0.833, 1.058, 1.271, 1.473, 1.667, 1.855, 2.036, 2.212, 2.385, 2.553, 2.718, 2.880, 3.040, 3.197, 3.352, 3.506, 3.658, 3.808, 3.957, 4.104, 4.250, 4.396, 4.540, 4.683, 4.826, 4.967, 5.108, 5.249 ], + [ 0.042, 0.338, 0.607, 0.854, 1.085, 1.304, 1.512, 1.711, 1.904, 2.090, 2.271, 2.448, 2.622, 2.791, 2.958, 3.122, 3.284, 3.444, 3.602, 3.758, 3.913, 4.066, 4.218, 4.369, 4.518, 4.667, 4.814, 4.961, 5.107, 5.252, 5.397 ], + [ 0.042, 0.344, 0.618, 0.870, 1.106, 1.328, 1.541, 1.744, 1.941, 2.131, 2.316, 2.497, 2.673, 2.847, 3.017, 3.185, 3.350, 3.513, 3.674, 3.834, 3.992, 4.148, 4.304, 4.458, 4.611, 4.762, 4.913, 5.063, 5.212, 5.361, 5.508 ], + [ 0.042, 0.348, 0.625, 0.881, 1.119, 1.345, 1.560, 1.766, 1.965, 2.158, 2.346, 2.529, 2.708, 2.883, 3.056, 3.226, 3.394, 3.559, 3.723, 3.884, 4.045, 4.203, 4.361, 4.517, 4.672, 4.826, 4.979, 5.131, 5.282, 5.433, 5.583 ], + [ 0.042, 0.350, 0.629, 0.886, 1.126, 1.353, 1.569, 1.777, 1.977, 2.171, 2.360, 2.544, 2.724, 2.901, 3.075, 3.246, 3.415, 3.582, 3.746, 3.909, 4.070, 4.230, 4.388, 4.546, 4.702, 4.857, 5.011, 5.164, 5.316, 5.468, 5.619 ], + [ 0.042, 0.350, 0.628, 0.885, 1.126, 1.353, 1.569, 1.776, 1.976, 2.170, 2.359, 2.543, 2.723, 2.900, 3.074, 3.245, 3.414, 3.580, 3.745, 3.908, 4.069, 4.228, 4.387, 4.544, 4.700, 4.855, 5.009, 5.162, 5.314, 5.466, 5.617 ], + [ 0.042, 0.348, 0.624, 0.880, 1.118, 1.344, 1.558, 1.764, 1.963, 2.156, 2.343, 2.526, 2.705, 2.880, 3.053, 3.223, 3.390, 3.555, 3.719, 3.880, 4.040, 4.199, 4.356, 4.512, 4.667, 4.820, 4.973, 5.125, 5.276, 5.427, 5.576 ], + [ 0.042, 0.344, 0.617, 0.868, 1.104, 1.326, 1.538, 1.741, 1.937, 2.127, 2.312, 2.492, 2.668, 2.841, 3.011, 3.179, 3.344, 3.507, 3.668, 3.827, 3.984, 4.141, 4.296, 4.449, 4.602, 4.753, 4.904, 5.054, 5.202, 5.351, 5.498 ], + [ 0.042, 0.338, 0.605, 0.852, 1.083, 1.300, 1.508, 1.707, 1.899, 2.085, 2.266, 2.442, 2.615, 2.784, 2.950, 3.114, 3.276, 3.435, 3.592, 3.748, 3.902, 4.055, 4.207, 4.357, 4.506, 4.654, 4.801, 4.948, 5.093, 5.238, 5.382 ], + [ 0.042, 0.330, 0.590, 0.830, 1.055, 1.266, 1.468, 1.662, 1.848, 2.029, 2.205, 2.376, 2.544, 2.709, 2.870, 3.029, 3.186, 3.341, 3.494, 3.645, 3.795, 3.943, 4.090, 4.236, 4.380, 4.524, 4.667, 4.809, 4.950, 5.090, 5.230 ], + [ 0.042, 0.320, 0.572, 0.804, 1.020, 1.225, 1.420, 1.606, 1.786, 1.961, 2.130, 2.296, 2.457, 2.616, 2.772, 2.925, 3.076, 3.225, 3.372, 3.518, 3.662, 3.804, 3.946, 4.086, 4.225, 4.364, 4.501, 4.637, 4.773, 4.908, 5.042 ], + [ 0.042, 0.309, 0.550, 0.772, 0.980, 1.176, 1.362, 1.541, 1.713, 1.880, 2.042, 2.200, 2.355, 2.507, 2.655, 2.802, 2.946, 3.088, 3.229, 3.368, 3.505, 3.641, 3.776, 3.910, 4.043, 4.174, 4.305, 4.435, 4.565, 4.693, 4.821 ], + [ 0.042, 0.295, 0.525, 0.736, 0.933, 1.119, 1.296, 1.466, 1.630, 1.788, 1.942, 2.091, 2.238, 2.381, 2.522, 2.661, 2.797, 2.932, 3.064, 3.196, 3.326, 3.454, 3.582, 3.708, 3.833, 3.958, 4.081, 4.204, 4.326, 4.448, 4.568 ], + [ 0.042, 0.281, 0.497, 0.696, 0.881, 1.056, 1.223, 1.382, 1.536, 1.685, 1.829, 1.969, 2.107, 2.241, 2.373, 2.503, 2.631, 2.757, 2.881, 3.004, 3.125, 3.245, 3.364, 3.483, 3.600, 3.716, 3.831, 3.946, 4.060, 4.173, 4.285 ], + [ 0.042, 0.265, 0.466, 0.651, 0.824, 0.987, 1.142, 1.291, 1.433, 1.571, 1.705, 1.836, 1.963, 2.088, 2.210, 2.330, 2.448, 2.565, 2.679, 2.793, 2.905, 3.016, 3.126, 3.235, 3.343, 3.450, 3.556, 3.662, 3.767, 3.871, 3.975 ], + [ 0.042, 0.247, 0.433, 0.603, 0.763, 0.913, 1.055, 1.191, 1.322, 1.449, 1.572, 1.691, 1.807, 1.921, 2.033, 2.143, 2.251, 2.357, 2.462, 2.565, 2.667, 2.768, 2.868, 2.967, 3.066, 3.163, 3.259, 3.355, 3.451, 3.545, 3.639 ], + [ 0.042, 0.229, 0.397, 0.552, 0.697, 0.833, 0.962, 1.085, 1.204, 1.318, 1.429, 1.537, 1.642, 1.744, 1.845, 1.943, 2.040, 2.135, 2.229, 2.322, 2.414, 2.504, 2.593, 2.682, 2.770, 2.857, 2.943, 3.028, 3.113, 3.197, 3.281 ], + [ 0.042, 0.209, 0.360, 0.499, 0.628, 0.749, 0.864, 0.974, 1.079, 1.180, 1.279, 1.374, 1.467, 1.557, 1.646, 1.733, 1.818, 1.902, 1.984, 2.066, 2.146, 2.225, 2.304, 2.381, 2.458, 2.534, 2.609, 2.683, 2.757, 2.831, 2.903 ], + [ 0.042, 0.189, 0.321, 0.442, 0.555, 0.661, 0.762, 0.857, 0.949, 1.037, 1.122, 1.204, 1.284, 1.362, 1.439, 1.513, 1.586, 1.658, 1.729, 1.798, 1.867, 1.934, 2.001, 2.067, 2.132, 2.196, 2.260, 2.323, 2.386, 2.448, 2.509 ], + [ 0.042, 0.168, 0.281, 0.384, 0.481, 0.571, 0.656, 0.737, 0.814, 0.888, 0.960, 1.029, 1.096, 1.161, 1.224, 1.286, 1.347, 1.406, 1.465, 1.522, 1.578, 1.634, 1.688, 1.742, 1.795, 1.848, 1.900, 1.951, 2.002, 2.052, 2.102 ], + [ 0.042, 0.146, 0.239, 0.325, 0.404, 0.478, 0.547, 0.613, 0.676, 0.736, 0.793, 0.849, 0.902, 0.954, 1.005, 1.054, 1.101, 1.148, 1.194, 1.239, 1.282, 1.326, 1.368, 1.409, 1.450, 1.491, 1.531, 1.570, 1.609, 1.647, 1.685 ], + [ 0.042, 0.124, 0.197, 0.264, 0.326, 0.384, 0.437, 0.488, 0.536, 0.581, 0.624, 0.666, 0.706, 0.744, 0.781, 0.817, 0.852, 0.886, 0.919, 0.951, 0.982, 1.012, 1.042, 1.071, 1.100, 1.128, 1.155, 1.182, 1.209, 1.235, 1.261 ], + [ 0.042, 0.102, 0.155, 0.204, 0.248, 0.288, 0.326, 0.361, 0.394, 0.425, 0.454, 0.482, 0.508, 0.533, 0.557, 0.579, 0.601, 0.622, 0.642, 0.661, 0.679, 0.697, 0.714, 0.731, 0.747, 0.762, 0.777, 0.792, 0.806, 0.820, 0.834 ], + [ 0.042, 0.080, 0.113, 0.143, 0.169, 0.193, 0.215, 0.235, 0.253, 0.269, 0.284, 0.298, 0.310, 0.322, 0.332, 0.341, 0.350, 0.358, 0.365, 0.371, 0.377, 0.382, 0.386, 0.390, 0.394, 0.397, 0.400, 0.402, 0.404, 0.405, 0.406 ], + [ 0.042, 0.058, 0.071, 0.082, 0.092, 0.099, 0.105, 0.110, 0.113, 0.115, 0.116, 0.115, 0.114, 0.112, 0.109, 0.105, 0.101, 0.096, 0.090, 0.083, 0.077, 0.069, 0.061, 0.053, 0.044, 0.035, 0.025, 0.015, 0.005, -0.006, -0.017 ], + [ 0.042, 0.036, 0.030, 0.023, 0.015, 0.007, -0.003, -0.013, -0.025, -0.037, -0.050, -0.064, -0.079, -0.094, -0.110, -0.127, -0.144, -0.162, -0.180, -0.199, -0.219, -0.238, -0.259, -0.279, -0.300, -0.322, -0.343, -0.365, -0.388, -0.410, -0.433 ], + [ 0.042, 0.015, -0.010, -0.035, -0.059, -0.084, -0.108, -0.133, -0.159, -0.185, -0.212, -0.239, -0.267, -0.295, -0.324, -0.353, -0.383, -0.413, -0.444, -0.475, -0.506, -0.538, -0.570, -0.603, -0.636, -0.669, -0.702, -0.736, -0.770, -0.805, -0.839 ], + [ 0.042, -0.005, -0.049, -0.091, -0.131, -0.171, -0.210, -0.250, -0.289, -0.328, -0.368, -0.408, -0.449, -0.489, -0.530, -0.572, -0.614, -0.656, -0.698, -0.741, -0.784, -0.828, -0.872, -0.916, -0.960, -1.005, -1.050, -1.095, -1.140, -1.186, -1.232 ], + [ 0.042, -0.025, -0.086, -0.144, -0.200, -0.255, -0.308, -0.361, -0.413, -0.465, -0.518, -0.570, -0.622, -0.675, -0.728, -0.781, -0.834, -0.888, -0.942, -0.996, -1.050, -1.105, -1.160, -1.215, -1.270, -1.326, -1.382, -1.438, -1.494, -1.551, -1.607 ], + [ 0.042, -0.043, -0.122, -0.195, -0.266, -0.334, -0.400, -0.466, -0.531, -0.595, -0.659, -0.723, -0.787, -0.851, -0.915, -0.979, -1.044, -1.108, -1.173, -1.237, -1.302, -1.368, -1.433, -1.499, -1.564, -1.630, -1.696, -1.763, -1.829, -1.896, -1.963 ], + [ 0.042, -0.060, -0.154, -0.243, -0.327, -0.408, -0.487, -0.564, -0.641, -0.717, -0.792, -0.867, -0.941, -1.016, -1.090, -1.165, -1.239, -1.314, -1.389, -1.463, -1.538, -1.613, -1.689, -1.764, -1.840, -1.915, -1.991, -2.067, -2.143, -2.219, -2.296 ], + [ 0.042, -0.076, -0.185, -0.286, -0.383, -0.476, -0.567, -0.655, -0.743, -0.829, -0.914, -0.999, -1.084, -1.168, -1.252, -1.336, -1.420, -1.504, -1.588, -1.672, -1.756, -1.841, -1.925, -2.009, -2.094, -2.178, -2.263, -2.348, -2.433, -2.518, -2.604 ], + [ 0.042, -0.091, -0.213, -0.326, -0.434, -0.538, -0.639, -0.738, -0.835, -0.931, -1.026, -1.120, -1.213, -1.307, -1.399, -1.492, -1.585, -1.677, -1.770, -1.862, -1.955, -2.047, -2.140, -2.232, -2.325, -2.418, -2.511, -2.604, -2.697, -2.790, -2.883 ], + [ 0.042, -0.104, -0.237, -0.362, -0.480, -0.594, -0.704, -0.812, -0.918, -1.022, -1.125, -1.227, -1.329, -1.430, -1.531, -1.631, -1.732, -1.832, -1.932, -2.032, -2.131, -2.231, -2.331, -2.431, -2.531, -2.631, -2.731, -2.832, -2.932, -3.032, -3.133 ], + [ 0.042, -0.115, -0.259, -0.393, -0.520, -0.642, -0.761, -0.876, -0.990, -1.101, -1.212, -1.321, -1.430, -1.538, -1.645, -1.753, -1.859, -1.966, -2.073, -2.179, -2.286, -2.392, -2.498, -2.605, -2.711, -2.817, -2.924, -3.030, -3.137, -3.244, -3.350 ], + [ 0.042, -0.125, -0.277, -0.419, -0.554, -0.683, -0.808, -0.931, -1.050, -1.168, -1.285, -1.400, -1.515, -1.629, -1.742, -1.855, -1.967, -2.080, -2.192, -2.304, -2.416, -2.527, -2.639, -2.751, -2.863, -2.974, -3.086, -3.198, -3.310, -3.422, -3.534 ], + [ 0.042, -0.132, -0.292, -0.440, -0.581, -0.716, -0.847, -0.974, -1.099, -1.222, -1.344, -1.464, -1.583, -1.702, -1.820, -1.937, -2.055, -2.171, -2.288, -2.404, -2.521, -2.637, -2.753, -2.869, -2.985, -3.101, -3.217, -3.334, -3.450, -3.566, -3.682 ], + [ 0.042, -0.138, -0.303, -0.456, -0.602, -0.741, -0.876, -1.007, -1.136, -1.263, -1.388, -1.512, -1.635, -1.757, -1.879, -2.000, -2.120, -2.240, -2.360, -2.480, -2.600, -2.719, -2.839, -2.958, -3.078, -3.197, -3.316, -3.436, -3.555, -3.674, -3.794 ], + [ 0.042, -0.142, -0.310, -0.467, -0.615, -0.758, -0.895, -1.029, -1.161, -1.290, -1.418, -1.544, -1.670, -1.794, -1.918, -2.041, -2.164, -2.286, -2.409, -2.531, -2.652, -2.774, -2.896, -3.017, -3.139, -3.260, -3.382, -3.503, -3.625, -3.747, -3.868 ], + [ 0.042, -0.144, -0.314, -0.472, -0.622, -0.766, -0.905, -1.040, -1.173, -1.303, -1.432, -1.560, -1.686, -1.812, -1.937, -2.061, -2.185, -2.309, -2.432, -2.555, -2.678, -2.801, -2.924, -3.046, -3.169, -3.291, -3.414, -3.537, -3.659, -3.782, -3.904 ], + [ 0.042, -0.144, -0.313, -0.472, -0.621, -0.765, -0.904, -1.039, -1.172, -1.303, -1.431, -1.559, -1.685, -1.811, -1.936, -2.060, -2.184, -2.307, -2.431, -2.554, -2.677, -2.799, -2.922, -3.045, -3.167, -3.290, -3.412, -3.535, -3.657, -3.780, -3.902 ], + [ 0.042, -0.142, -0.309, -0.466, -0.614, -0.756, -0.894, -1.028, -1.159, -1.288, -1.415, -1.542, -1.667, -1.791, -1.914, -2.038, -2.160, -2.282, -2.404, -2.526, -2.648, -2.769, -2.891, -3.012, -3.134, -3.255, -3.376, -3.498, -3.619, -3.740, -3.862 ], + [ 0.042, -0.138, -0.302, -0.455, -0.600, -0.739, -0.873, -1.004, -1.133, -1.259, -1.384, -1.508, -1.630, -1.752, -1.873, -1.994, -2.114, -2.234, -2.354, -2.473, -2.592, -2.712, -2.831, -2.950, -3.069, -3.188, -3.307, -3.426, -3.545, -3.664, -3.783 ], + [ 0.042, -0.132, -0.290, -0.438, -0.578, -0.713, -0.843, -0.970, -1.095, -1.217, -1.338, -1.458, -1.577, -1.695, -1.812, -1.929, -2.046, -2.162, -2.278, -2.394, -2.510, -2.626, -2.742, -2.857, -2.973, -3.089, -3.204, -3.320, -3.436, -3.552, -3.668 ], + [ 0.042, -0.124, -0.275, -0.416, -0.550, -0.679, -0.804, -0.925, -1.044, -1.161, -1.277, -1.392, -1.506, -1.619, -1.732, -1.844, -1.956, -2.068, -2.180, -2.291, -2.402, -2.514, -2.625, -2.736, -2.847, -2.958, -3.070, -3.181, -3.292, -3.404, -3.515 ], + [ 0.042, -0.114, -0.257, -0.390, -0.516, -0.637, -0.755, -0.870, -0.982, -1.093, -1.203, -1.311, -1.419, -1.527, -1.633, -1.740, -1.846, -1.952, -2.058, -2.164, -2.270, -2.375, -2.481, -2.587, -2.692, -2.798, -2.904, -3.010, -3.116, -3.222, -3.328 ], + [ 0.042, -0.102, -0.235, -0.358, -0.475, -0.588, -0.697, -0.804, -0.909, -1.012, -1.115, -1.216, -1.317, -1.417, -1.517, -1.617, -1.716, -1.815, -1.915, -2.014, -2.113, -2.212, -2.311, -2.410, -2.510, -2.609, -2.708, -2.808, -2.907, -3.007, -3.107 ], + [ 0.042, -0.089, -0.210, -0.322, -0.429, -0.532, -0.632, -0.729, -0.825, -0.920, -1.014, -1.107, -1.200, -1.292, -1.384, -1.476, -1.567, -1.659, -1.750, -1.842, -1.934, -2.025, -2.117, -2.209, -2.300, -2.392, -2.484, -2.577, -2.669, -2.761, -2.854 ], + [ 0.042, -0.075, -0.182, -0.282, -0.377, -0.469, -0.558, -0.646, -0.732, -0.817, -0.901, -0.985, -1.069, -1.152, -1.235, -1.318, -1.401, -1.484, -1.567, -1.650, -1.733, -1.816, -1.900, -1.983, -2.067, -2.150, -2.234, -2.318, -2.402, -2.486, -2.571 ], + [ 0.042, -0.059, -0.151, -0.238, -0.320, -0.400, -0.477, -0.554, -0.629, -0.704, -0.778, -0.851, -0.925, -0.998, -1.072, -1.145, -1.218, -1.292, -1.365, -1.439, -1.513, -1.587, -1.661, -1.735, -1.810, -1.885, -1.959, -2.034, -2.109, -2.185, -2.260 ], + [ 0.042, -0.041, -0.118, -0.190, -0.258, -0.325, -0.390, -0.454, -0.518, -0.581, -0.644, -0.707, -0.769, -0.832, -0.895, -0.958, -1.021, -1.084, -1.148, -1.211, -1.275, -1.339, -1.403, -1.468, -1.533, -1.597, -1.662, -1.728, -1.793, -1.859, -1.925 ], + [ 0.042, -0.023, -0.082, -0.139, -0.193, -0.245, -0.297, -0.349, -0.400, -0.450, -0.501, -0.552, -0.604, -0.655, -0.706, -0.758, -0.810, -0.863, -0.915, -0.968, -1.021, -1.075, -1.129, -1.182, -1.237, -1.291, -1.346, -1.401, -1.456, -1.511, -1.566 ], + [ 0.042, -0.003, -0.045, -0.085, -0.123, -0.161, -0.199, -0.237, -0.275, -0.313, -0.351, -0.390, -0.429, -0.468, -0.508, -0.548, -0.588, -0.629, -0.670, -0.712, -0.754, -0.796, -0.839, -0.881, -0.925, -0.968, -1.012, -1.056, -1.100, -1.144, -1.189 ], + [ 0.042, 0.017, -0.006, -0.029, -0.051, -0.074, -0.097, -0.120, -0.144, -0.169, -0.194, -0.220, -0.246, -0.273, -0.300, -0.328, -0.357, -0.386, -0.415, -0.445, -0.475, -0.505, -0.536, -0.567, -0.599, -0.631, -0.663, -0.696, -0.728, -0.762, -0.795 ], + [ 0.042, 0.039, 0.034, 0.029, 0.024, 0.017, 0.009, 0.000, -0.010, -0.020, -0.032, -0.044, -0.058, -0.071, -0.086, -0.101, -0.117, -0.134, -0.151, -0.168, -0.186, -0.204, -0.223, -0.243, -0.262, -0.282, -0.303, -0.324, -0.345, -0.366, -0.388 ], + [ 0.042, 0.060, 0.076, 0.089, 0.100, 0.110, 0.117, 0.124, 0.128, 0.132, 0.134, 0.136, 0.136, 0.135, 0.134, 0.131, 0.128, 0.125, 0.120, 0.115, 0.110, 0.104, 0.097, 0.090, 0.083, 0.075, 0.066, 0.058, 0.049, 0.039, 0.030 ] + ], + "type":"contourcarpet" + }, + { + "showlegend":true, + "name":"Pressure
contours", + "autocontour":false, + "z":[ + [ 0.361, 0.300, 0.246, 0.209, 0.182, 0.162, 0.145, 0.132, 0.121, 0.111, 0.103, 0.096, 0.090, 0.085, 0.080, 0.075, 0.072, 0.068, 0.065, 0.062, 0.059, 0.057, 0.055, 0.053, 0.051, 0.049, 0.047, 0.046, 0.044, 0.043, 0.042 ], + [ 0.261, 0.234, 0.199, 0.170, 0.147, 0.129, 0.115, 0.103, 0.093, 0.085, 0.078, 0.072, 0.066, 0.062, 0.058, 0.054, 0.051, 0.048, 0.045, 0.043, 0.041, 0.039, 0.037, 0.036, 0.034, 0.033, 0.031, 0.030, 0.029, 0.028, 0.027 ], + [ 0.180, 0.165, 0.143, 0.123, 0.105, 0.091, 0.080, 0.070, 0.062, 0.055, 0.050, 0.045, 0.041, 0.037, 0.034, 0.031, 0.029, 0.026, 0.024, 0.023, 0.021, 0.020, 0.018, 0.017, 0.016, 0.015, 0.014, 0.014, 0.013, 0.012, 0.012 ], + [ 0.102, 0.095, 0.084, 0.071, 0.059, 0.049, 0.041, 0.034, 0.028, 0.023, 0.019, 0.016, 0.013, 0.010, 0.008, 0.006, 0.005, 0.004, 0.002, 0.001, 0.000, -0.000, -0.001, -0.002, -0.002, -0.003, -0.003, -0.004, -0.004, -0.004, -0.005 ], + [ 0.024, 0.025, 0.021, 0.015, 0.009, 0.003, -0.001, -0.005, -0.008, -0.011, -0.013, -0.015, -0.017, -0.018, -0.019, -0.020, -0.020, -0.021, -0.021, -0.021, -0.021, -0.022, -0.022, -0.022, -0.022, -0.022, -0.022, -0.021, -0.021, -0.021, -0.021 ], + [ -0.055, -0.047, -0.044, -0.043, -0.044, -0.045, -0.046, -0.047, -0.047, -0.048, -0.048, -0.048, -0.048, -0.048, -0.047, -0.047, -0.046, -0.046, -0.045, -0.045, -0.044, -0.043, -0.043, -0.042, -0.042, -0.041, -0.040, -0.040, -0.039, -0.039, -0.038 ], + [ -0.136, -0.121, -0.111, -0.104, -0.099, -0.096, -0.093, -0.091, -0.088, -0.086, -0.084, -0.082, -0.080, -0.079, -0.077, -0.075, -0.073, -0.072, -0.070, -0.069, -0.067, -0.066, -0.065, -0.063, -0.062, -0.061, -0.060, -0.059, -0.058, -0.057, -0.056 ], + [ -0.220, -0.197, -0.180, -0.167, -0.157, -0.149, -0.142, -0.136, -0.131, -0.126, -0.122, -0.118, -0.114, -0.111, -0.107, -0.104, -0.101, -0.099, -0.096, -0.094, -0.091, -0.089, -0.087, -0.085, -0.083, -0.081, -0.079, -0.078, -0.076, -0.075, -0.073 ], + [ -0.307, -0.275, -0.250, -0.231, -0.216, -0.203, -0.192, -0.183, -0.174, -0.167, -0.160, -0.154, -0.149, -0.143, -0.139, -0.134, -0.130, -0.126, -0.122, -0.119, -0.116, -0.113, -0.110, -0.107, -0.104, -0.102, -0.099, -0.097, -0.095, -0.093, -0.091 ], + [ -0.396, -0.355, -0.323, -0.297, -0.276, -0.259, -0.244, -0.230, -0.219, -0.209, -0.200, -0.191, -0.184, -0.177, -0.170, -0.164, -0.159, -0.154, -0.149, -0.144, -0.140, -0.136, -0.132, -0.129, -0.125, -0.122, -0.119, -0.116, -0.114, -0.111, -0.108 ], + [ -0.488, -0.437, -0.397, -0.364, -0.338, -0.315, -0.296, -0.279, -0.264, -0.251, -0.239, -0.229, -0.219, -0.210, -0.202, -0.195, -0.188, -0.181, -0.175, -0.170, -0.164, -0.160, -0.155, -0.151, -0.146, -0.143, -0.139, -0.135, -0.132, -0.129, -0.126 ], + [ -0.581, -0.520, -0.472, -0.432, -0.400, -0.372, -0.348, -0.328, -0.310, -0.294, -0.279, -0.266, -0.254, -0.244, -0.234, -0.225, -0.217, -0.209, -0.202, -0.195, -0.189, -0.183, -0.177, -0.172, -0.167, -0.163, -0.158, -0.154, -0.150, -0.146, -0.143 ], + [ -0.677, -0.605, -0.548, -0.501, -0.462, -0.429, -0.401, -0.377, -0.355, -0.336, -0.319, -0.304, -0.290, -0.277, -0.265, -0.255, -0.245, -0.236, -0.227, -0.220, -0.212, -0.205, -0.199, -0.193, -0.187, -0.182, -0.177, -0.172, -0.168, -0.163, -0.159 ], + [ -0.773, -0.690, -0.624, -0.570, -0.525, -0.487, -0.454, -0.425, -0.400, -0.378, -0.358, -0.340, -0.324, -0.309, -0.296, -0.284, -0.273, -0.262, -0.252, -0.244, -0.235, -0.227, -0.220, -0.213, -0.207, -0.201, -0.195, -0.190, -0.185, -0.180, -0.175 ], + [ -0.870, -0.776, -0.701, -0.639, -0.587, -0.544, -0.506, -0.473, -0.445, -0.419, -0.397, -0.376, -0.358, -0.341, -0.326, -0.312, -0.299, -0.288, -0.277, -0.267, -0.257, -0.248, -0.240, -0.233, -0.225, -0.219, -0.212, -0.206, -0.201, -0.195, -0.190 ], + [ -0.968, -0.862, -0.777, -0.708, -0.649, -0.600, -0.557, -0.520, -0.488, -0.459, -0.434, -0.411, -0.390, -0.372, -0.355, -0.339, -0.325, -0.312, -0.299, -0.288, -0.278, -0.268, -0.259, -0.251, -0.243, -0.235, -0.228, -0.222, -0.215, -0.209, -0.204 ], + [ -1.066, -0.948, -0.853, -0.775, -0.710, -0.655, -0.607, -0.566, -0.530, -0.498, -0.469, -0.444, -0.421, -0.400, -0.381, -0.364, -0.349, -0.334, -0.321, -0.308, -0.297, -0.286, -0.276, -0.267, -0.259, -0.250, -0.243, -0.236, -0.229, -0.222, -0.216 ], + [ -1.164, -1.034, -0.929, -0.842, -0.770, -0.708, -0.656, -0.610, -0.570, -0.535, -0.503, -0.475, -0.450, -0.427, -0.407, -0.388, -0.371, -0.355, -0.340, -0.327, -0.314, -0.303, -0.292, -0.282, -0.273, -0.264, -0.256, -0.248, -0.241, -0.234, -0.227 ], + [ -1.262, -1.119, -1.003, -0.908, -0.828, -0.760, -0.702, -0.652, -0.608, -0.569, -0.535, -0.504, -0.476, -0.452, -0.429, -0.409, -0.390, -0.373, -0.358, -0.343, -0.330, -0.317, -0.306, -0.295, -0.285, -0.276, -0.267, -0.259, -0.251, -0.243, -0.237 ], + [ -1.360, -1.203, -1.076, -0.971, -0.884, -0.810, -0.746, -0.691, -0.643, -0.601, -0.563, -0.530, -0.500, -0.473, -0.449, -0.427, -0.407, -0.389, -0.372, -0.357, -0.343, -0.329, -0.317, -0.306, -0.295, -0.285, -0.276, -0.267, -0.259, -0.251, -0.244 ], + [ -1.457, -1.286, -1.148, -1.033, -0.937, -0.856, -0.787, -0.727, -0.675, -0.629, -0.589, -0.553, -0.521, -0.492, -0.466, -0.443, -0.422, -0.402, -0.384, -0.368, -0.353, -0.339, -0.326, -0.314, -0.303, -0.292, -0.283, -0.274, -0.265, -0.257, -0.249 ], + [ -1.555, -1.369, -1.218, -1.093, -0.988, -0.900, -0.824, -0.759, -0.703, -0.654, -0.610, -0.572, -0.538, -0.507, -0.480, -0.455, -0.432, -0.412, -0.393, -0.376, -0.360, -0.346, -0.332, -0.320, -0.308, -0.297, -0.287, -0.278, -0.269, -0.260, -0.252 ], + [ -1.654, -1.452, -1.287, -1.150, -1.036, -0.939, -0.858, -0.787, -0.727, -0.674, -0.627, -0.587, -0.550, -0.518, -0.489, -0.463, -0.439, -0.418, -0.398, -0.381, -0.364, -0.349, -0.335, -0.322, -0.310, -0.299, -0.289, -0.279, -0.270, -0.261, -0.253 ], + [ -1.755, -1.535, -1.354, -1.204, -1.080, -0.975, -0.886, -0.810, -0.745, -0.689, -0.639, -0.596, -0.558, -0.524, -0.494, -0.467, -0.442, -0.420, -0.400, -0.381, -0.365, -0.349, -0.335, -0.322, -0.309, -0.298, -0.287, -0.277, -0.268, -0.259, -0.251 ], + [ -1.861, -1.619, -1.420, -1.256, -1.119, -1.005, -0.909, -0.827, -0.758, -0.698, -0.646, -0.600, -0.560, -0.525, -0.494, -0.466, -0.440, -0.418, -0.397, -0.378, -0.361, -0.345, -0.331, -0.317, -0.305, -0.294, -0.283, -0.273, -0.264, -0.255, -0.247 ], + [ -1.973, -1.706, -1.485, -1.303, -1.153, -1.029, -0.925, -0.837, -0.763, -0.700, -0.645, -0.598, -0.557, -0.520, -0.488, -0.459, -0.434, -0.410, -0.390, -0.371, -0.353, -0.337, -0.323, -0.310, -0.297, -0.286, -0.275, -0.265, -0.256, -0.248, -0.240 ], + [ -2.095, -1.797, -1.549, -1.346, -1.180, -1.044, -0.932, -0.839, -0.760, -0.694, -0.637, -0.588, -0.546, -0.509, -0.476, -0.447, -0.421, -0.398, -0.377, -0.358, -0.341, -0.326, -0.311, -0.298, -0.286, -0.275, -0.264, -0.255, -0.246, -0.237, -0.230 ], + [ -2.234, -1.893, -1.612, -1.383, -1.199, -1.050, -0.929, -0.830, -0.748, -0.679, -0.621, -0.571, -0.528, -0.490, -0.458, -0.429, -0.403, -0.380, -0.360, -0.341, -0.325, -0.309, -0.296, -0.283, -0.271, -0.260, -0.250, -0.241, -0.232, -0.224, -0.217 ], + [ -2.397, -1.999, -1.672, -1.411, -1.206, -1.044, -0.914, -0.809, -0.724, -0.653, -0.594, -0.544, -0.501, -0.464, -0.432, -0.404, -0.379, -0.357, -0.337, -0.319, -0.303, -0.289, -0.276, -0.264, -0.252, -0.242, -0.233, -0.224, -0.216, -0.208, -0.201 ], + [ -2.597, -2.116, -1.727, -1.427, -1.197, -1.020, -0.883, -0.774, -0.687, -0.616, -0.557, -0.508, -0.466, -0.430, -0.399, -0.372, -0.349, -0.328, -0.309, -0.293, -0.278, -0.264, -0.252, -0.241, -0.230, -0.221, -0.212, -0.204, -0.197, -0.190, -0.183 ], + [ -2.854, -2.246, -1.772, -1.422, -1.166, -0.976, -0.832, -0.722, -0.635, -0.565, -0.508, -0.461, -0.422, -0.388, -0.359, -0.334, -0.313, -0.293, -0.276, -0.261, -0.248, -0.235, -0.224, -0.214, -0.205, -0.196, -0.189, -0.181, -0.175, -0.168, -0.163 ], + [ -3.199, -2.387, -1.795, -1.386, -1.104, -0.904, -0.759, -0.650, -0.566, -0.500, -0.447, -0.404, -0.368, -0.338, -0.312, -0.290, -0.271, -0.254, -0.239, -0.226, -0.214, -0.203, -0.193, -0.185, -0.177, -0.169, -0.162, -0.156, -0.150, -0.145, -0.140 ], + [ -3.683, -2.526, -1.773, -1.303, -1.002, -0.800, -0.660, -0.558, -0.481, -0.422, -0.375, -0.338, -0.307, -0.281, -0.259, -0.240, -0.224, -0.210, -0.197, -0.186, -0.176, -0.168, -0.160, -0.152, -0.146, -0.140, -0.134, -0.129, -0.124, -0.120, -0.116 ], + [ -4.381, -2.615, -1.666, -1.151, -0.849, -0.660, -0.534, -0.445, -0.380, -0.331, -0.293, -0.263, -0.238, -0.218, -0.201, -0.186, -0.173, -0.162, -0.153, -0.144, -0.137, -0.130, -0.124, -0.118, -0.113, -0.108, -0.104, -0.100, -0.097, -0.093, -0.090 ], + [ -5.328, -2.533, -1.422, -0.912, -0.643, -0.484, -0.384, -0.316, -0.267, -0.231, -0.204, -0.182, -0.165, -0.151, -0.139, -0.129, -0.120, -0.113, -0.106, -0.100, -0.095, -0.091, -0.086, -0.083, -0.079, -0.076, -0.073, -0.071, -0.068, -0.066, -0.064 ], + [ -6.040, -2.071, -1.002, -0.589, -0.391, -0.283, -0.218, -0.176, -0.147, -0.126, -0.111, -0.099, -0.089, -0.082, -0.076, -0.070, -0.066, -0.062, -0.059, -0.056, -0.053, -0.051, -0.049, -0.047, -0.045, -0.043, -0.042, -0.040, -0.039, -0.038, -0.037 ], + [ -4.472, -1.137, -0.449, -0.218, -0.121, -0.074, -0.049, -0.035, -0.027, -0.022, -0.018, -0.016, -0.015, -0.014, -0.013, -0.013, -0.012, -0.012, -0.012, -0.012, -0.012, -0.011, -0.011, -0.011, -0.011, -0.011, -0.011, -0.011, -0.011, -0.010, -0.010 ], + [ -1.177, -0.104, 0.094, 0.132, 0.131, 0.121, 0.108, 0.096, 0.086, 0.076, 0.069, 0.062, 0.056, 0.051, 0.046, 0.042, 0.039, 0.036, 0.033, 0.031, 0.028, 0.027, 0.025, 0.023, 0.022, 0.020, 0.019, 0.018, 0.017, 0.016, 0.015 ], + [ 0.491, 0.570, 0.488, 0.403, 0.335, 0.282, 0.242, 0.210, 0.184, 0.163, 0.146, 0.132, 0.120, 0.109, 0.100, 0.093, 0.086, 0.080, 0.075, 0.070, 0.066, 0.062, 0.059, 0.056, 0.053, 0.050, 0.048, 0.046, 0.044, 0.042, 0.040 ], + [ 0.948, 0.855, 0.702, 0.574, 0.476, 0.401, 0.344, 0.300, 0.264, 0.236, 0.212, 0.192, 0.175, 0.161, 0.148, 0.138, 0.128, 0.120, 0.113, 0.106, 0.100, 0.095, 0.090, 0.086, 0.082, 0.078, 0.074, 0.071, 0.068, 0.066, 0.063 ], + [ 0.995, 0.916, 0.781, 0.657, 0.557, 0.478, 0.415, 0.366, 0.325, 0.292, 0.264, 0.241, 0.221, 0.204, 0.189, 0.176, 0.165, 0.155, 0.146, 0.138, 0.130, 0.124, 0.118, 0.112, 0.107, 0.103, 0.099, 0.095, 0.091, 0.088, 0.085 ], + [ 0.926, 0.881, 0.783, 0.681, 0.593, 0.519, 0.458, 0.409, 0.367, 0.333, 0.304, 0.279, 0.257, 0.239, 0.222, 0.208, 0.195, 0.184, 0.174, 0.165, 0.157, 0.149, 0.142, 0.136, 0.130, 0.125, 0.120, 0.116, 0.111, 0.107, 0.104 ], + [ 0.832, 0.812, 0.746, 0.670, 0.597, 0.534, 0.479, 0.433, 0.393, 0.360, 0.331, 0.306, 0.284, 0.265, 0.248, 0.233, 0.220, 0.208, 0.197, 0.188, 0.179, 0.171, 0.163, 0.156, 0.150, 0.144, 0.139, 0.134, 0.129, 0.125, 0.121 ], + [ 0.741, 0.737, 0.695, 0.639, 0.583, 0.530, 0.484, 0.443, 0.407, 0.376, 0.348, 0.324, 0.303, 0.284, 0.268, 0.253, 0.239, 0.227, 0.216, 0.206, 0.197, 0.188, 0.180, 0.173, 0.167, 0.161, 0.155, 0.149, 0.144, 0.140, 0.135 ], + [ 0.661, 0.667, 0.641, 0.602, 0.559, 0.517, 0.478, 0.442, 0.411, 0.383, 0.357, 0.335, 0.315, 0.297, 0.281, 0.266, 0.253, 0.241, 0.230, 0.220, 0.211, 0.202, 0.194, 0.187, 0.180, 0.174, 0.168, 0.162, 0.157, 0.152, 0.148 ], + [ 0.595, 0.605, 0.590, 0.563, 0.530, 0.497, 0.465, 0.435, 0.408, 0.383, 0.360, 0.340, 0.322, 0.305, 0.290, 0.276, 0.263, 0.251, 0.241, 0.231, 0.222, 0.213, 0.205, 0.198, 0.191, 0.185, 0.179, 0.173, 0.168, 0.163, 0.158 ], + [ 0.540, 0.552, 0.544, 0.525, 0.501, 0.475, 0.449, 0.424, 0.400, 0.379, 0.359, 0.340, 0.324, 0.308, 0.294, 0.281, 0.269, 0.258, 0.248, 0.238, 0.229, 0.221, 0.213, 0.206, 0.199, 0.193, 0.187, 0.181, 0.176, 0.171, 0.166 ], + [ 0.495, 0.508, 0.505, 0.491, 0.473, 0.452, 0.431, 0.410, 0.390, 0.372, 0.354, 0.338, 0.322, 0.308, 0.295, 0.283, 0.272, 0.261, 0.252, 0.243, 0.234, 0.226, 0.219, 0.212, 0.205, 0.199, 0.193, 0.188, 0.182, 0.177, 0.173 ], + [ 0.460, 0.472, 0.471, 0.461, 0.447, 0.431, 0.413, 0.396, 0.379, 0.363, 0.347, 0.333, 0.319, 0.306, 0.294, 0.283, 0.273, 0.263, 0.254, 0.245, 0.237, 0.229, 0.222, 0.215, 0.209, 0.203, 0.197, 0.192, 0.187, 0.182, 0.177 ], + [ 0.432, 0.442, 0.442, 0.435, 0.424, 0.411, 0.396, 0.382, 0.367, 0.353, 0.339, 0.326, 0.314, 0.302, 0.291, 0.281, 0.271, 0.262, 0.254, 0.245, 0.238, 0.231, 0.224, 0.217, 0.211, 0.205, 0.200, 0.195, 0.190, 0.185, 0.180 ], + [ 0.410, 0.419, 0.419, 0.413, 0.404, 0.393, 0.380, 0.368, 0.355, 0.342, 0.330, 0.319, 0.308, 0.297, 0.287, 0.277, 0.269, 0.260, 0.252, 0.244, 0.237, 0.230, 0.224, 0.218, 0.212, 0.206, 0.201, 0.196, 0.191, 0.186, 0.182 ], + [ 0.393, 0.400, 0.399, 0.394, 0.386, 0.377, 0.366, 0.355, 0.343, 0.332, 0.321, 0.311, 0.301, 0.291, 0.282, 0.273, 0.265, 0.257, 0.249, 0.242, 0.235, 0.229, 0.223, 0.217, 0.211, 0.206, 0.201, 0.196, 0.191, 0.187, 0.183 ], + [ 0.381, 0.385, 0.383, 0.378, 0.371, 0.362, 0.353, 0.343, 0.332, 0.322, 0.312, 0.303, 0.293, 0.284, 0.276, 0.268, 0.260, 0.253, 0.245, 0.239, 0.232, 0.226, 0.220, 0.215, 0.209, 0.204, 0.199, 0.195, 0.190, 0.186, 0.182 ], + [ 0.371, 0.373, 0.370, 0.365, 0.358, 0.350, 0.341, 0.331, 0.322, 0.313, 0.303, 0.295, 0.286, 0.278, 0.270, 0.262, 0.255, 0.248, 0.241, 0.235, 0.229, 0.223, 0.217, 0.212, 0.207, 0.202, 0.197, 0.193, 0.188, 0.184, 0.180 ], + [ 0.365, 0.364, 0.360, 0.354, 0.347, 0.339, 0.330, 0.321, 0.312, 0.303, 0.295, 0.286, 0.278, 0.271, 0.263, 0.256, 0.249, 0.242, 0.236, 0.230, 0.224, 0.218, 0.213, 0.208, 0.203, 0.199, 0.194, 0.190, 0.186, 0.182, 0.178 ], + [ 0.360, 0.357, 0.352, 0.345, 0.337, 0.329, 0.320, 0.312, 0.303, 0.295, 0.286, 0.278, 0.271, 0.263, 0.256, 0.249, 0.243, 0.236, 0.230, 0.224, 0.219, 0.214, 0.208, 0.204, 0.199, 0.194, 0.190, 0.186, 0.182, 0.178, 0.175 ], + [ 0.357, 0.352, 0.345, 0.337, 0.329, 0.320, 0.312, 0.303, 0.295, 0.286, 0.278, 0.271, 0.263, 0.256, 0.249, 0.242, 0.236, 0.230, 0.224, 0.219, 0.213, 0.208, 0.203, 0.199, 0.194, 0.190, 0.186, 0.182, 0.178, 0.174, 0.171 ], + [ 0.355, 0.347, 0.339, 0.330, 0.321, 0.312, 0.303, 0.295, 0.286, 0.278, 0.270, 0.263, 0.255, 0.248, 0.242, 0.235, 0.229, 0.223, 0.217, 0.212, 0.207, 0.202, 0.197, 0.193, 0.188, 0.184, 0.180, 0.176, 0.173, 0.169, 0.166 ], + [ 0.354, 0.344, 0.334, 0.324, 0.315, 0.305, 0.296, 0.287, 0.278, 0.270, 0.262, 0.255, 0.247, 0.240, 0.234, 0.228, 0.222, 0.216, 0.210, 0.205, 0.200, 0.195, 0.191, 0.186, 0.182, 0.178, 0.174, 0.171, 0.167, 0.164, 0.160 ], + [ 0.353, 0.341, 0.330, 0.319, 0.308, 0.298, 0.288, 0.279, 0.270, 0.262, 0.254, 0.246, 0.239, 0.232, 0.226, 0.220, 0.214, 0.208, 0.203, 0.198, 0.193, 0.188, 0.184, 0.180, 0.176, 0.172, 0.168, 0.164, 0.161, 0.158, 0.154 ], + [ 0.352, 0.339, 0.326, 0.314, 0.302, 0.291, 0.281, 0.271, 0.262, 0.254, 0.246, 0.238, 0.231, 0.224, 0.217, 0.211, 0.205, 0.200, 0.195, 0.190, 0.185, 0.180, 0.176, 0.172, 0.168, 0.164, 0.161, 0.157, 0.154, 0.151, 0.148 ], + [ 0.352, 0.336, 0.322, 0.309, 0.296, 0.285, 0.274, 0.263, 0.254, 0.245, 0.237, 0.229, 0.222, 0.215, 0.208, 0.202, 0.197, 0.191, 0.186, 0.181, 0.176, 0.172, 0.168, 0.164, 0.160, 0.156, 0.153, 0.150, 0.146, 0.143, 0.140 ], + [ 0.351, 0.334, 0.319, 0.304, 0.290, 0.278, 0.266, 0.255, 0.245, 0.236, 0.227, 0.220, 0.212, 0.205, 0.199, 0.193, 0.187, 0.182, 0.177, 0.172, 0.167, 0.163, 0.159, 0.155, 0.151, 0.148, 0.144, 0.141, 0.138, 0.135, 0.132 ], + [ 0.350, 0.332, 0.315, 0.299, 0.284, 0.270, 0.258, 0.246, 0.236, 0.226, 0.217, 0.209, 0.202, 0.195, 0.188, 0.182, 0.177, 0.171, 0.166, 0.162, 0.157, 0.153, 0.149, 0.145, 0.142, 0.138, 0.135, 0.132, 0.129, 0.126, 0.124 ], + [ 0.350, 0.331, 0.312, 0.294, 0.277, 0.262, 0.248, 0.236, 0.225, 0.215, 0.206, 0.198, 0.190, 0.183, 0.177, 0.171, 0.165, 0.160, 0.155, 0.151, 0.146, 0.142, 0.139, 0.135, 0.132, 0.128, 0.125, 0.122, 0.119, 0.117, 0.114 ], + [ 0.351, 0.330, 0.308, 0.288, 0.269, 0.253, 0.238, 0.225, 0.213, 0.203, 0.194, 0.185, 0.178, 0.171, 0.164, 0.159, 0.153, 0.148, 0.143, 0.139, 0.135, 0.131, 0.127, 0.124, 0.120, 0.117, 0.114, 0.112, 0.109, 0.106, 0.104 ], + [ 0.354, 0.331, 0.305, 0.281, 0.260, 0.241, 0.226, 0.212, 0.200, 0.189, 0.180, 0.171, 0.164, 0.157, 0.151, 0.145, 0.140, 0.135, 0.130, 0.126, 0.122, 0.118, 0.115, 0.111, 0.108, 0.106, 0.103, 0.100, 0.098, 0.095, 0.093 ], + [ 0.364, 0.335, 0.302, 0.272, 0.248, 0.228, 0.211, 0.197, 0.184, 0.174, 0.164, 0.156, 0.148, 0.142, 0.135, 0.130, 0.125, 0.120, 0.116, 0.112, 0.108, 0.105, 0.101, 0.098, 0.095, 0.093, 0.090, 0.088, 0.086, 0.084, 0.081 ], + [ 0.388, 0.343, 0.295, 0.259, 0.232, 0.211, 0.193, 0.179, 0.166, 0.156, 0.146, 0.138, 0.131, 0.124, 0.119, 0.113, 0.109, 0.104, 0.100, 0.096, 0.093, 0.090, 0.087, 0.084, 0.082, 0.079, 0.077, 0.075, 0.073, 0.071, 0.069 ], + [ 0.493, 0.342, 0.279, 0.239, 0.211, 0.189, 0.172, 0.157, 0.145, 0.135, 0.126, 0.118, 0.111, 0.105, 0.100, 0.095, 0.091, 0.087, 0.083, 0.080, 0.077, 0.074, 0.071, 0.069, 0.067, 0.065, 0.063, 0.061, 0.059, 0.057, 0.056 ], + [ 0.361, 0.300, 0.246, 0.209, 0.182, 0.162, 0.145, 0.132, 0.121, 0.111, 0.103, 0.096, 0.090, 0.085, 0.080, 0.075, 0.072, 0.068, 0.065, 0.062, 0.059, 0.057, 0.055, 0.053, 0.051, 0.049, 0.047, 0.046, 0.044, 0.043, 0.042 ] + ], + "type":"contourcarpet", + "line":{ + "color":"rgba(0, 0, 0, 0.5)", + "smoothing":1 + }, + "contours":{ + "size":0.250, + "start":-4, + "coloring":"none", + "end":1.000, + "showlines":true + } + }, + { + "legendgroup":"g1", + "name":"Surface
pressure", + "mode":"lines", + "hoverinfo":"skip", + "y":[ 0.715, 0.712, 0.714, 0.722, 0.735, 0.751, 0.770, 0.791, 0.814, 0.837, 0.860, 0.882, 0.902, 0.921, 0.936, 0.948, 0.956, 0.960, 0.960, 0.955, 0.945, 0.930, 0.910, 0.885, 0.856, 0.821, 0.782, 0.737, 0.686, 0.629, 0.564, 0.488, + 0.396, 0.285, 0.156, 0.054, -0.048, -0.389, -0.678, -0.809, -0.865, -0.890, -0.902, -0.908, -0.910, -0.909, -0.906, -0.900, -0.892, -0.883, -0.871, -0.859, -0.845, -0.831, -0.817, -0.803, -0.789, -0.777, -0.765, -0.756, -0.747, -0.741, -0.736, -0.734, -0.732, + -0.732, -0.733, -0.734, -0.738, -0.741, 0.000, 0.000, 0.001, 0.002, 0.003, 0.003, 0.003, 0.001, -0.002, -0.007, -0.013, -0.021, -0.031, -0.043, -0.055, -0.069, -0.082, -0.096, -0.109, -0.121, -0.132, -0.140, -0.147, -0.151, -0.152, -0.150, -0.145, -0.138, + -0.127, -0.113, -0.096, -0.077, -0.055, -0.031, -0.005, 0.023, 0.053, 0.085, 0.117, 0.150, 0.183, 0.216, 0.247, 0.276, 0.302, 0.326, 0.347, 0.364, 0.377, 0.386, 0.390, 0.391, 0.386, 0.378, 0.365, 0.349, 0.329, 0.305, 0.280, 0.252, 0.223, + 0.192, 0.162, 0.133, 0.105, 0.079, 0.056, 0.036, 0.020, 0.009, 0.002 ], "x":[ 2.115, 2.088, 2.045, 1.984, 1.907, 1.815, 1.708, 1.590, 1.460, 1.320, 1.171, 1.015, 0.852, 0.684, 0.513, 0.339, 0.163, -0.014, -0.190, -0.364, -0.536, + -0.704, -0.868, -1.026, -1.178, -1.323, -1.460, -1.587, -1.705, -1.812, -1.907, -1.987, -2.049, -2.083, -2.076, -2.053, -2.214, -2.412, -2.341, -2.192, -2.048, -1.913, -1.781, -1.648, -1.510, -1.366, -1.216, -1.059, -0.895, -0.725, -0.550, -0.371, -0.189, -0.005, + 0.179, 0.361, 0.541, 0.716, 0.886, 1.047, 1.199, 1.340, 1.468, 1.582, 1.680, 1.762, 1.827, 1.873, 1.900, 2.051, "null", 1.940, 1.925, 1.890, 1.836, 1.763, 1.673, 1.567, 1.445, 1.310, 1.163, 1.005, 0.839, 0.666, 0.488, 0.307, 0.124, + -0.059, -0.240, -0.418, -0.592, -0.759, -0.920, -1.072, -1.214, -1.347, -1.468, -1.577, -1.674, -1.758, -1.828, -1.884, -1.925, -1.952, -1.964, -1.962, -1.945, -1.913, -1.869, -1.812, -1.742, -1.661, -1.569, -1.467, -1.355, -1.234, -1.106, -0.970, -0.828, -0.680, + -0.528, -0.372, -0.213, -0.052, 0.109, 0.270, 0.430, 0.588, 0.742, 0.892, 1.037, 1.175, 1.306, 1.427, 1.539, 1.640, 1.728, 1.803, 1.863, 1.906, 1.933 + ], + "line":{ + "color":"rgba(255, 0, 0, 0.5)", + "width":1, + "shape":"spline", + "smoothing":1 + }, + "fill":"toself", + "type":"scatter", + "fillcolor":"rgba(255, 0, 0, 0.2)" + }, + { + "showlegend":false, + "legendgroup":"g1", + "mode":"lines", + "hoverinfo":"skip", + "y":[ + 0.009, 0.712, 0.000, 0.056, 0.735, 0.000, 0.133, 0.791, 0.000, 0.223, 0.860, 0.000, 0.305, 0.921, 0.000, 0.365, 0.956, 0.000, 0.391, 0.955, 0.000, 0.377, 0.910, 0.000, 0.326, 0.821, 0.000, 0.247, 0.686, 0.000, 0.150, 0.488, 0.000, + 0.053, 0.156, 0.000, -0.031, -0.389, 0.000, -0.096, -0.865, 0.000, -0.138, -0.908, 0.000, -0.152, -0.906, 0.000, -0.140, -0.883, 0.000, -0.109, -0.845, 0.000, -0.069, -0.803, 0.000, -0.031, -0.765, 0.000, -0.007, -0.741, 0.000, 0.003, -0.732, 0.000, + 0.002, -0.734, 0.000 + ], + "x":[ 1.906, 2.088, "null", 1.728, 1.907, "null", 1.427, 1.590, "null", 1.037, 1.171, "null", 0.588, 0.684, "null", 0.109, 0.163, "null", -0.372, -0.364, "null", -0.828, -0.868, "null", -1.234, -1.323, "null", -1.569, -1.705, "null", -1.812, -1.987, + "null", -1.945, -2.076, "null", -1.952, -2.412, "null", -1.828, -2.048, "null", -1.577, -1.648, "null", -1.214, -1.216, "null", -0.759, -0.725, "null", -0.240, -0.189, "null", 0.307, 0.361, "null", 0.839, 0.886, "null", 1.310, 1.340, "null", 1.673, 1.680, + "null", 1.890, 1.873, "null" + ], + "line":{ + "color":"rgba(255, 0, 0, 0.3)", + "width":1 + }, + "type":"scatter" + }, + { + "showlegend":false, + "legendgroup":"g1", + "name":"cp", + "text":[ + "cp = 0.36", "cp = 0.26", "cp = 0.18", "cp = 0.1", "cp = 0.024", "cp = -0.055", "cp = -0.14", "cp = -0.22", "cp = -0.31", "cp = -0.4", + "cp = -0.49", "cp = -0.58", "cp = -0.68", "cp = -0.77", "cp = -0.87", "cp = -0.97", "cp = -1.1", "cp = -1.2", "cp = -1.3", "cp = -1.4", + "cp = -1.5", "cp = -1.6", "cp = -1.7", "cp = -1.8", "cp = -1.9", "cp = -2", "cp = -2.1", "cp = -2.2", "cp = -2.4", "cp = -2.6", + "cp = -2.9", "cp = -3.2", "cp = -3.7", "cp = -4.4", "cp = -5.3", "cp = -6", "cp = -4.5", "cp = -1.2", "cp = 0.49", "cp = 0.95", + "cp = 0.99", "cp = 0.93", "cp = 0.83", "cp = 0.74", "cp = 0.66", "cp = 0.59", "cp = 0.54", "cp = 0.5", "cp = 0.46", "cp = 0.43", + "cp = 0.41", "cp = 0.39", "cp = 0.38", "cp = 0.37", "cp = 0.36", "cp = 0.36", "cp = 0.36", "cp = 0.36", "cp = 0.35", "cp = 0.35", + "cp = 0.35", "cp = 0.35", "cp = 0.35", "cp = 0.35", "cp = 0.35", "cp = 0.35", "cp = 0.35", "cp = 0.36", "cp = 0.39", "cp = 0.49", + "cp = 0.36" + ], + "mode":"lines", + "hoverinfo":"text", + "y":[ + 0.715, 0.712, 0.714, 0.722, 0.735, 0.751, 0.770, 0.791, 0.814, 0.837, 0.860, 0.882, 0.902, 0.921, 0.936, 0.948, 0.956, 0.960, 0.960, 0.955, 0.945, 0.930, 0.910, 0.885, 0.856, 0.821, 0.782, 0.737, 0.686, 0.629, 0.564, 0.488, 0.396, + 0.285, 0.156, 0.054, -0.048, -0.389, -0.678, -0.809, -0.865, -0.890, -0.902, -0.908, -0.910, -0.909, -0.906, -0.900, -0.892, -0.883, -0.871, -0.859, -0.845, -0.831, -0.817, -0.803, -0.789, -0.777, -0.765, -0.756, -0.747, -0.741, + -0.736, -0.734, -0.732, -0.732, -0.733, -0.734, -0.738, -0.741 + ], + "x":[ + 2.115, 2.088, 2.045, 1.984, 1.907, 1.815, 1.708, 1.590, 1.460, 1.320, 1.171, 1.015, 0.852, 0.684, 0.513, 0.339, 0.163, -0.014, -0.190, -0.364, -0.536, -0.704, -0.868, -1.026, -1.178, -1.323, -1.460, -1.587, -1.705, -1.812, -1.907, + -1.987, -2.049, -2.083, -2.076, -2.053, -2.214, -2.412, -2.341, -2.192, -2.048, -1.913, -1.781, -1.648, -1.510, -1.366, -1.216, -1.059, -0.895, -0.725, -0.550, -0.371, -0.189, -0.005, 0.179, 0.361, 0.541, 0.716, 0.886, 1.047, 1.199, + 1.340, 1.468, 1.582, 1.680, 1.762, 1.827, 1.873, 1.900, 2.051 + ], + "line":{ + "color":"rgba(255, 0, 0, 0.2)", + "width":0 + }, + "type":"scatter" + } + ] +} diff --git a/test/image/mocks/carpet_axis.json b/test/image/mocks/carpet_axis.json new file mode 100644 index 00000000000..b55211fc334 --- /dev/null +++ b/test/image/mocks/carpet_axis.json @@ -0,0 +1,271 @@ +{ + "data": [ + { + "type": "carpet", + "a": [0, 5e-7, 0.000001], + "b": [0, 500000, 1000000], + "y": [ + [1.00000, 1.32287, 1.73205], + [1.32287, 1.58113, 1.93649], + [1.73205, 1.93649, 2.23606] + ], + "aaxis": { + "title": "length, l, m", + "ticksuffix": "m", + "tickformat": ".3s", + "tickmode": "linear", + "tick0": 1e-7, + "dtick": 2e-7, + "minorgridcount": 3, + "type": "linear", + "smoothing": 1 + }, + "baxis": { + "title": "pressure, p, Pa", + "ticksuffix": "Pa", + "tickformat": ".3s", + "tickmode": "linear", + "tick0": 100000, + "dtick": 200000, + "minorgridcount": 3, + "type": "linear", + "smoothing": 1 + }, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "a": [0, 1e-7, 2e-7, 3e-7, 4e-7, 5e-7, 6e-7, 7e-7, 8e-7, 9e-7, 0.000001], + "b": [ + 0, 50000, 100000, 150000, 200000, 250000, 300000, 350000, 400000, 450000, 500000, 550000, + 600000, 650000, 700000, 750000, 800000, 850000, 900000, 950000, 1000000 + ], + "y": [ + [1.0000, 1.0535, 1.1135, 1.1789, 1.2489, 1.3228, 1.4000, 1.4798, 1.5620, 1.6462, 1.7320], + [1.0259, 1.0781, 1.1368, 1.2010, 1.2698, 1.3425, 1.4186, 1.4974, 1.5787, 1.6620, 1.7471], + [1.0535, 1.1045, 1.1618, 1.2247, 1.2922, 1.3638, 1.4387, 1.5165, 1.5968, 1.6792, 1.7635], + [1.0828, 1.1324, 1.1884, 1.2500, 1.3162, 1.3865, 1.4603, 1.5370, 1.6163, 1.6977, 1.7811], + [1.1135, 1.1618, 1.2165, 1.2767, 1.3416, 1.4106, 1.4832, 1.5588, 1.6370, 1.7175, 1.8000], + [1.1456, 1.1926, 1.2459, 1.3047, 1.3683, 1.4361, 1.5074, 1.5819, 1.6590, 1.7385, 1.8200], + [1.1789, 1.2247, 1.2767, 1.3341, 1.3964, 1.4628, 1.5329, 1.6062, 1.6822, 1.7606, 1.8411], + [1.2134, 1.2579, 1.3086, 1.3647, 1.4256, 1.4908, 1.5596, 1.6317, 1.7066, 1.7839, 1.8634], + [1.2489, 1.2922, 1.3416, 1.3964, 1.4560, 1.5198, 1.5874, 1.6583, 1.7320, 1.8083, 1.8867], + [1.2854, 1.3275, 1.3756, 1.4291, 1.4874, 1.5500, 1.6163, 1.6859, 1.7585, 1.8337, 1.9111], + [1.3228, 1.3638, 1.4106, 1.4628, 1.5198, 1.5811, 1.6462, 1.7146, 1.7860, 1.8601, 1.9364], + [1.3610, 1.4008, 1.4465, 1.4974, 1.5532, 1.6132, 1.6770, 1.7442, 1.8145, 1.8874, 1.9627], + [1.4000, 1.4387, 1.4832, 1.5329, 1.5874, 1.6462, 1.7088, 1.7748, 1.8439, 1.9157, 1.9899], + [1.4396, 1.4773, 1.5206, 1.5692, 1.6224, 1.6800, 1.7414, 1.8062, 1.8741, 1.9448, 2.0180], + [1.4798, 1.5165, 1.5588, 1.6062, 1.6583, 1.7146, 1.7748, 1.8384, 1.9052, 1.9748, 2.0469], + [1.5206, 1.5564, 1.5976, 1.6439, 1.6948, 1.7500, 1.8090, 1.8714, 1.9371, 2.0056, 2.0766], + [1.5620, 1.5968, 1.6370, 1.6822, 1.7320, 1.7860, 1.8439, 1.9052, 1.9697, 2.0371, 2.1071], + [1.6039, 1.6378, 1.6770, 1.7211, 1.7698, 1.8227, 1.8794, 1.9397, 2.0031, 2.0694, 2.1383], + [1.6462, 1.6792, 1.7175, 1.7606, 1.8083, 1.8601, 1.9157, 1.9748, 2.0371, 2.1023, 2.1702], + [1.6889, 1.7211, 1.7585, 1.8006, 1.8472, 1.8980, 1.9525, 2.0105, 2.0718, 2.1360, 2.2028], + [1.7320, 1.7635, 1.8000, 1.8411, 1.8867, 1.9364, 1.9899, 2.0469, 2.1071, 2.1702, 2.2360] + ], + "aaxis": { + "title": "length, l, m", + "ticksuffix": "m", + "tickformat": ".3s", + "tickmode": "array", + "arraytick0": 1, + "arraydtick": 2, + "minorgridcount": 3, + "type": "linear", + "smoothing": 0 + }, + "baxis": { + "title": "pressure, p, Pa", + "ticksuffix": "Pa", + "tickformat": ".3s", + "tickmode": "array", + "arraytick0": 2, + "arraydtick": 4, + "minorgridcount": 3, + "type": "linear", + "smoothing": 0 + }, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "a": [0, 5e-7, 0.000001], + "b": [0, 500000, 1000000], + "x": [[0.25,0.5,1],[-0.5,0,0.5],[-1,-0.5,0]], + "y": [ + [1.00000, 1.32287, 1.73205], + [1.32287, 1.58113, 1.93649], + [1.73205, 1.93649, 2.23606] + ], + "aaxis": { + "title": "length, l, m", + "ticksuffix": "m", + "tickformat": ".3s", + "tickmode": "linear", + "tick0": 1e-7, + "dtick": 2e-7, + "minorgridcount": 3, + "type": "linear", + "smoothing": 1 + }, + "baxis": { + "title": "pressure, p, Pa", + "ticksuffix": "Pa", + "tickformat": ".3s", + "tickmode": "linear", + "tick0": 100000, + "dtick": 200000, + "minorgridcount": 3, + "type": "linear", + "smoothing": 1 + }, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "a": [0, 1e-7, 2e-7, 3e-7, 4e-7, 5e-7, 6e-7, 7e-7, 8e-7, 9e-7, 0.000001], + "b": [ + 0, 50000, 100000, 150000, 200000, 250000, 300000, 350000, 400000, 450000, 500000, 550000, + 600000, 650000, 700000, 750000, 800000, 850000, 900000, 950000, 1000000 + ], + "x": [ + [ 0.25, 0.30, 0.35, 0.40, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, 1.00], + [ 0.15, 0.2, 0.25, 0.30, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95], + [ 0.05, 0.10, 0.15, 0.20, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.90], + [-0.05, 0.00, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 0.85], + [-0.15, -0.10, 0.00, 0.10, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.80], + [-0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75], + [-0.30, -0.20, -0.10, 0.00, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.70], + [-0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65], + [-0.40, -0.30, -0.20, -0.10, 0.00, 0.1, 0.2, 0.3, 0.4, 0.5, 0.60], + [-0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55], + [-0.50, -0.40, -0.30, -0.20, -0.1, 0.00, 0.1, 0.2, 0.3, 0.4, 0.50], + [-0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45], + [-0.60, -0.50, -0.40, -0.30, -0.2, -0.1, 0.00, 0.1, 0.2, 0.3, 0.40], + [-0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25, 0.35], + [-0.70, -0.60, -0.50, -0.40, -0.3, -0.2, -0.1, 0.00, 0.1, 0.2, 0.30], + [-0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25], + [-0.80, -0.70, -0.60, -0.50, -0.4, -0.3, -0.2, -0.1, 0.00, 0.1, 0.20], + [-0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05, 0.15], + [-0.90, -0.80, -0.70, -0.60, -0.5, -0.4, -0.3, -0.2, -0.1, 0.00, 0.10], + [-0.95, -0.85, -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, 0.05], + [-1.00, -0.90, -0.80, -0.70, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.00] + ], + "y": [ + [1.0000, 1.0535, 1.1135, 1.1789, 1.2489, 1.3228, 1.4000, 1.4798, 1.5620, 1.6462, 1.7320], + [1.0259, 1.0781, 1.1368, 1.2010, 1.2698, 1.3425, 1.4186, 1.4974, 1.5787, 1.6620, 1.7471], + [1.0535, 1.1045, 1.1618, 1.2247, 1.2922, 1.3638, 1.4387, 1.5165, 1.5968, 1.6792, 1.7635], + [1.0828, 1.1324, 1.1884, 1.2500, 1.3162, 1.3865, 1.4603, 1.5370, 1.6163, 1.6977, 1.7811], + [1.1135, 1.1618, 1.2165, 1.2767, 1.3416, 1.4106, 1.4832, 1.5588, 1.6370, 1.7175, 1.8000], + [1.1456, 1.1926, 1.2459, 1.3047, 1.3683, 1.4361, 1.5074, 1.5819, 1.6590, 1.7385, 1.8200], + [1.1789, 1.2247, 1.2767, 1.3341, 1.3964, 1.4628, 1.5329, 1.6062, 1.6822, 1.7606, 1.8411], + [1.2134, 1.2579, 1.3086, 1.3647, 1.4256, 1.4908, 1.5596, 1.6317, 1.7066, 1.7839, 1.8634], + [1.2489, 1.2922, 1.3416, 1.3964, 1.4560, 1.5198, 1.5874, 1.6583, 1.7320, 1.8083, 1.8867], + [1.2854, 1.3275, 1.3756, 1.4291, 1.4874, 1.5500, 1.6163, 1.6859, 1.7585, 1.8337, 1.9111], + [1.3228, 1.3638, 1.4106, 1.4628, 1.5198, 1.5811, 1.6462, 1.7146, 1.7860, 1.8601, 1.9364], + [1.3610, 1.4008, 1.4465, 1.4974, 1.5532, 1.6132, 1.6770, 1.7442, 1.8145, 1.8874, 1.9627], + [1.4000, 1.4387, 1.4832, 1.5329, 1.5874, 1.6462, 1.7088, 1.7748, 1.8439, 1.9157, 1.9899], + [1.4396, 1.4773, 1.5206, 1.5692, 1.6224, 1.6800, 1.7414, 1.8062, 1.8741, 1.9448, 2.0180], + [1.4798, 1.5165, 1.5588, 1.6062, 1.6583, 1.7146, 1.7748, 1.8384, 1.9052, 1.9748, 2.0469], + [1.5206, 1.5564, 1.5976, 1.6439, 1.6948, 1.7500, 1.8090, 1.8714, 1.9371, 2.0056, 2.0766], + [1.5620, 1.5968, 1.6370, 1.6822, 1.7320, 1.7860, 1.8439, 1.9052, 1.9697, 2.0371, 2.1071], + [1.6039, 1.6378, 1.6770, 1.7211, 1.7698, 1.8227, 1.8794, 1.9397, 2.0031, 2.0694, 2.1383], + [1.6462, 1.6792, 1.7175, 1.7606, 1.8083, 1.8601, 1.9157, 1.9748, 2.0371, 2.1023, 2.1702], + [1.6889, 1.7211, 1.7585, 1.8006, 1.8472, 1.8980, 1.9525, 2.0105, 2.0718, 2.1360, 2.2028], + [1.7320, 1.7635, 1.8000, 1.8411, 1.8867, 1.9364, 1.9899, 2.0469, 2.1071, 2.1702, 2.2360] + ], + "aaxis": { + "title": "length, l, m", + "ticksuffix": "m", + "tickformat": ".3s", + "tickmode": "array", + "arraytick0": 1, + "arraydtick": 2, + "minorgridcount": 3, + "type": "linear", + "smoothing": 0 + }, + "baxis": { + "title": "pressure, p, Pa", + "ticksuffix": "Pa", + "tickformat": ".3s", + "tickmode": "array", + "arraytick0": 2, + "arraydtick": 4, + "minorgridcount": 3, + "type": "linear", + "smoothing": 0 + }, + "xaxis": "x2", + "yaxis": "y2" + } + ], + "layout": { + "width": 800, + "height": 800, + "annotations": [ + { + "xref": "x", + "yref": "y", + "showarrow": false, + "x": 0.5, + "y": 2.3, + "text": "coarse/smooth grid + cheater" + }, + { + "xref": "x", + "yref": "y2", + "showarrow": false, + "x": 0.5, + "y": 2.3, + "text": "fine/unsmoothed grid + cheater" + }, + { + "xref": "x2", + "yref": "y", + "showarrow": false, + "x": 0, + "y": 2.3, + "text": "coarse/smooth grid + carpet" + }, + { + "xref": "x2", + "yref": "y2", + "showarrow": false, + "x": 0, + "y": 2.3, + "text": "fine/unsmoothed grid + carpet" + } + ], + "margin": { + "t": 40, + "r": 20, + "b": 40, + "l": 40 + }, + "dragmode": "pan", + "xaxis": { + "domain": [0, 0.48], + "range": [-1.4444, 1.4444], + "autorange": true + }, + "xaxis2": { + "domain": [0.52, 1], + "range": [-1.4444, 1.4444], + "autorange": true + }, + "yaxis": { + "range": [0.7253, 2.5107], + "domain": [0, 0.48], + "autorange": true + }, + "yaxis2": { + "range": [0.7253, 2.5107], + "domain": [0.52, 1], + "autorange": true + } + } +} diff --git a/test/image/mocks/cheater.json b/test/image/mocks/cheater.json new file mode 100644 index 00000000000..60854f35495 --- /dev/null +++ b/test/image/mocks/cheater.json @@ -0,0 +1,147 @@ +{ + "data":[ + { + "type":"carpet", + "name": "carpet axis", + "a":[ + 0.1, 0.1, 0.1, 0.1, + 0.3, 0.3, 0.3, 0.3, + 0.5, 0.5, 0.5, 0.5 + ], + "b":[ + 1.01, 1.24, 1.56, 1.82, + 1.01, 1.24, 1.56, 1.82, + 1.01, 1.24, 1.56, 1.82 + ], + "y":[ + 4, 4.4, 4.8, 5, + 6.2, 6.6, 7, 7.2, + 8.8, 9.2, 9.6, 9.8 + ], + "cheaterslope":2, + "aaxis":{ + "title":"width, cm", + "tickformat":".1f", + "type":"linear", + "smoothing": 0 + }, + "baxis":{ + "title":"height, cm", + "tickformat":".2f", + "type":"linear", + "smoothing": 0 + } + }, + { + "type":"contourcarpet", + "name":"Power", + "z":[ + 100, 120, 180, 260, + 300, 320, 380, 460, + 500, 520, 580, 660 + ], + "opacity":0.4, + "contours":{ + "type":"constraint", + "operation":"][", + "value":[ + 400, + 540 + ] + }, + "line": { + "smoothing": 0 + }, + "colorscale":[ + [ 0, "red" ], + [ 1, "red" ] + ], + "a":[ + 0.1, 0.1, 0.1, 0.1, + 0.3, 0.3, 0.3, 0.3, + 0.5, 0.5, 0.5, 0.5 + ], + "b":[ + 1.01, 1.24, 1.56, 1.82, + 1.01, 1.24, 1.56, 1.82, + 1.01, 1.24, 1.56, 1.82 + ] + }, + { + "type":"contourcarpet", + "name":"Strength", + "z":[ + 0.1, 0.3, 0.5, 0.6, + 0.14, 0.34, 0.54, 0.64, + 0.18, 0.38, 0.58, 0.68 + ], + "opacity":0.4, + "contours":{ + "type":"constraint", + "operation":"<", + "value":[ + 0.5 + ] + }, + "line": { + "smoothing": 0 + }, + "colorscale":[ + [ 0, "blue" ], + [ 1, "blue" ] + ], + "uid":"977b5b", + "a":[ + 0.1, 0.1, 0.1, 0.1, + 0.3, 0.3, 0.3, 0.3, + 0.5, 0.5, 0.5, 0.5 + ], + "b":[ + 1.01, 1.24, 1.56, 1.82, + 1.01, 1.24, 1.56, 1.82, + 1.01, 1.24, 1.56, 1.82 + ] + }, + { + "type":"contourcarpet", + "name":"Agility", + "z":[ + 800, 802, 804, 805, + 820, 822, 824, 825, + 840, 842, 844, 845 + ], + "opacity":0.4, + "contours":{ + "type":"constraint", + "operation":">", + "value":810 + }, + "colorscale":[ + [ 0, "green" ], + [ 1, "green" ] + ], + "line": { + "smoothing": 0 + }, + "a":[ + 0.1, 0.1, 0.1, 0.1, + 0.3, 0.3, 0.3, 0.3, + 0.5, 0.5, 0.5, 0.5 + ], + "b":[ + 1.01, 1.24, 1.56, 1.82, + 1.01, 1.24, 1.56, 1.82, + 1.01, 1.24, 1.56, 1.82 + ] + } + ], + "layout":{ + "dragmode":"pan", + "width":800, + "height":600, + "yaxis":{ + "tickprefix":"€", + "tickformat":".2f" + } + } +} diff --git a/test/image/mocks/cheater_constraint_greater_than.json b/test/image/mocks/cheater_constraint_greater_than.json new file mode 100644 index 00000000000..94935d8ff41 --- /dev/null +++ b/test/image/mocks/cheater_constraint_greater_than.json @@ -0,0 +1,264 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 5 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 5 + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 3 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 3 + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 1 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": ">", + "value": 1 + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": -1 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": ">", + "value": -1 + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + } + ], + "layout": { + "title": "Greater than constraint", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.24] + }, + "yaxis2": { + "domain": [0.26, 0.49] + }, + "yaxis3": { + "domain": [0.51, 0.74] + }, + "yaxis4": { + "domain": [0.76, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y", + "text": "y > -1:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "y > 1:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "y > 3:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "y > 5:", + "showarrow": false, + "font": { + "size": 20 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_greater_than_with_hill.json b/test/image/mocks/cheater_constraint_greater_than_with_hill.json new file mode 100644 index 00000000000..568f9ded846 --- /dev/null +++ b/test/image/mocks/cheater_constraint_greater_than_with_hill.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 7 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 7 + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 5 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 5 + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 3 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 3 + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 1 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": ">", + "value": 1 + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": -1 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": ">", + "value": -1 + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Greater than constraint with z = 6 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y", + "text": "z > -1:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "z > 1:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "z > 3:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "z > 5:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "z > 7:", + "showarrow": false, + "font": { + "size": 20 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_greater_than_with_valley.json b/test/image/mocks/cheater_constraint_greater_than_with_valley.json new file mode 100644 index 00000000000..a931ca5f04f --- /dev/null +++ b/test/image/mocks/cheater_constraint_greater_than_with_valley.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 5 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 5 + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 3 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 3 + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 1 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": 1 + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": -1 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": ">", + "value": -1 + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": ">", + "value": -3 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": ">", + "value": -3 + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Greater than constraint with z = -2 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y", + "text": "z > -3:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "z > -1:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "z > 1:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "z > 3:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "z > 5:", + "showarrow": false, + "font": { + "size": 20 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_inner_range.json b/test/image/mocks/cheater_constraint_inner_range.json new file mode 100644 index 00000000000..44ca790a075 --- /dev/null +++ b/test/image/mocks/cheater_constraint_inner_range.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [5.5, 6.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [5.5, 6.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [3.5, 4.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [3.5, 4.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [1.5, 2.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [1.5, 2.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-0.5, 0.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-0.5, 0.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-2.5, -1.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-2.5, -1.5] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Inner range constraint", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "-2.5 < y < -1.5:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "-0.5 < y < 0.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "1.5 < y < 2.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "3.5 < y < 4.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "5.5 < y < 6.5:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_inner_range_hi_top.json b/test/image/mocks/cheater_constraint_inner_range_hi_top.json new file mode 100644 index 00000000000..3d44d105339 --- /dev/null +++ b/test/image/mocks/cheater_constraint_inner_range_hi_top.json @@ -0,0 +1,264 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [5, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [5, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [3, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [3, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [1, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [1, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-1, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-1, 100] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + } + ], + "layout": { + "title": "Inner range constraint", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.24] + }, + "yaxis2": { + "domain": [0.26, 0.49] + }, + "yaxis3": { + "domain": [0.51, 0.74] + }, + "yaxis4": { + "domain": [0.76, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "-1 < y < 100:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "1 < y < 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "3 < y < 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "5 < y < 100:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_inner_range_hi_top_with_hill.json b/test/image/mocks/cheater_constraint_inner_range_hi_top_with_hill.json new file mode 100644 index 00000000000..bebd6b0f5ec --- /dev/null +++ b/test/image/mocks/cheater_constraint_inner_range_hi_top_with_hill.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [7, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [7, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [5, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [5, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [3, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [3, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [1, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [1, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-1, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-1, 100] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Inner range constraint with z = 6 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "-1 < z < 100:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "1 < z < 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "3 < z < 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "5 < z < 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "7 < z < 100:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_inner_range_hi_top_with_valley.json b/test/image/mocks/cheater_constraint_inner_range_hi_top_with_valley.json new file mode 100644 index 00000000000..f5067f4c9d3 --- /dev/null +++ b/test/image/mocks/cheater_constraint_inner_range_hi_top_with_valley.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [5, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [5, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [3, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [3, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [1, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [1, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-1, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-1, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-3, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-3, 100] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Inner range constraint with z = -2 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "-3 < z < 100:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "-1 < z < 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "1 < z < 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "3 < z < 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "5 < z < 100:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_inner_range_lo_top.json b/test/image/mocks/cheater_constraint_inner_range_lo_top.json new file mode 100644 index 00000000000..6b3f9018527 --- /dev/null +++ b/test/image/mocks/cheater_constraint_inner_range_lo_top.json @@ -0,0 +1,264 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 5] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 3] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 3] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 1] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 1] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, -1] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, -1] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + } + ], + "layout": { + "title": "Inner range constraint", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.24] + }, + "yaxis2": { + "domain": [0.26, 0.49] + }, + "yaxis3": { + "domain": [0.51, 0.74] + }, + "yaxis4": { + "domain": [0.76, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "-100 < y < -1:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "-100 < y < 1:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "-100 < y < 3:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "-100 < y < 5:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_inner_range_lo_top_with_hill.json b/test/image/mocks/cheater_constraint_inner_range_lo_top_with_hill.json new file mode 100644 index 00000000000..54ac1e33669 --- /dev/null +++ b/test/image/mocks/cheater_constraint_inner_range_lo_top_with_hill.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 7] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 7] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 5] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 3] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 3] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 1] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 1] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, -1] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, -1] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Inner range constraint with z = 6 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "-100 < z < -1:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "-100 < z < 1:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "-100 < z < 3:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "-100 < z < 5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "-100 < z < 7:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_inner_range_lo_top_with_valley.json b/test/image/mocks/cheater_constraint_inner_range_lo_top_with_valley.json new file mode 100644 index 00000000000..bb8bdf7197d --- /dev/null +++ b/test/image/mocks/cheater_constraint_inner_range_lo_top_with_valley.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 5] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 3] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 3] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 1] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, 1] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, -1] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, -1] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, -3] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-100, -3] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Inner range constraint with z = -2 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "-100 < z < -3:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "-100 < z < -1:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "-100 < z < 1:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "-100 < z < 3:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "-100 < z < 5:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_inner_range_with_hill.json b/test/image/mocks/cheater_constraint_inner_range_with_hill.json new file mode 100644 index 00000000000..50478f95e8d --- /dev/null +++ b/test/image/mocks/cheater_constraint_inner_range_with_hill.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [5.5, 6.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [5.5, 6.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [3.5, 4.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [3.5, 4.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [1.5, 2.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [1.5, 2.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-0.5, 0.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-0.5, 0.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-2.5, -1.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-2.5, -1.5] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Inner range constraint with z = 6 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "-2.5 < z < -1.5:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "-0.5 < z < 0.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "1.5 < z < 2.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "3.5 < z < 4.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "5.5 < z < 6.5:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_inner_range_with_valley.json b/test/image/mocks/cheater_constraint_inner_range_with_valley.json new file mode 100644 index 00000000000..b629c692002 --- /dev/null +++ b/test/image/mocks/cheater_constraint_inner_range_with_valley.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [5.5, 6.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [5.5, 6.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [3.5, 4.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [3.5, 4.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [1.5, 2.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [1.5, 2.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-0.5, 0.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-0.5, 0.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-2.5, -1.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "[]", + "value": [-2.5, -1.5] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Inner range constraint with z = -2 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "-2.5 < z < -1.5:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "-0.5 < z < 0.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "1.5 < z < 2.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "3.5 < z < 4.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "5.5 < z < 6.5:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_less_than.json b/test/image/mocks/cheater_constraint_less_than.json new file mode 100644 index 00000000000..bfb8ffafac6 --- /dev/null +++ b/test/image/mocks/cheater_constraint_less_than.json @@ -0,0 +1,264 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 5 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 5 + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 3 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 3 + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 1 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "<", + "value": 1 + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": -1 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "<", + "value": -1 + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + } + ], + "layout": { + "title": "Less than constraint", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.24] + }, + "yaxis2": { + "domain": [0.26, 0.49] + }, + "yaxis3": { + "domain": [0.51, 0.74] + }, + "yaxis4": { + "domain": [0.76, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y", + "text": "y < -1:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "y < 1:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "y < 3:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "y < 5:", + "showarrow": false, + "font": { + "size": 20 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_less_than_with_hill.json b/test/image/mocks/cheater_constraint_less_than_with_hill.json new file mode 100644 index 00000000000..c733477a956 --- /dev/null +++ b/test/image/mocks/cheater_constraint_less_than_with_hill.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 7 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 7 + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 5 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 5 + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 3 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 3 + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 1 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "<", + "value": 1 + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": -1 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "<", + "value": -1 + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Less than constraint with z = 6 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y", + "text": "z < -1:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "z < 1:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "z < 3:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "z < 5:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "z < 7:", + "showarrow": false, + "font": { + "size": 20 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_less_than_with_valley.json b/test/image/mocks/cheater_constraint_less_than_with_valley.json new file mode 100644 index 00000000000..21160ac8aa5 --- /dev/null +++ b/test/image/mocks/cheater_constraint_less_than_with_valley.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 5 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 5 + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 3 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 3 + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 1 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": 1 + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": -1 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "<", + "value": -1 + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "<", + "value": -3 + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "<", + "value": -3 + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Less than constraint with z = -2 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y", + "text": "z < -3:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "z < -1:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "z < 1:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "z < 3:", + "showarrow": false, + "font": { + "size": 20 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "z < 5:", + "showarrow": false, + "font": { + "size": 20 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_outer_range.json b/test/image/mocks/cheater_constraint_outer_range.json new file mode 100644 index 00000000000..35fca3cf05c --- /dev/null +++ b/test/image/mocks/cheater_constraint_outer_range.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [5.5, 6.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [5.5, 6.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [3.5, 4.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [3.5, 4.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [1.5, 2.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [1.5, 2.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-0.5, 0.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-0.5, 0.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-2.5, -1.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-2.5, -1.5] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Outer range constraint", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "y < -2.5 | y > -1.5:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "y < -0.5 | y > 0.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "y < 1.5 | y > 2.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "y < 3.5 | y > 4.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "y < 5.5 | y > 6.5:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_outer_range_hi_top.json b/test/image/mocks/cheater_constraint_outer_range_hi_top.json new file mode 100644 index 00000000000..379e442a757 --- /dev/null +++ b/test/image/mocks/cheater_constraint_outer_range_hi_top.json @@ -0,0 +1,264 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [5, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [5, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [3, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [3, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [1, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [1, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-1, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-1, 100] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + } + ], + "layout": { + "title": "Outer range constraint", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.24] + }, + "yaxis2": { + "domain": [0.26, 0.49] + }, + "yaxis3": { + "domain": [0.51, 0.74] + }, + "yaxis4": { + "domain": [0.76, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "y < -1 | y > 100:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "y < 1 | y > 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "y < 3 | y > 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "y < 5 | y > 100:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_outer_range_hi_top_with_hill.json b/test/image/mocks/cheater_constraint_outer_range_hi_top_with_hill.json new file mode 100644 index 00000000000..43f8b36a7a2 --- /dev/null +++ b/test/image/mocks/cheater_constraint_outer_range_hi_top_with_hill.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [7, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [7, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [5, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [5, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [3, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [3, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [1, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [1, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-1, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-1, 100] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Outer range constraint with z = 6 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "z < -1 | z > 100:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "z < 1 | z > 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "z < 3 | z > 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "z < 5 | z > 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "z < 7 | z > 100:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_outer_range_hi_top_with_valley.json b/test/image/mocks/cheater_constraint_outer_range_hi_top_with_valley.json new file mode 100644 index 00000000000..cffecc142b4 --- /dev/null +++ b/test/image/mocks/cheater_constraint_outer_range_hi_top_with_valley.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [5, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [5, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [3, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [3, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [1, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [1, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-1, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-1, 100] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-3, 100] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-3, 100] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Outer range constraint with z = -2 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "z < -3 | z > 100:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "z < -1 | z > 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "z < 1 | z > 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "z < 3 | z > 100:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "z < 5 | z > 100:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_outer_range_lo_top.json b/test/image/mocks/cheater_constraint_outer_range_lo_top.json new file mode 100644 index 00000000000..c085b99f55f --- /dev/null +++ b/test/image/mocks/cheater_constraint_outer_range_lo_top.json @@ -0,0 +1,264 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 5] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 3] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 3] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 1] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 1] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, -1] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, -1] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + } + ], + "layout": { + "title": "Outer range constraint", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.24] + }, + "yaxis2": { + "domain": [0.26, 0.49] + }, + "yaxis3": { + "domain": [0.51, 0.74] + }, + "yaxis4": { + "domain": [0.76, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "y < -100 | y > -1:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "y < -100 | y > 1:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "y < -100 | y > 3:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "y < -100 | y > 5:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_outer_range_lo_top_with_hill.json b/test/image/mocks/cheater_constraint_outer_range_lo_top_with_hill.json new file mode 100644 index 00000000000..00865938b86 --- /dev/null +++ b/test/image/mocks/cheater_constraint_outer_range_lo_top_with_hill.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 7] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 7] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 5] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 3] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 3] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 1] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 1] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, -1] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, -1] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Outer range constraint with z = 6 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "z < -100 | z > -1:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "z < -100 | z > 1:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "z < -100 | z > 3:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "z < -100 | z > 5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "z < -100 | z > 7:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_outer_range_lo_top_with_valley.json b/test/image/mocks/cheater_constraint_outer_range_lo_top_with_valley.json new file mode 100644 index 00000000000..7e2cac53a2b --- /dev/null +++ b/test/image/mocks/cheater_constraint_outer_range_lo_top_with_valley.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 5] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 3] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 3] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 1] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, 1] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, -1] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, -1] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, -3] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-100, -3] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Outer range constraint with z = -2 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "z < -100 | z > -3:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "z < -100 | z > -1:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "z < -100 | z > 1:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "z < -100 | z > 3:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "z < -100 | z > 5:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_outer_range_with_hill.json b/test/image/mocks/cheater_constraint_outer_range_with_hill.json new file mode 100644 index 00000000000..e75332f7663 --- /dev/null +++ b/test/image/mocks/cheater_constraint_outer_range_with_hill.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [5.5, 6.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [5.5, 6.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [3.5, 4.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [3.5, 4.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [1.5, 2.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [1.5, 2.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-0.5, 0.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-0.5, 0.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-2.5, -1.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, 6, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-2.5, -1.5] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Outer range constraint with z = 6 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "z < -2.5 | z > -1.5:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "z < -0.5 | z > 0.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "z < 1.5 | z > 2.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "z < 3.5 | z > 4.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "z < 5.5 | z > 6.5:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraint_outer_range_with_valley.json b/test/image/mocks/cheater_constraint_outer_range_with_valley.json new file mode 100644 index 00000000000..f0045518e55 --- /dev/null +++ b/test/image/mocks/cheater_constraint_outer_range_with_valley.json @@ -0,0 +1,324 @@ +{ + "data": [ + { + "type": "contourcarpet", + "carpet": "c9", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [5.5, 6.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c10", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [5.5, 6.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c7", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [3.5, 4.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c8", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [3.5, 4.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c5", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [1.5, 2.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c6", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [1.5, 2.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c3", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-0.5, 0.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c4", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-0.5, 0.5] + } + }, + { + "type": "contourcarpet", + "carpet": "c1", + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "showlegend": false, + "contours": { + "type": "constraint", + "operation": "][", + "value": [-2.5, -1.5] + }, + "line": {"smoothing": 0} + }, + { + "type": "contourcarpet", + "carpet": "c2", + "showlegend": false, + "z": [[0, 0.8, 2], [1.2, -2, 3.2], [2, 2.8, 4]], + "contours": { + "type": "constraint", + "operation": "][", + "value": [-2.5, -1.5] + } + }, + { + "type": "carpet", + "carpet": "c1", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c2", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y" + }, + { + "type": "carpet", + "carpet": "c3", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c4", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "carpet", + "carpet": "c5", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c6", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "type": "carpet", + "carpet": "c7", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c8", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y4" + }, + { + "type": "carpet", + "carpet": "c9", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 0}, + "baxis": {"smoothing": 0}, + "xaxis": "x", + "yaxis": "y5" + }, + { + "type": "carpet", + "carpet": "c10", + "a": [1, 2, 3], + "b": [1, 2, 3], + "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]], + "aaxis": {"smoothing": 1}, + "baxis": {"smoothing": 1}, + "xaxis": "x2", + "yaxis": "y5" + } + ], + "layout": { + "title": "Outer range constraint with z = -2 in the middle", + "width": 600, + "height": 800, + "dragmode": "pan", + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + }, + "yaxis": { + "domain": [0, 0.19] + }, + "yaxis2": { + "domain": [0.21, 0.39] + }, + "yaxis3": { + "domain": [0.41, 0.59] + }, + "yaxis4": { + "domain": [0.61, 0.79] + }, + "yaxis5": { + "domain": [0.81, 1] + }, + "xaxis": { + "domain": [0, 0.49] + }, + "xaxis2": { + "domain": [0.51, 1.0] + }, + "annotations": [ + { + "x": 0, + "y": 4, + "text": "z < -2.5 | z > -1.5:", + "align": "left", + "xref": "x", + "yref": "y", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "align": "left", + "y": 4, + "xref": "x", + "yref": "y2", + "text": "z < -0.5 | z > 0.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y3", + "text": "z < 1.5 | z > 2.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y4", + "text": "z < 3.5 | z > 4.5:", + "showarrow": false, + "font": { + "size": 14 + } + }, + { + "x": 0, + "y": 4, + "align": "left", + "xref": "x", + "yref": "y5", + "text": "z < 5.5 | z > 6.5:", + "showarrow": false, + "font": { + "size": 14 + } + } + ] + } +} diff --git a/test/image/mocks/cheater_constraints.json b/test/image/mocks/cheater_constraints.json new file mode 100644 index 00000000000..7975e55b3ed --- /dev/null +++ b/test/image/mocks/cheater_constraints.json @@ -0,0 +1,399 @@ +{ + "data":[ + { + "carpet":"c1", + "type":"carpet", + "a":[1, 2, 3], + "b":[1, 2, 3], + "y":[[1, 2.3, 3], [2.3, 3, 3.7], [3, 3.7, 5]], + "xaxis":"x", + "yaxis":"y", + "aaxis":{ + "smoothing":1, + "title":"a", + "type":"linear" + }, + "baxis":{ + "smoothing":1, + "title":"b", + "type":"linear" + } + }, + { + "carpet":"c2", + "type":"carpet", + "a":[1, 2, 3], + "b":[1, 2, 3], + "y":[[1, 2.3, 3], [2.3, 3, 3.7], [3, 3.7, 5]], + "xaxis":"x2", + "yaxis":"y", + "aaxis":{ + "smoothing":0, + "title":"a", + "type":"linear" + }, + "baxis":{ + "smoothing":0, + "title":"b", + "type":"linear" + } + }, + { + "carpet":"c3", + "type":"carpet", + "a":[1, 2, 3], + "b":[1, 2, 3], + "y":[[1, 1.7, 3], [1.7, 3, 4.3], [3, 4.3, 5]], + "xaxis":"x", + "yaxis":"y2", + "aaxis":{ + "smoothing":1, + "title":"a", + "type":"linear" + }, + "baxis":{ + "smoothing":1, + "title":"b", + "type":"linear" + } + }, + { + "carpet":"c4", + "type":"carpet", + "a":[1, 2, 3], + "b":[1, 2, 3], + "y":[[1, 1.7, 3], [1.7, 3, 4.3], [3, 4.3, 5]], + "xaxis":"x2", + "yaxis":"y2", + "aaxis":{ + "smoothing":0, + "title":"a", + "type":"linear" + }, + "baxis":{ + "smoothing":0, + "title":"b", + "type":"linear" + } + }, + { + "carpet":"c1", + "type":"contourcarpet", + "name": "1A", + "z":[[1, 2.3, 3], [2.3, 3, 3.7], [3, 3.7, 5]], + "contours":{ + "type":"constraint", + "operation":"[]", + "value":[2.5, 3.5] + }, + "line":{ + "dash": "dashdot", + "width": 2, + "smoothing":1 + } + }, + { + "carpet":"c1", + "type":"contourcarpet", + "name": "1B", + "z":[[1, 2.3, 3], [2.3, 3, 3.7], [3, 3.7, 5]], + "contours":{ + "type":"constraint", + "operation":"][", + "value":[2.9, 3.1] + }, + "line":{ + "dash": "dash", + "width": 2, + "smoothing":1 + } + }, + { + "carpet":"c1", + "type":"contourcarpet", + "name": "1C", + "z":[[0, 0.5, 1], [-0.5, 0, 0.5], [-1, -0.5, 0]], + "contours":{ + "type":"constraint", + "operation":"<", + "value":0.25 + }, + "line":{ + "width": 2, + "smoothing":1 + } + }, + { + "carpet":"c1", + "type":"contourcarpet", + "name": "1D", + "z":[[0, 0.5, 1], [-0.5, 0, 0.5], [-1, -0.5, 0]], + "contours":{ + "type":"constraint", + "operation":">", + "value":-0.25 + }, + "line":{ + "dash": "dot", + "width": 2, + "smoothing":1 + } + }, + { + "carpet":"c2", + "xaxis":"x2", + "yaxis":"y", + "type":"contourcarpet", + "name": "2A", + "z":[[1, 2.3, 3], [2.3, 3, 3.7], [3, 3.7, 5]], + "contours":{ + "type":"constraint", + "operation":"[]", + "value":[2.5, 3.5] + }, + "line":{ + "dash": "dashdot", + "smoothing":1, + "width":2 + } + }, + { + "carpet":"c2", + "xaxis":"x2", + "yaxis":"y", + "type":"contourcarpet", + "name": "2B", + "z":[[1, 2.3, 3], [2.3, 3, 3.7], [3, 3.7, 5]], + "contours":{ + "type":"constraint", + "operation":"][", + "value":[ + 2.9, + 3.1 + ] + }, + "line":{ + "dash": "dash", + "width": 2, + "smoothing":1 + } + }, + { + "carpet":"c2", + "xaxis":"x2", + "yaxis":"y", + "type":"contourcarpet", + "name": "2C", + "z":[[0, 0.5, 1], [-0.5, 0, 0.5], [-1, -0.5, 0]], + "contours":{ + "type":"constraint", + "operation":"<", + "value":0.25 + }, + "line":{ + "smoothing":1, + "width": 2 + } + }, + { + "carpet":"c2", + "xaxis":"x2", + "yaxis":"y", + "type":"contourcarpet", + "name": "2D", + "z":[[0, 0.5, 1], [-0.5, 0, 0.5], [-1, -0.5, 0]], + "contours":{ + "type":"constraint", + "operation":">", + "value":-0.25 + }, + "line":{ + "dash": "dot", + "smoothing":1, + "width": 2 + } + }, + { + "carpet":"c3", + "xaxis":"x", + "yaxis":"y2", + "type":"contourcarpet", + "name": "3A", + "z":[[1, 2.3, 3], [2.3, 3, 3.7], [3, 3.7, 5]], + "contours":{ + "type":"constraint", + "operation":"[]", + "value":[2.5, 3.5] + }, + "line":{ + "dash": "dashdot", + "smoothing":1, + "width":2 + } + }, + { + "carpet":"c3", + "xaxis":"x", + "yaxis":"y2", + "type":"contourcarpet", + "name": "3B", + "z":[[1, 2.3, 3], [2.3, 3, 3.7], [3, 3.7, 5]], + "contours":{ + "type":"constraint", + "operation":"][", + "value":[2.9, 3.1] + }, + "line":{ + "dash": "dash", + "width": 2, + "smoothing":1 + } + }, + { + "carpet":"c3", + "xaxis":"x", + "yaxis":"y2", + "type":"contourcarpet", + "name": "3C", + "z":[[0, 0.5, 1], [-0.5, 0, 0.5], [-1, -0.5, 0]], + "contours":{ + "type":"constraint", + "operation":"<", + "value":0.25 + }, + "line":{ + "smoothing":1, + "width": 2 + } + }, + { + "carpet":"c3", + "xaxis":"x", + "yaxis":"y2", + "type":"contourcarpet", + "name": "3D", + "z":[[0, 0.5, 1], [-0.5, 0, 0.5], [-1, -0.5, 0]], + "contours":{ + "type":"constraint", + "operation":">", + "value":-0.25 + }, + "line":{ + "dash": "dot", + "smoothing":1, + "width": 2 + } + }, + { + "carpet":"c4", + "xaxis":"x2", + "yaxis":"y2", + "type":"contourcarpet", + "name": "4A", + "z":[[1, 2.3, 3], [2.3, 3, 3.7], [3, 3.7, 5]], + "contours":{ + "type":"constraint", + "operation":"[]", + "value":[2.5, 3.5] + }, + "line":{ + "dash": "dashdot", + "smoothing":1, + "width":2 + } + }, + { + "carpet":"c4", + "xaxis":"x2", + "yaxis":"y2", + "type":"contourcarpet", + "name": "4B", + "z":[[1, 2.3, 3], [2.3, 3, 3.7], [3, 3.7, 5]], + "contours":{ + "type":"constraint", + "operation":"][", + "value":[2.9, 3.1] + }, + "line":{ + "dash": "dash", + "width": 2, + "smoothing":1 + } + }, + { + "carpet":"c4", + "xaxis":"x2", + "yaxis":"y2", + "type":"contourcarpet", + "name": "4C", + "z":[[0, 0.5, 1], [-0.5, 0, 0.5], [-1, -0.5, 0]], + "contours":{ + "type":"constraint", + "operation":"<", + "value":0.25 + }, + "line":{ + "smoothing":1, + "width": 2 + } + }, + { + "carpet":"c4", + "xaxis":"x2", + "yaxis":"y2", + "type":"contourcarpet", + "name": "4D", + "z":[[0, 0.5, 1], [-0.5, 0, 0.5], [-1, -0.5, 0]], + "opacity":1.0, + "contours":{ + "type":"constraint", + "operation":">", + "value":-0.25 + }, + "line":{ + "dash": "dot", + "smoothing":1, + "width": 2 + } + } + ], + "layout":{ + "width":800, + "height":600, + "margin":{ + "t":30, + "r":30, + "b":30, + "l":30 + }, + "dragmode":"pan", + "xaxis":{ + "domain":[ + 0, + 0.49 + ], + "autorange":true + }, + "yaxis":{ + "domain":[ + 0, + 0.49 + ], + "autorange":true + }, + "xaxis2":{ + "domain":[ + 0.51, + 1 + ], + "autorange":true + }, + "yaxis2":{ + "domain":[ + 0.51, + 1 + ], + "autorange":true + } + } +} diff --git a/test/image/mocks/cheater_contour.json b/test/image/mocks/cheater_contour.json new file mode 100644 index 00000000000..cd78034730a --- /dev/null +++ b/test/image/mocks/cheater_contour.json @@ -0,0 +1,134 @@ +{ + "data": [ + { + "carpet":"c", + "type":"contourcarpet", + "autocontour":false, + "contours":{ + "start":1, + "end":14, + "size":1 + }, + "line":{ + "width":2, + "smoothing":0 + }, + "colorbar": { + "len": 0.4, + "y": 0.25 + }, + "z":[1, 1.96, 2.56, 3.0625, 4, 5.0625, 1, 7.5625, 9, 12.25, 15.21, 14.0625], + "a":[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3], + "b":[4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6], + "xaxis":"x", + "yaxis":"y" + }, + { + "carpet":"c", + "a":[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3], + "b":[4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6], + "y":[1, 1.4, 1.6, 1.75, 2, 2.5, 2.7, 2.75, 3, 3.5, 3.7, 3.75], + "x":[2, 3, 4, 5, 2.2, 3.1, 4.1, 5.1, 1.5, 2.5, 3.5, 4.5], + "type":"carpet", + "aaxis":{ + "tickprefix":"a = ", + "smoothing":0, + "minorgridcount":9, + "type":"linear" + }, + "baxis":{ + "tickprefix":"b = ", + "smoothing":0, + "minorgridcount":9, + "type":"linear" + }, + "xaxis":"x", + "yaxis":"y" + }, + { + "carpet":"c2", + "type":"contourcarpet", + "autocontour":false, + "contours":{ + "start":1, + "end":14, + "size":1 + }, + "line":{ + "width":2 + }, + "colorbar": { + "len": 0.4, + "y": 0.75 + }, + "z":[1, 1.96, 2.56, 3.0625, 4, 5.0625, 1, 7.5625, 9, 12.25, 15.21, 14.0625], + "a":[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3], + "b":[4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6], + "xaxis":"x", + "yaxis":"y2", + "zmin":1, + "zmax":15.21, + "zauto":true + }, + { + "carpet":"c2", + "a":[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3], + "b":[4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6], + "y":[1, 1.4, 1.6, 1.75, 2, 2.5, 2.7, 2.75, 3, 3.5, 3.7, 3.75], + "x":[2, 3, 4, 5, 2.2, 3.1, 4.1, 5.1, 1.5, 2.5, 3.5, 4.5], + "type":"carpet", + "aaxis":{ + "tickprefix":"a = ", + "smoothing":1, + "minorgridcount":9, + "type":"linear" + }, + "baxis":{ + "tickprefix":"b = ", + "smoothing":1, + "minorgridcount":9, + "type":"linear" + }, + "xaxis":"x", + "yaxis":"y2" + } + ], + "layout": { + "width": 600, + "height": 600, + "title": "Cheater plot with 1D input", + "margin":{ + "t":40, + "r":30, + "b":30, + "l":30 + }, + "dragmode":"pan", + "yaxis":{ + "domain":[ + 0, + 0.48 + ], + "range":[ + 0.38830491032049386, + 4.361695089679506 + ] + }, + "yaxis2":{ + "domain":[ + 0.52, + 1 + ], + "range":[ + 0.38830491032049386, + 4.361695089679506 + ] + }, + "xaxis":{ + "range":[ + 0.6676731793960924, + 5.932326820603907 + ] + } + } +} diff --git a/test/image/mocks/cheater_fully_filled.json b/test/image/mocks/cheater_fully_filled.json new file mode 100644 index 00000000000..fa40f2eec87 --- /dev/null +++ b/test/image/mocks/cheater_fully_filled.json @@ -0,0 +1,55 @@ +{ + "data": [ + { + "type": "carpet", + "a": [0, 1, 2], + "b": [0, 1, 2, 3], + "y": [[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2]], + "cheaterslope": -2, + "aaxis": { + "minorgridcount": 1 + }, + "baxis": { + "minorgridcount": 1 + } + }, + { + "type": "contourcarpet", + "z": [[0, 0, 0], [1, 1, 1], [2, -1, 2], [3, 3, 3]], + "name": "almost filled", + "contours": { + "type": "constraint", + "value": 0.05, + "operation": "<" + } + }, + { + "type": "contourcarpet", + "name": "fully filled", + "z": [[0, 0, 0], [1, 1, 1], [2, -1, 2], [3, 3, 3]], + "contours": { + "type": "constraint", + "value": -0.05, + "operation": "<" + } + } + ], + "layout": { + "title": "Fully filled carpet with a hole", + "annotations": [{ + "text": "These two fills differ only in a tiny threshold that affects whether the
contour fills the whole region or cuts off a sliver on the left side.
They should be effectively identical, not complementary.", + "align": "left", + "showarrow": false, + "x": 0.5, + "y": 2.5, + "xref": "x", + "yref": "y" + }], + "margin": { + "t": 60, + "r": 30, + "b": 30, + "l": 30 + } + } +} diff --git a/test/image/mocks/cheater_smooth.json b/test/image/mocks/cheater_smooth.json new file mode 100644 index 00000000000..4bc55cd7b64 --- /dev/null +++ b/test/image/mocks/cheater_smooth.json @@ -0,0 +1,159 @@ +{ + "data":[ + { + "type":"carpet", + "a":[ + 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, + 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, + 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 + ], + "b":[ + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82 + ], + "y":[ + 4, 4.2, 4.4, 4.6, 4.8, 5, + 5.1, 5.3, 5.5, 5.7, 5.9, 6.1, + 6.2, 6.4, 6.6, 6.8, 7, 7.2, + 7.4, 7.6, 7.8, 8, 8.2, 8.4, + 8.8, 9, 9.2, 9.4, 9.6, 9.8 + ], + "cheaterslope":2, + "aaxis":{ + "title":"width, cm", + "tickformat":".1f", + "type":"linear" + }, + "baxis":{ + "title":"height, cm", + "tickformat":".2f", + "type":"linear" + } + }, + { + "type":"contourcarpet", + "name":"Power", + "z":[ + 100, 110, 120, 140, 180, 260, + 200, 210, 220, 240, 280, 360, + 300, 310, 320, 340, 380, 460, + 400, 410, 420, 440, 480, 560, + 500, 510, 520, 540, 580, 660 + ], + "opacity":0.4, + "contours":{ + "type":"constraint", + "operation":"][", + "value":[ + 400, + 540 + ] + }, + "colorscale":[ + [ 0, "red" ], + [ 1, "red" ] + ], + "a":[ + 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, + 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, + 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 + ], + "b":[ + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82 + ] + }, + { + "type":"contourcarpet", + "name":"Strength", + "z":[ + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, + 0.12, 0.22, 0.32, 0.42, 0.52, 0.62, + 0.14, 0.24, 0.34, 0.44, 0.54, 0.64, + 0.16, 0.26, 0.36, 0.46, 0.56, 0.66, + 0.18, 0.28, 0.38, 0.48, 0.58, 0.68 + ], + "opacity":0.4, + "contours":{ + "type":"constraint", + "operation":"<", + "value":[ + 0.5 + ] + }, + "colorscale":[ + [ 0, "blue" ], + [ 1, "blue" ] + ], + "uid":"977b5b", + "a":[ + 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, + 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, + 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 + ], + "b":[ + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82 + ] + }, + { + "type":"contourcarpet", + "name":"Agility", + "z":[ + 800, 801, 802, 803, 804, 805, + 810, 811, 812, 813, 814, 815, + 820, 821, 822, 823, 824, 825, + 830, 831, 832, 833, 834, 835, + 840, 841, 842, 843, 844, 845 + ], + "opacity":0.4, + "contours":{ + "type":"constraint", + "operation":">", + "value":810 + }, + "colorscale":[ + [ 0, "green" ], + [ 1, "green" ] + ], + "a":[ + 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, + 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, + 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 + ], + "b":[ + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82, + 1.01, 1.12, 1.24, 1.38, 1.56, 1.82 + ] + } + ], + "layout":{ + "dragmode":"pan", + "width":800, + "height":600, + "yaxis":{ + "tickprefix":"€", + "tickformat":".2f" + } + } +} diff --git a/test/image/mocks/scattercarpet.json b/test/image/mocks/scattercarpet.json new file mode 100644 index 00000000000..dcc774cefaa --- /dev/null +++ b/test/image/mocks/scattercarpet.json @@ -0,0 +1,77 @@ +{ + "data": [ + { + "type": "carpet", + "a": [0.1, 0.2, 0.3], + "b": [1, 2, 3], + "y": [ + [1, 2.2, 3], + [1.5, 2.7, 3.5], + [1.7, 2.9, 3.7] + ], + "cheaterslope": 1, + "aaxis": { + "title": "a", + "tickmode": "linear", + "dtick": 0.05 + }, + "baxis": { + "title": "b", + "tickmode": "linear", + "dtick": 0.5 + } + }, + { + "type": "scattercarpet", + "name": "b = 1.5", + "a": [0.05, 0.15, 0.25, 0.35], + "b": [1.5, 1.5, 1.5, 1.5] + }, + { + "type": "scattercarpet", + "name": "b = 2", + "a": [0.05, 0.15, 0.25, 0.35], + "b": [2, 2, 2, 2] + }, + { + "type": "scattercarpet", + "name": "b = 2.5", + "a": [0.05, 0.15, 0.25, 0.35], + "b": [2.5, 2.5, 2.5, 2.5] + }, + { + "type": "scattercarpet", + "name": "a = 0.15", + "a": [0.15, 0.15, 0.15, 0.15], + "b": [0.5, 1.5, 2.5, 3.5], + "line": { + "smoothing": 1, + "shape": "spline" + } + }, + { + "type": "scattercarpet", + "name": "a = 0.2", + "a": [0.2, 0.2, 0.2, 0.2], + "b": [0.5, 1.5, 2.5, 3.5], + "line": { + "smoothing": 1, + "shape": "spline" + } + }, + { + "type": "scattercarpet", + "name": "a = 0.25", + "a": [0.25, 0.25, 0.25, 0.25], + "b": [0.5, 1.5, 2.5, 3.5], + "line": { + "smoothing": 1, + "shape": "spline" + } + } + ], + "layout": { + "title": "scattercarpet extrapolation, clipping, and smoothing", + "hovermode": "closest" + } +} diff --git a/test/jasmine/tests/carpet_test.js b/test/jasmine/tests/carpet_test.js new file mode 100644 index 00000000000..861641f64da --- /dev/null +++ b/test/jasmine/tests/carpet_test.js @@ -0,0 +1,432 @@ +// var Plotly = require('@lib/index'); +var Plots = require('@src/plots/plots'); +// var Lib = require('@src/lib'); + +var Carpet = require('@src/traces/carpet'); +var smoothFill2D = require('@src/traces/carpet/smooth_fill_2d_array'); +var smoothFill = require('@src/traces/carpet/smooth_fill_array'); +// var calc = require('@src/traces/carpet/calc'); + +var customMatchers = require('../assets/custom_matchers'); + +// var d3 = require('d3'); +// var createGraphDiv = require('../assets/create_graph_div'); +// var destroyGraphDiv = require('../assets/destroy_graph_div'); +// var customMatchers = require('../assets/custom_matchers'); + +var test = { + supplyDefaults: false, + smoothFill2D: true +}; + +describe('carpet supplyDefaults', function() { + if(!test.supplyDefaults) return; + 'use strict'; + + var traceIn, + traceOut; + + var supplyDefaults = Carpet.supplyDefaults; + + var defaultColor = '#444', + layout = { + font: Plots.layoutAttributes.font + }; + + beforeEach(function() { + traceOut = {}; + }); + + it('uses a, b, x, and y', function() { + traceIn = { + a: [0, 1], + b: [0, 1, 2], + x: [[1, 2, 3], [4, 5, 6]], + y: [[2, 3, 4], [5, 6, 7]] + }; + + supplyDefaults(traceIn, traceOut, defaultColor, layout); + + expect(traceOut.a).toEqual([0, 1]); + expect(traceOut.b).toEqual([0, 1, 2]); + expect(traceOut.x).toEqual([[1, 2, 3], [4, 5, 6]]); + expect(traceOut.y).toEqual([[2, 3, 4], [5, 6, 7]]); + expect(traceOut.da).toBeUndefined(); + expect(traceOut.db).toBeUndefined(); + expect(traceOut.a0).toBeUndefined(); + expect(traceOut.b0).toBeUndefined(); + expect(traceOut.visible).toBe(true); + }); + + it('sets a0/da when a not provided', function() { + traceIn = { + y: [[2, 3, 4], [5, 6, 7]], + b: [0, 1] + }; + supplyDefaults(traceIn, traceOut, defaultColor, layout); + + expect(traceOut.da).not.toBeUndefined(); + expect(traceOut.a0).not.toBeUndefined(); + expect(traceOut.db).toBeUndefined(); + expect(traceOut.b0).toBeUndefined(); + }); + + it('sets b0/db when b not provided', function() { + traceIn = { + y: [[2, 3, 4], [5, 6, 7]], + a: [0, 1, 2] + }; + supplyDefaults(traceIn, traceOut, defaultColor, layout); + + expect(traceOut.da).toBeUndefined(); + expect(traceOut.a0).toBeUndefined(); + expect(traceOut.db).not.toBeUndefined(); + expect(traceOut.b0).not.toBeUndefined(); + }); + + it('sets visible = false when x is not valid', function() { + traceIn = {y: [[1, 2], [3, 4]], x: [4]}; + supplyDefaults(traceIn, traceOut, defaultColor, layout); + expect(traceOut.visible).toBe(false); + }); + + it('sets visible = false when y is not valid', function() { + traceIn = {y: [1, 2]}; + supplyDefaults(traceIn, traceOut, defaultColor, layout); + expect(traceOut.visible).toBe(false); + }); + + it('sets visible = false if dim x !== dim y', function() { + traceIn = { + x: [[1, 2], [3, 4]], + y: [[1, 2, 3], [4, 5, 6]] + }; + supplyDefaults(traceIn, traceOut, defaultColor, layout); + expect(traceOut.visible).toBe(false); + }); + + /* it('sets _cheater = true when x is provided', function() { + traceIn = {y: [[1, 2], [3, 4]]}; + supplyDefaults(traceIn, traceOut, defaultColor, layout); + expect(traceOut._cheater).toBe(true); + }); + + it('sets cheater = false when x is not valid', function() { + traceIn = {y: [[1, 2], [3, 4]], x: [[3, 4], [1, 2]]}; + supplyDefaults(traceIn, traceOut, defaultColor, layout); + expect(traceOut._cheater).toBe(false); + });*/ +}); + +describe('supplyDefaults visibility check', function() { + it('does not hide empty subplots', function() { + var gd = {data: [], layout: {xaxis: {}}}; + Plots.supplyDefaults(gd); + expect(gd._fullLayout.xaxis.visible).toBe(true); + }); + + it('does not hide axes with non-carpet traces', function() { + var gd = {data: [{x: []}]}; + Plots.supplyDefaults(gd); + expect(gd._fullLayout.xaxis.visible).toBe(true); + }); + + it('does not hide axes with non-cheater carpet', function() { + var gd = {data: [{ + type: 'carpet', + a: [1, 2, 3], + b: [1, 2], + x: [[1, 2, 3], [4, 5, 6]], + y: [[1, 2, 3], [4, 5, 6]], + }, { + type: 'contourcarpet', + z: [[1, 2, 3], [4, 5, 6]], + }]}; + Plots.supplyDefaults(gd); + expect(gd._fullLayout.xaxis.visible).toBe(true); + }); + + it('hides axes with cheater', function() { + var gd = {data: [{ + type: 'carpet', + a: [1, 2, 3], + b: [1, 2], + y: [[1, 2, 3], [4, 5, 6]], + }, { + type: 'contourcarpet', + z: [[1, 2, 3], [4, 5, 6]], + }]}; + Plots.supplyDefaults(gd); + expect(gd._fullLayout.xaxis.visible).toBe(false); + }); + + it('does not hide an axis with cheater and non-cheater carpet', function() { + var gd = { + data: [{ + carpet: 'c1', + type: 'carpet', + a: [1, 2, 3], + b: [1, 2], + x: [[1, 2, 3], [4, 5, 6]], + y: [[1, 2, 3], [4, 5, 6]], + }, { + carpet: 'c2', + type: 'carpet', + a: [1, 2, 3], + b: [1, 2], + y: [[1, 2, 3], [4, 5, 6]], + }, { + carpet: 'c1', + type: 'contourcarpet', + z: [[1, 2, 3], [4, 5, 6]], + }, { + carpet: 'c2', + type: 'contourcarpet', + z: [[1, 2, 3], [4, 5, 6]], + }] + }; + + Plots.supplyDefaults(gd); + expect(gd._fullLayout.xaxis.visible).toBe(true); + }); + + it('does not hide an axis with cheater and non-cheater carpet', function() { + var gd = { + data: [{ + carpet: 'c1', + type: 'carpet', + a: [1, 2, 3], + b: [1, 2], + x: [[1, 2, 3], [4, 5, 6]], + y: [[1, 2, 3], [4, 5, 6]], + }, { + carpet: 'c2', + type: 'carpet', + a: [1, 2, 3], + b: [1, 2], + y: [[1, 2, 3], [4, 5, 6]], + }, { + carpet: 'c1', + type: 'contourcarpet', + z: [[1, 2, 3], [4, 5, 6]], + }, { + carpet: 'c2', + type: 'contourcarpet', + z: [[1, 2, 3], [4, 5, 6]], + }] + }; + + Plots.supplyDefaults(gd); + expect(gd._fullLayout.xaxis.visible).toBe(true); + }); +}); + +describe('carpet smooth_fill_2d_array', function() { + if(!test.smoothFill2D) return; + var _; + + beforeAll(function() { jasmine.addMatchers(customMatchers); }); + + it('fills in all points trivially', function() { + // Given only corners, should just propagate the constant throughout: + expect(smoothFill2D( + [[1, _, _, _, _, _, _, 1], + [_, _, _, _, _, _, _, _], + [_, _, _, _, _, _, _, _], + [_, _, _, _, _, _, _, _], + [_, _, _, _, _, _, _, _], + [_, _, _, _, _, _, _, _], + [1, _, _, _, _, _, _, 1]], + [1, 2, 3, 4, 5, 6, 7, 8], + [1, 2, 3, 4, 5, 6, 7] + )).toBeCloseTo2DArray([ + [1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1] + ], 3); + }); + + it('fills in linearly from corner data', function() { + // Similar, but in this case we want it to just fill linearly: + expect(smoothFill2D( + [[0, _, _, 3], + [_, _, _, _], + [_, _, _, _], + [_, _, _, _], + [4, _, _, 7]], + [1, 2, 3, 4], + [1, 2, 3, 4, 5] + )).toBeCloseTo2DArray([ + [0, 1, 2, 3], + [1, 2, 3, 4], + [2, 3, 4, 5], + [3, 4, 5, 6], + [4, 5, 6, 7] + ], 3); + }); + + it('fills in interior data', function() { + expect(smoothFill2D( + [[1, 2, 3, 4], + [4, _, _, 7], + [7, 8, 9, 10]], + [0, 1, 2, 3], + [0, 1, 2] + )).toBeCloseTo2DArray([ + [1, 2, 3, 4], + [4, 5, 6, 7], + [7, 8, 9, 10] + ], 3); + }); + + it('fills in exterior data', function() { + expect(smoothFill2D( + [[_, _, 3, _], + [4, 5, 6, _], + [_, 8, 9, _]], + [0, 1, 2, 3], + [0, 1, 2] + )).toBeCloseTo2DArray([ + [1, 2, 3, 4], + [4, 5, 6, 7], + [7, 8, 9, 10] + ], 3); + }); + + it('fills in heavily missing data', function() { + expect(smoothFill2D( + [[_, _, _, _], + [4, _, 6, _], + [_, 8, _, 10]], + [0, 1, 2, 3], + [0, 1, 2] + )).toBeCloseTo2DArray([ + [1, 2, 3, 4], + [4, 5, 6, 7], + [7, 8, 9, 10] + ], 3); + }); + + it('fills non-uniform interior data', function() { + expect(smoothFill2D( + [[1, 2, 4, 5], + [4, _, _, 8], + [10, 11, 13, 14]], + [0, 1, 3, 4], + [0, 1, 3] + )).toBeCloseTo2DArray([ + [1, 2, 4, 5], + [4, 5, 7, 8], + [10, 11, 13, 14] + ], 3); + }); + + it('fills non-uniform exterior data', function() { + expect(smoothFill2D( + [[_, 2, 4, _], + [4, 5, 7, 8], + [_, 11, 13, _]], + [0, 1, 3, 4], + [0, 1, 3] + )).toBeCloseTo2DArray([ + [1, 2, 4, 5], + [4, 5, 7, 8], + [10, 11, 13, 14] + ], 3); + }); + + it('fills heavily missing non-uniform data', function() { + expect(smoothFill2D( + [[_, _, 4, _], + [4, _, _, 8], + [_, 11, _, _]], + [0, 1, 3, 4], + [0, 1, 3] + )).toBeCloseTo2DArray([ + [1, 2, 4, 5], + [4, 5, 7, 8], + [10, 11, 13, 14] + ], 3); + }); + + it('applies laplacian smoothing', function() { + // The examples above all just assume a linear trend. Check + // to make sure it's actually smoothing: + expect(smoothFill2D( + [[0.5, 1, 1, 0.5], + [0, _, _, 0], + [0.5, 1, 1, 0.5]], + [0, 1, 2, 3], + [0, 1, 2] + )).toBeCloseTo2DArray([ + [0.5, 1, 1, 0.5], + [0, 2 / 3, 2 / 3, 0], + [0.5, 1, 1, 0.5] + ], 3); + }); + + it('applies laplacian smoothing symmetrically', function() { + // Just one more santiy check for a case that's particularly easy to guess + // due to the symmetry: + expect(smoothFill2D( + [[0.5, 1, 1, 0.5], + [0, _, _, 0], + [0, _, _, 0], + [0.5, 1, 1, 0.5]], + [0, 1, 2, 3], + [0, 1, 2, 3] + )).toBeCloseTo2DArray([ + [0.5, 1, 1, 0.5], + [0, 0.5, 0.5, 0], + [0, 0.5, 0.5, 0], + [0.5, 1, 1, 0.5] + ], 3); + }); +}); + +describe('smooth_fill_array', function() { + if(!test.smoothFill2D) return; + var _; + + beforeAll(function() { jasmine.addMatchers(customMatchers); }); + + it('fills in via linear interplation', function() { + expect(smoothFill([_, _, 2, 3, _, _, 6, 7, _, _, 10, 11, _])) + .toBeCloseToArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + }); + + it('fills with zero if no data', function() { + expect(smoothFill([_, _, _])) + .toBeCloseToArray([0, 0, 0]); + }); + + it('fills with constant if only one data point', function() { + expect(smoothFill([_, _, _, _, 8, _, _])) + .toBeCloseToArray([8, 8, 8, 8, 8, 8, 8]); + }); + + // Extra tests just to make sure the fence cases are handled properly: + it('fills in one leading point', function() { + expect(smoothFill([_, 1, 2, 3])) + .toBeCloseToArray([0, 1, 2, 3]); + }); + + it('fills in two leading points', function() { + expect(smoothFill([_, _, 2, 3])) + .toBeCloseToArray([0, 1, 2, 3]); + }); + + it('fills in one trailing point', function() { + expect(smoothFill([0, 1, 2, _])) + .toBeCloseToArray([0, 1, 2, 3]); + }); + + it('fills in two trailing points', function() { + expect(smoothFill([0, 1, _, _])) + .toBeCloseToArray([0, 1, 2, 3]); + }); +});