diff --git a/.eslintignore b/.eslintignore index 3a221b33c3a..37e5eb77547 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,4 +3,4 @@ dist build test/jasmine/assets/jquery-1.8.3.min.js -src/plots/polar/micropolar.js +src/plots/polar/legacy/micropolar.js diff --git a/lib/index.js b/lib/index.js index cba5213fbe7..39a357bac96 100644 --- a/lib/index.js +++ b/lib/index.js @@ -46,7 +46,9 @@ Plotly.register([ require('./contourcarpet'), require('./ohlc'), - require('./candlestick') + require('./candlestick'), + + require('./scatterpolar') ]); // transforms diff --git a/lib/scatterpolar.js b/lib/scatterpolar.js new file mode 100644 index 00000000000..1500f74d157 --- /dev/null +++ b/lib/scatterpolar.js @@ -0,0 +1,11 @@ +/** +* Copyright 2012-2018, 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/scatterpolar'); diff --git a/src/components/calendars/index.js b/src/components/calendars/index.js index e4524edea12..616ad35902e 100644 --- a/src/components/calendars/index.js +++ b/src/components/calendars/index.js @@ -238,6 +238,9 @@ module.exports = { // from yaxis if they only apply to x (rangeselector/rangeslider) yaxis: {calendar: axisAttrs}, zaxis: {calendar: axisAttrs} + }, + polar: { + radialaxis: {calendar: axisAttrs} } }, transforms: { diff --git a/src/components/dragelement/index.js b/src/components/dragelement/index.js index 4b505d3e5e5..f727bd99b8b 100644 --- a/src/components/dragelement/index.js +++ b/src/components/dragelement/index.js @@ -76,6 +76,10 @@ dragElement.unhoverRaw = unhover.raw; * numClicks is how many clicks we've registered within * a doubleclick time * e is the original mousedown event + * clampFn (optional, function(dx, dy) return [dx2, dy2]) + * Provide custom clamping function for small displacements. + * By default, clamping is done using `minDrag` to x and y displacements + * independently. */ dragElement.init = function init(options) { var gd = options.gd; @@ -99,6 +103,14 @@ dragElement.init = function init(options) { element.onmousedown = onStart; element.ontouchstart = onStart; + function _clampFn(dx, dy, minDrag) { + if(Math.abs(dx) < minDrag) dx = 0; + if(Math.abs(dy) < minDrag) dy = 0; + return [dx, dy]; + } + + var clampFn = options.clampFn || _clampFn; + function onStart(e) { // make dragging and dragged into properties of gd // so that others can look at and modify them @@ -145,12 +157,11 @@ dragElement.init = function init(options) { function onMove(e) { var offset = pointerOffset(e); - var dx = offset[0] - startX; - var dy = offset[1] - startY; var minDrag = options.minDrag || constants.MINDRAG; + var dxdy = clampFn(offset[0] - startX, offset[1] - startY, minDrag); + var dx = dxdy[0]; + var dy = dxdy[1]; - if(Math.abs(dx) < minDrag) dx = 0; - if(Math.abs(dy) < minDrag) dy = 0; if(dx || dy) { gd._dragged = true; dragElement.unhover(gd); diff --git a/src/components/modebar/manage.js b/src/components/modebar/manage.js index b1ddd21e981..8fb85bfe7db 100644 --- a/src/components/modebar/manage.js +++ b/src/components/modebar/manage.js @@ -81,6 +81,7 @@ function getButtonGroups(gd, buttonsToRemove, buttonsToAdd) { var hasGL2D = fullLayout._has('gl2d'); var hasTernary = fullLayout._has('ternary'); var hasMapbox = fullLayout._has('mapbox'); + var hasPolar = fullLayout._has('polar'); var groups = []; @@ -121,6 +122,9 @@ function getButtonGroups(gd, buttonsToRemove, buttonsToAdd) { if(hasMapbox || hasGeo) { dragModeGroup = ['pan2d']; } + if(hasPolar) { + dragModeGroup = ['zoom2d']; + } if(isSelectable(fullData)) { dragModeGroup.push('select2d'); dragModeGroup.push('lasso2d'); diff --git a/src/components/titles/index.js b/src/components/titles/index.js index e32d7f5c149..2fd89dd409e 100644 --- a/src/components/titles/index.js +++ b/src/components/titles/index.js @@ -51,6 +51,8 @@ var numStripRE = / [XY][0-9]* /; * offset - shift up/down in the rotated frame (unused?) * containerGroup - if an svg element already exists to hold this * title, include here. Otherwise it will go in fullLayout._infolayer + * + * @return {selection} d3 selection of title container group */ Titles.draw = function(gd, titleClass, options) { var cont = options.propContainer; @@ -63,13 +65,14 @@ Titles.draw = function(gd, titleClass, options) { var group = options.containerGroup; var fullLayout = gd._fullLayout; - var font = cont.titlefont.family; - var fontSize = cont.titlefont.size; - var fontColor = cont.titlefont.color; + var titlefont = cont.titlefont || {}; + var font = titlefont.family; + var fontSize = titlefont.size; + var fontColor = titlefont.color; var opacity = 1; var isplaceholder = false; - var txt = cont.title.trim(); + var txt = (cont.title || '').trim(); // only make this title editable if we positively identify its property // as one that has editing enabled. @@ -111,17 +114,28 @@ Titles.draw = function(gd, titleClass, options) { .attr('class', titleClass); el.exit().remove(); - if(!elShouldExist) return; + if(!elShouldExist) return group; function titleLayout(titleEl) { Lib.syncOrAsync([drawTitle, scootTitle], titleEl); } function drawTitle(titleEl) { - titleEl.attr('transform', transform ? - 'rotate(' + [transform.rotate, attributes.x, attributes.y] + - ') translate(0, ' + transform.offset + ')' : - null); + var transformVal; + + if(transform) { + transformVal = ''; + if(transform.rotate) { + transformVal += 'rotate(' + [transform.rotate, attributes.x, attributes.y] + ')'; + } + if(transform.offset) { + transformVal += 'translate(0, ' + transform.offset + ')'; + } + } else { + transformVal = null; + } + + titleEl.attr('transform', transformVal); titleEl.style({ 'font-family': font, @@ -236,4 +250,6 @@ Titles.draw = function(gd, titleClass, options) { }); } el.classed('js-placeholder', isplaceholder); + + return group; }; diff --git a/src/lib/angles.js b/src/lib/angles.js new file mode 100644 index 00000000000..ccfd1c83ffd --- /dev/null +++ b/src/lib/angles.js @@ -0,0 +1,29 @@ +/** +* Copyright 2012-2018, 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 PI = Math.PI; + +exports.deg2rad = function(deg) { + return deg / 180 * PI; +}; + +exports.rad2deg = function(rad) { + return rad / PI * 180; +}; + +exports.wrap360 = function(deg) { + var out = deg % 360; + return out < 0 ? out + 360 : out; +}; + +exports.wrap180 = function(deg) { + if(Math.abs(deg) > 180) deg -= Math.round(deg / 360) * 360; + return deg; +}; diff --git a/src/lib/coerce.js b/src/lib/coerce.js index ee4c66eaca0..07627121c1f 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -18,6 +18,7 @@ var colorscaleNames = Object.keys(require('../components/colorscale/scales')); var nestedProperty = require('./nested_property'); var counterRegex = require('./regex').counter; var DESELECTDIM = require('../constants/interactions').DESELECTDIM; +var wrap180 = require('./angles').wrap180; exports.valObjectMeta = { data_array: { @@ -182,10 +183,7 @@ exports.valObjectMeta = { coerceFunction: function(v, propOut, dflt) { if(v === 'auto') propOut.set('auto'); else if(!isNumeric(v)) propOut.set(dflt); - else { - if(Math.abs(v) > 180) v -= Math.round(v / 360) * 360; - propOut.set(+v); - } + else propOut.set(wrap180(+v)); } }, subplotid: { diff --git a/src/lib/filter_visible.js b/src/lib/filter_visible.js index 527e1b27852..71457fc3c9b 100644 --- a/src/lib/filter_visible.js +++ b/src/lib/filter_visible.js @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; /** Filter out object items with visible !== true @@ -17,13 +16,30 @@ * */ module.exports = function filterVisible(container) { + var filterFn = isCalcData(container) ? calcDataFilter : baseFilter; var out = []; for(var i = 0; i < container.length; i++) { var item = container[i]; - - if(item.visible === true) out.push(item); + if(filterFn(item)) out.push(item); } return out; }; + +function baseFilter(item) { + return item.visible === true; +} + +function calcDataFilter(item) { + return item[0].trace.visible === true; +} + +function isCalcData(cont) { + return ( + Array.isArray(cont) && + Array.isArray(cont[0]) && + cont[0][0] && + cont[0][0].trace + ); +} diff --git a/src/lib/index.js b/src/lib/index.js index 5249cad828e..fe501894dc0 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -77,6 +77,12 @@ lib.rotationXYMatrix = matrixModule.rotationXYMatrix; lib.apply2DTransform = matrixModule.apply2DTransform; lib.apply2DTransform2 = matrixModule.apply2DTransform2; +var anglesModule = require('./angles'); +lib.deg2rad = anglesModule.deg2rad; +lib.rad2deg = anglesModule.rad2deg; +lib.wrap360 = anglesModule.wrap360; +lib.wrap180 = anglesModule.wrap180; + var geom2dModule = require('./geometry2d'); lib.segmentsIntersect = geom2dModule.segmentsIntersect; lib.segmentDistance = geom2dModule.segmentDistance; diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 73d6e792fe8..356ebd484aa 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -22,7 +22,7 @@ var Queue = require('../lib/queue'); var Registry = require('../registry'); var PlotSchema = require('./plot_schema'); var Plots = require('../plots/plots'); -var Polar = require('../plots/polar'); +var Polar = require('../plots/polar/legacy'); var initInteractions = require('../plots/cartesian/graph_interact'); var Drawing = require('../components/drawing'); @@ -144,8 +144,11 @@ Plotly.plot = function(gd, data, layout, config) { var fullLayout = gd._fullLayout; - // Polar plots - if(data && data[0] && data[0].r) return plotPolar(gd, data, layout); + // Legacy polar plots + if(!fullLayout._has('polar') && data && data[0] && data[0].r) { + Lib.log('Legacy polar charts are deprecated!'); + return plotPolar(gd, data, layout); + } // so we don't try to re-call Plotly.plot from inside // legend and colorbar, if margins changed @@ -1941,6 +1944,16 @@ function _relayout(gd, aobj) { ax.range = (ax.range[1] > ax.range[0]) ? [1, 2] : [2, 1]; } + // clear polar view initial stash for radial range so that + // value get recomputed in correct units + if(Array.isArray(fullLayout._subplots.polar) && + fullLayout._subplots.polar.length && + fullLayout[p.parts[0]] && + p.parts[1] === 'radialaxis' + ) { + delete fullLayout[p.parts[0]]._subplot.viewInitial['radialaxis.range']; + } + // Annotations and images also need to convert to/from linearized coords // Shapes do not need this :) Registry.getComponentMethod('annotations', 'convertCoords')(gd, parentFull, vi, doextra); @@ -2865,6 +2878,9 @@ function makePlotFramework(gd) { // single cartesian layer for the whole plot fullLayout._cartesianlayer = fullLayout._paper.append('g').classed('cartesianlayer', true); + // single polar layer for the whole plot + fullLayout._polarlayer = fullLayout._paper.append('g').classed('polarlayer', true); + // single ternary layer for the whole plot fullLayout._ternarylayer = fullLayout._paper.append('g').classed('ternarylayer', true); diff --git a/src/plot_api/plot_schema.js b/src/plot_api/plot_schema.js index 61ba320491a..e3915e0297d 100644 --- a/src/plot_api/plot_schema.js +++ b/src/plot_api/plot_schema.js @@ -18,8 +18,8 @@ var frameAttributes = require('../plots/frame_attributes'); var animationAttributes = require('../plots/animation_attributes'); // polar attributes are not part of the Registry yet -var polarAreaAttrs = require('../plots/polar/area_attributes'); -var polarAxisAttrs = require('../plots/polar/axis_attributes'); +var polarAreaAttrs = require('../plots/polar/legacy/area_attributes'); +var polarAxisAttrs = require('../plots/polar/legacy/axis_attributes'); var editTypes = require('./edit_types'); diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 4aeea3bd743..b5d564126eb 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -762,6 +762,10 @@ axes.calcTicks = function calcTicks(ax) { minPx = ax._id.charAt(0) === 'y' ? 40 : 80; nt = Lib.constrain(ax._length / minPx, 4, 9) + 1; } + + // radial axes span half their domain, + // multiply nticks value by two to get correct number of auto ticks. + if(ax._name === 'radialaxis') nt *= 2; } // add a couple of extra digits for filling in ticks when we @@ -816,6 +820,12 @@ axes.calcTicks = function calcTicks(ax) { vals.push(x); } + // If same angle over a full circle, the last tick vals is a duplicate. + // TODO must do something similar for angular date axes. + if(ax._id === 'angular' && Math.abs(rng[1] - rng[0]) === 360) { + vals.pop(); + } + // save the last tick as well as first, so we can // show the exponent only on the last one ax._tmax = vals[vals.length - 1]; @@ -883,7 +893,9 @@ var roundBase10 = [2, 5, 10], // approx. tick positions for log axes, showing all (1) and just 1, 2, 5 (2) // these don't have to be exact, just close enough to round to the right value roundLog1 = [-0.046, 0, 0.301, 0.477, 0.602, 0.699, 0.778, 0.845, 0.903, 0.954, 1], - roundLog2 = [-0.301, 0, 0.301, 0.699, 1]; + roundLog2 = [-0.301, 0, 0.301, 0.699, 1], + // N.B. `thetaunit; 'radians' angular axes must be converted to degrees + roundAngles = [15, 30, 45, 90, 180]; function roundDTick(roughDTick, base, roundingSet) { return base * Lib.roundUp(roughDTick / base, roundingSet); @@ -908,6 +920,10 @@ function roundDTick(roughDTick, base, roundingSet) { axes.autoTicks = function(ax, roughDTick) { var base; + function getBase(v) { + return Math.pow(v, Math.floor(Math.log(roughDTick) / Math.LN10)); + } + if(ax.type === 'date') { ax.tick0 = Lib.dateTick0(ax.calendar); // the criteria below are all based on the rough spacing we calculate @@ -916,7 +932,7 @@ axes.autoTicks = function(ax, roughDTick) { if(roughX2 > ONEAVGYEAR) { roughDTick /= ONEAVGYEAR; - base = Math.pow(10, Math.floor(Math.log(roughDTick) / Math.LN10)); + base = getBase(10); ax.dtick = 'M' + (12 * roundDTick(roughDTick, base, roundBase10)); } else if(roughX2 > ONEAVGMONTH) { @@ -941,7 +957,7 @@ axes.autoTicks = function(ax, roughDTick) { } else { // milliseconds - base = Math.pow(10, Math.floor(Math.log(roughDTick) / Math.LN10)); + base = getBase(10); ax.dtick = roundDTick(roughDTick, base, roundBase10); } } @@ -960,7 +976,7 @@ axes.autoTicks = function(ax, roughDTick) { // ticks on a linear scale, labeled fully roughDTick = Math.abs(Math.pow(10, rng[1]) - Math.pow(10, rng[0])) / nt; - base = Math.pow(10, Math.floor(Math.log(roughDTick) / Math.LN10)); + base = getBase(10); ax.dtick = 'L' + roundDTick(roughDTick, base, roundBase10); } else { @@ -974,10 +990,15 @@ axes.autoTicks = function(ax, roughDTick) { ax.tick0 = 0; ax.dtick = Math.ceil(Math.max(roughDTick, 1)); } + else if(ax._id === 'angular') { + ax.tick0 = 0; + base = 1; + ax.dtick = roundDTick(roughDTick, base, roundAngles); + } else { // auto ticks always start at 0 ax.tick0 = 0; - base = Math.pow(10, Math.floor(Math.log(roughDTick) / Math.LN10)); + base = getBase(10); ax.dtick = roundDTick(roughDTick, base, roundBase10); } @@ -1205,6 +1226,7 @@ axes.tickText = function(ax, x, hover) { if(ax.type === 'date') formatDate(ax, out, hover, extraPrecision); else if(ax.type === 'log') formatLog(ax, out, hover, extraPrecision, hideexp); else if(ax.type === 'category') formatCategory(ax, out); + else if(ax._id === 'angular') formatAngle(ax, out, hover, extraPrecision, hideexp); else formatLinear(ax, out, hover, extraPrecision, hideexp); // add prefix and suffix @@ -1399,6 +1421,71 @@ function formatLinear(ax, out, hover, extraPrecision, hideexp) { out.text = numFormat(out.x, ax, hideexp, extraPrecision); } +function formatAngle(ax, out, hover, extraPrecision, hideexp) { + if(ax.thetaunit === 'radians' && !hover) { + var num = out.x / 180; + + if(num === 0) { + out.text = '0'; + } else { + var frac = num2frac(num); + + if(frac[1] >= 100) { + out.text = numFormat(Lib.deg2rad(out.x), ax, hideexp, extraPrecision); + } else { + var isNeg = out.x < 0; + + if(frac[1] === 1) { + if(frac[0] === 1) out.text = 'π'; + else out.text = frac[0] + 'π'; + } else { + out.text = [ + '', frac[0], '', + '⁄', + '', frac[1], '', + 'π' + ].join(''); + } + + if(isNeg) out.text = MINUS_SIGN + out.text; + } + } + } else { + out.text = numFormat(out.x, ax, hideexp, extraPrecision); + } +} + +// inspired by +// https://github.com/yisibl/num2fraction/blob/master/index.js +function num2frac(num) { + function almostEq(a, b) { + return Math.abs(a - b) <= 1e-6; + } + + function findGCD(a, b) { + return almostEq(b, 0) ? a : findGCD(b, a % b); + } + + function findPrecision(n) { + var e = 1; + while(!almostEq(Math.round(n * e) / e, n)) { + e *= 10; + } + return e; + } + + var precision = findPrecision(num); + var number = num * precision; + var gcd = Math.abs(findGCD(number, precision)); + + return [ + // numerator + Math.round(number / gcd), + // denominator + Math.round(precision / gcd) + ]; +} + // format a number (tick value) according to the axis settings // new, more reliable procedure than d3.round or similar: // add half the rounding increment, then stringify and truncate @@ -1747,7 +1834,7 @@ axes.doTicks = function(gd, axid, skipTitle) { var axLetter = axid.charAt(0), counterLetter = axes.counterLetter(axid), vals = axes.calcTicks(ax), - datafn = function(d) { return [d.text, d.x, ax.mirror].join('_'); }, + datafn = function(d) { return [d.text, d.x, ax.mirror, d.font, d.fontSize, d.fontColor].join('_'); }, tcls = axid + 'tick', gcls = axid + 'grid', zcls = axid + 'zl', @@ -1773,7 +1860,7 @@ axes.doTicks = function(gd, axid, skipTitle) { // positioning arguments for x vs y axes if(axLetter === 'x') { sides = ['bottom', 'top']; - transfn = function(d) { + transfn = ax._transfn || function(d) { return 'translate(' + (ax._offset + ax.l2p(d.x)) + ',0)'; }; tickpathfn = function(shift, len) { @@ -1786,7 +1873,7 @@ axes.doTicks = function(gd, axid, skipTitle) { } else if(axLetter === 'y') { sides = ['left', 'right']; - transfn = function(d) { + transfn = ax._transfn || function(d) { return 'translate(0,' + (ax._offset + ax.l2p(d.x)) + ')'; }; tickpathfn = function(shift, len) { @@ -1797,6 +1884,13 @@ axes.doTicks = function(gd, axid, skipTitle) { else return 'M' + shift + ',0h' + len; }; } + else if(axid === 'angular') { + sides = ['left', 'right']; + transfn = ax._transfn; + tickpathfn = function(shift, len) { + return 'M' + shift + ',0h' + len; + }; + } else { Lib.warn('Unrecognized doTicks axis:', axid); return; @@ -1811,6 +1905,10 @@ axes.doTicks = function(gd, axid, skipTitle) { if(!ax.visible) return; + if(ax._tickFilter) { + vals = vals.filter(ax._tickFilter); + } + // 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. @@ -1820,6 +1918,11 @@ axes.doTicks = function(gd, axid, skipTitle) { } var valsClipped = vals.filter(clipEnds); + // don't clip angular values + if(ax._id === 'angular') { + valsClipped = vals; + } + function drawTicks(container, tickpath) { var ticks = container.selectAll('path.' + tcls) .data(ax.ticks === 'inside' ? valsClipped : vals, datafn); @@ -1868,7 +1971,7 @@ axes.doTicks = function(gd, axid, skipTitle) { return (angle * flipit < 0) ? 'end' : 'start'; }; } - else { + else if(axLetter === 'y') { flipit = (axside === 'right') ? 1 : -1; labely = function(d) { return d.dy + d.fontSize * MID_SHIFT - labelShift * flipit; @@ -1884,6 +1987,16 @@ axes.doTicks = function(gd, axid, skipTitle) { return axside === 'right' ? 'start' : 'end'; }; } + else if(axid === 'angular') { + ax._labelShift = labelShift; + ax._labelStandoff = labelStandoff; + ax._pad = pad; + + labelx = ax._labelx; + labely = ax._labely; + labelanchor = ax._labelanchor; + } + var maxFontSize = 0, autoangle = 0, labelsReady = []; @@ -1921,9 +2034,16 @@ axes.doTicks = function(gd, axid, skipTitle) { maxFontSize = Math.max(maxFontSize, d.fontSize); }); + if(axid === 'angular') { + tickLabels.each(function(d) { + d3.select(this).select('text') + .call(svgTextUtils.positionText, labelx(d), labely(d)); + }); + } + function positionLabels(s, angle) { s.each(function(d) { - var anchor = labelanchor(angle); + var anchor = labelanchor(angle, d); var thisLabel = d3.select(this), mathjaxGroup = thisLabel.select('.text-math-group'), transform = transfn(d) + @@ -2217,6 +2337,7 @@ axes.doTicks = function(gd, axid, skipTitle) { grid.attr('transform', transfn) .call(Color.stroke, ax.gridcolor || '#ddd') .style('stroke-width', gridWidth + 'px'); + if(typeof gridpath === 'function') grid.attr('d', gridpath); grid.exit().remove(); // zero line diff --git a/src/plots/cartesian/axis_defaults.js b/src/plots/cartesian/axis_defaults.js index 06002cb3e90..c504ff593d1 100644 --- a/src/plots/cartesian/axis_defaults.js +++ b/src/plots/cartesian/axis_defaults.js @@ -6,24 +6,20 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; -var colorMix = require('tinycolor2').mix; - var Registry = require('../../registry'); var Lib = require('../../lib'); -var lightFraction = require('../../components/color/attributes').lightFraction; var layoutAttributes = require('./layout_attributes'); var handleTickValueDefaults = require('./tick_value_defaults'); var handleTickMarkDefaults = require('./tick_mark_defaults'); var handleTickLabelDefaults = require('./tick_label_defaults'); var handleCategoryOrderDefaults = require('./category_order_defaults'); +var handleLineGridDefaults = require('./line_grid_defaults'); var setConvert = require('./set_convert'); var orderedCategories = require('./ordered_categories'); - /** * options: object containing: * @@ -40,10 +36,6 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, var letter = options.letter; var font = options.font || {}; - function coerce2(attr, dflt) { - return Lib.coerce2(containerIn, containerOut, layoutAttributes, attr, dflt); - } - var visible = coerce('visible', !options.cheateronly); var axType = containerOut.type; @@ -84,35 +76,14 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, handleTickValueDefaults(containerIn, containerOut, coerce, axType); handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options); handleTickMarkDefaults(containerIn, containerOut, coerce, options); + handleLineGridDefaults(containerIn, containerOut, coerce, { + dfltColor: dfltColor, + bgColor: options.bgColor, + showGrid: options.showGrid, + attributes: layoutAttributes + }); - var lineColor = coerce2('linecolor', dfltColor), - lineWidth = coerce2('linewidth'), - showLine = coerce('showline', !!lineColor || !!lineWidth); - - if(!showLine) { - delete containerOut.linecolor; - delete containerOut.linewidth; - } - - if(showLine || containerOut.ticks) coerce('mirror'); - - var gridColor = coerce2('gridcolor', colorMix(dfltColor, options.bgColor, lightFraction).toRgbString()), - gridWidth = coerce2('gridwidth'), - showGridLines = coerce('showgrid', options.showGrid || !!gridColor || !!gridWidth); - - if(!showGridLines) { - delete containerOut.gridcolor; - delete containerOut.gridwidth; - } - - var zeroLineColor = coerce2('zerolinecolor', dfltColor), - zeroLineWidth = coerce2('zerolinewidth'), - showZeroLine = coerce('zeroline', options.showGrid || !!zeroLineColor || !!zeroLineWidth); - - if(!showZeroLine) { - delete containerOut.zerolinecolor; - delete containerOut.zerolinewidth; - } + if(containerOut.showline || containerOut.ticks) coerce('mirror'); return containerOut; }; diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index ab558194871..044e45be39e 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -46,7 +46,7 @@ var SHOWZOOMOUTTIP = true; // 's' - bottom only // 'ns' - top and bottom together, difference unchanged // ew - same for horizontal axis -module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { +function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { // mouseDown stores ms of first mousedown event in the last // DBLCLICKDELAY ms on the drag bars // numClicks stores how many mousedowns have been seen @@ -109,7 +109,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { recomputeAxisLists(); - var dragger = makeDragger(plotinfo, ns + ew + 'drag', cursor, x, y, w, h); + var dragger = makeRectDragger(plotinfo, ns + ew + 'drag', cursor, x, y, w, h); // still need to make the element if the axes are disabled // but nuke its events (except for maindrag which needs them for hover) @@ -330,11 +330,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { removeZoombox(gd); dragTail(); - - if(SHOWZOOMOUTTIP && gd.data && gd._context.showTips) { - Lib.notifier(Lib._(gd, 'Double-click to zoom back out'), 'long'); - SHOWZOOMOUTTIP = false; - } + showDoubleClickNotifier(gd); } // scroll zoom, on all draggers except corners @@ -434,9 +430,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { // everything but the corners gets wheel zoom if(ns.length * ew.length !== 1) { - // still seems to be some confusion about onwheel vs onmousewheel... - if(dragger.onwheel !== undefined) dragger.onwheel = zoomWheel; - else if(dragger.onmousewheel !== undefined) dragger.onmousewheel = zoomWheel; + attachWheelEventHandler(dragger, zoomWheel); } // plotDrag: move the plot in response to a drag @@ -785,23 +779,28 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { } return dragger; -}; +} -function makeDragger(plotinfo, dragClass, cursor, x, y, w, h) { +function makeDragger(plotinfo, nodeName, dragClass, cursor) { var dragger3 = plotinfo.draglayer.selectAll('.' + dragClass).data([0]); - dragger3.enter().append('rect') + dragger3.enter().append(nodeName) .classed('drag', true) .classed(dragClass, true) .style({fill: 'transparent', 'stroke-width': 0}) .attr('data-subplot', plotinfo.id); - dragger3.call(Drawing.setRect, x, y, w, h) - .call(setCursor, cursor); + dragger3.call(setCursor, cursor); return dragger3.node(); } +function makeRectDragger(plotinfo, dragClass, cursor, x, y, w, h) { + var dragger = makeDragger(plotinfo, 'rect', dragClass, cursor); + d3.select(dragger).call(Drawing.setRect, x, y, w, h); + return dragger; +} + function isDirectionActive(axList, activeVal) { for(var i = 0; i < axList.length; i++) { if(!axList[i].fixedrange) return activeVal; @@ -924,6 +923,10 @@ function updateZoombox(zb, corners, box, path0, dimmed, lum) { zb.attr('d', path0 + 'M' + (box.l) + ',' + (box.t) + 'v' + (box.h) + 'h' + (box.w) + 'v-' + (box.h) + 'h-' + (box.w) + 'Z'); + transitionZoombox(zb, corners, dimmed, lum); +} + +function transitionZoombox(zb, corners, dimmed, lum) { if(!dimmed) { zb.transition() .style('fill', lum > 0.2 ? 'rgba(0,0,0,0.4)' : @@ -941,6 +944,13 @@ function removeZoombox(gd) { .remove(); } +function showDoubleClickNotifier(gd) { + if(SHOWZOOMOUTTIP && gd.data && gd._context.showTips) { + Lib.notifier(Lib._(gd, 'Double-click to zoom back out'), 'long'); + SHOWZOOMOUTTIP = false; + } +} + function isSelectOrLasso(dragmode) { return dragmode === 'lasso' || dragmode === 'select'; } @@ -1028,3 +1038,27 @@ function calcLinks(constraintGroups, xIDs, yIDs) { xy: isSubplotConstrained }; } + +// still seems to be some confusion about onwheel vs onmousewheel... +function attachWheelEventHandler(element, handler) { + if(element.onwheel !== undefined) element.onwheel = handler; + else if(element.onmousewheel !== undefined) element.onmousewheel = handler; +} + +module.exports = { + makeDragBox: makeDragBox, + + makeDragger: makeDragger, + makeRectDragger: makeRectDragger, + makeZoombox: makeZoombox, + makeCorners: makeCorners, + + updateZoombox: updateZoombox, + xyCorners: xyCorners, + transitionZoombox: transitionZoombox, + removeZoombox: removeZoombox, + clearSelect: clearSelect, + showDoubleClickNotifier: showDoubleClickNotifier, + + attachWheelEventHandler: attachWheelEventHandler +}; diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index f560819d631..779703a2ce4 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -13,7 +13,7 @@ var Fx = require('../../components/fx'); var dragElement = require('../../components/dragelement'); var constants = require('./constants'); -var dragBox = require('./dragbox'); +var makeDragBox = require('./dragbox').makeDragBox; module.exports = function initInteractions(gd) { var fullLayout = gd._fullLayout; @@ -46,7 +46,7 @@ module.exports = function initInteractions(gd) { if(!plotinfo.mainplot) { // main dragger goes over the grids and data, so we use its // mousemove events for all data hover effects - var maindrag = dragBox(gd, plotinfo, xa._offset, ya._offset, + var maindrag = makeDragBox(gd, plotinfo, xa._offset, ya._offset, xa._length, ya._length, 'ns', 'ew'); maindrag.onmousemove = function(evt) { @@ -86,13 +86,13 @@ module.exports = function initInteractions(gd) { // corner draggers if(gd._context.showAxisDragHandles) { - dragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset - DRAGGERSIZE, + makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset - DRAGGERSIZE, DRAGGERSIZE, DRAGGERSIZE, 'n', 'w'); - dragBox(gd, plotinfo, xa._offset + xa._length, ya._offset - DRAGGERSIZE, + makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset - DRAGGERSIZE, DRAGGERSIZE, DRAGGERSIZE, 'n', 'e'); - dragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset + ya._length, + makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset + ya._length, DRAGGERSIZE, DRAGGERSIZE, 's', 'w'); - dragBox(gd, plotinfo, xa._offset + xa._length, ya._offset + ya._length, + makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset + ya._length, DRAGGERSIZE, DRAGGERSIZE, 's', 'e'); } } @@ -103,11 +103,11 @@ module.exports = function initInteractions(gd) { // the y position of the main x axis line var y0 = xa._mainLinePosition; if(xa.side === 'top') y0 -= DRAGGERSIZE; - dragBox(gd, plotinfo, xa._offset + xa._length * 0.1, y0, + makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.1, y0, xa._length * 0.8, DRAGGERSIZE, '', 'ew'); - dragBox(gd, plotinfo, xa._offset, y0, + makeDragBox(gd, plotinfo, xa._offset, y0, xa._length * 0.1, DRAGGERSIZE, '', 'w'); - dragBox(gd, plotinfo, xa._offset + xa._length * 0.9, y0, + makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.9, y0, xa._length * 0.1, DRAGGERSIZE, '', 'e'); } // y axis draggers @@ -115,11 +115,11 @@ module.exports = function initInteractions(gd) { // the x position of the main y axis line var x0 = ya._mainLinePosition; if(ya.side !== 'right') x0 -= DRAGGERSIZE; - dragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.1, + makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.1, DRAGGERSIZE, ya._length * 0.8, 'ns', ''); - dragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.9, + makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.9, DRAGGERSIZE, ya._length * 0.1, 's', ''); - dragBox(gd, plotinfo, x0, ya._offset, + makeDragBox(gd, plotinfo, x0, ya._offset, DRAGGERSIZE, ya._length * 0.1, 'n', ''); } } diff --git a/src/plots/cartesian/line_grid_defaults.js b/src/plots/cartesian/line_grid_defaults.js new file mode 100644 index 00000000000..f122254bfb4 --- /dev/null +++ b/src/plots/cartesian/line_grid_defaults.js @@ -0,0 +1,63 @@ +/** +* Copyright 2012-2018, 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 colorMix = require('tinycolor2').mix; +var lightFraction = require('../../components/color/attributes').lightFraction; +var Lib = require('../../lib'); + +/** + * @param {object} opts : + * - dfltColor {string} : default axis color + * - bgColor {string} : combined subplot bg color + * - blend {number, optional} : blend percentage (to compute dflt grid color) + * - showLine {boolean} : show line by default + * - showGrid {boolean} : show grid by default + * - noZeroLine {boolean} : don't coerce zeroline* attributes + * - attributes {object} : attribute object associated with input containers + */ +module.exports = function handleLineGridDefaults(containerIn, containerOut, coerce, opts) { + opts = opts || {}; + + var dfltColor = opts.dfltColor; + + function coerce2(attr, dflt) { + return Lib.coerce2(containerIn, containerOut, opts.attributes, attr, dflt); + } + + var lineColor = coerce2('linecolor', dfltColor); + var lineWidth = coerce2('linewidth'); + var showLine = coerce('showline', opts.showLine || !!lineColor || !!lineWidth); + + if(!showLine) { + delete containerOut.linecolor; + delete containerOut.linewidth; + } + + var gridColorDflt = colorMix(dfltColor, opts.bgColor, opts.blend || lightFraction).toRgbString(); + var gridColor = coerce2('gridcolor', gridColorDflt); + var gridWidth = coerce2('gridwidth'); + var showGridLines = coerce('showgrid', opts.showGrid || !!gridColor || !!gridWidth); + + if(!showGridLines) { + delete containerOut.gridcolor; + delete containerOut.gridwidth; + } + + if(!opts.noZeroLine) { + var zeroLineColor = coerce2('zerolinecolor', dfltColor); + var zeroLineWidth = coerce2('zerolinewidth'); + var showZeroLine = coerce('zeroline', opts.showGrid || !!zeroLineColor || !!zeroLineWidth); + + if(!showZeroLine) { + delete containerOut.zerolinecolor; + delete containerOut.zerolinewidth; + } + } +}; diff --git a/src/plots/cartesian/set_convert.js b/src/plots/cartesian/set_convert.js index d9aceab36f1..9718f64f23a 100644 --- a/src/plots/cartesian/set_convert.js +++ b/src/plots/cartesian/set_convert.js @@ -276,14 +276,16 @@ module.exports = function setConvert(ax, fullLayout) { * optional param rangeAttr: operate on a different attribute, like * ax._r, rather than ax.range */ - ax.cleanRange = function(rangeAttr) { + ax.cleanRange = function(rangeAttr, opts) { + if(!opts) opts = {}; if(!rangeAttr) rangeAttr = 'range'; - var range = Lib.nestedProperty(ax, rangeAttr).get(), - i, dflt; + + var range = Lib.nestedProperty(ax, rangeAttr).get(); + var i, dflt; if(ax.type === 'date') dflt = Lib.dfltRange(ax.calendar); else if(axLetter === 'y') dflt = constants.DFLTRANGEY; - else dflt = constants.DFLTRANGEX; + else dflt = opts.dfltRange || constants.DFLTRANGEX; // make sure we don't later mutate the defaults dflt = dflt.slice(); diff --git a/src/plots/cartesian/tick_label_defaults.js b/src/plots/cartesian/tick_label_defaults.js index 63879e52950..29f54b91730 100644 --- a/src/plots/cartesian/tick_label_defaults.js +++ b/src/plots/cartesian/tick_label_defaults.js @@ -21,7 +21,7 @@ module.exports = function handleTickLabelDefaults(containerIn, containerOut, coe var tickPrefix = coerce('tickprefix'); if(tickPrefix) coerce('showtickprefix', showAttrDflt); - var tickSuffix = coerce('ticksuffix'); + var tickSuffix = coerce('ticksuffix', options.tickSuffixDflt); if(tickSuffix) coerce('showticksuffix', showAttrDflt); var showTickLabels = coerce('showticklabels'); diff --git a/src/plots/domain_attributes.js b/src/plots/domain_attributes.js new file mode 100644 index 00000000000..d929be4f5c7 --- /dev/null +++ b/src/plots/domain_attributes.js @@ -0,0 +1,72 @@ +/** +* Copyright 2012-2018, 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; + +/** + * Make a xy domain attribute group + * + * @param {object} opts + * @param {string} + * opts.name: name to be inserted in the default description + * @param {boolean} + * opts.trace: set to true for trace containers + * @param {string} + * opts.editType: editType for all pieces + * + * @param {object} extra + * @param {string} + * extra.description: extra description. N.B we use + * a separate extra container to make it compatible with + * the compress_attributes transform. + * + * @return {object} attributes object containing {x,y} as specified + */ +module.exports = function(opts, extra) { + opts = opts || {}; + extra = extra || {}; + + var base = { + valType: 'info_array', + role: 'info', + editType: opts.editType, + items: [ + {valType: 'number', min: 0, max: 1}, + {valType: 'number', min: 0, max: 1} + ], + dflt: [0, 1] + }; + + var namePart = opts.name ? opts.name + ' ' : ''; + var contPart = opts.trace ? 'trace ' : 'subplot '; + var descPart = extra.description ? ' ' + extra.description : ''; + + return { + x: extendFlat({}, base, { + description: [ + 'Sets the horizontal domain of this ', + namePart, + contPart, + '(in plot fraction).', + descPart + ].join('') + }), + y: extendFlat({}, base, { + description: [ + 'Sets the vertical domain of this ', + namePart, + contPart, + '(in plot fraction).', + descPart + ].join('') + }), + editType: opts.editType + }; +}; diff --git a/src/plots/geo/layout/layout_attributes.js b/src/plots/geo/layout/layout_attributes.js index a2d04eec219..4e15c3e680b 100644 --- a/src/plots/geo/layout/layout_attributes.js +++ b/src/plots/geo/layout/layout_attributes.js @@ -9,6 +9,7 @@ 'use strict'; var colorAttrs = require('../../../components/color/attributes'); +var domainAttrs = require('../../domain_attributes'); var constants = require('../constants'); var overrideAll = require('../../../plot_api/edit_types').overrideAll; @@ -65,40 +66,14 @@ var geoAxesAttrs = { }; module.exports = overrideAll({ - domain: { - x: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1}, - {valType: 'number', min: 0, max: 1} - ], - dflt: [0, 1], - description: [ - 'Sets the maximum horizontal domain of this map', - '(in plot fraction).', - 'Note that geo subplots are constrained by domain.', - 'In general, when `projection.scale` is set to 1.', - 'a map will fit either its x or y domain, but not both. ' - ].join(' ') - }, - y: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1}, - {valType: 'number', min: 0, max: 1} - ], - dflt: [0, 1], - description: [ - 'Sets the maximum vertical domain of this map', - '(in plot fraction).', - 'Note that geo subplots are constrained by domain.', - 'In general, when `projection.scale` is set to 1.', - 'a map will fit either its x or y domain, but not both. ' - ].join(' ') - } - }, + domain: domainAttrs({name: 'geo'}, { + description: [ + 'Note that geo subplots are constrained by domain.', + 'In general, when `projection.scale` is set to 1.', + 'a map will fit either its x or y domain, but not both.' + ].join(' ') + }), + resolution: { valType: 'enumerated', values: [110, 50], diff --git a/src/plots/gl3d/layout/layout_attributes.js b/src/plots/gl3d/layout/layout_attributes.js index d0d50b12c36..ec32f254c30 100644 --- a/src/plots/gl3d/layout/layout_attributes.js +++ b/src/plots/gl3d/layout/layout_attributes.js @@ -10,10 +10,10 @@ 'use strict'; var gl3dAxisAttrs = require('./axis_attributes'); +var domainAttrs = require('../../domain_attributes'); var extendFlat = require('../../../lib/extend').extendFlat; var counterRegex = require('../../../lib').counterRegex; - function makeCameraVector(x, y, z) { return { x: { @@ -74,37 +74,7 @@ module.exports = { }), editType: 'camera' }, - domain: { - x: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1, editType: 'plot'}, - {valType: 'number', min: 0, max: 1, editType: 'plot'} - ], - dflt: [0, 1], - editType: 'plot', - description: [ - 'Sets the horizontal domain of this scene', - '(in plot fraction).' - ].join(' ') - }, - y: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1, editType: 'plot'}, - {valType: 'number', min: 0, max: 1, editType: 'plot'} - ], - dflt: [0, 1], - editType: 'plot', - description: [ - 'Sets the vertical domain of this scene', - '(in plot fraction).' - ].join(' ') - }, - editType: 'plot' - }, + domain: domainAttrs({name: 'scene', editType: 'plot'}), aspectmode: { valType: 'enumerated', role: 'info', diff --git a/src/plots/mapbox/layout_attributes.js b/src/plots/mapbox/layout_attributes.js index 69e158f4f98..b373105c29f 100644 --- a/src/plots/mapbox/layout_attributes.js +++ b/src/plots/mapbox/layout_attributes.js @@ -11,11 +11,11 @@ var Lib = require('../../lib'); var defaultLine = require('../../components/color').defaultLine; +var domainAttrs = require('../domain_attributes'); var fontAttrs = require('../font_attributes'); var textposition = require('../../traces/scatter/attributes').textposition; var overrideAll = require('../../plot_api/edit_types').overrideAll; - var fontAttr = fontAttrs({ description: [ 'Sets the icon text font.', @@ -26,34 +26,8 @@ fontAttr.family.dflt = 'Open Sans Regular, Arial Unicode MS Regular'; module.exports = overrideAll({ _arrayAttrRegexps: [Lib.counterRegex('mapbox', '.layers', true)], - domain: { - x: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1}, - {valType: 'number', min: 0, max: 1} - ], - dflt: [0, 1], - description: [ - 'Sets the horizontal domain of this subplot', - '(in plot fraction).' - ].join(' ') - }, - y: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1}, - {valType: 'number', min: 0, max: 1} - ], - dflt: [0, 1], - description: [ - 'Sets the vertical domain of this subplot', - '(in plot fraction).' - ].join(' ') - } - }, + + domain: domainAttrs({name: 'mapbox'}), accesstoken: { valType: 'string', diff --git a/src/plots/plots.js b/src/plots/plots.js index 2d5f90355d7..a54052cf16e 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1492,11 +1492,21 @@ plots.purge = function(gd) { plots.style = function(gd) { var _modules = gd._fullLayout._modules; + var styleModules = []; + var i; + + // some trace modules reuse the same style method, + // make sure to not unnecessary call them multiple times. - for(var i = 0; i < _modules.length; i++) { + for(i = 0; i < _modules.length; i++) { var _module = _modules[i]; + if(_module.style) { + Lib.pushUnique(styleModules, _module.style); + } + } - if(_module.style) _module.style(gd); + for(i = 0; i < styleModules.length; i++) { + styleModules[i](gd); } }; @@ -2312,6 +2322,15 @@ plots.doCalcdata = function(gd, traces) { trace._arrayAttrs = PlotSchema.findArrayAttributes(trace); } + // add polar axes to axis list + var polarIds = fullLayout._subplots.polar || []; + for(i = 0; i < polarIds.length; i++) { + axList.push( + fullLayout[polarIds[i]].radialaxis, + fullLayout[polarIds[i]].angularaxis + ); + } + initCategories(axList); var hasCalcTransform = false; @@ -2422,19 +2441,6 @@ plots.generalUpdatePerTraceModule = function(subplot, subplotCalcData, subplotLa traceHash = {}, i; - function filterVisible(calcDataIn) { - var calcDataOut = []; - - for(var i = 0; i < calcDataIn.length; i++) { - var calcTrace = calcDataIn[i], - trace = calcTrace[0].trace; - - if(trace.visible === true) calcDataOut.push(calcTrace); - } - - return calcDataOut; - } - // build up moduleName -> calcData hash for(i = 0; i < subplotCalcData.length; i++) { var calcTraces = subplotCalcData[i], @@ -2467,7 +2473,7 @@ plots.generalUpdatePerTraceModule = function(subplot, subplotCalcData, subplotLa var moduleCalcData = traceHash[moduleName]; var _module = moduleCalcData[0][0].trace._module; - _module.plot(subplot, filterVisible(moduleCalcData), subplotLayout); + _module.plot(subplot, Lib.filterVisible(moduleCalcData), subplotLayout); } // update moduleName -> calcData hash diff --git a/src/plots/polar/constants.js b/src/plots/polar/constants.js new file mode 100644 index 00000000000..ca0c0734803 --- /dev/null +++ b/src/plots/polar/constants.js @@ -0,0 +1,43 @@ +/** +* Copyright 2012-2018, 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 = { + attr: 'subplot', + name: 'polar', + + axisNames: ['angularaxis', 'radialaxis'], + axisName2dataArray: {angularaxis: 'theta', radialaxis: 'r'}, + + layerNames: [ + 'draglayer', + 'plotbg', + 'backplot', + 'angular-grid', + 'radial-grid', + 'frontplot', + 'angular-axis', + 'radial-axis', + 'angular-line', + 'radial-line' + ], + + radialDragBoxSize: 50, + angularDragBoxSize: 30, + cornerLen: 25, + cornerHalfWidth: 2, + + // pixels to move mouse before you stop clamping to starting point + MINDRAG: 8, + // smallest radial distance [px] allowed for a zoombox + MINZOOM: 20, + // distance [px] off (r=0) or (r=radius) where we transition + // from single-sided to two-sided radial zoom + OFFEDGE: 20 +}; diff --git a/src/plots/polar/helpers.js b/src/plots/polar/helpers.js new file mode 100644 index 00000000000..6a85594d1a5 --- /dev/null +++ b/src/plots/polar/helpers.js @@ -0,0 +1,61 @@ +/** +* Copyright 2012-2018, 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'); + +exports.setConvertAngular = function setConvertAngular(ax) { + var dir = {clockwise: -1, counterclockwise: 1}[ax.direction]; + var rot = Lib.deg2rad(ax.rotation); + var _c2rad; + var _rad2c; + + function getTotalNumberOfCategories() { + return ax.period ? + Math.max(ax.period, ax._categories.length) : + ax._categories.length; + } + + if(ax.type === 'linear') { + _c2rad = function(v, unit) { + if(unit === 'degrees') return Lib.deg2rad(v); + return v; + }; + _rad2c = function(v, unit) { + if(unit === 'degrees') return Lib.rad2deg(v); + return v; + }; + } + else if(ax.type === 'category') { + _c2rad = function(v) { + var tot = getTotalNumberOfCategories(); + return v * 2 * Math.PI / tot; + }; + _rad2c = function(v) { + var tot = getTotalNumberOfCategories(); + return v * tot / Math.PI / 2; + }; + } + + function transformRad(v) { return dir * v + rot; } + function unTransformRad(v) { return (v - rot) / dir; } + + // use the shift 'sector' to get right tick labels for non-default + // angularaxis 'rotation' and/or 'direction' + ax.unTransformRad = unTransformRad; + + // this version is used on hover + ax._c2rad = _c2rad; + + ax.c2rad = function(v, unit) { return transformRad(_c2rad(v, unit)); }; + ax.rad2c = function(v, unit) { return _rad2c(unTransformRad(v), unit); }; + + ax.c2deg = function(v, unit) { return Lib.rad2deg(ax.c2rad(v, unit)); }; + ax.deg2c = function(v, unit) { return ax.rad2c(Lib.deg2rad(v), unit); }; +}; diff --git a/src/plots/polar/index.js b/src/plots/polar/index.js index 1c281e96004..c0f134f52fe 100644 --- a/src/plots/polar/index.js +++ b/src/plots/polar/index.js @@ -8,6 +8,76 @@ 'use strict'; -var Polar = module.exports = require('./micropolar'); +var getSubplotCalcData = require('../get_data').getSubplotCalcData; +var counterRegex = require('../../lib').counterRegex; -Polar.manager = require('./micropolar_manager'); +var createPolar = require('./polar'); +var constants = require('./constants'); + +var attr = constants.attr; +var name = constants.name; +var counter = counterRegex(name); + +var attributes = {}; +attributes[attr] = { + valType: 'subplotid', + role: 'info', + dflt: name, + editType: 'calc', + description: [ + 'Sets a reference between this trace\'s data coordinates and', + 'a polar subplot.', + 'If *polar* (the default value), the data refer to `layout.polar`.', + 'If *polar2*, the data refer to `layout.polar2`, and so on.' + ].join(' ') +}; + +function plot(gd) { + var fullLayout = gd._fullLayout; + var calcData = gd.calcdata; + var subplotIds = fullLayout._subplots[name]; + + for(var i = 0; i < subplotIds.length; i++) { + var id = subplotIds[i]; + var subplotCalcData = getSubplotCalcData(calcData, name, id); + var subplot = fullLayout[id]._subplot; + + if(!subplot) { + subplot = createPolar(gd, id); + fullLayout[id]._subplot = subplot; + } + + subplot.plot(subplotCalcData, fullLayout, gd._promises); + } +} + +function clean(newFullData, newFullLayout, oldFullData, oldFullLayout) { + var oldIds = oldFullLayout._subplots[name] || []; + + for(var i = 0; i < oldIds.length; i++) { + var id = oldIds[i]; + var oldSubplot = oldFullLayout[id]._subplot; + + if(!newFullLayout[id] && !!oldSubplot) { + oldSubplot.framework.remove(); + oldSubplot.layers['radial-axis-title'].remove(); + + for(var k in oldSubplot.clipPaths) { + oldSubplot.clipPaths[k].remove(); + } + } + } +} + +module.exports = { + attr: attr, + name: name, + idRoot: name, + idRegex: counter, + attrRegex: counter, + attributes: attributes, + layoutAttributes: require('./layout_attributes'), + supplyLayoutDefaults: require('./layout_defaults'), + plot: plot, + clean: clean +}; diff --git a/src/plots/polar/layout_attributes.js b/src/plots/polar/layout_attributes.js new file mode 100644 index 00000000000..ff663ed518a --- /dev/null +++ b/src/plots/polar/layout_attributes.js @@ -0,0 +1,279 @@ +/** +* Copyright 2012-2018, 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 colorAttrs = require('../../components/color/attributes'); +var axesAttrs = require('../cartesian/layout_attributes'); +var domainAttrs = require('../domain_attributes'); +var extendFlat = require('../../lib').extendFlat; +var overrideAll = require('../../plot_api/edit_types').overrideAll; + +var axisLineGridAttr = overrideAll({ + color: axesAttrs.color, + showline: extendFlat({}, axesAttrs.showline, {dflt: true}), + linecolor: axesAttrs.linecolor, + linewidth: axesAttrs.linewidth, + showgrid: extendFlat({}, axesAttrs.showgrid, {dflt: true}), + gridcolor: axesAttrs.gridcolor, + gridwidth: axesAttrs.gridwidth + + // TODO add spike* attributes down the road + + // should we add zeroline* attributes? + +}, 'plot', 'from-root'); + +var axisTickAttrs = overrideAll({ + tickmode: axesAttrs.tickmode, + nticks: axesAttrs.nticks, + tick0: axesAttrs.tick0, + dtick: axesAttrs.dtick, + tickvals: axesAttrs.tickvals, + ticktext: axesAttrs.ticktext, + ticks: axesAttrs.ticks, + ticklen: axesAttrs.ticklen, + tickwidth: axesAttrs.tickwidth, + tickcolor: axesAttrs.tickcolor, + showticklabels: axesAttrs.showticklabels, + showtickprefix: axesAttrs.showtickprefix, + tickprefix: axesAttrs.tickprefix, + showticksuffix: axesAttrs.showticksuffix, + ticksuffix: axesAttrs.ticksuffix, + showexponent: axesAttrs.showexponent, + exponentformat: axesAttrs.exponentformat, + separatethousands: axesAttrs.separatethousands, + tickfont: axesAttrs.tickfont, + tickangle: axesAttrs.tickangle, + tickformat: axesAttrs.tickformat, + tickformatstops: axesAttrs.tickformatstops, + layer: axesAttrs.layer +}, 'plot', 'from-root'); + +var radialAxisAttrs = { + visible: extendFlat({}, axesAttrs.visible, {dflt: true}), + type: axesAttrs.type, + + autorange: axesAttrs.autorange, + rangemode: { + valType: 'enumerated', + values: ['tozero', 'nonnegative', 'normal'], + dflt: 'tozero', + role: 'style', + editType: 'calc', + description: [ + 'If *tozero*`, the range extends to 0,', + 'regardless of the input data', + 'If *nonnegative*, the range is non-negative,', + 'regardless of the input data.', + 'If *normal*, the range is computed in relation to the extrema', + 'of the input data (same behavior as for cartesian axes).' + ].join(' ') + }, + range: axesAttrs.range, + + categoryorder: axesAttrs.categoryorder, + categoryarray: axesAttrs.categoryarray, + + angle: { + valType: 'angle', + editType: 'plot', + role: 'info', + description: [ + 'Sets the angle (in degrees) from which the radial axis is drawn.', + 'Note that by default, radial axis line on the theta=0 line', + 'corresponds to a line pointing right (like what mathematicians prefer).', + 'Defaults to the first `polar.sector` angle.' + ].join(' ') + }, + + side: { + valType: 'enumerated', + // TODO add 'center' for `showline: false` radial axes + values: ['clockwise', 'counterclockwise'], + dflt: 'clockwise', + editType: 'plot', + role: 'info', + description: [ + 'Determines on which side of radial axis line', + 'the tick and tick labels appear.' + ].join(' ') + }, + + + title: extendFlat({}, axesAttrs.title, {editType: 'plot', dflt: ''}), + titlefont: overrideAll(axesAttrs.titlefont, 'plot', 'from-root'), + // might need a 'titleside' and even 'titledirection' down the road + + hoverformat: axesAttrs.hoverformat, + + // More attributes: + + // We'll need some attribute that determines the span + // to draw donut-like charts + // e.g. https://github.com/matplotlib/matplotlib/issues/4217 + // + // maybe something like 'span' or 'hole' (like pie, but pie set it in data coords?) + // span: {}, + // hole: 1 + + // maybe should add a boolean to enable square grid lines + // and square axis lines + // (most common in radar-like charts) + // e.g. squareline/squaregrid or showline/showgrid: 'square' (on-top of true) + + editType: 'calc' +}; + +extendFlat( + radialAxisAttrs, + + // N.B. radialaxis grid lines are circular, + // but radialaxis lines are straight from circle center to outer bound + axisLineGridAttr, + axisTickAttrs +); + +var angularAxisAttrs = { + visible: extendFlat({}, axesAttrs.visible, {dflt: true}), + type: { + valType: 'enumerated', + // 'linear' should maybe be called 'angle' or 'angular' here + // to make clear that axis here is periodic and more tightly match + // `thetaunit`? + // + // skip 'date' for first push + // no 'log' for now + values: ['-', 'linear', 'category'], + dflt: '-', + role: 'info', + editType: 'calc', + description: [ + 'Sets the angular axis type.', + 'If *linear*, set `thetaunit` to determine the unit in which axis value are shown.', + 'If *category, use `period` to set the number of integer coordinates around polar axis.' + ].join(' ') + }, + + categoryorder: axesAttrs.categoryorder, + categoryarray: axesAttrs.categoryarray, + + thetaunit: { + valType: 'enumerated', + values: ['radians', 'degrees'], + dflt: 'degrees', + role: 'info', + editType: 'calc', + description: [ + 'Sets the format unit of the formatted *theta* values.', + 'Has an effect only when `angularaxis.type` is *linear*.' + ].join(' ') + }, + + period: { + valType: 'number', + editType: 'calc', + min: 0, + role: 'info', + description: [ + 'Set the angular period.', + 'Has an effect only when `angularaxis.type` is *category*.', + ].join(' ') + // Examples for date axes: + // + // - period that equals the timeseries length + // http://flowingdata.com/2017/01/24/one-dataset-visualized-25-ways/18-polar-coordinates/ + // - and 1-year periods (focusing on seasonal change0 + // http://otexts.org/fpp2/seasonal-plots.html + // https://blogs.scientificamerican.com/sa-visual/why-are-so-many-babies-born-around-8-00-a-m/ + // http://www.seasonaladjustment.com/2012/09/05/clock-plot-visualising-seasonality-using-r-and-ggplot2-part-3/ + // https://i.pinimg.com/736x/49/b9/72/49b972ccb3206a1a6d6f870dac543280.jpg + // https://www.climate-lab-book.ac.uk/spirals/ + }, + + direction: { + valType: 'enumerated', + values: ['counterclockwise', 'clockwise'], + dflt: 'counterclockwise', + role: 'info', + editType: 'calc', + description: [ + 'Sets the direction corresponding to positive angles.' + ].join(' ') + }, + + rotation: { + valType: 'angle', + editType: 'calc', + role: 'info', + description: [ + 'Sets that start position (in degrees) of the angular axis', + 'By default, polar subplots with `direction` set to *counterclockwise*', + 'get a `rotation` of *0*', + 'which corresponds to due East (like what mathematicians prefer).', + 'In turn, polar with `direction` set to *clockwise* get a rotation of *90*', + 'which corresponds to due North (like on a compass),' + ].join(' ') + }, + + hoverformat: axesAttrs.hoverformat, + + editType: 'calc' +}; + +extendFlat( + angularAxisAttrs, + + // N.B. angular grid lines are straight lines from circle center to outer bound + // the angular line is circular bounding the polar plot area. + axisLineGridAttr, + + // N.B. ticksuffix defaults to '°' for angular axes with `thetaunit: 'degrees'` + axisTickAttrs +); + +module.exports = { + // TODO for x/y/zoom system for paper-based zooming: + // x: {}, + // y: {}, + // zoom: {}, + + domain: domainAttrs({name: 'polar', editType: 'plot'}), + + sector: { + valType: 'info_array', + items: [ + {valType: 'number', editType: 'plot'}, + {valType: 'number', editType: 'plot'} + ], + dflt: [0, 360], + role: 'info', + editType: 'plot', + description: [ + 'Sets angular span of this polar subplot with two angles (in degrees).', + 'Sector are assumed to be spanned in the counterclockwise direction', + 'with *0* corresponding to rightmost limit of the polar subplot.' + ].join(' ') + }, + + bgcolor: { + valType: 'color', + role: 'style', + editType: 'plot', + dflt: colorAttrs.background, + description: 'Set the background color of the subplot' + }, + + radialaxis: radialAxisAttrs, + angularaxis: angularAxisAttrs, + + // TODO maybe? + // annotations: + + editType: 'calc' +}; diff --git a/src/plots/polar/layout_defaults.js b/src/plots/polar/layout_defaults.js new file mode 100644 index 00000000000..87fe38826a6 --- /dev/null +++ b/src/plots/polar/layout_defaults.js @@ -0,0 +1,224 @@ +/** +* Copyright 2012-2018, 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 Color = require('../../components/color'); +var handleSubplotDefaults = require('../subplot_defaults'); +var getSubplotData = require('../get_data').getSubplotData; + +var handleTickValueDefaults = require('../cartesian/tick_value_defaults'); +var handleTickMarkDefaults = require('../cartesian/tick_mark_defaults'); +var handleTickLabelDefaults = require('../cartesian/tick_label_defaults'); +var handleCategoryOrderDefaults = require('../cartesian/category_order_defaults'); +var handleLineGridDefaults = require('../cartesian/line_grid_defaults'); +var autoType = require('../cartesian/axis_autotype'); +var orderedCategories = require('../cartesian/ordered_categories'); +var setConvert = require('../cartesian/set_convert'); + +var setConvertAngular = require('./helpers').setConvertAngular; +var layoutAttributes = require('./layout_attributes'); +var constants = require('./constants'); +var axisNames = constants.axisNames; + +function handleDefaults(contIn, contOut, coerce, opts) { + var bgColor = coerce('bgcolor'); + opts.bgColor = Color.combine(bgColor, opts.paper_bgcolor); + + var sector = coerce('sector'); + + // could optimize, subplotData is not always needed! + var subplotData = getSubplotData(opts.fullData, constants.name, opts.id); + var layoutOut = opts.layoutOut; + var axName; + + function coerceAxis(attr, dflt) { + return coerce(axName + '.' + attr, dflt); + } + + for(var i = 0; i < axisNames.length; i++) { + axName = axisNames[i]; + + if(!Lib.isPlainObject(contIn[axName])) { + contIn[axName] = {}; + } + + var axIn = contIn[axName]; + var axOut = contOut[axName] = {}; + axOut._id = axOut._name = axName; + + var dataAttr = constants.axisName2dataArray[axName]; + var axType = handleAxisTypeDefaults(axIn, axOut, coerceAxis, subplotData, dataAttr); + + handleCategoryOrderDefaults(axIn, axOut, coerceAxis); + axOut._initialCategories = axType === 'category' ? + orderedCategories(dataAttr, axOut.categoryorder, axOut.categoryarray, subplotData) : + []; + + var visible = coerceAxis('visible'); + setConvert(axOut, layoutOut); + + var dfltColor; + var dfltFontColor; + + if(visible) { + dfltColor = coerceAxis('color'); + dfltFontColor = (dfltColor === axIn.color) ? dfltColor : opts.font.color; + } + + // We don't want to make downstream code call ax.setScale, + // as both radial and angular axes don't have a set domain. + // Furthermore, angular axes don't have a set range. + // + // Mocked domains and ranges are set by the polar subplot instances, + // but Axes.expand uses the sign of _m to determine which padding value + // to use. + // + // By setting, _m to 1 here, we make Axes.expand think that range[1] > range[0], + // and vice-versa for `autorange: 'reversed'` below. + axOut._m = 1; + + switch(axName) { + case 'radialaxis': + var autoRange = coerceAxis('autorange', !axOut.isValidRange(axIn.range)); + if(autoRange) coerceAxis('rangemode'); + if(autoRange === 'reversed') axOut._m = -1; + + coerceAxis('range'); + axOut.cleanRange('range', {dfltRange: [0, 1]}); + + if(visible) { + coerceAxis('side'); + coerceAxis('angle', sector[0]); + + coerceAxis('title'); + Lib.coerceFont(coerceAxis, 'titlefont', { + family: opts.font.family, + size: Math.round(opts.font.size * 1.2), + color: dfltFontColor + }); + } + break; + + case 'angularaxis': + // We do not support 'true' date angular axes yet, + // users can still plot dates on angular axes by setting + // `angularaxis.type: 'category'`. + // + // Here, if a date angular axes is detected, we make + // all its corresponding traces invisible, so that + // when we do add support for data angular axes, the new + // behavior won't conflict with existing behavior + if(axType === 'date') { + Lib.log('Polar plots do not support date angular axes yet.'); + + for(var j = 0; j < subplotData.length; j++) { + subplotData[j].visible = false; + } + + // turn this into a 'dummy' linear axis so that + // the subplot still renders ok + axType = axIn.type = axOut.type = 'linear'; + } + + if(axType === 'linear') { + coerceAxis('thetaunit'); + } else { + coerceAxis('period'); + } + + var direction = coerceAxis('direction'); + coerceAxis('rotation', {counterclockwise: 0, clockwise: 90}[direction]); + + setConvertAngular(axOut); + break; + } + + if(visible) { + handleTickValueDefaults(axIn, axOut, coerceAxis, axOut.type); + handleTickLabelDefaults(axIn, axOut, coerceAxis, axOut.type, { + noHover: false, + tickSuffixDflt: axOut.thetaunit === 'degrees' ? '°' : undefined + }); + handleTickMarkDefaults(axIn, axOut, coerceAxis, {outerTicks: true}); + + var showTickLabels = coerceAxis('showticklabels'); + if(showTickLabels) { + Lib.coerceFont(coerceAxis, 'tickfont', { + family: opts.font.family, + size: opts.font.size, + color: dfltFontColor + }); + coerceAxis('tickangle'); + coerceAxis('tickformat'); + } + + handleLineGridDefaults(axIn, axOut, coerceAxis, { + dfltColor: dfltColor, + bgColor: opts.bgColor, + // default grid color is darker here (60%, vs cartesian default ~91%) + // because the grid is not square so the eye needs heavier cues to follow + blend: 60, + showLine: true, + showGrid: true, + noZeroLine: true, + attributes: layoutAttributes[axName] + }); + + coerceAxis('layer'); + } + + coerceAxis('hoverformat'); + + axOut._input = axIn; + } +} + +function handleAxisTypeDefaults(axIn, axOut, coerce, subplotData, dataAttr) { + var axType = coerce('type'); + + if(axType === '-') { + var trace; + + for(var i = 0; i < subplotData.length; i++) { + if(subplotData[i].visible) { + trace = subplotData[i]; + break; + } + } + + if(trace) { + axOut.type = autoType(trace[dataAttr], 'gregorian'); + } + + if(axOut.type === '-') { + axOut.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 + axIn.type = axOut.type; + } + } + + return axOut.type; +} + +module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { + handleSubplotDefaults(layoutIn, layoutOut, fullData, { + type: constants.name, + attributes: layoutAttributes, + handleDefaults: handleDefaults, + font: layoutOut.font, + paper_bgcolor: layoutOut.paper_bgcolor, + fullData: fullData, + layoutOut: layoutOut + }); +}; diff --git a/src/plots/polar/area_attributes.js b/src/plots/polar/legacy/area_attributes.js similarity index 89% rename from src/plots/polar/area_attributes.js rename to src/plots/polar/legacy/area_attributes.js index 9b775a81d20..4ab7ae76f87 100644 --- a/src/plots/polar/area_attributes.js +++ b/src/plots/polar/legacy/area_attributes.js @@ -8,7 +8,7 @@ 'use strict'; -var scatterAttrs = require('../../traces/scatter/attributes'); +var scatterAttrs = require('../../../traces/scatter/attributes'); var scatterMarkerAttrs = scatterAttrs.marker; module.exports = { diff --git a/src/plots/polar/axis_attributes.js b/src/plots/polar/legacy/axis_attributes.js similarity index 95% rename from src/plots/polar/axis_attributes.js rename to src/plots/polar/legacy/axis_attributes.js index 7aa0dd8ac54..3a6d899a198 100644 --- a/src/plots/polar/axis_attributes.js +++ b/src/plots/polar/legacy/axis_attributes.js @@ -9,9 +9,9 @@ 'use strict'; -var axesAttrs = require('../cartesian/layout_attributes'); -var extendFlat = require('../../lib/extend').extendFlat; -var overrideAll = require('../../plot_api/edit_types').overrideAll; +var axesAttrs = require('../../cartesian/layout_attributes'); +var extendFlat = require('../../../lib/extend').extendFlat; +var overrideAll = require('../../../plot_api/edit_types').overrideAll; var domainAttr = extendFlat({}, axesAttrs.domain, { description: [ diff --git a/src/plots/polar/legacy/index.js b/src/plots/polar/legacy/index.js new file mode 100644 index 00000000000..1c281e96004 --- /dev/null +++ b/src/plots/polar/legacy/index.js @@ -0,0 +1,13 @@ +/** +* Copyright 2012-2018, 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 Polar = module.exports = require('./micropolar'); + +Polar.manager = require('./micropolar_manager'); diff --git a/src/plots/polar/micropolar.js b/src/plots/polar/legacy/micropolar.js similarity index 99% rename from src/plots/polar/micropolar.js rename to src/plots/polar/legacy/micropolar.js index 11287948690..eba2a693341 100644 --- a/src/plots/polar/micropolar.js +++ b/src/plots/polar/legacy/micropolar.js @@ -7,9 +7,9 @@ */ var d3 = require('d3'); -var Lib = require('../../lib'); +var Lib = require('../../../lib'); var extendDeepAll = Lib.extendDeepAll; -var MID_SHIFT = require('../../constants/alignment').MID_SHIFT; +var MID_SHIFT = require('../../../constants/alignment').MID_SHIFT; var µ = module.exports = { version: '0.2.2' }; diff --git a/src/plots/polar/micropolar_manager.js b/src/plots/polar/legacy/micropolar_manager.js similarity index 97% rename from src/plots/polar/micropolar_manager.js rename to src/plots/polar/legacy/micropolar_manager.js index 74c4ac17ed0..a3fa83e0d78 100644 --- a/src/plots/polar/micropolar_manager.js +++ b/src/plots/polar/legacy/micropolar_manager.js @@ -11,8 +11,8 @@ 'use strict'; var d3 = require('d3'); -var Lib = require('../../lib'); -var Color = require('../../components/color'); +var Lib = require('../../../lib'); +var Color = require('../../../components/color'); var micropolar = require('./micropolar'); var UndoManager = require('./undo_manager'); diff --git a/src/plots/polar/undo_manager.js b/src/plots/polar/legacy/undo_manager.js similarity index 100% rename from src/plots/polar/undo_manager.js rename to src/plots/polar/legacy/undo_manager.js diff --git a/src/plots/polar/polar.js b/src/plots/polar/polar.js new file mode 100644 index 00000000000..d1cad6e1f62 --- /dev/null +++ b/src/plots/polar/polar.js @@ -0,0 +1,1201 @@ +/** +* Copyright 2012-2018, 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 tinycolor = require('tinycolor2'); + +var Plotly = require('../../plotly'); +var Registry = require('../../registry'); +var Lib = require('../../lib'); +var Color = require('../../components/color'); +var Drawing = require('../../components/drawing'); +var Plots = require('../plots'); +var Axes = require('../cartesian/axes'); +var dragElement = require('../../components/dragelement'); +var dragBox = require('../cartesian/dragbox'); +var Fx = require('../../components/fx'); +var Titles = require('../../components/titles'); +var prepSelect = require('../cartesian/select'); +var setCursor = require('../../lib/setcursor'); + +var MID_SHIFT = require('../../constants/alignment').MID_SHIFT; + +var _ = Lib._; +var deg2rad = Lib.deg2rad; +var rad2deg = Lib.rad2deg; +var wrap360 = Lib.wrap360; +var wrap180 = Lib.wrap180; + +var setConvertAngular = require('./helpers').setConvertAngular; +var constants = require('./constants'); + +function Polar(gd, id) { + this.id = id; + this.gd = gd; + + this._hasClipOnAxisFalse = null; + this.traceHash = {}; + this.layers = {}; + this.clipPaths = {}; + this.clipIds = {}; + this.viewInitial = {}; + + var fullLayout = gd._fullLayout; + var clipIdBase = 'clip' + fullLayout._uid + id; + + this.clipIds.circle = clipIdBase + '-circle'; + this.clipPaths.circle = fullLayout._clips.append('clipPath') + .attr('id', this.clipIds.circle); + this.clipPaths.circle.append('path'); + + this.framework = fullLayout._polarlayer.append('g') + .attr('class', id); + + // unfortunately, we have to keep track of some axis tick settings + // so that we don't have to call Axes.doTicks with its special redraw flag + this.radialTickLayout = null; + this.angularTickLayout = null; +} + +var proto = Polar.prototype; + +module.exports = function createPolar(gd, id) { + return new Polar(gd, id); +}; + +proto.plot = function(polarCalcData, fullLayout) { + var _this = this; + var polarLayout = fullLayout[_this.id]; + + _this._hasClipOnAxisFalse = false; + for(var i = 0; i < polarCalcData.length; i++) { + var trace = polarCalcData[i][0].trace; + if(trace.cliponaxis === false) { + _this._hasClipOnAxisFalse = true; + break; + } + } + + _this.updateLayers(fullLayout, polarLayout); + _this.updateLayout(fullLayout, polarLayout); + Plots.generalUpdatePerTraceModule(_this, polarCalcData, polarLayout); + _this.updateFx(fullLayout, polarLayout); +}; + +proto.updateLayers = function(fullLayout, polarLayout) { + var _this = this; + var layers = _this.layers; + var radialLayout = polarLayout.radialaxis; + var angularLayout = polarLayout.angularaxis; + var layerNames = constants.layerNames; + + var frontPlotIndex = layerNames.indexOf('frontplot'); + var layerData = layerNames.slice(0, frontPlotIndex); + var isAngularAxisBelowTraces = angularLayout.layer === 'below traces'; + var isRadialAxisBelowTraces = radialLayout.layer === 'below traces'; + + if(isAngularAxisBelowTraces) layerData.push('angular-axis'); + if(isRadialAxisBelowTraces) layerData.push('radial-axis'); + if(isAngularAxisBelowTraces) layerData.push('angular-line'); + if(isRadialAxisBelowTraces) layerData.push('radial-line'); + + layerData.push('frontplot'); + + if(!isAngularAxisBelowTraces) layerData.push('angular-axis'); + if(!isRadialAxisBelowTraces) layerData.push('radial-axis'); + if(!isAngularAxisBelowTraces) layerData.push('angular-line'); + if(!isRadialAxisBelowTraces) layerData.push('radial-line'); + + var join = _this.framework.selectAll('.polarsublayer') + .data(layerData, String); + + join.enter().append('g') + .attr('class', function(d) { return 'polarsublayer ' + d;}) + .each(function(d) { + var sel = layers[d] = d3.select(this); + + switch(d) { + case 'frontplot': + sel.append('g').classed('scatterlayer', true); + break; + case 'backplot': + sel.append('g').classed('maplayer', true); + break; + case 'plotbg': + layers.bgcircle = sel.append('path'); + break; + case 'radial-grid': + sel.style('fill', 'none'); + sel.append('g').classed('x', 1); + break; + case 'angular-grid': + sel.style('fill', 'none'); + sel.append('g').classed('angular', 1); + break; + case 'radial-line': + sel.append('line').style('fill', 'none'); + break; + case 'angular-line': + sel.append('path').style('fill', 'none'); + break; + } + }); + + join.order(); +}; + +proto.updateLayout = function(fullLayout, polarLayout) { + var _this = this; + var layers = _this.layers; + var gs = fullLayout._size; + + // layout domains + var xDomain = polarLayout.domain.x; + var yDomain = polarLayout.domain.y; + // offsets from paper edge to layout domain box + _this.xOffset = gs.l + gs.w * xDomain[0]; + _this.yOffset = gs.t + gs.h * (1 - yDomain[1]); + // lengths of the layout domain box + var xLength = _this.xLength = gs.w * (xDomain[1] - xDomain[0]); + var yLength = _this.yLength = gs.h * (yDomain[1] - yDomain[0]); + // sector to plot + var sector = _this.sector = polarLayout.sector; + var sectorBBox = _this.sectorBBox = computeSectorBBox(sector); + var dxSectorBBox = sectorBBox[2] - sectorBBox[0]; + var dySectorBBox = sectorBBox[3] - sectorBBox[1]; + // aspect ratios + var arDomain = yLength / xLength; + var arSector = Math.abs(dySectorBBox / dxSectorBBox); + // actual lengths and domains of subplot box + var xLength2, yLength2; + var xDomain2, yDomain2; + var gap; + if(arDomain > arSector) { + xLength2 = xLength; + yLength2 = xLength * arSector; + gap = (yLength - yLength2) / gs.h / 2; + xDomain2 = [xDomain[0], xDomain[1]]; + yDomain2 = [yDomain[0] + gap, yDomain[1] - gap]; + } else { + xLength2 = yLength / arSector; + yLength2 = yLength; + gap = (xLength - xLength2) / gs.w / 2; + xDomain2 = [xDomain[0] + gap, xDomain[1] - gap]; + yDomain2 = [yDomain[0], yDomain[1]]; + } + _this.xLength2 = xLength2; + _this.yLength2 = yLength2; + _this.xDomain2 = xDomain2; + _this.yDomain2 = yDomain2; + // actual offsets from paper edge to the subplot box top-left corner + var xOffset2 = _this.xOffset2 = gs.l + gs.w * xDomain2[0]; + var yOffset2 = _this.yOffset2 = gs.t + gs.h * (1 - yDomain2[1]); + // circle radius in px + var radius = _this.radius = xLength2 / dxSectorBBox; + // circle center position in px + var cx = _this.cx = xOffset2 - radius * sectorBBox[0]; + var cy = _this.cy = yOffset2 + radius * sectorBBox[3]; + // circle center in the coordinate system of plot area + var cxx = _this.cxx = cx - xOffset2; + var cyy = _this.cyy = cy - yOffset2; + + _this.updateRadialAxis(fullLayout, polarLayout); + _this.updateRadialAxisTitle(fullLayout, polarLayout); + _this.updateAngularAxis(fullLayout, polarLayout); + + var radialRange = _this.radialAxis.range; + var rSpan = radialRange[1] - radialRange[0]; + + var xaxis = _this.xaxis = { + type: 'linear', + _id: 'x', + range: [sectorBBox[0] * rSpan, sectorBBox[2] * rSpan], + domain: xDomain2 + }; + Axes.setConvert(xaxis, fullLayout); + xaxis.setScale(); + + var yaxis = _this.yaxis = { + type: 'linear', + _id: 'y', + range: [sectorBBox[1] * rSpan, sectorBBox[3] * rSpan], + domain: yDomain2 + }; + Axes.setConvert(yaxis, fullLayout); + yaxis.setScale(); + + xaxis.isPtWithinRange = function(d) { return _this.isPtWithinSector(d); }; + yaxis.isPtWithinRange = function() { return true; }; + + layers.frontplot + .attr('transform', strTranslate(xOffset2, yOffset2)) + .call(Drawing.setClipUrl, _this._hasClipOnAxisFalse ? null : _this.clipIds.circle); + + layers.bgcircle.attr({ + d: pathSectorClosed(radius, sector), + transform: strTranslate(cx, cy) + }) + .call(Color.fill, polarLayout.bgcolor); + + _this.clipPaths.circle.select('path') + .attr('d', pathSectorClosed(radius, sector)) + .attr('transform', strTranslate(cxx, cyy)); + + // remove crispEdges - all the off-square angles in polar plots + // make these counterproductive. + _this.framework.selectAll('.crisp').classed('crisp', 0); +}; + +proto.updateRadialAxis = function(fullLayout, polarLayout) { + var _this = this; + var gd = _this.gd; + var layers = _this.layers; + var radius = _this.radius; + var cx = _this.cx; + var cy = _this.cy; + var gs = fullLayout._size; + var radialLayout = polarLayout.radialaxis; + var sector = polarLayout.sector; + var a0 = wrap360(sector[0]); + + _this.fillViewInitialKey('radialaxis.angle', radialLayout.angle); + + var ax = _this.radialAxis = Lib.extendFlat({}, radialLayout, { + _axislayer: layers['radial-axis'], + _gridlayer: layers['radial-grid'], + + // make this an 'x' axis to make positioning (especially rotation) easier + _id: 'x', + _pos: 0, + + // convert to 'x' axis equivalent + side: {counterclockwise: 'top', clockwise: 'bottom'}[radialLayout.side], + + // spans length 1 radius + domain: [0, radius / gs.w], + + // to get _boundingBox computation right when showticklabels is false + anchor: 'free', + position: 0, + + // dummy truthy value to make Axes.doTicks draw the grid + _counteraxis: true + }); + + setScale(ax, radialLayout, fullLayout); + Axes.doAutoRange(ax); + radialLayout.range = ax.range.slice(); + radialLayout._input.range = ax.range.slice(); + _this.fillViewInitialKey('radialaxis.range', ax.range.slice()); + + // rotate auto tick labels by 180 if in quadrant II and III to make them + // readable from left-to-right + // + // TODO try moving deeper in doTicks for better results? + if(ax.tickangle === 'auto' && (a0 > 90 && a0 <= 270)) { + ax.tickangle = 180; + } + + // easier to set rotate angle with custom translate function + ax._transfn = function(d) { + return 'translate(' + ax.l2p(d.x) + ',0)'; + }; + + // set special grid path function + ax._gridpath = function(d) { + var r = ax.r2p(d.x); + return pathSector(r, sector); + }; + + var newTickLayout = strTickLayout(radialLayout); + if(_this.radialTickLayout !== newTickLayout) { + layers['radial-axis'].selectAll('.xtick').remove(); + _this.radialTickLayout = newTickLayout; + } + + Axes.doTicks(gd, ax, true); + + updateElement(layers['radial-axis'], radialLayout.showticklabels || radialLayout.ticks, { + transform: strTranslate(cx, cy) + strRotate(-radialLayout.angle) + }); + + // move all grid paths to about circle center, + // undo individual grid lines translations + updateElement(layers['radial-grid'], radialLayout.showgrid, { + transform: strTranslate(cx, cy) + }) + .selectAll('path').attr('transform', null); + + updateElement(layers['radial-line'].select('line'), radialLayout.showline, { + x1: 0, + y1: 0, + x2: radius, + y2: 0, + transform: strTranslate(cx, cy) + strRotate(-radialLayout.angle) + }) + .attr('stroke-width', radialLayout.linewidth) + .call(Color.stroke, radialLayout.linecolor); +}; + +proto.updateRadialAxisTitle = function(fullLayout, polarLayout, _angle) { + var _this = this; + var gd = _this.gd; + var radius = _this.radius; + var cx = _this.cx; + var cy = _this.cy; + var radialLayout = polarLayout.radialaxis; + var titleClass = _this.id + 'title'; + + var angle = _angle !== undefined ? _angle : radialLayout.angle; + var angleRad = deg2rad(angle); + var cosa = Math.cos(angleRad); + var sina = Math.sin(angleRad); + + var pad = 0; + if(radialLayout.title) { + var h = Drawing.bBox(_this.layers['radial-axis'].node()).height; + var ts = radialLayout.titlefont.size; + pad = radialLayout.side === 'counterclockwise' ? + -h - ts * 0.4 : + h + ts * 0.8; + } + + _this.layers['radial-axis-title'] = Titles.draw(gd, titleClass, { + propContainer: radialLayout, + propName: _this.id + '.radialaxis.title', + placeholder: _(gd, 'Click to enter radial axis title'), + attributes: { + x: cx + (radius / 2) * cosa + pad * sina, + y: cy - (radius / 2) * sina + pad * cosa, + 'text-anchor': 'middle' + }, + transform: {rotate: -angle} + }); +}; + +proto.updateAngularAxis = function(fullLayout, polarLayout) { + var _this = this; + var gd = _this.gd; + var layers = _this.layers; + var radius = _this.radius; + var cx = _this.cx; + var cy = _this.cy; + var angularLayout = polarLayout.angularaxis; + var sector = polarLayout.sector; + var sectorInRad = sector.map(deg2rad); + + _this.fillViewInitialKey('angularaxis.rotation', angularLayout.rotation); + + var ax = _this.angularAxis = Lib.extendFlat({}, angularLayout, { + _axislayer: layers['angular-axis'], + _gridlayer: layers['angular-grid'], + + // angular axes need *special* logic + _id: 'angular', + _pos: 0, + side: 'right', + + // to get auto nticks right + domain: [0, Math.PI], + + // to get _boundingBox computation right when showticklabels is false + anchor: 'free', + position: 0, + + // dummy truthy value to make Axes.doTicks draw the grid + _counteraxis: true + }); + + // Set the angular range in degrees to make auto-tick computation cleaner, + // changing rotation/direction should not affect the angular tick labels. + if(ax.type === 'linear') { + ax.autorange = false; + + if(isFullCircle(sector)) { + ax.range = sector.slice(); + } else { + ax.range = sectorInRad.map(ax.unTransformRad).map(rad2deg); + } + + // run rad2deg on tick0 and ditck for thetaunit: 'radians' axes + if(ax.thetaunit === 'radians') { + ax.tick0 = rad2deg(ax.tick0); + ax.dtick = rad2deg(ax.dtick); + } + } + // Use tickval filter for category axes instead of tweaking + // the range w.r.t sector, so that sectors that cross 360 can + // show all their ticks. + else if(ax.type === 'category') { + ax._tickFilter = function(d) { + return _this.isPtWithinSector({ + r: _this.radialAxis.range[1], + rad: ax.c2rad(d.x) + }); + }; + } + + setScale(ax, angularLayout, fullLayout); + Axes.doAutoRange(ax); + + // wrapper around c2rad from setConvertAngular + // note that linear ranges are always set in degrees for Axes.doTicks + function c2rad(d) { + return ax.c2rad(d.x, 'degrees'); + } + + // (x,y) at max radius + function rad2xy(rad) { + return [radius * Math.cos(rad), radius * Math.sin(rad)]; + } + + ax._transfn = function(d) { + var rad = c2rad(d); + var xy = rad2xy(rad); + var out = strTranslate(cx + xy[0], cy - xy[1]); + + // must also rotate ticks, but don't rotate labels and grid lines + var sel = d3.select(this); + if(sel && sel.node() && sel.classed('ticks')) { + out += strRotate(-rad2deg(rad)); + } + + return out; + }; + + ax._gridpath = function(d) { + var rad = c2rad(d); + var xy = rad2xy(rad); + return 'M0,0L' + (-xy[0]) + ',' + xy[1]; + }; + + var offset4fontsize = (angularLayout.ticks !== 'outside' ? 0.7 : 0.5); + + ax._labelx = function(d) { + var rad = c2rad(d); + var labelStandoff = ax._labelStandoff; + var pad = ax._pad; + + var offset4tx = signSin(rad) === 0 ? + 0 : + Math.cos(rad) * (labelStandoff + pad + offset4fontsize * d.fontSize); + var offset4tick = signCos(rad) * (d.dx + labelStandoff + pad); + + return offset4tx + offset4tick; + }; + + ax._labely = function(d) { + var rad = c2rad(d); + var labelStandoff = ax._labelStandoff; + var labelShift = ax._labelShift; + var pad = ax._pad; + + var offset4tx = d.dy + d.fontSize * MID_SHIFT - labelShift; + var offset4tick = -Math.sin(rad) * (labelStandoff + pad + offset4fontsize * d.fontSize); + + return offset4tx + offset4tick; + }; + + ax._labelanchor = function(angle, d) { + var rad = c2rad(d); + return signSin(rad) === 0 ? + (signCos(rad) > 0 ? 'start' : 'end') : + 'middle'; + }; + + var newTickLayout = strTickLayout(angularLayout); + if(_this.angularTickLayout !== newTickLayout) { + layers['angular-axis'].selectAll('.angulartick').remove(); + _this.angularTickLayout = newTickLayout; + } + + Axes.doTicks(gd, ax, true); + + updateElement(layers['angular-line'].select('path'), angularLayout.showline, { + d: pathSectorClosed(radius, sector), + transform: strTranslate(cx, cy) + }) + .attr('stroke-width', angularLayout.linewidth) + .call(Color.stroke, angularLayout.linecolor); +}; + +proto.updateFx = function(fullLayout, polarLayout) { + if(!this.gd._context.staticPlot) { + this.updateAngularDrag(fullLayout, polarLayout); + this.updateRadialDrag(fullLayout, polarLayout); + this.updateMainDrag(fullLayout, polarLayout); + } +}; + +proto.updateMainDrag = function(fullLayout, polarLayout) { + var _this = this; + var gd = _this.gd; + var layers = _this.layers; + var zoomlayer = fullLayout._zoomlayer; + var MINZOOM = constants.MINZOOM; + var OFFEDGE = constants.OFFEDGE; + var radius = _this.radius; + var cx = _this.cx; + var cy = _this.cy; + var cxx = _this.cxx; + var cyy = _this.cyy; + var sector = polarLayout.sector; + + var mainDrag = dragBox.makeDragger(layers, 'path', 'maindrag', 'crosshair'); + + d3.select(mainDrag) + .attr('d', pathSectorClosed(radius, sector)) + .attr('transform', strTranslate(cx, cy)); + + var dragOpts = { + element: mainDrag, + gd: gd, + subplot: _this.id, + plotinfo: { + xaxis: _this.xaxis, + yaxis: _this.yaxis + }, + xaxes: [_this.xaxis], + yaxes: [_this.yaxis] + }; + + // mouse px position at drag start (0), move (1) + var x0, y0; + // radial distance from circle center at drag start (0), move (1) + var r0, r1; + // zoombox persistent quantities + var path0, dimmed, lum; + // zoombox, corners elements + var zb, corners; + + function xy2r(x, y) { + var xx = x - cxx; + var yy = y - cyy; + return Math.sqrt(xx * xx + yy * yy); + } + + function xy2a(x, y) { + return Math.atan2(cyy - y, x - cxx); + } + + function ra2xy(r, a) { + return [r * Math.cos(a), r * Math.sin(-a)]; + } + + function pathCorner(r, a) { + var clen = constants.cornerLen; + var chw = constants.cornerHalfWidth; + + if(r === 0) return pathSectorClosed(2 * chw, sector); + + var da = clen / r / 2; + var am = a - da; + var ap = a + da; + var rb = Math.max(0, Math.min(r, radius)); + var rm = rb - chw; + var rp = rb + chw; + + return 'M' + ra2xy(rm, am) + + 'A' + [rm, rm] + ' 0,0,0 ' + ra2xy(rm, ap) + + 'L' + ra2xy(rp, ap) + + 'A' + [rp, rp] + ' 0,0,1 ' + ra2xy(rp, am) + + 'Z'; + } + + function zoomPrep() { + r0 = null; + r1 = null; + path0 = pathSectorClosed(radius, sector); + dimmed = false; + + var polarLayoutNow = gd._fullLayout[_this.id]; + lum = tinycolor(polarLayoutNow.bgcolor).getLuminance(); + + zb = dragBox.makeZoombox(zoomlayer, lum, cx, cy, path0); + zb.attr('fill-rule', 'evenodd'); + corners = dragBox.makeCorners(zoomlayer, cx, cy); + dragBox.clearSelect(zoomlayer); + } + + function zoomMove(dx, dy) { + var x1 = x0 + dx; + var y1 = y0 + dy; + var rr0 = xy2r(x0, y0); + var rr1 = Math.min(xy2r(x1, y1), radius); + var a0 = xy2a(x0, y0); + var a1 = xy2a(x1, y1); + + // starting or ending drag near center (outer edge), + // clamps radial distance at origin (at r=radius) + if(rr0 < OFFEDGE) rr0 = 0; + else if((radius - rr0) < OFFEDGE) rr0 = radius; + else if(rr1 < OFFEDGE) rr1 = 0; + else if((radius - rr1) < OFFEDGE) rr1 = radius; + + var path1; + var cpath; + + if(Math.abs(rr1 - rr0) > MINZOOM) { + // make sure r0 < r1, + // to get correct fill pattern in path1 below + if(rr0 < rr1) { + r0 = rr0; + r1 = rr1; + } else { + r0 = rr1; + r1 = rr0; + a1 = [a0, a0 = a1][0]; // swap a0 and a1 + } + + path1 = path0 + pathSectorClosed(r1, sector) + pathSectorClosed(r0, sector); + cpath = pathCorner(r0, a0) + pathCorner(r1, a1); + } else { + r0 = null; + r1 = null; + path1 = path0; + cpath = 'M0,0Z'; + } + + zb.attr('d', path1); + corners.attr('d', cpath); + dragBox.transitionZoombox(zb, corners, dimmed, lum); + dimmed = true; + } + + function zoomDone() { + dragBox.removeZoombox(gd); + + if(r0 === null || r1 === null) return; + + dragBox.showDoubleClickNotifier(gd); + + var radialAxis = _this.radialAxis; + var radialRange = radialAxis.range; + var drange = radialRange[1] - radialRange[0]; + var updateObj = {}; + updateObj[_this.id + '.radialaxis.range'] = [ + radialRange[0] + r0 * drange / radius, + radialRange[0] + r1 * drange / radius + ]; + + Plotly.relayout(gd, updateObj); + } + + dragOpts.prepFn = function(evt, startX, startY) { + var dragModeNow = gd._fullLayout.dragmode; + + var bbox = mainDrag.getBoundingClientRect(); + x0 = startX - bbox.left; + y0 = startY - bbox.top; + + switch(dragModeNow) { + case 'zoom': + dragOpts.moveFn = zoomMove; + dragOpts.doneFn = zoomDone; + zoomPrep(evt, startX, startY); + break; + case 'select': + case 'lasso': + prepSelect(evt, startX, startY, dragOpts, dragModeNow); + break; + } + }; + + dragOpts.clickFn = function(numClicks, evt) { + dragBox.removeZoombox(gd); + + // TODO double once vs twice logic (autorange vs fixed range) + if(numClicks === 2) { + var updateObj = {}; + for(var k in _this.viewInitial) { + updateObj[_this.id + '.' + k] = _this.viewInitial[k]; + } + + gd.emit('plotly_doubleclick', null); + Plotly.relayout(gd, updateObj); + } + + Fx.click(gd, evt, _this.id); + }; + + mainDrag.onmousemove = function(evt) { + Fx.hover(gd, evt, _this.id); + gd._fullLayout._lasthover = mainDrag; + gd._fullLayout._hoversubplot = _this.id; + }; + + mainDrag.onmouseout = function(evt) { + if(gd._dragging) return; + dragElement.unhover(gd, evt); + }; + + dragElement.init(dragOpts); +}; + +proto.updateRadialDrag = function(fullLayout, polarLayout) { + var _this = this; + var gd = _this.gd; + var layers = _this.layers; + var radius = _this.radius; + var cx = _this.cx; + var cy = _this.cy; + var radialAxis = _this.radialAxis; + var radialLayout = polarLayout.radialaxis; + var angle0 = deg2rad(radialLayout.angle); + var range0 = radialAxis.range.slice(); + var drange = range0[1] - range0[0]; + var bl = constants.radialDragBoxSize; + var bl2 = bl / 2; + + if(!radialLayout.visible) return; + + var radialDrag = dragBox.makeRectDragger(layers, 'radialdrag', 'crosshair', -bl2, -bl2, bl, bl); + var dragOpts = {element: radialDrag, gd: gd}; + var tx = cx + (radius + bl2) * Math.cos(angle0); + var ty = cy - (radius + bl2) * Math.sin(angle0); + + d3.select(radialDrag) + .attr('transform', strTranslate(tx, ty)); + + // move function (either rotate or re-range flavor) + var moveFn2; + // rotate angle on done + var angle1; + // re-range range[1] on done + var rng1; + + function moveFn(dx, dy) { + if(moveFn2) { + moveFn2(dx, dy); + } else { + var dvec = [dx, -dy]; + var rvec = [Math.cos(angle0), Math.sin(angle0)]; + var comp = Math.abs(Lib.dot(dvec, rvec) / Math.sqrt(Lib.dot(dvec, dvec))); + + // mostly perpendicular motions rotate, + // mostly parallel motions re-range + if(!isNaN(comp)) { + moveFn2 = comp < 0.5 ? rotateMove : rerangeMove; + } + } + } + + function doneFn() { + if(angle1 !== null) { + Plotly.relayout(gd, _this.id + '.radialaxis.angle', angle1); + } else if(rng1 !== null) { + Plotly.relayout(gd, _this.id + '.radialaxis.range[1]', rng1); + } + } + + function rotateMove(dx, dy) { + var x1 = tx + dx; + var y1 = ty + dy; + + angle1 = rad2deg(Math.atan2(cy - y1, x1 - cx)); + + var transform = strTranslate(cx, cy) + strRotate(-angle1); + layers['radial-axis'].attr('transform', transform); + layers['radial-line'].select('line').attr('transform', transform); + + var fullLayoutNow = _this.gd._fullLayout; + var polarLayoutNow = fullLayoutNow[_this.id]; + _this.updateRadialAxisTitle(fullLayoutNow, polarLayoutNow, angle1); + } + + function rerangeMove(dx, dy) { + // project (dx, dy) unto unit radial axis vector + var dr = Lib.dot([dx, -dy], [Math.cos(angle0), Math.sin(angle0)]); + var rprime = range0[1] - drange * dr / radius * 0.75; + + // make sure new range[1] does not change the range[0] -> range[1] sign + if((drange > 0) !== (rprime > range0[0])) return; + rng1 = radialAxis.range[1] = rprime; + + Axes.doTicks(gd, _this.radialAxis, true); + layers['radial-grid'] + .attr('transform', strTranslate(cx, cy)) + .selectAll('path').attr('transform', null); + + var rSpan = rng1 - range0[0]; + var sectorBBox = _this.sectorBBox; + _this.xaxis.range = [sectorBBox[0] * rSpan, sectorBBox[2] * rSpan]; + _this.yaxis.range = [sectorBBox[1] * rSpan, sectorBBox[3] * rSpan]; + _this.xaxis.setScale(); + _this.yaxis.setScale(); + + for(var k in _this.traceHash) { + var moduleCalcData = _this.traceHash[k]; + var moduleCalcDataVisible = Lib.filterVisible(moduleCalcData); + var _module = moduleCalcData[0][0].trace._module; + var polarLayoutNow = gd._fullLayout[_this.id]; + + _module.plot(_this, moduleCalcDataVisible, polarLayoutNow); + + if(!Registry.traceIs(k, 'gl')) { + for(var i = 0; i < moduleCalcDataVisible.length; i++) { + _module.style(gd, moduleCalcDataVisible[i]); + } + } + } + } + + dragOpts.prepFn = function() { + moveFn2 = null; + angle1 = null; + rng1 = null; + + dragOpts.moveFn = moveFn; + dragOpts.doneFn = doneFn; + + dragBox.clearSelect(fullLayout._zoomlayer); + }; + + dragOpts.clampFn = function(dx, dy) { + if(Math.sqrt(dx * dx + dy * dy) < constants.MINDRAG) { + dx = 0; + dy = 0; + } + return [dx, dy]; + }; + + dragElement.init(dragOpts); +}; + +proto.updateAngularDrag = function(fullLayout, polarLayout) { + var _this = this; + var gd = _this.gd; + var layers = _this.layers; + var radius = _this.radius; + var cx = _this.cx; + var cy = _this.cy; + var cxx = _this.cxx; + var cyy = _this.cyy; + var sector = polarLayout.sector; + var dbs = constants.angularDragBoxSize; + + var angularDrag = dragBox.makeDragger(layers, 'path', 'angulardrag', 'move'); + var dragOpts = {element: angularDrag, gd: gd}; + + d3.select(angularDrag) + .attr('d', pathAnnulus(radius, radius + dbs, sector)) + .attr('transform', strTranslate(cx, cy)) + .call(setCursor, 'move'); + + function xy2a(x, y) { + return Math.atan2(cyy + dbs - y, x - cxx - dbs); + } + + // scatter trace, points and textpoints selections + var scatterTraces = layers.frontplot.select('.scatterlayer').selectAll('.trace'); + var scatterPoints = scatterTraces.selectAll('.point'); + var scatterTextPoints = scatterTraces.selectAll('.textpoint'); + + // mouse px position at drag start (0), move (1) + var x0, y0; + // angular axis angle rotation at drag start (0), move (1) + var rot0, rot1; + // copy of polar sector value at drag start + var sector0; + // angle about circle center at drag start + var a0; + + function moveFn(dx, dy) { + var x1 = x0 + dx; + var y1 = y0 + dy; + var a1 = xy2a(x1, y1); + var da = rad2deg(a1 - a0); + rot1 = rot0 + da; + + layers.frontplot.attr('transform', + strTranslate(_this.xOffset2, _this.yOffset2) + strRotate([-da, cxx, cyy]) + ); + + _this.clipPaths.circle.select('path').attr('transform', + strTranslate(cxx, cyy) + strRotate(da) + ); + + // 'un-rotate' marker and text points + scatterPoints.each(function() { + var sel = d3.select(this); + var xy = Drawing.getTranslate(sel); + sel.attr('transform', strTranslate(xy.x, xy.y) + strRotate([da])); + }); + scatterTextPoints.each(function() { + var sel = d3.select(this); + var tx = sel.select('text'); + var xy = Drawing.getTranslate(sel); + // N.B rotate -> translate ordering matters + sel.attr('transform', strRotate([da, tx.attr('x'), tx.attr('y')]) + strTranslate(xy.x, xy.y)); + }); + + var angularAxis = _this.angularAxis; + angularAxis.rotation = wrap180(rot1); + + if(angularAxis.type === 'linear' && !isFullCircle(sector)) { + angularAxis.range = sector0 + .map(deg2rad) + .map(angularAxis.unTransformRad) + .map(rad2deg); + } + + setConvertAngular(angularAxis); + Axes.doTicks(gd, angularAxis, true); + + if(_this._hasClipOnAxisFalse && !isFullCircle(sector)) { + // mutate sector to trick isPtWithinSector + _this.sector = [sector0[0] - da, sector0[1] - da]; + scatterTraces.call(Drawing.hideOutsideRangePoints, _this); + } + + for(var k in _this.traceHash) { + if(Registry.traceIs(k, 'gl')) { + var moduleCalcData = _this.traceHash[k]; + var moduleCalcDataVisible = Lib.filterVisible(moduleCalcData); + var _module = moduleCalcData[0][0].trace._module; + var polarLayoutNow = gd._fullLayout[_this.id]; + + _module.plot(_this, moduleCalcDataVisible, polarLayoutNow); + } + } + } + + function doneFn() { + scatterTextPoints.select('text').attr('transform', null); + var updateObj = {}; + updateObj[_this.id + '.angularaxis.rotation'] = rot1; + Plotly.relayout(gd, updateObj); + } + + dragOpts.prepFn = function(evt, startX, startY) { + var polarLayoutNow = fullLayout[_this.id]; + sector0 = polarLayoutNow.sector.slice(); + rot0 = polarLayoutNow.angularaxis.rotation; + + var bbox = angularDrag.getBoundingClientRect(); + x0 = startX - bbox.left; + y0 = startY - bbox.top; + a0 = xy2a(x0, y0); + + dragOpts.moveFn = moveFn; + dragOpts.doneFn = doneFn; + + dragBox.clearSelect(fullLayout._zoomlayer); + }; + + dragElement.init(dragOpts); +}; + +proto.isPtWithinSector = function(d) { + var sector = this.sector; + var radialAxis = this.radialAxis; + var radialRange = radialAxis.range; + var r = radialAxis.c2r(d.r); + + var s0 = wrap360(sector[0]); + var s1 = wrap360(sector[1]); + if(s0 > s1) s1 += 360; + + var deg = wrap360(rad2deg(d.rad)); + var nextTurnDeg = deg + 360; + + var r0, r1; + if(radialRange[1] >= radialRange[0]) { + r0 = radialRange[0]; + r1 = radialRange[1]; + } else { + r0 = radialRange[1]; + r1 = radialRange[0]; + } + + return ( + (r >= r0 && r <= r1) && + (isFullCircle(sector) || + (deg >= s0 && deg <= s1) || + (nextTurnDeg >= s0 && nextTurnDeg <= s1) + ) + ); +}; + +proto.fillViewInitialKey = function(key, val) { + if(!(key in this.viewInitial)) { + this.viewInitial[key] = val; + } +}; + +function setScale(ax, axLayout, fullLayout) { + Axes.setConvert(ax, fullLayout); + + // _min and _max are filled in during Axes.expand + // and cleared during Axes.setConvert + ax._min = axLayout._min; + ax._max = axLayout._max; + + ax.setScale(); +} + +function strTickLayout(axLayout) { + var out = axLayout.ticks + String(axLayout.ticklen) + String(axLayout.showticklabels); + if('side' in axLayout) out += axLayout.side; + return out; +} + +// Finds the bounding box of a given circle sector, +// inspired by https://math.stackexchange.com/q/1852703 +// +// assumes: +// - sector[1] < sector[0] +// - counterclockwise rotation +function computeSectorBBox(sector) { + var s0 = sector[0]; + var s1 = sector[1]; + var arc = s1 - s0; + var a0 = wrap360(s0); + var a1 = a0 + arc; + + var ax0 = Math.cos(deg2rad(a0)); + var ay0 = Math.sin(deg2rad(a0)); + var ax1 = Math.cos(deg2rad(a1)); + var ay1 = Math.sin(deg2rad(a1)); + + var x0, y0, x1, y1; + + if((a0 <= 90 && a1 >= 90) || (a0 > 90 && a1 >= 450)) { + y1 = 1; + } else if(ay0 <= 0 && ay1 <= 0) { + y1 = 0; + } else { + y1 = Math.max(ay0, ay1); + } + + if((a0 <= 180 && a1 >= 180) || (a0 > 180 && a1 >= 540)) { + x0 = -1; + } else if(ax0 >= 0 && ax1 >= 0) { + x0 = 0; + } else { + x0 = Math.min(ax0, ax1); + } + + if((a0 <= 270 && a1 >= 270) || (a0 > 270 && a1 >= 630)) { + y0 = -1; + } else if(ay0 >= 0 && ay1 >= 0) { + y0 = 0; + } else { + y0 = Math.min(ay0, ay1); + } + + if(a1 >= 360) { + x1 = 1; + } else if(ax0 <= 0 && ax1 <= 0) { + x1 = 0; + } else { + x1 = Math.max(ax0, ax1); + } + + return [x0, y0, x1, y1]; +} + +function pathSector(r, sector) { + if(isFullCircle(sector)) { + return Drawing.symbolFuncs[0](r); + } + + var xs = r * Math.cos(deg2rad(sector[0])); + var ys = -r * Math.sin(deg2rad(sector[0])); + var xe = r * Math.cos(deg2rad(sector[1])); + var ye = -r * Math.sin(deg2rad(sector[1])); + + var arc = Math.abs(sector[1] - sector[0]); + var flags = arc <= 180 ? [0, 0, 0] : [0, 1, 0]; + + return 'M' + [xs, ys] + + 'A' + [r, r] + ' ' + flags + ' ' + [xe, ye]; +} + +function pathSectorClosed(r, sector) { + return pathSector(r, sector) + + (isFullCircle(sector) ? '' : 'L0,0Z'); +} + +// TODO recycle this routine with the ones used for pie traces. +function pathAnnulus(r0, r1, sector) { + var largeArc = Math.abs(sector[1] - sector[0]) <= 180 ? 0 : 1; + // sector angle at [s]tart, [m]iddle and [e]nd + var ss, sm, se; + + function pt(r, s) { + return [r * Math.cos(s), -r * Math.sin(s)]; + } + + function arc(r, s, cw) { + return 'A' + [r, r] + ' ' + [0, largeArc, cw] + ' ' + pt(r, s); + } + + if(isFullCircle(sector)) { + ss = 0; + se = 2 * Math.PI; + sm = Math.PI; + return 'M' + pt(r0, ss) + + arc(r0, sm, 0) + + arc(r0, se, 0) + + 'Z' + + 'M' + pt(r1, ss) + + arc(r1, sm, 1) + + arc(r1, se, 1) + + 'Z'; + } else { + ss = deg2rad(sector[0]); + se = deg2rad(sector[1]); + return 'M' + pt(r0, ss) + + 'L' + pt(r1, ss) + + arc(r1, se, 0) + + 'L' + pt(r0, se) + + arc(r0, ss, 1) + + 'Z'; + } +} + +function isFullCircle(sector) { + var arc = Math.abs(sector[1] - sector[0]); + return arc === 360; +} + +function updateElement(sel, showAttr, attrs) { + if(showAttr) { + sel.attr('display', null); + sel.attr(attrs); + } else if(sel) { + sel.attr('display', 'none'); + } + return sel; +} + +function strTranslate(x, y) { + return 'translate(' + x + ',' + y + ')'; +} + +function strRotate(angle) { + return 'rotate(' + angle + ')'; +} + +// because Math.sign(Math.cos(Math.PI / 2)) === 1 +// oh javascript ;) +function sign(v) { + return Math.abs(v) < 1e-10 ? 0 : + v > 0 ? 1 : -1; +} + +function signCos(v) { + return sign(Math.cos(v)); +} + +function signSin(v) { + return sign(Math.sin(v)); +} diff --git a/src/plots/ternary/index.js b/src/plots/ternary/index.js index 0d9545adc14..fa5a265cb01 100644 --- a/src/plots/ternary/index.js +++ b/src/plots/ternary/index.js @@ -67,6 +67,9 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) oldTernary.plotContainer.remove(); oldTernary.clipDef.remove(); oldTernary.clipDefRelative.remove(); + oldTernary.layers['a-title'].remove(); + oldTernary.layers['b-title'].remove(); + oldTernary.layers['c-title'].remove(); } } }; diff --git a/src/plots/ternary/layout/axis_defaults.js b/src/plots/ternary/layout/axis_defaults.js index b4dcfeca186..70b471c048f 100644 --- a/src/plots/ternary/layout/axis_defaults.js +++ b/src/plots/ternary/layout/axis_defaults.js @@ -6,20 +6,16 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; -var colorMix = require('tinycolor2').mix; var Lib = require('../../../lib'); - var layoutAttributes = require('./axis_attributes'); var handleTickLabelDefaults = require('../../cartesian/tick_label_defaults'); var handleTickMarkDefaults = require('../../cartesian/tick_mark_defaults'); var handleTickValueDefaults = require('../../cartesian/tick_value_defaults'); - +var handleLineGridDefaults = require('../../cartesian/line_grid_defaults'); module.exports = function supplyLayoutDefaults(containerIn, containerOut, options) { - function coerce(attr, dflt) { return Lib.coerce(containerIn, containerOut, layoutAttributes, attr, dflt); } @@ -64,21 +60,18 @@ module.exports = function supplyLayoutDefaults(containerIn, containerOut, option coerce('tickformat'); } - coerce('hoverformat'); - - var showLine = coerce('showline'); - if(showLine) { - coerce('linecolor', dfltColor); - coerce('linewidth'); - } - - var showGridLines = coerce('showgrid'); - if(showGridLines) { + handleLineGridDefaults(containerIn, containerOut, coerce, { + dfltColor: dfltColor, + bgColor: options.bgColor, // default grid color is darker here (60%, vs cartesian default ~91%) // because the grid is not square so the eye needs heavier cues to follow - coerce('gridcolor', colorMix(dfltColor, options.bgColor, 60).toRgbString()); - coerce('gridwidth'); - } + blend: 60, + showLine: true, + showGrid: true, + noZeroLine: true, + attributes: layoutAttributes + }); + coerce('hoverformat'); coerce('layer'); }; diff --git a/src/plots/ternary/layout/layout_attributes.js b/src/plots/ternary/layout/layout_attributes.js index 69f728960d3..10f59f289f8 100644 --- a/src/plots/ternary/layout/layout_attributes.js +++ b/src/plots/ternary/layout/layout_attributes.js @@ -9,39 +9,13 @@ 'use strict'; var colorAttrs = require('../../../components/color/attributes'); +var domainAttrs = require('../../domain_attributes'); var ternaryAxesAttrs = require('./axis_attributes'); var overrideAll = require('../../../plot_api/edit_types').overrideAll; - module.exports = overrideAll({ - domain: { - x: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1}, - {valType: 'number', min: 0, max: 1} - ], - dflt: [0, 1], - description: [ - 'Sets the horizontal domain of this subplot', - '(in plot fraction).' - ].join(' ') - }, - y: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1}, - {valType: 'number', min: 0, max: 1} - ], - dflt: [0, 1], - description: [ - 'Sets the vertical domain of this subplot', - '(in plot fraction).' - ].join(' ') - } - }, + domain: domainAttrs({name: 'ternary'}), + bgcolor: { valType: 'color', role: 'style', diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index 7a886fe0a1b..36b83231edf 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -386,7 +386,7 @@ proto.drawAxes = function(doTitles) { var apad = Math.max(aaxis.showticklabels ? aaxis.tickfont.size / 2 : 0, (caxis.showticklabels ? caxis.tickfont.size * 0.75 : 0) + (caxis.ticks === 'outside' ? caxis.ticklen * 0.87 : 0)); - Titles.draw(gd, 'a' + titlesuffix, { + _this.layers['a-title'] = Titles.draw(gd, 'a' + titlesuffix, { propContainer: aaxis, propName: _this.id + '.aaxis.title', placeholder: _(gd, 'Click to enter Component A title'), @@ -397,10 +397,11 @@ proto.drawAxes = function(doTitles) { } }); + var bpad = (baxis.showticklabels ? baxis.tickfont.size : 0) + (baxis.ticks === 'outside' ? baxis.ticklen : 0) + 3; - Titles.draw(gd, 'b' + titlesuffix, { + _this.layers['b-title'] = Titles.draw(gd, 'b' + titlesuffix, { propContainer: baxis, propName: _this.id + '.baxis.title', placeholder: _(gd, 'Click to enter Component B title'), @@ -411,7 +412,7 @@ proto.drawAxes = function(doTitles) { } }); - Titles.draw(gd, 'c' + titlesuffix, { + _this.layers['c-title'] = Titles.draw(gd, 'c' + titlesuffix, { propContainer: caxis, propName: _this.id + '.caxis.title', placeholder: _(gd, 'Click to enter Component C title'), diff --git a/src/registry.js b/src/registry.js index 71819c90d6e..53dc70fe74b 100644 --- a/src/registry.js +++ b/src/registry.js @@ -208,14 +208,6 @@ function mergeComponentAttrsToSubplot(componentName, subplotName) { * module object corresponding to trace type */ exports.getModule = function(trace) { - if(trace.r !== undefined) { - Loggers.warn('Tried to put a polar trace ' + - 'on an incompatible graph of cartesian ' + - 'data. Ignoring this dataset.', trace - ); - return false; - } - var _module = exports.modules[getTraceType(trace)]; if(!_module) return false; return _module._module; diff --git a/src/snapshot/helpers.js b/src/snapshot/helpers.js index a9d9c11e844..62057db8af1 100644 --- a/src/snapshot/helpers.js +++ b/src/snapshot/helpers.js @@ -19,9 +19,12 @@ exports.getDelay = function(fullLayout) { }; exports.getRedrawFunc = function(gd) { + var fullLayout = gd._fullLayout || {}; + var hasPolar = fullLayout._has && fullLayout._has('polar'); + var hasLegacyPolar = !hasPolar && gd.data && gd.data[0] && gd.data[0].r; - // do not work if polar is present - if((gd.data && gd.data[0] && gd.data[0].r)) return; + // do not work for legacy polar + if(hasLegacyPolar) return; return function() { (gd.calcdata || []).forEach(function(d) { diff --git a/src/traces/parcoords/attributes.js b/src/traces/parcoords/attributes.js index 3e7366605de..b9968d029b1 100644 --- a/src/traces/parcoords/attributes.js +++ b/src/traces/parcoords/attributes.js @@ -13,44 +13,14 @@ var colorbarAttrs = require('../../components/colorbar/attributes'); var colorscales = require('../../components/colorscale/scales'); var axesAttrs = require('../../plots/cartesian/layout_attributes'); var fontAttrs = require('../../plots/font_attributes'); +var domainAttrs = require('../../plots/domain_attributes'); var extend = require('../../lib/extend'); var extendDeepAll = extend.extendDeepAll; var extendFlat = extend.extendFlat; module.exports = { - - domain: { - x: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1, editType: 'calc'}, - {valType: 'number', min: 0, max: 1, editType: 'calc'} - ], - dflt: [0, 1], - editType: 'calc', - description: [ - 'Sets the horizontal domain of this `parcoords` trace', - '(in plot fraction).' - ].join(' ') - }, - y: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1, editType: 'calc'}, - {valType: 'number', min: 0, max: 1, editType: 'calc'} - ], - dflt: [0, 1], - editType: 'calc', - description: [ - 'Sets the vertical domain of this `parcoords` trace', - '(in plot fraction).' - ].join(' ') - }, - editType: 'calc' - }, + domain: domainAttrs({name: 'parcoords', trace: true, editType: 'calc'}), labelfont: fontAttrs({ editType: 'calc', diff --git a/src/traces/pie/attributes.js b/src/traces/pie/attributes.js index 4c77cfc304f..cf6ae715d4f 100644 --- a/src/traces/pie/attributes.js +++ b/src/traces/pie/attributes.js @@ -11,6 +11,7 @@ var colorAttrs = require('../../components/color/attributes'); var fontAttrs = require('../../plots/font_attributes'); var plotAttrs = require('../../plots/attributes'); +var domainAttrs = require('../../plots/domain_attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -180,37 +181,8 @@ module.exports = { }), // position and shape - domain: { - x: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1, editType: 'calc'}, - {valType: 'number', min: 0, max: 1, editType: 'calc'} - ], - dflt: [0, 1], - editType: 'calc', - description: [ - 'Sets the horizontal domain of this pie trace', - '(in plot fraction).' - ].join(' ') - }, - y: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1, editType: 'calc'}, - {valType: 'number', min: 0, max: 1, editType: 'calc'} - ], - dflt: [0, 1], - editType: 'calc', - description: [ - 'Sets the vertical domain of this pie trace', - '(in plot fraction).' - ].join(' ') - }, - editType: 'calc' - }, + domain: domainAttrs({name: 'pie', trace: true, editType: 'calc'}), + hole: { valType: 'number', role: 'style', diff --git a/src/traces/sankey/attributes.js b/src/traces/sankey/attributes.js index c9f597422a2..438f7912b63 100644 --- a/src/traces/sankey/attributes.js +++ b/src/traces/sankey/attributes.js @@ -12,6 +12,7 @@ var fontAttrs = require('../../plots/font_attributes'); var plotAttrs = require('../../plots/attributes'); var colorAttrs = require('../../components/color/attributes'); var fxAttrs = require('../../components/fx/attributes'); +var domainAttrs = require('../../plots/domain_attributes'); var extendFlat = require('../../lib/extend').extendFlat; var overrideAll = require('../../plot_api/edit_types').overrideAll; @@ -21,34 +22,8 @@ module.exports = overrideAll({ flags: ['label', 'text', 'value', 'percent', 'name'], }), hoverlabel: fxAttrs.hoverlabel, // needs editType override - domain: { - x: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1}, - {valType: 'number', min: 0, max: 1} - ], - dflt: [0, 1], - description: [ - 'Sets the horizontal domain of this `sankey` trace', - '(in plot fraction).' - ].join(' ') - }, - y: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1}, - {valType: 'number', min: 0, max: 1} - ], - dflt: [0, 1], - description: [ - 'Sets the vertical domain of this `sankey` trace', - '(in plot fraction).' - ].join(' ') - } - }, + + domain: domainAttrs({name: 'sankey', trace: true}), orientation: { valType: 'enumerated', diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index 732f0d70c00..e132100b5af 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -477,7 +477,8 @@ module.exports = { valType: 'data_array', editType: 'calc', description: [ - 'For polar chart only.', + 'For legacy polar chart only.', + 'Please switch to *scatterpolar* trace type.', 'Sets the radial coordinates.' ].join('') }, @@ -485,7 +486,8 @@ module.exports = { valType: 'data_array', editType: 'calc', description: [ - 'For polar chart only.', + 'For legacy polar chart only.', + 'Please switch to *scatterpolar* trace type.', 'Sets the angular coordinates.' ].join('') }, diff --git a/src/traces/scatter/calc.js b/src/traces/scatter/calc.js index 2fc19f4bd78..49f3105f9e9 100644 --- a/src/traces/scatter/calc.js +++ b/src/traces/scatter/calc.js @@ -19,17 +19,12 @@ var calcColorscale = require('./colorscale_calc'); var arraysToCalcdata = require('./arrays_to_calcdata'); var calcSelection = require('./calc_selection'); -module.exports = function calc(gd, trace) { - var xa = Axes.getFromId(gd, trace.xaxis || 'x'), - ya = Axes.getFromId(gd, trace.yaxis || 'y'); - - var x = xa.makeCalcdata(trace, 'x'), - y = ya.makeCalcdata(trace, 'y'); - - var serieslen = Math.min(x.length, y.length), - marker, - s, - i; +function calc(gd, trace) { + var xa = Axes.getFromId(gd, trace.xaxis || 'x'); + var ya = Axes.getFromId(gd, trace.yaxis || 'y'); + var x = xa.makeCalcdata(trace, 'x'); + var y = ya.makeCalcdata(trace, 'y'); + var serieslen = Math.min(x.length, y.length); // cancel minimum tick spacings (only applies to bars and boxes) xa._minDtick = 0; @@ -40,41 +35,11 @@ module.exports = function calc(gd, trace) { // check whether bounds should be tight, padded, extended to zero... // most cases both should be padded on both ends, so start with that. - var xOptions = {padded: true}, - yOptions = {padded: true}; - - 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)) { - // I tried auto-type but category and dates dont make much sense. - var ax = {type: 'linear'}; - Axes.setConvert(ax); - s = ax.makeCalcdata(trace.marker, 'size'); - if(s.length > serieslen) s.splice(serieslen, s.length - serieslen); - } - - var sizeref = 1.6 * (trace.marker.sizeref || 1), - markerTrans; - if(trace.marker.sizemode === 'area') { - markerTrans = function(v) { - return Math.max(Math.sqrt((v || 0) / sizeref), 3); - }; - } - else { - markerTrans = function(v) { - return Math.max((v || 0) / sizeref, 3); - }; - } - xOptions.ppad = yOptions.ppad = Array.isArray(s) ? - s.map(markerTrans) : markerTrans(s); - } + var xOptions = {padded: true}; + var yOptions = {padded: true}; - calcColorscale(trace); + var ppad = calcMarkerSize(trace, serieslen); + if(ppad) xOptions.ppad = yOptions.ppad = ppad; // TODO: text size @@ -113,7 +78,7 @@ module.exports = function calc(gd, trace) { // create the "calculated data" to plot var cd = new Array(serieslen); - for(i = 0; i < serieslen; i++) { + for(var i = 0; i < serieslen; i++) { cd[i] = (isNumeric(x[i]) && isNumeric(y[i])) ? {x: x[i], y: y[i]} : {x: BADNUM, y: BADNUM}; @@ -123,8 +88,47 @@ module.exports = function calc(gd, trace) { } arraysToCalcdata(cd, trace); + calcColorscale(trace); calcSelection(cd, trace); gd.firstscatter = false; return cd; +} + +function calcMarkerSize(trace, serieslen) { + if(!subTypes.hasMarkers(trace)) return; + + // Treat size like x or y arrays --- Run d2c + // this needs to go before ppad computation + var marker = trace.marker; + var sizeref = 1.6 * (trace.marker.sizeref || 1); + var markerTrans; + + if(trace.marker.sizemode === 'area') { + markerTrans = function(v) { + return Math.max(Math.sqrt((v || 0) / sizeref), 3); + }; + } else { + markerTrans = function(v) { + return Math.max((v || 0) / sizeref, 3); + }; + } + + if(Array.isArray(marker.size)) { + // I tried auto-type but category and dates dont make much sense. + var ax = {type: 'linear'}; + Axes.setConvert(ax); + + var s = ax.makeCalcdata(trace.marker, 'size'); + if(s.length > serieslen) s.splice(serieslen, s.length - serieslen); + + return s.map(markerTrans); + } else { + return markerTrans(marker.size); + } +} + +module.exports = { + calc: calc, + calcMarkerSize: calcMarkerSize }; diff --git a/src/traces/scatter/defaults.js b/src/traces/scatter/defaults.js index 0be2ef56bbb..6f3cc0cbf69 100644 --- a/src/traces/scatter/defaults.js +++ b/src/traces/scatter/defaults.js @@ -57,6 +57,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var dfltHoverOn = []; if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) { + coerce('cliponaxis'); coerce('marker.maxdisplayed'); dfltHoverOn.push('points'); } @@ -75,7 +76,5 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'y'}); errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'x', inherit: 'y'}); - coerce('cliponaxis'); - Lib.coerceSelectionMarkerOpacity(traceOut, coerce); }; diff --git a/src/traces/scatter/hover.js b/src/traces/scatter/hover.js index 66d5973ad18..28a9a4cda0e 100644 --- a/src/traces/scatter/hover.js +++ b/src/traces/scatter/hover.js @@ -134,9 +134,11 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { if((y0 > yAvg) !== (y1 >= yAvg)) { x0 = pts[j - 1][0]; x1 = pts[j][0]; - xCross = x0 + (x1 - x0) * (yAvg - y0) / (y1 - y0); - xmin = Math.min(xmin, xCross); - xmax = Math.max(xmax, xCross); + if(y1 - y0) { + xCross = x0 + (x1 - x0) * (yAvg - y0) / (y1 - y0); + xmin = Math.min(xmin, xCross); + xmax = Math.max(xmax, xCross); + } } } } diff --git a/src/traces/scatter/index.js b/src/traces/scatter/index.js index 8f25e0fb3d1..8bca686de6a 100644 --- a/src/traces/scatter/index.js +++ b/src/traces/scatter/index.js @@ -22,7 +22,7 @@ Scatter.isBubble = subtypes.isBubble; Scatter.attributes = require('./attributes'); Scatter.supplyDefaults = require('./defaults'); Scatter.cleanData = require('./clean_data'); -Scatter.calc = require('./calc'); +Scatter.calc = require('./calc').calc; Scatter.arraysToCalcdata = require('./arrays_to_calcdata'); Scatter.plot = require('./plot'); Scatter.colorbar = require('./colorbar'); diff --git a/src/traces/scatter/line_points.js b/src/traces/scatter/line_points.js index fd9b9872468..6814f27125a 100644 --- a/src/traces/scatter/line_points.js +++ b/src/traces/scatter/line_points.js @@ -60,9 +60,10 @@ module.exports = function linePoints(d, opts) { // turn one calcdata point into pixel coordinates function getPt(index) { - var x = xa.c2p(d[index].x); - var y = ya.c2p(d[index].y); - if(x === BADNUM || y === BADNUM) return false; + var di = d[index]; + var x = xa.c2p(di.x); + var y = ya.c2p(di.y); + if(x === BADNUM || y === BADNUM) return di.intoCenter || false; return [x, y]; } diff --git a/src/traces/scattercarpet/calc.js b/src/traces/scattercarpet/calc.js index a6be48100f1..64d10706e06 100644 --- a/src/traces/scattercarpet/calc.js +++ b/src/traces/scattercarpet/calc.js @@ -11,12 +11,10 @@ var isNumeric = require('fast-isnumeric'); -var Axes = require('../../plots/cartesian/axes'); - -var subTypes = require('../scatter/subtypes'); var calcColorscale = require('../scatter/colorscale_calc'); var arraysToCalcdata = require('../scatter/arrays_to_calcdata'); var calcSelection = require('../scatter/calc_selection'); +var calcMarkerSize = require('../scatter/calc').calcMarkerSize; var lookupCarpet = require('../carpet/lookup_carpetid'); module.exports = function calc(gd, trace) { @@ -51,22 +49,7 @@ module.exports = function calc(gd, trace) { 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); - } - } - + calcMarkerSize(trace, serieslen); calcColorscale(trace); arraysToCalcdata(cd, trace); calcSelection(cd, trace); diff --git a/src/traces/scattercarpet/index.js b/src/traces/scattercarpet/index.js index c782aab5738..689f4cedf5f 100644 --- a/src/traces/scattercarpet/index.js +++ b/src/traces/scattercarpet/index.js @@ -15,7 +15,7 @@ ScatterCarpet.supplyDefaults = require('./defaults'); ScatterCarpet.colorbar = require('../scatter/colorbar'); ScatterCarpet.calc = require('./calc'); ScatterCarpet.plot = require('./plot'); -ScatterCarpet.style = require('./style'); +ScatterCarpet.style = require('../scatter/style').style; ScatterCarpet.hoverPoints = require('./hover'); ScatterCarpet.selectPoints = require('../scatter/select'); ScatterCarpet.eventData = require('./event_data'); diff --git a/src/traces/scattercarpet/style.js b/src/traces/scattercarpet/style.js deleted file mode 100644 index a5faaa32565..00000000000 --- a/src/traces/scattercarpet/style.js +++ /dev/null @@ -1,27 +0,0 @@ -/** -* Copyright 2012-2018, 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').style; - -module.exports = function style(gd, cd) { - // 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. - if(!cd) { - var modules = gd._fullLayout._modules; - for(var i = 0; i < modules.length; i++) { - if(modules[i].name === 'scatter') return; - } - } - - scatterStyle(gd, cd); -}; diff --git a/src/traces/scatterpolar/attributes.js b/src/traces/scatterpolar/attributes.js new file mode 100644 index 00000000000..576780bd047 --- /dev/null +++ b/src/traces/scatterpolar/attributes.js @@ -0,0 +1,92 @@ +/** +* Copyright 2012-2018, 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 scatterAttrs = require('../scatter/attributes'); +var plotAttrs = require('../../plots/attributes'); +var lineAttrs = scatterAttrs.line; + +module.exports = { + mode: scatterAttrs.mode, + + r: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + description: 'Sets the radial coordinates' + }, + + theta: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + description: 'Sets the angular coordinates' + }, + + thetaunit: { + valType: 'enumerated', + values: ['radians', 'degrees', 'gradians'], + dflt: 'degrees', + role: 'info', + editType: 'calc+clearAxisTypes', + description: [ + 'Sets the unit of input *theta* values.', + 'Has an effect only when on *linear* angular axes.' + ].join(' ') + }, + + text: scatterAttrs.text, + hovertext: scatterAttrs.hovertext, + + line: { + color: lineAttrs.color, + width: lineAttrs.width, + dash: lineAttrs.dash, + shape: extendFlat({}, lineAttrs.shape, { + values: ['linear', 'spline'] + }), + smoothing: lineAttrs.smoothing, + editType: 'calc' + }, + connectgaps: scatterAttrs.connectgaps, + + marker: scatterAttrs.marker, + cliponaxis: extendFlat({}, scatterAttrs.cliponaxis, {dflt: false}), + + textposition: scatterAttrs.textposition, + textfont: scatterAttrs.textfont, + + fill: extendFlat({}, scatterAttrs.fill, { + values: ['none', 'toself', 'tonext'], + description: [ + 'Sets the area to fill with a solid color.', + 'Use with `fillcolor` if not *none*.', + 'scatterpolar 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, + + // TODO error bars + // https://stackoverflow.com/a/26597487/4068492 + // error_x (error_r, error_theta) + // error_y + + hoverinfo: extendFlat({}, plotAttrs.hoverinfo, { + flags: ['r', 'theta', 'text', 'name'] + }), + hoveron: scatterAttrs.hoveron, + + selected: scatterAttrs.selected, + unselected: scatterAttrs.unselected +}; diff --git a/src/traces/scatterpolar/calc.js b/src/traces/scatterpolar/calc.js new file mode 100644 index 00000000000..18a7f3b12bc --- /dev/null +++ b/src/traces/scatterpolar/calc.js @@ -0,0 +1,63 @@ +/** +* Copyright 2012-2018, 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 BADNUM = require('../../constants/numerical').BADNUM; + +var Axes = require('../../plots/cartesian/axes'); + +var calcColorscale = require('../scatter/colorscale_calc'); +var arraysToCalcdata = require('../scatter/arrays_to_calcdata'); +var calcSelection = require('../scatter/calc_selection'); +var calcMarkerSize = require('../scatter/calc').calcMarkerSize; + +module.exports = function calc(gd, trace) { + var fullLayout = gd._fullLayout; + var subplotId = trace.subplot; + var radialAxis = fullLayout[subplotId].radialaxis; + var angularAxis = fullLayout[subplotId].angularaxis; + var rArray = radialAxis.makeCalcdata(trace, 'r'); + var thetaArray = angularAxis.makeCalcdata(trace, 'theta'); + var len = rArray.length; + var cd = new Array(len); + + function c2rad(v) { + return angularAxis.c2rad(v, trace.thetaunit); + } + + for(var i = 0; i < len; i++) { + var r = rArray[i]; + var theta = thetaArray[i]; + var cdi = cd[i] = {}; + + if(isNumeric(r) && isNumeric(theta)) { + cdi.r = r; + cdi.theta = theta; + cdi.rad = c2rad(theta); + } else { + cdi.r = BADNUM; + } + } + + var ppad = calcMarkerSize(trace, len); + Axes.expand(radialAxis, rArray, {ppad: ppad}); + + if(angularAxis.type !== 'linear') { + angularAxis.autorange = true; + Axes.expand(angularAxis, thetaArray); + } + + calcColorscale(trace); + arraysToCalcdata(cd, trace); + calcSelection(cd, trace); + + return cd; +}; diff --git a/src/traces/scatterpolar/defaults.js b/src/traces/scatterpolar/defaults.js new file mode 100644 index 00000000000..294553ecb6e --- /dev/null +++ b/src/traces/scatterpolar/defaults.js @@ -0,0 +1,80 @@ +/** +* Copyright 2012-2018, 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 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 PTS_LINESONLY = require('../scatter/constants').PTS_LINESONLY; + +var attributes = require('./attributes'); + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var r = coerce('r'); + var theta = coerce('theta'); + var len = (r && theta) ? Math.min(r.length, theta.length) : 0; + + if(!len) { + traceOut.visible = false; + return; + } + + if(len < r.length) traceOut.r = r.slice(0, len); + if(len < theta.length) traceOut.theta = theta.slice(0, len); + + coerce('thetaunit'); + coerce('mode', len < PTS_LINESONLY ? 'lines+markers' : 'lines'); + coerce('text'); + coerce('hovertext'); + + 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, {gradient: true}); + } + + if(subTypes.hasText(traceOut)) { + handleTextDefaults(traceIn, traceOut, layout, coerce); + } + + var dfltHoverOn = []; + + if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) { + coerce('cliponaxis'); + 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); + } + + if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') { + dfltHoverOn.push('fills'); + } + coerce('hoveron', dfltHoverOn.join('+') || 'points'); + + Lib.coerceSelectionMarkerOpacity(traceOut, coerce); +}; diff --git a/src/traces/scatterpolar/hover.js b/src/traces/scatterpolar/hover.js new file mode 100644 index 00000000000..b81abf2fa98 --- /dev/null +++ b/src/traces/scatterpolar/hover.js @@ -0,0 +1,64 @@ +/** +* Copyright 2012-2018, 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'); +var Axes = require('../../plots/cartesian/axes'); +var Lib = require('../../lib'); + +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]; + + // hovering on fill case + if(newPointData.index === undefined) { + return scatterPointData; + } + + var subplot = pointData.subplot; + var cdi = newPointData.cd[newPointData.index]; + + if(!subplot.isPtWithinSector(cdi)) return; + + newPointData.xLabelVal = undefined; + newPointData.yLabelVal = undefined; + + var trace = newPointData.trace; + var radialAxis = subplot.radialAxis; + var angularAxis = subplot.angularAxis; + var hoverinfo = cdi.hi || trace.hoverinfo; + var parts = hoverinfo.split('+'); + var text = []; + var rad = angularAxis._c2rad(cdi.theta, trace.thetaunit); + + radialAxis._hovertitle = 'r'; + angularAxis._hovertitle = 'θ'; + + // show theta value in unit of angular axis + var theta; + if(angularAxis.type === 'linear' && trace.thetaunit !== angularAxis.thetaunit) { + theta = angularAxis.thetaunit === 'degrees' ? Lib.rad2deg(rad) : rad; + } else { + theta = cdi.theta; + } + + function textPart(ax, val) { + text.push(ax._hovertitle + ': ' + Axes.tickText(ax, val, 'hover').text); + } + + if(parts.indexOf('all') !== -1) parts = ['r', 'theta']; + if(parts.indexOf('r') !== -1) textPart(radialAxis, radialAxis.c2r(cdi.r)); + if(parts.indexOf('theta') !== -1) textPart(angularAxis, theta); + + newPointData.extraText = text.join('
'); + + return scatterPointData; +}; diff --git a/src/traces/scatterpolar/index.js b/src/traces/scatterpolar/index.js new file mode 100644 index 00000000000..cd7b0734a51 --- /dev/null +++ b/src/traces/scatterpolar/index.js @@ -0,0 +1,37 @@ +/** +* Copyright 2012-2018, 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 = { + moduleType: 'trace', + name: 'scatterpolar', + basePlotModule: require('../../plots/polar'), + categories: ['polar', 'symbols', 'markerColorscale', 'showLegend', 'scatter-like'], + + attributes: require('./attributes'), + supplyDefaults: require('./defaults'), + calc: require('./calc'), + plot: require('./plot'), + style: require('../scatter/style').style, + hoverPoints: require('./hover'), + selectPoints: require('../scatter/select'), + + meta: { + hrName: 'scatter_polar', + description: [ + 'The scatterpolar trace type encompasses line charts, scatter charts, text charts, and bubble charts.', + 'in polar coordinates.', + 'The data visualized as scatter point or lines is set in', + '`r` (radial) and `theta` (angular). coordintes', + 'Text (appearing either on the chart or on hover only) is via `text`.', + 'Bubble charts are achieved by setting `marker.size` and/or `marker.color`', + 'to numerical arrays.' + ].join(' ') + } +}; diff --git a/src/traces/scatterpolar/plot.js b/src/traces/scatterpolar/plot.js new file mode 100644 index 00000000000..5513416d909 --- /dev/null +++ b/src/traces/scatterpolar/plot.js @@ -0,0 +1,64 @@ +/** +* Copyright 2012-2018, 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 BADNUM = require('../../constants/numerical').BADNUM; + +module.exports = function plot(subplot, moduleCalcData) { + var i, j; + + var plotinfo = { + xaxis: subplot.xaxis, + yaxis: subplot.yaxis, + plot: subplot.framework, + layerClipId: subplot._hasClipOnAxisFalse ? subplot.clipIds.circle : null + }; + + var radialAxis = subplot.radialAxis; + var radialRange = radialAxis.range; + var rFilter; + + if(radialRange[0] > radialRange[1]) { + rFilter = function(v) { return v <= 0; }; + } else { + rFilter = function(v) { return v >= 0; }; + } + + // map (r, theta) first to a 'geometric' r and then to (x,y) + // on-par with what scatterPlot expects. + + for(i = 0; i < moduleCalcData.length; i++) { + for(j = 0; j < moduleCalcData[i].length; j++) { + var cdi = moduleCalcData[i][j]; + var r = cdi.r; + + if(r !== BADNUM) { + // convert to 'r' data to fit with mocked polar x/y axis + // which are always `type: 'linear'` + var rr = radialAxis.c2r(r) - radialRange[0]; + if(rFilter(rr)) { + var rad = cdi.rad; + cdi.x = rr * Math.cos(rad); + cdi.y = rr * Math.sin(rad); + continue; + } else { + // flag for scatter/line_points.js + // to extend line (and fills) into center + cdi.intoCenter = [subplot.cxx, subplot.cyy]; + } + } + + cdi.x = BADNUM; + cdi.y = BADNUM; + } + } + + scatterPlot(subplot.graphDiv, plotinfo, moduleCalcData); +}; diff --git a/src/traces/scatterternary/calc.js b/src/traces/scatterternary/calc.js index 1d72e9ef63e..f7bcc43cbef 100644 --- a/src/traces/scatterternary/calc.js +++ b/src/traces/scatterternary/calc.js @@ -11,12 +11,10 @@ var isNumeric = require('fast-isnumeric'); -var Axes = require('../../plots/cartesian/axes'); - -var subTypes = require('../scatter/subtypes'); var calcColorscale = require('../scatter/colorscale_calc'); var arraysToCalcdata = require('../scatter/arrays_to_calcdata'); var calcSelection = require('../scatter/calc_selection'); +var calcMarkerSize = require('../scatter/calc').calcMarkerSize; var dataArrays = ['a', 'b', 'c']; var arraysToFill = {a: ['b', 'c'], b: ['a', 'c'], c: ['a', 'b']}; @@ -72,22 +70,7 @@ module.exports = function calc(gd, trace) { else cd[i] = {x: false, y: false}; } - // 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); - } - } - + calcMarkerSize(trace, serieslen); calcColorscale(trace); arraysToCalcdata(cd, trace); calcSelection(cd, trace); diff --git a/src/traces/scatterternary/defaults.js b/src/traces/scatterternary/defaults.js index ee841741770..cb982c46a03 100644 --- a/src/traces/scatterternary/defaults.js +++ b/src/traces/scatterternary/defaults.js @@ -85,6 +85,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var dfltHoverOn = []; if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) { + coerce('cliponaxis'); coerce('marker.maxdisplayed'); dfltHoverOn.push('points'); } @@ -100,7 +101,5 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } coerce('hoveron', dfltHoverOn.join('+') || 'points'); - coerce('cliponaxis'); - Lib.coerceSelectionMarkerOpacity(traceOut, coerce); }; diff --git a/src/traces/scatterternary/hover.js b/src/traces/scatterternary/hover.js index ece85deb41f..808bc451ff8 100644 --- a/src/traces/scatterternary/hover.js +++ b/src/traces/scatterternary/hover.js @@ -50,7 +50,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { // TODO: nice formatting, and label by axis title, for a, b, and c? var trace = newPointData.trace; - var ternary = trace._ternary; + var ternary = newPointData.subplot; var hoverinfo = cdi.hi || trace.hoverinfo; var parts = hoverinfo.split('+'); var text = []; diff --git a/src/traces/scatterternary/index.js b/src/traces/scatterternary/index.js index 56f1b7fdd3b..e6670b95a45 100644 --- a/src/traces/scatterternary/index.js +++ b/src/traces/scatterternary/index.js @@ -15,7 +15,7 @@ ScatterTernary.supplyDefaults = require('./defaults'); ScatterTernary.colorbar = require('../scatter/colorbar'); ScatterTernary.calc = require('./calc'); ScatterTernary.plot = require('./plot'); -ScatterTernary.style = require('./style'); +ScatterTernary.style = require('../scatter/style').style; ScatterTernary.hoverPoints = require('./hover'); ScatterTernary.selectPoints = require('../scatter/select'); ScatterTernary.eventData = require('./event_data'); diff --git a/src/traces/scatterternary/plot.js b/src/traces/scatterternary/plot.js index bcbc189bba8..02dee000684 100644 --- a/src/traces/scatterternary/plot.js +++ b/src/traces/scatterternary/plot.js @@ -26,10 +26,5 @@ module.exports = function plot(ternary, moduleCalcData) { layerClipId: ternary._hasClipOnAxisFalse ? ternary.clipIdRelative : null }; - // add ref to ternary subplot object in fullData traces - for(var i = 0; i < moduleCalcData.length; i++) { - moduleCalcData[i][0].trace._ternary = ternary; - } - scatterPlot(ternary.graphDiv, plotinfo, moduleCalcData); }; diff --git a/src/traces/scatterternary/style.js b/src/traces/scatterternary/style.js deleted file mode 100644 index a5faaa32565..00000000000 --- a/src/traces/scatterternary/style.js +++ /dev/null @@ -1,27 +0,0 @@ -/** -* Copyright 2012-2018, 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').style; - -module.exports = function style(gd, cd) { - // 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. - if(!cd) { - var modules = gd._fullLayout._modules; - for(var i = 0; i < modules.length; i++) { - if(modules[i].name === 'scatter') return; - } - } - - scatterStyle(gd, cd); -}; diff --git a/src/traces/table/attributes.js b/src/traces/table/attributes.js index 5e086e06b69..7dbfededbe0 100644 --- a/src/traces/table/attributes.js +++ b/src/traces/table/attributes.js @@ -12,36 +12,10 @@ var annAttrs = require('../../components/annotations/attributes'); var extendFlat = require('../../lib/extend').extendFlat; var overrideAll = require('../../plot_api/edit_types').overrideAll; var fontAttrs = require('../../plots/font_attributes'); +var domainAttrs = require('../../plots/domain_attributes'); module.exports = overrideAll({ - domain: { - x: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1}, - {valType: 'number', min: 0, max: 1} - ], - dflt: [0, 1], - description: [ - 'Sets the horizontal domain of this `table` trace', - '(in plot fraction).' - ].join(' ') - }, - y: { - valType: 'info_array', - role: 'info', - items: [ - {valType: 'number', min: 0, max: 1}, - {valType: 'number', min: 0, max: 1} - ], - dflt: [0, 1], - description: [ - 'Sets the vertical domain of this `table` trace', - '(in plot fraction).' - ].join(' ') - } - }, + domain: domainAttrs({name: 'table', trace: true}), columnwidth: { valType: 'number', diff --git a/test/image/baselines/polar_blank.png b/test/image/baselines/polar_blank.png new file mode 100644 index 00000000000..d5c9b75dd63 Binary files /dev/null and b/test/image/baselines/polar_blank.png differ diff --git a/test/image/baselines/polar_categories.png b/test/image/baselines/polar_categories.png new file mode 100644 index 00000000000..36e92602dc3 Binary files /dev/null and b/test/image/baselines/polar_categories.png differ diff --git a/test/image/baselines/polar_dates.png b/test/image/baselines/polar_dates.png new file mode 100644 index 00000000000..5ae17bc9f23 Binary files /dev/null and b/test/image/baselines/polar_dates.png differ diff --git a/test/image/baselines/polar_direction.png b/test/image/baselines/polar_direction.png new file mode 100644 index 00000000000..967aead4abc Binary files /dev/null and b/test/image/baselines/polar_direction.png differ diff --git a/test/image/baselines/polar_fills.png b/test/image/baselines/polar_fills.png new file mode 100644 index 00000000000..efd3df8448b Binary files /dev/null and b/test/image/baselines/polar_fills.png differ diff --git a/test/image/baselines/polar_line.png b/test/image/baselines/polar_line.png index a55331f8e68..8b5849a3303 100644 Binary files a/test/image/baselines/polar_line.png and b/test/image/baselines/polar_line.png differ diff --git a/test/image/baselines/polar_radial-range.png b/test/image/baselines/polar_radial-range.png new file mode 100644 index 00000000000..56e6f4a853c Binary files /dev/null and b/test/image/baselines/polar_radial-range.png differ diff --git a/test/image/baselines/polar_scatter.png b/test/image/baselines/polar_scatter.png index ceb3a086fe2..2a94c6851c4 100644 Binary files a/test/image/baselines/polar_scatter.png and b/test/image/baselines/polar_scatter.png differ diff --git a/test/image/baselines/polar_sector.png b/test/image/baselines/polar_sector.png new file mode 100644 index 00000000000..78e5585892a Binary files /dev/null and b/test/image/baselines/polar_sector.png differ diff --git a/test/image/baselines/polar_subplots.png b/test/image/baselines/polar_subplots.png new file mode 100644 index 00000000000..ad3d762b395 Binary files /dev/null and b/test/image/baselines/polar_subplots.png differ diff --git a/test/image/baselines/polar_ticks.png b/test/image/baselines/polar_ticks.png new file mode 100644 index 00000000000..2cb629b084a Binary files /dev/null and b/test/image/baselines/polar_ticks.png differ diff --git a/test/image/mocks/polar_blank.json b/test/image/mocks/polar_blank.json new file mode 100644 index 00000000000..a09c6e5a4b4 --- /dev/null +++ b/test/image/mocks/polar_blank.json @@ -0,0 +1,75 @@ +{ + "data": [{ + "type": "scatterpolar", + "r": [], + "theta": [] + }, { + "type": "scatterpolar", + "r": [], + "theta": [], + "subplot": "polar2" + }, { + "type": "scatterpolar", + "r": [], + "theta": [], + "subplot": "polar3" + }, { + "type": "scatterpolar", + "mode": "markers+text", + "r": [0], + "theta": [0], + "text": "N.B.
radial
auotrange
for (0,0)
gives [0,1]", + "textposition": "left", + "subplot": "polar4" + }], + "layout": { + "polar": { + "domain": { + "x": [0, 0.46], + "y": [0.56, 1] + }, + "radialaxis": { + "title": "blank", + "angle": 180 + } + }, + "polar2": { + "domain": { + "x": [0, 0.46], + "y": [0, 0.44] + }, + "angularaxis": { + "showline": true, + "showgrid": false, + "showticklabels": false, + "ticks": "" + }, + "radialaxis": { + "showline": false, + "showgrid": false, + "showticklabels": false, + "ticks": "" + } + }, + "polar3": { + "domain": { + "x": [0.54, 1], + "y": [0.56, 1] + }, + "radialaxis": { + "title": "blank", + "side": "counterclockwise" + }, + "angularaxis": {"visible": false} + }, + "polar4": { + "domain": { + "x": [0.54, 1], + "y": [0, 0.44] + }, + "radialaxis": {"visible": false} + }, + "width": 600, + "height": 500 + } +} diff --git a/test/image/mocks/polar_categories.json b/test/image/mocks/polar_categories.json new file mode 100644 index 00000000000..e1dc18d3a21 --- /dev/null +++ b/test/image/mocks/polar_categories.json @@ -0,0 +1,103 @@ +{ + "data": [ + { + "type": "scatterpolar", + "name": "angular categories", + "r": [5, 4, 2, 4, 5], + "theta": ["a", "b", "c", "d", "a"], + "fill": "toself" + }, + { + "type": "scatterpolar", + "name": "radial categories", + "r": ["a", "b", "c", "d", "b", "f", "a"], + "theta": [1, 4, 2, 1.5, 1.5, 6, 5], + "thetaunit": "radians", + "fill": "toself", + "subplot": "polar2" + }, + { + "type": "scatterpolar", + "name": "angular categories (w/ categoryarray)", + "r": [5, 4, 2, 4, 5], + "theta": ["a", "b", "c", "d", "a"], + "fill": "toself", + "subplot": "polar3" + }, + { + "type": "scatterpolar", + "name": "radial categories (w/ category descending)", + "r": ["a", "b", "c", "d", "b", "f", "a", "a"], + "theta": [45, 90, 180, 200, 300, 15, 20, 45], + "fill": "toself", + "subplot": "polar4" + }, + { + "type": "scatterpolar", + "name": "angular categories (w/ extra category)", + "r": [5, 4, 2, 4, 5, 5], + "theta": ["b", "c", "d", "e", "a", "b"], + "fill": "toself" + } + ], + "layout": { + "polar": { + "domain": { + "x": [0, 0.46], + "y": [0.56, 1] + }, + "radialaxis": { + "angle": 45 + }, + "angularaxis": { + "direction": "clockwise", + "period": 6 + } + }, + "polar2": { + "domain": { + "x": [0, 0.46], + "y": [0, 0.44] + }, + "radialaxis": { + "angle": 180, + "tickangle": -180 + } + }, + "polar3": { + "domain": { + "x": [0.54, 1], + "y": [0.56, 1] + }, + "sector": [150, 400], + "radialaxis": { + "angle": -45 + }, + "angularaxis": { + "categoryarray": ["d", "a", "c", "b"] + } + }, + "polar4": { + "domain": { + "x": [0.54, 1], + "y": [0, 0.44] + }, + "radialaxis": { + "categoryorder": "category descending" + }, + "angularaxis": { + "thetaunit": "radians", + "dtick": 0.3141592653589793 + } + }, + "legend": { + "x": 0.5, + "y": -0.05, + "xanchor": "center", + "yanchor": "top" + }, + "width": 550, + "height": 550, + "margin": {"l": 40, "r": 40, "b": 60, "t": 20, "pad": 0} + } +} diff --git a/test/image/mocks/polar_dates.json b/test/image/mocks/polar_dates.json new file mode 100644 index 00000000000..c7336e08dc4 --- /dev/null +++ b/test/image/mocks/polar_dates.json @@ -0,0 +1,92 @@ +{ + "data": [ + { + "type": "scatterpolar", + "r": [ + 5, 4, 2, 4, 3, + 5, 4, 2, 4, + 5, 4, 2, 4, 3 + ], + "theta": [ + "2017-01-01", "2017-04-01", "2017-08-01", "2017-12-01", + "2018-01-01", "2018-03-01", "2018-10-20", + "2019-01-01", "2019-07-09", "2019-08-20", "2019-12-10" + ], + "line": { + "shape": "spline" + } + }, + { + "type": "scatterpolar", + "r": [ + 5, 4, 2, 4, 3, + 5, 4, 2, 4, + 5, 4, 2, 4, 3 + ], + "theta": [ + "2017-01-01", "2017-04-01", "2017-08-01", "2017-12-01", + "2018-01-01", "2018-03-01", "2018-10-20", + "2019-01-01", "2019-07-09", "2019-08-20", "2019-12-10" + ], + "line": { + "shape": "spline" + }, + "subplot": "polar2" + }, + { + "x": [ + 5, 4, 2, 4, 3, + 5, 4, 2, 4, + 5, 4, 2, 4, 3 + ], + "y": [ + "2017-01-01", "2017-04-01", "2017-08-01", "2017-12-01", + "2018-01-01", "2018-03-01", "2018-10-20", + "2019-01-01", "2019-07-09", "2019-08-20", "2019-12-10" + ], + "line": { + "shape": "spline" + } + } + ], + "layout": { + "polar": { + "domain": { + "x": [0, 0.46], + "y": [0.54, 1] + }, + "angularaxis": { + "rotation": 90, + "direction": "clockwise" + } + }, + "polar2": { + "domain": { + "x": [0.54, 1], + "y": [0.54, 1] + }, + "angularaxis": { + "period": 2629800000, + "rotation": 90, + "direction": "clockwise" + } + }, + "yaxis": { + "domain": [0, 0.46] + }, + "hovermode": "closest", + "showlegend": false, + "width": 650, + "height": 550, + "margin": {"l": 80, "r": 40, "b": 60, "t": 60, "pad": 0}, + "annotations": [{ + "showarrow": false, + "text": "date angular axes are not supported yet.", + "xref": "paper", + "yref": "paper", + "x": 0.5, + "y": 1.05, + "yanchor": "bottom" + }] + } +} diff --git a/test/image/mocks/polar_direction.json b/test/image/mocks/polar_direction.json new file mode 100644 index 00000000000..bf044ab7011 --- /dev/null +++ b/test/image/mocks/polar_direction.json @@ -0,0 +1,510 @@ +{ + "data": [ + { + "type": "scatterpolar", + "mode": "lines+markers", + "r": [ + 1, + 2, + 3, + 4, + 5 + ], + "theta": [ + 0, + 90, + 180, + 360 + ], + "line": { + "color": "#ff66ab" + }, + "marker": { + "color": "#8090c7", + "symbol": "square", + "size": 8 + }, + "subplot": "polar", + "text": "sector: 0->360
rotation: -90
direction: clockwise" + }, + { + "type": "scatterpolar", + "mode": "lines+markers", + "r": [ + 1, + 2, + 3, + 4, + 5 + ], + "theta": [ + 0, + 90, + 180, + 360 + ], + "line": { + "color": "#ff66ab" + }, + "marker": { + "color": "#8090c7", + "symbol": "square", + "size": 8 + }, + "subplot": "polar2", + "text": "sector: 0->360
rotation: 90
direction: clockwise" + }, + { + "type": "scatterpolar", + "mode": "lines+markers", + "r": [ + 1, + 2, + 3, + 4, + 5 + ], + "theta": [ + 0, + 90, + 180, + 360 + ], + "line": { + "color": "#ff66ab" + }, + "marker": { + "color": "#8090c7", + "symbol": "square", + "size": 8 + }, + "subplot": "polar3", + "text": "sector: 45->135
rotation: 90
direction: clockwise" + }, + { + "type": "scatterpolar", + "mode": "lines+markers", + "r": [ + 1, + 2, + 3, + 4, + 5 + ], + "theta": [ + 0, + 90, + 180, + 360 + ], + "line": { + "color": "#ff66ab" + }, + "marker": { + "color": "#8090c7", + "symbol": "square", + "size": 8 + }, + "subplot": "polar4", + "text": "sector: 135->225
rotation: 90
direction: clockwise" + }, + { + "type": "scatterpolar", + "mode": "lines+markers", + "r": [ + 1, + 2, + 3, + 4, + 5 + ], + "theta": [ + 0, + 90, + 180, + 360 + ], + "line": { + "color": "#ff66ab" + }, + "marker": { + "color": "#8090c7", + "symbol": "square", + "size": 8 + }, + "subplot": "polar5", + "text": "sector: 135->225
rotation: 90
direction: counterclockwise" + }, + { + "type": "scatterpolar", + "mode": "lines+markers", + "r": [ + 1, + 2, + 3, + 4, + 5 + ], + "theta": [ + 0, + 90, + 180, + 360 + ], + "line": { + "color": "#ff66ab" + }, + "marker": { + "color": "#8090c7", + "symbol": "square", + "size": 8 + }, + "subplot": "polar6", + "text": "sector: 45->135
rotation: 90
direction: counterclockwise" + }, + { + "type": "scatterpolar", + "mode": "lines+markers", + "r": [ + 1, + 2, + 3, + 4, + 5 + ], + "theta": [ + 0, + 90, + 180, + 360 + ], + "line": { + "color": "#ff66ab" + }, + "marker": { + "color": "#8090c7", + "symbol": "square", + "size": 8 + }, + "subplot": "polar7", + "text": "sector: 0->360
rotation: 90
direction: counterclockwise" + }, + { + "type": "scatterpolar", + "mode": "lines+markers", + "r": [ + 1, + 2, + 3, + 4, + 5 + ], + "theta": [ + 0, + 90, + 180, + 360 + ], + "line": { + "color": "#ff66ab" + }, + "marker": { + "color": "#8090c7", + "symbol": "square", + "size": 8 + }, + "subplot": "polar8", + "text": "sector: 0->360
rotation: 0
direction: clockwise" + }, + { + "type": "scatterpolar", + "mode": "lines+markers", + "r": [ + 1, + 2, + 3, + 4, + 5 + ], + "theta": [ + 0, + 90, + 180, + 360 + ], + "line": { + "color": "#ff66ab" + }, + "marker": { + "color": "#8090c7", + "symbol": "square", + "size": 8 + }, + "subplot": "polar9", + "text": "sector: 0->360
rotation: 0
direction: counterclockwise" + } + ], + "layout": { + "margin": { + "l": 40, + "r": 40, + "t": 40, + "b": 40 + }, + "width": 600, + "height": 800, + "showlegend": false, + "polar": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0, + 0.3 + ], + "y": [ + 0.6833333333333333, + 1 + ] + }, + "radialaxis": { + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + }, + "rotation": -90, + "direction": "clockwise" + } + }, + "polar2": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0, + 0.3 + ], + "y": [ + 0.35, + 0.6499999999999999 + ] + }, + "radialaxis": { + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + }, + "rotation": 90, + "direction": "clockwise" + } + }, + "polar3": { + "sector": [ + 45, + 135 + ], + "domain": { + "x": [ + 0, + 0.3 + ], + "y": [ + 0, + 0.31666666666666665 + ] + }, + "radialaxis": { + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + }, + "rotation": 90, + "direction": "clockwise" + } + }, + "polar4": { + "sector": [ + 135, + 225 + ], + "domain": { + "x": [ + 0.36666666666666664, + 0.6333333333333333 + ], + "y": [ + 0.6833333333333333, + 1 + ] + }, + "radialaxis": { + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + }, + "rotation": 90, + "direction": "clockwise" + } + }, + "polar5": { + "sector": [ + 135, + 225 + ], + "domain": { + "x": [ + 0.36666666666666664, + 0.6333333333333333 + ], + "y": [ + 0.35, + 0.6499999999999999 + ] + }, + "radialaxis": { + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + }, + "rotation": 90, + "direction": "counterclockwise" + } + }, + "polar6": { + "sector": [ + 45, + 135 + ], + "domain": { + "x": [ + 0.36666666666666664, + 0.6333333333333333 + ], + "y": [ + 0, + 0.31666666666666665 + ] + }, + "radialaxis": { + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + }, + "rotation": 90, + "direction": "counterclockwise" + } + }, + "polar7": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0.7, + 1 + ], + "y": [ + 0.6833333333333333, + 1 + ] + }, + "radialaxis": { + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + }, + "rotation": 90, + "direction": "counterclockwise" + } + }, + "polar8": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0.7, + 1 + ], + "y": [ + 0.35, + 0.6499999999999999 + ] + }, + "radialaxis": { + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + }, + "rotation": 0, + "direction": "clockwise" + } + }, + "polar9": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0.7, + 1 + ], + "y": [ + 0, + 0.31666666666666665 + ] + }, + "radialaxis": { + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + }, + "rotation": 0, + "direction": "counterclockwise" + } + } + } +} diff --git a/test/image/mocks/polar_fills.json b/test/image/mocks/polar_fills.json new file mode 100644 index 00000000000..12c333a7af7 --- /dev/null +++ b/test/image/mocks/polar_fills.json @@ -0,0 +1,57 @@ +{ + "data": [{ + "type": "scatterpolar", + "mode": "lines", + "r": [ 3, 3, 4, 3 ], + "theta": [ 0, 45, 0, 0 ], + "fill": "toself", + "hoveron": "fills" + }, { + "type": "scatterpolar", + "mode": "lines", + "r": [ 2, 2, 2, 2, 4, 2], + "theta": [ 0, 90, 180, 270, 350, 360 ], + "fill": "toself", + "hoveron": "fills" + }, { + "type": "scatterpolar", + "mode": "lines", + "r": [5, 1, 2, 4, 5], + "theta": [0, 90, 180, 270, 0], + "fill": "toself", + "hoveron": "fills", + "subplot": "polar2" + }, { + "type": "scatterpolar", + "mode": "markers", + "r": [5, 1, 1.5, 4, 5], + "theta": [0, 270, 180, 90, 0], + "fill": "toself", + "hoveron": "fills", + "subplot": "polar2" + }], + "layout": { + "polar": { + "domain": { + "x": [0, 1], + "y": [0, 0.46] + }, + "radialaxis": { + "range": [0, 5] + } + }, + "polar2": { + "domain": { + "x": [0, 1], + "y": [0.54, 1] + }, + "radialaxis": { + "range": [2, 5] + } + }, + "showlegend": false, + "margin": {"t": 40, "b": 40}, + "width": 500, + "height": 800 + } +} diff --git a/test/image/mocks/polar_line.json b/test/image/mocks/polar_line.json index ca700af4019..0d5b8358a75 100644 --- a/test/image/mocks/polar_line.json +++ b/test/image/mocks/polar_line.json @@ -64,7 +64,7 @@ 0.995, 1 ], - "t": [ + "theta": [ 0, 6, 12, @@ -129,13 +129,11 @@ ], "mode": "lines", "name": "Figure8", - "marker": { - "color": "none", - "line": { - "color": "peru" - } + "line": { + "color": "peru", + "shape": "spline" }, - "type": "scatter" + "type": "scatterpolar" }, { "r": [ @@ -201,7 +199,7 @@ 0.997, 1 ], - "t": [ + "theta": [ 0, 6, 12, @@ -266,13 +264,11 @@ ], "mode": "lines", "name": "Cardioid", - "marker": { - "color": "none", - "line": { - "color": "darkviolet" - } + "line": { + "color": "darkviolet", + "shape": "spline" }, - "type": "scatter" + "type": "scatterpolar" }, { "r": [ @@ -338,7 +334,7 @@ 0.996, 1 ], - "t": [ + "theta": [ 0, 6, 12, @@ -403,13 +399,11 @@ ], "mode": "lines", "name": "Hypercardioid", - "marker": { - "color": "none", - "line": { - "color": "deepskyblue" - } + "line": { + "color": "deepskyblue", + "shape": "spline" }, - "type": "scatter" + "type": "scatterpolar" }, { "r": [ @@ -475,7 +469,7 @@ 0.998, 1 ], - "t": [ + "theta": [ 0, 6, 12, @@ -540,13 +534,11 @@ ], "mode": "lines", "name": "Subcardioid", - "marker": { - "color": "none", - "line": { - "color": "orangered" - } + "line": { + "color": "orangered", + "shape": "spline" }, - "type": "scatter" + "type": "scatterpolar" }, { "r": [ @@ -612,7 +604,7 @@ 0.997, 1 ], - "t": [ + "theta": [ 0, 6, 12, @@ -677,13 +669,11 @@ ], "mode": "lines", "name": "Supercardioid", - "marker": { - "color": "none", - "line": { - "color": "green" - } + "line": { + "color": "green", + "shape": "spline" }, - "type": "scatter" + "type": "scatterpolar" } ], "layout": { @@ -693,18 +683,25 @@ "size": 12, "color": "#000" }, - "showlegend": true, "width": 500, "height": 400, - "margin": { - "l": 40, - "r": 40, - "b": 20, - "t": 40, - "pad": 0 + "margin": {"l": 40, "r": 40, "b": 30, "t": 60, "pad": 0}, + "polar": { + "bgcolor": "rgb(255, 255, 255)", + "radialaxis": { + "range": [0, 1.1], + "tickangle": 45, + "tick0": 0, + "dtick": 0.1 + }, + "angularaxis": { + "thetaunit": "radians", + "showgrid": true, + "tickwidth": 2, + "ticklen": 5 + } }, - "paper_bgcolor": "rgb(255, 255, 255)", - "plot_bgcolor": "rgb(255, 255, 255)", - "orientation": -90 + "showlegend": false, + "paper_bgcolor": "rgb(255, 255, 255)" } } diff --git a/test/image/mocks/polar_radial-range.json b/test/image/mocks/polar_radial-range.json new file mode 100644 index 00000000000..fdead28258d --- /dev/null +++ b/test/image/mocks/polar_radial-range.json @@ -0,0 +1,3506 @@ +{ + "data": [ + { + "type": "scatterpolar", + "mode": "markers", + "thetaunit": "radians", + "r": [ + -7, + -6.677171657295865, + -5.8602297267763, + -4.783801847887817, + -3.5874515326384593, + -2.3390950225962595, + -1.0735269436884014, + 0.18920519215874165, + 1.4360601879488435, + 2.6576160452466553, + 3.846483811157743, + 4.996514127794006, + 6.102377312297609, + 7.159330851891905, + 8.163086228628476, + 9.109730904238052, + 9.995682152642399, + 10.817659867047684, + 11.572670943194037, + 12.25800083299363, + 12.871209560461125, + 13.410130487594078, + 13.872870719986338, + 14.257812416539497, + 14.56361450710342, + 14.789214479225297, + 14.933830001678452, + 14.996960227048515, + 14.978386670256494, + 14.878173602246008, + 14.696667933527127, + 14.434498594947609, + 14.092575456547515, + 13.672087863382735, + 13.174502914343229, + 12.601563672528265, + 11.955287583029516, + 11.237965500647096, + 10.45216191984316, + 9.600717291725175, + 8.686753776042433, + 7.713686530256631, + 6.685243901789182, + 5.605502076697052, + 4.478943657079398, + 3.3105569410231084, + 2.106006890508896, + 0.8719378300752894, + -0.38346929739190116, + -1.6494181255953269, + -2.9102767589706247, + -4.140684742281467, + -5.294089380351261, + -6.276056828377587, + -6.900360078316798, + -6.93045552311237, + -6.3512713745354, + -5.392446982236445, + -4.249682677387055, + -3.023887459696285, + -1.764542166186665, + -0.4983108255887885, + 0.7585525964717466, + 1.9949289127341512, + 3.2024551195254833, + 4.374375518263381, + 5.504951297995955, + 6.5891411378116835, + 7.622421362455494, + 8.600681721434391, + 9.520163953508973, + 10.377425432664612, + 11.169317934266157, + 11.89297570027832, + 12.545809285224603, + 13.12550299140446, + 13.630014491243095, + 14.057575718262928, + 14.406694412794486, + 14.676155905710921, + 14.865024854723394, + 14.972646737936063, + 14.99864897369961, + 14.942941584047475, + 14.805717357650007, + 14.587451501958043, + 14.288900806813285, + 13.911102376857627, + 13.455372031599552, + 12.923302525209174, + 12.316761810488643, + 11.637891674409182, + 10.88910722427482, + 10.073097933803993, + 9.192831317670752, + 8.251560879081833, + 7.252840925046961, + 6.200552459146959, + 5.098947199758895, + 3.9527219453586024, + 2.7671453348038746, + 1.5482785951237918, + 0.3033728154409481, + -0.9583838181144966, + -2.224380548066125, + -3.475431772907479, + -4.67861416203452, + -5.770266936574653, + -6.619368107122337, + -6.997186679891282, + -6.730863076197168, + -5.948131625673865, + -4.888045582474995, + -3.699062100915378, + -2.4536764616552933, + -1.188697946883904, + 0.07490402544249086, + 1.3236313731451403, + 2.547815790529782, + 3.7399241622284087, + 4.893715645617382, + 6.0037976481590185, + 7.0653808704534065, + 8.074139365293558, + 9.026128686551374, + 9.917737469189777, + 10.745658876663125, + 11.506874147110722, + 12.198643626284845, + 12.818502460058085, + 13.364259163239698, + 13.833995910975325, + 14.226069789660574, + 14.53911449342608, + 14.772042115531686, + 14.924044794200249, + 14.994596049355305, + 14.98345170277986, + 14.890650317427195, + 14.716513127523012, + 14.461643463878833, + 14.126925712100608, + 13.713523878821988, + 13.222879887178173, + 12.656711783544559, + 12.01701212209868, + 11.30604691605108, + 10.526355727102729, + 9.680753745578347, + 8.772337157419077, + 7.804493814765269, + 6.78092243138655, + 5.7056656021300896, + 4.583165656484351, + 3.4183592443732316, + 2.21683990299392, + 0.9851450221385711, + -0.26871798036271954, + -1.5342581395038284, + -2.796427574960201, + -4.031080579695696, + -5.194380496024298, + -6.197957552108722, + -6.865172889158288, + -6.9553048872586745, + -6.423410689400473, + -5.489353768436682, + -4.358026951631645, + -3.1372350654797163, + -1.8796158394079452, + -0.6132330511005932, + 0.6449962649265899, + 1.8836114524659138, + 3.094058363254483, + 4.2694652237952155, + 5.4040168173241145, + 6.492617354524352, + 7.530701246781785, + 8.514123682916576, + 9.4390961939271, + 10.302148513580132, + 11.100106256040139, + 11.830078306934823, + 12.4894502495081, + 13.075881540649771, + 13.587304978075252, + 14.02192750498196, + 14.378231715853918, + 14.654977631973974, + 14.851204451235947, + 14.966232070006594, + 14.999662241009936, + 14.951379280610162, + 14.821550278179114, + 14.610624794253209, + 14.319334066746727, + 13.948689779165878, + 13.499982485514437, + 12.974779838467526, + 12.374924837626374, + 11.702534414211193 + ], + "theta": [ + 0, + 0.031415926535897934, + 0.06283185307179587, + 0.0942477796076938, + 0.12566370614359174, + 0.15707963267948966, + 0.18849555921538758, + 0.2199114857512855, + 0.2513274122871834, + 0.28274333882308134, + 0.31415926535897926, + 0.3455751918948772, + 0.3769911184307751, + 0.408407044966673, + 0.43982297150257094, + 0.47123889803846886, + 0.5026548245743668, + 0.5340707511102648, + 0.5654866776461628, + 0.5969026041820608, + 0.6283185307179587, + 0.6597344572538567, + 0.6911503837897547, + 0.7225663103256527, + 0.7539822368615506, + 0.7853981633974486, + 0.8168140899333466, + 0.8482300164692446, + 0.8796459430051425, + 0.9110618695410405, + 0.9424777960769385, + 0.9738937226128365, + 1.0053096491487343, + 1.0367255756846323, + 1.0681415022205303, + 1.0995574287564283, + 1.1309733552923262, + 1.1623892818282242, + 1.1938052083641222, + 1.2252211349000202, + 1.2566370614359181, + 1.288052987971816, + 1.319468914507714, + 1.350884841043612, + 1.38230076757951, + 1.413716694115408, + 1.445132620651306, + 1.476548547187204, + 1.507964473723102, + 1.539380400259, + 1.570796326794898, + 1.6022122533307959, + 1.6336281798666938, + 1.6650441064025918, + 1.6964600329384898, + 1.7278759594743878, + 1.7592918860102857, + 1.7907078125461837, + 1.8221237390820817, + 1.8535396656179797, + 1.8849555921538776, + 1.9163715186897756, + 1.9477874452256736, + 1.9792033717615716, + 2.0106192982974695, + 2.0420352248333673, + 2.073451151369265, + 2.104867077905163, + 2.1362830044410606, + 2.1676989309769583, + 2.199114857512856, + 2.230530784048754, + 2.2619467105846516, + 2.2933626371205493, + 2.324778563656447, + 2.356194490192345, + 2.3876104167282426, + 2.4190263432641403, + 2.450442269800038, + 2.481858196335936, + 2.5132741228718336, + 2.5446900494077314, + 2.576105975943629, + 2.607521902479527, + 2.6389378290154246, + 2.6703537555513224, + 2.70176968208722, + 2.733185608623118, + 2.7646015351590156, + 2.7960174616949134, + 2.827433388230811, + 2.858849314766709, + 2.8902652413026066, + 2.9216811678385044, + 2.953097094374402, + 2.9845130209103, + 3.0159289474461977, + 3.0473448739820954, + 3.078760800517993, + 3.110176727053891, + 3.1415926535897887, + 3.1730085801256864, + 3.204424506661584, + 3.235840433197482, + 3.2672563597333797, + 3.2986722862692774, + 3.330088212805175, + 3.361504139341073, + 3.3929200658769707, + 3.4243359924128685, + 3.455751918948766, + 3.487167845484664, + 3.5185837720205617, + 3.5499996985564595, + 3.5814156250923572, + 3.612831551628255, + 3.6442474781641527, + 3.6756634047000505, + 3.7070793312359482, + 3.738495257771846, + 3.7699111843077437, + 3.8013271108436415, + 3.8327430373795393, + 3.864158963915437, + 3.8955748904513348, + 3.9269908169872325, + 3.9584067435231303, + 3.989822670059028, + 4.021238596594926, + 4.0526545231308235, + 4.084070449666721, + 4.115486376202619, + 4.146902302738517, + 4.1783182292744145, + 4.209734155810312, + 4.24115008234621, + 4.272566008882108, + 4.3039819354180056, + 4.335397861953903, + 4.366813788489801, + 4.398229715025699, + 4.429645641561597, + 4.461061568097494, + 4.492477494633392, + 4.52389342116929, + 4.555309347705188, + 4.586725274241085, + 4.618141200776983, + 4.649557127312881, + 4.680973053848779, + 4.712388980384676, + 4.743804906920574, + 4.775220833456472, + 4.80663675999237, + 4.838052686528267, + 4.869468613064165, + 4.900884539600063, + 4.932300466135961, + 4.963716392671858, + 4.995132319207756, + 5.026548245743654, + 5.057964172279552, + 5.089380098815449, + 5.120796025351347, + 5.152211951887245, + 5.183627878423143, + 5.21504380495904, + 5.246459731494938, + 5.277875658030836, + 5.309291584566734, + 5.340707511102631, + 5.372123437638529, + 5.403539364174427, + 5.434955290710325, + 5.466371217246222, + 5.49778714378212, + 5.529203070318018, + 5.560618996853916, + 5.5920349233898135, + 5.623450849925711, + 5.654866776461609, + 5.686282702997507, + 5.7176986295334045, + 5.749114556069302, + 5.7805304826052, + 5.811946409141098, + 5.8433623356769955, + 5.874778262212893, + 5.906194188748791, + 5.937610115284689, + 5.9690260418205865, + 6.000441968356484, + 6.031857894892382, + 6.06327382142828, + 6.0946897479641775, + 6.126105674500075, + 6.157521601035973, + 6.188937527571871, + 6.2203534541077685, + 6.251769380643666, + 6.283185307179564 + ], + "marker": { + "color": "#85144b" + }, + "subplot": "polar" + }, + { + "type": "scatterpolar", + "mode": "markers", + "thetaunit": "radians", + "r": [ + -7, + -6.677171657295865, + -5.8602297267763, + -4.783801847887817, + -3.5874515326384593, + -2.3390950225962595, + -1.0735269436884014, + 0.18920519215874165, + 1.4360601879488435, + 2.6576160452466553, + 3.846483811157743, + 4.996514127794006, + 6.102377312297609, + 7.159330851891905, + 8.163086228628476, + 9.109730904238052, + 9.995682152642399, + 10.817659867047684, + 11.572670943194037, + 12.25800083299363, + 12.871209560461125, + 13.410130487594078, + 13.872870719986338, + 14.257812416539497, + 14.56361450710342, + 14.789214479225297, + 14.933830001678452, + 14.996960227048515, + 14.978386670256494, + 14.878173602246008, + 14.696667933527127, + 14.434498594947609, + 14.092575456547515, + 13.672087863382735, + 13.174502914343229, + 12.601563672528265, + 11.955287583029516, + 11.237965500647096, + 10.45216191984316, + 9.600717291725175, + 8.686753776042433, + 7.713686530256631, + 6.685243901789182, + 5.605502076697052, + 4.478943657079398, + 3.3105569410231084, + 2.106006890508896, + 0.8719378300752894, + -0.38346929739190116, + -1.6494181255953269, + -2.9102767589706247, + -4.140684742281467, + -5.294089380351261, + -6.276056828377587, + -6.900360078316798, + -6.93045552311237, + -6.3512713745354, + -5.392446982236445, + -4.249682677387055, + -3.023887459696285, + -1.764542166186665, + -0.4983108255887885, + 0.7585525964717466, + 1.9949289127341512, + 3.2024551195254833, + 4.374375518263381, + 5.504951297995955, + 6.5891411378116835, + 7.622421362455494, + 8.600681721434391, + 9.520163953508973, + 10.377425432664612, + 11.169317934266157, + 11.89297570027832, + 12.545809285224603, + 13.12550299140446, + 13.630014491243095, + 14.057575718262928, + 14.406694412794486, + 14.676155905710921, + 14.865024854723394, + 14.972646737936063, + 14.99864897369961, + 14.942941584047475, + 14.805717357650007, + 14.587451501958043, + 14.288900806813285, + 13.911102376857627, + 13.455372031599552, + 12.923302525209174, + 12.316761810488643, + 11.637891674409182, + 10.88910722427482, + 10.073097933803993, + 9.192831317670752, + 8.251560879081833, + 7.252840925046961, + 6.200552459146959, + 5.098947199758895, + 3.9527219453586024, + 2.7671453348038746, + 1.5482785951237918, + 0.3033728154409481, + -0.9583838181144966, + -2.224380548066125, + -3.475431772907479, + -4.67861416203452, + -5.770266936574653, + -6.619368107122337, + -6.997186679891282, + -6.730863076197168, + -5.948131625673865, + -4.888045582474995, + -3.699062100915378, + -2.4536764616552933, + -1.188697946883904, + 0.07490402544249086, + 1.3236313731451403, + 2.547815790529782, + 3.7399241622284087, + 4.893715645617382, + 6.0037976481590185, + 7.0653808704534065, + 8.074139365293558, + 9.026128686551374, + 9.917737469189777, + 10.745658876663125, + 11.506874147110722, + 12.198643626284845, + 12.818502460058085, + 13.364259163239698, + 13.833995910975325, + 14.226069789660574, + 14.53911449342608, + 14.772042115531686, + 14.924044794200249, + 14.994596049355305, + 14.98345170277986, + 14.890650317427195, + 14.716513127523012, + 14.461643463878833, + 14.126925712100608, + 13.713523878821988, + 13.222879887178173, + 12.656711783544559, + 12.01701212209868, + 11.30604691605108, + 10.526355727102729, + 9.680753745578347, + 8.772337157419077, + 7.804493814765269, + 6.78092243138655, + 5.7056656021300896, + 4.583165656484351, + 3.4183592443732316, + 2.21683990299392, + 0.9851450221385711, + -0.26871798036271954, + -1.5342581395038284, + -2.796427574960201, + -4.031080579695696, + -5.194380496024298, + -6.197957552108722, + -6.865172889158288, + -6.9553048872586745, + -6.423410689400473, + -5.489353768436682, + -4.358026951631645, + -3.1372350654797163, + -1.8796158394079452, + -0.6132330511005932, + 0.6449962649265899, + 1.8836114524659138, + 3.094058363254483, + 4.2694652237952155, + 5.4040168173241145, + 6.492617354524352, + 7.530701246781785, + 8.514123682916576, + 9.4390961939271, + 10.302148513580132, + 11.100106256040139, + 11.830078306934823, + 12.4894502495081, + 13.075881540649771, + 13.587304978075252, + 14.02192750498196, + 14.378231715853918, + 14.654977631973974, + 14.851204451235947, + 14.966232070006594, + 14.999662241009936, + 14.951379280610162, + 14.821550278179114, + 14.610624794253209, + 14.319334066746727, + 13.948689779165878, + 13.499982485514437, + 12.974779838467526, + 12.374924837626374, + 11.702534414211193 + ], + "theta": [ + 0, + 0.031415926535897934, + 0.06283185307179587, + 0.0942477796076938, + 0.12566370614359174, + 0.15707963267948966, + 0.18849555921538758, + 0.2199114857512855, + 0.2513274122871834, + 0.28274333882308134, + 0.31415926535897926, + 0.3455751918948772, + 0.3769911184307751, + 0.408407044966673, + 0.43982297150257094, + 0.47123889803846886, + 0.5026548245743668, + 0.5340707511102648, + 0.5654866776461628, + 0.5969026041820608, + 0.6283185307179587, + 0.6597344572538567, + 0.6911503837897547, + 0.7225663103256527, + 0.7539822368615506, + 0.7853981633974486, + 0.8168140899333466, + 0.8482300164692446, + 0.8796459430051425, + 0.9110618695410405, + 0.9424777960769385, + 0.9738937226128365, + 1.0053096491487343, + 1.0367255756846323, + 1.0681415022205303, + 1.0995574287564283, + 1.1309733552923262, + 1.1623892818282242, + 1.1938052083641222, + 1.2252211349000202, + 1.2566370614359181, + 1.288052987971816, + 1.319468914507714, + 1.350884841043612, + 1.38230076757951, + 1.413716694115408, + 1.445132620651306, + 1.476548547187204, + 1.507964473723102, + 1.539380400259, + 1.570796326794898, + 1.6022122533307959, + 1.6336281798666938, + 1.6650441064025918, + 1.6964600329384898, + 1.7278759594743878, + 1.7592918860102857, + 1.7907078125461837, + 1.8221237390820817, + 1.8535396656179797, + 1.8849555921538776, + 1.9163715186897756, + 1.9477874452256736, + 1.9792033717615716, + 2.0106192982974695, + 2.0420352248333673, + 2.073451151369265, + 2.104867077905163, + 2.1362830044410606, + 2.1676989309769583, + 2.199114857512856, + 2.230530784048754, + 2.2619467105846516, + 2.2933626371205493, + 2.324778563656447, + 2.356194490192345, + 2.3876104167282426, + 2.4190263432641403, + 2.450442269800038, + 2.481858196335936, + 2.5132741228718336, + 2.5446900494077314, + 2.576105975943629, + 2.607521902479527, + 2.6389378290154246, + 2.6703537555513224, + 2.70176968208722, + 2.733185608623118, + 2.7646015351590156, + 2.7960174616949134, + 2.827433388230811, + 2.858849314766709, + 2.8902652413026066, + 2.9216811678385044, + 2.953097094374402, + 2.9845130209103, + 3.0159289474461977, + 3.0473448739820954, + 3.078760800517993, + 3.110176727053891, + 3.1415926535897887, + 3.1730085801256864, + 3.204424506661584, + 3.235840433197482, + 3.2672563597333797, + 3.2986722862692774, + 3.330088212805175, + 3.361504139341073, + 3.3929200658769707, + 3.4243359924128685, + 3.455751918948766, + 3.487167845484664, + 3.5185837720205617, + 3.5499996985564595, + 3.5814156250923572, + 3.612831551628255, + 3.6442474781641527, + 3.6756634047000505, + 3.7070793312359482, + 3.738495257771846, + 3.7699111843077437, + 3.8013271108436415, + 3.8327430373795393, + 3.864158963915437, + 3.8955748904513348, + 3.9269908169872325, + 3.9584067435231303, + 3.989822670059028, + 4.021238596594926, + 4.0526545231308235, + 4.084070449666721, + 4.115486376202619, + 4.146902302738517, + 4.1783182292744145, + 4.209734155810312, + 4.24115008234621, + 4.272566008882108, + 4.3039819354180056, + 4.335397861953903, + 4.366813788489801, + 4.398229715025699, + 4.429645641561597, + 4.461061568097494, + 4.492477494633392, + 4.52389342116929, + 4.555309347705188, + 4.586725274241085, + 4.618141200776983, + 4.649557127312881, + 4.680973053848779, + 4.712388980384676, + 4.743804906920574, + 4.775220833456472, + 4.80663675999237, + 4.838052686528267, + 4.869468613064165, + 4.900884539600063, + 4.932300466135961, + 4.963716392671858, + 4.995132319207756, + 5.026548245743654, + 5.057964172279552, + 5.089380098815449, + 5.120796025351347, + 5.152211951887245, + 5.183627878423143, + 5.21504380495904, + 5.246459731494938, + 5.277875658030836, + 5.309291584566734, + 5.340707511102631, + 5.372123437638529, + 5.403539364174427, + 5.434955290710325, + 5.466371217246222, + 5.49778714378212, + 5.529203070318018, + 5.560618996853916, + 5.5920349233898135, + 5.623450849925711, + 5.654866776461609, + 5.686282702997507, + 5.7176986295334045, + 5.749114556069302, + 5.7805304826052, + 5.811946409141098, + 5.8433623356769955, + 5.874778262212893, + 5.906194188748791, + 5.937610115284689, + 5.9690260418205865, + 6.000441968356484, + 6.031857894892382, + 6.06327382142828, + 6.0946897479641775, + 6.126105674500075, + 6.157521601035973, + 6.188937527571871, + 6.2203534541077685, + 6.251769380643666, + 6.283185307179564 + ], + "marker": { + "color": "#85144b" + }, + "subplot": "polar2" + }, + { + "type": "scatterpolar", + "mode": "markers", + "thetaunit": "radians", + "r": [ + -7, + -6.677171657295865, + -5.8602297267763, + -4.783801847887817, + -3.5874515326384593, + -2.3390950225962595, + -1.0735269436884014, + 0.18920519215874165, + 1.4360601879488435, + 2.6576160452466553, + 3.846483811157743, + 4.996514127794006, + 6.102377312297609, + 7.159330851891905, + 8.163086228628476, + 9.109730904238052, + 9.995682152642399, + 10.817659867047684, + 11.572670943194037, + 12.25800083299363, + 12.871209560461125, + 13.410130487594078, + 13.872870719986338, + 14.257812416539497, + 14.56361450710342, + 14.789214479225297, + 14.933830001678452, + 14.996960227048515, + 14.978386670256494, + 14.878173602246008, + 14.696667933527127, + 14.434498594947609, + 14.092575456547515, + 13.672087863382735, + 13.174502914343229, + 12.601563672528265, + 11.955287583029516, + 11.237965500647096, + 10.45216191984316, + 9.600717291725175, + 8.686753776042433, + 7.713686530256631, + 6.685243901789182, + 5.605502076697052, + 4.478943657079398, + 3.3105569410231084, + 2.106006890508896, + 0.8719378300752894, + -0.38346929739190116, + -1.6494181255953269, + -2.9102767589706247, + -4.140684742281467, + -5.294089380351261, + -6.276056828377587, + -6.900360078316798, + -6.93045552311237, + -6.3512713745354, + -5.392446982236445, + -4.249682677387055, + -3.023887459696285, + -1.764542166186665, + -0.4983108255887885, + 0.7585525964717466, + 1.9949289127341512, + 3.2024551195254833, + 4.374375518263381, + 5.504951297995955, + 6.5891411378116835, + 7.622421362455494, + 8.600681721434391, + 9.520163953508973, + 10.377425432664612, + 11.169317934266157, + 11.89297570027832, + 12.545809285224603, + 13.12550299140446, + 13.630014491243095, + 14.057575718262928, + 14.406694412794486, + 14.676155905710921, + 14.865024854723394, + 14.972646737936063, + 14.99864897369961, + 14.942941584047475, + 14.805717357650007, + 14.587451501958043, + 14.288900806813285, + 13.911102376857627, + 13.455372031599552, + 12.923302525209174, + 12.316761810488643, + 11.637891674409182, + 10.88910722427482, + 10.073097933803993, + 9.192831317670752, + 8.251560879081833, + 7.252840925046961, + 6.200552459146959, + 5.098947199758895, + 3.9527219453586024, + 2.7671453348038746, + 1.5482785951237918, + 0.3033728154409481, + -0.9583838181144966, + -2.224380548066125, + -3.475431772907479, + -4.67861416203452, + -5.770266936574653, + -6.619368107122337, + -6.997186679891282, + -6.730863076197168, + -5.948131625673865, + -4.888045582474995, + -3.699062100915378, + -2.4536764616552933, + -1.188697946883904, + 0.07490402544249086, + 1.3236313731451403, + 2.547815790529782, + 3.7399241622284087, + 4.893715645617382, + 6.0037976481590185, + 7.0653808704534065, + 8.074139365293558, + 9.026128686551374, + 9.917737469189777, + 10.745658876663125, + 11.506874147110722, + 12.198643626284845, + 12.818502460058085, + 13.364259163239698, + 13.833995910975325, + 14.226069789660574, + 14.53911449342608, + 14.772042115531686, + 14.924044794200249, + 14.994596049355305, + 14.98345170277986, + 14.890650317427195, + 14.716513127523012, + 14.461643463878833, + 14.126925712100608, + 13.713523878821988, + 13.222879887178173, + 12.656711783544559, + 12.01701212209868, + 11.30604691605108, + 10.526355727102729, + 9.680753745578347, + 8.772337157419077, + 7.804493814765269, + 6.78092243138655, + 5.7056656021300896, + 4.583165656484351, + 3.4183592443732316, + 2.21683990299392, + 0.9851450221385711, + -0.26871798036271954, + -1.5342581395038284, + -2.796427574960201, + -4.031080579695696, + -5.194380496024298, + -6.197957552108722, + -6.865172889158288, + -6.9553048872586745, + -6.423410689400473, + -5.489353768436682, + -4.358026951631645, + -3.1372350654797163, + -1.8796158394079452, + -0.6132330511005932, + 0.6449962649265899, + 1.8836114524659138, + 3.094058363254483, + 4.2694652237952155, + 5.4040168173241145, + 6.492617354524352, + 7.530701246781785, + 8.514123682916576, + 9.4390961939271, + 10.302148513580132, + 11.100106256040139, + 11.830078306934823, + 12.4894502495081, + 13.075881540649771, + 13.587304978075252, + 14.02192750498196, + 14.378231715853918, + 14.654977631973974, + 14.851204451235947, + 14.966232070006594, + 14.999662241009936, + 14.951379280610162, + 14.821550278179114, + 14.610624794253209, + 14.319334066746727, + 13.948689779165878, + 13.499982485514437, + 12.974779838467526, + 12.374924837626374, + 11.702534414211193 + ], + "theta": [ + 0, + 0.031415926535897934, + 0.06283185307179587, + 0.0942477796076938, + 0.12566370614359174, + 0.15707963267948966, + 0.18849555921538758, + 0.2199114857512855, + 0.2513274122871834, + 0.28274333882308134, + 0.31415926535897926, + 0.3455751918948772, + 0.3769911184307751, + 0.408407044966673, + 0.43982297150257094, + 0.47123889803846886, + 0.5026548245743668, + 0.5340707511102648, + 0.5654866776461628, + 0.5969026041820608, + 0.6283185307179587, + 0.6597344572538567, + 0.6911503837897547, + 0.7225663103256527, + 0.7539822368615506, + 0.7853981633974486, + 0.8168140899333466, + 0.8482300164692446, + 0.8796459430051425, + 0.9110618695410405, + 0.9424777960769385, + 0.9738937226128365, + 1.0053096491487343, + 1.0367255756846323, + 1.0681415022205303, + 1.0995574287564283, + 1.1309733552923262, + 1.1623892818282242, + 1.1938052083641222, + 1.2252211349000202, + 1.2566370614359181, + 1.288052987971816, + 1.319468914507714, + 1.350884841043612, + 1.38230076757951, + 1.413716694115408, + 1.445132620651306, + 1.476548547187204, + 1.507964473723102, + 1.539380400259, + 1.570796326794898, + 1.6022122533307959, + 1.6336281798666938, + 1.6650441064025918, + 1.6964600329384898, + 1.7278759594743878, + 1.7592918860102857, + 1.7907078125461837, + 1.8221237390820817, + 1.8535396656179797, + 1.8849555921538776, + 1.9163715186897756, + 1.9477874452256736, + 1.9792033717615716, + 2.0106192982974695, + 2.0420352248333673, + 2.073451151369265, + 2.104867077905163, + 2.1362830044410606, + 2.1676989309769583, + 2.199114857512856, + 2.230530784048754, + 2.2619467105846516, + 2.2933626371205493, + 2.324778563656447, + 2.356194490192345, + 2.3876104167282426, + 2.4190263432641403, + 2.450442269800038, + 2.481858196335936, + 2.5132741228718336, + 2.5446900494077314, + 2.576105975943629, + 2.607521902479527, + 2.6389378290154246, + 2.6703537555513224, + 2.70176968208722, + 2.733185608623118, + 2.7646015351590156, + 2.7960174616949134, + 2.827433388230811, + 2.858849314766709, + 2.8902652413026066, + 2.9216811678385044, + 2.953097094374402, + 2.9845130209103, + 3.0159289474461977, + 3.0473448739820954, + 3.078760800517993, + 3.110176727053891, + 3.1415926535897887, + 3.1730085801256864, + 3.204424506661584, + 3.235840433197482, + 3.2672563597333797, + 3.2986722862692774, + 3.330088212805175, + 3.361504139341073, + 3.3929200658769707, + 3.4243359924128685, + 3.455751918948766, + 3.487167845484664, + 3.5185837720205617, + 3.5499996985564595, + 3.5814156250923572, + 3.612831551628255, + 3.6442474781641527, + 3.6756634047000505, + 3.7070793312359482, + 3.738495257771846, + 3.7699111843077437, + 3.8013271108436415, + 3.8327430373795393, + 3.864158963915437, + 3.8955748904513348, + 3.9269908169872325, + 3.9584067435231303, + 3.989822670059028, + 4.021238596594926, + 4.0526545231308235, + 4.084070449666721, + 4.115486376202619, + 4.146902302738517, + 4.1783182292744145, + 4.209734155810312, + 4.24115008234621, + 4.272566008882108, + 4.3039819354180056, + 4.335397861953903, + 4.366813788489801, + 4.398229715025699, + 4.429645641561597, + 4.461061568097494, + 4.492477494633392, + 4.52389342116929, + 4.555309347705188, + 4.586725274241085, + 4.618141200776983, + 4.649557127312881, + 4.680973053848779, + 4.712388980384676, + 4.743804906920574, + 4.775220833456472, + 4.80663675999237, + 4.838052686528267, + 4.869468613064165, + 4.900884539600063, + 4.932300466135961, + 4.963716392671858, + 4.995132319207756, + 5.026548245743654, + 5.057964172279552, + 5.089380098815449, + 5.120796025351347, + 5.152211951887245, + 5.183627878423143, + 5.21504380495904, + 5.246459731494938, + 5.277875658030836, + 5.309291584566734, + 5.340707511102631, + 5.372123437638529, + 5.403539364174427, + 5.434955290710325, + 5.466371217246222, + 5.49778714378212, + 5.529203070318018, + 5.560618996853916, + 5.5920349233898135, + 5.623450849925711, + 5.654866776461609, + 5.686282702997507, + 5.7176986295334045, + 5.749114556069302, + 5.7805304826052, + 5.811946409141098, + 5.8433623356769955, + 5.874778262212893, + 5.906194188748791, + 5.937610115284689, + 5.9690260418205865, + 6.000441968356484, + 6.031857894892382, + 6.06327382142828, + 6.0946897479641775, + 6.126105674500075, + 6.157521601035973, + 6.188937527571871, + 6.2203534541077685, + 6.251769380643666, + 6.283185307179564 + ], + "marker": { + "color": "#85144b" + }, + "subplot": "polar3" + }, + { + "type": "scatterpolar", + "mode": "markers", + "thetaunit": "radians", + "r": [ + -7, + -6.677171657295865, + -5.8602297267763, + -4.783801847887817, + -3.5874515326384593, + -2.3390950225962595, + -1.0735269436884014, + 0.18920519215874165, + 1.4360601879488435, + 2.6576160452466553, + 3.846483811157743, + 4.996514127794006, + 6.102377312297609, + 7.159330851891905, + 8.163086228628476, + 9.109730904238052, + 9.995682152642399, + 10.817659867047684, + 11.572670943194037, + 12.25800083299363, + 12.871209560461125, + 13.410130487594078, + 13.872870719986338, + 14.257812416539497, + 14.56361450710342, + 14.789214479225297, + 14.933830001678452, + 14.996960227048515, + 14.978386670256494, + 14.878173602246008, + 14.696667933527127, + 14.434498594947609, + 14.092575456547515, + 13.672087863382735, + 13.174502914343229, + 12.601563672528265, + 11.955287583029516, + 11.237965500647096, + 10.45216191984316, + 9.600717291725175, + 8.686753776042433, + 7.713686530256631, + 6.685243901789182, + 5.605502076697052, + 4.478943657079398, + 3.3105569410231084, + 2.106006890508896, + 0.8719378300752894, + -0.38346929739190116, + -1.6494181255953269, + -2.9102767589706247, + -4.140684742281467, + -5.294089380351261, + -6.276056828377587, + -6.900360078316798, + -6.93045552311237, + -6.3512713745354, + -5.392446982236445, + -4.249682677387055, + -3.023887459696285, + -1.764542166186665, + -0.4983108255887885, + 0.7585525964717466, + 1.9949289127341512, + 3.2024551195254833, + 4.374375518263381, + 5.504951297995955, + 6.5891411378116835, + 7.622421362455494, + 8.600681721434391, + 9.520163953508973, + 10.377425432664612, + 11.169317934266157, + 11.89297570027832, + 12.545809285224603, + 13.12550299140446, + 13.630014491243095, + 14.057575718262928, + 14.406694412794486, + 14.676155905710921, + 14.865024854723394, + 14.972646737936063, + 14.99864897369961, + 14.942941584047475, + 14.805717357650007, + 14.587451501958043, + 14.288900806813285, + 13.911102376857627, + 13.455372031599552, + 12.923302525209174, + 12.316761810488643, + 11.637891674409182, + 10.88910722427482, + 10.073097933803993, + 9.192831317670752, + 8.251560879081833, + 7.252840925046961, + 6.200552459146959, + 5.098947199758895, + 3.9527219453586024, + 2.7671453348038746, + 1.5482785951237918, + 0.3033728154409481, + -0.9583838181144966, + -2.224380548066125, + -3.475431772907479, + -4.67861416203452, + -5.770266936574653, + -6.619368107122337, + -6.997186679891282, + -6.730863076197168, + -5.948131625673865, + -4.888045582474995, + -3.699062100915378, + -2.4536764616552933, + -1.188697946883904, + 0.07490402544249086, + 1.3236313731451403, + 2.547815790529782, + 3.7399241622284087, + 4.893715645617382, + 6.0037976481590185, + 7.0653808704534065, + 8.074139365293558, + 9.026128686551374, + 9.917737469189777, + 10.745658876663125, + 11.506874147110722, + 12.198643626284845, + 12.818502460058085, + 13.364259163239698, + 13.833995910975325, + 14.226069789660574, + 14.53911449342608, + 14.772042115531686, + 14.924044794200249, + 14.994596049355305, + 14.98345170277986, + 14.890650317427195, + 14.716513127523012, + 14.461643463878833, + 14.126925712100608, + 13.713523878821988, + 13.222879887178173, + 12.656711783544559, + 12.01701212209868, + 11.30604691605108, + 10.526355727102729, + 9.680753745578347, + 8.772337157419077, + 7.804493814765269, + 6.78092243138655, + 5.7056656021300896, + 4.583165656484351, + 3.4183592443732316, + 2.21683990299392, + 0.9851450221385711, + -0.26871798036271954, + -1.5342581395038284, + -2.796427574960201, + -4.031080579695696, + -5.194380496024298, + -6.197957552108722, + -6.865172889158288, + -6.9553048872586745, + -6.423410689400473, + -5.489353768436682, + -4.358026951631645, + -3.1372350654797163, + -1.8796158394079452, + -0.6132330511005932, + 0.6449962649265899, + 1.8836114524659138, + 3.094058363254483, + 4.2694652237952155, + 5.4040168173241145, + 6.492617354524352, + 7.530701246781785, + 8.514123682916576, + 9.4390961939271, + 10.302148513580132, + 11.100106256040139, + 11.830078306934823, + 12.4894502495081, + 13.075881540649771, + 13.587304978075252, + 14.02192750498196, + 14.378231715853918, + 14.654977631973974, + 14.851204451235947, + 14.966232070006594, + 14.999662241009936, + 14.951379280610162, + 14.821550278179114, + 14.610624794253209, + 14.319334066746727, + 13.948689779165878, + 13.499982485514437, + 12.974779838467526, + 12.374924837626374, + 11.702534414211193 + ], + "theta": [ + 0, + 0.031415926535897934, + 0.06283185307179587, + 0.0942477796076938, + 0.12566370614359174, + 0.15707963267948966, + 0.18849555921538758, + 0.2199114857512855, + 0.2513274122871834, + 0.28274333882308134, + 0.31415926535897926, + 0.3455751918948772, + 0.3769911184307751, + 0.408407044966673, + 0.43982297150257094, + 0.47123889803846886, + 0.5026548245743668, + 0.5340707511102648, + 0.5654866776461628, + 0.5969026041820608, + 0.6283185307179587, + 0.6597344572538567, + 0.6911503837897547, + 0.7225663103256527, + 0.7539822368615506, + 0.7853981633974486, + 0.8168140899333466, + 0.8482300164692446, + 0.8796459430051425, + 0.9110618695410405, + 0.9424777960769385, + 0.9738937226128365, + 1.0053096491487343, + 1.0367255756846323, + 1.0681415022205303, + 1.0995574287564283, + 1.1309733552923262, + 1.1623892818282242, + 1.1938052083641222, + 1.2252211349000202, + 1.2566370614359181, + 1.288052987971816, + 1.319468914507714, + 1.350884841043612, + 1.38230076757951, + 1.413716694115408, + 1.445132620651306, + 1.476548547187204, + 1.507964473723102, + 1.539380400259, + 1.570796326794898, + 1.6022122533307959, + 1.6336281798666938, + 1.6650441064025918, + 1.6964600329384898, + 1.7278759594743878, + 1.7592918860102857, + 1.7907078125461837, + 1.8221237390820817, + 1.8535396656179797, + 1.8849555921538776, + 1.9163715186897756, + 1.9477874452256736, + 1.9792033717615716, + 2.0106192982974695, + 2.0420352248333673, + 2.073451151369265, + 2.104867077905163, + 2.1362830044410606, + 2.1676989309769583, + 2.199114857512856, + 2.230530784048754, + 2.2619467105846516, + 2.2933626371205493, + 2.324778563656447, + 2.356194490192345, + 2.3876104167282426, + 2.4190263432641403, + 2.450442269800038, + 2.481858196335936, + 2.5132741228718336, + 2.5446900494077314, + 2.576105975943629, + 2.607521902479527, + 2.6389378290154246, + 2.6703537555513224, + 2.70176968208722, + 2.733185608623118, + 2.7646015351590156, + 2.7960174616949134, + 2.827433388230811, + 2.858849314766709, + 2.8902652413026066, + 2.9216811678385044, + 2.953097094374402, + 2.9845130209103, + 3.0159289474461977, + 3.0473448739820954, + 3.078760800517993, + 3.110176727053891, + 3.1415926535897887, + 3.1730085801256864, + 3.204424506661584, + 3.235840433197482, + 3.2672563597333797, + 3.2986722862692774, + 3.330088212805175, + 3.361504139341073, + 3.3929200658769707, + 3.4243359924128685, + 3.455751918948766, + 3.487167845484664, + 3.5185837720205617, + 3.5499996985564595, + 3.5814156250923572, + 3.612831551628255, + 3.6442474781641527, + 3.6756634047000505, + 3.7070793312359482, + 3.738495257771846, + 3.7699111843077437, + 3.8013271108436415, + 3.8327430373795393, + 3.864158963915437, + 3.8955748904513348, + 3.9269908169872325, + 3.9584067435231303, + 3.989822670059028, + 4.021238596594926, + 4.0526545231308235, + 4.084070449666721, + 4.115486376202619, + 4.146902302738517, + 4.1783182292744145, + 4.209734155810312, + 4.24115008234621, + 4.272566008882108, + 4.3039819354180056, + 4.335397861953903, + 4.366813788489801, + 4.398229715025699, + 4.429645641561597, + 4.461061568097494, + 4.492477494633392, + 4.52389342116929, + 4.555309347705188, + 4.586725274241085, + 4.618141200776983, + 4.649557127312881, + 4.680973053848779, + 4.712388980384676, + 4.743804906920574, + 4.775220833456472, + 4.80663675999237, + 4.838052686528267, + 4.869468613064165, + 4.900884539600063, + 4.932300466135961, + 4.963716392671858, + 4.995132319207756, + 5.026548245743654, + 5.057964172279552, + 5.089380098815449, + 5.120796025351347, + 5.152211951887245, + 5.183627878423143, + 5.21504380495904, + 5.246459731494938, + 5.277875658030836, + 5.309291584566734, + 5.340707511102631, + 5.372123437638529, + 5.403539364174427, + 5.434955290710325, + 5.466371217246222, + 5.49778714378212, + 5.529203070318018, + 5.560618996853916, + 5.5920349233898135, + 5.623450849925711, + 5.654866776461609, + 5.686282702997507, + 5.7176986295334045, + 5.749114556069302, + 5.7805304826052, + 5.811946409141098, + 5.8433623356769955, + 5.874778262212893, + 5.906194188748791, + 5.937610115284689, + 5.9690260418205865, + 6.000441968356484, + 6.031857894892382, + 6.06327382142828, + 6.0946897479641775, + 6.126105674500075, + 6.157521601035973, + 6.188937527571871, + 6.2203534541077685, + 6.251769380643666, + 6.283185307179564 + ], + "marker": { + "color": "#85144b" + }, + "subplot": "polar4" + }, + { + "type": "scatterpolar", + "mode": "markers", + "thetaunit": "radians", + "r": [ + -7, + -6.677171657295865, + -5.8602297267763, + -4.783801847887817, + -3.5874515326384593, + -2.3390950225962595, + -1.0735269436884014, + 0.18920519215874165, + 1.4360601879488435, + 2.6576160452466553, + 3.846483811157743, + 4.996514127794006, + 6.102377312297609, + 7.159330851891905, + 8.163086228628476, + 9.109730904238052, + 9.995682152642399, + 10.817659867047684, + 11.572670943194037, + 12.25800083299363, + 12.871209560461125, + 13.410130487594078, + 13.872870719986338, + 14.257812416539497, + 14.56361450710342, + 14.789214479225297, + 14.933830001678452, + 14.996960227048515, + 14.978386670256494, + 14.878173602246008, + 14.696667933527127, + 14.434498594947609, + 14.092575456547515, + 13.672087863382735, + 13.174502914343229, + 12.601563672528265, + 11.955287583029516, + 11.237965500647096, + 10.45216191984316, + 9.600717291725175, + 8.686753776042433, + 7.713686530256631, + 6.685243901789182, + 5.605502076697052, + 4.478943657079398, + 3.3105569410231084, + 2.106006890508896, + 0.8719378300752894, + -0.38346929739190116, + -1.6494181255953269, + -2.9102767589706247, + -4.140684742281467, + -5.294089380351261, + -6.276056828377587, + -6.900360078316798, + -6.93045552311237, + -6.3512713745354, + -5.392446982236445, + -4.249682677387055, + -3.023887459696285, + -1.764542166186665, + -0.4983108255887885, + 0.7585525964717466, + 1.9949289127341512, + 3.2024551195254833, + 4.374375518263381, + 5.504951297995955, + 6.5891411378116835, + 7.622421362455494, + 8.600681721434391, + 9.520163953508973, + 10.377425432664612, + 11.169317934266157, + 11.89297570027832, + 12.545809285224603, + 13.12550299140446, + 13.630014491243095, + 14.057575718262928, + 14.406694412794486, + 14.676155905710921, + 14.865024854723394, + 14.972646737936063, + 14.99864897369961, + 14.942941584047475, + 14.805717357650007, + 14.587451501958043, + 14.288900806813285, + 13.911102376857627, + 13.455372031599552, + 12.923302525209174, + 12.316761810488643, + 11.637891674409182, + 10.88910722427482, + 10.073097933803993, + 9.192831317670752, + 8.251560879081833, + 7.252840925046961, + 6.200552459146959, + 5.098947199758895, + 3.9527219453586024, + 2.7671453348038746, + 1.5482785951237918, + 0.3033728154409481, + -0.9583838181144966, + -2.224380548066125, + -3.475431772907479, + -4.67861416203452, + -5.770266936574653, + -6.619368107122337, + -6.997186679891282, + -6.730863076197168, + -5.948131625673865, + -4.888045582474995, + -3.699062100915378, + -2.4536764616552933, + -1.188697946883904, + 0.07490402544249086, + 1.3236313731451403, + 2.547815790529782, + 3.7399241622284087, + 4.893715645617382, + 6.0037976481590185, + 7.0653808704534065, + 8.074139365293558, + 9.026128686551374, + 9.917737469189777, + 10.745658876663125, + 11.506874147110722, + 12.198643626284845, + 12.818502460058085, + 13.364259163239698, + 13.833995910975325, + 14.226069789660574, + 14.53911449342608, + 14.772042115531686, + 14.924044794200249, + 14.994596049355305, + 14.98345170277986, + 14.890650317427195, + 14.716513127523012, + 14.461643463878833, + 14.126925712100608, + 13.713523878821988, + 13.222879887178173, + 12.656711783544559, + 12.01701212209868, + 11.30604691605108, + 10.526355727102729, + 9.680753745578347, + 8.772337157419077, + 7.804493814765269, + 6.78092243138655, + 5.7056656021300896, + 4.583165656484351, + 3.4183592443732316, + 2.21683990299392, + 0.9851450221385711, + -0.26871798036271954, + -1.5342581395038284, + -2.796427574960201, + -4.031080579695696, + -5.194380496024298, + -6.197957552108722, + -6.865172889158288, + -6.9553048872586745, + -6.423410689400473, + -5.489353768436682, + -4.358026951631645, + -3.1372350654797163, + -1.8796158394079452, + -0.6132330511005932, + 0.6449962649265899, + 1.8836114524659138, + 3.094058363254483, + 4.2694652237952155, + 5.4040168173241145, + 6.492617354524352, + 7.530701246781785, + 8.514123682916576, + 9.4390961939271, + 10.302148513580132, + 11.100106256040139, + 11.830078306934823, + 12.4894502495081, + 13.075881540649771, + 13.587304978075252, + 14.02192750498196, + 14.378231715853918, + 14.654977631973974, + 14.851204451235947, + 14.966232070006594, + 14.999662241009936, + 14.951379280610162, + 14.821550278179114, + 14.610624794253209, + 14.319334066746727, + 13.948689779165878, + 13.499982485514437, + 12.974779838467526, + 12.374924837626374, + 11.702534414211193 + ], + "theta": [ + 0, + 0.031415926535897934, + 0.06283185307179587, + 0.0942477796076938, + 0.12566370614359174, + 0.15707963267948966, + 0.18849555921538758, + 0.2199114857512855, + 0.2513274122871834, + 0.28274333882308134, + 0.31415926535897926, + 0.3455751918948772, + 0.3769911184307751, + 0.408407044966673, + 0.43982297150257094, + 0.47123889803846886, + 0.5026548245743668, + 0.5340707511102648, + 0.5654866776461628, + 0.5969026041820608, + 0.6283185307179587, + 0.6597344572538567, + 0.6911503837897547, + 0.7225663103256527, + 0.7539822368615506, + 0.7853981633974486, + 0.8168140899333466, + 0.8482300164692446, + 0.8796459430051425, + 0.9110618695410405, + 0.9424777960769385, + 0.9738937226128365, + 1.0053096491487343, + 1.0367255756846323, + 1.0681415022205303, + 1.0995574287564283, + 1.1309733552923262, + 1.1623892818282242, + 1.1938052083641222, + 1.2252211349000202, + 1.2566370614359181, + 1.288052987971816, + 1.319468914507714, + 1.350884841043612, + 1.38230076757951, + 1.413716694115408, + 1.445132620651306, + 1.476548547187204, + 1.507964473723102, + 1.539380400259, + 1.570796326794898, + 1.6022122533307959, + 1.6336281798666938, + 1.6650441064025918, + 1.6964600329384898, + 1.7278759594743878, + 1.7592918860102857, + 1.7907078125461837, + 1.8221237390820817, + 1.8535396656179797, + 1.8849555921538776, + 1.9163715186897756, + 1.9477874452256736, + 1.9792033717615716, + 2.0106192982974695, + 2.0420352248333673, + 2.073451151369265, + 2.104867077905163, + 2.1362830044410606, + 2.1676989309769583, + 2.199114857512856, + 2.230530784048754, + 2.2619467105846516, + 2.2933626371205493, + 2.324778563656447, + 2.356194490192345, + 2.3876104167282426, + 2.4190263432641403, + 2.450442269800038, + 2.481858196335936, + 2.5132741228718336, + 2.5446900494077314, + 2.576105975943629, + 2.607521902479527, + 2.6389378290154246, + 2.6703537555513224, + 2.70176968208722, + 2.733185608623118, + 2.7646015351590156, + 2.7960174616949134, + 2.827433388230811, + 2.858849314766709, + 2.8902652413026066, + 2.9216811678385044, + 2.953097094374402, + 2.9845130209103, + 3.0159289474461977, + 3.0473448739820954, + 3.078760800517993, + 3.110176727053891, + 3.1415926535897887, + 3.1730085801256864, + 3.204424506661584, + 3.235840433197482, + 3.2672563597333797, + 3.2986722862692774, + 3.330088212805175, + 3.361504139341073, + 3.3929200658769707, + 3.4243359924128685, + 3.455751918948766, + 3.487167845484664, + 3.5185837720205617, + 3.5499996985564595, + 3.5814156250923572, + 3.612831551628255, + 3.6442474781641527, + 3.6756634047000505, + 3.7070793312359482, + 3.738495257771846, + 3.7699111843077437, + 3.8013271108436415, + 3.8327430373795393, + 3.864158963915437, + 3.8955748904513348, + 3.9269908169872325, + 3.9584067435231303, + 3.989822670059028, + 4.021238596594926, + 4.0526545231308235, + 4.084070449666721, + 4.115486376202619, + 4.146902302738517, + 4.1783182292744145, + 4.209734155810312, + 4.24115008234621, + 4.272566008882108, + 4.3039819354180056, + 4.335397861953903, + 4.366813788489801, + 4.398229715025699, + 4.429645641561597, + 4.461061568097494, + 4.492477494633392, + 4.52389342116929, + 4.555309347705188, + 4.586725274241085, + 4.618141200776983, + 4.649557127312881, + 4.680973053848779, + 4.712388980384676, + 4.743804906920574, + 4.775220833456472, + 4.80663675999237, + 4.838052686528267, + 4.869468613064165, + 4.900884539600063, + 4.932300466135961, + 4.963716392671858, + 4.995132319207756, + 5.026548245743654, + 5.057964172279552, + 5.089380098815449, + 5.120796025351347, + 5.152211951887245, + 5.183627878423143, + 5.21504380495904, + 5.246459731494938, + 5.277875658030836, + 5.309291584566734, + 5.340707511102631, + 5.372123437638529, + 5.403539364174427, + 5.434955290710325, + 5.466371217246222, + 5.49778714378212, + 5.529203070318018, + 5.560618996853916, + 5.5920349233898135, + 5.623450849925711, + 5.654866776461609, + 5.686282702997507, + 5.7176986295334045, + 5.749114556069302, + 5.7805304826052, + 5.811946409141098, + 5.8433623356769955, + 5.874778262212893, + 5.906194188748791, + 5.937610115284689, + 5.9690260418205865, + 6.000441968356484, + 6.031857894892382, + 6.06327382142828, + 6.0946897479641775, + 6.126105674500075, + 6.157521601035973, + 6.188937527571871, + 6.2203534541077685, + 6.251769380643666, + 6.283185307179564 + ], + "marker": { + "color": "#85144b" + }, + "subplot": "polar5" + }, + { + "type": "scatterpolar", + "mode": "markers", + "thetaunit": "radians", + "r": [ + -7, + -6.677171657295865, + -5.8602297267763, + -4.783801847887817, + -3.5874515326384593, + -2.3390950225962595, + -1.0735269436884014, + 0.18920519215874165, + 1.4360601879488435, + 2.6576160452466553, + 3.846483811157743, + 4.996514127794006, + 6.102377312297609, + 7.159330851891905, + 8.163086228628476, + 9.109730904238052, + 9.995682152642399, + 10.817659867047684, + 11.572670943194037, + 12.25800083299363, + 12.871209560461125, + 13.410130487594078, + 13.872870719986338, + 14.257812416539497, + 14.56361450710342, + 14.789214479225297, + 14.933830001678452, + 14.996960227048515, + 14.978386670256494, + 14.878173602246008, + 14.696667933527127, + 14.434498594947609, + 14.092575456547515, + 13.672087863382735, + 13.174502914343229, + 12.601563672528265, + 11.955287583029516, + 11.237965500647096, + 10.45216191984316, + 9.600717291725175, + 8.686753776042433, + 7.713686530256631, + 6.685243901789182, + 5.605502076697052, + 4.478943657079398, + 3.3105569410231084, + 2.106006890508896, + 0.8719378300752894, + -0.38346929739190116, + -1.6494181255953269, + -2.9102767589706247, + -4.140684742281467, + -5.294089380351261, + -6.276056828377587, + -6.900360078316798, + -6.93045552311237, + -6.3512713745354, + -5.392446982236445, + -4.249682677387055, + -3.023887459696285, + -1.764542166186665, + -0.4983108255887885, + 0.7585525964717466, + 1.9949289127341512, + 3.2024551195254833, + 4.374375518263381, + 5.504951297995955, + 6.5891411378116835, + 7.622421362455494, + 8.600681721434391, + 9.520163953508973, + 10.377425432664612, + 11.169317934266157, + 11.89297570027832, + 12.545809285224603, + 13.12550299140446, + 13.630014491243095, + 14.057575718262928, + 14.406694412794486, + 14.676155905710921, + 14.865024854723394, + 14.972646737936063, + 14.99864897369961, + 14.942941584047475, + 14.805717357650007, + 14.587451501958043, + 14.288900806813285, + 13.911102376857627, + 13.455372031599552, + 12.923302525209174, + 12.316761810488643, + 11.637891674409182, + 10.88910722427482, + 10.073097933803993, + 9.192831317670752, + 8.251560879081833, + 7.252840925046961, + 6.200552459146959, + 5.098947199758895, + 3.9527219453586024, + 2.7671453348038746, + 1.5482785951237918, + 0.3033728154409481, + -0.9583838181144966, + -2.224380548066125, + -3.475431772907479, + -4.67861416203452, + -5.770266936574653, + -6.619368107122337, + -6.997186679891282, + -6.730863076197168, + -5.948131625673865, + -4.888045582474995, + -3.699062100915378, + -2.4536764616552933, + -1.188697946883904, + 0.07490402544249086, + 1.3236313731451403, + 2.547815790529782, + 3.7399241622284087, + 4.893715645617382, + 6.0037976481590185, + 7.0653808704534065, + 8.074139365293558, + 9.026128686551374, + 9.917737469189777, + 10.745658876663125, + 11.506874147110722, + 12.198643626284845, + 12.818502460058085, + 13.364259163239698, + 13.833995910975325, + 14.226069789660574, + 14.53911449342608, + 14.772042115531686, + 14.924044794200249, + 14.994596049355305, + 14.98345170277986, + 14.890650317427195, + 14.716513127523012, + 14.461643463878833, + 14.126925712100608, + 13.713523878821988, + 13.222879887178173, + 12.656711783544559, + 12.01701212209868, + 11.30604691605108, + 10.526355727102729, + 9.680753745578347, + 8.772337157419077, + 7.804493814765269, + 6.78092243138655, + 5.7056656021300896, + 4.583165656484351, + 3.4183592443732316, + 2.21683990299392, + 0.9851450221385711, + -0.26871798036271954, + -1.5342581395038284, + -2.796427574960201, + -4.031080579695696, + -5.194380496024298, + -6.197957552108722, + -6.865172889158288, + -6.9553048872586745, + -6.423410689400473, + -5.489353768436682, + -4.358026951631645, + -3.1372350654797163, + -1.8796158394079452, + -0.6132330511005932, + 0.6449962649265899, + 1.8836114524659138, + 3.094058363254483, + 4.2694652237952155, + 5.4040168173241145, + 6.492617354524352, + 7.530701246781785, + 8.514123682916576, + 9.4390961939271, + 10.302148513580132, + 11.100106256040139, + 11.830078306934823, + 12.4894502495081, + 13.075881540649771, + 13.587304978075252, + 14.02192750498196, + 14.378231715853918, + 14.654977631973974, + 14.851204451235947, + 14.966232070006594, + 14.999662241009936, + 14.951379280610162, + 14.821550278179114, + 14.610624794253209, + 14.319334066746727, + 13.948689779165878, + 13.499982485514437, + 12.974779838467526, + 12.374924837626374, + 11.702534414211193 + ], + "theta": [ + 0, + 0.031415926535897934, + 0.06283185307179587, + 0.0942477796076938, + 0.12566370614359174, + 0.15707963267948966, + 0.18849555921538758, + 0.2199114857512855, + 0.2513274122871834, + 0.28274333882308134, + 0.31415926535897926, + 0.3455751918948772, + 0.3769911184307751, + 0.408407044966673, + 0.43982297150257094, + 0.47123889803846886, + 0.5026548245743668, + 0.5340707511102648, + 0.5654866776461628, + 0.5969026041820608, + 0.6283185307179587, + 0.6597344572538567, + 0.6911503837897547, + 0.7225663103256527, + 0.7539822368615506, + 0.7853981633974486, + 0.8168140899333466, + 0.8482300164692446, + 0.8796459430051425, + 0.9110618695410405, + 0.9424777960769385, + 0.9738937226128365, + 1.0053096491487343, + 1.0367255756846323, + 1.0681415022205303, + 1.0995574287564283, + 1.1309733552923262, + 1.1623892818282242, + 1.1938052083641222, + 1.2252211349000202, + 1.2566370614359181, + 1.288052987971816, + 1.319468914507714, + 1.350884841043612, + 1.38230076757951, + 1.413716694115408, + 1.445132620651306, + 1.476548547187204, + 1.507964473723102, + 1.539380400259, + 1.570796326794898, + 1.6022122533307959, + 1.6336281798666938, + 1.6650441064025918, + 1.6964600329384898, + 1.7278759594743878, + 1.7592918860102857, + 1.7907078125461837, + 1.8221237390820817, + 1.8535396656179797, + 1.8849555921538776, + 1.9163715186897756, + 1.9477874452256736, + 1.9792033717615716, + 2.0106192982974695, + 2.0420352248333673, + 2.073451151369265, + 2.104867077905163, + 2.1362830044410606, + 2.1676989309769583, + 2.199114857512856, + 2.230530784048754, + 2.2619467105846516, + 2.2933626371205493, + 2.324778563656447, + 2.356194490192345, + 2.3876104167282426, + 2.4190263432641403, + 2.450442269800038, + 2.481858196335936, + 2.5132741228718336, + 2.5446900494077314, + 2.576105975943629, + 2.607521902479527, + 2.6389378290154246, + 2.6703537555513224, + 2.70176968208722, + 2.733185608623118, + 2.7646015351590156, + 2.7960174616949134, + 2.827433388230811, + 2.858849314766709, + 2.8902652413026066, + 2.9216811678385044, + 2.953097094374402, + 2.9845130209103, + 3.0159289474461977, + 3.0473448739820954, + 3.078760800517993, + 3.110176727053891, + 3.1415926535897887, + 3.1730085801256864, + 3.204424506661584, + 3.235840433197482, + 3.2672563597333797, + 3.2986722862692774, + 3.330088212805175, + 3.361504139341073, + 3.3929200658769707, + 3.4243359924128685, + 3.455751918948766, + 3.487167845484664, + 3.5185837720205617, + 3.5499996985564595, + 3.5814156250923572, + 3.612831551628255, + 3.6442474781641527, + 3.6756634047000505, + 3.7070793312359482, + 3.738495257771846, + 3.7699111843077437, + 3.8013271108436415, + 3.8327430373795393, + 3.864158963915437, + 3.8955748904513348, + 3.9269908169872325, + 3.9584067435231303, + 3.989822670059028, + 4.021238596594926, + 4.0526545231308235, + 4.084070449666721, + 4.115486376202619, + 4.146902302738517, + 4.1783182292744145, + 4.209734155810312, + 4.24115008234621, + 4.272566008882108, + 4.3039819354180056, + 4.335397861953903, + 4.366813788489801, + 4.398229715025699, + 4.429645641561597, + 4.461061568097494, + 4.492477494633392, + 4.52389342116929, + 4.555309347705188, + 4.586725274241085, + 4.618141200776983, + 4.649557127312881, + 4.680973053848779, + 4.712388980384676, + 4.743804906920574, + 4.775220833456472, + 4.80663675999237, + 4.838052686528267, + 4.869468613064165, + 4.900884539600063, + 4.932300466135961, + 4.963716392671858, + 4.995132319207756, + 5.026548245743654, + 5.057964172279552, + 5.089380098815449, + 5.120796025351347, + 5.152211951887245, + 5.183627878423143, + 5.21504380495904, + 5.246459731494938, + 5.277875658030836, + 5.309291584566734, + 5.340707511102631, + 5.372123437638529, + 5.403539364174427, + 5.434955290710325, + 5.466371217246222, + 5.49778714378212, + 5.529203070318018, + 5.560618996853916, + 5.5920349233898135, + 5.623450849925711, + 5.654866776461609, + 5.686282702997507, + 5.7176986295334045, + 5.749114556069302, + 5.7805304826052, + 5.811946409141098, + 5.8433623356769955, + 5.874778262212893, + 5.906194188748791, + 5.937610115284689, + 5.9690260418205865, + 6.000441968356484, + 6.031857894892382, + 6.06327382142828, + 6.0946897479641775, + 6.126105674500075, + 6.157521601035973, + 6.188937527571871, + 6.2203534541077685, + 6.251769380643666, + 6.283185307179564 + ], + "marker": { + "color": "#85144b" + }, + "subplot": "polar6" + }, + { + "type": "scatterpolar", + "mode": "markers", + "thetaunit": "radians", + "r": [ + -7, + -6.677171657295865, + -5.8602297267763, + -4.783801847887817, + -3.5874515326384593, + -2.3390950225962595, + -1.0735269436884014, + 0.18920519215874165, + 1.4360601879488435, + 2.6576160452466553, + 3.846483811157743, + 4.996514127794006, + 6.102377312297609, + 7.159330851891905, + 8.163086228628476, + 9.109730904238052, + 9.995682152642399, + 10.817659867047684, + 11.572670943194037, + 12.25800083299363, + 12.871209560461125, + 13.410130487594078, + 13.872870719986338, + 14.257812416539497, + 14.56361450710342, + 14.789214479225297, + 14.933830001678452, + 14.996960227048515, + 14.978386670256494, + 14.878173602246008, + 14.696667933527127, + 14.434498594947609, + 14.092575456547515, + 13.672087863382735, + 13.174502914343229, + 12.601563672528265, + 11.955287583029516, + 11.237965500647096, + 10.45216191984316, + 9.600717291725175, + 8.686753776042433, + 7.713686530256631, + 6.685243901789182, + 5.605502076697052, + 4.478943657079398, + 3.3105569410231084, + 2.106006890508896, + 0.8719378300752894, + -0.38346929739190116, + -1.6494181255953269, + -2.9102767589706247, + -4.140684742281467, + -5.294089380351261, + -6.276056828377587, + -6.900360078316798, + -6.93045552311237, + -6.3512713745354, + -5.392446982236445, + -4.249682677387055, + -3.023887459696285, + -1.764542166186665, + -0.4983108255887885, + 0.7585525964717466, + 1.9949289127341512, + 3.2024551195254833, + 4.374375518263381, + 5.504951297995955, + 6.5891411378116835, + 7.622421362455494, + 8.600681721434391, + 9.520163953508973, + 10.377425432664612, + 11.169317934266157, + 11.89297570027832, + 12.545809285224603, + 13.12550299140446, + 13.630014491243095, + 14.057575718262928, + 14.406694412794486, + 14.676155905710921, + 14.865024854723394, + 14.972646737936063, + 14.99864897369961, + 14.942941584047475, + 14.805717357650007, + 14.587451501958043, + 14.288900806813285, + 13.911102376857627, + 13.455372031599552, + 12.923302525209174, + 12.316761810488643, + 11.637891674409182, + 10.88910722427482, + 10.073097933803993, + 9.192831317670752, + 8.251560879081833, + 7.252840925046961, + 6.200552459146959, + 5.098947199758895, + 3.9527219453586024, + 2.7671453348038746, + 1.5482785951237918, + 0.3033728154409481, + -0.9583838181144966, + -2.224380548066125, + -3.475431772907479, + -4.67861416203452, + -5.770266936574653, + -6.619368107122337, + -6.997186679891282, + -6.730863076197168, + -5.948131625673865, + -4.888045582474995, + -3.699062100915378, + -2.4536764616552933, + -1.188697946883904, + 0.07490402544249086, + 1.3236313731451403, + 2.547815790529782, + 3.7399241622284087, + 4.893715645617382, + 6.0037976481590185, + 7.0653808704534065, + 8.074139365293558, + 9.026128686551374, + 9.917737469189777, + 10.745658876663125, + 11.506874147110722, + 12.198643626284845, + 12.818502460058085, + 13.364259163239698, + 13.833995910975325, + 14.226069789660574, + 14.53911449342608, + 14.772042115531686, + 14.924044794200249, + 14.994596049355305, + 14.98345170277986, + 14.890650317427195, + 14.716513127523012, + 14.461643463878833, + 14.126925712100608, + 13.713523878821988, + 13.222879887178173, + 12.656711783544559, + 12.01701212209868, + 11.30604691605108, + 10.526355727102729, + 9.680753745578347, + 8.772337157419077, + 7.804493814765269, + 6.78092243138655, + 5.7056656021300896, + 4.583165656484351, + 3.4183592443732316, + 2.21683990299392, + 0.9851450221385711, + -0.26871798036271954, + -1.5342581395038284, + -2.796427574960201, + -4.031080579695696, + -5.194380496024298, + -6.197957552108722, + -6.865172889158288, + -6.9553048872586745, + -6.423410689400473, + -5.489353768436682, + -4.358026951631645, + -3.1372350654797163, + -1.8796158394079452, + -0.6132330511005932, + 0.6449962649265899, + 1.8836114524659138, + 3.094058363254483, + 4.2694652237952155, + 5.4040168173241145, + 6.492617354524352, + 7.530701246781785, + 8.514123682916576, + 9.4390961939271, + 10.302148513580132, + 11.100106256040139, + 11.830078306934823, + 12.4894502495081, + 13.075881540649771, + 13.587304978075252, + 14.02192750498196, + 14.378231715853918, + 14.654977631973974, + 14.851204451235947, + 14.966232070006594, + 14.999662241009936, + 14.951379280610162, + 14.821550278179114, + 14.610624794253209, + 14.319334066746727, + 13.948689779165878, + 13.499982485514437, + 12.974779838467526, + 12.374924837626374, + 11.702534414211193 + ], + "theta": [ + 0, + 0.031415926535897934, + 0.06283185307179587, + 0.0942477796076938, + 0.12566370614359174, + 0.15707963267948966, + 0.18849555921538758, + 0.2199114857512855, + 0.2513274122871834, + 0.28274333882308134, + 0.31415926535897926, + 0.3455751918948772, + 0.3769911184307751, + 0.408407044966673, + 0.43982297150257094, + 0.47123889803846886, + 0.5026548245743668, + 0.5340707511102648, + 0.5654866776461628, + 0.5969026041820608, + 0.6283185307179587, + 0.6597344572538567, + 0.6911503837897547, + 0.7225663103256527, + 0.7539822368615506, + 0.7853981633974486, + 0.8168140899333466, + 0.8482300164692446, + 0.8796459430051425, + 0.9110618695410405, + 0.9424777960769385, + 0.9738937226128365, + 1.0053096491487343, + 1.0367255756846323, + 1.0681415022205303, + 1.0995574287564283, + 1.1309733552923262, + 1.1623892818282242, + 1.1938052083641222, + 1.2252211349000202, + 1.2566370614359181, + 1.288052987971816, + 1.319468914507714, + 1.350884841043612, + 1.38230076757951, + 1.413716694115408, + 1.445132620651306, + 1.476548547187204, + 1.507964473723102, + 1.539380400259, + 1.570796326794898, + 1.6022122533307959, + 1.6336281798666938, + 1.6650441064025918, + 1.6964600329384898, + 1.7278759594743878, + 1.7592918860102857, + 1.7907078125461837, + 1.8221237390820817, + 1.8535396656179797, + 1.8849555921538776, + 1.9163715186897756, + 1.9477874452256736, + 1.9792033717615716, + 2.0106192982974695, + 2.0420352248333673, + 2.073451151369265, + 2.104867077905163, + 2.1362830044410606, + 2.1676989309769583, + 2.199114857512856, + 2.230530784048754, + 2.2619467105846516, + 2.2933626371205493, + 2.324778563656447, + 2.356194490192345, + 2.3876104167282426, + 2.4190263432641403, + 2.450442269800038, + 2.481858196335936, + 2.5132741228718336, + 2.5446900494077314, + 2.576105975943629, + 2.607521902479527, + 2.6389378290154246, + 2.6703537555513224, + 2.70176968208722, + 2.733185608623118, + 2.7646015351590156, + 2.7960174616949134, + 2.827433388230811, + 2.858849314766709, + 2.8902652413026066, + 2.9216811678385044, + 2.953097094374402, + 2.9845130209103, + 3.0159289474461977, + 3.0473448739820954, + 3.078760800517993, + 3.110176727053891, + 3.1415926535897887, + 3.1730085801256864, + 3.204424506661584, + 3.235840433197482, + 3.2672563597333797, + 3.2986722862692774, + 3.330088212805175, + 3.361504139341073, + 3.3929200658769707, + 3.4243359924128685, + 3.455751918948766, + 3.487167845484664, + 3.5185837720205617, + 3.5499996985564595, + 3.5814156250923572, + 3.612831551628255, + 3.6442474781641527, + 3.6756634047000505, + 3.7070793312359482, + 3.738495257771846, + 3.7699111843077437, + 3.8013271108436415, + 3.8327430373795393, + 3.864158963915437, + 3.8955748904513348, + 3.9269908169872325, + 3.9584067435231303, + 3.989822670059028, + 4.021238596594926, + 4.0526545231308235, + 4.084070449666721, + 4.115486376202619, + 4.146902302738517, + 4.1783182292744145, + 4.209734155810312, + 4.24115008234621, + 4.272566008882108, + 4.3039819354180056, + 4.335397861953903, + 4.366813788489801, + 4.398229715025699, + 4.429645641561597, + 4.461061568097494, + 4.492477494633392, + 4.52389342116929, + 4.555309347705188, + 4.586725274241085, + 4.618141200776983, + 4.649557127312881, + 4.680973053848779, + 4.712388980384676, + 4.743804906920574, + 4.775220833456472, + 4.80663675999237, + 4.838052686528267, + 4.869468613064165, + 4.900884539600063, + 4.932300466135961, + 4.963716392671858, + 4.995132319207756, + 5.026548245743654, + 5.057964172279552, + 5.089380098815449, + 5.120796025351347, + 5.152211951887245, + 5.183627878423143, + 5.21504380495904, + 5.246459731494938, + 5.277875658030836, + 5.309291584566734, + 5.340707511102631, + 5.372123437638529, + 5.403539364174427, + 5.434955290710325, + 5.466371217246222, + 5.49778714378212, + 5.529203070318018, + 5.560618996853916, + 5.5920349233898135, + 5.623450849925711, + 5.654866776461609, + 5.686282702997507, + 5.7176986295334045, + 5.749114556069302, + 5.7805304826052, + 5.811946409141098, + 5.8433623356769955, + 5.874778262212893, + 5.906194188748791, + 5.937610115284689, + 5.9690260418205865, + 6.000441968356484, + 6.031857894892382, + 6.06327382142828, + 6.0946897479641775, + 6.126105674500075, + 6.157521601035973, + 6.188937527571871, + 6.2203534541077685, + 6.251769380643666, + 6.283185307179564 + ], + "marker": { + "color": "#85144b" + }, + "subplot": "polar7" + }, + { + "type": "scatterpolar", + "mode": "markers", + "thetaunit": "radians", + "r": [ + -7, + -6.677171657295865, + -5.8602297267763, + -4.783801847887817, + -3.5874515326384593, + -2.3390950225962595, + -1.0735269436884014, + 0.18920519215874165, + 1.4360601879488435, + 2.6576160452466553, + 3.846483811157743, + 4.996514127794006, + 6.102377312297609, + 7.159330851891905, + 8.163086228628476, + 9.109730904238052, + 9.995682152642399, + 10.817659867047684, + 11.572670943194037, + 12.25800083299363, + 12.871209560461125, + 13.410130487594078, + 13.872870719986338, + 14.257812416539497, + 14.56361450710342, + 14.789214479225297, + 14.933830001678452, + 14.996960227048515, + 14.978386670256494, + 14.878173602246008, + 14.696667933527127, + 14.434498594947609, + 14.092575456547515, + 13.672087863382735, + 13.174502914343229, + 12.601563672528265, + 11.955287583029516, + 11.237965500647096, + 10.45216191984316, + 9.600717291725175, + 8.686753776042433, + 7.713686530256631, + 6.685243901789182, + 5.605502076697052, + 4.478943657079398, + 3.3105569410231084, + 2.106006890508896, + 0.8719378300752894, + -0.38346929739190116, + -1.6494181255953269, + -2.9102767589706247, + -4.140684742281467, + -5.294089380351261, + -6.276056828377587, + -6.900360078316798, + -6.93045552311237, + -6.3512713745354, + -5.392446982236445, + -4.249682677387055, + -3.023887459696285, + -1.764542166186665, + -0.4983108255887885, + 0.7585525964717466, + 1.9949289127341512, + 3.2024551195254833, + 4.374375518263381, + 5.504951297995955, + 6.5891411378116835, + 7.622421362455494, + 8.600681721434391, + 9.520163953508973, + 10.377425432664612, + 11.169317934266157, + 11.89297570027832, + 12.545809285224603, + 13.12550299140446, + 13.630014491243095, + 14.057575718262928, + 14.406694412794486, + 14.676155905710921, + 14.865024854723394, + 14.972646737936063, + 14.99864897369961, + 14.942941584047475, + 14.805717357650007, + 14.587451501958043, + 14.288900806813285, + 13.911102376857627, + 13.455372031599552, + 12.923302525209174, + 12.316761810488643, + 11.637891674409182, + 10.88910722427482, + 10.073097933803993, + 9.192831317670752, + 8.251560879081833, + 7.252840925046961, + 6.200552459146959, + 5.098947199758895, + 3.9527219453586024, + 2.7671453348038746, + 1.5482785951237918, + 0.3033728154409481, + -0.9583838181144966, + -2.224380548066125, + -3.475431772907479, + -4.67861416203452, + -5.770266936574653, + -6.619368107122337, + -6.997186679891282, + -6.730863076197168, + -5.948131625673865, + -4.888045582474995, + -3.699062100915378, + -2.4536764616552933, + -1.188697946883904, + 0.07490402544249086, + 1.3236313731451403, + 2.547815790529782, + 3.7399241622284087, + 4.893715645617382, + 6.0037976481590185, + 7.0653808704534065, + 8.074139365293558, + 9.026128686551374, + 9.917737469189777, + 10.745658876663125, + 11.506874147110722, + 12.198643626284845, + 12.818502460058085, + 13.364259163239698, + 13.833995910975325, + 14.226069789660574, + 14.53911449342608, + 14.772042115531686, + 14.924044794200249, + 14.994596049355305, + 14.98345170277986, + 14.890650317427195, + 14.716513127523012, + 14.461643463878833, + 14.126925712100608, + 13.713523878821988, + 13.222879887178173, + 12.656711783544559, + 12.01701212209868, + 11.30604691605108, + 10.526355727102729, + 9.680753745578347, + 8.772337157419077, + 7.804493814765269, + 6.78092243138655, + 5.7056656021300896, + 4.583165656484351, + 3.4183592443732316, + 2.21683990299392, + 0.9851450221385711, + -0.26871798036271954, + -1.5342581395038284, + -2.796427574960201, + -4.031080579695696, + -5.194380496024298, + -6.197957552108722, + -6.865172889158288, + -6.9553048872586745, + -6.423410689400473, + -5.489353768436682, + -4.358026951631645, + -3.1372350654797163, + -1.8796158394079452, + -0.6132330511005932, + 0.6449962649265899, + 1.8836114524659138, + 3.094058363254483, + 4.2694652237952155, + 5.4040168173241145, + 6.492617354524352, + 7.530701246781785, + 8.514123682916576, + 9.4390961939271, + 10.302148513580132, + 11.100106256040139, + 11.830078306934823, + 12.4894502495081, + 13.075881540649771, + 13.587304978075252, + 14.02192750498196, + 14.378231715853918, + 14.654977631973974, + 14.851204451235947, + 14.966232070006594, + 14.999662241009936, + 14.951379280610162, + 14.821550278179114, + 14.610624794253209, + 14.319334066746727, + 13.948689779165878, + 13.499982485514437, + 12.974779838467526, + 12.374924837626374, + 11.702534414211193 + ], + "theta": [ + 0, + 0.031415926535897934, + 0.06283185307179587, + 0.0942477796076938, + 0.12566370614359174, + 0.15707963267948966, + 0.18849555921538758, + 0.2199114857512855, + 0.2513274122871834, + 0.28274333882308134, + 0.31415926535897926, + 0.3455751918948772, + 0.3769911184307751, + 0.408407044966673, + 0.43982297150257094, + 0.47123889803846886, + 0.5026548245743668, + 0.5340707511102648, + 0.5654866776461628, + 0.5969026041820608, + 0.6283185307179587, + 0.6597344572538567, + 0.6911503837897547, + 0.7225663103256527, + 0.7539822368615506, + 0.7853981633974486, + 0.8168140899333466, + 0.8482300164692446, + 0.8796459430051425, + 0.9110618695410405, + 0.9424777960769385, + 0.9738937226128365, + 1.0053096491487343, + 1.0367255756846323, + 1.0681415022205303, + 1.0995574287564283, + 1.1309733552923262, + 1.1623892818282242, + 1.1938052083641222, + 1.2252211349000202, + 1.2566370614359181, + 1.288052987971816, + 1.319468914507714, + 1.350884841043612, + 1.38230076757951, + 1.413716694115408, + 1.445132620651306, + 1.476548547187204, + 1.507964473723102, + 1.539380400259, + 1.570796326794898, + 1.6022122533307959, + 1.6336281798666938, + 1.6650441064025918, + 1.6964600329384898, + 1.7278759594743878, + 1.7592918860102857, + 1.7907078125461837, + 1.8221237390820817, + 1.8535396656179797, + 1.8849555921538776, + 1.9163715186897756, + 1.9477874452256736, + 1.9792033717615716, + 2.0106192982974695, + 2.0420352248333673, + 2.073451151369265, + 2.104867077905163, + 2.1362830044410606, + 2.1676989309769583, + 2.199114857512856, + 2.230530784048754, + 2.2619467105846516, + 2.2933626371205493, + 2.324778563656447, + 2.356194490192345, + 2.3876104167282426, + 2.4190263432641403, + 2.450442269800038, + 2.481858196335936, + 2.5132741228718336, + 2.5446900494077314, + 2.576105975943629, + 2.607521902479527, + 2.6389378290154246, + 2.6703537555513224, + 2.70176968208722, + 2.733185608623118, + 2.7646015351590156, + 2.7960174616949134, + 2.827433388230811, + 2.858849314766709, + 2.8902652413026066, + 2.9216811678385044, + 2.953097094374402, + 2.9845130209103, + 3.0159289474461977, + 3.0473448739820954, + 3.078760800517993, + 3.110176727053891, + 3.1415926535897887, + 3.1730085801256864, + 3.204424506661584, + 3.235840433197482, + 3.2672563597333797, + 3.2986722862692774, + 3.330088212805175, + 3.361504139341073, + 3.3929200658769707, + 3.4243359924128685, + 3.455751918948766, + 3.487167845484664, + 3.5185837720205617, + 3.5499996985564595, + 3.5814156250923572, + 3.612831551628255, + 3.6442474781641527, + 3.6756634047000505, + 3.7070793312359482, + 3.738495257771846, + 3.7699111843077437, + 3.8013271108436415, + 3.8327430373795393, + 3.864158963915437, + 3.8955748904513348, + 3.9269908169872325, + 3.9584067435231303, + 3.989822670059028, + 4.021238596594926, + 4.0526545231308235, + 4.084070449666721, + 4.115486376202619, + 4.146902302738517, + 4.1783182292744145, + 4.209734155810312, + 4.24115008234621, + 4.272566008882108, + 4.3039819354180056, + 4.335397861953903, + 4.366813788489801, + 4.398229715025699, + 4.429645641561597, + 4.461061568097494, + 4.492477494633392, + 4.52389342116929, + 4.555309347705188, + 4.586725274241085, + 4.618141200776983, + 4.649557127312881, + 4.680973053848779, + 4.712388980384676, + 4.743804906920574, + 4.775220833456472, + 4.80663675999237, + 4.838052686528267, + 4.869468613064165, + 4.900884539600063, + 4.932300466135961, + 4.963716392671858, + 4.995132319207756, + 5.026548245743654, + 5.057964172279552, + 5.089380098815449, + 5.120796025351347, + 5.152211951887245, + 5.183627878423143, + 5.21504380495904, + 5.246459731494938, + 5.277875658030836, + 5.309291584566734, + 5.340707511102631, + 5.372123437638529, + 5.403539364174427, + 5.434955290710325, + 5.466371217246222, + 5.49778714378212, + 5.529203070318018, + 5.560618996853916, + 5.5920349233898135, + 5.623450849925711, + 5.654866776461609, + 5.686282702997507, + 5.7176986295334045, + 5.749114556069302, + 5.7805304826052, + 5.811946409141098, + 5.8433623356769955, + 5.874778262212893, + 5.906194188748791, + 5.937610115284689, + 5.9690260418205865, + 6.000441968356484, + 6.031857894892382, + 6.06327382142828, + 6.0946897479641775, + 6.126105674500075, + 6.157521601035973, + 6.188937527571871, + 6.2203534541077685, + 6.251769380643666, + 6.283185307179564 + ], + "marker": { + "color": "#85144b" + }, + "subplot": "polar8" + } + ], + "layout": { + "margin": { + "l": 40, + "r": 40, + "t": 40, + "b": 40 + }, + "width": 600, + "height": 900, + "showlegend": false, + "polar": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0, + 0.45 + ], + "y": [ + 0.775, + 1 + ] + }, + "radialaxis": { + "range": [ + -15, + 30 + ] + }, + "angularaxis": {} + }, + "polar2": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0, + 0.45 + ], + "y": [ + 0.525, + 0.725 + ] + }, + "radialaxis": { + "range": [ + 10, + 20 + ] + }, + "angularaxis": {} + }, + "polar3": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0, + 0.45 + ], + "y": [ + 0.275, + 0.475 + ] + }, + "radialaxis": { + "type": "log", + "range": [ + 0.5, + 1.5 + ] + }, + "angularaxis": {} + }, + "polar4": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0, + 0.45 + ], + "y": [ + 0, + 0.225 + ] + }, + "radialaxis": { + "range": [ + 30, + -10 + ] + }, + "angularaxis": {} + }, + "polar5": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0.55, + 1 + ], + "y": [ + 0.775, + 1 + ] + }, + "radialaxis": { + "rangemode": "reversed" + }, + "angularaxis": {} + }, + "polar6": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0.55, + 1 + ], + "y": [ + 0.525, + 0.725 + ] + }, + "radialaxis": { + "autorange": "reversed" + }, + "angularaxis": {} + }, + "polar7": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0.55, + 1 + ], + "y": [ + 0.275, + 0.475 + ] + }, + "radialaxis": { + "rangemode": "nonnegative" + }, + "angularaxis": {} + }, + "polar8": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0.55, + 1 + ], + "y": [ + 0, + 0.225 + ] + }, + "radialaxis": {}, + "angularaxis": {} + } + } +} diff --git a/test/image/mocks/polar_scatter.json b/test/image/mocks/polar_scatter.json index 4e76a86a1eb..8f8f81ab9ec 100644 --- a/test/image/mocks/polar_scatter.json +++ b/test/image/mocks/polar_scatter.json @@ -1,6 +1,7 @@ { "data": [ { + "type": "scatterpolar", "r": [ 6.80498578527, 3.38959601061, @@ -63,7 +64,7 @@ 7.58301557326, 3.51320214534 ], - "t": [ + "theta": [ -30.3529443619, -25.6114598545, -12.4252274527, @@ -129,15 +130,15 @@ "name": "Trial 1", "marker": { "color": "rgb(27,158,119)", - "size": 110, + "size": 15, "line": { "color": "white" }, "opacity": 0.7 - }, - "type": "scatter" + } }, { + "type": "scatterpolar", "r": [ 3.48804392301, 2.91847857636, @@ -200,7 +201,7 @@ 6.85304926109, 4.14035507494 ], - "t": [ + "theta": [ 14.8066257809, 79.0063403726, 49.0220655413, @@ -266,15 +267,15 @@ "name": "Trial 2", "marker": { "color": "rgb(217,95,2)", - "size": 110, + "size": 20, "line": { "color": "white" }, "opacity": 0.7 - }, - "type": "scatter" + } }, { + "type": "scatterpolar", "r": [ 1.85587083503, 5.28696206204, @@ -337,7 +338,7 @@ 4.60247924389, 5.67005205083 ], - "t": [ + "theta": [ 151.294255181, 147.188025028, 125.282157112, @@ -403,15 +404,15 @@ "name": "Trial 3", "marker": { "color": "rgb(117,112,179)", - "size": 110, + "size": 12, "line": { "color": "white" }, "opacity": 0.7 - }, - "type": "scatter" + } }, { + "type": "scatterpolar", "r": [ 5.37247092432, 7.09635557204, @@ -474,7 +475,7 @@ 2.4370447709, 6.5081863479 ], - "t": [ + "theta": [ -140.203327641, -168.084245433, -166.285141329, @@ -540,15 +541,15 @@ "name": "Trial 4", "marker": { "color": "rgb(231,41,138)", - "size": 110, + "size": 22, "line": { "color": "white" }, "opacity": 0.7 - }, - "type": "scatter" + } }, { + "type": "scatterpolar", "r": [ 7.93755787138, 7.30274649152, @@ -611,7 +612,7 @@ 4.05919058739, 6.12833898429 ], - "t": [ + "theta": [ -101.833785776, -127.478391579, -112.244284997, @@ -677,15 +678,15 @@ "name": "Trial 5", "marker": { "color": "rgb(102,166,30)", - "size": 110, + "size": 19, "line": { "color": "white" }, "opacity": 0.7 - }, - "type": "scatter" + } }, { + "type": "scatterpolar", "r": [ 8.46918052789, 5.82199756737, @@ -748,7 +749,7 @@ 6.50065359862, 4.74864071013 ], - "t": [ + "theta": [ -66.5358363273, -84.5144226769, -63.3397416996, @@ -814,13 +815,12 @@ "name": "Trial 6", "marker": { "color": "rgb(230,171,2)", - "size": 110, + "size": 10, "line": { "color": "white" }, "opacity": 0.7 - }, - "type": "scatter" + } } ], "layout": { @@ -829,9 +829,24 @@ "size": 15 }, "showlegend": false, - "plot_bgcolor": "rgb(223, 223, 223)", - "angularaxis": { - "tickcolor": "rgb(253,253,253)" - } + "polar": { + "bgcolor": "rgb(223, 223, 223)", + "angularaxis": { + "tickwidth": 2, + "linewidth": 3, + "layer": "below traces" + }, + "radialaxis": { + "side": "counterclockwise", + "showline": true, + "linewidth": 2, + "tickwidth": 2, + "gridcolor": "white", + "gridwidth": 2 + } + }, + "paper_bgcolor": "rgb(223, 223, 223)", + "width": 450, + "height": 450 } } diff --git a/test/image/mocks/polar_sector.json b/test/image/mocks/polar_sector.json new file mode 100644 index 00000000000..b6fbce98b5f --- /dev/null +++ b/test/image/mocks/polar_sector.json @@ -0,0 +1,1175 @@ +{ + "data": [ + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar2" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar3" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar4" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar5" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar6" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar7" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar8" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar9" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar10" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar11" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar12" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar13" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar14" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar15" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar16" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar17" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar18" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar19" + }, + { + "type": "scatterpolar", + "r": [ + 1, + 2, + 3, + 4, + 5, + 2 + ], + "theta": [ + 0, + 90, + 180, + 250, + 300, + 400 + ], + "line": { + "color": "#6a3d9a" + }, + "marker": { + "color": "#b15928", + "size": 10 + }, + "subplot": "polar20" + } + ], + "layout": { + "margin": { + "l": 40, + "r": 40, + "t": 40, + "b": 40 + }, + "width": 600, + "height": 800, + "showlegend": false, + "polar": { + "sector": [ + 200, + 400 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0, + 0.225 + ], + "y": [ + 0.81, + 1 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar2": { + "sector": [ + 300, + 420 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0, + 0.225 + ], + "y": [ + 0.6100000000000001, + 0.79 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar3": { + "sector": [ + 240, + 300 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0, + 0.225 + ], + "y": [ + 0.41000000000000003, + 0.5900000000000001 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar4": { + "sector": [ + 45, + 135 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0, + 0.225 + ], + "y": [ + 0.21000000000000002, + 0.39 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar5": { + "sector": [ + 120, + 240 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0, + 0.225 + ], + "y": [ + 0, + 0.19 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar6": { + "sector": [ + 45, + 375 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.275, + 0.475 + ], + "y": [ + 0.81, + 1 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar7": { + "sector": [ + 45, + 80 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.275, + 0.475 + ], + "y": [ + 0.6100000000000001, + 0.79 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar8": { + "sector": [ + 45, + 300 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.275, + 0.475 + ], + "y": [ + 0.41000000000000003, + 0.5900000000000001 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar9": { + "sector": [ + 45, + 360 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.275, + 0.475 + ], + "y": [ + 0.21000000000000002, + 0.39 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar10": { + "sector": [ + 45, + 360 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.275, + 0.475 + ], + "y": [ + 0, + 0.19 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar11": { + "sector": [ + 180, + 360 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.525, + 0.725 + ], + "y": [ + 0.81, + 1 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar12": { + "sector": [ + 0, + 315 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.525, + 0.725 + ], + "y": [ + 0.6100000000000001, + 0.79 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar13": { + "sector": [ + 0, + 225 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.525, + 0.725 + ], + "y": [ + 0.41000000000000003, + 0.5900000000000001 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar14": { + "sector": [ + 0, + 270 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.525, + 0.725 + ], + "y": [ + 0.21000000000000002, + 0.39 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar15": { + "sector": [ + 0, + 270 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.525, + 0.725 + ], + "y": [ + 0, + 0.19 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar16": { + "sector": [ + 0, + 45 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.775, + 1 + ], + "y": [ + 0.81, + 1 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar17": { + "sector": [ + 0, + 90 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.775, + 1 + ], + "y": [ + 0.6100000000000001, + 0.79 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar18": { + "sector": [ + 0, + 135 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.775, + 1 + ], + "y": [ + 0.41000000000000003, + 0.5900000000000001 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar19": { + "sector": [ + 0, + 180 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.775, + 1 + ], + "y": [ + 0.21000000000000002, + 0.39 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + }, + "polar20": { + "sector": [ + -180, + 180 + ], + "bgcolor": "#d3d3d3", + "domain": { + "x": [ + 0.775, + 1 + ], + "y": [ + 0, + 0.19 + ] + }, + "radialaxis": { + "range": [ + 0, + 5 + ], + "tickfont": { + "size": 8 + } + }, + "angularaxis": { + "tickfont": { + "size": 8 + } + } + } + } +} diff --git a/test/image/mocks/polar_subplots.json b/test/image/mocks/polar_subplots.json new file mode 100644 index 00000000000..28df993bda8 --- /dev/null +++ b/test/image/mocks/polar_subplots.json @@ -0,0 +1,65 @@ +{ + "data": [{ + "type": "scatterpolar", + "mode": "markers+lines+text", + "r": [1, 2, 3], + "theta": [0, 45, 180], + "text": ["A0", "B0", "C0"], + "hovertext": ["hover A0", "hover B0", "hover C0"], + "line": {"shape": "spline"}, + "marker": {"symbol": "square", "size": 15}, + "textfont": { + "color": ["red", "green", "blue"], + "size": 20 + }, + "textposition": ["top left", "bottom right", "bottom right"] + }, { + "type": "scatterpolar", + "mode": "markers+lines+text", + "r": [1, 2, 3], + "theta": [-0, -45, -180], + "text": ["A1", "B1", "C1"], + "hovertext": ["hover A1", "hover B1", "hover C1"], + "line": {"shape": "spline"}, + "marker": {"symbol": "square", "size": 15}, + "textfont": { + "color": "green", + "size": [20, 15, 10] + }, + "textposition": "top left", + "subplot": "polar2" + }], + + "layout": { + "showlegend": false, + "polar": { + "domain": { + "x": [0, 0.44] + }, + "radialaxis": { + "showgrid": false, + "title": "1st subplot", + "titlefont": { + "size": 20, + "color": "blue" + }, + "angle": -45 + } + }, + "polar2": { + "domain": { + "x": [0.56, 1] + }, + "radialaxis": { + "range": [0, 4.7], + "showgrid": false, + "title": "2nd subplot", + "titlefont": { + "size": 10, + "color": "orange" + }, + "angle": 45 + } + } + } +} diff --git a/test/image/mocks/polar_ticks.json b/test/image/mocks/polar_ticks.json new file mode 100644 index 00000000000..c23782f3108 --- /dev/null +++ b/test/image/mocks/polar_ticks.json @@ -0,0 +1,1928 @@ +{ + "data": [ + { + "type": "scatterpolar", + "mode": "lines", + "thetaunit": "radians", + "r": [ + 3.5, + 3.5101069780115313, + 3.5399002890543394, + 3.5878528813194026, + 3.6515921856519458, + 3.7281173520733097, + 3.814037602559748, + 3.9057957513598405, + 3.999854737597697, + 4.092839226534218, + 4.181634605928617, + 4.263451031313349, + 4.335861616679521, + 4.3968230543317475, + 4.4446852217199195, + 4.478194509529551, + 4.496494057069119, + 4.499122904935259, + 4.486015230080536, + 4.457500219838926, + 4.414302660387005, + 4.357543852184939, + 4.288741911087211, + 4.209809759447415, + 4.123048054333455, + 4.031128874149274, + 3.9370642287949904, + 3.8441516345302094, + 3.75588775392503, + 3.675841614689789, + 3.607482782010221, + 3.5539684565476826, + 3.5179066723671726, + 3.5011274021787484, + 3.504502988262685, + 3.5278568472685214, + 3.5699822520321516, + 3.628766598845516, + 3.701392176060956, + 3.784572010623282, + 3.8747815451326066, + 3.968459029112283, + 4.0621622417275844, + 4.152681007332425, + 4.237111889749941, + 4.312904011124049, + 4.377884707217135, + 4.430272192767401, + 4.468680559578299, + 4.492120768959218, + 4.5, + 4.492120768959217, + 4.468680559578297, + 4.430272192767397, + 4.377884707217131, + 4.3129040111240435, + 4.237111889749934, + 4.152681007332418, + 4.062162241727577, + 3.9684590291122754, + 3.874781545132599, + 3.784572010623276, + 3.701392176060949, + 3.6287665988455107, + 3.5699822520321476, + 3.527856847268519, + 3.5045029882626846, + 3.501127402178749, + 3.5179066723671735, + 3.553968456547685, + 3.6074827820102233, + 3.6758416146897916, + 3.7558877539250317, + 3.844151634530211, + 3.9370642287949917, + 4.031128874149275, + 4.123048054333455, + 4.209809759447415, + 4.28874191108721, + 4.357543852184937, + 4.414302660387003, + 4.457500219838925, + 4.486015230080535, + 4.499122904935259, + 4.49649405706912, + 4.478194509529552, + 4.444685221719923, + 4.396823054331751, + 4.335861616679527, + 4.263451031313355, + 4.181634605928625, + 4.0928392265342275, + 3.999854737597707, + 3.9057957513598502, + 3.814037602559758, + 3.728117352073319, + 3.651592185651954, + 3.5878528813194097, + 3.5399002890543443, + 3.5101069780115344, + 3.5 + ], + "theta": [ + 0, + 0.06283185307179587, + 0.12566370614359174, + 0.1884955592153876, + 0.25132741228718347, + 0.3141592653589793, + 0.37699111843077515, + 0.439822971502571, + 0.5026548245743668, + 0.5654866776461627, + 0.6283185307179585, + 0.6911503837897544, + 0.7539822368615502, + 0.816814089933346, + 0.8796459430051419, + 0.9424777960769377, + 1.0053096491487337, + 1.0681415022205296, + 1.1309733552923256, + 1.1938052083641215, + 1.2566370614359175, + 1.3194689145077134, + 1.3823007675795094, + 1.4451326206513053, + 1.5079644737231013, + 1.5707963267948972, + 1.6336281798666932, + 1.6964600329384891, + 1.759291886010285, + 1.822123739082081, + 1.884955592153877, + 1.947787445225673, + 2.0106192982974687, + 2.0734511513692646, + 2.1362830044410606, + 2.1991148575128565, + 2.2619467105846525, + 2.3247785636564484, + 2.3876104167282444, + 2.4504422698000403, + 2.5132741228718363, + 2.576105975943632, + 2.638937829015428, + 2.701769682087224, + 2.76460153515902, + 2.827433388230816, + 2.890265241302612, + 2.953097094374408, + 3.015928947446204, + 3.078760800518, + 3.141592653589796, + 3.2044245066615917, + 3.2672563597333877, + 3.3300882128051836, + 3.3929200658769796, + 3.4557519189487755, + 3.5185837720205715, + 3.5814156250923674, + 3.6442474781641634, + 3.7070793312359593, + 3.7699111843077553, + 3.8327430373795512, + 3.895574890451347, + 3.958406743523143, + 4.021238596594939, + 4.084070449666735, + 4.14690230273853, + 4.209734155810326, + 4.272566008882121, + 4.335397861953917, + 4.398229715025712, + 4.461061568097508, + 4.523893421169303, + 4.586725274241099, + 4.649557127312894, + 4.71238898038469, + 4.775220833456485, + 4.838052686528281, + 4.900884539600076, + 4.963716392671872, + 5.026548245743667, + 5.089380098815463, + 5.152211951887258, + 5.215043804959054, + 5.277875658030849, + 5.340707511102645, + 5.40353936417444, + 5.466371217246236, + 5.529203070318031, + 5.592034923389827, + 5.654866776461622, + 5.717698629533418, + 5.780530482605213, + 5.843362335677009, + 5.906194188748804, + 5.9690260418206, + 6.031857894892395, + 6.094689747964191, + 6.157521601035986, + 6.220353454107782, + 6.283185307179577 + ], + "line": { + "color": "#3D9970", + "dash": "dot", + "width": 3 + }, + "subplot": "polar" + }, + { + "type": "scatterpolar", + "mode": "lines", + "thetaunit": "radians", + "r": [ + 3.5, + 3.5101069780115313, + 3.5399002890543394, + 3.5878528813194026, + 3.6515921856519458, + 3.7281173520733097, + 3.814037602559748, + 3.9057957513598405, + 3.999854737597697, + 4.092839226534218, + 4.181634605928617, + 4.263451031313349, + 4.335861616679521, + 4.3968230543317475, + 4.4446852217199195, + 4.478194509529551, + 4.496494057069119, + 4.499122904935259, + 4.486015230080536, + 4.457500219838926, + 4.414302660387005, + 4.357543852184939, + 4.288741911087211, + 4.209809759447415, + 4.123048054333455, + 4.031128874149274, + 3.9370642287949904, + 3.8441516345302094, + 3.75588775392503, + 3.675841614689789, + 3.607482782010221, + 3.5539684565476826, + 3.5179066723671726, + 3.5011274021787484, + 3.504502988262685, + 3.5278568472685214, + 3.5699822520321516, + 3.628766598845516, + 3.701392176060956, + 3.784572010623282, + 3.8747815451326066, + 3.968459029112283, + 4.0621622417275844, + 4.152681007332425, + 4.237111889749941, + 4.312904011124049, + 4.377884707217135, + 4.430272192767401, + 4.468680559578299, + 4.492120768959218, + 4.5, + 4.492120768959217, + 4.468680559578297, + 4.430272192767397, + 4.377884707217131, + 4.3129040111240435, + 4.237111889749934, + 4.152681007332418, + 4.062162241727577, + 3.9684590291122754, + 3.874781545132599, + 3.784572010623276, + 3.701392176060949, + 3.6287665988455107, + 3.5699822520321476, + 3.527856847268519, + 3.5045029882626846, + 3.501127402178749, + 3.5179066723671735, + 3.553968456547685, + 3.6074827820102233, + 3.6758416146897916, + 3.7558877539250317, + 3.844151634530211, + 3.9370642287949917, + 4.031128874149275, + 4.123048054333455, + 4.209809759447415, + 4.28874191108721, + 4.357543852184937, + 4.414302660387003, + 4.457500219838925, + 4.486015230080535, + 4.499122904935259, + 4.49649405706912, + 4.478194509529552, + 4.444685221719923, + 4.396823054331751, + 4.335861616679527, + 4.263451031313355, + 4.181634605928625, + 4.0928392265342275, + 3.999854737597707, + 3.9057957513598502, + 3.814037602559758, + 3.728117352073319, + 3.651592185651954, + 3.5878528813194097, + 3.5399002890543443, + 3.5101069780115344, + 3.5 + ], + "theta": [ + 0, + 0.06283185307179587, + 0.12566370614359174, + 0.1884955592153876, + 0.25132741228718347, + 0.3141592653589793, + 0.37699111843077515, + 0.439822971502571, + 0.5026548245743668, + 0.5654866776461627, + 0.6283185307179585, + 0.6911503837897544, + 0.7539822368615502, + 0.816814089933346, + 0.8796459430051419, + 0.9424777960769377, + 1.0053096491487337, + 1.0681415022205296, + 1.1309733552923256, + 1.1938052083641215, + 1.2566370614359175, + 1.3194689145077134, + 1.3823007675795094, + 1.4451326206513053, + 1.5079644737231013, + 1.5707963267948972, + 1.6336281798666932, + 1.6964600329384891, + 1.759291886010285, + 1.822123739082081, + 1.884955592153877, + 1.947787445225673, + 2.0106192982974687, + 2.0734511513692646, + 2.1362830044410606, + 2.1991148575128565, + 2.2619467105846525, + 2.3247785636564484, + 2.3876104167282444, + 2.4504422698000403, + 2.5132741228718363, + 2.576105975943632, + 2.638937829015428, + 2.701769682087224, + 2.76460153515902, + 2.827433388230816, + 2.890265241302612, + 2.953097094374408, + 3.015928947446204, + 3.078760800518, + 3.141592653589796, + 3.2044245066615917, + 3.2672563597333877, + 3.3300882128051836, + 3.3929200658769796, + 3.4557519189487755, + 3.5185837720205715, + 3.5814156250923674, + 3.6442474781641634, + 3.7070793312359593, + 3.7699111843077553, + 3.8327430373795512, + 3.895574890451347, + 3.958406743523143, + 4.021238596594939, + 4.084070449666735, + 4.14690230273853, + 4.209734155810326, + 4.272566008882121, + 4.335397861953917, + 4.398229715025712, + 4.461061568097508, + 4.523893421169303, + 4.586725274241099, + 4.649557127312894, + 4.71238898038469, + 4.775220833456485, + 4.838052686528281, + 4.900884539600076, + 4.963716392671872, + 5.026548245743667, + 5.089380098815463, + 5.152211951887258, + 5.215043804959054, + 5.277875658030849, + 5.340707511102645, + 5.40353936417444, + 5.466371217246236, + 5.529203070318031, + 5.592034923389827, + 5.654866776461622, + 5.717698629533418, + 5.780530482605213, + 5.843362335677009, + 5.906194188748804, + 5.9690260418206, + 6.031857894892395, + 6.094689747964191, + 6.157521601035986, + 6.220353454107782, + 6.283185307179577 + ], + "line": { + "color": "#3D9970", + "dash": "dot", + "width": 3 + }, + "subplot": "polar2" + }, + { + "type": "scatterpolar", + "mode": "lines", + "thetaunit": "radians", + "r": [ + 3.5, + 3.5101069780115313, + 3.5399002890543394, + 3.5878528813194026, + 3.6515921856519458, + 3.7281173520733097, + 3.814037602559748, + 3.9057957513598405, + 3.999854737597697, + 4.092839226534218, + 4.181634605928617, + 4.263451031313349, + 4.335861616679521, + 4.3968230543317475, + 4.4446852217199195, + 4.478194509529551, + 4.496494057069119, + 4.499122904935259, + 4.486015230080536, + 4.457500219838926, + 4.414302660387005, + 4.357543852184939, + 4.288741911087211, + 4.209809759447415, + 4.123048054333455, + 4.031128874149274, + 3.9370642287949904, + 3.8441516345302094, + 3.75588775392503, + 3.675841614689789, + 3.607482782010221, + 3.5539684565476826, + 3.5179066723671726, + 3.5011274021787484, + 3.504502988262685, + 3.5278568472685214, + 3.5699822520321516, + 3.628766598845516, + 3.701392176060956, + 3.784572010623282, + 3.8747815451326066, + 3.968459029112283, + 4.0621622417275844, + 4.152681007332425, + 4.237111889749941, + 4.312904011124049, + 4.377884707217135, + 4.430272192767401, + 4.468680559578299, + 4.492120768959218, + 4.5, + 4.492120768959217, + 4.468680559578297, + 4.430272192767397, + 4.377884707217131, + 4.3129040111240435, + 4.237111889749934, + 4.152681007332418, + 4.062162241727577, + 3.9684590291122754, + 3.874781545132599, + 3.784572010623276, + 3.701392176060949, + 3.6287665988455107, + 3.5699822520321476, + 3.527856847268519, + 3.5045029882626846, + 3.501127402178749, + 3.5179066723671735, + 3.553968456547685, + 3.6074827820102233, + 3.6758416146897916, + 3.7558877539250317, + 3.844151634530211, + 3.9370642287949917, + 4.031128874149275, + 4.123048054333455, + 4.209809759447415, + 4.28874191108721, + 4.357543852184937, + 4.414302660387003, + 4.457500219838925, + 4.486015230080535, + 4.499122904935259, + 4.49649405706912, + 4.478194509529552, + 4.444685221719923, + 4.396823054331751, + 4.335861616679527, + 4.263451031313355, + 4.181634605928625, + 4.0928392265342275, + 3.999854737597707, + 3.9057957513598502, + 3.814037602559758, + 3.728117352073319, + 3.651592185651954, + 3.5878528813194097, + 3.5399002890543443, + 3.5101069780115344, + 3.5 + ], + "theta": [ + 0, + 0.06283185307179587, + 0.12566370614359174, + 0.1884955592153876, + 0.25132741228718347, + 0.3141592653589793, + 0.37699111843077515, + 0.439822971502571, + 0.5026548245743668, + 0.5654866776461627, + 0.6283185307179585, + 0.6911503837897544, + 0.7539822368615502, + 0.816814089933346, + 0.8796459430051419, + 0.9424777960769377, + 1.0053096491487337, + 1.0681415022205296, + 1.1309733552923256, + 1.1938052083641215, + 1.2566370614359175, + 1.3194689145077134, + 1.3823007675795094, + 1.4451326206513053, + 1.5079644737231013, + 1.5707963267948972, + 1.6336281798666932, + 1.6964600329384891, + 1.759291886010285, + 1.822123739082081, + 1.884955592153877, + 1.947787445225673, + 2.0106192982974687, + 2.0734511513692646, + 2.1362830044410606, + 2.1991148575128565, + 2.2619467105846525, + 2.3247785636564484, + 2.3876104167282444, + 2.4504422698000403, + 2.5132741228718363, + 2.576105975943632, + 2.638937829015428, + 2.701769682087224, + 2.76460153515902, + 2.827433388230816, + 2.890265241302612, + 2.953097094374408, + 3.015928947446204, + 3.078760800518, + 3.141592653589796, + 3.2044245066615917, + 3.2672563597333877, + 3.3300882128051836, + 3.3929200658769796, + 3.4557519189487755, + 3.5185837720205715, + 3.5814156250923674, + 3.6442474781641634, + 3.7070793312359593, + 3.7699111843077553, + 3.8327430373795512, + 3.895574890451347, + 3.958406743523143, + 4.021238596594939, + 4.084070449666735, + 4.14690230273853, + 4.209734155810326, + 4.272566008882121, + 4.335397861953917, + 4.398229715025712, + 4.461061568097508, + 4.523893421169303, + 4.586725274241099, + 4.649557127312894, + 4.71238898038469, + 4.775220833456485, + 4.838052686528281, + 4.900884539600076, + 4.963716392671872, + 5.026548245743667, + 5.089380098815463, + 5.152211951887258, + 5.215043804959054, + 5.277875658030849, + 5.340707511102645, + 5.40353936417444, + 5.466371217246236, + 5.529203070318031, + 5.592034923389827, + 5.654866776461622, + 5.717698629533418, + 5.780530482605213, + 5.843362335677009, + 5.906194188748804, + 5.9690260418206, + 6.031857894892395, + 6.094689747964191, + 6.157521601035986, + 6.220353454107782, + 6.283185307179577 + ], + "line": { + "color": "#3D9970", + "dash": "dot", + "width": 3 + }, + "subplot": "polar3" + }, + { + "type": "scatterpolar", + "mode": "lines", + "thetaunit": "radians", + "r": [ + 3.5, + 3.5101069780115313, + 3.5399002890543394, + 3.5878528813194026, + 3.6515921856519458, + 3.7281173520733097, + 3.814037602559748, + 3.9057957513598405, + 3.999854737597697, + 4.092839226534218, + 4.181634605928617, + 4.263451031313349, + 4.335861616679521, + 4.3968230543317475, + 4.4446852217199195, + 4.478194509529551, + 4.496494057069119, + 4.499122904935259, + 4.486015230080536, + 4.457500219838926, + 4.414302660387005, + 4.357543852184939, + 4.288741911087211, + 4.209809759447415, + 4.123048054333455, + 4.031128874149274, + 3.9370642287949904, + 3.8441516345302094, + 3.75588775392503, + 3.675841614689789, + 3.607482782010221, + 3.5539684565476826, + 3.5179066723671726, + 3.5011274021787484, + 3.504502988262685, + 3.5278568472685214, + 3.5699822520321516, + 3.628766598845516, + 3.701392176060956, + 3.784572010623282, + 3.8747815451326066, + 3.968459029112283, + 4.0621622417275844, + 4.152681007332425, + 4.237111889749941, + 4.312904011124049, + 4.377884707217135, + 4.430272192767401, + 4.468680559578299, + 4.492120768959218, + 4.5, + 4.492120768959217, + 4.468680559578297, + 4.430272192767397, + 4.377884707217131, + 4.3129040111240435, + 4.237111889749934, + 4.152681007332418, + 4.062162241727577, + 3.9684590291122754, + 3.874781545132599, + 3.784572010623276, + 3.701392176060949, + 3.6287665988455107, + 3.5699822520321476, + 3.527856847268519, + 3.5045029882626846, + 3.501127402178749, + 3.5179066723671735, + 3.553968456547685, + 3.6074827820102233, + 3.6758416146897916, + 3.7558877539250317, + 3.844151634530211, + 3.9370642287949917, + 4.031128874149275, + 4.123048054333455, + 4.209809759447415, + 4.28874191108721, + 4.357543852184937, + 4.414302660387003, + 4.457500219838925, + 4.486015230080535, + 4.499122904935259, + 4.49649405706912, + 4.478194509529552, + 4.444685221719923, + 4.396823054331751, + 4.335861616679527, + 4.263451031313355, + 4.181634605928625, + 4.0928392265342275, + 3.999854737597707, + 3.9057957513598502, + 3.814037602559758, + 3.728117352073319, + 3.651592185651954, + 3.5878528813194097, + 3.5399002890543443, + 3.5101069780115344, + 3.5 + ], + "theta": [ + 0, + 0.06283185307179587, + 0.12566370614359174, + 0.1884955592153876, + 0.25132741228718347, + 0.3141592653589793, + 0.37699111843077515, + 0.439822971502571, + 0.5026548245743668, + 0.5654866776461627, + 0.6283185307179585, + 0.6911503837897544, + 0.7539822368615502, + 0.816814089933346, + 0.8796459430051419, + 0.9424777960769377, + 1.0053096491487337, + 1.0681415022205296, + 1.1309733552923256, + 1.1938052083641215, + 1.2566370614359175, + 1.3194689145077134, + 1.3823007675795094, + 1.4451326206513053, + 1.5079644737231013, + 1.5707963267948972, + 1.6336281798666932, + 1.6964600329384891, + 1.759291886010285, + 1.822123739082081, + 1.884955592153877, + 1.947787445225673, + 2.0106192982974687, + 2.0734511513692646, + 2.1362830044410606, + 2.1991148575128565, + 2.2619467105846525, + 2.3247785636564484, + 2.3876104167282444, + 2.4504422698000403, + 2.5132741228718363, + 2.576105975943632, + 2.638937829015428, + 2.701769682087224, + 2.76460153515902, + 2.827433388230816, + 2.890265241302612, + 2.953097094374408, + 3.015928947446204, + 3.078760800518, + 3.141592653589796, + 3.2044245066615917, + 3.2672563597333877, + 3.3300882128051836, + 3.3929200658769796, + 3.4557519189487755, + 3.5185837720205715, + 3.5814156250923674, + 3.6442474781641634, + 3.7070793312359593, + 3.7699111843077553, + 3.8327430373795512, + 3.895574890451347, + 3.958406743523143, + 4.021238596594939, + 4.084070449666735, + 4.14690230273853, + 4.209734155810326, + 4.272566008882121, + 4.335397861953917, + 4.398229715025712, + 4.461061568097508, + 4.523893421169303, + 4.586725274241099, + 4.649557127312894, + 4.71238898038469, + 4.775220833456485, + 4.838052686528281, + 4.900884539600076, + 4.963716392671872, + 5.026548245743667, + 5.089380098815463, + 5.152211951887258, + 5.215043804959054, + 5.277875658030849, + 5.340707511102645, + 5.40353936417444, + 5.466371217246236, + 5.529203070318031, + 5.592034923389827, + 5.654866776461622, + 5.717698629533418, + 5.780530482605213, + 5.843362335677009, + 5.906194188748804, + 5.9690260418206, + 6.031857894892395, + 6.094689747964191, + 6.157521601035986, + 6.220353454107782, + 6.283185307179577 + ], + "line": { + "color": "#3D9970", + "dash": "dot", + "width": 3 + }, + "subplot": "polar4" + }, + { + "type": "scatterpolar", + "mode": "lines", + "thetaunit": "radians", + "r": [ + 3.5, + 3.5101069780115313, + 3.5399002890543394, + 3.5878528813194026, + 3.6515921856519458, + 3.7281173520733097, + 3.814037602559748, + 3.9057957513598405, + 3.999854737597697, + 4.092839226534218, + 4.181634605928617, + 4.263451031313349, + 4.335861616679521, + 4.3968230543317475, + 4.4446852217199195, + 4.478194509529551, + 4.496494057069119, + 4.499122904935259, + 4.486015230080536, + 4.457500219838926, + 4.414302660387005, + 4.357543852184939, + 4.288741911087211, + 4.209809759447415, + 4.123048054333455, + 4.031128874149274, + 3.9370642287949904, + 3.8441516345302094, + 3.75588775392503, + 3.675841614689789, + 3.607482782010221, + 3.5539684565476826, + 3.5179066723671726, + 3.5011274021787484, + 3.504502988262685, + 3.5278568472685214, + 3.5699822520321516, + 3.628766598845516, + 3.701392176060956, + 3.784572010623282, + 3.8747815451326066, + 3.968459029112283, + 4.0621622417275844, + 4.152681007332425, + 4.237111889749941, + 4.312904011124049, + 4.377884707217135, + 4.430272192767401, + 4.468680559578299, + 4.492120768959218, + 4.5, + 4.492120768959217, + 4.468680559578297, + 4.430272192767397, + 4.377884707217131, + 4.3129040111240435, + 4.237111889749934, + 4.152681007332418, + 4.062162241727577, + 3.9684590291122754, + 3.874781545132599, + 3.784572010623276, + 3.701392176060949, + 3.6287665988455107, + 3.5699822520321476, + 3.527856847268519, + 3.5045029882626846, + 3.501127402178749, + 3.5179066723671735, + 3.553968456547685, + 3.6074827820102233, + 3.6758416146897916, + 3.7558877539250317, + 3.844151634530211, + 3.9370642287949917, + 4.031128874149275, + 4.123048054333455, + 4.209809759447415, + 4.28874191108721, + 4.357543852184937, + 4.414302660387003, + 4.457500219838925, + 4.486015230080535, + 4.499122904935259, + 4.49649405706912, + 4.478194509529552, + 4.444685221719923, + 4.396823054331751, + 4.335861616679527, + 4.263451031313355, + 4.181634605928625, + 4.0928392265342275, + 3.999854737597707, + 3.9057957513598502, + 3.814037602559758, + 3.728117352073319, + 3.651592185651954, + 3.5878528813194097, + 3.5399002890543443, + 3.5101069780115344, + 3.5 + ], + "theta": [ + 0, + 0.06283185307179587, + 0.12566370614359174, + 0.1884955592153876, + 0.25132741228718347, + 0.3141592653589793, + 0.37699111843077515, + 0.439822971502571, + 0.5026548245743668, + 0.5654866776461627, + 0.6283185307179585, + 0.6911503837897544, + 0.7539822368615502, + 0.816814089933346, + 0.8796459430051419, + 0.9424777960769377, + 1.0053096491487337, + 1.0681415022205296, + 1.1309733552923256, + 1.1938052083641215, + 1.2566370614359175, + 1.3194689145077134, + 1.3823007675795094, + 1.4451326206513053, + 1.5079644737231013, + 1.5707963267948972, + 1.6336281798666932, + 1.6964600329384891, + 1.759291886010285, + 1.822123739082081, + 1.884955592153877, + 1.947787445225673, + 2.0106192982974687, + 2.0734511513692646, + 2.1362830044410606, + 2.1991148575128565, + 2.2619467105846525, + 2.3247785636564484, + 2.3876104167282444, + 2.4504422698000403, + 2.5132741228718363, + 2.576105975943632, + 2.638937829015428, + 2.701769682087224, + 2.76460153515902, + 2.827433388230816, + 2.890265241302612, + 2.953097094374408, + 3.015928947446204, + 3.078760800518, + 3.141592653589796, + 3.2044245066615917, + 3.2672563597333877, + 3.3300882128051836, + 3.3929200658769796, + 3.4557519189487755, + 3.5185837720205715, + 3.5814156250923674, + 3.6442474781641634, + 3.7070793312359593, + 3.7699111843077553, + 3.8327430373795512, + 3.895574890451347, + 3.958406743523143, + 4.021238596594939, + 4.084070449666735, + 4.14690230273853, + 4.209734155810326, + 4.272566008882121, + 4.335397861953917, + 4.398229715025712, + 4.461061568097508, + 4.523893421169303, + 4.586725274241099, + 4.649557127312894, + 4.71238898038469, + 4.775220833456485, + 4.838052686528281, + 4.900884539600076, + 4.963716392671872, + 5.026548245743667, + 5.089380098815463, + 5.152211951887258, + 5.215043804959054, + 5.277875658030849, + 5.340707511102645, + 5.40353936417444, + 5.466371217246236, + 5.529203070318031, + 5.592034923389827, + 5.654866776461622, + 5.717698629533418, + 5.780530482605213, + 5.843362335677009, + 5.906194188748804, + 5.9690260418206, + 6.031857894892395, + 6.094689747964191, + 6.157521601035986, + 6.220353454107782, + 6.283185307179577 + ], + "line": { + "color": "#3D9970", + "dash": "dot", + "width": 3 + }, + "subplot": "polar5" + }, + { + "type": "scatterpolar", + "mode": "lines", + "thetaunit": "radians", + "r": [ + 3.5, + 3.5101069780115313, + 3.5399002890543394, + 3.5878528813194026, + 3.6515921856519458, + 3.7281173520733097, + 3.814037602559748, + 3.9057957513598405, + 3.999854737597697, + 4.092839226534218, + 4.181634605928617, + 4.263451031313349, + 4.335861616679521, + 4.3968230543317475, + 4.4446852217199195, + 4.478194509529551, + 4.496494057069119, + 4.499122904935259, + 4.486015230080536, + 4.457500219838926, + 4.414302660387005, + 4.357543852184939, + 4.288741911087211, + 4.209809759447415, + 4.123048054333455, + 4.031128874149274, + 3.9370642287949904, + 3.8441516345302094, + 3.75588775392503, + 3.675841614689789, + 3.607482782010221, + 3.5539684565476826, + 3.5179066723671726, + 3.5011274021787484, + 3.504502988262685, + 3.5278568472685214, + 3.5699822520321516, + 3.628766598845516, + 3.701392176060956, + 3.784572010623282, + 3.8747815451326066, + 3.968459029112283, + 4.0621622417275844, + 4.152681007332425, + 4.237111889749941, + 4.312904011124049, + 4.377884707217135, + 4.430272192767401, + 4.468680559578299, + 4.492120768959218, + 4.5, + 4.492120768959217, + 4.468680559578297, + 4.430272192767397, + 4.377884707217131, + 4.3129040111240435, + 4.237111889749934, + 4.152681007332418, + 4.062162241727577, + 3.9684590291122754, + 3.874781545132599, + 3.784572010623276, + 3.701392176060949, + 3.6287665988455107, + 3.5699822520321476, + 3.527856847268519, + 3.5045029882626846, + 3.501127402178749, + 3.5179066723671735, + 3.553968456547685, + 3.6074827820102233, + 3.6758416146897916, + 3.7558877539250317, + 3.844151634530211, + 3.9370642287949917, + 4.031128874149275, + 4.123048054333455, + 4.209809759447415, + 4.28874191108721, + 4.357543852184937, + 4.414302660387003, + 4.457500219838925, + 4.486015230080535, + 4.499122904935259, + 4.49649405706912, + 4.478194509529552, + 4.444685221719923, + 4.396823054331751, + 4.335861616679527, + 4.263451031313355, + 4.181634605928625, + 4.0928392265342275, + 3.999854737597707, + 3.9057957513598502, + 3.814037602559758, + 3.728117352073319, + 3.651592185651954, + 3.5878528813194097, + 3.5399002890543443, + 3.5101069780115344, + 3.5 + ], + "theta": [ + 0, + 0.06283185307179587, + 0.12566370614359174, + 0.1884955592153876, + 0.25132741228718347, + 0.3141592653589793, + 0.37699111843077515, + 0.439822971502571, + 0.5026548245743668, + 0.5654866776461627, + 0.6283185307179585, + 0.6911503837897544, + 0.7539822368615502, + 0.816814089933346, + 0.8796459430051419, + 0.9424777960769377, + 1.0053096491487337, + 1.0681415022205296, + 1.1309733552923256, + 1.1938052083641215, + 1.2566370614359175, + 1.3194689145077134, + 1.3823007675795094, + 1.4451326206513053, + 1.5079644737231013, + 1.5707963267948972, + 1.6336281798666932, + 1.6964600329384891, + 1.759291886010285, + 1.822123739082081, + 1.884955592153877, + 1.947787445225673, + 2.0106192982974687, + 2.0734511513692646, + 2.1362830044410606, + 2.1991148575128565, + 2.2619467105846525, + 2.3247785636564484, + 2.3876104167282444, + 2.4504422698000403, + 2.5132741228718363, + 2.576105975943632, + 2.638937829015428, + 2.701769682087224, + 2.76460153515902, + 2.827433388230816, + 2.890265241302612, + 2.953097094374408, + 3.015928947446204, + 3.078760800518, + 3.141592653589796, + 3.2044245066615917, + 3.2672563597333877, + 3.3300882128051836, + 3.3929200658769796, + 3.4557519189487755, + 3.5185837720205715, + 3.5814156250923674, + 3.6442474781641634, + 3.7070793312359593, + 3.7699111843077553, + 3.8327430373795512, + 3.895574890451347, + 3.958406743523143, + 4.021238596594939, + 4.084070449666735, + 4.14690230273853, + 4.209734155810326, + 4.272566008882121, + 4.335397861953917, + 4.398229715025712, + 4.461061568097508, + 4.523893421169303, + 4.586725274241099, + 4.649557127312894, + 4.71238898038469, + 4.775220833456485, + 4.838052686528281, + 4.900884539600076, + 4.963716392671872, + 5.026548245743667, + 5.089380098815463, + 5.152211951887258, + 5.215043804959054, + 5.277875658030849, + 5.340707511102645, + 5.40353936417444, + 5.466371217246236, + 5.529203070318031, + 5.592034923389827, + 5.654866776461622, + 5.717698629533418, + 5.780530482605213, + 5.843362335677009, + 5.906194188748804, + 5.9690260418206, + 6.031857894892395, + 6.094689747964191, + 6.157521601035986, + 6.220353454107782, + 6.283185307179577 + ], + "line": { + "color": "#3D9970", + "dash": "dot", + "width": 3 + }, + "subplot": "polar6" + }, + { + "type": "scatterpolar", + "mode": "lines", + "thetaunit": "radians", + "r": [ + 3.5, + 3.5101069780115313, + 3.5399002890543394, + 3.5878528813194026, + 3.6515921856519458, + 3.7281173520733097, + 3.814037602559748, + 3.9057957513598405, + 3.999854737597697, + 4.092839226534218, + 4.181634605928617, + 4.263451031313349, + 4.335861616679521, + 4.3968230543317475, + 4.4446852217199195, + 4.478194509529551, + 4.496494057069119, + 4.499122904935259, + 4.486015230080536, + 4.457500219838926, + 4.414302660387005, + 4.357543852184939, + 4.288741911087211, + 4.209809759447415, + 4.123048054333455, + 4.031128874149274, + 3.9370642287949904, + 3.8441516345302094, + 3.75588775392503, + 3.675841614689789, + 3.607482782010221, + 3.5539684565476826, + 3.5179066723671726, + 3.5011274021787484, + 3.504502988262685, + 3.5278568472685214, + 3.5699822520321516, + 3.628766598845516, + 3.701392176060956, + 3.784572010623282, + 3.8747815451326066, + 3.968459029112283, + 4.0621622417275844, + 4.152681007332425, + 4.237111889749941, + 4.312904011124049, + 4.377884707217135, + 4.430272192767401, + 4.468680559578299, + 4.492120768959218, + 4.5, + 4.492120768959217, + 4.468680559578297, + 4.430272192767397, + 4.377884707217131, + 4.3129040111240435, + 4.237111889749934, + 4.152681007332418, + 4.062162241727577, + 3.9684590291122754, + 3.874781545132599, + 3.784572010623276, + 3.701392176060949, + 3.6287665988455107, + 3.5699822520321476, + 3.527856847268519, + 3.5045029882626846, + 3.501127402178749, + 3.5179066723671735, + 3.553968456547685, + 3.6074827820102233, + 3.6758416146897916, + 3.7558877539250317, + 3.844151634530211, + 3.9370642287949917, + 4.031128874149275, + 4.123048054333455, + 4.209809759447415, + 4.28874191108721, + 4.357543852184937, + 4.414302660387003, + 4.457500219838925, + 4.486015230080535, + 4.499122904935259, + 4.49649405706912, + 4.478194509529552, + 4.444685221719923, + 4.396823054331751, + 4.335861616679527, + 4.263451031313355, + 4.181634605928625, + 4.0928392265342275, + 3.999854737597707, + 3.9057957513598502, + 3.814037602559758, + 3.728117352073319, + 3.651592185651954, + 3.5878528813194097, + 3.5399002890543443, + 3.5101069780115344, + 3.5 + ], + "theta": [ + 0, + 0.06283185307179587, + 0.12566370614359174, + 0.1884955592153876, + 0.25132741228718347, + 0.3141592653589793, + 0.37699111843077515, + 0.439822971502571, + 0.5026548245743668, + 0.5654866776461627, + 0.6283185307179585, + 0.6911503837897544, + 0.7539822368615502, + 0.816814089933346, + 0.8796459430051419, + 0.9424777960769377, + 1.0053096491487337, + 1.0681415022205296, + 1.1309733552923256, + 1.1938052083641215, + 1.2566370614359175, + 1.3194689145077134, + 1.3823007675795094, + 1.4451326206513053, + 1.5079644737231013, + 1.5707963267948972, + 1.6336281798666932, + 1.6964600329384891, + 1.759291886010285, + 1.822123739082081, + 1.884955592153877, + 1.947787445225673, + 2.0106192982974687, + 2.0734511513692646, + 2.1362830044410606, + 2.1991148575128565, + 2.2619467105846525, + 2.3247785636564484, + 2.3876104167282444, + 2.4504422698000403, + 2.5132741228718363, + 2.576105975943632, + 2.638937829015428, + 2.701769682087224, + 2.76460153515902, + 2.827433388230816, + 2.890265241302612, + 2.953097094374408, + 3.015928947446204, + 3.078760800518, + 3.141592653589796, + 3.2044245066615917, + 3.2672563597333877, + 3.3300882128051836, + 3.3929200658769796, + 3.4557519189487755, + 3.5185837720205715, + 3.5814156250923674, + 3.6442474781641634, + 3.7070793312359593, + 3.7699111843077553, + 3.8327430373795512, + 3.895574890451347, + 3.958406743523143, + 4.021238596594939, + 4.084070449666735, + 4.14690230273853, + 4.209734155810326, + 4.272566008882121, + 4.335397861953917, + 4.398229715025712, + 4.461061568097508, + 4.523893421169303, + 4.586725274241099, + 4.649557127312894, + 4.71238898038469, + 4.775220833456485, + 4.838052686528281, + 4.900884539600076, + 4.963716392671872, + 5.026548245743667, + 5.089380098815463, + 5.152211951887258, + 5.215043804959054, + 5.277875658030849, + 5.340707511102645, + 5.40353936417444, + 5.466371217246236, + 5.529203070318031, + 5.592034923389827, + 5.654866776461622, + 5.717698629533418, + 5.780530482605213, + 5.843362335677009, + 5.906194188748804, + 5.9690260418206, + 6.031857894892395, + 6.094689747964191, + 6.157521601035986, + 6.220353454107782, + 6.283185307179577 + ], + "line": { + "color": "#3D9970", + "dash": "dot", + "width": 3 + }, + "subplot": "polar7" + }, + { + "type": "scatterpolar", + "mode": "lines", + "thetaunit": "radians", + "r": [ + 3.5, + 3.5101069780115313, + 3.5399002890543394, + 3.5878528813194026, + 3.6515921856519458, + 3.7281173520733097, + 3.814037602559748, + 3.9057957513598405, + 3.999854737597697, + 4.092839226534218, + 4.181634605928617, + 4.263451031313349, + 4.335861616679521, + 4.3968230543317475, + 4.4446852217199195, + 4.478194509529551, + 4.496494057069119, + 4.499122904935259, + 4.486015230080536, + 4.457500219838926, + 4.414302660387005, + 4.357543852184939, + 4.288741911087211, + 4.209809759447415, + 4.123048054333455, + 4.031128874149274, + 3.9370642287949904, + 3.8441516345302094, + 3.75588775392503, + 3.675841614689789, + 3.607482782010221, + 3.5539684565476826, + 3.5179066723671726, + 3.5011274021787484, + 3.504502988262685, + 3.5278568472685214, + 3.5699822520321516, + 3.628766598845516, + 3.701392176060956, + 3.784572010623282, + 3.8747815451326066, + 3.968459029112283, + 4.0621622417275844, + 4.152681007332425, + 4.237111889749941, + 4.312904011124049, + 4.377884707217135, + 4.430272192767401, + 4.468680559578299, + 4.492120768959218, + 4.5, + 4.492120768959217, + 4.468680559578297, + 4.430272192767397, + 4.377884707217131, + 4.3129040111240435, + 4.237111889749934, + 4.152681007332418, + 4.062162241727577, + 3.9684590291122754, + 3.874781545132599, + 3.784572010623276, + 3.701392176060949, + 3.6287665988455107, + 3.5699822520321476, + 3.527856847268519, + 3.5045029882626846, + 3.501127402178749, + 3.5179066723671735, + 3.553968456547685, + 3.6074827820102233, + 3.6758416146897916, + 3.7558877539250317, + 3.844151634530211, + 3.9370642287949917, + 4.031128874149275, + 4.123048054333455, + 4.209809759447415, + 4.28874191108721, + 4.357543852184937, + 4.414302660387003, + 4.457500219838925, + 4.486015230080535, + 4.499122904935259, + 4.49649405706912, + 4.478194509529552, + 4.444685221719923, + 4.396823054331751, + 4.335861616679527, + 4.263451031313355, + 4.181634605928625, + 4.0928392265342275, + 3.999854737597707, + 3.9057957513598502, + 3.814037602559758, + 3.728117352073319, + 3.651592185651954, + 3.5878528813194097, + 3.5399002890543443, + 3.5101069780115344, + 3.5 + ], + "theta": [ + 0, + 0.06283185307179587, + 0.12566370614359174, + 0.1884955592153876, + 0.25132741228718347, + 0.3141592653589793, + 0.37699111843077515, + 0.439822971502571, + 0.5026548245743668, + 0.5654866776461627, + 0.6283185307179585, + 0.6911503837897544, + 0.7539822368615502, + 0.816814089933346, + 0.8796459430051419, + 0.9424777960769377, + 1.0053096491487337, + 1.0681415022205296, + 1.1309733552923256, + 1.1938052083641215, + 1.2566370614359175, + 1.3194689145077134, + 1.3823007675795094, + 1.4451326206513053, + 1.5079644737231013, + 1.5707963267948972, + 1.6336281798666932, + 1.6964600329384891, + 1.759291886010285, + 1.822123739082081, + 1.884955592153877, + 1.947787445225673, + 2.0106192982974687, + 2.0734511513692646, + 2.1362830044410606, + 2.1991148575128565, + 2.2619467105846525, + 2.3247785636564484, + 2.3876104167282444, + 2.4504422698000403, + 2.5132741228718363, + 2.576105975943632, + 2.638937829015428, + 2.701769682087224, + 2.76460153515902, + 2.827433388230816, + 2.890265241302612, + 2.953097094374408, + 3.015928947446204, + 3.078760800518, + 3.141592653589796, + 3.2044245066615917, + 3.2672563597333877, + 3.3300882128051836, + 3.3929200658769796, + 3.4557519189487755, + 3.5185837720205715, + 3.5814156250923674, + 3.6442474781641634, + 3.7070793312359593, + 3.7699111843077553, + 3.8327430373795512, + 3.895574890451347, + 3.958406743523143, + 4.021238596594939, + 4.084070449666735, + 4.14690230273853, + 4.209734155810326, + 4.272566008882121, + 4.335397861953917, + 4.398229715025712, + 4.461061568097508, + 4.523893421169303, + 4.586725274241099, + 4.649557127312894, + 4.71238898038469, + 4.775220833456485, + 4.838052686528281, + 4.900884539600076, + 4.963716392671872, + 5.026548245743667, + 5.089380098815463, + 5.152211951887258, + 5.215043804959054, + 5.277875658030849, + 5.340707511102645, + 5.40353936417444, + 5.466371217246236, + 5.529203070318031, + 5.592034923389827, + 5.654866776461622, + 5.717698629533418, + 5.780530482605213, + 5.843362335677009, + 5.906194188748804, + 5.9690260418206, + 6.031857894892395, + 6.094689747964191, + 6.157521601035986, + 6.220353454107782, + 6.283185307179577 + ], + "line": { + "color": "#3D9970", + "dash": "dot", + "width": 3 + }, + "subplot": "polar8" + } + ], + "layout": { + "margin": { + "l": 40, + "r": 40, + "t": 40, + "b": 40 + }, + "width": 600, + "height": 930, + "showlegend": false, + "paper_bgcolor": "#DDDDDD", + "polar": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0, + 0.45 + ], + "y": [ + 0.775, + 1 + ] + }, + "radialaxis": {}, + "angularaxis": { + "thetaunit": "radians", + "dtick": 1 + } + }, + "polar2": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0, + 0.45 + ], + "y": [ + 0.525, + 0.725 + ] + }, + "radialaxis": {}, + "angularaxis": { + "thetaunit": "radians", + "dtick": 0.5235987755982988 + } + }, + "polar3": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0, + 0.45 + ], + "y": [ + 0.275, + 0.475 + ] + }, + "radialaxis": { + "showgrid": false + }, + "angularaxis": { + "ticks": "", + "showline": false + } + }, + "polar4": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0, + 0.45 + ], + "y": [ + 0, + 0.225 + ] + }, + "radialaxis": { + "ticks": "", + "showline": false + }, + "angularaxis": { + "ticks": "inside", + "showgrid": false + } + }, + "polar5": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0.55, + 1 + ], + "y": [ + 0.775, + 1 + ] + }, + "radialaxis": {}, + "angularaxis": { + "dtick": 10, + "tickfont": { + "size": 10, + "color": "#001f3f" + } + } + }, + "polar6": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0.55, + 1 + ], + "y": [ + 0.525, + 0.725 + ] + }, + "radialaxis": {}, + "angularaxis": { + "nticks": 30, + "ticks": "inside", + "showgrid": false + } + }, + "polar7": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0.55, + 1 + ], + "y": [ + 0.275, + 0.475 + ] + }, + "radialaxis": {}, + "angularaxis": { + "nticks": 30, + "ticklen": 10, + "tickwidth": 3, + "tickcolor": "#85144b" + } + }, + "polar8": { + "sector": [ + 0, + 360 + ], + "domain": { + "x": [ + 0.55, + 1 + ], + "y": [ + 0, + 0.225 + ] + }, + "radialaxis": {}, + "angularaxis": {} + } + } +} diff --git a/test/jasmine/assets/drag.js b/test/jasmine/assets/drag.js index d120f291080..72190e4fd43 100644 --- a/test/jasmine/assets/drag.js +++ b/test/jasmine/assets/drag.js @@ -1,3 +1,4 @@ +var isNumeric = require('fast-isnumeric'); var mouseEvent = require('./mouse_event'); var getNodeCoords = require('./get_node_coords'); @@ -6,12 +7,10 @@ var getNodeCoords = require('./get_node_coords'); * optionally specify an edge ('n', 'se', 'w' etc) * to grab it by an edge or corner (otherwise the middle is used) */ -module.exports = function(node, dx, dy, edge) { - +module.exports = function(node, dx, dy, edge, x0, y0) { var coords = getNodeCoords(node, edge); - var fromX = coords.x; - var fromY = coords.y; - + var fromX = isNumeric(x0) ? x0 : coords.x; + var fromY = isNumeric(y0) ? y0 : coords.y; var toX = fromX + dx; var toY = fromY + dy; diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index 74238e593df..d9ae9a75404 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -1270,6 +1270,36 @@ describe('hover on fill', function() { }).then(done); }); + it('should always show one label in the right place (symmetric fill edge case)', function(done) { + var gd = createGraphDiv(); + + Plotly.plot(gd, [{ + x: [6, 7, 8, 7, 6], + y: [5, 4, 5, 6, 5], + fill: 'tonext', + hoveron: 'fills' + }], { + width: 500, + height: 500, + margin: {l: 50, t: 50, r: 50, b: 50} + }) + .then(function() { + return assertLabelsCorrect([200, 200], [73.75, 250], 'trace 0'); + }) + .then(function() { + return Plotly.restyle(gd, { + x: [[6, 7, 8, 7]], + y: [[5, 4, 5, 6]] + }); + }) + .then(function() { + // gives same results w/o closing point + return assertLabelsCorrect([200, 200], [73.75, 250], 'trace 0'); + }) + .catch(fail) + .then(done); + }); + it('should work for scatterternary too', function(done) { var mock = Lib.extendDeep({}, require('@mocks/ternary_fill.json')); var gd = createGraphDiv(); diff --git a/test/jasmine/tests/plot_interact_test.js b/test/jasmine/tests/plot_interact_test.js index 29d9792b684..640b57c8908 100644 --- a/test/jasmine/tests/plot_interact_test.js +++ b/test/jasmine/tests/plot_interact_test.js @@ -515,25 +515,6 @@ describe('Test plot structure', function() { }); }); }); - - describe('polar plots', function() { - var mock = require('@mocks/polar_scatter.json'); - - beforeEach(function(done) { - Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(done); - }); - - it('has as many *mark dot* nodes as there are points', function() { - var nodes = d3.selectAll('path.mark.dot'); - - var Npts = 0; - mock.data.forEach(function(trace) { - Npts += trace.r.length; - }); - - expect(nodes.size()).toEqual(Npts); - }); - }); }); describe('plot svg clip paths', function() { diff --git a/test/jasmine/tests/plots_test.js b/test/jasmine/tests/plots_test.js index a290d30eb93..b155d65a808 100644 --- a/test/jasmine/tests/plots_test.js +++ b/test/jasmine/tests/plots_test.js @@ -5,10 +5,10 @@ var Lib = require('@src/lib'); var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); +var fail = require('../assets/fail_test'); var supplyAllDefaults = require('../assets/supply_defaults'); var failTest = require('../assets/fail_test'); - describe('Test Plots', function() { 'use strict'; @@ -672,6 +672,55 @@ describe('Test Plots', function() { }); }); + describe('Plots.style', function() { + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(destroyGraphDiv); + + it('should call reused style modules only once per graph', function(done) { + var Drawing = require('@src/components/drawing'); + + Plotly.plot(gd, [{ + mode: 'markers', + y: [1, 2, 1] + }, { + type: 'scatterternary', + mode: 'markers', + a: [1, 2, 3], + b: [2, 1, 3], + c: [3, 2, 1] + }, { + type: 'scatterpolar', + mode: 'markers', + r: [1, 2, 3], + theta: [0, 90, 120] + }]) + .then(function() { + expect(gd._fullLayout._modules.length).toBe(3); + + // A routine that gets called inside Scatter.style, + // once per trace. + // + // Start spying on it here, so that calls outside of + // Plots.style are ignored. + spyOn(Drawing, 'pointStyle'); + + return Plots.style(gd); + }) + .then(function() { + // N.B. Drawing.pointStyle would be called 9 times w/o + // some special Plots.style logic. + expect(Drawing.pointStyle).toHaveBeenCalledTimes(3); + }) + .catch(fail) + .then(done); + }); + }); + describe('subplot cleaning logic', function() { var gd; diff --git a/test/jasmine/tests/plotschema_test.js b/test/jasmine/tests/plotschema_test.js index 1d79a6163e5..a8bc35290ba 100644 --- a/test/jasmine/tests/plotschema_test.js +++ b/test/jasmine/tests/plotschema_test.js @@ -11,7 +11,7 @@ var surface = require('@src/traces/surface'); var baseLayoutAttrs = require('@src/plots/layout_attributes'); var cartesianAttrs = require('@src/plots/cartesian').layoutAttributes; var gl3dAttrs = require('@src/plots/gl3d').layoutAttributes; -var polarLayoutAttrs = require('@src/plots/polar/axis_attributes'); +var polarLayoutAttrs = require('@src/plots/polar/legacy/axis_attributes'); var annotationAttrs = require('@src/components/annotations').layoutAttributes; var updatemenuAttrs = require('@src/components/updatemenus').layoutAttributes; @@ -127,7 +127,7 @@ describe('plot schema', function() { it('all subplot objects should contain _isSubplotObj', function() { var IS_SUBPLOT_OBJ = '_isSubplotObj', - astrs = ['xaxis', 'yaxis', 'scene', 'geo', 'ternary', 'mapbox'], + astrs = ['xaxis', 'yaxis', 'scene', 'geo', 'ternary', 'mapbox', 'polar'], cnt = 0; // check if the subplot objects have '_isSubplotObj' @@ -390,7 +390,7 @@ describe('getTraceValObject', function() { }); it('supports polar area attributes', function() { - var areaAttrs = require('@src/plots/polar/area_attributes'); + var areaAttrs = require('@src/plots/polar/legacy/area_attributes'); expect(getTraceValObject({type: 'area'}, ['r'])).toBe(areaAttrs.r); expect(getTraceValObject({type: 'area'}, ['t', 23])).toBe(areaAttrs.t); expect(getTraceValObject({type: 'area'}, ['q'])).toBe(false); diff --git a/test/jasmine/tests/polar_test.js b/test/jasmine/tests/polar_test.js new file mode 100644 index 00000000000..27a5ddb91d1 --- /dev/null +++ b/test/jasmine/tests/polar_test.js @@ -0,0 +1,939 @@ +var Plotly = require('@lib'); +var Lib = require('@src/lib'); +var Polar = require('@src/plots/polar'); +var constants = require('@src/plots/polar/constants'); + +var d3 = require('d3'); +var createGraphDiv = require('../assets/create_graph_div'); +var destroyGraphDiv = require('../assets/destroy_graph_div'); +var fail = require('../assets/fail_test'); +var mouseEvent = require('../assets/mouse_event'); +var click = require('../assets/click'); +var doubleClick = require('../assets/double_click'); +var drag = require('../assets/drag'); +var delay = require('../assets/delay'); + +describe('Test legacy polar plots logs:', function() { + var gd; + + beforeEach(function() { + spyOn(Lib, 'log'); + gd = createGraphDiv(); + }); + + afterEach(destroyGraphDiv); + + var specs = [{ + name: 'legacy polar scatter traces', + data: [{ + r: [1, 2, 3], + t: [1, 2, 3] + }] + }, { + name: 'legacy polar bar traces', + data: [{ + type: 'bar', + r: [1, 2, 3], + t: [1, 2, 3] + }] + }, { + name: 'legacy area traces', + data: [{ + type: 'area', + r: [1, 2, 3], + t: [1, 2, 3] + }] + }]; + + specs.forEach(function(s) { + it('should log deprecation warning on ' + s.name, function(done) { + Plotly.plot(gd, s.data) + .then(function() { + expect(Lib.log).toHaveBeenCalledTimes(1); + expect(Lib.log).toHaveBeenCalledWith('Legacy polar charts are deprecated!'); + }) + .catch(fail) + .then(done); + }); + }); +}); + +describe('Test polar plots defaults:', function() { + var layoutOut; + + function _supply(layoutIn, fullData) { + fullData = fullData || [{ + type: 'scatterpolar', + r: [], + theta: [], + subplot: 'polar' + }]; + + layoutOut = { + font: {color: 'red'}, + _subplots: {polar: ['polar']} + }; + + Polar.supplyLayoutDefaults(layoutIn, layoutOut, fullData); + } + + it('should default *radialaxis.angle* to first sector angle', function() { + _supply({ + polar: { + sector: [45, 135] + } + }); + expect(layoutOut.polar.radialaxis.angle).toBe(45); + }); + + it('should coerce *angularaxis.thetaunit* only for linear angular axes', function() { + _supply({ + polar: { + angularaxis: {thetaunit: 'radians'} + } + }); + expect(layoutOut.polar.angularaxis.thetaunit).toBe('radians'); + + _supply({ + polar: { + angularaxis: { + type: 'category', + thetaunit: 'radians' + } + } + }); + expect(layoutOut.polar.angularaxis.thetaunit).toBeUndefined(); + }); + + it('should not try to autotype visible false traces', function() { + _supply({ + polar: {} + }, [{ + type: 'scatterpolar', + visible: false, + r: ['2017-01-20', '2017-02-10', '2017-03-03'], + theta: ['a', 'b', 'c'], + subplot: 'polar' + }]); + + expect(layoutOut.polar.radialaxis.type).toBe('linear', 'not date'); + expect(layoutOut.polar.angularaxis.type).toBe('linear', 'not category'); + }); + + it('should propagate axis *color* settings', function() { + _supply({ + polar: { + angularaxis: {color: 'red'}, + radialaxis: {color: 'blue'} + } + }); + + expect(layoutOut.polar.angularaxis.linecolor).toBe('red'); + expect(layoutOut.polar.angularaxis.gridcolor).toBe('rgb(255, 153, 153)', 'blend by 60% with bgcolor'); + + expect(layoutOut.polar.radialaxis.titlefont.color).toBe('blue'); + expect(layoutOut.polar.radialaxis.linecolor).toBe('blue'); + expect(layoutOut.polar.radialaxis.gridcolor).toBe('rgb(153, 153, 255)', 'blend by 60% with bgcolor'); + }); + + it('should default *rotation* to 90 when clockwise *direction*', function() { + _supply({ + polar: {} + }); + + expect(layoutOut.polar.angularaxis.direction).toBe('counterclockwise'); + expect(layoutOut.polar.angularaxis.rotation).toBe(0); + + _supply({ + polar: { + angularaxis: {direction: 'clockwise'} + } + }); + + expect(layoutOut.polar.angularaxis.direction).toBe('clockwise'); + expect(layoutOut.polar.angularaxis.rotation).toBe(90); + }); + + it('(for now) should log message when detecting *date* angular axes and fallback to *linear*', function() { + spyOn(Lib, 'log'); + + _supply({}, [{ + type: 'scatterpolar', + r: [1, 2], + theta: ['2017-01-01', '2018-01-01'], + visible: true, + subplot: 'polar' + }]); + + expect(Lib.log).toHaveBeenCalledWith('Polar plots do not support date angular axes yet.'); + expect(layoutOut.polar.angularaxis.type).toBe('linear'); + }); +}); + +describe('Test relayout on polar subplots:', function() { + afterEach(destroyGraphDiv); + + it('should be able to reorder axis layers when relayout\'ing *layer*', function(done) { + var gd = createGraphDiv(); + var fig = Lib.extendDeep({}, require('@mocks/polar_line.json')); + var dflt = constants.layerNames; + + function _assert(expected) { + var actual = d3.selectAll('g.polar > .polarsublayer'); + + expect(actual.size()).toBe(expected.length, '# of layer'); + + actual.each(function(d, i) { + var className = d3.select(this) + .attr('class') + .split('polarsublayer ')[1]; + + expect(className).toBe(expected[i], 'layer ' + i); + }); + } + + Plotly.plot(gd, fig).then(function() { + _assert(dflt); + return Plotly.relayout(gd, 'polar.radialaxis.layer', 'below traces'); + }) + .then(function() { + _assert([ + 'draglayer', 'plotbg', 'backplot', 'angular-grid', 'radial-grid', + 'radial-axis', 'radial-line', + 'frontplot', + 'angular-axis', 'angular-line' + ]); + return Plotly.relayout(gd, 'polar.angularaxis.layer', 'below traces'); + }) + .then(function() { + _assert([ + 'draglayer', 'plotbg', 'backplot', 'angular-grid', 'radial-grid', + 'angular-axis', + 'radial-axis', + 'angular-line', + 'radial-line', + 'frontplot' + ]); + return Plotly.relayout(gd, 'polar.radialaxis.layer', 'above traces'); + }) + .then(function() { + _assert([ + 'draglayer', 'plotbg', 'backplot', 'angular-grid', 'radial-grid', + 'angular-axis', 'angular-line', + 'frontplot', + 'radial-axis', 'radial-line' + ]); + return Plotly.relayout(gd, 'polar.angularaxis.layer', null); + }) + .then(function() { + _assert(dflt); + }) + .catch(fail) + .then(done); + }); + + it('should be able to relayout axis types', function(done) { + var gd = createGraphDiv(); + var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json')); + + Plotly.plot(gd, fig).then(function() { + expect(gd._fullLayout.polar._subplot.viewInitial['radialaxis.range']) + .toBeCloseToArray([0, 11.225]); + expect(gd._fullLayout.polar.radialaxis.range) + .toBeCloseToArray([0, 11.225]); + + return Plotly.relayout(gd, 'polar.radialaxis.type', 'log'); + }) + .then(function() { + expect(gd._fullLayout.polar._subplot.viewInitial['radialaxis.range']) + .toBeCloseToArray([-0.53, 1.158]); + expect(gd._fullLayout.polar.radialaxis.range) + .toBeCloseToArray([-0.53, 1.158]); + + return Plotly.relayout(gd, 'polar.radialaxis.type', 'linear'); + }) + .then(function() { + expect(gd._fullLayout.polar._subplot.viewInitial['radialaxis.range']) + .toBeCloseToArray([0, 11.225]); + expect(gd._fullLayout.polar.radialaxis.range) + .toBeCloseToArray([0, 11.225]); + }) + .catch(fail) + .then(done); + }); + + it('should be propagate angular settings down to tick labels', function(done) { + var gd = createGraphDiv(); + var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json')); + var pos0 = []; + var pos1 = []; + + Plotly.plot(gd, fig).then(function() { + d3.selectAll('.angulartick> text').each(function() { + var tx = d3.select(this); + pos0.push([tx.attr('x'), tx.attr('y')]); + }); + return Plotly.relayout(gd, 'polar.angularaxis.rotation', 90); + }) + .then(function() { + d3.selectAll('.angulartick> text').each(function() { + var tx = d3.select(this); + pos1.push([tx.attr('x'), tx.attr('y')]); + }); + + // if they're the same, the tick label position did not update + expect(pos1).not.toBeCloseTo2DArray(pos0); + }) + .catch(fail) + .then(done); + }); + + it('should be able to relayout angular ticks layout', function(done) { + var gd = createGraphDiv(); + var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json')); + + function check(cnt, expected) { + var ticks = d3.selectAll('path.angulartick'); + + expect(ticks.size()).toBe(cnt, '# of ticks'); + ticks.each(function() { + expect(d3.select(this).attr('d')).toBe(expected); + }); + } + + Plotly.plot(gd, fig).then(function() { + check(8, 'M1.5,0h5'); + return Plotly.relayout(gd, 'polar.angularaxis.ticks', 'inside'); + }) + .then(function() { + check(8, 'M-1.5,0h-5'); + return Plotly.relayout(gd, 'polar.angularaxis.ticks', 'outside'); + }) + .then(function() { + check(8, 'M1.5,0h5'); + return Plotly.relayout(gd, 'polar.angularaxis.ticks', ''); + }) + .then(function() { + check(0); + return Plotly.relayout(gd, 'polar.angularaxis.ticks', 'inside'); + }) + .then(function() { + check(8, 'M-1.5,0h-5'); + }) + .catch(fail) + .then(done); + }); + + it('should be able to toggle axis features', function(done) { + var gd = createGraphDiv(); + var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json')); + + function assertCnt(selector, expected, msg) { + var sel = d3.select(gd).selectAll(selector); + expect(sel.size()).toBe(expected, msg); + } + + function assertDisplay(selector, expected, msg) { + var sel = d3.select(gd).selectAll(selector); + + if(!sel.size()) fail(selector + ' not found'); + + sel.each(function() { + expect(d3.select(this).attr('display')).toBe(expected, msg); + }); + } + + function toggle(astr, vals, exps, selector, fn) { + return Plotly.relayout(gd, astr, vals[0]).then(function() { + fn(selector, exps[0], astr + ' ' + vals[0]); + return Plotly.relayout(gd, astr, vals[1]); + }) + .then(function() { + fn(selector, exps[1], astr + ' ' + vals[1]); + return Plotly.relayout(gd, astr, vals[0]); + }) + .then(function() { + fn(selector, exps[0], astr + ' ' + vals[0]); + }); + } + + Plotly.plot(gd, fig).then(function() { + return toggle( + 'polar.radialaxis.showline', + [true, false], [null, 'none'], + '.radial-line > line', assertDisplay + ); + }) + .then(function() { + return toggle( + 'polar.radialaxis.showgrid', + [true, false], [null, 'none'], + '.radial-grid', assertDisplay + ); + }) + .then(function() { + return toggle( + 'polar.radialaxis.showticklabels', + [true, false], [6, 0], + '.radial-axis > .xtick > text', assertCnt + ); + }) + .then(function() { + return toggle( + 'polar.radialaxis.ticks', + ['outside', ''], [6, 0], + '.radial-axis > path.xtick', assertCnt + ); + }) + .then(function() { + return toggle( + 'polar.angularaxis.showline', + [true, false], [null, 'none'], + '.angular-line > path', assertDisplay + ); + }) + .then(function() { + return toggle( + 'polar.angularaxis.showgrid', + [true, false], [8, 0], + '.angular-grid > .angular > path', assertCnt + ); + }) + .then(function() { + return toggle( + 'polar.angularaxis.showticklabels', + [true, false], [8, 0], + '.angular-axis > .angulartick > text', assertCnt + ); + }) + .then(function() { + return toggle( + 'polar.angularaxis.ticks', + ['outside', ''], [8, 0], + '.angular-axis > path.angulartick', assertCnt + ); + }) + .catch(fail) + .then(done); + }); + + it('should be able to restyle radial axis title', function(done) { + var gd = createGraphDiv(); + var lastBBox; + + function assertTitle(content, didBBoxChanged) { + var radialAxisTitle = d3.select('g.g-polartitle'); + var txt = radialAxisTitle.select('text'); + var bb = radialAxisTitle.node().getBBox(); + var newBBox = [bb.x, bb.y, bb.width, bb.height]; + + if(content === '') { + expect(txt.size()).toBe(0, 'cleared '); + } else { + expect(txt.text()).toBe(content, 'radial axis title'); + } + + expect(newBBox).negateIf(didBBoxChanged).toEqual(lastBBox, 'did bbox change'); + lastBBox = newBBox; + } + + Plotly.plot(gd, [{ + type: 'scatterpolar', + r: [1, 2, 3], + theta: [10, 20, 30] + }], { + polar: { + radialaxis: {title: 'yo'} + } + }) + .then(function() { + assertTitle('yo', true); + return Plotly.relayout(gd, 'polar.radialaxis.title', ''); + }) + .then(function() { + assertTitle('', true); + return Plotly.relayout(gd, 'polar.radialaxis.title', 'yo2'); + }) + .then(function() { + assertTitle('yo2', true); + return Plotly.relayout(gd, 'polar.radialaxis.ticklen', 20); + }) + .then(function() { + assertTitle('yo2', true); + return Plotly.relayout(gd, 'polar.radialaxis.titlefont.color', 'red'); + }) + .then(function() { + assertTitle('yo2', false); + return Plotly.relayout(gd, 'title', 'dummy'); + }) + .then(function() { + assertTitle('yo2', false); + }) + .catch(fail) + .then(done); + }); + + it('should clean up its framework, clip paths and info layers when getting deleted', function(done) { + var gd = createGraphDiv(); + var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json')); + var traces = Lib.extendDeep([], fig.data); + var inds = traces.map(function(_, i) { return i; }); + + function _assert(exp) { + expect(d3.selectAll('g.polar').size()).toBe(exp.subplot, '# subplot layer'); + expect(d3.selectAll('g.g-polartitle').size()).toBe(exp.rtitle, '# radial title'); + + var clipCnt = 0; + d3.selectAll('clipPath').each(function() { + if(/polar-circle$/.test(this.id)) clipCnt++; + }); + expect(clipCnt).toBe(exp.clip, '# clip paths'); + } + + Plotly.plot(gd, fig).then(function() { + _assert({subplot: 1, clip: 1, rtitle: 1}); + + return Plotly.deleteTraces(gd, inds); + }) + .then(function() { + _assert({subplot: 0, clip: 0, rtitle: 0}); + + return Plotly.addTraces(gd, traces); + }) + .then(function() { + _assert({subplot: 1, clip: 1, rtitle: 1}); + }) + .catch(fail) + .then(done); + }); +}); + +describe('Test polar interactions:', function() { + var gd; + var eventData; + var eventCnts; + + var eventNames = [ + 'plotly_hover', 'plotly_unhover', + 'plotly_click', 'plotly_doubleclick', + 'plotly_relayout' + ]; + + beforeEach(function() { + eventData = ''; + eventCnts = {}; + gd = createGraphDiv(); + }); + + afterEach(destroyGraphDiv); + + function _plot(fig) { + return Plotly.plot(gd, fig).then(function() { + eventNames.forEach(function(k) { + eventCnts[k] = 0; + gd.on(k, function(d) { + eventData = d; + eventCnts[k]++; + Lib.clearThrottle(); + }); + }); + }); + } + + function assertEventPointData(expected, msg) { + var actual = eventData.points || []; + + expect(actual.length) + .toBe(expected.length, msg + ' same number of pts'); + + expected.forEach(function(e, i) { + var a = actual[i]; + var m = msg + ' (pt ' + i + ')'; + + for(var k in e) { + expect(a[k]).toBeCloseTo(e[k], 1, m + ' ' + k); + } + }); + } + + function assertEventCnt(expected, msg) { + eventNames.forEach(function(k) { + var m = msg + ' event cnt for ' + k; + + if(k in expected) { + expect(eventCnts[k]).toBe(expected[k], m); + } else { + expect(eventCnts[k]).toBe(0, m); + } + }); + } + + function _hover(pos) { + eventData = ''; + mouseEvent('mousemove', pos[0], pos[1]); + } + + function _unhover(pos) { + eventData = ''; + mouseEvent('mouseout', pos[0], pos[1]); + } + + function _click(pos, opts) { + eventData = ''; + gd._mouseDownTime = 0; + click(pos[0], pos[1], opts); + } + + function _doubleClick(pos) { + gd._mouseDownTime = 0; + eventData = ''; + return doubleClick(pos[0], pos[1]); + } + + var modClickOpts = { + altKey: true, + ctrlKey: true, // this makes it effectively into a right-click + metaKey: true, + shiftKey: true, + button: 0, + cancelContext: true + }; + + var rightClickOpts = { + altKey: false, + ctrlKey: false, + metaKey: false, + shiftKey: false, + button: 2, + cancelContext: true + }; + + it('should trigger hover/unhover/click/doubleclick events', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json')); + var ptPos = [250, 200]; + var blankPos = [200, 120]; + var marginPos = [20, 20]; + + function _assert(ptExpectation, cntExpecation, msg) { + if(Array.isArray(ptExpectation)) { + assertEventPointData(ptExpectation, msg); + } else { + expect(eventData).toBe(ptExpectation, msg); + } + assertEventCnt(cntExpecation, msg); + } + + _plot(fig) + .then(function() { _hover(ptPos); }) + .then(function() { + _assert([{ + r: 3.26, + theta: 68.08 + }], { + plotly_hover: 1 + }, 'after hover on pt'); + }) + .then(function() { _unhover(blankPos);}) + .then(function() { + _assert([{ + r: 3.26, + theta: 68.08 + }], { + plotly_hover: 1, + plotly_unhover: 1 + }, 'after unhover off pt'); + }) + .then(function() { _hover(marginPos);}) + .then(function() { + _assert('', { + plotly_hover: 1, + plotly_unhover: 1, + }, 'after hovering in margin'); + }) + .then(function() { _click(ptPos); }) + .then(function() { + _assert([{ + r: 3.26, + theta: 68.08 + }], { + plotly_hover: 2, + plotly_unhover: 1, + plotly_click: 1 + }, 'after click'); + }) + .then(function() { return _doubleClick(ptPos); }) + .then(function() { + assertEventCnt({ + plotly_hover: 2, + plotly_unhover: 1, + plotly_click: 3, + plotly_doubleclick: 1, + plotly_relayout: 1 + }, 'after doubleclick'); + }) + .then(function() { _click(ptPos, modClickOpts); }) + .then(function() { + _assert([{ + r: 3.26, + theta: 68.08 + }], { + plotly_hover: 2, + plotly_unhover: 1, + plotly_click: 4, + plotly_doubleclick: 1, + plotly_relayout: 1 + }, 'after modified click'); + }) + .then(function() { _click(ptPos, rightClickOpts); }) + .then(function() { + _assert([{ + r: 3.26, + theta: 68.08 + }], { + plotly_hover: 2, + plotly_unhover: 1, + plotly_click: 5, + plotly_doubleclick: 1, + plotly_relayout: 1 + }, 'after right click'); + }) + .catch(fail) + .then(done); + }); + + it('should response to drag interactions on plot area', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json')); + + // adjust margins so that middle of plot area is at 300x300 + // with its middle at [200,200] + fig.layout.width = 400; + fig.layout.height = 400; + fig.layout.margin = {l: 50, t: 50, b: 50, r: 50}; + + var mid = [200, 200]; + var relayoutNumber = 0; + var resetNumber = 0; + + function _drag(p0, dp) { + var node = d3.select('.polar > .draglayer > .maindrag').node(); + return drag(node, dp[0], dp[1], null, p0[0], p0[1]); + } + + function _assertRange(rng, msg) { + expect(gd._fullLayout.polar.radialaxis.range).toBeCloseToArray(rng, 1, msg); + } + + function _assertDrag(rng, msg) { + relayoutNumber++; + _assertRange(rng, msg); + + if(eventCnts.plotly_relayout === relayoutNumber) { + expect(eventData['polar.radialaxis.range']) + .toBeCloseToArray(rng, 1, msg + '- event data'); + } else { + fail('incorrect number of plotly_relayout events triggered - ' + msg); + } + } + + function _assertBase(extra) { + var msg = 'base range' + (extra ? ' ' + extra : ''); + _assertRange([0, 11.1], msg); + } + + function _reset() { + return _doubleClick(mid).then(function() { + relayoutNumber++; + resetNumber++; + + var extra = '(reset ' + resetNumber + ')'; + _assertBase(extra); + expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra); + }); + } + + _plot(fig) + .then(_assertBase) + .then(function() { return _drag(mid, [50, 50]); }) + .then(function() { + _assertDrag([0, 5.24], 'from center move toward bottom-right'); + }) + .then(_reset) + .then(function() { return _drag(mid, [-50, -50]); }) + .then(function() { + _assertDrag([0, 5.24], 'from center move toward top-left'); + }) + .then(_reset) + .then(function() { return _drag([mid[0] + 30, mid[0] - 30], [50, -50]); }) + .then(function() { + _assertDrag([3.1, 8.4], 'from quadrant #1 move top-right'); + }) + .then(_reset) + .then(function() { return _drag([345, 200], [-50, 0]); }) + .then(function() { + _assertDrag([7.0, 11.1], 'from right edge move left'); + }) + .then(_reset) + .then(function() { return _drag(mid, [10, 10]);}) + .then(function() { _assertBase('from center to not far enough'); }) + .then(function() { return _drag([mid[0] + 30, mid[0] - 30], [-10, 0]);}) + .then(function() { _assertBase('from quadrant #1 to not far enough'); }) + .then(function() { return _drag([345, 200], [-10, 0]);}) + .then(function() { _assertBase('from right edge to not far enough'); }) + .then(function() { + expect(eventCnts.plotly_relayout) + .toBe(relayoutNumber, 'no new relayout events after *not far enough* cases'); + }) + .catch(fail) + .then(done); + }); + + it('should response to drag interactions on radial drag area', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json')); + + // adjust margins so that middle of plot area is at 300x300 + // with its middle at [200,200] + fig.layout.width = 400; + fig.layout.height = 400; + fig.layout.margin = {l: 50, t: 50, b: 50, r: 50}; + + var dragPos0 = [375, 200]; + var resetNumber = 0; + + // use 'special' drag method - as we need two mousemove events + // to activate the radial drag mode + function _drag(p0, dp) { + var node = d3.select('.polar > .draglayer > .radialdrag').node(); + var p1 = [p0[0] + dp[0] / 2, p0[1] + dp[1] / 2]; + var p2 = [p0[0] + dp[0], p0[1] + dp[1]]; + + mouseEvent('mousemove', p0[0], p0[1], {element: node}); + mouseEvent('mousedown', p0[0], p0[1], {element: node}); + + return delay(250)() + .then(function() { mouseEvent('mousemove', p1[0], p1[1], {element: document}); }) + .then(delay(50)) + .then(function() { mouseEvent('mousemove', p2[0], p2[1], {element: document}); }) + .then(function() { mouseEvent('mouseup', p2[0], p2[1], {element: document}); }) + .then(delay(50)); + } + + function _assert(rng, angle, evtRng1, evtAngle, msg) { + expect(gd._fullLayout.polar.radialaxis.range) + .toBeCloseToArray(rng, 1, msg + ' - range'); + expect(gd._fullLayout.polar.radialaxis.angle) + .toBeCloseTo(angle, 1, msg + ' - angle'); + + if(evtRng1 !== null) { + expect(eventData['polar.radialaxis.range[1]']) + .toBeCloseTo(evtRng1, 1, msg + ' - range[1] event data'); + } + if(evtAngle !== null) { + expect(eventData['polar.radialaxis.angle']) + .toBeCloseTo(evtAngle, 1, msg + ' - angle event data'); + } + } + + function _assertBase(extra) { + extra = extra ? ' ' + extra : ''; + _assert([0, 11.1], 0, null, null, 'base' + extra); + } + + function _reset() { + return _doubleClick([200, 200]).then(function() { + resetNumber++; + + var extra = '(reset ' + resetNumber + ')'; + _assertBase(extra); + expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra); + }); + } + + _plot(fig) + .then(_assertBase) + .then(function() { return _drag(dragPos0, [-50, 0]); }) + .then(function() { + _assert([0, 13.9], 0, 13.9, null, 'move inward'); + }) + .then(_reset) + .then(function() { return _drag(dragPos0, [50, 0]); }) + .then(function() { + _assert([0, 8.33], 0, 8.33, null, 'move outward'); + }) + .then(_reset) + .then(function() { return _drag(dragPos0, [0, -50]); }) + .then(function() { + _assert([0, 11.1], 15.94, null, 15.94, 'move counterclockwise'); + }) + .then(_reset) + .then(function() { return _drag(dragPos0, [0, 50]); }) + .then(function() { + _assert([0, 11.1], -15.94, null, -15.94, 'move clockwise'); + }) + .then(_reset) + .then(function() { + expect(eventCnts.plotly_relayout).toBe(8, 'total # of relayout events'); + }) + .catch(fail) + .then(done); + }); + + it('should response to drag interactions on angular drag area', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json')); + + // adjust margins so that middle of plot area is at 300x300 + // with its middle at [200,200] + fig.layout.width = 400; + fig.layout.height = 400; + fig.layout.margin = {l: 50, t: 50, b: 50, r: 50}; + + var dragPos0 = [350, 150]; + var resetNumber = 0; + + function _drag(p0, dp) { + var node = d3.select('.polar > .draglayer > .angulardrag').node(); + return drag(node, dp[0], dp[1], null, p0[0], p0[1]); + } + + function _assert(rot, msg, noEvent) { + expect(gd._fullLayout.polar.angularaxis.rotation) + .toBeCloseTo(rot, 1, msg + ' - rotation'); + if(!noEvent) { + expect(eventData['polar.angularaxis.rotation']) + .toBeCloseTo(rot, 1, msg + ' - rotation event data'); + } + } + + function _assertBase(extra) { + extra = extra ? ' ' + extra : ''; + _assert(0, 'base' + extra, true); + } + + function _reset() { + return _doubleClick([200, 200]).then(function() { + resetNumber++; + + var extra = '(reset ' + resetNumber + ')'; + _assertBase(extra); + expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra); + }); + } + + _plot(fig) + .then(_assertBase) + .then(function() { return _drag(dragPos0, [-20, -20]); }) + .then(function() { + _assert(9.9, 'move counterclockwise'); + }) + .then(_reset) + .then(function() { return _drag(dragPos0, [20, 20]); }) + .then(function() { + _assert(-8.4, 'move clockwise'); + }) + .then(_reset) + .then(function() { + expect(eventCnts.plotly_relayout).toBe(4, 'total # of relayout events'); + }) + .catch(fail) + .then(done); + }); +}); diff --git a/test/jasmine/tests/register_test.js b/test/jasmine/tests/register_test.js index 2e436659878..291561dd45c 100644 --- a/test/jasmine/tests/register_test.js +++ b/test/jasmine/tests/register_test.js @@ -48,7 +48,6 @@ describe('Test Registry', function() { it('should return false for types it doesn\'t know', function() { expect(Registry.getModule('notatype')).toBe(false); expect(Registry.getModule({type: 'notatype'})).toBe(false); - expect(Registry.getModule({type: 'newtype', r: 'this is polar'})).toBe(false); }); it('should find the categories for this type', function() { diff --git a/test/jasmine/tests/scatterpolar_test.js b/test/jasmine/tests/scatterpolar_test.js new file mode 100644 index 00000000000..1b46f59d9c4 --- /dev/null +++ b/test/jasmine/tests/scatterpolar_test.js @@ -0,0 +1,118 @@ +var Plotly = require('@lib'); +var Lib = require('@src/lib'); +var ScatterPolar = require('@src/traces/scatterpolar'); + +var createGraphDiv = require('../assets/create_graph_div'); +var destroyGraphDiv = require('../assets/destroy_graph_div'); +var fail = require('../assets/fail_test'); +var mouseEvent = require('../assets/mouse_event'); + +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; + +describe('Test scatterpolar trace defaults:', function() { + var traceOut; + + function _supply(traceIn, layout) { + traceOut = {}; + ScatterPolar.supplyDefaults(traceIn, traceOut, '#444', layout || {}); + } + + it('should truncate *r* when longer than *theta*', function() { + _supply({ + r: [1, 2, 3, 4, 5], + theta: [1, 2, 3] + }); + + expect(traceOut.r).toEqual([1, 2, 3]); + expect(traceOut.theta).toEqual([1, 2, 3]); + }); + + it('should truncate *theta* when longer than *r*', function() { + _supply({ + r: [1, 2, 3], + theta: [1, 2, 3, 4, 5] + }); + + expect(traceOut.r).toEqual([1, 2, 3]); + expect(traceOut.theta).toEqual([1, 2, 3]); + }); +}); + +describe('Test scatterpolar hover:', function() { + var gd; + + afterEach(destroyGraphDiv); + + function run(specs) { + gd = createGraphDiv(); + + var fig = Lib.extendDeep( + {width: 700, height: 500}, + specs.mock || require('@mocks/polar_scatter.json') + ); + + if(specs.patch) { + fig = specs.patch(fig); + } + + var pos = specs.pos || [200, 200]; + + return Plotly.plot(gd, fig).then(function() { + mouseEvent('mousemove', pos[0], pos[1]); + assertHoverLabelContent(specs); + }); + } + + [{ + desc: 'base', + nums: 'r: 4.022892\nθ: 128.342°', + name: 'Trial 3' + }, { + desc: '(no labels - out of sector)', + patch: function(fig) { + fig.layout.polar.sector = [15, 75]; + return fig; + }, + pos: [144, 350], + nums: '', + name: '' + }, { + desc: 'on a `thetaunit: radians` polar subplot', + patch: function(fig) { + fig.layout.polar.angularaxis.thetaunit = 'radians'; + return fig; + }, + nums: 'r: 4.022892\nθ: 2.239991', + name: 'Trial 3' + }, { + desc: 'on log radial axis', + patch: function(fig) { + fig.layout.polar.radialaxis.type = 'log'; + return fig; + }, + nums: 'r: 1.108937\nθ: 115.4969°', + name: 'Trial 3' + }, { + desc: 'on fills', + mock: require('@mocks/polar_fills.json'), + pos: [300, 230], + nums: 'trace 2', + name: '' + }, { + desc: 'on category axes', + mock: require('@mocks/polar_categories.json'), + patch: function(fig) { + fig.data.forEach(function(t) { t.fill = 'none'; }); + return fig; + }, + pos: [465, 90], + nums: 'r: 4\nθ: d', + name: 'angular cate...' + }] + .forEach(function(specs) { + it('should generate correct hover labels ' + specs.desc, function(done) { + run(specs).catch(fail).then(done); + }); + }); +}); diff --git a/test/jasmine/tests/scatterternary_test.js b/test/jasmine/tests/scatterternary_test.js index 4396be60261..bbd1d6b7003 100644 --- a/test/jasmine/tests/scatterternary_test.js +++ b/test/jasmine/tests/scatterternary_test.js @@ -332,7 +332,8 @@ describe('scatterternary hover', function() { cd: cd[0], trace: cd[0][0].trace, xa: ternary.xaxis, - ya: ternary.yaxis + ya: ternary.yaxis, + subplot: ternary }; return ScatterTernary.hoverPoints(pointData, xval, yval, hovermode); diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index 9c1e33975df..49efc4a33cd 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -867,6 +867,44 @@ describe('Test select box and lasso per trace:', function() { .then(done); }, LONG_TIMEOUT_INTERVAL); + it('should work on scatterpolar traces', function(done) { + var assertPoints = makeAssertPoints(['r', 'theta']); + var assertSelectedPoints = makeAssertSelectedPoints(); + + var fig = Lib.extendDeep({}, require('@mocks/polar_subplots')); + fig.layout.width = 800; + fig.layout.dragmode = 'select'; + addInvisible(fig); + + Plotly.plot(gd, fig).then(function() { + return _run( + [[150, 150], [350, 250]], + function() { + assertPoints([[1, 0], [2, 45]]); + assertSelectedPoints({0: [0, 1]}); + }, + [200, 200], + BOXEVENTS, 'scatterpolar select' + ); + }) + .then(function() { + return Plotly.relayout(gd, 'dragmode', 'lasso'); + }) + .then(function() { + return _run( + [[150, 150], [350, 150], [350, 250], [150, 250], [150, 150]], + function() { + assertPoints([[1, 0], [2, 45]]); + assertSelectedPoints({0: [0, 1]}); + }, + [200, 200], + LASSOEVENTS, 'scatterpolar lasso' + ); + }) + .catch(fail) + .then(done); + }); + it('should work on choropleth traces', function(done) { var assertPoints = makeAssertPoints(['location', 'z']); var assertSelectedPoints = makeAssertSelectedPoints(); diff --git a/test/jasmine/tests/ternary_test.js b/test/jasmine/tests/ternary_test.js index 561d94c530f..c8e3ccc21be 100644 --- a/test/jasmine/tests/ternary_test.js +++ b/test/jasmine/tests/ternary_test.js @@ -59,12 +59,20 @@ describe('ternary plots', function() { }); it('should be able to delete and add traces', function(done) { + function checkTitles(cnt) { + expect(d3.selectAll('.g-atitle').size()).toBe(cnt, 'aaxis title'); + expect(d3.selectAll('.g-btitle').size()).toBe(cnt, 'baxis title'); + expect(d3.selectAll('.g-ctitle').size()).toBe(cnt, 'caxis title'); + } + expect(countTernarySubplot()).toEqual(1); expect(countTraces('scatter')).toEqual(1); + checkTitles(1); Plotly.deleteTraces(gd, [0]).then(function() { expect(countTernarySubplot()).toEqual(0); expect(countTraces('scatter')).toEqual(0); + checkTitles(0); var trace = Lib.extendDeep({}, mock.data[0]); @@ -72,6 +80,7 @@ describe('ternary plots', function() { }).then(function() { expect(countTernarySubplot()).toEqual(1); expect(countTraces('scatter')).toEqual(1); + checkTitles(1); var trace = Lib.extendDeep({}, mock.data[0]); @@ -79,11 +88,13 @@ describe('ternary plots', function() { }).then(function() { expect(countTernarySubplot()).toEqual(1); expect(countTraces('scatter')).toEqual(2); + checkTitles(1); return Plotly.deleteTraces(gd, [0]); }).then(function() { expect(countTernarySubplot()).toEqual(1); expect(countTraces('scatter')).toEqual(1); + checkTitles(1); done(); }); @@ -338,6 +349,39 @@ describe('ternary plots', function() { .then(done); }); + it('should be able to relayout axis tickfont attributes', function(done) { + var gd = createGraphDiv(); + var fig = Lib.extendDeep({}, require('@mocks/ternary_simple.json')); + + function _assert(family, color, size) { + var tick = d3.select('g.aaxis > g.ytick > text').node(); + + expect(tick.style['font-family']).toBe(family, 'font family'); + expect(parseFloat(tick.style['font-size'])).toBe(size, 'font size'); + expect(tick.style.fill).toBe(color, 'font color'); + } + + Plotly.plot(gd, fig).then(function() { + _assert('"Open Sans", verdana, arial, sans-serif', 'rgb(204, 204, 204)', 12); + + return Plotly.relayout(gd, 'ternary.aaxis.tickfont.size', 5); + }) + .then(function() { + _assert('"Open Sans", verdana, arial, sans-serif', 'rgb(204, 204, 204)', 5); + + return Plotly.relayout(gd, 'ternary.aaxis.tickfont', { + family: 'Roboto', + color: 'red', + size: 20 + }); + }) + .then(function() { + _assert('Roboto', 'rgb(255, 0, 0)', 20); + }) + .catch(fail) + .then(done); + }); + function countTernarySubplot() { return d3.selectAll('.ternary').size(); }