diff --git a/src/components/colorbar/connect.js b/src/components/colorbar/connect.js deleted file mode 100644 index 7afd261873b..00000000000 --- a/src/components/colorbar/connect.js +++ /dev/null @@ -1,66 +0,0 @@ -/** -* Copyright 2012-2019, 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 drawColorbar = require('./draw'); -var flipScale = require('../colorscale/helpers').flipScale; - -/** - * connectColorbar: create a colorbar from a trace, using its module to - * describe the connection. - * - * @param {DOM element} gd - * - * @param {Array} cd - * calcdata entry for this trace. cd[0].trace is the trace itself, and the - * colorbar object will be stashed in cd[0].t.cb - * - * @param {object|function} moduleOpts - * may be a function(gd, cd) to override the standard handling below. If - * an object, should have these keys: - * @param {Optional(string)} moduleOpts.container - * name of the container inside the trace where the colorbar and colorscale - * attributes live (ie 'marker', 'line') - omit if they're at the trace root. - * @param {string} moduleOpts.min - * name of the attribute holding the value of the minimum color - * @param {string} moduleOpts.max - * name of the attribute holding the value of the maximum color - * @param {Optional(string)} moduleOpts.vals - * name of the attribute holding the (numeric) color data - * used only if min/max fail. May be omitted if these are always - * pre-calculated. - */ -module.exports = function connectColorbar(gd, cd, moduleOpts) { - if(typeof moduleOpts === 'function') return moduleOpts(gd, cd); - - var trace = cd[0].trace; - var cbId = 'cb' + trace.uid; - moduleOpts = Array.isArray(moduleOpts) ? moduleOpts : [moduleOpts]; - - for(var i = 0; i < moduleOpts.length; i++) { - var containerName = moduleOpts[i].container; - - var container = containerName ? trace[containerName] : trace; - - gd._fullLayout._infolayer.selectAll('.' + cbId).remove(); - if(!container || !container.showscale) continue; - - var cb = cd[0].t.cb = drawColorbar(gd, cbId); - - var scl = container.reversescale ? - flipScale(container.colorscale) : - container.colorscale; - - cb.fillgradient(scl) - .zrange([container[moduleOpts[i].min], container[moduleOpts[i].max]]) - .options(container.colorbar)(); - - return; - } -}; diff --git a/src/components/colorbar/draw.js b/src/components/colorbar/draw.js index 6354ab471dd..95b9660b447 100644 --- a/src/components/colorbar/draw.js +++ b/src/components/colorbar/draw.js @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var d3 = require('d3'); @@ -23,661 +22,699 @@ var Drawing = require('../drawing'); var Color = require('../color'); var Titles = require('../titles'); var svgTextUtils = require('../../lib/svg_text_utils'); -var alignmentConstants = require('../../constants/alignment'); -var LINE_SPACING = alignmentConstants.LINE_SPACING; -var FROM_TL = alignmentConstants.FROM_TL; -var FROM_BR = alignmentConstants.FROM_BR; +var flipScale = require('../colorscale/helpers').flipScale; var handleAxisDefaults = require('../../plots/cartesian/axis_defaults'); var handleAxisPositionDefaults = require('../../plots/cartesian/position_defaults'); var axisLayoutAttrs = require('../../plots/cartesian/layout_attributes'); -var attributes = require('./attributes'); +var alignmentConstants = require('../../constants/alignment'); +var LINE_SPACING = alignmentConstants.LINE_SPACING; +var FROM_TL = alignmentConstants.FROM_TL; +var FROM_BR = alignmentConstants.FROM_BR; + var cn = require('./constants').cn; -module.exports = function draw(gd, id) { - // opts: options object, containing everything from attributes - // plus a few others that are the equivalent of the colorbar "data" - var opts = {}; - for(var k in attributes) { - opts[k] = null; - } - // fillcolor can be a d3 scale, domain is z values, range is colors - // or leave it out for no fill, - // or set to a string constant for single-color fill - opts.fillcolor = null; - // line.color has the same options as fillcolor - opts.line = {color: null, width: null, dash: null}; - // levels of lines to draw. - // note that this DOES NOT determine the extent of the bar - // that's given by the domain of fillcolor - // (or line.color if no fillcolor domain) - opts.levels = {start: null, end: null, size: null}; - // separate fill levels (for example, heatmap coloring of a - // contour map) if this is omitted, fillcolors will be - // evaluated halfway between levels - opts.filllevels = null; - // for continuous colorscales: fill with a gradient instead of explicit levels - // value should be the colorscale [[0, c0], [v1, c1], ..., [1, cEnd]] - opts.fillgradient = null; - // when using a gradient, we need the data range specified separately - opts.zrange = null; - - function component() { - var fullLayout = gd._fullLayout; - var gs = fullLayout._size; - if((typeof opts.fillcolor !== 'function') && - (typeof opts.line.color !== 'function') && - !opts.fillgradient) { - fullLayout._infolayer.selectAll('g.' + id).remove(); - return; +function draw(gd) { + var fullLayout = gd._fullLayout; + + var colorBars = fullLayout._infolayer + .selectAll('g.' + cn.colorbar) + .data(makeColorBarData(gd), function(opts) { return opts._id; }); + + colorBars.enter().append('g') + .attr('class', function(opts) { return opts._id; }) + .classed(cn.colorbar, true); + + colorBars.each(function(opts) { + var g = d3.select(this); + + Lib.ensureSingle(g, 'rect', cn.cbbg); + Lib.ensureSingle(g, 'g', cn.cbfills); + Lib.ensureSingle(g, 'g', cn.cblines); + Lib.ensureSingle(g, 'g', cn.cbaxis, function(s) { s.classed(cn.crisp, true); }); + Lib.ensureSingle(g, 'g', cn.cbtitleunshift, function(s) { s.append('g').classed(cn.cbtitle, true); }); + Lib.ensureSingle(g, 'rect', cn.cboutline); + + var done = drawColorBar(g, opts, gd); + if(done && done.then) (gd._promises || []).push(done); + + if(gd._context.edits.colorbarPosition) { + makeEditable(g, opts, gd); } - var zrange = opts.zrange || (d3.extent(((typeof opts.fillcolor === 'function') ? - opts.fillcolor : opts.line.color).domain())); - var linelevels = []; - var filllevels = []; - var linecolormap = typeof opts.line.color === 'function' ? - opts.line.color : function() { return opts.line.color; }; - var fillcolormap = typeof opts.fillcolor === 'function' ? - opts.fillcolor : function() { return opts.fillcolor; }; - var l; - var i; - - var l0 = opts.levels.end + opts.levels.size / 100; - var ls = opts.levels.size; - var zr0 = (1.001 * zrange[0] - 0.001 * zrange[1]); - var zr1 = (1.001 * zrange[1] - 0.001 * zrange[0]); - for(i = 0; i < 1e5; i++) { - l = opts.levels.start + i * ls; - if(ls > 0 ? (l >= l0) : (l <= l0)) break; - if(l > zr0 && l < zr1) linelevels.push(l); + }); + + colorBars.exit() + .each(function(opts) { Plots.autoMargin(gd, opts._id); }) + .remove(); + + colorBars.order(); +} + +function makeColorBarData(gd) { + var fullLayout = gd._fullLayout; + var calcdata = gd.calcdata; + var out = []; + + // single out item + var opts; + // colorbar attr parent container + var cont; + // trace attr container + var trace; + // colorbar options + var cbOpt; + + function initOpts(opts) { + return extendFlat(opts, { + // fillcolor can be a d3 scale, domain is z values, range is colors + // or leave it out for no fill, + // or set to a string constant for single-color fill + _fillcolor: null, + // line.color has the same options as fillcolor + _line: {color: null, width: null, dash: null}, + // levels of lines to draw. + // note that this DOES NOT determine the extent of the bar + // that's given by the domain of fillcolor + // (or line.color if no fillcolor domain) + _levels: {start: null, end: null, size: null}, + // separate fill levels (for example, heatmap coloring of a + // contour map) if this is omitted, fillcolors will be + // evaluated halfway between levels + _filllevels: null, + // for continuous colorscales: fill with a gradient instead of explicit levels + // value should be the colorscale [[0, c0], [v1, c1], ..., [1, cEnd]] + _fillgradient: null, + // when using a gradient, we need the data range specified separately + _zrange: null + }); + } + + function calcOpts() { + if(typeof cbOpt.calc === 'function') { + cbOpt.calc(gd, trace, opts); + } else { + opts._fillgradient = cont.reversescale ? + flipScale(cont.colorscale) : + cont.colorscale; + opts._zrange = [cont[cbOpt.min], cont[cbOpt.max]]; } + } - if(opts.fillgradient) { - filllevels = [0]; - } else if(typeof opts.fillcolor === 'function') { - if(opts.filllevels) { - l0 = opts.filllevels.end + opts.filllevels.size / 100; - ls = opts.filllevels.size; - for(i = 0; i < 1e5; i++) { - l = opts.filllevels.start + i * ls; - if(ls > 0 ? (l >= l0) : (l <= l0)) break; - if(l > zrange[0] && l < zrange[1]) filllevels.push(l); + for(var i = 0; i < calcdata.length; i++) { + var cd = calcdata[i]; + trace = cd[0].trace; + var moduleOpts = trace._module.colorbar; + + if(trace.visible === true && moduleOpts) { + var allowsMultiplotCbs = Array.isArray(moduleOpts); + var cbOpts = allowsMultiplotCbs ? moduleOpts : [moduleOpts]; + + for(var j = 0; j < cbOpts.length; j++) { + cbOpt = cbOpts[j]; + var contName = cbOpt.container; + cont = contName ? trace[contName] : trace; + + if(cont && cont.showscale) { + opts = initOpts(cont.colorbar); + opts._id = 'cb' + trace.uid + (allowsMultiplotCbs && contName ? '-' + contName : ''); + opts._traceIndex = trace.index; + opts._propPrefix = (contName ? contName + '.' : '') + 'colorbar.'; + calcOpts(); + out.push(opts); } - } else { - filllevels = linelevels.map(function(v) { - return v - opts.levels.size / 2; - }); - filllevels.push(filllevels[filllevels.length - 1] + - opts.levels.size); } - } else if(opts.fillcolor && typeof opts.fillcolor === 'string') { - // doesn't matter what this value is, with a single value - // we'll make a single fill rect covering the whole bar - filllevels = [0]; } + } + + for(var k in fullLayout._colorAxes) { + cont = fullLayout[k]; - if(opts.levels.size < 0) { - linelevels.reverse(); - filllevels.reverse(); + if(cont.showscale) { + var colorAxOpts = fullLayout._colorAxes[k]; + + opts = initOpts(cont.colorbar); + opts._id = 'cb' + k; + opts._propPrefix = k + '.colorbar.'; + + cbOpt = {min: 'cmin', max: 'cmax'}; + if(colorAxOpts[0] !== 'heatmap') { + trace = colorAxOpts[1]; + cbOpt.calc = trace._module.colorbar.calc; + } + + calcOpts(); + out.push(opts); } + } - // now make a Plotly Axes object to scale with and draw ticks - // TODO: does not support orientation other than right - - // we calculate pixel sizes based on the specified graph size, - // not the actual (in case something pushed the margins around) - // which is a little odd but avoids an odd iterative effect - // when the colorbar itself is pushing the margins. - // but then the fractional size is calculated based on the - // actual graph size, so that the axes will size correctly. - var plotHeight = gs.h; - var plotWidth = gs.w; - var thickPx = Math.round(opts.thickness * (opts.thicknessmode === 'fraction' ? plotWidth : 1)); - var thickFrac = thickPx / gs.w; - var lenPx = Math.round(opts.len * (opts.lenmode === 'fraction' ? plotHeight : 1)); - var lenFrac = lenPx / gs.h; - var xpadFrac = opts.xpad / gs.w; - var yExtraPx = (opts.borderwidth + opts.outlinewidth) / 2; - var ypadFrac = opts.ypad / gs.h; - - // x positioning: do it initially just for left anchor, - // then fix at the end (since we don't know the width yet) - var xLeft = Math.round(opts.x * gs.w + opts.xpad); - // for dragging... this is getting a little muddled... - var xLeftFrac = opts.x - thickFrac * ({middle: 0.5, right: 1}[opts.xanchor]||0); - - // y positioning we can do correctly from the start - var yBottomFrac = opts.y + lenFrac * (({top: -0.5, bottom: 0.5}[opts.yanchor] || 0) - 0.5); - var yBottomPx = Math.round(gs.h * (1 - yBottomFrac)); - var yTopPx = yBottomPx - lenPx; - - var titleEl; - - var cbAxisIn = { - type: 'linear', - range: zrange, - tickmode: opts.tickmode, - nticks: opts.nticks, - tick0: opts.tick0, - dtick: opts.dtick, - tickvals: opts.tickvals, - ticktext: opts.ticktext, - ticks: opts.ticks, - ticklen: opts.ticklen, - tickwidth: opts.tickwidth, - tickcolor: opts.tickcolor, - showticklabels: opts.showticklabels, - tickfont: opts.tickfont, - tickangle: opts.tickangle, - tickformat: opts.tickformat, - exponentformat: opts.exponentformat, - separatethousands: opts.separatethousands, - showexponent: opts.showexponent, - showtickprefix: opts.showtickprefix, - tickprefix: opts.tickprefix, - showticksuffix: opts.showticksuffix, - ticksuffix: opts.ticksuffix, - title: opts.title, - showline: true, - anchor: 'free', - side: 'right', - position: 1 - }; - var cbAxisOut = { - type: 'linear', - _id: 'y' + id - }; - var axisOptions = { - letter: 'y', - font: fullLayout.font, - noHover: true, - noTickson: true, - calendar: fullLayout.calendar // not really necessary (yet?) - }; + return out; +} + +function drawColorBar(g, opts, gd) { + var fullLayout = gd._fullLayout; + var gs = fullLayout._size; + + var fillColor = opts._fillcolor; + var line = opts._line; + var title = opts.title; + var titleSide = title.side; + + var zrange = opts._zrange || + d3.extent((typeof fillColor === 'function' ? fillColor : line.color).domain()); + + var lineColormap = typeof line.color === 'function' ? + line.color : + function() { return line.color; }; + var fillColormap = typeof fillColor === 'function' ? + fillColor : + function() { return fillColor; }; + + var levelsIn = opts._levels; + var levelsOut = calcLevels(gd, opts, zrange); + var fillLevels = levelsOut.fill; + var lineLevels = levelsOut.line; + + // we calculate pixel sizes based on the specified graph size, + // not the actual (in case something pushed the margins around) + // which is a little odd but avoids an odd iterative effect + // when the colorbar itself is pushing the margins. + // but then the fractional size is calculated based on the + // actual graph size, so that the axes will size correctly. + var thickPx = Math.round(opts.thickness * (opts.thicknessmode === 'fraction' ? gs.w : 1)); + var thickFrac = thickPx / gs.w; + var lenPx = Math.round(opts.len * (opts.lenmode === 'fraction' ? gs.h : 1)); + var lenFrac = lenPx / gs.h; + var xpadFrac = opts.xpad / gs.w; + var yExtraPx = (opts.borderwidth + opts.outlinewidth) / 2; + var ypadFrac = opts.ypad / gs.h; + + // x positioning: do it initially just for left anchor, + // then fix at the end (since we don't know the width yet) + var xLeft = Math.round(opts.x * gs.w + opts.xpad); + // for dragging... this is getting a little muddled... + var xLeftFrac = opts.x - thickFrac * ({middle: 0.5, right: 1}[opts.xanchor] || 0); + + // y positioning we can do correctly from the start + var yBottomFrac = opts.y + lenFrac * (({top: -0.5, bottom: 0.5}[opts.yanchor] || 0) - 0.5); + var yBottomPx = Math.round(gs.h * (1 - yBottomFrac)); + var yTopPx = yBottomPx - lenPx; + + // stash a few things for makeEditable + opts._lenFrac = lenFrac; + opts._thickFrac = thickFrac; + opts._xLeftFrac = xLeftFrac; + opts._yBottomFrac = yBottomFrac; + + var ax = mockColorBarAxis(gd, opts, zrange); + + // position can't go in through supplyDefaults + // because that restricts it to [0,1] + ax.position = opts.x + xpadFrac + thickFrac; + + if(['top', 'bottom'].indexOf(titleSide) !== -1) { + ax.title.side = titleSide; + ax.titlex = opts.x + xpadFrac; + ax.titley = yBottomFrac + (title.side === 'top' ? lenFrac - ypadFrac : ypadFrac); + } - // Coerce w.r.t. Axes layoutAttributes: - // re-use axes.js logic without updating _fullData - function coerce(attr, dflt) { - return Lib.coerce(cbAxisIn, cbAxisOut, axisLayoutAttrs, attr, dflt); + if(line.color && opts.tickmode === 'auto') { + ax.tickmode = 'linear'; + ax.tick0 = levelsIn.start; + var dtick = levelsIn.size; + // expand if too many contours, so we don't get too many ticks + var autoNtick = Lib.constrain((yBottomPx - yTopPx) / 50, 4, 15) + 1; + var dtFactor = (zrange[1] - zrange[0]) / ((opts.nticks || autoNtick) * dtick); + if(dtFactor > 1) { + var dtexp = Math.pow(10, Math.floor(Math.log(dtFactor) / Math.LN10)); + dtick *= dtexp * Lib.roundUp(dtFactor / dtexp, [2, 5, 10]); + // if the contours are at round multiples, reset tick0 + // so they're still at round multiples. Otherwise, + // keep the first label on the first contour level + if((Math.abs(levelsIn.start) / levelsIn.size + 1e-6) % 1 < 2e-6) { + ax.tick0 = 0; + } } + ax.dtick = dtick; + } - // Prepare the Plotly axis object - handleAxisDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions, fullLayout); - handleAxisPositionDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions); + // set domain after init, because we may want to + // allow it outside [0,1] + ax.domain = [ + yBottomFrac + ypadFrac, + yBottomFrac + lenFrac - ypadFrac + ]; - // position can't go in through supplyDefaults - // because that restricts it to [0,1] - cbAxisOut.position = opts.x + xpadFrac + thickFrac; + ax.setScale(); - // save for other callers to access this axis - component.axis = cbAxisOut; + g.attr('transform', 'translate(' + Math.round(gs.l) + ',' + Math.round(gs.t) + ')'); - if(['top', 'bottom'].indexOf(opts.title.side) !== -1) { - cbAxisOut.title.side = opts.title.side; - cbAxisOut.titlex = opts.x + xpadFrac; - cbAxisOut.titley = yBottomFrac + - (opts.title.side === 'top' ? lenFrac - ypadFrac : ypadFrac); - } + var titleCont = g.select('.' + cn.cbtitleunshift) + .attr('transform', 'translate(-' + Math.round(gs.l) + ',-' + Math.round(gs.t) + ')'); - if(opts.line.color && opts.tickmode === 'auto') { - cbAxisOut.tickmode = 'linear'; - cbAxisOut.tick0 = opts.levels.start; - var dtick = opts.levels.size; - // expand if too many contours, so we don't get too many ticks - var autoNtick = Lib.constrain((yBottomPx - yTopPx) / 50, 4, 15) + 1; - var dtFactor = (zrange[1] - zrange[0]) / ((opts.nticks || autoNtick) * dtick); - if(dtFactor > 1) { - var dtexp = Math.pow(10, Math.floor( - Math.log(dtFactor) / Math.LN10)); - dtick *= dtexp * Lib.roundUp(dtFactor / dtexp, [2, 5, 10]); - // if the contours are at round multiples, reset tick0 - // so they're still at round multiples. Otherwise, - // keep the first label on the first contour level - if((Math.abs(opts.levels.start) / - opts.levels.size + 1e-6) % 1 < 2e-6) { - cbAxisOut.tick0 = 0; - } - } - cbAxisOut.dtick = dtick; - } + var axLayer = g.select('.' + cn.cbaxis); + var titleEl; + var titleHeight = 0; - // set domain after init, because we may want to - // allow it outside [0,1] - cbAxisOut.domain = [ - yBottomFrac + ypadFrac, - yBottomFrac + lenFrac - ypadFrac - ]; - cbAxisOut.setScale(); - - // now draw the elements - var container = Lib.ensureSingle(fullLayout._infolayer, 'g', id, function(s) { - s.classed(cn.colorbar, true) - .each(function() { - var s = d3.select(this); - s.append('rect').classed(cn.cbbg, true); - s.append('g').classed(cn.cbfills, true); - s.append('g').classed(cn.cblines, true); - s.append('g').classed(cn.cbaxis, true).classed(cn.crisp, true); - s.append('g').classed(cn.cbtitleunshift, true) - .append('g').classed(cn.cbtitle, true); - s.append('rect').classed(cn.cboutline, true); - s.select('.cbtitle').datum(0); - }); - }); + function drawTitle(titleClass, titleOpts) { + var dfltTitleOpts = { + propContainer: ax, + propName: opts._propPrefix + 'title', + traceIndex: opts._traceIndex, + placeholder: fullLayout._dfltTitle.colorbar, + containerGroup: g.select('.' + cn.cbtitle) + }; - container.attr('transform', 'translate(' + Math.round(gs.l) + - ',' + Math.round(gs.t) + ')'); - // TODO: this opposite transform is a hack until we make it - // more rational which items get this offset - var titleCont = container.select('.cbtitleunshift') - .attr('transform', 'translate(-' + - Math.round(gs.l) + ',-' + - Math.round(gs.t) + ')'); + // this class-to-rotate thing with convertToTspans is + // getting hackier and hackier... delete groups with the + // wrong class (in case earlier the colorbar was drawn on + // a different side, I think?) + var otherClass = titleClass.charAt(0) === 'h' ? + titleClass.substr(1) : + 'h' + titleClass; + g.selectAll('.' + otherClass + ',.' + otherClass + '-math-group').remove(); - var axisLayer = container.select('.cbaxis'); + Titles.draw(gd, titleClass, extendFlat(dfltTitleOpts, titleOpts || {})); + } - var titleHeight = 0; - if(['top', 'bottom'].indexOf(opts.title.side) !== -1) { + function drawDummyTitle() { + if(['top', 'bottom'].indexOf(titleSide) !== -1) { // draw the title so we know how much room it needs // when we squish the axis. This one only applies to // top or bottom titles, not right side. var x = gs.l + (opts.x + xpadFrac) * gs.w; - var fontSize = cbAxisOut.title.font.size; + var fontSize = ax.title.font.size; var y; - if(opts.title.side === 'top') { + if(titleSide === 'top') { y = (1 - (yBottomFrac + lenFrac - ypadFrac)) * gs.h + gs.t + 3 + fontSize * 0.75; } else { y = (1 - (yBottomFrac + ypadFrac)) * gs.h + gs.t - 3 - fontSize * 0.25; } - drawTitle(cbAxisOut._id + 'title', { + drawTitle(ax._id + 'title', { attributes: {x: x, y: y, 'text-anchor': 'start'} }); } + } - function drawAxis() { - if(['top', 'bottom'].indexOf(opts.title.side) !== -1) { - // squish the axis top to make room for the title - var titleGroup = container.select('.cbtitle'); - var titleText = titleGroup.select('text'); - var titleTrans = [-opts.outlinewidth / 2, opts.outlinewidth / 2]; - var mathJaxNode = titleGroup - .select('.h' + cbAxisOut._id + 'title-math-group') - .node(); - var lineSize = 15.6; - if(titleText.node()) { - lineSize = - parseInt(titleText.node().style.fontSize, 10) * LINE_SPACING; - } - if(mathJaxNode) { - titleHeight = Drawing.bBox(mathJaxNode).height; - if(titleHeight > lineSize) { - // not entirely sure how mathjax is doing - // vertical alignment, but this seems to work. - titleTrans[1] -= (titleHeight - lineSize) / 2; - } - } else if(titleText.node() && - !titleText.classed(cn.jsPlaceholder)) { - titleHeight = Drawing.bBox(titleText.node()).height; - } - if(titleHeight) { - // buffer btwn colorbar and title - // TODO: configurable - titleHeight += 5; - - if(opts.title.side === 'top') { - cbAxisOut.domain[1] -= titleHeight / gs.h; - titleTrans[1] *= -1; - } else { - cbAxisOut.domain[0] += titleHeight / gs.h; - var nlines = svgTextUtils.lineCount(titleText); - titleTrans[1] += (1 - nlines) * lineSize; - } - - titleGroup.attr('transform', - 'translate(' + titleTrans + ')'); - - cbAxisOut.setScale(); + function drawCbTitle() { + if(['top', 'bottom'].indexOf(titleSide) === -1) { + var fontSize = ax.title.font.size; + var y = ax._offset + ax._length / 2; + var x = gs.l + (ax.position || 0) * gs.w + ((ax.side === 'right') ? + 10 + fontSize * ((ax.showticklabels ? 1 : 0.5)) : + -10 - fontSize * ((ax.showticklabels ? 0.5 : 0))); + + // the 'h' + is a hack to get around the fact that + // convertToTspans rotates any 'y...' class by 90 degrees. + // TODO: find a better way to control this. + drawTitle('h' + ax._id + 'title', { + avoid: { + selection: d3.select(gd).selectAll('g.' + ax._id + 'tick'), + side: titleSide, + offsetLeft: gs.l, + offsetTop: 0, + maxShift: fullLayout.width + }, + attributes: {x: x, y: y, 'text-anchor': 'middle'}, + transform: {rotate: '-90', offset: 0} + }); + } + } + + function drawAxis() { + if(['top', 'bottom'].indexOf(titleSide) !== -1) { + // squish the axis top to make room for the title + var titleGroup = g.select('.' + cn.cbtitle); + var titleText = titleGroup.select('text'); + var titleTrans = [-opts.outlinewidth / 2, opts.outlinewidth / 2]; + var mathJaxNode = titleGroup + .select('.h' + ax._id + 'title-math-group') + .node(); + var lineSize = 15.6; + if(titleText.node()) { + lineSize = parseInt(titleText.node().style.fontSize, 10) * LINE_SPACING; + } + if(mathJaxNode) { + titleHeight = Drawing.bBox(mathJaxNode).height; + if(titleHeight > lineSize) { + // not entirely sure how mathjax is doing + // vertical alignment, but this seems to work. + titleTrans[1] -= (titleHeight - lineSize) / 2; } + } else if(titleText.node() && !titleText.classed(cn.jsPlaceholder)) { + titleHeight = Drawing.bBox(titleText.node()).height; } - - container.selectAll('.cbfills,.cblines') - .attr('transform', 'translate(0,' + - Math.round(gs.h * (1 - cbAxisOut.domain[1])) + ')'); - - axisLayer.attr('transform', 'translate(0,' + Math.round(-gs.t) + ')'); - - var fills = container.select('.cbfills') - .selectAll('rect.cbfill') - .data(filllevels); - fills.enter().append('rect') - .classed(cn.cbfill, true) - .style('stroke', 'none'); - fills.exit().remove(); - - var zBounds = zrange - .map(cbAxisOut.c2p) - .map(Math.round) - .sort(function(a, b) { return a - b; }); - - fills.each(function(d, i) { - var z = [ - (i === 0) ? zrange[0] : - (filllevels[i] + filllevels[i - 1]) / 2, - (i === filllevels.length - 1) ? zrange[1] : - (filllevels[i] + filllevels[i + 1]) / 2 - ] - .map(cbAxisOut.c2p) - .map(Math.round); - - // offset the side adjoining the next rectangle so they - // overlap, to prevent antialiasing gaps - z[1] = Lib.constrain(z[1] + (z[1] > z[0]) ? 1 : -1, zBounds[0], zBounds[1]); - - // Colorbar cannot currently support opacities so we - // use an opaque fill even when alpha channels present - var fillEl = d3.select(this).attr({ - x: xLeft, - width: Math.max(thickPx, 2), - y: d3.min(z), - height: Math.max(d3.max(z) - d3.min(z), 2), - }); - - if(opts.fillgradient) { - Drawing.gradient(fillEl, gd, id, 'vertical', - opts.fillgradient, 'fill'); + if(titleHeight) { + // buffer btwn colorbar and title + // TODO: configurable + titleHeight += 5; + + if(titleSide === 'top') { + ax.domain[1] -= titleHeight / gs.h; + titleTrans[1] *= -1; } else { - // Tinycolor can't handle exponents and - // at this scale, removing it makes no difference. - var colorString = fillcolormap(d).replace('e-', ''); - fillEl.attr('fill', tinycolor(colorString).toHexString()); + ax.domain[0] += titleHeight / gs.h; + var nlines = svgTextUtils.lineCount(titleText); + titleTrans[1] += (1 - nlines) * lineSize; } - }); - var lines = container.select('.cblines') - .selectAll('path.cbline') - .data(opts.line.color && opts.line.width ? - linelevels : []); - lines.enter().append('path') - .classed(cn.cbline, true); - lines.exit().remove(); - lines.each(function(d) { - d3.select(this) - .attr('d', 'M' + xLeft + ',' + - (Math.round(cbAxisOut.c2p(d)) + (opts.line.width / 2) % 1) + - 'h' + thickPx) - .call(Drawing.lineGroupStyle, - opts.line.width, linecolormap(d), opts.line.dash); - }); - - // force full redraw of labels and ticks - axisLayer.selectAll('g.' + cbAxisOut._id + 'tick,path').remove(); - - // separate out axis and title drawing, - // so we don't need such complicated logic in Titles.draw - // if title is on the top or bottom, we've already drawn it - // this title call only handles side=right - return Lib.syncOrAsync([ - function() { - var shift = xLeft + thickPx + - (opts.outlinewidth || 0) / 2 - (opts.ticks === 'outside' ? 1 : 0); - - var vals = Axes.calcTicks(cbAxisOut); - var transFn = Axes.makeTransFn(cbAxisOut); - var tickSign = Axes.getTickSigns(cbAxisOut)[2]; - - Axes.drawTicks(gd, cbAxisOut, { - vals: cbAxisOut.ticks === 'inside' ? Axes.clipEnds(cbAxisOut, vals) : vals, - layer: axisLayer, - path: Axes.makeTickPath(cbAxisOut, shift, tickSign), - transFn: transFn - }); - - return Axes.drawLabels(gd, cbAxisOut, { - vals: vals, - layer: axisLayer, - transFn: transFn, - labelFns: Axes.makeLabelFns(cbAxisOut, shift) - }); - }, - function() { - if(['top', 'bottom'].indexOf(opts.title.side) === -1) { - var fontSize = cbAxisOut.title.font.size; - var y = cbAxisOut._offset + cbAxisOut._length / 2; - var x = gs.l + (cbAxisOut.position || 0) * gs.w + ((cbAxisOut.side === 'right') ? - 10 + fontSize * ((cbAxisOut.showticklabels ? 1 : 0.5)) : - -10 - fontSize * ((cbAxisOut.showticklabels ? 0.5 : 0))); - - // the 'h' + is a hack to get around the fact that - // convertToTspans rotates any 'y...' class by 90 degrees. - // TODO: find a better way to control this. - drawTitle('h' + cbAxisOut._id + 'title', { - avoid: { - selection: d3.select(gd).selectAll('g.' + cbAxisOut._id + 'tick'), - side: opts.title.side, - offsetLeft: gs.l, - offsetTop: 0, - maxShift: fullLayout.width - }, - attributes: {x: x, y: y, 'text-anchor': 'middle'}, - transform: {rotate: '-90', offset: 0} - }); - } - }]); + titleGroup.attr('transform', 'translate(' + titleTrans + ')'); + ax.setScale(); + } } - function drawTitle(titleClass, titleOpts) { - var dfltTitleOpts = { - propContainer: cbAxisOut, - propName: getPropName('title'), - traceIndex: getTrace().index, - placeholder: fullLayout._dfltTitle.colorbar, - containerGroup: container.select('.cbtitle') - }; - - // this class-to-rotate thing with convertToTspans is - // getting hackier and hackier... delete groups with the - // wrong class (in case earlier the colorbar was drawn on - // a different side, I think?) - var otherClass = titleClass.charAt(0) === 'h' ? - titleClass.substr(1) : ('h' + titleClass); - container.selectAll('.' + otherClass + ',.' + otherClass + '-math-group') - .remove(); - - Titles.draw(gd, titleClass, - extendFlat(dfltTitleOpts, titleOpts || {})); - } + g.selectAll('.' + cn.cbfills + ',.' + cn.cblines) + .attr('transform', 'translate(0,' + Math.round(gs.h * (1 - ax.domain[1])) + ')'); - function positionCB() { - // wait for the axis & title to finish rendering before - // continuing positioning - // TODO: why are we redrawing multiple times now with this? - // I guess autoMargin doesn't like being post-promise? - var innerWidth = thickPx + opts.outlinewidth / 2 + - Drawing.bBox(axisLayer.node()).width; - titleEl = titleCont.select('text'); - if(titleEl.node() && !titleEl.classed(cn.jsPlaceholder)) { - var mathJaxNode = titleCont - .select('.h' + cbAxisOut._id + 'title-math-group') - .node(); - var titleWidth; - if(mathJaxNode && - ['top', 'bottom'].indexOf(opts.title.side) !== -1) { - titleWidth = Drawing.bBox(mathJaxNode).width; - } else { - // note: the formula below works for all title sides, - // (except for top/bottom mathjax, above) - // but the weird gs.l is because the titleunshift - // transform gets removed by Drawing.bBox - titleWidth = - Drawing.bBox(titleCont.node()).right - - xLeft - gs.l; - } - innerWidth = Math.max(innerWidth, titleWidth); - } + axLayer.attr('transform', 'translate(0,' + Math.round(-gs.t) + ')'); + + var fills = g.select('.' + cn.cbfills) + .selectAll('rect.' + cn.cbfill) + .data(fillLevels); + fills.enter().append('rect') + .classed(cn.cbfill, true) + .style('stroke', 'none'); + fills.exit().remove(); + + var zBounds = zrange + .map(ax.c2p) + .map(Math.round) + .sort(function(a, b) { return a - b; }); - var outerwidth = 2 * opts.xpad + innerWidth + - opts.borderwidth + opts.outlinewidth / 2; - var outerheight = yBottomPx - yTopPx; - - container.select('.cbbg').attr({ - x: xLeft - opts.xpad - - (opts.borderwidth + opts.outlinewidth) / 2, - y: yTopPx - yExtraPx, - width: Math.max(outerwidth, 2), - height: Math.max(outerheight + 2 * yExtraPx, 2) - }) - .call(Color.fill, opts.bgcolor) - .call(Color.stroke, opts.bordercolor) - .style({'stroke-width': opts.borderwidth}); - - container.selectAll('.cboutline').attr({ + fills.each(function(d, i) { + var z = [ + (i === 0) ? zrange[0] : (fillLevels[i] + fillLevels[i - 1]) / 2, + (i === fillLevels.length - 1) ? zrange[1] : (fillLevels[i] + fillLevels[i + 1]) / 2 + ] + .map(ax.c2p) + .map(Math.round); + + // offset the side adjoining the next rectangle so they + // overlap, to prevent antialiasing gaps + z[1] = Lib.constrain(z[1] + (z[1] > z[0]) ? 1 : -1, zBounds[0], zBounds[1]); + + + // Colorbar cannot currently support opacities so we + // use an opaque fill even when alpha channels present + var fillEl = d3.select(this).attr({ x: xLeft, - y: yTopPx + opts.ypad + - (opts.title.side === 'top' ? titleHeight : 0), width: Math.max(thickPx, 2), - height: Math.max(outerheight - 2 * opts.ypad - titleHeight, 2) - }) - .call(Color.stroke, opts.outlinecolor) - .style({ - fill: 'None', - 'stroke-width': opts.outlinewidth + y: d3.min(z), + height: Math.max(d3.max(z) - d3.min(z), 2), }); - // fix positioning for xanchor!='left' - var xoffset = ({center: 0.5, right: 1}[opts.xanchor] || 0) * - outerwidth; - container.attr('transform', - 'translate(' + (gs.l - xoffset) + ',' + gs.t + ')'); - - // auto margin adjustment - var marginOpts = {}; - var tFrac = FROM_TL[opts.yanchor]; - var bFrac = FROM_BR[opts.yanchor]; - if(opts.lenmode === 'pixels') { - marginOpts.y = opts.y; - marginOpts.t = outerheight * tFrac; - marginOpts.b = outerheight * bFrac; + if(opts._fillgradient) { + Drawing.gradient(fillEl, gd, opts._id, 'vertical', opts._fillgradient, 'fill'); } else { - marginOpts.t = marginOpts.b = 0; - marginOpts.yt = opts.y + opts.len * tFrac; - marginOpts.yb = opts.y - opts.len * bFrac; + // tinycolor can't handle exponents and + // at this scale, removing it makes no difference. + var colorString = fillColormap(d).replace('e-', ''); + fillEl.attr('fill', tinycolor(colorString).toHexString()); } + }); + + var lines = g.select('.' + cn.cblines) + .selectAll('path.' + cn.cbline) + .data(line.color && line.width ? lineLevels : []); + lines.enter().append('path') + .classed(cn.cbline, true); + lines.exit().remove(); + lines.each(function(d) { + d3.select(this) + .attr('d', 'M' + xLeft + ',' + + (Math.round(ax.c2p(d)) + (line.width / 2) % 1) + 'h' + thickPx) + .call(Drawing.lineGroupStyle, line.width, lineColormap(d), line.dash); + }); + + // force full redraw of labels and ticks + axLayer.selectAll('g.' + ax._id + 'tick,path').remove(); + + var shift = xLeft + thickPx + + (opts.outlinewidth || 0) / 2 - (opts.ticks === 'outside' ? 1 : 0); + + var vals = Axes.calcTicks(ax); + var transFn = Axes.makeTransFn(ax); + var tickSign = Axes.getTickSigns(ax)[2]; + + Axes.drawTicks(gd, ax, { + vals: ax.ticks === 'inside' ? Axes.clipEnds(ax, vals) : vals, + layer: axLayer, + path: Axes.makeTickPath(ax, shift, tickSign), + transFn: transFn + }); + + return Axes.drawLabels(gd, ax, { + vals: vals, + layer: axLayer, + transFn: transFn, + labelFns: Axes.makeLabelFns(ax, shift) + }); + } - var lFrac = FROM_TL[opts.xanchor]; - var rFrac = FROM_BR[opts.xanchor]; - if(opts.thicknessmode === 'pixels') { - marginOpts.x = opts.x; - marginOpts.l = outerwidth * lFrac; - marginOpts.r = outerwidth * rFrac; + // wait for the axis & title to finish rendering before + // continuing positioning + // TODO: why are we redrawing multiple times now with this? + // I guess autoMargin doesn't like being post-promise? + function positionCB() { + var innerWidth = thickPx + opts.outlinewidth / 2 + Drawing.bBox(axLayer.node()).width; + titleEl = titleCont.select('text'); + + if(titleEl.node() && !titleEl.classed(cn.jsPlaceholder)) { + var mathJaxNode = titleCont.select('.h' + ax._id + 'title-math-group').node(); + var titleWidth; + if(mathJaxNode && ['top', 'bottom'].indexOf(titleSide) !== -1) { + titleWidth = Drawing.bBox(mathJaxNode).width; } else { - var extraThickness = outerwidth - thickPx; - marginOpts.l = extraThickness * lFrac; - marginOpts.r = extraThickness * rFrac; - marginOpts.xl = opts.x - opts.thickness * lFrac; - marginOpts.xr = opts.x + opts.thickness * rFrac; + // note: the formula below works for all title sides, + // (except for top/bottom mathjax, above) + // but the weird gs.l is because the titleunshift + // transform gets removed by Drawing.bBox + titleWidth = Drawing.bBox(titleCont.node()).right - xLeft - gs.l; } - Plots.autoMargin(gd, id, marginOpts); + innerWidth = Math.max(innerWidth, titleWidth); } - var cbDone = Lib.syncOrAsync([ - Plots.previousPromises, - drawAxis, - Plots.previousPromises, - positionCB - ], gd); + var outerwidth = 2 * opts.xpad + innerWidth + opts.borderwidth + opts.outlinewidth / 2; + var outerheight = yBottomPx - yTopPx; + + g.select('.' + cn.cbbg).attr({ + x: xLeft - opts.xpad - (opts.borderwidth + opts.outlinewidth) / 2, + y: yTopPx - yExtraPx, + width: Math.max(outerwidth, 2), + height: Math.max(outerheight + 2 * yExtraPx, 2) + }) + .call(Color.fill, opts.bgcolor) + .call(Color.stroke, opts.bordercolor) + .style('stroke-width', opts.borderwidth); + + g.selectAll('.' + cn.cboutline).attr({ + x: xLeft, + y: yTopPx + opts.ypad + (titleSide === 'top' ? titleHeight : 0), + width: Math.max(thickPx, 2), + height: Math.max(outerheight - 2 * opts.ypad - titleHeight, 2) + }) + .call(Color.stroke, opts.outlinecolor) + .style({ + fill: 'none', + 'stroke-width': opts.outlinewidth + }); - if(cbDone && cbDone.then) (gd._promises || []).push(cbDone); + // fix positioning for xanchor!='left' + var xoffset = ({center: 0.5, right: 1}[opts.xanchor] || 0) * outerwidth; + g.attr('transform', 'translate(' + (gs.l - xoffset) + ',' + gs.t + ')'); + + // auto margin adjustment + var marginOpts = {}; + var tFrac = FROM_TL[opts.yanchor]; + var bFrac = FROM_BR[opts.yanchor]; + if(opts.lenmode === 'pixels') { + marginOpts.y = opts.y; + marginOpts.t = outerheight * tFrac; + marginOpts.b = outerheight * bFrac; + } else { + marginOpts.t = marginOpts.b = 0; + marginOpts.yt = opts.y + opts.len * tFrac; + marginOpts.yb = opts.y - opts.len * bFrac; + } - // dragging... - if(gd._context.edits.colorbarPosition) { - var t0, - xf, - yf; - - dragElement.init({ - element: container.node(), - gd: gd, - prepFn: function() { - t0 = container.attr('transform'); - setCursor(container); - }, - moveFn: function(dx, dy) { - container.attr('transform', - t0 + ' ' + 'translate(' + dx + ',' + dy + ')'); - - xf = dragElement.align(xLeftFrac + (dx / gs.w), thickFrac, - 0, 1, opts.xanchor); - yf = dragElement.align(yBottomFrac - (dy / gs.h), lenFrac, - 0, 1, opts.yanchor); - - var csr = dragElement.getCursor(xf, yf, - opts.xanchor, opts.yanchor); - setCursor(container, csr); - }, - doneFn: function() { - setCursor(container); - - if(xf !== undefined && yf !== undefined) { - var update = {}; - update[getPropName('x')] = xf; - update[getPropName('y')] = yf; - Registry.call('_guiRestyle', gd, update, getTrace().index); - } - } - }); + var lFrac = FROM_TL[opts.xanchor]; + var rFrac = FROM_BR[opts.xanchor]; + if(opts.thicknessmode === 'pixels') { + marginOpts.x = opts.x; + marginOpts.l = outerwidth * lFrac; + marginOpts.r = outerwidth * rFrac; + } else { + var extraThickness = outerwidth - thickPx; + marginOpts.l = extraThickness * lFrac; + marginOpts.r = extraThickness * rFrac; + marginOpts.xl = opts.x - opts.thickness * lFrac; + marginOpts.xr = opts.x + opts.thickness * rFrac; } - return cbDone; + + Plots.autoMargin(gd, opts._id, marginOpts); } - function getTrace() { - var idNum = id.substr(2); - for(var i = 0; i < gd._fullData.length; i++) { - var trace = gd._fullData[i]; - if(trace.uid === idNum) return trace; + return Lib.syncOrAsync([ + Plots.previousPromises, + drawDummyTitle, + drawAxis, + drawCbTitle, + Plots.previousPromises, + positionCB + ], gd); +} + +function makeEditable(g, opts, gd) { + var fullLayout = gd._fullLayout; + var gs = fullLayout._size; + var t0, xf, yf; + + dragElement.init({ + element: g.node(), + gd: gd, + prepFn: function() { + t0 = g.attr('transform'); + setCursor(g); + }, + moveFn: function(dx, dy) { + g.attr('transform', t0 + ' ' + 'translate(' + dx + ',' + dy + ')'); + + xf = dragElement.align(opts._xLeftFrac + (dx / gs.w), opts._thickFrac, + 0, 1, opts.xanchor); + yf = dragElement.align(opts._yBottomFrac - (dy / gs.h), opts._lenFrac, + 0, 1, opts.yanchor); + + var csr = dragElement.getCursor(xf, yf, opts.xanchor, opts.yanchor); + setCursor(g, csr); + }, + doneFn: function() { + setCursor(g); + + if(xf !== undefined && yf !== undefined) { + var update = {}; + update[opts._propPrefix + 'x'] = xf; + update[opts._propPrefix + 'y'] = yf; + if(opts._traceIndex !== undefined) { + Registry.call('_guiRestyle', gd, update, opts._traceIndex); + } else { + Registry.call('_guiRelayout', gd, update); + } + } } + }); +} + +function calcLevels(gd, opts, zrange) { + var levelsIn = opts._levels; + var lineLevels = []; + var fillLevels = []; + var l; + var i; + + var l0 = levelsIn.end + levelsIn.size / 100; + var ls = levelsIn.size; + var zr0 = (1.001 * zrange[0] - 0.001 * zrange[1]); + var zr1 = (1.001 * zrange[1] - 0.001 * zrange[0]); + + for(i = 0; i < 1e5; i++) { + l = levelsIn.start + i * ls; + if(ls > 0 ? (l >= l0) : (l <= l0)) break; + if(l > zr0 && l < zr1) lineLevels.push(l); } - function getPropName(suffix) { - var trace = getTrace(); - var propName = 'colorbar.'; - var containerName = trace._module.colorbar.container; - if(containerName) propName = containerName + '.' + propName; - return propName + suffix; + if(opts._fillgradient) { + fillLevels = [0]; + } else if(typeof opts._fillcolor === 'function') { + var fillLevelsIn = opts._filllevels; + + if(fillLevelsIn) { + l0 = fillLevelsIn.end + fillLevelsIn.size / 100; + ls = fillLevelsIn.size; + for(i = 0; i < 1e5; i++) { + l = fillLevelsIn.start + i * ls; + if(ls > 0 ? (l >= l0) : (l <= l0)) break; + if(l > zrange[0] && l < zrange[1]) fillLevels.push(l); + } + } else { + fillLevels = lineLevels.map(function(v) { + return v - levelsIn.size / 2; + }); + fillLevels.push(fillLevels[fillLevels.length - 1] + levelsIn.size); + } + } else if(opts._fillcolor && typeof opts._fillcolor === 'string') { + // doesn't matter what this value is, with a single value + // we'll make a single fill rect covering the whole bar + fillLevels = [0]; } - // setter/getters for every item defined in opts - Object.keys(opts).forEach(function(name) { - component[name] = function(v) { - // getter - if(!arguments.length) return opts[name]; + if(levelsIn.size < 0) { + lineLevels.reverse(); + fillLevels.reverse(); + } - // setter - for multi-part properties, - // set only the parts that are provided - opts[name] = Lib.isPlainObject(opts[name]) ? - Lib.extendFlat(opts[name], v) : - v; + return {line: lineLevels, fill: fillLevels}; +} + +function mockColorBarAxis(gd, opts, zrange) { + var fullLayout = gd._fullLayout; + + var cbAxisIn = { + type: 'linear', + range: zrange, + tickmode: opts.tickmode, + nticks: opts.nticks, + tick0: opts.tick0, + dtick: opts.dtick, + tickvals: opts.tickvals, + ticktext: opts.ticktext, + ticks: opts.ticks, + ticklen: opts.ticklen, + tickwidth: opts.tickwidth, + tickcolor: opts.tickcolor, + showticklabels: opts.showticklabels, + tickfont: opts.tickfont, + tickangle: opts.tickangle, + tickformat: opts.tickformat, + exponentformat: opts.exponentformat, + separatethousands: opts.separatethousands, + showexponent: opts.showexponent, + showtickprefix: opts.showtickprefix, + tickprefix: opts.tickprefix, + showticksuffix: opts.showticksuffix, + ticksuffix: opts.ticksuffix, + title: opts.title, + showline: true, + anchor: 'free', + side: 'right', + position: 1 + }; - return component; - }; - }); + var cbAxisOut = { + type: 'linear', + _id: 'y' + opts._id + }; - // or use .options to set multiple options at once via a dictionary - component.options = function(o) { - for(var name in o) { - // in case something random comes through - // that's not an option, ignore it - if(typeof component[name] === 'function') { - component[name](o[name]); - } - } - return component; + var axisOptions = { + letter: 'y', + font: fullLayout.font, + noHover: true, + noTickson: true, + calendar: fullLayout.calendar // not really necessary (yet?) }; - component._opts = opts; + function coerce(attr, dflt) { + return Lib.coerce(cbAxisIn, cbAxisOut, axisLayoutAttrs, attr, dflt); + } + + handleAxisDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions, fullLayout); + handleAxisPositionDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions); + + return cbAxisOut; +} - return component; +module.exports = { + draw: draw }; diff --git a/src/components/colorbar/index.js b/src/components/colorbar/index.js index b6f624ee800..51a22b8701f 100644 --- a/src/components/colorbar/index.js +++ b/src/components/colorbar/index.js @@ -6,12 +6,15 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; +module.exports = { + moduleType: 'component', + name: 'colorbar', + + attributes: require('./attributes'), + supplyDefaults: require('./defaults'), -exports.attributes = require('./attributes'); -exports.supplyDefaults = require('./defaults'); -exports.connect = require('./connect'); -exports.draw = require('./draw'); -exports.hasColorbar = require('./has_colorbar'); + draw: require('./draw').draw, + hasColorbar: require('./has_colorbar') +}; diff --git a/src/components/colorscale/attributes.js b/src/components/colorscale/attributes.js index 014377c1c91..3df6baec2a5 100644 --- a/src/components/colorscale/attributes.js +++ b/src/components/colorscale/attributes.js @@ -8,6 +8,9 @@ 'use strict'; +var colorbarAttrs = require('../colorbar/attributes'); +var counterRegex = require('../../lib/regex').counter; + var palettes = require('./scales.js').scales; var paletteStr = Object.keys(palettes); @@ -240,6 +243,25 @@ module.exports = function colorScaleAttrs(context, opts) { effectDesc ].join('') }; + + attrs.colorbar = colorbarAttrs; + } + + if(!opts.noColorAxis) { + attrs.coloraxis = { + valType: 'subplotid', + role: 'info', + regex: counterRegex('coloraxis'), + dflt: null, + editType: 'calc', + description: [ + 'Sets a reference to a shared color axis.', + 'References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc.', + 'Settings for these shared color axes are set in the layout, under', + '`layout.coloraxis`, `layout.coloraxis2`, etc.', + 'Note that multiple color scales can be linked to the same color axis.' + ].join(' ') + }; } return attrs; diff --git a/src/components/colorscale/calc.js b/src/components/colorscale/calc.js index ed2491f9420..54911a76452 100644 --- a/src/components/colorscale/calc.js +++ b/src/components/colorscale/calc.js @@ -8,37 +8,50 @@ 'use strict'; +var isNumeric = require('fast-isnumeric'); + var Lib = require('../../lib'); +var extractOpts = require('./helpers').extractOpts; module.exports = function calc(gd, trace, opts) { var fullLayout = gd._fullLayout; var vals = opts.vals; var containerStr = opts.containerStr; - var cLetter = opts.cLetter; var container = containerStr ? Lib.nestedProperty(trace, containerStr).get() : trace; - var autoAttr = cLetter + 'auto'; - var minAttr = cLetter + 'min'; - var maxAttr = cLetter + 'max'; - var midAttr = cLetter + 'mid'; - var auto = container[autoAttr]; - var min = container[minAttr]; - var max = container[maxAttr]; - var mid = container[midAttr]; - var scl = container.colorscale; + var cOpts = extractOpts(container); + var auto = cOpts.auto !== false; + var min = cOpts.min; + var max = cOpts.max; + var mid = cOpts.mid; + + var minVal = function() { return Lib.aggNums(Math.min, null, vals); }; + var maxVal = function() { return Lib.aggNums(Math.max, null, vals); }; - if(auto !== false || min === undefined) { - min = Lib.aggNums(Math.min, null, vals); + if(min === undefined) { + min = minVal(); + } else if(auto) { + if(container._colorAx && isNumeric(min)) { + min = Math.min(min, minVal()); + } else { + min = minVal(); + } } - if(auto !== false || max === undefined) { - max = Lib.aggNums(Math.max, null, vals); + if(max === undefined) { + max = maxVal(); + } else if(auto) { + if(container._colorAx && isNumeric(max)) { + max = Math.max(max, maxVal()); + } else { + max = maxVal(); + } } - if(auto !== false && mid !== undefined) { + if(auto && mid !== undefined) { if(max - mid > mid - min) { min = mid - (max - mid); } else if(max - mid < mid - min) { @@ -51,14 +64,14 @@ module.exports = function calc(gd, trace, opts) { max += 0.5; } - container['_' + minAttr] = container[minAttr] = min; - container['_' + maxAttr] = container[maxAttr] = max; + cOpts._sync('min', min); + cOpts._sync('max', max); - if(container.autocolorscale) { + if(cOpts.autocolorscale) { + var scl; if(min * max < 0) scl = fullLayout.colorscale.diverging; else if(min >= 0) scl = fullLayout.colorscale.sequential; else scl = fullLayout.colorscale.sequentialminus; - - container._colorscale = container.colorscale = scl; + cOpts._sync('colorscale', scl); } }; diff --git a/src/components/colorscale/cross_trace_defaults.js b/src/components/colorscale/cross_trace_defaults.js index 5933b669166..706bb7ccfb7 100644 --- a/src/components/colorscale/cross_trace_defaults.js +++ b/src/components/colorscale/cross_trace_defaults.js @@ -10,8 +10,9 @@ var Lib = require('../../lib'); var hasColorscale = require('./helpers').hasColorscale; +var extractOpts = require('./helpers').extractOpts; -module.exports = function crossTraceDefaults(fullData) { +module.exports = function crossTraceDefaults(fullData, fullLayout) { function replace(cont, k) { var val = cont['_' + k]; if(val !== undefined) { @@ -19,44 +20,46 @@ module.exports = function crossTraceDefaults(fullData) { } } - function relinkColorAtts(trace, cAttrs) { - var cont = cAttrs.container ? - Lib.nestedProperty(trace, cAttrs.container).get() : - trace; + function relinkColorAtts(outerCont, cbOpt) { + var cont = cbOpt.container ? + Lib.nestedProperty(outerCont, cbOpt.container).get() : + outerCont; if(cont) { - var isAuto = cont.zauto || cont.cauto; - var minAttr = cAttrs.min; - var maxAttr = cAttrs.max; + if(cont.coloraxis) { + // stash ref to color axis + cont._colorAx = fullLayout[cont.coloraxis]; + } else { + var cOpts = extractOpts(cont); + var isAuto = cOpts.auto; - if(isAuto || cont[minAttr] === undefined) { - replace(cont, minAttr); - } - if(isAuto || cont[maxAttr] === undefined) { - replace(cont, maxAttr); - } - if(cont.autocolorscale) { - replace(cont, 'colorscale'); + if(isAuto || cOpts.min === undefined) { + replace(cont, cbOpt.min); + } + if(isAuto || cOpts.max === undefined) { + replace(cont, cbOpt.max); + } + if(cOpts.autocolorscale) { + replace(cont, 'colorscale'); + } } } } for(var i = 0; i < fullData.length; i++) { var trace = fullData[i]; - var colorbar = trace._module.colorbar; + var cbOpts = trace._module.colorbar; - if(colorbar) { - if(Array.isArray(colorbar)) { - for(var j = 0; j < colorbar.length; j++) { - relinkColorAtts(trace, colorbar[j]); + if(cbOpts) { + if(Array.isArray(cbOpts)) { + for(var j = 0; j < cbOpts.length; j++) { + relinkColorAtts(trace, cbOpts[j]); } } else { - relinkColorAtts(trace, colorbar); + relinkColorAtts(trace, cbOpts); } } - // TODO could generalize _module.colorscale and use it here? - if(hasColorscale(trace, 'marker.line')) { relinkColorAtts(trace, { container: 'marker.line', @@ -64,13 +67,9 @@ module.exports = function crossTraceDefaults(fullData) { max: 'cmax' }); } + } - if(hasColorscale(trace, 'line')) { - relinkColorAtts(trace, { - container: 'line', - min: 'cmin', - max: 'cmax' - }); - } + for(var k in fullLayout._colorAxes) { + relinkColorAtts(fullLayout[k], {min: 'cmin', max: 'cmax'}); } }; diff --git a/src/components/colorscale/defaults.js b/src/components/colorscale/defaults.js index 75cd284b2be..0cb953129b5 100644 --- a/src/components/colorscale/defaults.js +++ b/src/components/colorscale/defaults.js @@ -15,20 +15,74 @@ var hasColorbar = require('../colorbar/has_colorbar'); var colorbarDefaults = require('../colorbar/defaults'); var isValidScale = require('./scales').isValid; +var traceIs = require('../../registry').traceIs; -function npMaybe(cont, prefix) { +function npMaybe(parentCont, prefix) { var containerStr = prefix.slice(0, prefix.length - 1); return prefix ? - Lib.nestedProperty(cont, containerStr).get() || {} : - cont; + Lib.nestedProperty(parentCont, containerStr).get() || {} : + parentCont; } -module.exports = function colorScaleDefaults(traceIn, traceOut, layout, coerce, opts) { +/** + * Colorscale / colorbar default handler + * + * @param {object} parentContIn : user (input) parent container (e.g. trace or layout coloraxis object) + * @param {object} parentContOut : full parent container + * @param {object} layout : (full) layout object + * @param {fn} coerce : Lib.coerce wrapper + * @param {object} opts : + * - prefix {string} : attr string prefix to colorscale container from parent root + * - cLetter {string} : 'c or 'z' color letter + */ +module.exports = function colorScaleDefaults(parentContIn, parentContOut, layout, coerce, opts) { var prefix = opts.prefix; var cLetter = opts.cLetter; - var containerIn = npMaybe(traceIn, prefix); - var containerOut = npMaybe(traceOut, prefix); - var template = npMaybe(traceOut._template || {}, prefix) || {}; + var inTrace = '_module' in parentContOut; + var containerIn = npMaybe(parentContIn, prefix); + var containerOut = npMaybe(parentContOut, prefix); + var template = npMaybe(parentContOut._template || {}, prefix) || {}; + + // colorScaleDefaults wrapper called if-ever we need to reset the colorscale + // attributes for containers that were linked to invalid color axes + var thisFn = function() { + delete parentContIn.coloraxis; + delete parentContOut.coloraxis; + return colorScaleDefaults(parentContIn, parentContOut, layout, coerce, opts); + }; + + if(inTrace) { + var colorAxes = layout._colorAxes || {}; + var colorAx = coerce(prefix + 'coloraxis'); + + if(colorAx) { + var colorbarVisuals = ( + traceIs(parentContOut, 'contour') && + Lib.nestedProperty(parentContOut, 'contours.coloring').get() + ) || 'heatmap'; + + var stash = colorAxes[colorAx]; + + if(stash) { + stash[2].push(thisFn); + + if(stash[0] !== colorbarVisuals) { + stash[0] = false; + Lib.warn([ + 'Ignoring coloraxis:', colorAx, 'setting', + 'as it is linked to incompatible colorscales.' + ].join(' ')); + } + } else { + // stash: + // - colorbar visual 'type' + // - colorbar options to help in Colorbar.draw + // - list of colorScaleDefaults wrapper functions + colorAxes[colorAx] = [colorbarVisuals, parentContOut, [thisFn]]; + } + return; + } + } var minIn = containerIn[cLetter + 'min']; var maxIn = containerIn[cLetter + 'max']; @@ -54,11 +108,11 @@ module.exports = function colorScaleDefaults(traceIn, traceOut, layout, coerce, coerce(prefix + 'colorscale'); coerce(prefix + 'reversescale'); - if(!opts.noScale && prefix !== 'marker.line.') { + if(prefix !== 'marker.line.') { // handles both the trace case where the dflt is listed in attributes and // the marker case where the dflt is determined by hasColorbar var showScaleDflt; - if(prefix) showScaleDflt = hasColorbar(containerIn); + if(prefix && inTrace) showScaleDflt = hasColorbar(containerIn); var showScale = coerce(prefix + 'showscale', showScaleDflt); if(showScale) colorbarDefaults(containerIn, containerOut, layout); diff --git a/src/components/colorscale/helpers.js b/src/components/colorscale/helpers.js index f9e262f3a18..73ca3994476 100644 --- a/src/components/colorscale/helpers.js +++ b/src/components/colorscale/helpers.js @@ -44,6 +44,67 @@ function hasColorscale(trace, containerStr) { ); } +var constantAttrs = ['showscale', 'autocolorscale', 'colorscale', 'reversescale', 'colorbar']; +var letterAttrs = ['min', 'max', 'mid', 'auto']; + +/** + * Extract 'c' / 'z', trace / color axis colorscale options + * + * Note that it would be nice to replace all z* with c* equivalents in v2 + * + * @param {object} cont : attribute container + * @return {object}: + * - min: cmin or zmin + * - max: cmax or zmax + * - mid: cmid or zmid + * - auto: cauto or zauto + * - *scale: *scale attrs + * - colorbar: colorbar + * - _sync: function syncing attr and underscore dual (useful when calc'ing min/max) + */ +function extractOpts(cont) { + var colorAx = cont._colorAx; + var cont2 = colorAx ? colorAx : cont; + var out = {}; + var cLetter; + var i, k; + + for(i = 0; i < constantAttrs.length; i++) { + k = constantAttrs[i]; + out[k] = cont2[k]; + } + + if(colorAx) { + cLetter = 'c'; + for(i = 0; i < letterAttrs.length; i++) { + k = letterAttrs[i]; + out[k] = cont2['c' + k]; + } + } else { + var k2; + for(i = 0; i < letterAttrs.length; i++) { + k = letterAttrs[i]; + k2 = 'c' + k; + if(k2 in cont2) { + out[k] = cont2[k2]; + continue; + } + k2 = 'z' + k; + if(k2 in cont2) { + out[k] = cont2[k2]; + } + } + cLetter = k2.charAt(0); + } + + out._sync = function(k, v) { + var k2 = letterAttrs.indexOf(k) !== -1 ? cLetter + k : k; + cont2[k2] = cont2['_' + k2] = v; + }; + + return out; +} + /** * Extract colorscale into numeric domain and color range. * @@ -52,24 +113,19 @@ function hasColorscale(trace, containerStr) { * - cmin/zmin {number} * - cmax/zmax {number} * - reversescale {boolean} - * @param {object} opts - * - cLetter {string} 'c' (for cmin/cmax) or 'z' (for zmin/zmax) * * @return {object} * - domain {array} * - range {array} */ -function extractScale(cont, opts) { - var cLetter = opts.cLetter; +function extractScale(cont) { + var cOpts = extractOpts(cont); + var cmin = cOpts.min; + var cmax = cOpts.max; - var scl = cont.reversescale ? - flipScale(cont.colorscale) : - cont.colorscale; - - // minimum color value (used to clamp scale) - var cmin = cont[cLetter + 'min']; - // maximum color value (used to clamp scale) - var cmax = cont[cLetter + 'max']; + var scl = cOpts.reversescale ? + flipScale(cOpts.colorscale) : + cOpts.colorscale; var N = scl.length; var domain = new Array(N); @@ -81,10 +137,7 @@ function extractScale(cont, opts) { range[i] = si[1]; } - return { - domain: domain, - range: range - }; + return {domain: domain, range: range}; } function flipScale(scl) { @@ -154,14 +207,16 @@ function makeColorScaleFunc(specs, opts) { } // colorbar draw looks into the d3 scale closure for domain and range - sclFunc.domain = _sclFunc.domain; - sclFunc.range = function() { return range; }; return sclFunc; } +function makeColorScaleFuncFromTrace(trace, opts) { + return makeColorScaleFunc(extractScale(trace), opts); +} + function colorArray2rbga(colorArray) { var colorObj = { r: colorArray[0], @@ -175,7 +230,9 @@ function colorArray2rbga(colorArray) { module.exports = { hasColorscale: hasColorscale, + extractOpts: extractOpts, extractScale: extractScale, flipScale: flipScale, - makeColorScaleFunc: makeColorScaleFunc + makeColorScaleFunc: makeColorScaleFunc, + makeColorScaleFuncFromTrace: makeColorScaleFuncFromTrace }; diff --git a/src/components/colorscale/index.js b/src/components/colorscale/index.js index 224cc41de65..2976b51926b 100644 --- a/src/components/colorscale/index.js +++ b/src/components/colorscale/index.js @@ -32,7 +32,9 @@ module.exports = { isValidScale: scales.isValid, hasColorscale: helpers.hasColorscale, - flipScale: helpers.flipScale, + extractOpts: helpers.extractOpts, extractScale: helpers.extractScale, - makeColorScaleFunc: helpers.makeColorScaleFunc + flipScale: helpers.flipScale, + makeColorScaleFunc: helpers.makeColorScaleFunc, + makeColorScaleFuncFromTrace: helpers.makeColorScaleFuncFromTrace }; diff --git a/src/components/colorscale/layout_attributes.js b/src/components/colorscale/layout_attributes.js index 75ec78007af..3612884c172 100644 --- a/src/components/colorscale/layout_attributes.js +++ b/src/components/colorscale/layout_attributes.js @@ -8,40 +8,63 @@ 'use strict'; +var extendFlat = require('../../lib/extend').extendFlat; + +var colorScaleAttrs = require('./attributes'); var scales = require('./scales').scales; var msg = 'Note that `autocolorscale` must be true for this attribute to work.'; module.exports = { editType: 'calc', - sequential: { - valType: 'colorscale', - dflt: scales.Reds, - role: 'style', - editType: 'calc', - description: [ - 'Sets the default sequential colorscale for positive values.', - msg - ].join(' ') - }, - sequentialminus: { - valType: 'colorscale', - dflt: scales.Blues, - role: 'style', + + colorscale: { editType: 'calc', - description: [ - 'Sets the default sequential colorscale for negative values.', - msg - ].join(' ') + + sequential: { + valType: 'colorscale', + dflt: scales.Reds, + role: 'style', + editType: 'calc', + description: [ + 'Sets the default sequential colorscale for positive values.', + msg + ].join(' ') + }, + sequentialminus: { + valType: 'colorscale', + dflt: scales.Blues, + role: 'style', + editType: 'calc', + description: [ + 'Sets the default sequential colorscale for negative values.', + msg + ].join(' ') + }, + diverging: { + valType: 'colorscale', + dflt: scales.RdBu, + role: 'style', + editType: 'calc', + description: [ + 'Sets the default diverging colorscale.', + msg + ].join(' ') + } }, - diverging: { - valType: 'colorscale', - dflt: scales.RdBu, - role: 'style', + + coloraxis: extendFlat({ + // not really a 'subplot' attribute container, + // but this is the flag we use to denote attributes that + // support yaxis, yaxis2, yaxis3, ... counters + _isSubplotObj: true, editType: 'calc', description: [ - 'Sets the default diverging colorscale.', - msg + '' ].join(' ') - } + }, colorScaleAttrs('', { + colorAttr: 'corresponding trace color array(s)', + noColorAxis: true, + showScaleDflt: true + })) }; diff --git a/src/components/colorscale/layout_defaults.js b/src/components/colorscale/layout_defaults.js index b05d56895c0..744e575de83 100644 --- a/src/components/colorscale/layout_defaults.js +++ b/src/components/colorscale/layout_defaults.js @@ -9,17 +9,41 @@ 'use strict'; var Lib = require('../../lib'); -var colorscaleAttrs = require('./layout_attributes'); var Template = require('../../plot_api/plot_template'); +var colorScaleAttrs = require('./layout_attributes'); +var colorScaleDefaults = require('./defaults'); + module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { - var colorscaleIn = layoutIn.colorscale; - var colorscaleOut = Template.newContainer(layoutOut, 'colorscale'); function coerce(attr, dflt) { - return Lib.coerce(colorscaleIn, colorscaleOut, colorscaleAttrs, attr, dflt); + return Lib.coerce(layoutIn, layoutOut, colorScaleAttrs, attr, dflt); } - coerce('sequential'); - coerce('sequentialminus'); - coerce('diverging'); + coerce('colorscale.sequential'); + coerce('colorscale.sequentialminus'); + coerce('colorscale.diverging'); + + var colorAxes = layoutOut._colorAxes; + var colorAxIn, colorAxOut; + + function coerceAx(attr, dflt) { + return Lib.coerce(colorAxIn, colorAxOut, colorScaleAttrs.coloraxis, attr, dflt); + } + + for(var k in colorAxes) { + var stash = colorAxes[k]; + + if(stash[0]) { + colorAxIn = layoutIn[k] || {}; + colorAxOut = Template.newContainer(layoutOut, k, 'coloraxis'); + colorAxOut._name = k; + colorScaleDefaults(colorAxIn, colorAxOut, layoutOut, coerceAx, {prefix: '', cLetter: 'c'}); + } else { + // re-coerce colorscale attributes w/o coloraxis + for(var i = 0; i < stash[2].length; i++) { + stash[2][i](); + } + delete layoutOut._colorAxes[k]; + } + } }; diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index 9e764d93562..de3f243d87c 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -638,13 +638,9 @@ drawing.tryColorscale = function(marker, prefix) { var cont = prefix ? Lib.nestedProperty(marker, prefix).get() : marker; if(cont) { - var scl = cont.colorscale; var colorArray = cont.color; - - if(scl && Lib.isArrayOrTypedArray(colorArray)) { - return Colorscale.makeColorScaleFunc( - Colorscale.extractScale(cont, {cLetter: 'c'}) - ); + if((cont.colorscale || cont._colorAx) && Lib.isArrayOrTypedArray(colorArray)) { + return Colorscale.makeColorScaleFuncFromTrace(cont); } } return Lib.identity; diff --git a/src/core.js b/src/core.js index cf4bf2d6ade..c05ab0d6b72 100644 --- a/src/core.js +++ b/src/core.js @@ -55,7 +55,8 @@ register([ require('./components/rangeselector'), require('./components/grid'), require('./components/errorbars'), - require('./components/colorscale') + require('./components/colorscale'), + require('./components/colorbar') ]); // locales en and en-US are required for default behavior diff --git a/src/lib/gl_format_color.js b/src/lib/gl_format_color.js index cfa7a587aa6..36cd70d5ba7 100644 --- a/src/lib/gl_format_color.js +++ b/src/lib/gl_format_color.js @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var isNumeric = require('fast-isnumeric'); @@ -47,9 +46,7 @@ function formatColor(containerIn, opacityIn, len) { var sclFunc, getColor, getOpacity, colori, opacityi; if(containerIn.colorscale !== undefined) { - sclFunc = Colorscale.makeColorScaleFunc( - Colorscale.extractScale(containerIn, {cLetter: 'c'}) - ); + sclFunc = Colorscale.makeColorScaleFuncFromTrace(containerIn); } else { sclFunc = validateColor; } @@ -81,9 +78,11 @@ function formatColor(containerIn, opacityIn, len) { function parseColorScale(cont, alpha) { if(alpha === undefined) alpha = 1; - var colorscale = cont.reversescale ? - Colorscale.flipScale(cont.colorscale) : - cont.colorscale; + var cOpts = Colorscale.extractOpts(cont); + + var colorscale = cOpts.reversescale ? + Colorscale.flipScale(cOpts.colorscale) : + cOpts.colorscale; return colorscale.map(function(elem) { var index = elem[0]; diff --git a/src/plot_api/edit_types.js b/src/plot_api/edit_types.js index 6f849283de0..73972c449c6 100644 --- a/src/plot_api/edit_types.js +++ b/src/plot_api/edit_types.js @@ -35,7 +35,7 @@ var layoutOpts = { extras: ['none'], flags: [ 'calc', 'plot', 'legend', 'ticks', 'axrange', - 'layoutstyle', 'modebar', 'camera', 'arraydraw' + 'layoutstyle', 'modebar', 'camera', 'arraydraw', 'colorbars' ], description: [ 'layout attributes should include an `editType` string matching this flaglist.', @@ -49,7 +49,8 @@ var layoutOpts = { '*modebar* just updates the modebar.', '*camera* just updates the camera settings for gl3d scenes.', '*arraydraw* allows component arrays to invoke the redraw routines just for the', - 'component(s) that changed.' + 'component(s) that changed.', + '*colorbars* only redraws colorbars.' ].join(' ') }; diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 81d227918cb..68c20f476f8 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -28,7 +28,6 @@ var Polar = require('../plots/polar/legacy'); var Axes = require('../plots/cartesian/axes'); var Drawing = require('../components/drawing'); var Color = require('../components/color'); -var connectColorbar = require('../components/colorbar/connect'); var initInteractions = require('../plots/cartesian/graph_interact').initInteractions; var xmlnsNamespaces = require('../constants/xmlns_namespaces'); var svgTextUtils = require('../lib/svg_text_utils'); @@ -283,9 +282,6 @@ function plot(gd, data, layout, config) { // draw anything that can affect margins. function marginPushers() { - var calcdata = gd.calcdata; - var i, cd, trace; - // First reset the list of things that are allowed to change the margins // So any deleted traces or components will be wiped out of the // automargin calculation. @@ -296,15 +292,6 @@ function plot(gd, data, layout, config) { subroutines.drawMarginPushers(gd); Axes.allowAutoMargin(gd); - for(i = 0; i < calcdata.length; i++) { - cd = calcdata[i]; - trace = cd[0].trace; - var colorbarOpts = trace._module.colorbar; - if(trace.visible !== true || !colorbarOpts) { - Plots.autoMargin(gd, 'cb' + trace.uid); - } else connectColorbar(gd, cd, colorbarOpts); - } - Plots.doAutoMargin(gd); return Plots.previousPromises(gd); } @@ -1883,6 +1870,7 @@ function relayout(gd, astr, val) { if(flags.ticks) seq.push(subroutines.doTicksRelayout); if(flags.modebar) seq.push(subroutines.doModeBar); if(flags.camera) seq.push(subroutines.doCamera); + if(flags.colorbars) seq.push(subroutines.doColorBars); seq.push(emitAfterPlot); } @@ -2393,7 +2381,7 @@ function update(gd, traceUpdate, layoutUpdate, _traces) { axRangeSupplyDefaultsByPass(gd, relayoutFlags, relayoutSpecs) || Plots.supplyDefaults(gd); if(restyleFlags.style) seq.push(subroutines.doTraceStyle); - if(restyleFlags.colorbars) seq.push(subroutines.doColorBars); + if(restyleFlags.colorbars || relayoutFlags.colorbars) seq.push(subroutines.doColorBars); if(relayoutFlags.legend) seq.push(subroutines.doLegend); if(relayoutFlags.layoutstyle) seq.push(subroutines.layoutStyles); if(relayoutFlags.axrange) addAxRangeSequence(seq, relayoutSpecs.rangesAltered); @@ -2786,7 +2774,7 @@ function react(gd, data, layout, config) { seq.push(Plots.previousPromises); if(restyleFlags.style) seq.push(subroutines.doTraceStyle); - if(restyleFlags.colorbars) seq.push(subroutines.doColorBars); + if(restyleFlags.colorbars || relayoutFlags.colorbars) seq.push(subroutines.doColorBars); if(relayoutFlags.legend) seq.push(subroutines.doLegend); if(relayoutFlags.layoutstyle) seq.push(subroutines.layoutStyles); if(relayoutFlags.axrange) addAxRangeSequence(seq); diff --git a/src/plot_api/plot_schema.js b/src/plot_api/plot_schema.js index 5dbe3fb4774..516a79c0fcf 100644 --- a/src/plot_api/plot_schema.js +++ b/src/plot_api/plot_schema.js @@ -373,7 +373,9 @@ function layoutHeadAttr(fullLayout, head) { */ for(key in Registry.componentsRegistry) { _module = Registry.componentsRegistry[key]; - if(!_module.schema && (head === _module.name)) { + if(_module.name === 'colorscale' && head.indexOf('coloraxis') === 0) { + return _module.layoutAttributes[head]; + } else if(!_module.schema && (head === _module.name)) { return _module.layoutAttributes; } } @@ -544,24 +546,26 @@ function getLayoutAttributes() { var schema = _module.schema; if(schema && (schema.subplots || schema.layout)) { - /* - * Components with defined schema have already been merged in at register time - * but a few components define attributes that apply only to xaxis - * not yaxis (rangeselector, rangeslider) - delete from y schema. - * Note that the input attributes for xaxis/yaxis are the same object - * so it's not possible to only add them to xaxis from the start. - * If we ever have such asymmetry the other way, or anywhere else, - * we will need to extend both this code and mergeComponentAttrsToSubplot - * (which will not find yaxis only for example) - */ - + /* + * Components with defined schema have already been merged in at register time + * but a few components define attributes that apply only to xaxis + * not yaxis (rangeselector, rangeslider) - delete from y schema. + * Note that the input attributes for xaxis/yaxis are the same object + * so it's not possible to only add them to xaxis from the start. + * If we ever have such asymmetry the other way, or anywhere else, + * we will need to extend both this code and mergeComponentAttrsToSubplot + * (which will not find yaxis only for example) + */ var subplots = schema.subplots; if(subplots && subplots.xaxis && !subplots.yaxis) { - for(var xkey in subplots.xaxis) delete layoutAttributes.yaxis[xkey]; + for(var xkey in subplots.xaxis) { + delete layoutAttributes.yaxis[xkey]; + } } + } else if(_module.name === 'colorscale') { + extendDeepAll(layoutAttributes, _module.layoutAttributes); } else if(_module.layoutAttributes) { - // older style without schema need to be explicitly merged in now - + // older style without schema need to be explicitly merged in now insertAttrs(layoutAttributes, _module.layoutAttributes, _module.name); } } diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 8607065f898..a310ae31bcc 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -528,29 +528,7 @@ exports.doTraceStyle = function(gd) { }; exports.doColorBars = function(gd) { - for(var i = 0; i < gd.calcdata.length; i++) { - var cdi0 = gd.calcdata[i][0]; - - if((cdi0.t || {}).cb) { - var trace = cdi0.trace; - var cb = cdi0.t.cb; - - if(Registry.traceIs(trace, 'contour')) { - cb.line({ - width: trace.contours.showlines !== false ? - trace.line.width : 0, - dash: trace.line.dash, - color: trace.contours.coloring === 'line' ? - cb._opts.line.color : trace.line.color - }); - } - var moduleOpts = trace._module.colorbar; - var containerName = moduleOpts.container; - var opts = (containerName ? trace[containerName] : trace).colorbar; - cb.options(opts)(); - } - } - + Registry.getComponentMethod('colorbar', 'draw')(gd); return Plots.previousPromises(gd); }; @@ -607,22 +585,12 @@ exports.doCamera = function(gd) { exports.drawData = function(gd) { var fullLayout = gd._fullLayout; - var calcdata = gd.calcdata; - var i; - - // remove old colorbars explicitly - for(i = 0; i < calcdata.length; i++) { - var trace = calcdata[i][0].trace; - if(trace.visible !== true || !trace._module.colorbar) { - fullLayout._infolayer.select('.cb' + trace.uid).remove(); - } - } clearGlCanvases(gd); // loop over the base plot modules present on graph var basePlotModules = fullLayout._basePlotModules; - for(i = 0; i < basePlotModules.length; i++) { + for(var i = 0; i < basePlotModules.length; i++) { basePlotModules[i].plot(gd); } @@ -768,4 +736,5 @@ exports.drawMarginPushers = function(gd) { Registry.getComponentMethod('rangeselector', 'draw')(gd); Registry.getComponentMethod('sliders', 'draw')(gd); Registry.getComponentMethod('updatemenus', 'draw')(gd); + Registry.getComponentMethod('colorbar', 'draw')(gd); }; diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index ac204dbe8c6..66b55d13da8 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -11,7 +11,6 @@ var fontAttrs = require('./font_attributes'); var animationAttrs = require('./animation_attributes'); var colorAttrs = require('../components/color/attributes'); -var colorscaleAttrs = require('../components/colorscale/layout_attributes'); var padAttrs = require('./pad_attributes'); var extendFlat = require('../lib/extend').extendFlat; @@ -292,7 +291,6 @@ module.exports = { editType: 'calc', description: 'Sets the default trace colors.' }, - colorscale: colorscaleAttrs, datarevision: { valType: 'any', role: 'info', diff --git a/src/plots/plots.js b/src/plots/plots.js index e3081aff948..6e6494e0fb7 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -54,18 +54,18 @@ plots.hasSimpleAPICommandBindings = commandModule.hasSimpleAPICommandBindings; plots.redrawText = function(gd) { gd = Lib.getGraphDiv(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; + if(hasLegacyPolar) return; return new Promise(function(resolve) { setTimeout(function() { Registry.getComponentMethod('annotations', 'draw')(gd); Registry.getComponentMethod('legend', 'draw')(gd); - - (gd.calcdata || []).forEach(function(d) { - if(d[0] && d[0].t && d[0].t.cb) d[0].t.cb(); - }); - + Registry.getComponentMethod('colorbar', 'draw')(gd); resolve(plots.previousPromises(gd)); }, 300); }); @@ -390,6 +390,8 @@ plots.supplyDefaults = function(gd, opts) { newFullLayout._firstScatter = {}; // for grouped bar/box/violin trace to share config across traces newFullLayout._alignmentOpts = {}; + // track color axes referenced in the data + newFullLayout._colorAxes = {}; // for traces to request a default rangeslider on their x axes // eg set `_requestRangeslider.x2 = true` for xaxis2 @@ -445,7 +447,6 @@ plots.supplyDefaults = function(gd, opts) { for(i = 0; i < crossTraceDefaultsFuncs.length; i++) { crossTraceDefaultsFuncs[i](newFullData, newFullLayout); } - Registry.getComponentMethod('colorscale', 'crossTraceDefaults')(newFullData, newFullLayout); // turn on flag to optimize large splom-only graphs // mostly by omitting SVG layers during Cartesian.drawFramework @@ -485,6 +486,9 @@ plots.supplyDefaults = function(gd, opts) { // relink functions and _ attributes to promote consistency between plots relinkPrivateKeys(newFullLayout, oldFullLayout); + // colorscale crossTraceDefaults needs newFullLayout with relinked keys + Registry.getComponentMethod('colorscale', 'crossTraceDefaults')(newFullData, newFullLayout); + // For persisting GUI-driven changes in layout // _preGUI and _tracePreGUI were already copied over in relinkPrivateKeys if(!newFullLayout._preGUI) newFullLayout._preGUI = {}; diff --git a/src/snapshot/helpers.js b/src/snapshot/helpers.js index b6f6ae21423..8b10678872b 100644 --- a/src/snapshot/helpers.js +++ b/src/snapshot/helpers.js @@ -6,9 +6,10 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; +var Registry = require('../registry'); + exports.getDelay = function(fullLayout) { if(!fullLayout._has) return 0; @@ -20,16 +21,13 @@ 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 for legacy polar - if(hasLegacyPolar) return; - return function() { - (gd.calcdata || []).forEach(function(d) { - if(d[0] && d[0].t && d[0].t.cb) d[0].t.cb(); - }); + var fullLayout = gd._fullLayout || {}; + var hasPolar = fullLayout._has && fullLayout._has('polar'); + var hasLegacyPolar = !hasPolar && gd.data && gd.data[0] && gd.data[0].r; + + if(!hasLegacyPolar) { + Registry.getComponentMethod('colorbar', 'draw')(gd); + } }; }; diff --git a/src/traces/bar/attributes.js b/src/traces/bar/attributes.js index 5cf9003da59..b9c4526df62 100644 --- a/src/traces/bar/attributes.js +++ b/src/traces/bar/attributes.js @@ -10,8 +10,7 @@ var scatterAttrs = require('../scatter/attributes'); var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); -var colorAttributes = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var fontAttrs = require('../../plots/font_attributes'); var constants = require('./constants.js'); @@ -33,13 +32,12 @@ var markerLineWidth = extendFlat({}, var markerLine = extendFlat({ width: markerLineWidth, editType: 'calc' -}, colorAttributes('marker.line')); +}, colorScaleAttrs('marker.line')); var marker = extendFlat({ line: markerLine, editType: 'calc' -}, colorAttributes('marker'), { - colorbar: colorbarAttrs, +}, colorScaleAttrs('marker'), { opacity: { valType: 'number', arrayOk: true, diff --git a/src/traces/choropleth/attributes.js b/src/traces/choropleth/attributes.js index 5e68b2b7f71..827f25d7dfa 100644 --- a/src/traces/choropleth/attributes.js +++ b/src/traces/choropleth/attributes.js @@ -10,8 +10,7 @@ var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); var scatterGeoAttrs = require('../scattergeo/attributes'); -var colorscaleAttrs = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var plotAttrs = require('../../plots/attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -80,9 +79,8 @@ module.exports = extendFlat({ hovertemplate: hovertemplateAttrs(), }, - colorscaleAttrs('', { + colorScaleAttrs('', { cLetter: 'z', editTypeOverride: 'calc' - }), - {colorbar: colorbarAttrs} + }) ); diff --git a/src/traces/choropleth/style.js b/src/traces/choropleth/style.js index 9da61ac1580..c87efa20954 100644 --- a/src/traces/choropleth/style.js +++ b/src/traces/choropleth/style.js @@ -24,9 +24,7 @@ function styleTrace(gd, calcTrace) { var marker = trace.marker || {}; var markerLine = marker.line || {}; - var sclFunc = Colorscale.makeColorScaleFunc( - Colorscale.extractScale(trace, {cLetter: 'z'}) - ); + var sclFunc = Colorscale.makeColorScaleFuncFromTrace(trace); locs.each(function(d) { d3.select(this) diff --git a/src/traces/cone/attributes.js b/src/traces/cone/attributes.js index 0df3c8d9964..a47c8dc8ee5 100644 --- a/src/traces/cone/attributes.js +++ b/src/traces/cone/attributes.js @@ -8,8 +8,7 @@ 'use strict'; -var colorscaleAttrs = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); var mesh3dAttrs = require('../mesh3d/attributes'); var baseAttrs = require('../../plots/attributes'); @@ -170,13 +169,11 @@ var attrs = { hovertemplate: hovertemplateAttrs({editType: 'calc'}, {keys: ['norm']}) }; -extendFlat(attrs, colorscaleAttrs('', { +extendFlat(attrs, colorScaleAttrs('', { colorAttr: 'u/v/w norm', showScaleDflt: true, editTypeOverride: 'calc' -}), { - colorbar: colorbarAttrs -}); +})); var fromMesh3d = ['opacity', 'lightposition', 'lighting']; diff --git a/src/traces/cone/convert.js b/src/traces/cone/convert.js index dcdfcb9d5f1..740258a4998 100644 --- a/src/traces/cone/convert.js +++ b/src/traces/cone/convert.js @@ -13,6 +13,7 @@ var createConeMesh = require('gl-cone3d').createConeMesh; var simpleMap = require('../../lib').simpleMap; var parseColorScale = require('../../lib/gl_format_color').parseColorScale; +var extractOpts = require('../../components/colorscale').extractOpts; var zip3 = require('../../plots/gl3d/zip3'); function Cone(scene, uid) { @@ -80,8 +81,9 @@ function convert(scene, trace) { trace._len ); + var cOpts = extractOpts(trace); coneOpts.colormap = parseColorScale(trace); - coneOpts.vertexIntensityBounds = [trace.cmin / trace._normMax, trace.cmax / trace._normMax]; + coneOpts.vertexIntensityBounds = [cOpts.min / trace._normMax, cOpts.max / trace._normMax]; coneOpts.coneOffset = anchor2coneOffset[trace.anchor]; if(trace.sizemode === 'scaled') { diff --git a/src/traces/contour/attributes.js b/src/traces/contour/attributes.js index 4e48ff14beb..4d2fef35fcc 100644 --- a/src/traces/contour/attributes.js +++ b/src/traces/contour/attributes.js @@ -10,8 +10,7 @@ var heatmapAttrs = require('../heatmap/attributes'); var scatterAttrs = require('../scatter/attributes'); -var colorscaleAttrs = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var dash = require('../../components/drawing/attributes').dash; var fontAttrs = require('../../plots/font_attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -251,10 +250,9 @@ module.exports = extendFlat({ editType: 'plot' } }, - colorscaleAttrs('', { + colorScaleAttrs('', { cLetter: 'z', autoColorDflt: false, editTypeOverride: 'calc' - }), - { colorbar: colorbarAttrs } + }) ); diff --git a/src/traces/contour/calc.js b/src/traces/contour/calc.js index 5f16e8f312c..7f764e22f2e 100644 --- a/src/traces/contour/calc.js +++ b/src/traces/contour/calc.js @@ -6,17 +6,46 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; +var Colorscale = require('../../components/colorscale'); + var heatmapCalc = require('../heatmap/calc'); var setContours = require('./set_contours'); +var endPlus = require('./end_plus'); // most is the same as heatmap calc, then adjust it // though a few things inside heatmap calc still look for // contour maps, because the makeBoundArray calls are too entangled module.exports = function calc(gd, trace) { var cd = heatmapCalc(gd, trace); - setContours(trace); + + var zOut = cd[0].z; + setContours(trace, zOut); + + var contours = trace.contours; + var cOpts = Colorscale.extractOpts(trace); + var cVals; + + if(contours.coloring === 'heatmap' && cOpts.auto && trace.autocontour === false) { + var start = contours.start; + var end = endPlus(contours); + var cs = contours.size || 1; + var nc = Math.floor((end - start) / cs) + 1; + + if(!isFinite(cs)) { + cs = 1; + nc = 1; + } + + var min0 = start - cs / 2; + var max0 = min0 + nc * cs; + cVals = [min0, max0]; + } else { + cVals = zOut; + } + + Colorscale.calc(gd, trace, {vals: cVals, cLetter: 'z'}); + return cd; }; diff --git a/src/traces/contour/colorbar.js b/src/traces/contour/colorbar.js index 0187ed5b7d1..46514b90a78 100644 --- a/src/traces/contour/colorbar.js +++ b/src/traces/contour/colorbar.js @@ -6,44 +6,42 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; -var drawColorbar = require('../../components/colorbar/draw'); - +var extractOpts = require('../../components/colorscale').extractOpts; var makeColorMap = require('./make_color_map'); var endPlus = require('./end_plus'); - -module.exports = function colorbar(gd, cd) { - var trace = cd[0].trace; - var cbId = 'cb' + trace.uid; - - gd._fullLayout._infolayer.selectAll('.' + cbId).remove(); - - if(!trace.showscale) return; - - var cb = cd[0].t.cb = drawColorbar(gd, cbId); - +function calc(gd, trace, opts) { var contours = trace.contours; var line = trace.line; var cs = contours.size || 1; var coloring = contours.coloring; - var colorMap = makeColorMap(trace, {isColorbar: true}); - cb.fillgradient(coloring === 'heatmap' ? trace.colorscale : '') - .zrange(coloring === 'heatmap' ? [trace.zmin, trace.zmax] : '') - .fillcolor((coloring === 'fill') ? colorMap : '') - .line({ - color: coloring === 'lines' ? colorMap : line.color, - width: contours.showlines !== false ? line.width : 0, - dash: line.dash - }) - .levels({ - start: contours.start, - end: endPlus(contours), - size: cs - }) - .options(trace.colorbar)(); + if(coloring === 'heatmap') { + var cOpts = extractOpts(trace); + opts._fillgradient = trace.colorscale; + opts._zrange = [cOpts.min, cOpts.max]; + } else if(coloring === 'fill') { + opts._fillcolor = colorMap; + } + + opts._line = { + color: coloring === 'lines' ? colorMap : line.color, + width: contours.showlines !== false ? line.width : 0, + dash: line.dash + }; + + opts._levels = { + start: contours.start, + end: endPlus(contours), + size: cs + }; +} + +module.exports = { + min: 'zmin', + max: 'zmax', + calc: calc }; diff --git a/src/traces/contour/make_color_map.js b/src/traces/contour/make_color_map.js index 74c1ca90889..c739bf25500 100644 --- a/src/traces/contour/make_color_map.js +++ b/src/traces/contour/make_color_map.js @@ -6,10 +6,10 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var d3 = require('d3'); + var Colorscale = require('../../components/colorscale'); var endPlus = require('./end_plus'); @@ -20,15 +20,16 @@ module.exports = function makeColorMap(trace) { var cs = contours.size || 1; var nc = Math.floor((end - start) / cs) + 1; var extra = contours.coloring === 'lines' ? 0 : 1; + var cOpts = Colorscale.extractOpts(trace); if(!isFinite(cs)) { cs = 1; nc = 1; } - var scl = trace.reversescale ? - Colorscale.flipScale(trace.colorscale) : - trace.colorscale; + var scl = cOpts.reversescale ? + Colorscale.flipScale(cOpts.colorscale) : + cOpts.colorscale; var len = scl.length; var domain = new Array(len); @@ -37,51 +38,45 @@ module.exports = function makeColorMap(trace) { var si, i; if(contours.coloring === 'heatmap') { - if(trace.zauto && trace.autocontour === false) { - trace.zmin = start - cs / 2; - trace.zmax = trace.zmin + nc * cs; - } + var zmin0 = cOpts.min; + var zmax0 = cOpts.max; for(i = 0; i < len; i++) { si = scl[i]; - - domain[i] = si[0] * (trace.zmax - trace.zmin) + trace.zmin; + domain[i] = si[0] * (zmax0 - zmin0) + zmin0; range[i] = si[1]; } // do the contours extend beyond the colorscale? // if so, extend the colorscale with constants var zRange = d3.extent([ - trace.zmin, - trace.zmax, + zmin0, + zmax0, contours.start, contours.start + cs * (nc - 1) ]); - var zmin = zRange[trace.zmin < trace.zmax ? 0 : 1]; - var zmax = zRange[trace.zmin < trace.zmax ? 1 : 0]; + var zmin = zRange[zmin0 < zmax0 ? 0 : 1]; + var zmax = zRange[zmin0 < zmax0 ? 1 : 0]; - if(zmin !== trace.zmin) { + if(zmin !== zmin0) { domain.splice(0, 0, zmin); - range.splice(0, 0, Range[0]); + range.splice(0, 0, range[0]); } - if(zmax !== trace.zmax) { + if(zmax !== zmax0) { domain.push(zmax); range.push(range[range.length - 1]); } } else { for(i = 0; i < len; i++) { si = scl[i]; - domain[i] = (si[0] * (nc + extra - 1) - (extra / 2)) * cs + start; range[i] = si[1]; } } - return Colorscale.makeColorScaleFunc({ - domain: domain, - range: range, - }, { - noNumericCheck: true - }); + return Colorscale.makeColorScaleFunc( + {domain: domain, range: range}, + {noNumericCheck: true} + ); }; diff --git a/src/traces/contour/plot.js b/src/traces/contour/plot.js index 0411e864695..201965f9765 100644 --- a/src/traces/contour/plot.js +++ b/src/traces/contour/plot.js @@ -43,12 +43,6 @@ exports.plot = function plot(gd, plotinfo, cdcontours, contourLayer) { var heatmapColoringLayer = Lib.ensureSingle(plotGroup, 'g', 'heatmapcoloring'); var cdheatmaps = []; if(contours.coloring === 'heatmap') { - if(trace.zauto && (trace.autocontour === false)) { - trace._input.zmin = trace.zmin = - contours.start - contours.size / 2; - trace._input.zmax = trace.zmax = - trace.zmin + pathinfo.length * contours.size; - } cdheatmaps = [cd]; } heatmapPlot(gd, plotinfo, cdheatmaps, heatmapColoringLayer); diff --git a/src/traces/contour/set_contours.js b/src/traces/contour/set_contours.js index 0804413a4a3..31d8f7f0f8b 100644 --- a/src/traces/contour/set_contours.js +++ b/src/traces/contour/set_contours.js @@ -6,28 +6,29 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var Axes = require('../../plots/cartesian/axes'); var Lib = require('../../lib'); - -module.exports = function setContours(trace) { +module.exports = function setContours(trace, vals) { var contours = trace.contours; // check if we need to auto-choose contour levels if(trace.autocontour) { + // N.B. do not try to use coloraxis cmin/cmax, + // these values here are meant to remain "per-trace" for now var zmin = trace.zmin; var zmax = trace.zmax; - if(zmin === undefined || zmax === undefined) { - zmin = Lib.aggNums(Math.min, null, trace._z); - zmax = Lib.aggNums(Math.max, null, trace._z); + if(trace.zauto || zmin === undefined) { + zmin = Lib.aggNums(Math.min, null, vals); + } + if(trace.zauto || zmax === undefined) { + zmax = Lib.aggNums(Math.max, null, vals); } - var dummyAx = autoContours(zmin, zmax, trace.ncontours); + var dummyAx = autoContours(zmin, zmax, trace.ncontours); contours.size = dummyAx.dtick; - contours.start = Axes.tickFirst(dummyAx); dummyAx.range.reverse(); contours.end = Axes.tickFirst(dummyAx); diff --git a/src/traces/contourcarpet/attributes.js b/src/traces/contourcarpet/attributes.js index 6bcec99d63f..d2d28b86aef 100644 --- a/src/traces/contourcarpet/attributes.js +++ b/src/traces/contourcarpet/attributes.js @@ -12,8 +12,7 @@ var heatmapAttrs = require('../heatmap/attributes'); var contourAttrs = require('../contour/attributes'); var contourContourAttrs = contourAttrs.contours; var scatterAttrs = require('../scatter/attributes'); -var colorscaleAttrs = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -95,9 +94,8 @@ module.exports = extendFlat({ transforms: undefined }, - colorscaleAttrs('', { + colorScaleAttrs('', { cLetter: 'z', autoColorDflt: false - }), - { colorbar: colorbarAttrs } + }) ); diff --git a/src/traces/contourcarpet/calc.js b/src/traces/contourcarpet/calc.js index 07d5919ab34..73f418dbccc 100644 --- a/src/traces/contourcarpet/calc.js +++ b/src/traces/contourcarpet/calc.js @@ -45,8 +45,7 @@ module.exports = function calc(gd, trace) { } var cd = heatmappishCalc(gd, trace); - - setContours(trace); + setContours(trace, trace._z); return cd; }; diff --git a/src/traces/heatmap/attributes.js b/src/traces/heatmap/attributes.js index 89cc79bf02c..4821fcbeb0f 100644 --- a/src/traces/heatmap/attributes.js +++ b/src/traces/heatmap/attributes.js @@ -10,8 +10,7 @@ var scatterAttrs = require('../scatter/attributes'); var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); -var colorscaleAttrs = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -121,9 +120,5 @@ module.exports = extendFlat({ }, { transforms: undefined }, - colorscaleAttrs('', { - cLetter: 'z', - autoColorDflt: false - }), - { colorbar: colorbarAttrs } + colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false}) ); diff --git a/src/traces/heatmap/calc.js b/src/traces/heatmap/calc.js index f86bb23ed37..69633c278f3 100644 --- a/src/traces/heatmap/calc.js +++ b/src/traces/heatmap/calc.js @@ -142,13 +142,8 @@ module.exports = function calc(gd, trace) { cd0.pts = binned.pts; } - // auto-z and autocolorscale if applicable - if(!isContour || trace.contours.type !== 'constraint') { - colorscaleCalc(gd, trace, { - vals: z, - containerStr: '', - cLetter: 'z' - }); + if(!isContour) { + colorscaleCalc(gd, trace, {vals: z, cLetter: 'z'}); } if(isContour && trace.contours && trace.contours.coloring === 'heatmap') { diff --git a/src/traces/heatmap/hover.js b/src/traces/heatmap/hover.js index cfc2e72f0c2..55e9f63d195 100644 --- a/src/traces/heatmap/hover.js +++ b/src/traces/heatmap/hover.js @@ -6,12 +6,12 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var Fx = require('../../components/fx'); var Lib = require('../../lib'); var Axes = require('../../plots/cartesian/axes'); +var extractOpts = require('../../components/colorscale').extractOpts; module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLayer, contour) { var cd0 = pointData.cd[0]; @@ -24,7 +24,6 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay var xc = cd0.xCenter; var yc = cd0.yCenter; var zmask = cd0.zmask; - var range = [trace.zmin, trace.zmax]; var zhoverformat = trace.zhoverformat; var x2 = x; var y2 = y; @@ -95,17 +94,16 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay text = cd0.text[ny][nx]; } - var zLabel; // dummy axis for formatting the z value + var cOpts = extractOpts(trace); var dummyAx = { type: 'linear', - range: range, + range: [cOpts.min, cOpts.max], hoverformat: zhoverformat, _separators: xa._separators, _numFormat: xa._numFormat }; - var zLabelObj = Axes.tickText(dummyAx, zVal, 'hover'); - zLabel = zLabelObj.text; + var zLabel = Axes.tickText(dummyAx, zVal, 'hover').text; return [Lib.extendFlat(pointData, { index: [ny, nx], diff --git a/src/traces/heatmap/plot.js b/src/traces/heatmap/plot.js index 190d56e2a4b..66259cfcf1c 100644 --- a/src/traces/heatmap/plot.js +++ b/src/traces/heatmap/plot.js @@ -14,7 +14,7 @@ var tinycolor = require('tinycolor2'); var Registry = require('../../registry'); var Lib = require('../../lib'); -var Colorscale = require('../../components/colorscale'); +var makeColorScaleFuncFromTrace = require('../../components/colorscale').makeColorScaleFuncFromTrace; var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) { @@ -140,10 +140,7 @@ module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) { canvas.height = canvasH; var context = canvas.getContext('2d'); - var sclFunc = Colorscale.makeColorScaleFunc( - Colorscale.extractScale(trace, {cLetter: 'z'}), - { noNumericCheck: true, returnArray: true } - ); + var sclFunc = makeColorScaleFuncFromTrace(trace, {noNumericCheck: true, returnArray: true}); // map brick boundaries to image pixels var xpx, diff --git a/src/traces/heatmapgl/attributes.js b/src/traces/heatmapgl/attributes.js index 081fb12499b..8769247f0d3 100644 --- a/src/traces/heatmapgl/attributes.js +++ b/src/traces/heatmapgl/attributes.js @@ -8,15 +8,12 @@ 'use strict'; - var heatmapAttrs = require('../heatmap/attributes'); -var colorscaleAttrs = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var extendFlat = require('../../lib/extend').extendFlat; var overrideAll = require('../../plot_api/edit_types').overrideAll; - var commonList = [ 'z', 'x', 'x0', 'dx', @@ -34,8 +31,7 @@ for(var i = 0; i < commonList.length; i++) { extendFlat( attrs, - colorscaleAttrs('', {cLetter: 'z', autoColorDflt: false}), - {colorbar: colorbarAttrs} + colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false}) ); module.exports = overrideAll(attrs, 'calc', 'nested'); diff --git a/src/traces/histogram2d/attributes.js b/src/traces/histogram2d/attributes.js index 72befadf1fd..ae4648c929f 100644 --- a/src/traces/histogram2d/attributes.js +++ b/src/traces/histogram2d/attributes.js @@ -12,8 +12,7 @@ var histogramAttrs = require('../histogram/attributes'); var makeBinAttrs = require('../histogram/bin_attributes'); var heatmapAttrs = require('../heatmap/attributes'); var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); -var colorscaleAttrs = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -51,9 +50,5 @@ module.exports = extendFlat( zhoverformat: heatmapAttrs.zhoverformat, hovertemplate: hovertemplateAttrs({}, {keys: 'z'}) }, - colorscaleAttrs('', { - cLetter: 'z', - autoColorDflt: false - }), - { colorbar: colorbarAttrs } + colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false}) ); diff --git a/src/traces/histogram2dcontour/attributes.js b/src/traces/histogram2dcontour/attributes.js index 2dfe6dc89c2..19c4db142bf 100644 --- a/src/traces/histogram2dcontour/attributes.js +++ b/src/traces/histogram2dcontour/attributes.js @@ -10,8 +10,7 @@ var histogram2dAttrs = require('../histogram2d/attributes'); var contourAttrs = require('../contour/attributes'); -var colorscaleAttrs = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -37,9 +36,8 @@ module.exports = extendFlat({ zhoverformat: histogram2dAttrs.zhoverformat, hovertemplate: histogram2dAttrs.hovertemplate }, - colorscaleAttrs('', { + colorScaleAttrs('', { cLetter: 'z', editTypeOverride: 'calc' - }), - { colorbar: colorbarAttrs } + }) ); diff --git a/src/traces/isosurface/attributes.js b/src/traces/isosurface/attributes.js index bff5d187dd3..37cf47f316e 100644 --- a/src/traces/isosurface/attributes.js +++ b/src/traces/isosurface/attributes.js @@ -8,8 +8,7 @@ 'use strict'; -var colorscaleAttrs = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); var meshAttrs = require('../mesh3d/attributes'); var baseAttrs = require('../../plots/attributes'); @@ -236,14 +235,11 @@ var attrs = module.exports = overrideAll(extendFlat({ hovertemplate: hovertemplateAttrs() }, -colorscaleAttrs('', { +colorScaleAttrs('', { colorAttr: '`value`', showScaleDflt: true, editTypeOverride: 'calc' }), { - - colorbar: colorbarAttrs, - opacity: meshAttrs.opacity, lightposition: meshAttrs.lightposition, lighting: meshAttrs.lighting, diff --git a/src/traces/isosurface/convert.js b/src/traces/isosurface/convert.js index 0e50885f9e7..d3a67e00f25 100644 --- a/src/traces/isosurface/convert.js +++ b/src/traces/isosurface/convert.js @@ -6,15 +6,16 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var createMesh = require('gl-mesh3d'); +var Lib = require('../../lib'); + var parseColorScale = require('../../lib/gl_format_color').parseColorScale; var str2RgbaArray = require('../../lib/str2rgbarray'); +var extractOpts = require('../../components/colorscale').extractOpts; var zip3 = require('../../plots/gl3d/zip3'); -var Lib = require('../../lib'); function distinctVals(col) { return Lib.distinctVals(col).vals; @@ -121,8 +122,9 @@ proto.update = function(data) { useFacetNormals: data.flatshading }; + var cOpts = extractOpts(data); config.vertexIntensity = data._intensity; - config.vertexIntensityBounds = [data.cmin, data.cmax]; + config.vertexIntensityBounds = [cOpts.min, cOpts.max]; config.colormap = parseColorScale(data); // Update mesh diff --git a/src/traces/mesh3d/attributes.js b/src/traces/mesh3d/attributes.js index bd90dfb8e86..c383df63a7a 100644 --- a/src/traces/mesh3d/attributes.js +++ b/src/traces/mesh3d/attributes.js @@ -8,8 +8,7 @@ 'use strict'; -var colorscaleAttrs = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); var surfaceAtts = require('../surface/attributes'); var baseAttrs = require('../../plots/attributes'); @@ -178,14 +177,11 @@ module.exports = extendFlat({ transforms: undefined }, -colorscaleAttrs('', { +colorScaleAttrs('', { colorAttr: '`intensity`', showScaleDflt: true, editTypeOverride: 'calc' }), { - - colorbar: colorbarAttrs, - opacity: surfaceAtts.opacity, // Flat shaded mode diff --git a/src/traces/mesh3d/convert.js b/src/traces/mesh3d/convert.js index dca58ed8355..bc9b69ebfa4 100644 --- a/src/traces/mesh3d/convert.js +++ b/src/traces/mesh3d/convert.js @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var createMesh = require('gl-mesh3d'); @@ -16,6 +15,7 @@ var convexHull = require('convex-hull'); var parseColorScale = require('../../lib/gl_format_color').parseColorScale; var str2RgbaArray = require('../../lib/str2rgbarray'); +var extractOpts = require('../../components/colorscale').extractOpts; var zip3 = require('../../plots/gl3d/zip3'); function Mesh3DTrace(scene, mesh, uid) { @@ -158,9 +158,10 @@ proto.update = function(data) { }; if(data.intensity) { + var cOpts = extractOpts(data); this.color = '#fff'; config.vertexIntensity = data.intensity; - config.vertexIntensityBounds = [data.cmin, data.cmax]; + config.vertexIntensityBounds = [cOpts.min, cOpts.max]; config.colormap = parseColorScale(data); } else if(data.vertexcolor) { this.color = data.vertexcolor[0]; diff --git a/src/traces/parcats/attributes.js b/src/traces/parcats/attributes.js index 98b6ada66cd..a2188a08636 100644 --- a/src/traces/parcats/attributes.js +++ b/src/traces/parcats/attributes.js @@ -11,19 +11,14 @@ var extendFlat = require('../../lib/extend').extendFlat; var plotAttrs = require('../../plots/attributes'); var fontAttrs = require('../../plots/font_attributes'); -var colorAttributes = require('../../components/colorscale/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); var domainAttrs = require('../../plots/domain').attributes; -var scatterAttrs = require('../scatter/attributes'); -var scatterLineAttrs = scatterAttrs.line; -var colorbarAttrs = require('../../components/colorbar/attributes'); -var line = extendFlat({ - editType: 'calc' -}, colorAttributes('line', {editType: 'calc'}), +var line = extendFlat( + {editType: 'calc'}, + colorScaleAttrs('line', {editTypeOverride: 'calc'}), { - showscale: scatterLineAttrs.showscale, - colorbar: colorbarAttrs, shape: { valType: 'enumerated', values: ['linear', 'hspline'], @@ -46,7 +41,8 @@ var line = extendFlat({ 'This value here applies when hovering over lines.' ].join(' ') }) - }); + } +); module.exports = { domain: domainAttrs({name: 'parcats', trace: true, editType: 'calc'}), diff --git a/src/traces/parcoords/attributes.js b/src/traces/parcoords/attributes.js index 7994896990b..bf1f7223410 100644 --- a/src/traces/parcoords/attributes.js +++ b/src/traces/parcoords/attributes.js @@ -8,8 +8,7 @@ 'use strict'; -var colorAttributes = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var axesAttrs = require('../../plots/cartesian/layout_attributes'); var fontAttrs = require('../../plots/font_attributes'); var domainAttrs = require('../../plots/domain').attributes; @@ -112,15 +111,13 @@ module.exports = { description: 'The dimensions (variables) of the parallel coordinates chart. 2..60 dimensions are supported.' }), - line: extendFlat( - colorAttributes('line', { + line: extendFlat({editType: 'calc'}, + colorScaleAttrs('line', { // the default autocolorscale isn't quite usable for parcoords due to context ambiguity around 0 (grey, off-white) // autocolorscale therefore defaults to false too, to avoid being overridden by the blue-white-red autocolor palette colorscaleDflt: 'Viridis', autoColorDflt: false, editTypeOverride: 'calc' - }), { - colorbar: colorbarAttrs, - editType: 'calc' }) + ) }; diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index 86f8161f82e..e46a5f75513 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -9,8 +9,7 @@ 'use strict'; var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); -var colorAttributes = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var fontAttrs = require('../../plots/font_attributes'); var dash = require('../../components/drawing/attributes').dash; @@ -415,8 +414,6 @@ module.exports = { ].join(' ') }, - colorbar: colorbarAttrs, - line: extendFlat({ width: { valType: 'number', @@ -429,7 +426,7 @@ module.exports = { }, editType: 'calc' }, - colorAttributes('marker.line', {anim: true}) + colorScaleAttrs('marker.line', {anim: true}) ), gradient: { type: { @@ -458,7 +455,7 @@ module.exports = { }, editType: 'calc' }, - colorAttributes('marker', {anim: true}) + colorScaleAttrs('marker', {anim: true}) ), selected: { marker: { diff --git a/src/traces/scattercarpet/attributes.js b/src/traces/scattercarpet/attributes.js index c263e3b333a..3c2fed05336 100644 --- a/src/traces/scattercarpet/attributes.js +++ b/src/traces/scattercarpet/attributes.js @@ -11,8 +11,7 @@ var scatterAttrs = require('../scatter/attributes'); var plotAttrs = require('../../plots/attributes'); var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); -var colorAttributes = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -101,13 +100,13 @@ module.exports = { width: scatterMarkerLineAttrs.width, editType: 'calc' }, - colorAttributes('marker.line') + colorScaleAttrs('marker.line') ), gradient: scatterMarkerAttrs.gradient, editType: 'calc' - }, colorAttributes('marker'), { - colorbar: colorbarAttrs - }), + }, + colorScaleAttrs('marker') + ), textfont: scatterAttrs.textfont, textposition: scatterAttrs.textposition, diff --git a/src/traces/scattergl/attributes.js b/src/traces/scattergl/attributes.js index fb78d50a5b8..4aa74ec4202 100644 --- a/src/traces/scattergl/attributes.js +++ b/src/traces/scattergl/attributes.js @@ -10,7 +10,7 @@ var plotAttrs = require('../../plots/attributes'); var scatterAttrs = require('../scatter/attributes'); -var colorAttrs = require('../../components/colorscale/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var extendFlat = require('../../lib/extend').extendFlat; var overrideAll = require('../../plot_api/edit_types').overrideAll; @@ -65,7 +65,7 @@ var attrs = module.exports = overrideAll({ description: 'Sets the style of the lines.' } }, - marker: extendFlat({}, colorAttrs('marker'), { + marker: extendFlat({}, colorScaleAttrs('marker'), { symbol: scatterMarkerAttrs.symbol, size: scatterMarkerAttrs.size, sizeref: scatterMarkerAttrs.sizeref, @@ -73,7 +73,7 @@ var attrs = module.exports = overrideAll({ sizemode: scatterMarkerAttrs.sizemode, opacity: scatterMarkerAttrs.opacity, colorbar: scatterMarkerAttrs.colorbar, - line: extendFlat({}, colorAttrs('marker.line'), { + line: extendFlat({}, colorScaleAttrs('marker.line'), { width: scatterMarkerLineAttrs.width }) }), diff --git a/src/traces/scattermapbox/attributes.js b/src/traces/scattermapbox/attributes.js index 8609904e1a2..876e9e4f9c2 100644 --- a/src/traces/scattermapbox/attributes.js +++ b/src/traces/scattermapbox/attributes.js @@ -13,7 +13,7 @@ var scatterGeoAttrs = require('../scattergeo/attributes'); var scatterAttrs = require('../scatter/attributes'); var mapboxAttrs = require('../../plots/mapbox/layout_attributes'); var plotAttrs = require('../../plots/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var extendFlat = require('../../lib/extend').extendFlat; var overrideAll = require('../../plot_api/edit_types').overrideAll; @@ -70,7 +70,7 @@ module.exports = overrideAll({ connectgaps: scatterAttrs.connectgaps, - marker: { + marker: extendFlat({ symbol: { valType: 'string', dflt: 'circle', @@ -87,20 +87,11 @@ module.exports = overrideAll({ size: markerAttrs.size, sizeref: markerAttrs.sizeref, sizemin: markerAttrs.sizemin, - sizemode: markerAttrs.sizemode, - color: markerAttrs.color, - colorscale: markerAttrs.colorscale, - cauto: markerAttrs.cauto, - cmax: markerAttrs.cmax, - cmin: markerAttrs.cmin, - cmid: markerAttrs.cmid, - autocolorscale: markerAttrs.autocolorscale, - reversescale: markerAttrs.reversescale, - showscale: markerAttrs.showscale, - colorbar: colorbarAttrs, - - // line + sizemode: markerAttrs.sizemode }, + colorScaleAttrs('marker') + // line + ), fill: scatterGeoAttrs.fill, fillcolor: scatterAttrs.fillcolor, diff --git a/src/traces/scattermapbox/convert.js b/src/traces/scattermapbox/convert.js index 5dbc94ced6a..8aba0245549 100644 --- a/src/traces/scattermapbox/convert.js +++ b/src/traces/scattermapbox/convert.js @@ -157,9 +157,7 @@ function makeCircleOpts(calcTrace) { var colorFn; if(arrayColor) { if(Colorscale.hasColorscale(trace, 'marker')) { - colorFn = Colorscale.makeColorScaleFunc( - Colorscale.extractScale(marker, {cLetter: 'c'}) - ); + colorFn = Colorscale.makeColorScaleFuncFromTrace(marker); } else { colorFn = Lib.identity; } diff --git a/src/traces/scatterternary/attributes.js b/src/traces/scatterternary/attributes.js index 439259f7897..9b86099c84b 100644 --- a/src/traces/scatterternary/attributes.js +++ b/src/traces/scatterternary/attributes.js @@ -11,8 +11,7 @@ var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); var scatterAttrs = require('../scatter/attributes'); var plotAttrs = require('../../plots/attributes'); -var colorAttributes = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var dash = require('../../components/drawing/attributes').dash; var extendFlat = require('../../lib/extend').extendFlat; @@ -130,13 +129,13 @@ module.exports = { width: scatterMarkerLineAttrs.width, editType: 'calc' }, - colorAttributes('marker.line') + colorScaleAttrs('marker.line') ), gradient: scatterMarkerAttrs.gradient, editType: 'calc' - }, colorAttributes('marker'), { - colorbar: colorbarAttrs - }), + }, + colorScaleAttrs('marker') + ), textfont: scatterAttrs.textfont, textposition: scatterAttrs.textposition, diff --git a/src/traces/splom/attributes.js b/src/traces/splom/attributes.js index d7c8e7fc311..e75878fdf25 100644 --- a/src/traces/splom/attributes.js +++ b/src/traces/splom/attributes.js @@ -9,7 +9,7 @@ 'use strict'; var scatterAttrs = require('../scatter/attributes'); -var colorAttrs = require('../../components/colorscale/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); var scatterGlAttrs = require('../scattergl/attributes'); var cartesianIdRegex = require('../../plots/cartesian/constants').idRegex; @@ -19,12 +19,12 @@ var extendFlat = require('../../lib/extend').extendFlat; var scatterMarkerAttrs = scatterAttrs.marker; var scatterMarkerLineAttrs = scatterMarkerAttrs.line; -var markerLineAttrs = extendFlat(colorAttrs('marker.line', {editTypeOverride: 'calc'}), { +var markerLineAttrs = extendFlat(colorScaleAttrs('marker.line', {editTypeOverride: 'calc'}), { width: extendFlat({}, scatterMarkerLineAttrs.width, {editType: 'calc'}), editType: 'calc' }); -var markerAttrs = extendFlat(colorAttrs('marker'), { +var markerAttrs = extendFlat(colorScaleAttrs('marker'), { symbol: scatterMarkerAttrs.symbol, size: extendFlat({}, scatterMarkerAttrs.size, {editType: 'markerSize'}), sizeref: scatterMarkerAttrs.sizeref, diff --git a/src/traces/streamtube/attributes.js b/src/traces/streamtube/attributes.js index c1a4c08810e..47b4f2f103b 100644 --- a/src/traces/streamtube/attributes.js +++ b/src/traces/streamtube/attributes.js @@ -8,8 +8,7 @@ 'use strict'; -var colorscaleAttrs = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); var mesh3dAttrs = require('../mesh3d/attributes'); var baseAttrs = require('../../plots/attributes'); @@ -148,13 +147,11 @@ var attrs = { }) }; -extendFlat(attrs, colorscaleAttrs('', { +extendFlat(attrs, colorScaleAttrs('', { colorAttr: 'u/v/w norm', showScaleDflt: true, editTypeOverride: 'calc' -}), { - colorbar: colorbarAttrs -}); +})); var fromMesh3d = ['opacity', 'lightposition', 'lighting']; fromMesh3d.forEach(function(k) { diff --git a/src/traces/streamtube/convert.js b/src/traces/streamtube/convert.js index ced5c0b1508..a958fb3cbfc 100644 --- a/src/traces/streamtube/convert.js +++ b/src/traces/streamtube/convert.js @@ -13,6 +13,7 @@ var createTubeMesh = tube2mesh.createTubeMesh; var Lib = require('../../lib'); var parseColorScale = require('../../lib/gl_format_color').parseColorScale; +var extractOpts = require('../../components/colorscale').extractOpts; var zip3 = require('../../plots/gl3d/zip3'); var axisName2scaleIndex = {xaxis: 0, yaxis: 1, zaxis: 2}; @@ -177,7 +178,8 @@ function convert(scene, trace) { // N.B. cmin/cmax correspond to the min/max vector norm // in the u/v/w arrays, which in general is NOT equal to max // intensity that colors the tubes. - meshData.vertexIntensityBounds = [trace.cmin / trace._normMax, trace.cmax / trace._normMax]; + var cOpts = extractOpts(trace); + meshData.vertexIntensityBounds = [cOpts.min / trace._normMax, cOpts.max / trace._normMax]; // pass gl-mesh3d lighting attributes var lp = trace.lightposition; diff --git a/src/traces/surface/attributes.js b/src/traces/surface/attributes.js index 56c8f14cfe8..7e0fbfe0a6f 100644 --- a/src/traces/surface/attributes.js +++ b/src/traces/surface/attributes.js @@ -9,8 +9,7 @@ 'use strict'; var Color = require('../../components/color'); -var colorscaleAttrs = require('../../components/colorscale/attributes'); -var colorbarAttrs = require('../../components/colorbar/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); var baseAttrs = require('../../plots/attributes'); @@ -188,14 +187,12 @@ var attrs = module.exports = overrideAll(extendFlat({ }, }, -colorscaleAttrs('', { +colorScaleAttrs('', { colorAttr: 'z or surfacecolor', showScaleDflt: true, autoColorDflt: false, editTypeOverride: 'calc' }), { - colorbar: colorbarAttrs, - contours: { x: makeContourAttr('x'), y: makeContourAttr('y'), @@ -302,13 +299,13 @@ colorscaleAttrs('', { }, _deprecated: { - zauto: extendFlat({}, colorscaleAttrs.zauto, { + zauto: extendFlat({}, colorScaleAttrs.zauto, { description: 'Obsolete. Use `cauto` instead.' }), - zmin: extendFlat({}, colorscaleAttrs.zmin, { + zmin: extendFlat({}, colorScaleAttrs.zmin, { description: 'Obsolete. Use `cmin` instead.' }), - zmax: extendFlat({}, colorscaleAttrs.zmax, { + zmax: extendFlat({}, colorScaleAttrs.zmax, { description: 'Obsolete. Use `cmax` instead.' }) }, diff --git a/src/traces/surface/convert.js b/src/traces/surface/convert.js index 1be1c4d50e9..9b2e44eb123 100644 --- a/src/traces/surface/convert.js +++ b/src/traces/surface/convert.js @@ -18,6 +18,7 @@ var fill = require('ndarray-fill'); var isArrayOrTypedArray = require('../../lib').isArrayOrTypedArray; var parseColorScale = require('../../lib/gl_format_color').parseColorScale; var str2RgbaArray = require('../../lib/str2rgbarray'); +var extractOpts = require('../../components/colorscale').extractOpts; var interp2d = require('../heatmap/interp2d'); var findEmpties = require('../heatmap/find_empties'); @@ -534,7 +535,8 @@ proto.update = function(data) { opacity: data.opacity }; - params.intensityBounds = [data.cmin, data.cmax]; + var cOpts = extractOpts(data); + params.intensityBounds = [cOpts.min, cOpts.max]; // Refine surface color if necessary if(data.surfacecolor) { diff --git a/src/traces/volume/attributes.js b/src/traces/volume/attributes.js index f677ed0b1cd..adf1e14eb6f 100644 --- a/src/traces/volume/attributes.js +++ b/src/traces/volume/attributes.js @@ -8,7 +8,7 @@ 'use strict'; -var colorscaleAttrs = require('../../components/colorscale/attributes'); +var colorScaleAttrs = require('../../components/colorscale/attributes'); var isosurfaceAttrs = require('../isosurface/attributes'); var baseAttrs = require('../../plots/attributes'); @@ -55,7 +55,7 @@ var attrs = module.exports = overrideAll(extendFlat({ hovertemplate: isosurfaceAttrs.hovertemplate }, -colorscaleAttrs('', { +colorScaleAttrs('', { colorAttr: '`value`', showScaleDflt: true, editTypeOverride: 'calc' diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index 219b191fd60..03437b0ad37 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -6,13 +6,13 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var createMesh = require('gl-mesh3d'); var parseColorScale = require('../../lib/gl_format_color').parseColorScale; var str2RgbaArray = require('../../lib/str2rgbarray'); +var extractOpts = require('../../components/colorscale').extractOpts; var zip3 = require('../../plots/gl3d/zip3'); var findNearestOnAxis = require('../isosurface/convert').findNearestOnAxis; @@ -103,8 +103,9 @@ proto.update = function(data) { useFacetNormals: data.flatshading }; + var cOpts = extractOpts(data); config.vertexIntensity = data._intensity; - config.vertexIntensityBounds = [data.cmin, data.cmax]; + config.vertexIntensityBounds = [cOpts.min, cOpts.max]; config.colormap = parseColorScale(data); // Update mesh diff --git a/test/image/baselines/contour-heatmap-coloring-set-contours.png b/test/image/baselines/contour-heatmap-coloring-set-contours.png new file mode 100644 index 00000000000..2bce86a4600 Binary files /dev/null and b/test/image/baselines/contour-heatmap-coloring-set-contours.png differ diff --git a/test/image/baselines/gl3d_coloraxes.png b/test/image/baselines/gl3d_coloraxes.png new file mode 100644 index 00000000000..1030595b4c4 Binary files /dev/null and b/test/image/baselines/gl3d_coloraxes.png differ diff --git a/test/image/baselines/gl3d_ribbons.png b/test/image/baselines/gl3d_ribbons.png index 0309f7c7ccc..f9083f8357f 100644 Binary files a/test/image/baselines/gl3d_ribbons.png and b/test/image/baselines/gl3d_ribbons.png differ diff --git a/test/image/baselines/gl3d_scatter3d-colorscale-marker-and-line.png b/test/image/baselines/gl3d_scatter3d-colorscale-marker-and-line.png new file mode 100644 index 00000000000..b5b807b9f5c Binary files /dev/null and b/test/image/baselines/gl3d_scatter3d-colorscale-marker-and-line.png differ diff --git a/test/image/baselines/shared_coloraxes.png b/test/image/baselines/shared_coloraxes.png new file mode 100644 index 00000000000..9941fa6a668 Binary files /dev/null and b/test/image/baselines/shared_coloraxes.png differ diff --git a/test/image/baselines/shared_coloraxes_contour.png b/test/image/baselines/shared_coloraxes_contour.png new file mode 100644 index 00000000000..b8e32c1db24 Binary files /dev/null and b/test/image/baselines/shared_coloraxes_contour.png differ diff --git a/test/image/mocks/contour-heatmap-coloring-set-contours.json b/test/image/mocks/contour-heatmap-coloring-set-contours.json new file mode 100644 index 00000000000..8b74c0f6250 --- /dev/null +++ b/test/image/mocks/contour-heatmap-coloring-set-contours.json @@ -0,0 +1,28 @@ +{ + "data": [{ + "name": "with set contours", + "type": "contour", + "x": [1, 2, 3], + "z": [[1, 2, 3], [1, 2, 3]], + "contours": { + "coloring": "heatmap", + "start": 0, "end": 5 + }, + "colorbar": { + "x": -0.05, "xanchor": "right", + "title": {"text": "with set
contours"} + } + }, { + "type": "contour", + "name": "auto contours", + "x": [5, 6, 7], + "z": [[1, 2, 3], [1, 2, 3]], + "contours": { + "coloring": "heatmap" + }, + "colorbar": { + "x": 1.05, "xanchor": "left", + "title": {"text": "auto
contours"} + } + }] +} diff --git a/test/image/mocks/gl3d_coloraxes.json b/test/image/mocks/gl3d_coloraxes.json new file mode 100644 index 00000000000..377cb0653a9 --- /dev/null +++ b/test/image/mocks/gl3d_coloraxes.json @@ -0,0 +1,1243 @@ +{ + "data": [{ + "type": "cone", + "x": [2, 3], + "y": [2, 3], + "z": [2, 3], + "u": [0, 0], + "v": [3, 0], + "w": [0, 2], + "coloraxis": "coloraxis" + }, { + "type": "streamtube", + "x": [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2], + "y": [0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2], + "z": [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2], + "u": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1.8414709848078965, + 1.8414709848078965, + 1.8414709848078965, + 1.8414709848078965, + 1.8414709848078965, + 1.8414709848078965, + 1.8414709848078965, + 1.8414709848078965, + 1.8414709848078965, + 1.9092974268256817, + 1.9092974268256817, + 1.9092974268256817, + 1.9092974268256817, + 1.9092974268256817, + 1.9092974268256817, + 1.9092974268256817, + 1.9092974268256817, + 1.9092974268256817 + ], + "v": [ + 1, + 1, + 1, + 0.5403023058681398, + 0.5403023058681398, + 0.5403023058681398, + -0.4161468365471424, + -0.4161468365471424, + -0.4161468365471424, + 1, + 1, + 1, + 0.5403023058681398, + 0.5403023058681398, + 0.5403023058681398, + -0.4161468365471424, + -0.4161468365471424, + -0.4161468365471424, + 1, + 1, + 1, + 0.5403023058681398, + 0.5403023058681398, + 0.5403023058681398, + -0.4161468365471424, + -0.4161468365471424, + -0.4161468365471424 + ], + "w": [ + 0, + 0.08865606199840186, + 0.1693927420185106, + 0, + 0.08865606199840186, + 0.1693927420185106, + 0, + 0.08865606199840186, + 0.1693927420185106, + 0, + 0.08865606199840186, + 0.1693927420185106, + 0, + 0.08865606199840186, + 0.1693927420185106, + 0, + 0.08865606199840186, + 0.1693927420185106, + 0, + 0.08865606199840186, + 0.1693927420185106, + 0, + 0.08865606199840186, + 0.1693927420185106, + 0, + 0.08865606199840186, + 0.1693927420185106 + ], + "sizeref": 0.5, + "coloraxis": "coloraxis" + }, { + "type":"mesh3d", + "x":[0, 1, 2, 0], + "y":[0, 0, 1, 2], + "z":[0, 2, 0, 1], + "i":[0, 0, 0, 1], + "j":[1, 2, 3, 2], + "k":[2, 3, 1, 3], + "intensity": [0, 1, 2, 3], + "coloraxis": "coloraxis" + }, + + { + "type": "isosurface", + "value": [ + 0, 125, 250, 375, 500, 625, 750, 875, 1000, + -125, 0, 125, 250, 375, 500, 625, 750, 875, + -250, -125, 0, 125, 250, 375, 500, 625, 750, + -375, -250, -125, 0, 125, 250, 375, 500, 625, + -500, -375, -250, -125, 0, 125, 250, 375, 500, + -625, -500, -375, -250, -125, 0, 125, 250, 375, + -750, -625, -500, -375, -250, -125, 0, 125, 250, + -875, -750, -625, -500, -375, -250, -125, 0, 125, + -1000, -875, -750, -625, -500, -375, -250, -125, 0, + + -125, 0, 125, 250, 375, 500, 625, 750, 875, + -250, -121, 8, 137, 266, 395, 523, 652, 781, + -375, -242, -109, 23, 156, 289, 422, 555, 688, + -500, -363, -227, -90, 47, 184, 320, 457, 594, + -625, -484, -344, -203, -63, 78, 219, 359, 500, + -750, -605, -461, -316, -172, -27, 117, 262, 406, + -875, -727, -578, -430, -281, -133, 16, 164, 313, + -1000, -848, -695, -543, -391, -238, -86, 66, 219, + -1125, -969, -813, -656, -500, -344, -188, -31, 125, + + -250, -125, 0, 125, 250, 375, 500, 625, 750, + -375, -242, -109, 23, 156, 289, 422, 555, 688, + -500, -359, -219, -78, 63, 203, 344, 484, 625, + -625, -477, -328, -180, -31, 117, 266, 414, 563, + -750, -594, -438, -281, -125, 31, 188, 344, 500, + -875, -711, -547, -383, -219, -55, 109, 273, 438, + -1000, -828, -656, -484, -313, -141, 31, 203, 375, + -1125, -945, -766, -586, -406, -227, -47, 133, 313, + -1250, -1063, -875, -688, -500, -313, -125, 63, 250, + + -375, -250, -125, 0, 125, 250, 375, 500, 625, + -500, -363, -227, -90, 47, 184, 320, 457, 594, + -625, -477, -328, -180, -31, 117, 266, 414, 563, + -750, -590, -430, -270, -109, 51, 211, 371, 531, + -875, -703, -531, -359, -188, -16, 156, 328, 500, + -1000, -816, -633, -449, -266, -82, 102, 285, 469, + -1125, -930, -734, -539, -344, -148, 47, 242, 438, + -1250, -1043, -836, -629, -422, -215, -8, 199, 406, + -1375, -1156, -938, -719, -500, -281, -63, 156, 375, + + -500, -375, -250, -125, 0, 125, 250, 375, 500, + -625, -484, -344, -203, -63, 78, 219, 359, 500, + -750, -594, -438, -281, -125, 31, 188, 344, 500, + -875, -703, -531, -359, -188, -16, 156, 328, 500, + -1000, -813, -625, -438, -250, -63, 125, 313, 500, + -1125, -922, -719, -516, -313, -109, 94, 297, 500, + -1250, -1031, -813, -594, -375, -156, 63, 281, 500, + -1375, -1141, -906, -672, -438, -203, 31, 266, 500, + -1500, -1250, -1000, -750, -500, -250, 0, 250, 500, + + -625, -500, -375, -250, -125, 0, 125, 250, 375, + -750, -605, -461, -316, -172, -27, 117, 262, 406, + -875, -711, -547, -383, -219, -55, 109, 273, 438, + -1000, -816, -633, -449, -266, -82, 102, 285, 469, + -1125, -922, -719, -516, -313, -109, 94, 297, 500, + -1250, -1027, -805, -582, -359, -137, 86, 309, 531, + -1375, -1133, -891, -648, -406, -164, 78, 320, 563, + -1500, -1238, -977, -715, -453, -191, 70, 332, 594, + -1625, -1344, -1063, -781, -500, -219, 63, 344, 625, + + -750, -625, -500, -375, -250, -125, 0, 125, 250, + -875, -727, -578, -430, -281, -133, 16, 164, 313, + -1000, -828, -656, -484, -313, -141, 31, 203, 375, + -1125, -930, -734, -539, -344, -148, 47, 242, 438, + -1250, -1031, -813, -594, -375, -156, 63, 281, 500, + -1375, -1133, -891, -648, -406, -164, 78, 320, 563, + -1500, -1234, -969, -703, -438, -172, 94, 359, 625, + -1625, -1336, -1047, -758, -469, -180, 109, 398, 688, + -1750, -1438, -1125, -813, -500, -188, 125, 438, 750, + + -875, -750, -625, -500, -375, -250, -125, 0, 125, + -1000, -848, -695, -543, -391, -238, -86, 66, 219, + -1125, -945, -766, -586, -406, -227, -47, 133, 313, + -1250, -1043, -836, -629, -422, -215, -8, 199, 406, + -1375, -1141, -906, -672, -438, -203, 31, 266, 500, + -1500, -1238, -977, -715, -453, -191, 70, 332, 594, + -1625, -1336, -1047, -758, -469, -180, 109, 398, 688, + -1750, -1434, -1117, -801, -484, -168, 148, 465, 781, + -1875, -1531, -1188, -844, -500, -156, 188, 531, 875, + + -1000, -875, -750, -625, -500, -375, -250, -125, 0, + -1125, -969, -813, -656, -500, -344, -188, -31, 125, + -1250, -1063, -875, -688, -500, -313, -125, 63, 250, + -1375, -1156, -938, -719, -500, -281, -63, 156, 375, + -1500, -1250, -1000, -750, -500, -250, 0, 250, 500, + -1625, -1344, -1063, -781, -500, -219, 63, 344, 625, + -1750, -1438, -1125, -813, -500, -188, 125, 438, 750, + -1875, -1531, -1188, -844, -500, -156, 188, 531, 875, + -2000, -1625, -1250, -875, -500, -125, 250, 625, 1000 + ], + "x": [ + 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, + 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, + 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, + 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, + 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, + 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, + 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, + 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, + 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000, + + 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, + 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, + 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, + 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, + 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, + 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, + 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, + 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, + 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, 1.875, + + 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, + 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, + 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, + 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, + 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, + 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, + 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, + 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, + 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, 1.750, + + 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, + 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, + 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, + 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, + 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, + 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, + 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, + 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, + 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, 1.625, + + 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, + 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, + 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, + 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, + 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, + 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, + 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, + 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, + 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, 1.500, + + 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, + 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, + 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, + 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, + 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, + 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, + 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, + 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, + 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, 1.375, + + 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, + 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, + 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, + 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, + 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, + 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, + 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, + 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, + 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, 1.250, + + 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, + 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, + 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, + 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, + 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, + 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, + 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, + 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, + 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, + + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000 + ], + "y": [ + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, 0.875, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000 + ], + "z": [ + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000, + 0.000, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875, 1.000 + ], + + "scene": "scene2", + "coloraxis": "coloraxis2" + }, { + "type": "volume", + "value": [ + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 1500.000, 1366.025, 1000.000, 500.000, 0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 1866.025, 1732.051, 1366.025, 866.025, 366.025, -0.000, -133.975, 0.000, 366.025, 866.025, 1366.025, 1732.051, 1866.025, + 2000.000, 1866.025, 1500.000, 1000.000, 500.000, 133.975, 0.000, 133.975, 500.000, 1000.000, 1500.000, 1866.025, 2000.000, + 1866.025, 1732.051, 1366.025, 866.025, 366.025, 0.000, -133.975, 0.000, 366.025, 866.025, 1366.025, 1732.051, 1866.025, + 1500.000, 1366.025, 1000.000, 500.000, 0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 500.000, 366.025, 0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, 0.000, 366.025, 500.000, + 133.975, 0.000, -366.025, -866.025, -1366.025, -1732.051, -1866.025, -1732.051, -1366.025, -866.025, -366.025, -0.000, 133.975, + 0.000, -133.975, -500.000, -1000.000, -1500.000, -1866.025, -2000.000, -1866.025, -1500.000, -1000.000, -500.000, -133.975, 0.000, + 133.975, 0.000, -366.025, -866.025, -1366.025, -1732.051, -1866.025, -1732.051, -1366.025, -866.025, -366.025, -0.000, 133.975, + 500.000, 366.025, -0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, -0.000, 366.025, 500.000, + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + + 1500.000, 1366.025, 1000.000, 500.000, 0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 2000.000, 1866.025, 1500.000, 1000.000, 500.000, 133.975, -0.000, 133.975, 500.000, 1000.000, 1500.000, 1866.025, 2000.000, + 2366.025, 2232.051, 1866.025, 1366.025, 866.025, 500.000, 366.025, 500.000, 866.025, 1366.025, 1866.025, 2232.051, 2366.025, + 2500.000, 2366.025, 2000.000, 1500.000, 1000.000, 633.975, 500.000, 633.975, 1000.000, 1500.000, 2000.000, 2366.025, 2500.000, + 2366.025, 2232.051, 1866.025, 1366.025, 866.025, 500.000, 366.025, 500.000, 866.025, 1366.025, 1866.025, 2232.051, 2366.025, + 2000.000, 1866.025, 1500.000, 1000.000, 500.000, 133.975, -0.000, 133.975, 500.000, 1000.000, 1500.000, 1866.025, 2000.000, + 1500.000, 1366.025, 1000.000, 500.000, 0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 633.975, 500.000, 133.975, -366.025, -866.025, -1232.051, -1366.025, -1232.051, -866.025, -366.025, 133.975, 500.000, 633.975, + 500.000, 366.025, 0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, 0.000, 366.025, 500.000, + 633.975, 500.000, 133.975, -366.025, -866.025, -1232.051, -1366.025, -1232.051, -866.025, -366.025, 133.975, 500.000, 633.975, + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 1500.000, 1366.025, 1000.000, 500.000, -0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + + 1866.025, 1732.051, 1366.025, 866.025, 366.025, -0.000, -133.975, 0.000, 366.025, 866.025, 1366.025, 1732.051, 1866.025, + 2366.025, 2232.051, 1866.025, 1366.025, 866.025, 500.000, 366.025, 500.000, 866.025, 1366.025, 1866.025, 2232.051, 2366.025, + 2732.051, 2598.076, 2232.051, 1732.051, 1232.051, 866.025, 732.051, 866.025, 1232.051, 1732.051, 2232.051, 2598.076, 2732.051, + 2866.025, 2732.051, 2366.025, 1866.025, 1366.025, 1000.000, 866.025, 1000.000, 1366.025, 1866.025, 2366.025, 2732.051, 2866.025, + 2732.051, 2598.076, 2232.051, 1732.051, 1232.051, 866.025, 732.051, 866.025, 1232.051, 1732.051, 2232.051, 2598.076, 2732.051, + 2366.025, 2232.051, 1866.025, 1366.025, 866.025, 500.000, 366.025, 500.000, 866.025, 1366.025, 1866.025, 2232.051, 2366.025, + 1866.025, 1732.051, 1366.025, 866.025, 366.025, 0.000, -133.975, 0.000, 366.025, 866.025, 1366.025, 1732.051, 1866.025, + 1366.025, 1232.051, 866.025, 366.025, -133.975, -500.000, -633.975, -500.000, -133.975, 366.025, 866.025, 1232.051, 1366.025, + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 866.025, 732.051, 366.025, -133.975, -633.975, -1000.000, -1133.975, -1000.000, -633.975, -133.975, 366.025, 732.051, 866.025, + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 1366.025, 1232.051, 866.025, 366.025, -133.975, -500.000, -633.975, -500.000, -133.975, 366.025, 866.025, 1232.051, 1366.025, + 1866.025, 1732.051, 1366.025, 866.025, 366.025, -0.000, -133.975, -0.000, 366.025, 866.025, 1366.025, 1732.051, 1866.025, + + 2000.000, 1866.025, 1500.000, 1000.000, 500.000, 133.975, 0.000, 133.975, 500.000, 1000.000, 1500.000, 1866.025, 2000.000, + 2500.000, 2366.025, 2000.000, 1500.000, 1000.000, 633.975, 500.000, 633.975, 1000.000, 1500.000, 2000.000, 2366.025, 2500.000, + 2866.025, 2732.051, 2366.025, 1866.025, 1366.025, 1000.000, 866.025, 1000.000, 1366.025, 1866.025, 2366.025, 2732.051, 2866.025, + 3000.000, 2866.025, 2500.000, 2000.000, 1500.000, 1133.975, 1000.000, 1133.975, 1500.000, 2000.000, 2500.000, 2866.025, 3000.000, + 2866.025, 2732.051, 2366.025, 1866.025, 1366.025, 1000.000, 866.025, 1000.000, 1366.025, 1866.025, 2366.025, 2732.051, 2866.025, + 2500.000, 2366.025, 2000.000, 1500.000, 1000.000, 633.975, 500.000, 633.975, 1000.000, 1500.000, 2000.000, 2366.025, 2500.000, + 2000.000, 1866.025, 1500.000, 1000.000, 500.000, 133.975, 0.000, 133.975, 500.000, 1000.000, 1500.000, 1866.025, 2000.000, + 1500.000, 1366.025, 1000.000, 500.000, 0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 1133.975, 1000.000, 633.975, 133.975, -366.025, -732.051, -866.025, -732.051, -366.025, 133.975, 633.975, 1000.000, 1133.975, + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 1133.975, 1000.000, 633.975, 133.975, -366.025, -732.051, -866.025, -732.051, -366.025, 133.975, 633.975, 1000.000, 1133.975, + 1500.000, 1366.025, 1000.000, 500.000, -0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 2000.000, 1866.025, 1500.000, 1000.000, 500.000, 133.975, -0.000, 133.975, 500.000, 1000.000, 1500.000, 1866.025, 2000.000, + + 1866.025, 1732.051, 1366.025, 866.025, 366.025, 0.000, -133.975, 0.000, 366.025, 866.025, 1366.025, 1732.051, 1866.025, + 2366.025, 2232.051, 1866.025, 1366.025, 866.025, 500.000, 366.025, 500.000, 866.025, 1366.025, 1866.025, 2232.051, 2366.025, + 2732.051, 2598.076, 2232.051, 1732.051, 1232.051, 866.025, 732.051, 866.025, 1232.051, 1732.051, 2232.051, 2598.076, 2732.051, + 2866.025, 2732.051, 2366.025, 1866.025, 1366.025, 1000.000, 866.025, 1000.000, 1366.025, 1866.025, 2366.025, 2732.051, 2866.025, + 2732.051, 2598.076, 2232.051, 1732.051, 1232.051, 866.025, 732.051, 866.025, 1232.051, 1732.051, 2232.051, 2598.076, 2732.051, + 2366.025, 2232.051, 1866.025, 1366.025, 866.025, 500.000, 366.025, 500.000, 866.025, 1366.025, 1866.025, 2232.051, 2366.025, + 1866.025, 1732.051, 1366.025, 866.025, 366.025, 0.000, -133.975, 0.000, 366.025, 866.025, 1366.025, 1732.051, 1866.025, + 1366.025, 1232.051, 866.025, 366.025, -133.975, -500.000, -633.975, -500.000, -133.975, 366.025, 866.025, 1232.051, 1366.025, + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, 0.000, 500.000, 866.025, 1000.000, + 866.025, 732.051, 366.025, -133.975, -633.975, -1000.000, -1133.975, -1000.000, -633.975, -133.975, 366.025, 732.051, 866.025, + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 1366.025, 1232.051, 866.025, 366.025, -133.975, -500.000, -633.975, -500.000, -133.975, 366.025, 866.025, 1232.051, 1366.025, + 1866.025, 1732.051, 1366.025, 866.025, 366.025, -0.000, -133.975, -0.000, 366.025, 866.025, 1366.025, 1732.051, 1866.025, + + 1500.000, 1366.025, 1000.000, 500.000, 0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 2000.000, 1866.025, 1500.000, 1000.000, 500.000, 133.975, -0.000, 133.975, 500.000, 1000.000, 1500.000, 1866.025, 2000.000, + 2366.025, 2232.051, 1866.025, 1366.025, 866.025, 500.000, 366.025, 500.000, 866.025, 1366.025, 1866.025, 2232.051, 2366.025, + 2500.000, 2366.025, 2000.000, 1500.000, 1000.000, 633.975, 500.000, 633.975, 1000.000, 1500.000, 2000.000, 2366.025, 2500.000, + 2366.025, 2232.051, 1866.025, 1366.025, 866.025, 500.000, 366.025, 500.000, 866.025, 1366.025, 1866.025, 2232.051, 2366.025, + 2000.000, 1866.025, 1500.000, 1000.000, 500.000, 133.975, -0.000, 133.975, 500.000, 1000.000, 1500.000, 1866.025, 2000.000, + 1500.000, 1366.025, 1000.000, 500.000, 0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 633.975, 500.000, 133.975, -366.025, -866.025, -1232.051, -1366.025, -1232.051, -866.025, -366.025, 133.975, 500.000, 633.975, + 500.000, 366.025, 0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, 0.000, 366.025, 500.000, + 633.975, 500.000, 133.975, -366.025, -866.025, -1232.051, -1366.025, -1232.051, -866.025, -366.025, 133.975, 500.000, 633.975, + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 1500.000, 1366.025, 1000.000, 500.000, -0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 1500.000, 1366.025, 1000.000, 500.000, 0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 1866.025, 1732.051, 1366.025, 866.025, 366.025, 0.000, -133.975, 0.000, 366.025, 866.025, 1366.025, 1732.051, 1866.025, + 2000.000, 1866.025, 1500.000, 1000.000, 500.000, 133.975, 0.000, 133.975, 500.000, 1000.000, 1500.000, 1866.025, 2000.000, + 1866.025, 1732.051, 1366.025, 866.025, 366.025, 0.000, -133.975, 0.000, 366.025, 866.025, 1366.025, 1732.051, 1866.025, + 1500.000, 1366.025, 1000.000, 500.000, 0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, 0.000, 500.000, 866.025, 1000.000, + 500.000, 366.025, 0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, 0.000, 366.025, 500.000, + 133.975, 0.000, -366.025, -866.025, -1366.025, -1732.051, -1866.025, -1732.051, -1366.025, -866.025, -366.025, 0.000, 133.975, + 0.000, -133.975, -500.000, -1000.000, -1500.000, -1866.025, -2000.000, -1866.025, -1500.000, -1000.000, -500.000, -133.975, 0.000, + 133.975, 0.000, -366.025, -866.025, -1366.025, -1732.051, -1866.025, -1732.051, -1366.025, -866.025, -366.025, -0.000, 133.975, + 500.000, 366.025, -0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, -0.000, 366.025, 500.000, + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + + 500.000, 366.025, 0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, 0.000, 366.025, 500.000, + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 1366.025, 1232.051, 866.025, 366.025, -133.975, -500.000, -633.975, -500.000, -133.975, 366.025, 866.025, 1232.051, 1366.025, + 1500.000, 1366.025, 1000.000, 500.000, 0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 1366.025, 1232.051, 866.025, 366.025, -133.975, -500.000, -633.975, -500.000, -133.975, 366.025, 866.025, 1232.051, 1366.025, + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 500.000, 366.025, 0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, 0.000, 366.025, 500.000, + -0.000, -133.975, -500.000, -1000.000, -1500.000, -1866.025, -2000.000, -1866.025, -1500.000, -1000.000, -500.000, -133.975, -0.000, + -366.025, -500.000, -866.025, -1366.025, -1866.025, -2232.051, -2366.025, -2232.051, -1866.025, -1366.025, -866.025, -500.000, -366.025, + -500.000, -633.975, -1000.000, -1500.000, -2000.000, -2366.025, -2500.000, -2366.025, -2000.000, -1500.000, -1000.000, -633.975, -500.000, + -366.025, -500.000, -866.025, -1366.025, -1866.025, -2232.051, -2366.025, -2232.051, -1866.025, -1366.025, -866.025, -500.000, -366.025, + -0.000, -133.975, -500.000, -1000.000, -1500.000, -1866.025, -2000.000, -1866.025, -1500.000, -1000.000, -500.000, -133.975, -0.000, + 500.000, 366.025, -0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, -0.000, 366.025, 500.000, + + 133.975, 0.000, -366.025, -866.025, -1366.025, -1732.051, -1866.025, -1732.051, -1366.025, -866.025, -366.025, -0.000, 133.975, + 633.975, 500.000, 133.975, -366.025, -866.025, -1232.051, -1366.025, -1232.051, -866.025, -366.025, 133.975, 500.000, 633.975, + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 1133.975, 1000.000, 633.975, 133.975, -366.025, -732.051, -866.025, -732.051, -366.025, 133.975, 633.975, 1000.000, 1133.975, + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, 0.000, 500.000, 866.025, 1000.000, + 633.975, 500.000, 133.975, -366.025, -866.025, -1232.051, -1366.025, -1232.051, -866.025, -366.025, 133.975, 500.000, 633.975, + 133.975, 0.000, -366.025, -866.025, -1366.025, -1732.051, -1866.025, -1732.051, -1366.025, -866.025, -366.025, 0.000, 133.975, + -366.025, -500.000, -866.025, -1366.025, -1866.025, -2232.051, -2366.025, -2232.051, -1866.025, -1366.025, -866.025, -500.000, -366.025, + -732.051, -866.025, -1232.051, -1732.051, -2232.051, -2598.076, -2732.051, -2598.076, -2232.051, -1732.051, -1232.051, -866.025, -732.051, + -866.025, -1000.000, -1366.025, -1866.025, -2366.025, -2732.051, -2866.025, -2732.051, -2366.025, -1866.025, -1366.025, -1000.000, -866.025, + -732.051, -866.025, -1232.051, -1732.051, -2232.051, -2598.076, -2732.051, -2598.076, -2232.051, -1732.051, -1232.051, -866.025, -732.051, + -366.025, -500.000, -866.025, -1366.025, -1866.025, -2232.051, -2366.025, -2232.051, -1866.025, -1366.025, -866.025, -500.000, -366.025, + 133.975, 0.000, -366.025, -866.025, -1366.025, -1732.051, -1866.025, -1732.051, -1366.025, -866.025, -366.025, -0.000, 133.975, + + 0.000, -133.975, -500.000, -1000.000, -1500.000, -1866.025, -2000.000, -1866.025, -1500.000, -1000.000, -500.000, -133.975, 0.000, + 500.000, 366.025, 0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, 0.000, 366.025, 500.000, + 866.025, 732.051, 366.025, -133.975, -633.975, -1000.000, -1133.975, -1000.000, -633.975, -133.975, 366.025, 732.051, 866.025, + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 866.025, 732.051, 366.025, -133.975, -633.975, -1000.000, -1133.975, -1000.000, -633.975, -133.975, 366.025, 732.051, 866.025, + 500.000, 366.025, 0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, 0.000, 366.025, 500.000, + 0.000, -133.975, -500.000, -1000.000, -1500.000, -1866.025, -2000.000, -1866.025, -1500.000, -1000.000, -500.000, -133.975, 0.000, + -500.000, -633.975, -1000.000, -1500.000, -2000.000, -2366.025, -2500.000, -2366.025, -2000.000, -1500.000, -1000.000, -633.975, -500.000, + -866.025, -1000.000, -1366.025, -1866.025, -2366.025, -2732.051, -2866.025, -2732.051, -2366.025, -1866.025, -1366.025, -1000.000, -866.025, + -1000.000, -1133.975, -1500.000, -2000.000, -2500.000, -2866.025, -3000.000, -2866.025, -2500.000, -2000.000, -1500.000, -1133.975, -1000.000, + -866.025, -1000.000, -1366.025, -1866.025, -2366.025, -2732.051, -2866.025, -2732.051, -2366.025, -1866.025, -1366.025, -1000.000, -866.025, + -500.000, -633.975, -1000.000, -1500.000, -2000.000, -2366.025, -2500.000, -2366.025, -2000.000, -1500.000, -1000.000, -633.975, -500.000, + -0.000, -133.975, -500.000, -1000.000, -1500.000, -1866.025, -2000.000, -1866.025, -1500.000, -1000.000, -500.000, -133.975, -0.000, + + 133.975, 0.000, -366.025, -866.025, -1366.025, -1732.051, -1866.025, -1732.051, -1366.025, -866.025, -366.025, -0.000, 133.975, + 633.975, 500.000, 133.975, -366.025, -866.025, -1232.051, -1366.025, -1232.051, -866.025, -366.025, 133.975, 500.000, 633.975, + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 1133.975, 1000.000, 633.975, 133.975, -366.025, -732.051, -866.025, -732.051, -366.025, 133.975, 633.975, 1000.000, 1133.975, + 1000.000, 866.025, 500.000, 0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 633.975, 500.000, 133.975, -366.025, -866.025, -1232.051, -1366.025, -1232.051, -866.025, -366.025, 133.975, 500.000, 633.975, + 133.975, 0.000, -366.025, -866.025, -1366.025, -1732.051, -1866.025, -1732.051, -1366.025, -866.025, -366.025, -0.000, 133.975, + -366.025, -500.000, -866.025, -1366.025, -1866.025, -2232.051, -2366.025, -2232.051, -1866.025, -1366.025, -866.025, -500.000, -366.025, + -732.051, -866.025, -1232.051, -1732.051, -2232.051, -2598.076, -2732.051, -2598.076, -2232.051, -1732.051, -1232.051, -866.025, -732.051, + -866.025, -1000.000, -1366.025, -1866.025, -2366.025, -2732.051, -2866.025, -2732.051, -2366.025, -1866.025, -1366.025, -1000.000, -866.025, + -732.051, -866.025, -1232.051, -1732.051, -2232.051, -2598.076, -2732.051, -2598.076, -2232.051, -1732.051, -1232.051, -866.025, -732.051, + -366.025, -500.000, -866.025, -1366.025, -1866.025, -2232.051, -2366.025, -2232.051, -1866.025, -1366.025, -866.025, -500.000, -366.025, + 133.975, -0.000, -366.025, -866.025, -1366.025, -1732.051, -1866.025, -1732.051, -1366.025, -866.025, -366.025, -0.000, 133.975, + + 500.000, 366.025, -0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, -0.000, 366.025, 500.000, + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 1366.025, 1232.051, 866.025, 366.025, -133.975, -500.000, -633.975, -500.000, -133.975, 366.025, 866.025, 1232.051, 1366.025, + 1500.000, 1366.025, 1000.000, 500.000, -0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 1366.025, 1232.051, 866.025, 366.025, -133.975, -500.000, -633.975, -500.000, -133.975, 366.025, 866.025, 1232.051, 1366.025, + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 500.000, 366.025, -0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, -0.000, 366.025, 500.000, + -0.000, -133.975, -500.000, -1000.000, -1500.000, -1866.025, -2000.000, -1866.025, -1500.000, -1000.000, -500.000, -133.975, -0.000, + -366.025, -500.000, -866.025, -1366.025, -1866.025, -2232.051, -2366.025, -2232.051, -1866.025, -1366.025, -866.025, -500.000, -366.025, + -500.000, -633.975, -1000.000, -1500.000, -2000.000, -2366.025, -2500.000, -2366.025, -2000.000, -1500.000, -1000.000, -633.975, -500.000, + -366.025, -500.000, -866.025, -1366.025, -1866.025, -2232.051, -2366.025, -2232.051, -1866.025, -1366.025, -866.025, -500.000, -366.025, + -0.000, -133.975, -500.000, -1000.000, -1500.000, -1866.025, -2000.000, -1866.025, -1500.000, -1000.000, -500.000, -133.975, -0.000, + 500.000, 366.025, -0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, -0.000, 366.025, 500.000, + + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 1500.000, 1366.025, 1000.000, 500.000, -0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 1866.025, 1732.051, 1366.025, 866.025, 366.025, -0.000, -133.975, -0.000, 366.025, 866.025, 1366.025, 1732.051, 1866.025, + 2000.000, 1866.025, 1500.000, 1000.000, 500.000, 133.975, -0.000, 133.975, 500.000, 1000.000, 1500.000, 1866.025, 2000.000, + 1866.025, 1732.051, 1366.025, 866.025, 366.025, -0.000, -133.975, -0.000, 366.025, 866.025, 1366.025, 1732.051, 1866.025, + 1500.000, 1366.025, 1000.000, 500.000, -0.000, -366.025, -500.000, -366.025, -0.000, 500.000, 1000.000, 1366.025, 1500.000, + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000, + 500.000, 366.025, -0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, -0.000, 366.025, 500.000, + 133.975, 0.000, -366.025, -866.025, -1366.025, -1732.051, -1866.025, -1732.051, -1366.025, -866.025, -366.025, -0.000, 133.975, + -0.000, -133.975, -500.000, -1000.000, -1500.000, -1866.025, -2000.000, -1866.025, -1500.000, -1000.000, -500.000, -133.975, -0.000, + 133.975, -0.000, -366.025, -866.025, -1366.025, -1732.051, -1866.025, -1732.051, -1366.025, -866.025, -366.025, -0.000, 133.975, + 500.000, 366.025, -0.000, -500.000, -1000.000, -1366.025, -1500.000, -1366.025, -1000.000, -500.000, -0.000, 366.025, 500.000, + 1000.000, 866.025, 500.000, -0.000, -500.000, -866.025, -1000.000, -866.025, -500.000, -0.000, 500.000, 866.025, 1000.000 + ], + "x": [ + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000 + ], + "y": [ + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, + + 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, + 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, 0.083, + 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, 0.167, + 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, 0.250, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, 0.417, + 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, + 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, 0.583, + 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, + 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, 0.750, + 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, 0.833, + 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, 0.917, + 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000 + ], + "z": [ + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000, + 0.000, 0.007, 0.028, 0.063, 0.111, 0.174, 0.250, 0.340, 0.444, 0.563, 0.694, 0.840, 1.000 + ], + + "scene": "scene2", + "coloraxis": "coloraxis2" + }], + + "layout": { + "title": "gl3d trace with shared coloraxes", + + "coloraxis": { + "colorscale": "Viridis", + "colorbar": { + "x": -0.1, + "xanchor": "right" + } + }, + "coloraxis2": { + "colorscale": "Viridis", + "colorbar": { + "x": 1.1, + "xanchor": "left" + } + } + } +} diff --git a/test/image/mocks/gl3d_ribbons.json b/test/image/mocks/gl3d_ribbons.json index 0aa647b52b1..dd993dc2b0a 100644 --- a/test/image/mocks/gl3d_ribbons.json +++ b/test/image/mocks/gl3d_ribbons.json @@ -607,7 +607,8 @@ 0.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -1216,7 +1217,8 @@ 1.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -1825,7 +1827,8 @@ 2.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -2434,7 +2437,8 @@ 3.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -3043,7 +3047,8 @@ 4.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -3652,7 +3657,8 @@ 5.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -4261,7 +4267,8 @@ 6.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -4870,7 +4877,8 @@ 7.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -5479,7 +5487,8 @@ 8.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -6088,7 +6097,8 @@ 9.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -6697,7 +6707,8 @@ 10.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -7306,7 +7317,8 @@ 11.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -7915,7 +7927,8 @@ 12.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -8524,7 +8537,8 @@ 13.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -9133,7 +9147,8 @@ 14.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -9742,7 +9757,8 @@ 15.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -10351,7 +10367,8 @@ 16.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -10960,7 +10977,8 @@ 17.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" }, { "z": [ @@ -11569,7 +11587,8 @@ 18.75 ] ], - "type": "surface" + "type": "surface", + "coloraxis": "coloraxis" } ], "layout": { diff --git a/test/image/mocks/gl3d_scatter3d-colorscale-marker-and-line.json b/test/image/mocks/gl3d_scatter3d-colorscale-marker-and-line.json new file mode 100644 index 00000000000..6e08b7f8c23 --- /dev/null +++ b/test/image/mocks/gl3d_scatter3d-colorscale-marker-and-line.json @@ -0,0 +1,39 @@ +{ + "data": [ + { + "x": [1, 2, 4, 8, 16], + "y": [-1, -2, -4, -8, -16], + "z": [0, 1, 0, 1, 0], + "type": "scatter3d", + "mode": "lines+markers", + "line": { + "color": [0, 0.25, 0.5, 0.75, 1.0], + "width": 10, + "colorscale": "Blues", + "colorbar": { + "len": 0.5, + "y": 1, + "yanchor": "top", + "title": {"text": "line colorscale", "side": "right"} + } + }, + "marker": { + "color": [1.0, 0.75, 0.5, 0.25, 0], + "size": 10, + "colorscale": "Reds", + "colorbar": { + "len": 0.5, + "y": 0, + "yanchor": "bottom", + "title": {"text": "marker colorscale", "side": "right"} + } + + } + } + ], + "layout": { + "title": "Scatter3d show line AND marker color scales", + "width": 600, + "height": 600 + } +} diff --git a/test/image/mocks/shared_coloraxes.json b/test/image/mocks/shared_coloraxes.json new file mode 100644 index 00000000000..43634c68042 --- /dev/null +++ b/test/image/mocks/shared_coloraxes.json @@ -0,0 +1,125 @@ +{ + "data": [{ + "name": "heatmap 1 - coloraxis 1", + "type": "heatmap", + "x": [1, 2], + "z": [[1, 2], [4, -3]], + "coloraxis": "coloraxis" + }, { + "name": "heatmap 2 - coloraxis 1", + "type": "heatmap", + "x": [3, 4], + "z": [[10, 2], [4, 3]], + "coloraxis": "coloraxis" + }, { + "name": "heatmap 3 - coloraxis 1", + "type": "heatmap", + "x": [5, 6], + "z": [[3, -2], [4, 5]], + "coloraxis": "coloraxis" + }, + + { + "name": "bar marker.color scale - coloraxis 2", + "type": "bar", + "y": [1, 2, 1], + "marker": { + "color": [20, 2, 30], + "coloraxis": "coloraxis2" + }, + "xaxis": "x2", + "yaxis": "y2" + }, { + "name": "marker.line.color scale - coloraxis 2", + "mode": "markers", + "y": [1.5, 2.5, 1.5], + "marker": { + "color": "#444", + "size": 10, + "line": { + "width": 2, + "color": [12, 2, 10], + "coloraxis": "coloraxis2" + } + }, + "xaxis": "x2", + "yaxis": "y2" + }, + + { + "name": "histogram2d 1 - coloraxis 3", + "type": "histogram2d", + "x": [1, 2, 3, 3, 3, 4, 5], + "y": [1, 2, 5, 5, 5, 2, 1], + "coloraxis": "coloraxis3", + "xaxis": "x3", + "yaxis": "y3" + }, { + "name": "histogram2d 1 - coloraxis 3", + "type": "histogram2d", + "x": [1, 2, 5, 5, 5, 2, 1], + "y": [1, 2, 3, 3, 3, 4, 5], + "coloraxis": "coloraxis3", + "xaxis": "x3", + "yaxis": "y3" + }, + + { + "name": "scatterpolar marker.color - coloraxis 3 N.B. across subplots!", + "type": "scatterpolar", + "mode": "markers", + "r": [1, 2, 3], + "marker": { + "symbol": "diamond", + "size": 10, + "color": [2, 3, 4], + "coloraxis": "coloraxis3" + } + }], + "layout": { + "showlegend": false, + "grid": {"rows": 2, "columns": 2, "pattern": "independent"}, + "polar": {"domain": {"row": 1, "column": 1}}, + + "coloraxis": { + "colorbar": { + "len": 0.5, + "x": -0.05, "xanchor": "right", + "y": 1, "yanchor": "top" + } + }, + "coloraxis2": { + "colorbar": { + "len": 0.5, + "x": 1.05, "xanchor": "left", + "y": 1, "yanchor": "top" + } + }, + "coloraxis3": { + "cauto": false, + "cmax": 5, + "colorscale": "Viridis", + "colorbar": { + "len": 0.5, + "x": -0.05, "xanchor": "right", + "y": 0, "yanchor": "bottom", + "dtick": 1 + } + }, + + "colorscale": { + "sequential": "Greens" + }, + + "template": { + "layout": { + "coloraxis": { + "colorbar": {"outlinewidth": 2} + }, + "coloraxis3": { + "colorbar": {"outlinecolor": "blue", "outlinewidth": 4} + } + } + } + } +} diff --git a/test/image/mocks/shared_coloraxes_contour.json b/test/image/mocks/shared_coloraxes_contour.json new file mode 100644 index 00000000000..7f5df767f46 --- /dev/null +++ b/test/image/mocks/shared_coloraxes_contour.json @@ -0,0 +1,138 @@ +{ + "data": [{ + "name": "contour coloring:heatmap 1 - coloraxis 1", + "type": "contour", + "x": [1, 2], + "z": [[1, 2], [10, -3]], + "contours": {"coloring": "heatmap"}, + "coloraxis": "coloraxis" + }, { + "name": "heatmap - coloraxis 1", + "type": "heatmap", + "x": [3, 4], + "z": [[4, 2], [4, 3]], + "coloraxis": "coloraxis" + }, { + "name": "markers - coloraxis 1", + "mode": "markers", + "x": [1, 3, 5], + "y": [1, 2, 1], + "marker": { + "size": 20, + "symbol": "diamond", + "color": [1, 2, 0], + "coloraxis": "coloraxis" + } + }, { + "name": "contour coloring:heatmap 2 - coloraxis 1", + "type": "contour", + "x": [5, 6], + "z": [[3, -6], [4, 5]], + "contours": {"coloring": "heatmap"}, + "coloraxis": "coloraxis" + }, + + { + "name": "contour 1 - coloraxis 2", + "type": "contour", + "x": [1, 2], + "z": [[3, -6], [4, 5]], + "coloraxis": "coloraxis2", + "xaxis": "x2", + "yaxis": "y2" + }, { + + "name": "contour 2 - coloraxis 2", + "type": "contour", + "x": [2, 3], + "z": [[1, 2], [10, -3]], + "coloraxis": "coloraxis2", + "xaxis": "x2", + "yaxis": "y2" + }, + + { + "name": "contour coloring:lines - coloraxis 3", + "type": "contour", + "x": [1, 2], + "z": [[3, -6], [4, 5]], + "contours": {"coloring": "lines"}, + "line": {"width": 2}, + "coloraxis": "coloraxis3", + "xaxis": "x3", + "yaxis": "y3" + }, { + + "name": "contour coloring:lines 2 - coloraxis 3", + "type": "contour", + "x": [2, 3], + "z": [[1, 2], [10, -3]], + "contours": {"coloring": "lines"}, + "line": {"width": 2}, + "coloraxis": "coloraxis3", + "xaxis": "x3", + "yaxis": "y3" + }, + + { + "name": "contour coloring:heatmap edge case - coloraxis 4", + "type": "contour", + "x": [1, 2, 3], + "z": [[1, 2, 3], [1, 2, 3]], + "contours": { + "coloring": "heatmap", + "start": 1, "end": 5 + }, + "coloraxis": "coloraxis4", + "xaxis": "x4", + "yaxis": "y4" + }, { + "name": "contour coloring:heatmap edge case 2 - coloraxis 4", + "type": "contour", + "x": [5, 6, 7], + "z": [[2, 3, 4], [2, 3, 4]], + "contours": { + "coloring": "heatmap", + "start": 0, "end": 3 + }, + "coloraxis": "coloraxis4", + "xaxis": "x4", + "yaxis": "y4" + }], + + "layout": { + "showlegend": false, + "grid": {"rows": 2, "columns": 2, "pattern": "independent"}, + + "coloraxis": { + "colorbar": { + "len": 0.5, + "x": -0.05, "xanchor": "right", + "y": 1, "yanchor": "top" + } + }, + "coloraxis2": { + "colorbar": { + "dtick": 1, + "len": 0.5, + "x": 1.05, "xanchor": "left", + "y": 1, "yanchor": "top" + } + }, + "coloraxis3": { + "colorbar": { + "len": 0.5, + "x": -0.05, "xanchor": "right", + "y": 0, "yanchor": "bottom" + } + }, + "coloraxis4": { + "colorscale": "Viridis", + "colorbar": { + "len": 0.5, + "x": 1.05, "xanchor": "left", + "y": 0, "yanchor": "bottom" + } + } + } +} diff --git a/test/jasmine/bundle_tests/plotschema_test.js b/test/jasmine/bundle_tests/plotschema_test.js index a515f5523cd..b92a9ec9b62 100644 --- a/test/jasmine/bundle_tests/plotschema_test.js +++ b/test/jasmine/bundle_tests/plotschema_test.js @@ -136,7 +136,8 @@ describe('plot schema', function() { 'xaxis', 'yaxis', 'scene', 'geo', 'ternary', 'mapbox', 'polar', // not really a 'subplot' object but supports yaxis, yaxis2, yaxis3, // ... counters, so list it here - 'xaxis.rangeslider.yaxis' + 'xaxis.rangeslider.yaxis', + 'coloraxis' ]; // check if the subplot objects have '_isSubplotObj' @@ -146,7 +147,7 @@ describe('plot schema', function() { plotSchema.layout.layoutAttributes, astr + '.' + IS_SUBPLOT_OBJ ).get() - ).toBe(true); + ).toBe(true, astr); }); // check that no other object has '_isSubplotObj' diff --git a/test/jasmine/tests/colorbar_test.js b/test/jasmine/tests/colorbar_test.js index 9468df91489..1314d3b6b7c 100644 --- a/test/jasmine/tests/colorbar_test.js +++ b/test/jasmine/tests/colorbar_test.js @@ -2,6 +2,8 @@ var d3 = require('d3'); var Plotly = require('@lib/index'); var Colorbar = require('@src/components/colorbar'); +var Plots = require('@src/plots/plots'); +var subroutines = require('@src/plot_api/subroutines'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); @@ -10,7 +12,6 @@ var supplyAllDefaults = require('../assets/supply_defaults'); var assertPlotSize = require('../assets/custom_assertions').assertPlotSize; var drag = require('../assets/drag'); - describe('Test colorbar:', function() { 'use strict'; @@ -226,6 +227,41 @@ describe('Test colorbar:', function() { .then(done); }); + it('can show and hide colorbars of shared color axes', function(done) { + Plotly.newPlot(gd, [{ + y: [1, 2, 3], + marker: {color: [1, 2, 3], coloraxis: 'coloraxis'} + }, { + y: [1, 2, 3], + marker: {color: [1, 0, 3], coloraxis: 'coloraxis'} + }], { + showlegend: false, + height: 500, + width: 500, + margin: {l: 50, r: 50, t: 50, b: 50} + }) + .then(function() { + assertCB('initial', true, {expandedMarginR: true}); + + return Plotly.relayout(gd, {'coloraxis.showscale': false}); + }) + .then(function() { + assertCB('hidden', false, {expandedMarginR: false}); + + return Plotly.relayout(gd, {'coloraxis.showscale': true, 'coloraxis.colorbar.x': 0.7}); + }) + .then(function() { + assertCB('mid-plot', true, {expandedMarginR: false}); + + return Plotly.relayout(gd, {'coloraxis.colorbar.x': 1.1}); + }) + .then(function() { + assertCB('far right', true, {expandedMarginR: true}); + }) + .catch(failTest) + .then(done); + }); + // histogram colorbars could not be edited before it('can show and hide histogram colorbars', function(done) { Plotly.newPlot(gd, [{ @@ -399,5 +435,93 @@ describe('Test colorbar:', function() { .catch(failTest) .then(done); }); + + it('can drag colorbars linked to color axes in editable mode', function(done) { + Plotly.newPlot(gd, + [{z: [[1, 2], [3, 4]], type: 'heatmap', coloraxis: 'coloraxis'}], + {coloraxis: {}, width: 400, height: 400}, + {editable: true} + ) + .then(function() { + expect(gd.layout.coloraxis.colorbar).toBeUndefined(); + expect(gd._fullLayout.coloraxis.colorbar.x).toBe(1.02); + expect(gd._fullLayout.coloraxis.colorbar.y).toBe(0.5); + return drag(getCBNode(), -100, 100); + }) + .then(function() { + expect(gd.layout.coloraxis.colorbar.x).toBeWithin(0.591, 0.01); + expect(gd.layout.coloraxis.colorbar.y).toBeWithin(0.045, 0.01); + expect(gd._fullLayout.coloraxis.colorbar.x).toBeWithin(0.591, 0.01); + expect(gd._fullLayout.coloraxis.colorbar.y).toBeWithin(0.045, 0.01); + }) + .catch(failTest) + .then(done); + }); + + it('can edit colorbar visuals in optimized edit pathway', function(done) { + spyOn(subroutines, 'doColorBars').and.callThrough(); + spyOn(Plots, 'doCalcdata').and.callThrough(); + + function getOutline(cb) { + return Number(cb.select('.cboutline').node().style['stroke-width']); + } + + function _assert(msg, exp) { + var gd3 = d3.select(gd); + var cb0 = gd3.select('.cbtrace0'); + var cb1 = gd3.select('.cbcoloraxis'); + + if(msg !== 'base') { + expect(subroutines.doColorBars).toHaveBeenCalledTimes(1); + expect(Plots.doCalcdata).toHaveBeenCalledTimes(0); + } + subroutines.doColorBars.calls.reset(); + Plots.doCalcdata.calls.reset(); + + expect(getOutline(cb0)).toBe(exp.outline[0], 'trace0 cb outline'); + expect(getOutline(cb1)).toBe(exp.outline[1], 'coloraxis cb outline'); + } + + Plotly.newPlot(gd, [{ + type: 'heatmap', + z: [[1, 2, 3], [2, 1, 2]], + uid: 'trace0' + }, { + y: [1, 2, 3], + marker: {color: [2, 1, 2], coloraxis: 'coloraxis'} + }], { + width: 500, + height: 500 + }) + .then(function() { _assert('base', {outline: [1, 1]}); }) + .then(function() { + return Plotly.restyle(gd, 'colorbar.outlinewidth', 2, [0]); + }) + .then(function() { _assert('after restyle', {outline: [2, 1]}); }) + .then(function() { + return Plotly.relayout(gd, 'coloraxis.colorbar.outlinewidth', 5); + }) + .then(function() { _assert('after relayout', {outline: [2, 5]}); }) + .then(function() { + return Plotly.update(gd, {'colorbar.outlinewidth': 1}, {}, [0]); + }) + .then(function() { _assert('after trace update', {outline: [1, 5]}); }) + .then(function() { + return Plotly.update(gd, {}, {'coloraxis.colorbar.outlinewidth': 1}); + }) + .then(function() { _assert('after layout update', {outline: [1, 1]}); }) + .then(function() { + gd.data[0].colorbar = {outlinewidth: 10}; + return Plotly.react(gd, gd.data, gd.layout); + }) + .then(function() { _assert('after trace react', {outline: [10, 1]}); }) + .then(function() { + gd.layout.coloraxis = {colorbar: {outlinewidth: 10}}; + return Plotly.react(gd, gd.data, gd.layout); + }) + .then(function() { _assert('after layout trace', {outline: [10, 10]}); }) + .catch(failTest) + .then(done); + }); }); }); diff --git a/test/jasmine/tests/colorscale_test.js b/test/jasmine/tests/colorscale_test.js index 281ab5ff2b9..551050987c7 100644 --- a/test/jasmine/tests/colorscale_test.js +++ b/test/jasmine/tests/colorscale_test.js @@ -13,14 +13,12 @@ var destroyGraphDiv = require('../assets/destroy_graph_div'); var failTest = require('../assets/fail_test'); var supplyAllDefaults = require('../assets/supply_defaults'); -function _supply(trace, layout) { +function _supply(arg, layout) { var gd = { - data: [trace], + data: Array.isArray(arg) ? arg : [arg], layout: layout || {} }; - supplyAllDefaults(gd); - return gd; } @@ -259,7 +257,7 @@ describe('Test colorscale:', function() { } beforeEach(function() { - traceOut = {}; + traceOut = {_module: {}}; }); it('should set auto to true when min/max are valid', function() { @@ -330,7 +328,10 @@ describe('Test colorscale:', function() { } beforeEach(function() { - traceOut = { marker: {} }; + traceOut = { + marker: {}, + _module: {} + }; }); it('should coerce autocolorscale to true by default', function() { @@ -368,6 +369,117 @@ describe('Test colorscale:', function() { }); }); + describe('handleDefaults (coloraxis version)', function() { + it('should not coerced colorscale/colorbar attributes when referencing a shared color axis', function() { + var gd = _supply([ + {type: 'heatmap', z: [[0]]}, + {type: 'heatmap', z: [[2]], coloraxis: 'coloraxis'}, + {type: 'heatmap', z: [[2]], coloraxis: 'coloraxis'}, + ]); + + var fullData = gd._fullData; + var fullLayout = gd._fullLayout; + + var zAttrs = ['zauto', 'colorscale', 'reversescale']; + zAttrs.forEach(function(attr) { + expect(fullData[0][attr]).not.toBe(undefined, 'trace 0 ' + attr); + expect(fullData[1][attr]).toBe(undefined, 'trace 1 ' + attr); + expect(fullData[2][attr]).toBe(undefined, 'trace 2 ' + attr); + }); + + var cAttrs = ['cauto', 'colorscale', 'reversescale']; + cAttrs.forEach(function(attr) { + expect(fullLayout.coloraxis[attr]).not.toBe(undefined, 'coloraxis ' + attr); + }); + + expect(fullData[0].coloraxis).toBe(undefined); + expect(fullData[1].coloraxis).toBe('coloraxis'); + expect(fullData[2].coloraxis).toBe('coloraxis'); + expect(fullLayout.coloraxis.coloraxis).toBe(undefined); + expect(fullLayout.coloraxis.showscale).toBe(true, 'showscale is true by dflt in color axes'); + }); + + it('should keep track of all the color axes referenced in the traces', function() { + var gd = _supply([ + {type: 'heatmap', z: [[1]], coloraxis: 'coloraxis'}, + {y: [1], marker: {color: [1], coloraxis: 'coloraxis'}}, + {type: 'contour', z: [[1]], coloraxis: 'coloraxis3'}, + // invalid + {y: [1], marker: {color: [1], coloraxis: 'c1'}}, + // not coerced - visible:false trace + {marker: {color: [1], coloraxis: 'coloraxis2'}} + ], { + // not referenced in traces, shouldn't get coerced + coloraxis4: {colorscale: 'Viridis'} + }); + + var fullData = gd._fullData; + var fullLayout = gd._fullLayout; + + expect(fullData[0].coloraxis).toBe('coloraxis'); + expect(fullData[1].marker.coloraxis).toBe('coloraxis'); + expect(fullData[2].coloraxis).toBe('coloraxis3'); + expect(fullData[3].coloraxis).toBe(undefined); + + expect(fullData[0]._colorAx).toBe(fullLayout.coloraxis); + expect(fullData[1].marker._colorAx).toBe(fullLayout.coloraxis); + expect(fullData[2]._colorAx).toBe(fullLayout.coloraxis3); + + expect(fullLayout.coloraxis).not.toBe(undefined); + expect(fullLayout.coloraxis2).toBe(undefined); + expect(fullLayout.coloraxis3).not.toBe(undefined); + expect(fullLayout.coloraxis4).toBe(undefined); + + expect(Object.keys(fullLayout._colorAxes)).toEqual(['coloraxis', 'coloraxis3']); + expect(fullLayout._colorAxes.coloraxis[0]).toBe('heatmap'); + expect(fullLayout._colorAxes.coloraxis3[0]).toBe('fill'); + }); + + it('should log warning when trying to shared color axis with traces with incompatible color bars', function() { + spyOn(Lib, 'warn'); + + var gd = _supply([ + // ok + {type: 'heatmap', z: [[1]], coloraxis: 'coloraxis'}, + {y: [1], marker: {color: [1], coloraxis: 'coloraxis'}}, + {type: 'contour', z: [[1]], contours: {coloring: 'heatmap'}, coloraxis: 'coloraxis'}, + // invalid (coloring dflt is 'fill') + {type: 'heatmap', z: [[1]], coloraxis: 'coloraxis2'}, + {type: 'contour', z: [[1]], coloraxis: 'coloraxis2'}, + // invalid + {type: 'contour', z: [[1]], contours: {coloring: 'lines'}, coloraxis: 'coloraxis3'}, + {type: 'contour', z: [[1]], contours: {coloring: 'heatmap'}, coloraxis: 'coloraxis3'}, + // ok + {type: 'contour', z: [[1]], coloraxis: 'coloraxis4'}, + {type: 'contour', z: [[1]], coloraxis: 'coloraxis4'}, + // ok + {type: 'contour', z: [[1]], contours: {coloring: 'lines'}, coloraxis: 'coloraxis5'}, + {type: 'contour', z: [[1]], contours: {coloring: 'lines'}, coloraxis: 'coloraxis5'} + ]); + + var fullData = gd._fullData; + var fullLayout = gd._fullLayout; + + expect(Object.keys(fullLayout._colorAxes)).toEqual(['coloraxis', 'coloraxis4', 'coloraxis5']); + expect(fullLayout._colorAxes.coloraxis[0]).toBe('heatmap'); + expect(fullLayout._colorAxes.coloraxis4[0]).toBe('fill'); + expect(fullLayout._colorAxes.coloraxis5[0]).toBe('lines'); + expect(Lib.warn).toHaveBeenCalledTimes(2); + + var zAttrs = ['zauto', 'colorscale', 'reversescale']; + var withColorAx = [0, 1, 2, 7, 8, 9, 10]; + var woColorAx = [3, 4, 5, 6]; + zAttrs.forEach(function(attr) { + withColorAx.forEach(function(i) { + expect(fullData[i][attr]).toBe(undefined, 'trace ' + i + ' ' + attr); + }); + woColorAx.forEach(function(i) { + expect(fullData[i][attr]).not.toBe(undefined, 'trace ' + i + ' ' + attr); + }); + }); + }); + }); + describe('calc', function() { var calcColorscale = Colorscale.calc; var trace, z; @@ -520,6 +632,27 @@ describe('Test colorscale:', function() { expect(trace.autocolorscale).toBe(true); expect(trace.colorscale).toEqual(colorscale); }); + + it('should compute min/max across trace linked to same color axis', function() { + gd = _supply([ + {type: 'heatmap', z: [[1, 3, 4], [2, 3, 1]], coloraxis: 'coloraxis'}, + {y: [1, 3, 1], marker: {color: [3, 4, -2], coloraxis: 'coloraxis'}}, + ]); + + Plots.doCalcdata(gd); + + var fullData = gd._fullData; + expect(fullData[0].zmin).toBe(undefined); + expect(fullData[0].zmax).toBe(undefined); + expect(fullData[1].marker.cmin).toBe(undefined); + expect(fullData[1].marker.cmax).toBe(undefined); + + var fullLayout = gd._fullLayout; + expect(fullLayout.coloraxis.cmin).toBe(-2); + expect(fullLayout.coloraxis._cmin).toBe(-2); + expect(fullLayout.coloraxis.cmax).toBe(4); + expect(fullLayout.coloraxis._cmax).toBe(4); + }); }); describe('extractScale + makeColorScaleFunc', function() { @@ -535,11 +668,11 @@ describe('Test colorscale:', function() { it('should constrain color array values between cmin and cmax', function() { var trace = { colorscale: scale, - pmin: 2, - pmax: 3 + cmin: 2, + cmax: 3 }; - var specs = Colorscale.extractScale(trace, {cLetter: 'p'}); + var specs = Colorscale.extractScale(trace); var sclFunc = Colorscale.makeColorScaleFunc(specs); var color1 = sclFunc(1); @@ -557,11 +690,11 @@ describe('Test colorscale:', function() { var trace = { colorscale: scale, reversescale: true, - pmin: 2, - pmax: 3 + zmin: 2, + zmax: 3 }; - var specs = Colorscale.extractScale(trace, {cLetter: 'p'}); + var specs = Colorscale.extractScale(trace); var sclFunc = Colorscale.makeColorScaleFunc(specs); var color1 = sclFunc(1); @@ -574,6 +707,29 @@ describe('Test colorscale:', function() { expect(color3).toEqual(color4); expect(color4).toEqual('rgb(5, 10, 172)'); }); + + it('should extract coloraxis options, if present', function() { + var trace = { + _colorAx: { + colorscale: scale, + cmin: 2, + cmax: 3 + } + }; + + var specs = Colorscale.extractScale(trace); + var sclFunc = Colorscale.makeColorScaleFunc(specs); + + var color1 = sclFunc(1); + var color2 = sclFunc(2); + var color3 = sclFunc(3); + var color4 = sclFunc(4); + + expect(color1).toEqual(color2); + expect(color1).toEqual('rgb(5, 10, 172)'); + expect(color3).toEqual(color4); + expect(color4).toEqual('rgb(178, 10, 28)'); + }); }); }); @@ -865,6 +1021,86 @@ describe('Test colorscale restyle calls:', function() { .then(done); }); + it('should be able to toggle between autocolorscale true/false and set colorscales (coloraxis case)', function(done) { + function _assert(msg, exp) { + var mcc = []; + d3.selectAll('path.point').each(function() { mcc.push(getFill(this)); }); + expect(mcc).toEqual(exp.mcc); + + expect(gd._fullLayout.coloraxis.colorscale).toEqual(exp.colorscale); + expect(gd._fullLayout.coloraxis.autocolorscale).toBe(exp.autocolorscale, msg); + expect((gd.layout.coloraxis || {}).colorscale).toEqual(exp.colorscaleIn); + expect((gd.layout.coloraxis || {}).autocolorscale).toBe(exp.autocolorscaleIn, msg); + } + + // update via, assert then assert again (and again ;) after non-calc edits + function _run(msg, updateObj, exp) { + return Plotly.relayout(gd, updateObj) + .then(function() { _assert(msg, exp); }) + .then(function() { return Plotly.relayout(gd, 'xaxis.range', [-1, 5]); }) + .then(function() { _assert(msg + ' after axrange relayout', exp); }) + .then(function() { return Plotly.relayout(gd, 'xaxis.autorange', true); }) + .then(function() { _assert(msg + ' after autorange', exp); }) + .then(function() { return Plotly.restyle(gd, 'marker.symbol', 'square'); }) + .then(function() { _assert(msg + ' after marker.symbol restyle', exp); }) + .then(function() { return Plotly.restyle(gd, 'marker.symbol', null); }) + .then(function() { _assert(msg + ' back to original marker.symbol', exp); }); + } + + var rdbu = ['rgb(5, 10, 172)', 'rgb(53, 70, 208)', 'rgb(227, 153, 104)', + 'rgb(53, 70, 208)', 'rgb(53, 70, 208)', 'rgb(178, 10, 28)']; + var grns = ['rgb(0, 68, 27)', 'rgb(12, 119, 52)', 'rgb(174, 222, 167)', + 'rgb(12, 119, 52)', 'rgb(12, 119, 52)', 'rgb(247, 252, 245)']; + + Plotly.plot(gd, [{ + mode: 'markers', + y: [1, 2, 3], + marker: {color: [-1, 0, 3], coloraxis: 'coloraxis'} + }, { + mode: 'markers', + y: [2, 3, 4], + marker: {color: [0, 0, 5], coloraxis: 'coloraxis'} + }]) + .then(function() { + _assert('base (autocolorscale:true by dflt)', { + mcc: rdbu, + autocolorscale: true, + autocolorscaleIn: undefined, + colorscale: Colorscale.scales.RdBu, + colorscaleIn: undefined + }); + }) + .then(function() { + return _run('set *Greens* colorscale', {'coloraxis.colorscale': 'Greens'}, { + mcc: grns, + autocolorscale: false, + autocolorscaleIn: false, + colorscale: Colorscale.scales.Greens, + colorscaleIn: 'Greens' + }); + }) + .then(function() { + return _run('back to autocolorscale:true', {'coloraxis.autocolorscale': true}, { + mcc: rdbu, + autocolorscale: true, + autocolorscaleIn: true, + colorscale: Colorscale.scales.RdBu, + colorscaleIn: 'Greens' + }); + }) + .then(function() { + return _run('back to autocolorscale:false w/ colorscale set', {'coloraxis.autocolorscale': false}, { + mcc: grns, + autocolorscale: false, + autocolorscaleIn: false, + colorscale: Colorscale.scales.Greens, + colorscaleIn: 'Greens' + }); + }) + .catch(failTest) + .then(done); + }); + it('should work with templates', function(done) { function _assert(msg, exp) { var mcc = []; @@ -910,11 +1146,14 @@ describe('Test colorscale restyle calls:', function() { x: [1, 2, 3], y: [1, 2, 3], z: [1, 2, 1], - marker: {color: [1, 2, 1], showscale: true} + marker: {color: [1, 2, 1], showscale: true}, + line: {color: [2, 3, 4], showscale: true} }]) .then(function() { expect(gd._fullData[0].marker.cmin).toBe(1); expect(gd._fullData[0].marker.cmax).toBe(2); + expect(gd._fullData[0].line.cmin).toBe(2); + expect(gd._fullData[0].line.cmax).toBe(4); }) .then(function() { // some non-calc edit @@ -923,6 +1162,8 @@ describe('Test colorscale restyle calls:', function() { .then(function() { expect(gd._fullData[0].marker.cmin).toBe(1); expect(gd._fullData[0].marker.cmax).toBe(2); + expect(gd._fullData[0].line.cmin).toBe(2); + expect(gd._fullData[0].line.cmax).toBe(4); }) .catch(failTest) .then(done);