diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index ca3ac9bca02..a52bc3537fd 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -221,6 +221,7 @@ var SYMBOLDEFS = require('./symbol_defs'); drawing.symbolNames = []; drawing.symbolFuncs = []; +drawing.symbolBackOffs = []; drawing.symbolNeedLines = {}; drawing.symbolNoDot = {}; drawing.symbolNoFill = {}; @@ -240,6 +241,7 @@ Object.keys(SYMBOLDEFS).forEach(function(k) { ); drawing.symbolNames[n] = k; drawing.symbolFuncs[n] = symDef.f; + drawing.symbolBackOffs[n] = symDef.backoff || 0; if(symDef.needLine) { drawing.symbolNeedLines[n] = true; @@ -287,9 +289,9 @@ drawing.symbolNumber = function(v) { 0 : Math.floor(Math.max(v, 0)); }; -function makePointPath(symbolNumber, r) { +function makePointPath(symbolNumber, r, t, s) { var base = symbolNumber % 100; - return drawing.symbolFuncs[base](r) + (symbolNumber >= 200 ? DOTPATH : ''); + return drawing.symbolFuncs[base](r, t, s) + (symbolNumber >= 200 ? DOTPATH : ''); } var HORZGRADIENT = {x1: 1, x2: 0, y1: 0, y2: 0}; @@ -649,7 +651,10 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd) { // because that impacts how to handle colors d.om = x % 200 >= 100; - sel.attr('d', makePointPath(x, r)); + var angle = getMarkerAngle(d, trace); + var standoff = getMarkerStandoff(d, trace); + + sel.attr('d', makePointPath(x, r, angle, standoff)); } var perPointGradient = false; @@ -898,7 +903,7 @@ drawing.selectedPointStyle = function(s, trace) { var mx = d.mx || marker.symbol || 0; var mrc2 = fns.selectedSizeFn(d); - pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2)); + pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2, getMarkerAngle(d, trace), getMarkerStandoff(d, trace))); // save for Drawing.selectedTextStyle d.mrc2 = mrc2; @@ -1069,6 +1074,26 @@ drawing.smoothclosed = function(pts, smoothness) { return path; }; +var lastDrawnX, lastDrawnY; + +function roundEnd(pt, isY, isLastPoint) { + if(isLastPoint) pt = applyBackoff(pt); + + return isY ? roundY(pt[1]) : roundX(pt[0]); +} + +function roundX(p) { + var v = d3.round(p, 2); + lastDrawnX = v; + return v; +} + +function roundY(p) { + var v = d3.round(p, 2); + lastDrawnY = v; + return v; +} + function makeTangent(prevpt, thispt, nextpt, smoothness) { var d1x = prevpt[0] - thispt[0]; var d1y = prevpt[1] - thispt[1]; @@ -1082,11 +1107,11 @@ function makeTangent(prevpt, thispt, nextpt, smoothness) { var denom2 = 3 * d1a * (d1a + d2a); return [ [ - d3.round(thispt[0] + (denom1 && numx / denom1), 2), - d3.round(thispt[1] + (denom1 && numy / denom1), 2) + roundX(thispt[0] + (denom1 && numx / denom1)), + roundY(thispt[1] + (denom1 && numy / denom1)) ], [ - d3.round(thispt[0] - (denom2 && numx / denom2), 2), - d3.round(thispt[1] - (denom2 && numy / denom2), 2) + roundX(thispt[0] - (denom2 && numx / denom2)), + roundY(thispt[1] - (denom2 && numy / denom2)) ] ]; } @@ -1094,35 +1119,99 @@ function makeTangent(prevpt, thispt, nextpt, smoothness) { // step paths - returns a generator function for paths // with the given step shape var STEPPATH = { - hv: function(p0, p1) { - return 'H' + d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2); + hv: function(p0, p1, isLastPoint) { + return 'H' + + roundX(p1[0]) + 'V' + + roundEnd(p1, 1, isLastPoint); }, - vh: function(p0, p1) { - return 'V' + d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2); + vh: function(p0, p1, isLastPoint) { + return 'V' + + roundY(p1[1]) + 'H' + + roundEnd(p1, 0, isLastPoint); }, - hvh: function(p0, p1) { - return 'H' + d3.round((p0[0] + p1[0]) / 2, 2) + 'V' + - d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2); + hvh: function(p0, p1, isLastPoint) { + return 'H' + + roundX((p0[0] + p1[0]) / 2) + 'V' + + roundY(p1[1]) + 'H' + + roundEnd(p1, 0, isLastPoint); }, - vhv: function(p0, p1) { - return 'V' + d3.round((p0[1] + p1[1]) / 2, 2) + 'H' + - d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2); + vhv: function(p0, p1, isLastPoint) { + return 'V' + + roundY((p0[1] + p1[1]) / 2) + 'H' + + roundX(p1[0]) + 'V' + + roundEnd(p1, 1, isLastPoint); } }; -var STEPLINEAR = function(p0, p1) { - return 'L' + d3.round(p1[0], 2) + ',' + d3.round(p1[1], 2); +var STEPLINEAR = function(p0, p1, isLastPoint) { + return 'L' + + roundEnd(p1, 0, isLastPoint) + ',' + + roundEnd(p1, 1, isLastPoint); }; drawing.steps = function(shape) { var onestep = STEPPATH[shape] || STEPLINEAR; return function(pts) { - var path = 'M' + d3.round(pts[0][0], 2) + ',' + d3.round(pts[0][1], 2); - for(var i = 1; i < pts.length; i++) { - path += onestep(pts[i - 1], pts[i]); + var path = 'M' + roundX(pts[0][0]) + ',' + roundY(pts[0][1]); + var len = pts.length; + for(var i = 1; i < len; i++) { + path += onestep(pts[i - 1], pts[i], i === len - 1); } return path; }; }; +function applyBackoff(pt, start) { + var backoff = pt.backoff; + var trace = pt.trace; + var d = pt.d; + var i = pt.i; + + if(backoff && trace && + trace.marker && + trace.marker.angle % 360 === 0 && + trace.line && + trace.line.shape !== 'spline' + ) { + var arrayBackoff = Lib.isArrayOrTypedArray(backoff); + var end = pt; + + var x1 = start ? start[0] : lastDrawnX || 0; + var y1 = start ? start[1] : lastDrawnY || 0; + + var x2 = end[0]; + var y2 = end[1]; + + var dx = x2 - x1; + var dy = y2 - y1; + + var t = Math.atan2(dy, dx); + + var b = arrayBackoff ? backoff[i] : backoff; + + if(b === 'auto') { + var endI = end.i; + if(trace.type === 'scatter') endI--; // Why we need this hack? + + var endMarker = end.marker; + b = endMarker ? drawing.symbolBackOffs[drawing.symbolNumber(endMarker.symbol)] * endMarker.size : 0; + b += drawing.getMarkerStandoff(d[endI], trace) || 0; + } + + var x = x2 - b * Math.cos(t); + var y = y2 - b * Math.sin(t); + + if( + ((x <= x2 && x >= x1) || (x >= x2 && x <= x1)) && + ((y <= y2 && y >= y1) || (y >= y2 && y <= y1)) + ) { + pt = [x, y]; + } + } + + return pt; +} + +drawing.applyBackoff = applyBackoff; + // off-screen svg render testing element, shared by the whole page // uses the id 'js-plotly-tester' and stores it in drawing.tester drawing.makeTester = function() { @@ -1447,3 +1536,168 @@ drawing.setTextPointsScale = function(selection, xScale, yScale) { el.attr('transform', transforms.join('')); }); }; + +function getMarkerStandoff(d, trace) { + var standoff; + + if(d) standoff = d.mf; + + if(standoff === undefined) { + standoff = trace.marker ? trace.marker.standoff || 0 : 0; + } + + if(!trace._geo && !trace._xA) { + // case of legends + return -standoff; + } + + return standoff; +} + +drawing.getMarkerStandoff = getMarkerStandoff; + +var atan2 = Math.atan2; +var cos = Math.cos; +var sin = Math.sin; + +function rotate(t, xy) { + var x = xy[0]; + var y = xy[1]; + return [ + x * cos(t) - y * sin(t), + x * sin(t) + y * cos(t) + ]; +} + +var previousLon; +var previousLat; +var previousX; +var previousY; +var previousI; +var previousTraceUid; + +function getMarkerAngle(d, trace) { + var angle = d.ma; + + if(angle === undefined) { + angle = trace.marker.angle || 0; + } + + var x, y; + var ref = trace.marker.angleref; + if(ref === 'previous' || ref === 'north') { + if(trace._geo) { + var p = trace._geo.project(d.lonlat); + x = p[0]; + y = p[1]; + } else { + var xa = trace._xA; + var ya = trace._yA; + if(xa && ya) { + x = xa.c2p(d.x); + y = ya.c2p(d.y); + } else { + // case of legends + return 90; + } + } + + if(trace._geo) { + var lon = d.lonlat[0]; + var lat = d.lonlat[1]; + + var north = trace._geo.project([ + lon, + lat + 1e-5 // epsilon + ]); + + var east = trace._geo.project([ + lon + 1e-5, // epsilon + lat + ]); + + var u = atan2( + east[1] - y, + east[0] - x + ); + + var v = atan2( + north[1] - y, + north[0] - x + ); + + var t; + if(ref === 'north') { + t = angle / 180 * Math.PI; + // To use counter-clockwise angles i.e. + // East: 90, West: -90 + // to facilitate wind visualisations + // in future we should use t = -t here. + } else if(ref === 'previous') { + var lon1 = lon / 180 * Math.PI; + var lat1 = lat / 180 * Math.PI; + var lon2 = previousLon / 180 * Math.PI; + var lat2 = previousLat / 180 * Math.PI; + + var dLon = lon2 - lon1; + + var deltaY = cos(lat2) * sin(dLon); + var deltaX = sin(lat2) * cos(lat1) - cos(lat2) * sin(lat1) * cos(dLon); + + t = -atan2( + deltaY, + deltaX + ) - Math.PI; + + previousLon = lon; + previousLat = lat; + } + + var A = rotate(u, [cos(t), 0]); + var B = rotate(v, [sin(t), 0]); + + angle = atan2( + A[1] + B[1], + A[0] + B[0] + ) / Math.PI * 180; + + if(ref === 'previous' && !( + previousTraceUid === trace.uid && + d.i === previousI + 1 + )) { + angle = null; + } + } + + if(ref === 'previous' && !trace._geo) { + if( + previousTraceUid === trace.uid && + d.i === previousI + 1 && + isNumeric(x) && + isNumeric(y) + ) { + var dX = x - previousX; + var dY = y - previousY; + + var shape = trace.line ? trace.line.shape || '' : ''; + + var lastShapeChar = shape.slice(shape.length - 1); + if(lastShapeChar === 'h') dY = 0; + if(lastShapeChar === 'v') dX = 0; + + angle += atan2(dY, dX) / Math.PI * 180 + 90; + } else { + angle = null; + } + } + } + + previousX = x; + previousY = y; + previousI = d.i; + previousTraceUid = trace.uid; + + return angle; +} + +drawing.getMarkerAngle = getMarkerAngle; diff --git a/src/components/drawing/symbol_defs.js b/src/components/drawing/symbol_defs.js index 77b02946170..ce99d7c430d 100644 --- a/src/components/drawing/symbol_defs.js +++ b/src/components/drawing/symbol_defs.js @@ -1,6 +1,7 @@ 'use strict'; -var d3 = require('@plotly/d3'); +var parseSvgPath = require('parse-svg-path'); +var round = require('@plotly/d3').round; /** Marker symbol definitions * users can specify markers either by number or name @@ -10,340 +11,416 @@ var d3 = require('@plotly/d3'); * add both and you get both */ + +var emptyPath = 'M0,0Z'; +var sqrt2 = Math.sqrt(2); +var sqrt3 = Math.sqrt(3); +var PI = Math.PI; +var cos = Math.cos; +var sin = Math.sin; + module.exports = { circle: { n: 0, - f: function(r) { - var rs = d3.round(r, 2); - return 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs + - 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rs = round(r, 2); + var circle = 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs + 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z'; + return standoff ? align(angle, standoff, circle) : circle; } }, square: { n: 1, - f: function(r) { - var rs = d3.round(r, 2); - return 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rs = round(r, 2); + return align(angle, standoff, 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z'); } }, diamond: { n: 2, - f: function(r) { - var rd = d3.round(r * 1.3, 2); - return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rd = round(r * 1.3, 2); + return align(angle, standoff, 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z'); } }, cross: { n: 3, - f: function(r) { - var rc = d3.round(r * 0.4, 2); - var rc2 = d3.round(r * 1.2, 2); - return 'M' + rc2 + ',' + rc + 'H' + rc + 'V' + rc2 + 'H-' + rc + + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rc = round(r * 0.4, 2); + var rc2 = round(r * 1.2, 2); + return align(angle, standoff, 'M' + rc2 + ',' + rc + 'H' + rc + 'V' + rc2 + 'H-' + rc + 'V' + rc + 'H-' + rc2 + 'V-' + rc + 'H-' + rc + 'V-' + rc2 + - 'H' + rc + 'V-' + rc + 'H' + rc2 + 'Z'; + 'H' + rc + 'V-' + rc + 'H' + rc2 + 'Z'); } }, x: { n: 4, - f: function(r) { - var rx = d3.round(r * 0.8 / Math.sqrt(2), 2); + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rx = round(r * 0.8 / sqrt2, 2); var ne = 'l' + rx + ',' + rx; var se = 'l' + rx + ',-' + rx; var sw = 'l-' + rx + ',-' + rx; var nw = 'l-' + rx + ',' + rx; - return 'M0,' + rx + ne + se + sw + se + sw + nw + sw + nw + ne + nw + ne + 'Z'; + return align(angle, standoff, 'M0,' + rx + ne + se + sw + se + sw + nw + sw + nw + ne + nw + ne + 'Z'); } }, 'triangle-up': { n: 5, - f: function(r) { - var rt = d3.round(r * 2 / Math.sqrt(3), 2); - var r2 = d3.round(r / 2, 2); - var rs = d3.round(r, 2); - return 'M-' + rt + ',' + r2 + 'H' + rt + 'L0,-' + rs + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rt = round(r * 2 / sqrt3, 2); + var r2 = round(r / 2, 2); + var rs = round(r, 2); + return align(angle, standoff, 'M-' + rt + ',' + r2 + 'H' + rt + 'L0,-' + rs + 'Z'); } }, 'triangle-down': { n: 6, - f: function(r) { - var rt = d3.round(r * 2 / Math.sqrt(3), 2); - var r2 = d3.round(r / 2, 2); - var rs = d3.round(r, 2); - return 'M-' + rt + ',-' + r2 + 'H' + rt + 'L0,' + rs + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rt = round(r * 2 / sqrt3, 2); + var r2 = round(r / 2, 2); + var rs = round(r, 2); + return align(angle, standoff, 'M-' + rt + ',-' + r2 + 'H' + rt + 'L0,' + rs + 'Z'); } }, 'triangle-left': { n: 7, - f: function(r) { - var rt = d3.round(r * 2 / Math.sqrt(3), 2); - var r2 = d3.round(r / 2, 2); - var rs = d3.round(r, 2); - return 'M' + r2 + ',-' + rt + 'V' + rt + 'L-' + rs + ',0Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rt = round(r * 2 / sqrt3, 2); + var r2 = round(r / 2, 2); + var rs = round(r, 2); + return align(angle, standoff, 'M' + r2 + ',-' + rt + 'V' + rt + 'L-' + rs + ',0Z'); } }, 'triangle-right': { n: 8, - f: function(r) { - var rt = d3.round(r * 2 / Math.sqrt(3), 2); - var r2 = d3.round(r / 2, 2); - var rs = d3.round(r, 2); - return 'M-' + r2 + ',-' + rt + 'V' + rt + 'L' + rs + ',0Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rt = round(r * 2 / sqrt3, 2); + var r2 = round(r / 2, 2); + var rs = round(r, 2); + return align(angle, standoff, 'M-' + r2 + ',-' + rt + 'V' + rt + 'L' + rs + ',0Z'); } }, 'triangle-ne': { n: 9, - f: function(r) { - var r1 = d3.round(r * 0.6, 2); - var r2 = d3.round(r * 1.2, 2); - return 'M-' + r2 + ',-' + r1 + 'H' + r1 + 'V' + r2 + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var r1 = round(r * 0.6, 2); + var r2 = round(r * 1.2, 2); + return align(angle, standoff, 'M-' + r2 + ',-' + r1 + 'H' + r1 + 'V' + r2 + 'Z'); } }, 'triangle-se': { n: 10, - f: function(r) { - var r1 = d3.round(r * 0.6, 2); - var r2 = d3.round(r * 1.2, 2); - return 'M' + r1 + ',-' + r2 + 'V' + r1 + 'H-' + r2 + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var r1 = round(r * 0.6, 2); + var r2 = round(r * 1.2, 2); + return align(angle, standoff, 'M' + r1 + ',-' + r2 + 'V' + r1 + 'H-' + r2 + 'Z'); } }, 'triangle-sw': { n: 11, - f: function(r) { - var r1 = d3.round(r * 0.6, 2); - var r2 = d3.round(r * 1.2, 2); - return 'M' + r2 + ',' + r1 + 'H-' + r1 + 'V-' + r2 + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var r1 = round(r * 0.6, 2); + var r2 = round(r * 1.2, 2); + return align(angle, standoff, 'M' + r2 + ',' + r1 + 'H-' + r1 + 'V-' + r2 + 'Z'); } }, 'triangle-nw': { n: 12, - f: function(r) { - var r1 = d3.round(r * 0.6, 2); - var r2 = d3.round(r * 1.2, 2); - return 'M-' + r1 + ',' + r2 + 'V-' + r1 + 'H' + r2 + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var r1 = round(r * 0.6, 2); + var r2 = round(r * 1.2, 2); + return align(angle, standoff, 'M-' + r1 + ',' + r2 + 'V-' + r1 + 'H' + r2 + 'Z'); } }, pentagon: { n: 13, - f: function(r) { - var x1 = d3.round(r * 0.951, 2); - var x2 = d3.round(r * 0.588, 2); - var y0 = d3.round(-r, 2); - var y1 = d3.round(r * -0.309, 2); - var y2 = d3.round(r * 0.809, 2); - return 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 + 'H-' + x2 + - 'L-' + x1 + ',' + y1 + 'L0,' + y0 + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var x1 = round(r * 0.951, 2); + var x2 = round(r * 0.588, 2); + var y0 = round(-r, 2); + var y1 = round(r * -0.309, 2); + var y2 = round(r * 0.809, 2); + return align(angle, standoff, 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 + 'H-' + x2 + + 'L-' + x1 + ',' + y1 + 'L0,' + y0 + 'Z'); } }, hexagon: { n: 14, - f: function(r) { - var y0 = d3.round(r, 2); - var y1 = d3.round(r / 2, 2); - var x = d3.round(r * Math.sqrt(3) / 2, 2); - return 'M' + x + ',-' + y1 + 'V' + y1 + 'L0,' + y0 + - 'L-' + x + ',' + y1 + 'V-' + y1 + 'L0,-' + y0 + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var y0 = round(r, 2); + var y1 = round(r / 2, 2); + var x = round(r * sqrt3 / 2, 2); + return align(angle, standoff, 'M' + x + ',-' + y1 + 'V' + y1 + 'L0,' + y0 + + 'L-' + x + ',' + y1 + 'V-' + y1 + 'L0,-' + y0 + 'Z'); } }, hexagon2: { n: 15, - f: function(r) { - var x0 = d3.round(r, 2); - var x1 = d3.round(r / 2, 2); - var y = d3.round(r * Math.sqrt(3) / 2, 2); - return 'M-' + x1 + ',' + y + 'H' + x1 + 'L' + x0 + - ',0L' + x1 + ',-' + y + 'H-' + x1 + 'L-' + x0 + ',0Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var x0 = round(r, 2); + var x1 = round(r / 2, 2); + var y = round(r * sqrt3 / 2, 2); + return align(angle, standoff, 'M-' + x1 + ',' + y + 'H' + x1 + 'L' + x0 + + ',0L' + x1 + ',-' + y + 'H-' + x1 + 'L-' + x0 + ',0Z'); } }, octagon: { n: 16, - f: function(r) { - var a = d3.round(r * 0.924, 2); - var b = d3.round(r * 0.383, 2); - return 'M-' + b + ',-' + a + 'H' + b + 'L' + a + ',-' + b + 'V' + b + - 'L' + b + ',' + a + 'H-' + b + 'L-' + a + ',' + b + 'V-' + b + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var a = round(r * 0.924, 2); + var b = round(r * 0.383, 2); + return align(angle, standoff, 'M-' + b + ',-' + a + 'H' + b + 'L' + a + ',-' + b + 'V' + b + + 'L' + b + ',' + a + 'H-' + b + 'L-' + a + ',' + b + 'V-' + b + 'Z'); } }, star: { n: 17, - f: function(r) { + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + var rs = r * 1.4; - var x1 = d3.round(rs * 0.225, 2); - var x2 = d3.round(rs * 0.951, 2); - var x3 = d3.round(rs * 0.363, 2); - var x4 = d3.round(rs * 0.588, 2); - var y0 = d3.round(-rs, 2); - var y1 = d3.round(rs * -0.309, 2); - var y3 = d3.round(rs * 0.118, 2); - var y4 = d3.round(rs * 0.809, 2); - var y5 = d3.round(rs * 0.382, 2); - return 'M' + x1 + ',' + y1 + 'H' + x2 + 'L' + x3 + ',' + y3 + + var x1 = round(rs * 0.225, 2); + var x2 = round(rs * 0.951, 2); + var x3 = round(rs * 0.363, 2); + var x4 = round(rs * 0.588, 2); + var y0 = round(-rs, 2); + var y1 = round(rs * -0.309, 2); + var y3 = round(rs * 0.118, 2); + var y4 = round(rs * 0.809, 2); + var y5 = round(rs * 0.382, 2); + return align(angle, standoff, 'M' + x1 + ',' + y1 + 'H' + x2 + 'L' + x3 + ',' + y3 + 'L' + x4 + ',' + y4 + 'L0,' + y5 + 'L-' + x4 + ',' + y4 + 'L-' + x3 + ',' + y3 + 'L-' + x2 + ',' + y1 + 'H-' + x1 + - 'L0,' + y0 + 'Z'; + 'L0,' + y0 + 'Z'); } }, hexagram: { n: 18, - f: function(r) { - var y = d3.round(r * 0.66, 2); - var x1 = d3.round(r * 0.38, 2); - var x2 = d3.round(r * 0.76, 2); - return 'M-' + x2 + ',0l-' + x1 + ',-' + y + 'h' + x2 + + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var y = round(r * 0.66, 2); + var x1 = round(r * 0.38, 2); + var x2 = round(r * 0.76, 2); + return align(angle, standoff, 'M-' + x2 + ',0l-' + x1 + ',-' + y + 'h' + x2 + 'l' + x1 + ',-' + y + 'l' + x1 + ',' + y + 'h' + x2 + 'l-' + x1 + ',' + y + 'l' + x1 + ',' + y + 'h-' + x2 + - 'l-' + x1 + ',' + y + 'l-' + x1 + ',-' + y + 'h-' + x2 + 'Z'; + 'l-' + x1 + ',' + y + 'l-' + x1 + ',-' + y + 'h-' + x2 + 'Z'); } }, 'star-triangle-up': { n: 19, - f: function(r) { - var x = d3.round(r * Math.sqrt(3) * 0.8, 2); - var y1 = d3.round(r * 0.8, 2); - var y2 = d3.round(r * 1.6, 2); - var rc = d3.round(r * 4, 2); + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var x = round(r * sqrt3 * 0.8, 2); + var y1 = round(r * 0.8, 2); + var y2 = round(r * 1.6, 2); + var rc = round(r * 4, 2); var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 '; - return 'M-' + x + ',' + y1 + aPart + x + ',' + y1 + - aPart + '0,-' + y2 + aPart + '-' + x + ',' + y1 + 'Z'; + return align(angle, standoff, 'M-' + x + ',' + y1 + aPart + x + ',' + y1 + + aPart + '0,-' + y2 + aPart + '-' + x + ',' + y1 + 'Z'); } }, 'star-triangle-down': { n: 20, - f: function(r) { - var x = d3.round(r * Math.sqrt(3) * 0.8, 2); - var y1 = d3.round(r * 0.8, 2); - var y2 = d3.round(r * 1.6, 2); - var rc = d3.round(r * 4, 2); + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var x = round(r * sqrt3 * 0.8, 2); + var y1 = round(r * 0.8, 2); + var y2 = round(r * 1.6, 2); + var rc = round(r * 4, 2); var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 '; - return 'M' + x + ',-' + y1 + aPart + '-' + x + ',-' + y1 + - aPart + '0,' + y2 + aPart + x + ',-' + y1 + 'Z'; + return align(angle, standoff, 'M' + x + ',-' + y1 + aPart + '-' + x + ',-' + y1 + + aPart + '0,' + y2 + aPart + x + ',-' + y1 + 'Z'); } }, 'star-square': { n: 21, - f: function(r) { - var rp = d3.round(r * 1.1, 2); - var rc = d3.round(r * 2, 2); + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rp = round(r * 1.1, 2); + var rc = round(r * 2, 2); var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 '; - return 'M-' + rp + ',-' + rp + aPart + '-' + rp + ',' + rp + + return align(angle, standoff, 'M-' + rp + ',-' + rp + aPart + '-' + rp + ',' + rp + aPart + rp + ',' + rp + aPart + rp + ',-' + rp + - aPart + '-' + rp + ',-' + rp + 'Z'; + aPart + '-' + rp + ',-' + rp + 'Z'); } }, 'star-diamond': { n: 22, - f: function(r) { - var rp = d3.round(r * 1.4, 2); - var rc = d3.round(r * 1.9, 2); + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rp = round(r * 1.4, 2); + var rc = round(r * 1.9, 2); var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 '; - return 'M-' + rp + ',0' + aPart + '0,' + rp + + return align(angle, standoff, 'M-' + rp + ',0' + aPart + '0,' + rp + aPart + rp + ',0' + aPart + '0,-' + rp + - aPart + '-' + rp + ',0' + 'Z'; + aPart + '-' + rp + ',0' + 'Z'); } }, 'diamond-tall': { n: 23, - f: function(r) { - var x = d3.round(r * 0.7, 2); - var y = d3.round(r * 1.4, 2); - return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var x = round(r * 0.7, 2); + var y = round(r * 1.4, 2); + return align(angle, standoff, 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z'); } }, 'diamond-wide': { n: 24, - f: function(r) { - var x = d3.round(r * 1.4, 2); - var y = d3.round(r * 0.7, 2); - return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var x = round(r * 1.4, 2); + var y = round(r * 0.7, 2); + return align(angle, standoff, 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z'); } }, hourglass: { n: 25, - f: function(r) { - var rs = d3.round(r, 2); - return 'M' + rs + ',' + rs + 'H-' + rs + 'L' + rs + ',-' + rs + 'H-' + rs + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rs = round(r, 2); + return align(angle, standoff, 'M' + rs + ',' + rs + 'H-' + rs + 'L' + rs + ',-' + rs + 'H-' + rs + 'Z'); }, noDot: true }, bowtie: { n: 26, - f: function(r) { - var rs = d3.round(r, 2); - return 'M' + rs + ',' + rs + 'V-' + rs + 'L-' + rs + ',' + rs + 'V-' + rs + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rs = round(r, 2); + return align(angle, standoff, 'M' + rs + ',' + rs + 'V-' + rs + 'L-' + rs + ',' + rs + 'V-' + rs + 'Z'); }, noDot: true }, 'circle-cross': { n: 27, - f: function(r) { - var rs = d3.round(r, 2); - return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs + + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rs = round(r, 2); + return align(angle, standoff, 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs + 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs + - 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z'; + 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z'); }, needLine: true, noDot: true }, 'circle-x': { n: 28, - f: function(r) { - var rs = d3.round(r, 2); - var rc = d3.round(r / Math.sqrt(2), 2); - return 'M' + rc + ',' + rc + 'L-' + rc + ',-' + rc + + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rs = round(r, 2); + var rc = round(r / sqrt2, 2); + return align(angle, standoff, 'M' + rc + ',' + rc + 'L-' + rc + ',-' + rc + 'M' + rc + ',-' + rc + 'L-' + rc + ',' + rc + 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs + - 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z'; + 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z'); }, needLine: true, noDot: true }, 'square-cross': { n: 29, - f: function(r) { - var rs = d3.round(r, 2); - return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs + - 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rs = round(r, 2); + return align(angle, standoff, 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs + + 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z'); }, needLine: true, noDot: true }, 'square-x': { n: 30, - f: function(r) { - var rs = d3.round(r, 2); - return 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs + + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rs = round(r, 2); + return align(angle, standoff, 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs + 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs + - 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z'; + 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z'); }, needLine: true, noDot: true }, 'diamond-cross': { n: 31, - f: function(r) { - var rd = d3.round(r * 1.3, 2); - return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' + - 'M0,-' + rd + 'V' + rd + 'M-' + rd + ',0H' + rd; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rd = round(r * 1.3, 2); + return align(angle, standoff, 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' + + 'M0,-' + rd + 'V' + rd + 'M-' + rd + ',0H' + rd); }, needLine: true, noDot: true }, 'diamond-x': { n: 32, - f: function(r) { - var rd = d3.round(r * 1.3, 2); - var r2 = d3.round(r * 0.65, 2); - return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' + + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rd = round(r * 1.3, 2); + var r2 = round(r * 0.65, 2); + return align(angle, standoff, 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' + 'M-' + r2 + ',-' + r2 + 'L' + r2 + ',' + r2 + - 'M-' + r2 + ',' + r2 + 'L' + r2 + ',-' + r2; + 'M-' + r2 + ',' + r2 + 'L' + r2 + ',-' + r2); }, needLine: true, noDot: true }, 'cross-thin': { n: 33, - f: function(r) { - var rc = d3.round(r * 1.4, 2); - return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rc = round(r * 1.4, 2); + return align(angle, standoff, 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc); }, needLine: true, noDot: true, @@ -351,10 +428,12 @@ module.exports = { }, 'x-thin': { n: 34, - f: function(r) { - var rx = d3.round(r, 2); - return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx + - 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rx = round(r, 2); + return align(angle, standoff, 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx + + 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx); }, needLine: true, noDot: true, @@ -362,12 +441,14 @@ module.exports = { }, asterisk: { n: 35, - f: function(r) { - var rc = d3.round(r * 1.2, 2); - var rs = d3.round(r * 0.85, 2); - return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc + + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rc = round(r * 1.2, 2); + var rs = round(r * 0.85, 2); + return align(angle, standoff, 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc + 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs + - 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs; + 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs); }, needLine: true, noDot: true, @@ -375,24 +456,29 @@ module.exports = { }, hash: { n: 36, - f: function(r) { - var r1 = d3.round(r / 2, 2); - var r2 = d3.round(r, 2); - return 'M' + r1 + ',' + r2 + 'V-' + r2 + - 'm-' + r2 + ',0V' + r2 + + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var r1 = round(r / 2, 2); + var r2 = round(r, 2); + + return align(angle, standoff, 'M' + r1 + ',' + r2 + 'V-' + r2 + + 'M' + (r1 - r2) + ',-' + r2 + 'V' + r2 + 'M' + r2 + ',' + r1 + 'H-' + r2 + - 'm0,-' + r2 + 'H' + r2; + 'M-' + r2 + ',' + (r1 - r2) + 'H' + r2); }, needLine: true, noFill: true }, 'y-up': { n: 37, - f: function(r) { - var x = d3.round(r * 1.2, 2); - var y0 = d3.round(r * 1.6, 2); - var y1 = d3.round(r * 0.8, 2); - return 'M-' + x + ',' + y1 + 'L0,0M' + x + ',' + y1 + 'L0,0M0,-' + y0 + 'L0,0'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var x = round(r * 1.2, 2); + var y0 = round(r * 1.6, 2); + var y1 = round(r * 0.8, 2); + return align(angle, standoff, 'M-' + x + ',' + y1 + 'L0,0M' + x + ',' + y1 + 'L0,0M0,-' + y0 + 'L0,0'); }, needLine: true, noDot: true, @@ -400,11 +486,13 @@ module.exports = { }, 'y-down': { n: 38, - f: function(r) { - var x = d3.round(r * 1.2, 2); - var y0 = d3.round(r * 1.6, 2); - var y1 = d3.round(r * 0.8, 2); - return 'M-' + x + ',-' + y1 + 'L0,0M' + x + ',-' + y1 + 'L0,0M0,' + y0 + 'L0,0'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var x = round(r * 1.2, 2); + var y0 = round(r * 1.6, 2); + var y1 = round(r * 0.8, 2); + return align(angle, standoff, 'M-' + x + ',-' + y1 + 'L0,0M' + x + ',-' + y1 + 'L0,0M0,' + y0 + 'L0,0'); }, needLine: true, noDot: true, @@ -412,11 +500,13 @@ module.exports = { }, 'y-left': { n: 39, - f: function(r) { - var y = d3.round(r * 1.2, 2); - var x0 = d3.round(r * 1.6, 2); - var x1 = d3.round(r * 0.8, 2); - return 'M' + x1 + ',' + y + 'L0,0M' + x1 + ',-' + y + 'L0,0M-' + x0 + ',0L0,0'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var y = round(r * 1.2, 2); + var x0 = round(r * 1.6, 2); + var x1 = round(r * 0.8, 2); + return align(angle, standoff, 'M' + x1 + ',' + y + 'L0,0M' + x1 + ',-' + y + 'L0,0M-' + x0 + ',0L0,0'); }, needLine: true, noDot: true, @@ -424,11 +514,13 @@ module.exports = { }, 'y-right': { n: 40, - f: function(r) { - var y = d3.round(r * 1.2, 2); - var x0 = d3.round(r * 1.6, 2); - var x1 = d3.round(r * 0.8, 2); - return 'M-' + x1 + ',' + y + 'L0,0M-' + x1 + ',-' + y + 'L0,0M' + x0 + ',0L0,0'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var y = round(r * 1.2, 2); + var x0 = round(r * 1.6, 2); + var x1 = round(r * 0.8, 2); + return align(angle, standoff, 'M-' + x1 + ',' + y + 'L0,0M-' + x1 + ',-' + y + 'L0,0M' + x0 + ',0L0,0'); }, needLine: true, noDot: true, @@ -436,9 +528,11 @@ module.exports = { }, 'line-ew': { n: 41, - f: function(r) { - var rc = d3.round(r * 1.4, 2); - return 'M' + rc + ',0H-' + rc; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rc = round(r * 1.4, 2); + return align(angle, standoff, 'M' + rc + ',0H-' + rc); }, needLine: true, noDot: true, @@ -446,9 +540,11 @@ module.exports = { }, 'line-ns': { n: 42, - f: function(r) { - var rc = d3.round(r * 1.4, 2); - return 'M0,' + rc + 'V-' + rc; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rc = round(r * 1.4, 2); + return align(angle, standoff, 'M0,' + rc + 'V-' + rc); }, needLine: true, noDot: true, @@ -456,9 +552,11 @@ module.exports = { }, 'line-ne': { n: 43, - f: function(r) { - var rx = d3.round(r, 2); - return 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rx = round(r, 2); + return align(angle, standoff, 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx); }, needLine: true, noDot: true, @@ -466,9 +564,11 @@ module.exports = { }, 'line-nw': { n: 44, - f: function(r) { - var rx = d3.round(r, 2); - return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rx = round(r, 2); + return align(angle, standoff, 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx); }, needLine: true, noDot: true, @@ -476,78 +576,235 @@ module.exports = { }, 'arrow-up': { n: 45, - f: function(r) { - var rx = d3.round(r, 2); - var ry = d3.round(r * 2, 2); - return 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rx = round(r, 2); + var ry = round(r * 2, 2); + return align(angle, standoff, 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z'); }, + backoff: 1, noDot: true }, 'arrow-down': { n: 46, - f: function(r) { - var rx = d3.round(r, 2); - var ry = d3.round(r * 2, 2); - return 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rx = round(r, 2); + var ry = round(r * 2, 2); + return align(angle, standoff, 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z'); }, noDot: true }, 'arrow-left': { n: 47, - f: function(r) { - var rx = d3.round(r * 2, 2); - var ry = d3.round(r, 2); - return 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rx = round(r * 2, 2); + var ry = round(r, 2); + return align(angle, standoff, 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z'); }, noDot: true }, 'arrow-right': { n: 48, - f: function(r) { - var rx = d3.round(r * 2, 2); - var ry = d3.round(r, 2); - return 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rx = round(r * 2, 2); + var ry = round(r, 2); + return align(angle, standoff, 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z'); }, noDot: true }, 'arrow-bar-up': { n: 49, - f: function(r) { - var rx = d3.round(r, 2); - var ry = d3.round(r * 2, 2); - return 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rx = round(r, 2); + var ry = round(r * 2, 2); + return align(angle, standoff, 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z'); }, + backoff: 1, needLine: true, noDot: true }, 'arrow-bar-down': { n: 50, - f: function(r) { - var rx = d3.round(r, 2); - var ry = d3.round(r * 2, 2); - return 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rx = round(r, 2); + var ry = round(r * 2, 2); + return align(angle, standoff, 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z'); }, needLine: true, noDot: true }, 'arrow-bar-left': { n: 51, - f: function(r) { - var rx = d3.round(r * 2, 2); - var ry = d3.round(r, 2); - return 'M0,-' + ry + 'V' + ry + 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rx = round(r * 2, 2); + var ry = round(r, 2); + return align(angle, standoff, 'M0,-' + ry + 'V' + ry + 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z'); }, needLine: true, noDot: true }, 'arrow-bar-right': { n: 52, - f: function(r) { - var rx = d3.round(r * 2, 2); - var ry = d3.round(r, 2); - return 'M0,-' + ry + 'V' + ry + 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z'; + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var rx = round(r * 2, 2); + var ry = round(r, 2); + return align(angle, standoff, 'M0,-' + ry + 'V' + ry + 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z'); }, needLine: true, noDot: true + }, + 'arrow': { + n: 53, + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var headAngle = PI / 2.5; // 36 degrees - golden ratio + var x = 2 * r * cos(headAngle); + var y = 2 * r * sin(headAngle); + + return align(angle, standoff, + 'M0,0' + + 'L' + -x + ',' + y + + 'L' + x + ',' + y + + 'Z' + ); + }, + backoff: 0.9, + noDot: true + }, + 'arrow-wide': { + n: 54, + f: function(r, angle, standoff) { + if(skipAngle(angle)) return emptyPath; + + var headAngle = PI / 4; // 90 degrees + var x = 2 * r * cos(headAngle); + var y = 2 * r * sin(headAngle); + + return align(angle, standoff, + 'M0,0' + + 'L' + -x + ',' + y + + 'A ' + 2 * r + ',' + 2 * r + ' 0 0 1 ' + x + ',' + y + + 'Z' + ); + }, + backoff: 0.4, + noDot: true } }; + +function skipAngle(angle) { + return angle === null; +} + +var lastPathIn, lastPathOut; +var lastAngle, lastStandoff; + +function align(angle, standoff, path) { + if((!angle || angle % 360 === 0) && !standoff) return path; + + if( + lastAngle === angle && + lastStandoff === standoff && + lastPathIn === path + ) return lastPathOut; + + lastAngle = angle; + lastStandoff = standoff; + lastPathIn = path; + + function rotate(t, xy) { + var cosT = cos(t); + var sinT = sin(t); + + var x = xy[0]; + var y = xy[1] + (standoff || 0); + return [ + x * cosT - y * sinT, + x * sinT + y * cosT + ]; + } + + var t = angle / 180 * PI; + + var x = 0; + var y = 0; + var cmd = parseSvgPath(path); + var str = ''; + + for(var i = 0; i < cmd.length; i++) { + var cmdI = cmd[i]; + var op = cmdI[0]; + + var x0 = x; + var y0 = y; + + if(op === 'M' || op === 'L') { + x = +cmdI[1]; + y = +cmdI[2]; + } else if(op === 'm' || op === 'l') { + x += +cmdI[1]; + y += +cmdI[2]; + } else if(op === 'H') { + x = +cmdI[1]; + } else if(op === 'h') { + x += +cmdI[1]; + } else if(op === 'V') { + y = +cmdI[1]; + } else if(op === 'v') { + y += +cmdI[1]; + } else if(op === 'A') { + x = +cmdI[1]; + y = +cmdI[2]; + + var E = rotate(t, [+cmdI[6], +cmdI[7]]); + cmdI[6] = E[0]; + cmdI[7] = E[1]; + cmdI[3] = +cmdI[3] + angle; + } + + // change from H, V, h, v to L or l + if(op === 'H' || op === 'V') op = 'L'; + if(op === 'h' || op === 'v') op = 'l'; + + if(op === 'm' || op === 'l') { + x -= x0; + y -= y0; + } + + var B = rotate(t, [x, y]); + + if(op === 'H' || op === 'V') op = 'L'; + + + if( + op === 'M' || op === 'L' || + op === 'm' || op === 'l' + ) { + cmdI[1] = B[0]; + cmdI[2] = B[1]; + } + cmdI[0] = op; + + str += cmdI[0] + cmdI.slice(1).join(','); + } + + lastPathOut = str; + + return str; +} diff --git a/src/components/selections/select.js b/src/components/selections/select.js index 9155f5417cd..e2c94201fa7 100644 --- a/src/components/selections/select.js +++ b/src/components/selections/select.js @@ -1155,13 +1155,13 @@ function reselect(gd, mayEmitSelected, selectionTesters, searchTraces, dragOptio if(_selectionTesters) { var _searchTraces = searchTraces; if(!hadSearchTraces) { - var _xaxis = getFromId(gd, _xRef, 'x'); - var _yaxis = getFromId(gd, _yRef, 'y'); + var _xA = getFromId(gd, _xRef, 'x'); + var _yA = getFromId(gd, _yRef, 'y'); _searchTraces = determineSearchTraces( gd, - [_xaxis], - [_yaxis], + [_xA], + [_yA], subplot ); @@ -1178,8 +1178,8 @@ function reselect(gd, mayEmitSelected, selectionTesters, searchTraces, dragOptio cd0.t.xpx = []; cd0.t.ypx = []; for(var j = 0; j < len; j++) { - cd0.t.xpx[j] = _xaxis.c2p(x[j]); - cd0.t.ypx[j] = _yaxis.c2p(y[j]); + cd0.t.xpx[j] = _xA.c2p(x[j]); + cd0.t.ypx[j] = _yA.c2p(y[j]); } } diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 731b16bb631..44a56c2323e 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -171,7 +171,7 @@ exports.valObjectMeta = { 'A number (in degree) between -180 and 180.' ].join(' '), requiredOpts: [], - otherOpts: ['dflt'], + otherOpts: ['dflt', 'arrayOk'], coerceFunction: function(v, propOut, dflt) { if(v === 'auto') propOut.set('auto'); else if(!isNumeric(v)) propOut.set(dflt); diff --git a/src/plots/geo/geo.js b/src/plots/geo/geo.js index 8665e0162f4..3c057de94b2 100644 --- a/src/plots/geo/geo.js +++ b/src/plots/geo/geo.js @@ -67,8 +67,13 @@ module.exports = function createGeo(opts) { return new Geo(opts); }; -proto.plot = function(geoCalcData, fullLayout, promises) { +proto.plot = function(geoCalcData, fullLayout, promises, replot) { var _this = this; + if(replot) return _this.update(geoCalcData, fullLayout, true); + + _this._geoCalcData = geoCalcData; + _this._fullLayout = fullLayout; + var geoLayout = fullLayout[this.id]; var geoPromises = []; @@ -79,12 +84,24 @@ proto.plot = function(geoCalcData, fullLayout, promises) { break; } } + + var hasMarkerAngles = false; for(var i = 0; i < geoCalcData.length; i++) { - if(geoCalcData[0][0].trace.locationmode) { + var trace = geoCalcData[0][0].trace; + trace._geo = _this; + + if(trace.locationmode) { needsTopojson = true; - break; + } + + var marker = trace.marker; + if(marker) { + var angle = marker.angle; + var angleref = marker.angleref; + if(angle || angleref === 'north' || angleref === 'previous') hasMarkerAngles = true; } } + this._hasMarkerAngles = hasMarkerAngles; if(needsTopojson) { var topojsonNameNew = topojsonUtils.getTopojsonName(geoLayout); @@ -137,7 +154,7 @@ proto.fetchTopojson = function() { }); }; -proto.update = function(geoCalcData, fullLayout) { +proto.update = function(geoCalcData, fullLayout, replot) { var geoLayout = fullLayout[this.id]; // important: maps with choropleth traces have a different layer order @@ -155,11 +172,13 @@ proto.update = function(geoCalcData, fullLayout) { } } - var hasInvalidBounds = this.updateProjection(geoCalcData, fullLayout); - if(hasInvalidBounds) return; + if(!replot) { + var hasInvalidBounds = this.updateProjection(geoCalcData, fullLayout); + if(hasInvalidBounds) return; - if(!this.viewInitial || this.scope !== geoLayout.scope) { - this.saveViewInitial(geoLayout); + if(!this.viewInitial || this.scope !== geoLayout.scope) { + this.saveViewInitial(geoLayout); + } } this.scope = geoLayout.scope; @@ -177,7 +196,7 @@ proto.update = function(geoCalcData, fullLayout) { var choroplethLayer = this.layers.backplot.select('.choroplethlayer'); this.dataPaths.choropleth = choroplethLayer.selectAll('path'); - this.render(); + this._render(); }; proto.updateProjection = function(geoCalcData, fullLayout) { @@ -591,8 +610,16 @@ proto.saveViewInitial = function(geoLayout) { Lib.extendFlat(this.viewInitial, extra); }; +proto.render = function(mayRedrawOnUpdates) { + if(this._hasMarkerAngles && mayRedrawOnUpdates) { + this.plot(this._geoCalcData, this._fullLayout, [], true); + } else { + this._render(); + } +}; + // [hot code path] (re)draw all paths which depend on the projection -proto.render = function() { +proto._render = function() { var projection = this.projection; var pathFn = projection.getPath(); var k; @@ -619,7 +646,7 @@ proto.render = function() { for(k in this.dataPoints) { this.dataPoints[k] .attr('display', hideShowPoints) - .attr('transform', translatePoints); + .attr('transform', translatePoints); // TODO: need to redraw points with marker angle instead of calling translatePoints } }; diff --git a/src/plots/geo/zoom.js b/src/plots/geo/zoom.js index d486122ddbc..2d79d69f581 100644 --- a/src/plots/geo/zoom.js +++ b/src/plots/geo/zoom.js @@ -77,7 +77,7 @@ function zoomScoped(geo, projection) { projection .scale(d3.event.scale) .translate(d3.event.translate); - geo.render(); + geo.render(true); var center = projection.invert(geo.midPt); geo.graphDiv.emit('plotly_relayouting', { @@ -162,7 +162,7 @@ function zoomNonClipped(geo, projection) { } didZoom = true; - geo.render(); + geo.render(true); var rotate = projection.rotate(); var center = projection.invert(geo.midPt); @@ -268,7 +268,7 @@ function zoomClipped(geo, projection) { sync(geo, projection, syncCb); }) .on('zoom.redraw', function() { - geo.render(); + geo.render(true); var _rotate = projection.rotate(); geo.graphDiv.emit('plotly_relayouting', { diff --git a/src/traces/box/attributes.js b/src/traces/box/attributes.js index 0d540a40a74..81fdce06524 100644 --- a/src/traces/box/attributes.js +++ b/src/traces/box/attributes.js @@ -312,6 +312,8 @@ module.exports = { {arrayOk: false, editType: 'plot'}), opacity: extendFlat({}, scatterMarkerAttrs.opacity, {arrayOk: false, dflt: 1, editType: 'style'}), + angle: extendFlat({}, scatterMarkerAttrs.angle, + {arrayOk: false, editType: 'calc'}), size: extendFlat({}, scatterMarkerAttrs.size, {arrayOk: false, editType: 'calc'}), color: extendFlat({}, scatterMarkerAttrs.color, diff --git a/src/traces/box/defaults.js b/src/traces/box/defaults.js index ad037009559..1a8980d0a64 100644 --- a/src/traces/box/defaults.js +++ b/src/traces/box/defaults.js @@ -251,6 +251,8 @@ function handlePointsDefaults(traceIn, traceOut, coerce, opts) { coerce('marker.symbol'); coerce('marker.opacity'); coerce('marker.size'); + coerce('marker.angle'); + coerce('marker.color', traceOut.line.color); coerce('marker.line.color'); coerce('marker.line.width'); diff --git a/src/traces/choropleth/style.js b/src/traces/choropleth/style.js index 4ca8671a64d..cbf27d7a89c 100644 --- a/src/traces/choropleth/style.js +++ b/src/traces/choropleth/style.js @@ -26,7 +26,7 @@ function styleTrace(gd, calcTrace) { .style('opacity', marker.opacity); }); - Drawing.selectedPointStyle(locs, trace, gd); + Drawing.selectedPointStyle(locs, trace); } function styleOnSelect(gd, calcTrace) { @@ -34,7 +34,7 @@ function styleOnSelect(gd, calcTrace) { var trace = calcTrace[0].trace; if(trace.selectedpoints) { - Drawing.selectedPointStyle(s.selectAll('.choroplethlocation'), trace, gd); + Drawing.selectedPointStyle(s.selectAll('.choroplethlocation'), trace); } else { styleTrace(gd, calcTrace); } diff --git a/src/traces/scatter/arrays_to_calcdata.js b/src/traces/scatter/arrays_to_calcdata.js index 44f63d81609..94dfbd06813 100644 --- a/src/traces/scatter/arrays_to_calcdata.js +++ b/src/traces/scatter/arrays_to_calcdata.js @@ -24,6 +24,8 @@ module.exports = function arraysToCalcdata(cd, trace) { Lib.mergeArrayCastPositive(marker.size, cd, 'ms'); Lib.mergeArrayCastPositive(marker.opacity, cd, 'mo'); Lib.mergeArray(marker.symbol, cd, 'mx'); + Lib.mergeArray(marker.angle, cd, 'ma'); + Lib.mergeArray(marker.standoff, cd, 'mf'); Lib.mergeArray(marker.color, cd, 'mc'); var markerLine = marker.line; diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index ad59a800fb1..cce2de0cd7d 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -292,6 +292,18 @@ module.exports = { ].join(' ') }, dash: extendFlat({}, dash, {editType: 'style'}), + backoff: { // we want to have a similar option for the start of the line + valType: 'number', + min: 0, + dflt: 'auto', + arrayOk: true, + editType: 'plot', + description: [ + 'Sets the line back off from the end point of the nth line segment (in px).', + 'This option is useful e.g. to avoid overlap with arrowhead markers.', + 'With *auto* the lines would trim before markers if `marker.angleref` is set to *previous*.' + ].join(' ') + }, simplify: { valType: 'boolean', dflt: true, @@ -389,6 +401,41 @@ module.exports = { anim: true, description: 'Sets the marker opacity.' }, + angle: { + valType: 'angle', + dflt: 0, + arrayOk: true, + editType: 'plot', + anim: false, // TODO: possibly set to true in future + description: [ + 'Sets the marker angle in respect to `angleref`.' + ].join(' ') + }, + angleref: { + valType: 'enumerated', + values: ['previous', 'up'], + dflt: 'up', + editType: 'plot', + anim: false, + description: [ + 'Sets the reference for marker angle.', + 'With *previous*, angle 0 points along the line from the previous point to this one.', + 'With *up*, angle 0 points toward the top of the screen.' + ].join(' ') + }, + standoff: { + valType: 'number', + min: 0, + dflt: 0, + arrayOk: true, + editType: 'plot', + anim: true, + description: [ + 'Moves the marker away from the data point in the direction of `angle` (in px).', + 'This can be useful for example if you have another marker at this', + 'location and you want to point an arrowhead marker at it.' + ].join(' ') + }, size: { valType: 'number', min: 0, diff --git a/src/traces/scatter/calc.js b/src/traces/scatter/calc.js index 2c2ac558a84..a7471c0d85b 100644 --- a/src/traces/scatter/calc.js +++ b/src/traces/scatter/calc.js @@ -14,8 +14,8 @@ var calcSelection = require('./calc_selection'); function calc(gd, trace) { var fullLayout = gd._fullLayout; - var xa = Axes.getFromId(gd, trace.xaxis || 'x'); - var ya = Axes.getFromId(gd, trace.yaxis || 'y'); + var xa = trace._xA = Axes.getFromId(gd, trace.xaxis || 'x', 'x'); + var ya = trace._yA = Axes.getFromId(gd, trace.yaxis || 'y', 'y'); var origX = xa.makeCalcdata(trace, 'x'); var origY = ya.makeCalcdata(trace, 'y'); var xObj = alignPeriod(trace, xa, 'x', origX); diff --git a/src/traces/scatter/defaults.js b/src/traces/scatter/defaults.js index 0d47c52c216..33ac037c920 100644 --- a/src/traces/scatter/defaults.js +++ b/src/traces/scatter/defaults.js @@ -39,7 +39,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('mode', defaultMode); if(subTypes.hasLines(traceOut)) { - handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); + handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true}); handleLineShapeDefaults(traceIn, traceOut, coerce); coerce('connectgaps'); coerce('line.simplify'); diff --git a/src/traces/scatter/line_defaults.js b/src/traces/scatter/line_defaults.js index 1812112a077..fe70e8f847a 100644 --- a/src/traces/scatter/line_defaults.js +++ b/src/traces/scatter/line_defaults.js @@ -5,6 +5,8 @@ var hasColorscale = require('../../components/colorscale/helpers').hasColorscale var colorscaleDefaults = require('../../components/colorscale/defaults'); module.exports = function lineDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) { + if(!opts) opts = {}; + var markerColor = (traceIn.marker || {}).color; coerce('line.color', defaultColor); @@ -17,5 +19,7 @@ module.exports = function lineDefaults(traceIn, traceOut, defaultColor, layout, } coerce('line.width'); - if(!(opts || {}).noDash) coerce('line.dash'); + + if(!opts.noDash) coerce('line.dash'); + if(opts.backoff) coerce('line.backoff'); }; diff --git a/src/traces/scatter/line_points.js b/src/traces/scatter/line_points.js index 58c9436dc59..3ae36f952c3 100644 --- a/src/traces/scatter/line_points.js +++ b/src/traces/scatter/line_points.js @@ -1,5 +1,6 @@ 'use strict'; +var Drawing = require('../../components/drawing'); var numConstants = require('../../constants/numerical'); var BADNUM = numConstants.BADNUM; var LOG_CLIP = numConstants.LOG_CLIP; @@ -12,17 +13,20 @@ var constants = require('./constants'); module.exports = function linePoints(d, opts) { + var trace = opts.trace || {}; var xa = opts.xaxis; var ya = opts.yaxis; var xLog = xa.type === 'log'; var yLog = ya.type === 'log'; var xLen = xa._length; var yLen = ya._length; + var backoff = opts.backoff; + var marker = trace.marker; var connectGaps = opts.connectGaps; var baseTolerance = opts.baseTolerance; var shape = opts.shape; var linear = shape === 'linear'; - var fill = opts.fill && opts.fill !== 'none'; + var fill = trace.fill && trace.fill !== 'none'; var segments = []; var minTolerance = constants.minTolerance; var len = d.length; @@ -275,7 +279,17 @@ module.exports = function linePoints(d, opts) { lastXEdge = lastYEdge = 0; } + var arrayMarker = Lib.isArrayOrTypedArray(marker); + function addPt(pt) { + if(pt && backoff) { + pt.i = i; + pt.d = d; + pt.trace = trace; + pt.marker = arrayMarker ? marker[pt.i] : marker; + pt.backoff = backoff; + } + latestXFrac = pt[0] / xLen; latestYFrac = pt[1] / yLen; // Are we more than maxScreensAway off-screen any direction? @@ -446,5 +460,36 @@ module.exports = function linePoints(d, opts) { segments.push(pts.slice(0, pti)); } + + var lastShapeChar = shape.slice(shape.length - 1); + if(backoff && lastShapeChar !== 'h' && lastShapeChar !== 'v') { + var trimmed = false; + var n = -1; + var newSegments = []; + + for(var j = 0; j < segments.length; j++) { + for(var k = 0; k < segments[j].length - 1; k++) { + var start = segments[j][k]; + var end = segments[j][k + 1]; + + var xy = Drawing.applyBackoff(end, start); + if( + xy[0] !== end[0] || + xy[1] !== end[1] + ) { + trimmed = true; + } + if(!newSegments[n + 1]) { + n++; + newSegments[n] = [ + start, [xy[0], xy[1]] + ]; + } + } + } + + return trimmed ? newSegments : segments; + } + return segments; }; diff --git a/src/traces/scatter/marker_defaults.js b/src/traces/scatter/marker_defaults.js index a881cc7cf3c..c4358730534 100644 --- a/src/traces/scatter/marker_defaults.js +++ b/src/traces/scatter/marker_defaults.js @@ -25,6 +25,16 @@ module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout coerce('marker.symbol'); coerce('marker.opacity', isBubble ? 0.7 : 1); coerce('marker.size'); + if(!opts.noAngle) { + coerce('marker.angle'); + if(!opts.noAngleRef) { + coerce('marker.angleref'); + } + + if(!opts.noStandOff) { + coerce('marker.standoff'); + } + } coerce('marker.color', defaultColor); if(hasColorscale(traceIn, 'marker')) { diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js index 7fb3e6c3cc9..3a63470687e 100644 --- a/src/traces/scatter/plot.js +++ b/src/traces/scatter/plot.js @@ -209,9 +209,11 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition segments = linePoints(cdscatter, { xaxis: xa, yaxis: ya, + trace: trace, connectGaps: trace.connectgaps, baseTolerance: Math.max(line.width || 1, 3) / 4, shape: line.shape, + backoff: line.backoff, simplify: line.simplify, fill: trace.fill }); diff --git a/src/traces/scatter3d/defaults.js b/src/traces/scatter3d/defaults.js index eb9e130565b..2cb922e5be6 100644 --- a/src/traces/scatter3d/defaults.js +++ b/src/traces/scatter3d/defaults.js @@ -36,7 +36,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noSelect: true}); + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noSelect: true, noAngle: true}); } if(subTypes.hasText(traceOut)) { diff --git a/src/traces/scattercarpet/attributes.js b/src/traces/scattercarpet/attributes.js index 54a81e80b37..6022878cc9a 100644 --- a/src/traces/scattercarpet/attributes.js +++ b/src/traces/scattercarpet/attributes.js @@ -61,6 +61,7 @@ module.exports = { color: scatterLineAttrs.color, width: scatterLineAttrs.width, dash: scatterLineAttrs.dash, + backoff: scatterLineAttrs.backoff, shape: extendFlat({}, scatterLineAttrs.shape, {values: ['linear', 'spline']}), smoothing: scatterLineAttrs.smoothing, @@ -87,6 +88,9 @@ module.exports = { symbol: scatterMarkerAttrs.symbol, opacity: scatterMarkerAttrs.opacity, maxdisplayed: scatterMarkerAttrs.maxdisplayed, + angle: scatterMarkerAttrs.angle, + angleref: scatterMarkerAttrs.angleref, + standoff: scatterMarkerAttrs.standoff, size: scatterMarkerAttrs.size, sizeref: scatterMarkerAttrs.sizeref, sizemin: scatterMarkerAttrs.sizemin, diff --git a/src/traces/scattercarpet/defaults.js b/src/traces/scattercarpet/defaults.js index 29986064fde..b21f4e956df 100644 --- a/src/traces/scattercarpet/defaults.js +++ b/src/traces/scattercarpet/defaults.js @@ -42,7 +42,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('mode', defaultMode); if(subTypes.hasLines(traceOut)) { - handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); + handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true}); handleLineShapeDefaults(traceIn, traceOut, coerce); coerce('connectgaps'); } diff --git a/src/traces/scattercarpet/plot.js b/src/traces/scattercarpet/plot.js index d94ae5d6c51..6d7de9cacd1 100644 --- a/src/traces/scattercarpet/plot.js +++ b/src/traces/scattercarpet/plot.js @@ -8,13 +8,24 @@ module.exports = function plot(gd, plotinfoproxy, data, layer) { var i, trace, node; var carpet = data[0][0].carpet; + + var xaxis = Axes.getFromId(gd, carpet.xaxis || 'x'); + var yaxis = Axes.getFromId(gd, carpet.yaxis || 'y'); + // mimic cartesian plotinfo var plotinfo = { - xaxis: Axes.getFromId(gd, carpet.xaxis || 'x'), - yaxis: Axes.getFromId(gd, carpet.yaxis || 'y'), + xaxis: xaxis, + yaxis: yaxis, plot: plotinfoproxy.plot, }; + for(i = 0; i < data.length; i++) { + trace = data[i][0].trace; + + trace._xA = xaxis; + trace._yA = yaxis; + } + scatterPlot(gd, plotinfo, data, layer); for(i = 0; i < data.length; i++) { diff --git a/src/traces/scattergeo/attributes.js b/src/traces/scattergeo/attributes.js index 7a9b7509d21..d80b62c7735 100644 --- a/src/traces/scattergeo/attributes.js +++ b/src/traces/scattergeo/attributes.js @@ -114,6 +114,17 @@ module.exports = overrideAll({ marker: extendFlat({ symbol: scatterMarkerAttrs.symbol, opacity: scatterMarkerAttrs.opacity, + angle: scatterMarkerAttrs.angle, + angleref: extendFlat({}, scatterMarkerAttrs.angleref, { + values: ['previous', 'up', 'north'], + description: [ + 'Sets the reference for marker angle.', + 'With *previous*, angle 0 points along the line from the previous point to this one.', + 'With *up*, angle 0 points toward the top of the screen.', + 'With *north*, angle 0 points north based on the current map projection.', + ].join(' ') + }), + standoff: scatterMarkerAttrs.standoff, size: scatterMarkerAttrs.size, sizeref: scatterMarkerAttrs.sizeref, sizemin: scatterMarkerAttrs.sizemin, diff --git a/src/traces/scattergl/attributes.js b/src/traces/scattergl/attributes.js index cf49750ed4f..f458b27086b 100644 --- a/src/traces/scattergl/attributes.js +++ b/src/traces/scattergl/attributes.js @@ -67,6 +67,7 @@ var attrs = module.exports = overrideAll({ }, marker: extendFlat({}, colorScaleAttrs('marker'), { symbol: scatterMarkerAttrs.symbol, + angle: scatterMarkerAttrs.angle, size: scatterMarkerAttrs.size, sizeref: scatterMarkerAttrs.sizeref, sizemin: scatterMarkerAttrs.sizemin, diff --git a/src/traces/scattergl/calc.js b/src/traces/scattergl/calc.js index d27f12e5fc4..240418b07af 100644 --- a/src/traces/scattergl/calc.js +++ b/src/traces/scattergl/calc.js @@ -20,8 +20,9 @@ var TOO_MANY_POINTS = require('./constants').TOO_MANY_POINTS; module.exports = function calc(gd, trace) { var fullLayout = gd._fullLayout; - var xa = AxisIDs.getFromId(gd, trace.xaxis); - var ya = AxisIDs.getFromId(gd, trace.yaxis); + var xa = trace._xA = AxisIDs.getFromId(gd, trace.xaxis, 'x'); + var ya = trace._yA = AxisIDs.getFromId(gd, trace.yaxis, 'y'); + var subplot = fullLayout._plots[trace.xaxis + trace.yaxis]; var len = trace._length; var hasTooManyPoints = len >= TOO_MANY_POINTS; diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 3a42dc4050c..66c43d753cb 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -50,9 +50,9 @@ function convertStyle(gd, trace) { } if(subTypes.hasMarkers(trace)) { - opts.marker = convertMarkerStyle(trace); - opts.markerSel = convertMarkerSelection(trace, trace.selected); - opts.markerUnsel = convertMarkerSelection(trace, trace.unselected); + opts.marker = convertMarkerStyle(gd, trace); + opts.markerSel = convertMarkerSelection(gd, trace, trace.selected); + opts.markerUnsel = convertMarkerSelection(gd, trace, trace.unselected); if(!trace.unselected && Lib.isArrayOrTypedArray(trace.marker.opacity)) { var mo = trace.marker.opacity; @@ -207,13 +207,14 @@ function convertTextStyle(gd, trace) { } -function convertMarkerStyle(trace) { +function convertMarkerStyle(gd, trace) { var count = trace._length; var optsIn = trace.marker; var optsOut = {}; var i; var multiSymbol = Lib.isArrayOrTypedArray(optsIn.symbol); + var multiAngle = Lib.isArrayOrTypedArray(optsIn.angle); var multiColor = Lib.isArrayOrTypedArray(optsIn.color); var multiLineColor = Lib.isArrayOrTypedArray(optsIn.line.color); var multiOpacity = Lib.isArrayOrTypedArray(optsIn.opacity); @@ -224,10 +225,14 @@ function convertMarkerStyle(trace) { if(!multiSymbol) isOpen = helpers.isOpenSymbol(optsIn.symbol); // prepare colors - if(multiSymbol || multiColor || multiLineColor || multiOpacity) { + if(multiSymbol || multiColor || multiLineColor || multiOpacity || multiAngle) { + optsOut.symbols = new Array(count); + optsOut.angles = new Array(count); optsOut.colors = new Array(count); optsOut.borderColors = new Array(count); + var symbols = optsIn.symbol; + var angles = optsIn.angle; var colors = formatColor(optsIn, optsIn.opacity, count); var borderColors = formatColor(optsIn.line, optsIn.opacity, count); @@ -245,14 +250,29 @@ function convertMarkerStyle(trace) { colors[i] = color; } } + if(!Array.isArray(symbols)) { + var symbol = symbols; + symbols = Array(count); + for(i = 0; i < count; i++) { + symbols[i] = symbol; + } + } + if(!Array.isArray(angles)) { + var angle = angles; + angles = Array(count); + for(i = 0; i < count; i++) { + angles[i] = angle; + } + } + optsOut.symbols = symbols; + optsOut.angles = angles; optsOut.colors = colors; optsOut.borderColors = borderColors; for(i = 0; i < count; i++) { if(multiSymbol) { - var symbol = optsIn.symbol[i]; - isOpen = helpers.isOpenSymbol(symbol); + isOpen = helpers.isOpenSymbol(optsIn.symbol[i]); } if(isOpen) { borderColors[i] = colors[i].slice(); @@ -262,6 +282,14 @@ function convertMarkerStyle(trace) { } optsOut.opacity = trace.opacity; + + optsOut.markers = new Array(count); + for(i = 0; i < count; i++) { + optsOut.markers[i] = getSymbolSdf({ + mx: optsOut.symbols[i], + ma: optsOut.angles[i] + }, trace); + } } else { if(isOpen) { optsOut.color = rgba(optsIn.color, 'uint8'); @@ -273,16 +301,11 @@ function convertMarkerStyle(trace) { } optsOut.opacity = trace.opacity * optsIn.opacity; - } - // prepare symbols - if(multiSymbol) { - optsOut.markers = new Array(count); - for(i = 0; i < count; i++) { - optsOut.markers[i] = getSymbolSdf(optsIn.symbol[i]); - } - } else { - optsOut.marker = getSymbolSdf(optsIn.symbol); + optsOut.marker = getSymbolSdf({ + mx: optsIn.symbol, + ma: optsIn.angle + }, trace); } // prepare sizes @@ -330,14 +353,14 @@ function convertMarkerStyle(trace) { return optsOut; } -function convertMarkerSelection(trace, target) { +function convertMarkerSelection(gd, trace, target) { var optsIn = trace.marker; var optsOut = {}; if(!target) return optsOut; if(target.marker && target.marker.symbol) { - optsOut = convertMarkerStyle(Lib.extendFlat({}, optsIn, target.marker)); + optsOut = convertMarkerStyle(gd, Lib.extendFlat({}, optsIn, target.marker)); } else if(target.marker) { if(target.marker.size) optsOut.size = target.marker.size; if(target.marker.color) optsOut.colors = target.marker.color; @@ -389,7 +412,8 @@ var SYMBOL_STROKE = constants.SYMBOL_STROKE; var SYMBOL_SDF = {}; var SYMBOL_SVG_CIRCLE = Drawing.symbolFuncs[0](SYMBOL_SIZE * 0.05); -function getSymbolSdf(symbol) { +function getSymbolSdf(d, trace) { + var symbol = d.mx; if(symbol === 'circle') return null; var symbolPath, symbolSdf; @@ -400,13 +424,17 @@ function getSymbolSdf(symbol) { var isDot = helpers.isDotSymbol(symbol); + // until we may handle angles in shader? + if(d.ma) symbol += '_' + d.ma; + // get symbol sdf from cache or generate it if(SYMBOL_SDF[symbol]) return SYMBOL_SDF[symbol]; + var angle = Drawing.getMarkerAngle(d, trace); if(isDot && !symbolNoDot) { - symbolPath = symbolFunc(SYMBOL_SIZE * 1.1) + SYMBOL_SVG_CIRCLE; + symbolPath = symbolFunc(SYMBOL_SIZE * 1.1, angle) + SYMBOL_SVG_CIRCLE; } else { - symbolPath = symbolFunc(SYMBOL_SIZE); + symbolPath = symbolFunc(SYMBOL_SIZE, angle); } symbolSdf = svgSdf(symbolPath, { @@ -415,6 +443,7 @@ function getSymbolSdf(symbol) { viewBox: [-SYMBOL_SIZE, -SYMBOL_SIZE, SYMBOL_SIZE, SYMBOL_SIZE], stroke: symbolNoFill ? SYMBOL_STROKE : -SYMBOL_STROKE }); + SYMBOL_SDF[symbol] = symbolSdf; return symbolSdf || null; @@ -545,8 +574,8 @@ function convertLinePositions(gd, trace, positions) { function convertErrorBarPositions(gd, trace, positions, x, y) { var makeComputeError = Registry.getComponentMethod('errorbars', 'makeComputeError'); - var xa = AxisIDs.getFromId(gd, trace.xaxis); - var ya = AxisIDs.getFromId(gd, trace.yaxis); + var xa = AxisIDs.getFromId(gd, trace.xaxis, 'x'); + var ya = AxisIDs.getFromId(gd, trace.yaxis, 'y'); var count = positions.length / 2; var out = {}; diff --git a/src/traces/scattergl/defaults.js b/src/traces/scattergl/defaults.js index b4cce07a5d7..a15ff09e254 100644 --- a/src/traces/scattergl/defaults.js +++ b/src/traces/scattergl/defaults.js @@ -46,7 +46,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce); + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noAngleRef: true, noStandOff: true}); coerce('marker.line.width', isOpen || isBubble ? 1 : 0); } diff --git a/src/traces/scattergl/hover.js b/src/traces/scattergl/hover.js index 41e25458f6f..74efc4776b3 100644 --- a/src/traces/scattergl/hover.js +++ b/src/traces/scattergl/hover.js @@ -139,6 +139,7 @@ function calcHover(pointData, x, y, trace) { di.ms = Lib.isArrayOrTypedArray(marker.size) ? marker.size[id] : marker.size; di.mo = Lib.isArrayOrTypedArray(marker.opacity) ? marker.opacity[id] : marker.opacity; di.mx = Lib.isArrayOrTypedArray(marker.symbol) ? marker.symbol[id] : marker.symbol; + di.ma = Lib.isArrayOrTypedArray(marker.angle) ? marker.angle[id] : marker.angle; di.mc = Lib.isArrayOrTypedArray(marker.color) ? marker.color[id] : marker.color; } diff --git a/src/traces/scattermapbox/defaults.js b/src/traces/scattermapbox/defaults.js index 7d6747783ad..e3b87adc3df 100644 --- a/src/traces/scattermapbox/defaults.js +++ b/src/traces/scattermapbox/defaults.js @@ -33,7 +33,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noLine: true}); + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noLine: true, noAngle: true}); coerce('marker.allowoverlap'); coerce('marker.angle'); diff --git a/src/traces/scatterpolar/attributes.js b/src/traces/scatterpolar/attributes.js index 81b6ae72482..f5d0321c519 100644 --- a/src/traces/scatterpolar/attributes.js +++ b/src/traces/scatterpolar/attributes.js @@ -82,6 +82,7 @@ module.exports = { color: lineAttrs.color, width: lineAttrs.width, dash: lineAttrs.dash, + backoff: lineAttrs.backoff, shape: extendFlat({}, lineAttrs.shape, { values: ['linear', 'spline'] }), diff --git a/src/traces/scatterpolar/defaults.js b/src/traces/scatterpolar/defaults.js index 0d18056ba38..5bf2c5e97b9 100644 --- a/src/traces/scatterpolar/defaults.js +++ b/src/traces/scatterpolar/defaults.js @@ -30,7 +30,7 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { if(traceOut.hoveron !== 'fills') coerce('hovertemplate'); if(subTypes.hasLines(traceOut)) { - handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); + handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true}); handleLineShapeDefaults(traceIn, traceOut, coerce); coerce('connectgaps'); } diff --git a/src/traces/scatterpolar/plot.js b/src/traces/scatterpolar/plot.js index a62ff1bdd48..a456701c160 100644 --- a/src/traces/scatterpolar/plot.js +++ b/src/traces/scatterpolar/plot.js @@ -6,9 +6,12 @@ var BADNUM = require('../../constants/numerical').BADNUM; module.exports = function plot(gd, subplot, moduleCalcData) { var mlayer = subplot.layers.frontplot.select('g.scatterlayer'); + var xa = subplot.xaxis; + var ya = subplot.yaxis; + var plotinfo = { - xaxis: subplot.xaxis, - yaxis: subplot.yaxis, + xaxis: xa, + yaxis: ya, plot: subplot.framework, layerClipId: subplot._hasClipOnAxisFalse ? subplot.clipIds.forTraces : null }; @@ -22,6 +25,11 @@ module.exports = function plot(gd, subplot, moduleCalcData) { var cdi = moduleCalcData[i]; for(var j = 0; j < cdi.length; j++) { + if(j === 0) { + cdi[0].trace._xA = xa; + cdi[0].trace._yA = ya; + } + var cd = cdi[j]; var r = cd.r; diff --git a/src/traces/scatterpolargl/defaults.js b/src/traces/scatterpolargl/defaults.js index 65c25f26a06..6b64bcb3179 100644 --- a/src/traces/scatterpolargl/defaults.js +++ b/src/traces/scatterpolargl/defaults.js @@ -35,7 +35,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce); + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noAngleRef: true, noStandOff: true}); } if(subTypes.hasText(traceOut)) { diff --git a/src/traces/scattersmith/attributes.js b/src/traces/scattersmith/attributes.js index 90311398341..2344a5fc2a5 100644 --- a/src/traces/scattersmith/attributes.js +++ b/src/traces/scattersmith/attributes.js @@ -38,6 +38,7 @@ module.exports = { color: lineAttrs.color, width: lineAttrs.width, dash: lineAttrs.dash, + backoff: lineAttrs.backoff, shape: extendFlat({}, lineAttrs.shape, { values: ['linear', 'spline'] }), diff --git a/src/traces/scattersmith/defaults.js b/src/traces/scattersmith/defaults.js index ad00da49fd4..d1001487803 100644 --- a/src/traces/scattersmith/defaults.js +++ b/src/traces/scattersmith/defaults.js @@ -29,7 +29,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout if(traceOut.hoveron !== 'fills') coerce('hovertemplate'); if(subTypes.hasLines(traceOut)) { - handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); + handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true}); handleLineShapeDefaults(traceIn, traceOut, coerce); coerce('connectgaps'); } diff --git a/src/traces/scattersmith/plot.js b/src/traces/scattersmith/plot.js index 5a9c12a3bcf..7bd853abc98 100644 --- a/src/traces/scattersmith/plot.js +++ b/src/traces/scattersmith/plot.js @@ -8,9 +8,12 @@ var smith = helpers.smith; module.exports = function plot(gd, subplot, moduleCalcData) { var mlayer = subplot.layers.frontplot.select('g.scatterlayer'); + var xa = subplot.xaxis; + var ya = subplot.yaxis; + var plotinfo = { - xaxis: subplot.xaxis, - yaxis: subplot.yaxis, + xaxis: xa, + yaxis: ya, plot: subplot.framework, layerClipId: subplot._hasClipOnAxisFalse ? subplot.clipIds.forTraces : null }; @@ -21,6 +24,11 @@ module.exports = function plot(gd, subplot, moduleCalcData) { var cdi = moduleCalcData[i]; for(var j = 0; j < cdi.length; j++) { + if(j === 0) { + cdi[0].trace._xA = xa; + cdi[0].trace._yA = ya; + } + var cd = cdi[j]; var real = cd.real; diff --git a/src/traces/scatterternary/attributes.js b/src/traces/scatterternary/attributes.js index 1b87b52ff01..23c0bba0621 100644 --- a/src/traces/scatterternary/attributes.js +++ b/src/traces/scatterternary/attributes.js @@ -89,6 +89,7 @@ module.exports = { color: scatterLineAttrs.color, width: scatterLineAttrs.width, dash: dash, + backoff: scatterLineAttrs.backoff, shape: extendFlat({}, scatterLineAttrs.shape, {values: ['linear', 'spline']}), smoothing: scatterLineAttrs.smoothing, @@ -115,6 +116,9 @@ module.exports = { marker: extendFlat({ symbol: scatterMarkerAttrs.symbol, opacity: scatterMarkerAttrs.opacity, + angle: scatterMarkerAttrs.angle, + angleref: scatterMarkerAttrs.angleref, + standoff: scatterMarkerAttrs.standoff, maxdisplayed: scatterMarkerAttrs.maxdisplayed, size: scatterMarkerAttrs.size, sizeref: scatterMarkerAttrs.sizeref, diff --git a/src/traces/scatterternary/defaults.js b/src/traces/scatterternary/defaults.js index 3e1569f0482..cff521b24b9 100644 --- a/src/traces/scatterternary/defaults.js +++ b/src/traces/scatterternary/defaults.js @@ -56,7 +56,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('mode', defaultMode); if(subTypes.hasLines(traceOut)) { - handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); + handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true}); handleLineShapeDefaults(traceIn, traceOut, coerce); coerce('connectgaps'); } diff --git a/src/traces/scatterternary/plot.js b/src/traces/scatterternary/plot.js index a3d0e4172b5..cac38c11635 100644 --- a/src/traces/scatterternary/plot.js +++ b/src/traces/scatterternary/plot.js @@ -9,14 +9,25 @@ module.exports = function plot(gd, ternary, moduleCalcData) { plotContainer.select('.scatterlayer').selectAll('*').remove(); // mimic cartesian plotinfo + var xa = ternary.xaxis; + var ya = ternary.yaxis; + var plotinfo = { - xaxis: ternary.xaxis, - yaxis: ternary.yaxis, + xaxis: xa, + yaxis: ya, plot: plotContainer, layerClipId: ternary._hasClipOnAxisFalse ? ternary.clipIdRelative : null }; var scatterLayer = ternary.layers.frontplot.select('g.scatterlayer'); + for(var i = 0; i < moduleCalcData.length; i++) { + var cdi = moduleCalcData[i]; + if(cdi.length) { + cdi[0].trace._xA = xa; + cdi[0].trace._yA = ya; + } + } + scatterPlot(gd, plotinfo, moduleCalcData, scatterLayer); }; diff --git a/src/traces/splom/attributes.js b/src/traces/splom/attributes.js index dfbb585255a..0c1c09b8457 100644 --- a/src/traces/splom/attributes.js +++ b/src/traces/splom/attributes.js @@ -19,6 +19,7 @@ var markerLineAttrs = extendFlat(colorScaleAttrs('marker.line', {editTypeOverrid var markerAttrs = extendFlat(colorScaleAttrs('marker'), { symbol: scatterMarkerAttrs.symbol, + angle: scatterMarkerAttrs.angle, size: extendFlat({}, scatterMarkerAttrs.size, {editType: 'markerSize'}), sizeref: scatterMarkerAttrs.sizeref, sizemin: scatterMarkerAttrs.sizemin, diff --git a/src/traces/splom/calc.js b/src/traces/splom/calc.js index c7544278866..5cdb35bcad1 100644 --- a/src/traces/splom/calc.js +++ b/src/traces/splom/calc.js @@ -67,7 +67,7 @@ module.exports = function calc(gd, trace) { } calcColorscale(gd, trace); - Lib.extendFlat(opts, convertMarkerStyle(trace)); + Lib.extendFlat(opts, convertMarkerStyle(gd, trace)); var visibleLength = cdata.length; var hasTooManyPoints = (visibleLength * commonLength) > TOO_MANY_POINTS; @@ -94,8 +94,8 @@ module.exports = function calc(gd, trace) { if(!scene.matrix) scene.matrix = true; scene.matrixOptions = opts; - scene.selectedOptions = convertMarkerSelection(trace, trace.selected); - scene.unselectedOptions = convertMarkerSelection(trace, trace.unselected); + scene.selectedOptions = convertMarkerSelection(gd, trace, trace.selected); + scene.unselectedOptions = convertMarkerSelection(gd, trace, trace.unselected); return [{x: false, y: false, t: {}, trace: trace}]; }; diff --git a/src/traces/splom/defaults.js b/src/traces/splom/defaults.js index 8e450badf07..72cb247733a 100644 --- a/src/traces/splom/defaults.js +++ b/src/traces/splom/defaults.js @@ -36,7 +36,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('xhoverformat'); coerce('yhoverformat'); - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce); + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noAngleRef: true, noStandOff: true}); var isOpen = isOpenSymbol(traceOut.marker.symbol); var isBubble = subTypes.isBubble(traceOut); diff --git a/src/traces/splom/edit_style.js b/src/traces/splom/edit_style.js index f53071295a9..c3abca799ea 100644 --- a/src/traces/splom/edit_style.js +++ b/src/traces/splom/edit_style.js @@ -11,7 +11,7 @@ module.exports = function editStyle(gd, cd0) { if(scene) { calcColorscale(gd, trace); - Lib.extendFlat(scene.matrixOptions, convertMarkerStyle(trace)); + Lib.extendFlat(scene.matrixOptions, convertMarkerStyle(gd, trace)); // TODO [un]selected styles? var opts = Lib.extendFlat({}, scene.matrixOptions, scene.viewOpts); diff --git a/src/traces/violin/plot.js b/src/traces/violin/plot.js index 62f46650266..b46292d319d 100644 --- a/src/traces/violin/plot.js +++ b/src/traces/violin/plot.js @@ -13,10 +13,11 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { var xa = plotinfo.xaxis; var ya = plotinfo.yaxis; - function makePath(pts) { + function makePath(pts, trace) { var segments = linePoints(pts, { xaxis: xa, yaxis: ya, + trace: trace, connectGaps: true, baseTolerance: 0.75, shape: 'spline', @@ -80,7 +81,7 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { pt[t.posLetter] = posCenter + (density[i].v / scale); pt[t.valLetter] = valAxis.c2l(density[i].t, true); } - pathPos = makePath(pts); + pathPos = makePath(pts, trace); } if(hasNegativeSide) { @@ -90,7 +91,7 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { pt[t.posLetter] = posCenter - (density[i].v / scale); pt[t.valLetter] = valAxis.c2l(density[i].t, true); } - pathNeg = makePath(pts); + pathNeg = makePath(pts, trace); } if(hasBothSides) { diff --git a/test/image/baselines/19.png b/test/image/baselines/19.png index e98f113080f..1baf427a06c 100644 Binary files a/test/image/baselines/19.png and b/test/image/baselines/19.png differ diff --git a/test/image/baselines/box_grouped_horz.png b/test/image/baselines/box_grouped_horz.png index d3c07ef0049..f904a3757cb 100644 Binary files a/test/image/baselines/box_grouped_horz.png and b/test/image/baselines/box_grouped_horz.png differ diff --git a/test/image/baselines/geo_canadian-cities.png b/test/image/baselines/geo_canadian-cities.png index 2415bf9248e..aeedf21e9fb 100644 Binary files a/test/image/baselines/geo_canadian-cities.png and b/test/image/baselines/geo_canadian-cities.png differ diff --git a/test/image/baselines/geo_conic-conformal.png b/test/image/baselines/geo_conic-conformal.png index b6a6ad20a2d..b4900e61c1c 100644 Binary files a/test/image/baselines/geo_conic-conformal.png and b/test/image/baselines/geo_conic-conformal.png differ diff --git a/test/image/baselines/geo_scattergeo-out-of-usa.png b/test/image/baselines/geo_scattergeo-out-of-usa.png index a5679ab2c38..623f7d08cf0 100644 Binary files a/test/image/baselines/geo_scattergeo-out-of-usa.png and b/test/image/baselines/geo_scattergeo-out-of-usa.png differ diff --git a/test/image/baselines/geo_text_chart_arrays.png b/test/image/baselines/geo_text_chart_arrays.png index 36d3459582c..0f4a3b5e05a 100644 Binary files a/test/image/baselines/geo_text_chart_arrays.png and b/test/image/baselines/geo_text_chart_arrays.png differ diff --git a/test/image/baselines/gl2d_marker_symbols.png b/test/image/baselines/gl2d_marker_symbols.png index 56246eea2ac..c25f6b7fe13 100644 Binary files a/test/image/baselines/gl2d_marker_symbols.png and b/test/image/baselines/gl2d_marker_symbols.png differ diff --git a/test/image/baselines/glpolar_subplots.png b/test/image/baselines/glpolar_subplots.png index e603cdaa040..e25d4eb6913 100644 Binary files a/test/image/baselines/glpolar_subplots.png and b/test/image/baselines/glpolar_subplots.png differ diff --git a/test/image/baselines/marker_symbols.png b/test/image/baselines/marker_symbols.png index 8a902aabd21..f7ceb938ac2 100644 Binary files a/test/image/baselines/marker_symbols.png and b/test/image/baselines/marker_symbols.png differ diff --git a/test/image/baselines/polar_direction.png b/test/image/baselines/polar_direction.png index 17620d34c69..abb99d44401 100644 Binary files a/test/image/baselines/polar_direction.png and b/test/image/baselines/polar_direction.png differ diff --git a/test/image/baselines/scattercarpet-on-two-carpets.png b/test/image/baselines/scattercarpet-on-two-carpets.png index 1bca750da1e..c86c3857cb9 100644 Binary files a/test/image/baselines/scattercarpet-on-two-carpets.png and b/test/image/baselines/scattercarpet-on-two-carpets.png differ diff --git a/test/image/baselines/smith_gaps.png b/test/image/baselines/smith_gaps.png index 0b5efb1a5d5..25eaa1f3f80 100644 Binary files a/test/image/baselines/smith_gaps.png and b/test/image/baselines/smith_gaps.png differ diff --git a/test/image/baselines/splom_log.png b/test/image/baselines/splom_log.png index 39c842cc11d..d12325b9700 100644 Binary files a/test/image/baselines/splom_log.png and b/test/image/baselines/splom_log.png differ diff --git a/test/image/baselines/ternary_markers.png b/test/image/baselines/ternary_markers.png index 41740a11fb2..aeed01798be 100644 Binary files a/test/image/baselines/ternary_markers.png and b/test/image/baselines/ternary_markers.png differ diff --git a/test/image/baselines/violin_ridgeplot.png b/test/image/baselines/violin_ridgeplot.png index 66d3edbeb37..cb1de0b284c 100644 Binary files a/test/image/baselines/violin_ridgeplot.png and b/test/image/baselines/violin_ridgeplot.png differ diff --git a/test/image/baselines/z-gl2d_marker_symbols-angle.png b/test/image/baselines/z-gl2d_marker_symbols-angle.png new file mode 100644 index 00000000000..001d27f695f Binary files /dev/null and b/test/image/baselines/z-gl2d_marker_symbols-angle.png differ diff --git a/test/image/baselines/z-line-shape-arrow.png b/test/image/baselines/z-line-shape-arrow.png new file mode 100644 index 00000000000..8037c4d4625 Binary files /dev/null and b/test/image/baselines/z-line-shape-arrow.png differ diff --git a/test/image/baselines/z-line-shape-arrow_backoff-auto.png b/test/image/baselines/z-line-shape-arrow_backoff-auto.png new file mode 100644 index 00000000000..ac2811e4d84 Binary files /dev/null and b/test/image/baselines/z-line-shape-arrow_backoff-auto.png differ diff --git a/test/image/baselines/z-line-shape-arrow_backoff.png b/test/image/baselines/z-line-shape-arrow_backoff.png new file mode 100644 index 00000000000..bcab37977e9 Binary files /dev/null and b/test/image/baselines/z-line-shape-arrow_backoff.png differ diff --git a/test/image/baselines/z-marker-standoff.png b/test/image/baselines/z-marker-standoff.png new file mode 100644 index 00000000000..54db57a75a5 Binary files /dev/null and b/test/image/baselines/z-marker-standoff.png differ diff --git a/test/image/baselines/z-marker-standoff_auto-backoff.png b/test/image/baselines/z-marker-standoff_auto-backoff.png new file mode 100644 index 00000000000..34abdddae0c Binary files /dev/null and b/test/image/baselines/z-marker-standoff_auto-backoff.png differ diff --git a/test/image/baselines/z-marker_symbols-angle.png b/test/image/baselines/z-marker_symbols-angle.png new file mode 100644 index 00000000000..cac234aabbb Binary files /dev/null and b/test/image/baselines/z-marker_symbols-angle.png differ diff --git a/test/image/mocks/19.json b/test/image/mocks/19.json index 007d414dc63..8d645078d05 100644 --- a/test/image/mocks/19.json +++ b/test/image/mocks/19.json @@ -1,6 +1,11 @@ { "data": [ { + "marker": { + "size": 10, + "symbol": "arrow-wide", + "angleref": "previous" + }, "x": [ 1, 2, @@ -15,6 +20,12 @@ "type": "scatter" }, { + "marker": { + "size": 10, + "symbol": "arrow", + "angleref": "up", + "angle": [180, 0, 180] + }, "x": [ 20, 30, diff --git a/test/image/mocks/box_grouped_horz.json b/test/image/mocks/box_grouped_horz.json index 0f9dedb0d9d..fdc0c85eb02 100644 --- a/test/image/mocks/box_grouped_horz.json +++ b/test/image/mocks/box_grouped_horz.json @@ -3,7 +3,7 @@ "y": ["day 1", "day 1", "day 1", "day 1", "day 1", "day 1", "day 2", "day 2", "day 2", "day 2", "day 2", "day 2"], "name": "kale", - "marker": {"color": "#3D9970"}, + "marker": {"color": "#3D9970", "symbol": "arrow-wide", "angle": 0, "size": 10}, "type": "box", "orientation": "h", "pointpos": -1.5, @@ -16,7 +16,7 @@ "y": ["day 1", "day 1", "day 1", "day 1", "day 1", "day 1", "day 2", "day 2", "day 2", "day 2", "day 2", "day 2"], "name": "radishes", - "marker": {"color": "#FF4136"}, + "marker": {"color": "#FF4136", "symbol": "arrow", "angle": -90, "size": 10}, "type": "box", "orientation": "h", "pointpos": -1.5, @@ -29,7 +29,7 @@ "y": ["day 1", "day 1", "day 1", "day 1", "day 1", "day 1", "day 2", "day 2", "day 2", "day 2", "day 2", "day 2"], "name": "carrots", - "marker": {"color": "#FF851B"}, + "marker": {"color": "#FF851B", "symbol": "arrow", "angle": 90, "size": 10}, "type": "box", "orientation": "h", "pointpos": -1.5, diff --git a/test/image/mocks/geo_canadian-cities.json b/test/image/mocks/geo_canadian-cities.json index 11058db5e39..75db415dfdd 100644 --- a/test/image/mocks/geo_canadian-cities.json +++ b/test/image/mocks/geo_canadian-cities.json @@ -2,7 +2,7 @@ "data": [ { "type": "scattergeo", - "mode": "markers+text", + "mode": "markers+lines+text", "lon": [ -73.57, -79.24, @@ -39,7 +39,13 @@ "Winnepeg", "Regina" ], + "line": { + "color": "green", + "dash": "dot" + }, "marker": { + "symbol": "arrow-wide", + "angleref": "previous", "size": 10, "color": [ "#bebada", diff --git a/test/image/mocks/geo_conic-conformal.json b/test/image/mocks/geo_conic-conformal.json index e5da3ed1933..60d74a42b56 100644 --- a/test/image/mocks/geo_conic-conformal.json +++ b/test/image/mocks/geo_conic-conformal.json @@ -10,6 +10,9 @@ "FRA" ], "marker": { + "symbol": "arrow", + "angleref": "north", + "angle": [0, -90, 180, 90, 0], "color": [ 80, 20, diff --git a/test/image/mocks/geo_scattergeo-out-of-usa.json b/test/image/mocks/geo_scattergeo-out-of-usa.json index 6ed41dc33ec..37d407649ec 100644 --- a/test/image/mocks/geo_scattergeo-out-of-usa.json +++ b/test/image/mocks/geo_scattergeo-out-of-usa.json @@ -20009,7 +20009,12 @@ ], "mode": "markers", "marker": { - "color": "#19d3f3" + "symbol": "arrow", + "angleref": "north", + "angle": -90, + "size": 12, + "opacity": 0.1, + "color": "blue" }, "type": "scattergeo" } diff --git a/test/image/mocks/geo_text_chart_arrays.json b/test/image/mocks/geo_text_chart_arrays.json index b6b5a763827..a12e4b2efd3 100644 --- a/test/image/mocks/geo_text_chart_arrays.json +++ b/test/image/mocks/geo_text_chart_arrays.json @@ -40,7 +40,9 @@ "Regina" ], "marker": { - "size": 10, + "symbol": "arrow", + "angleref": "north", + "size": 20, "color": [ "#bebada", "#fdb462", diff --git a/test/image/mocks/gl2d_marker_symbols.json b/test/image/mocks/gl2d_marker_symbols.json index 8460619f1b4..a805c471405 100644 --- a/test/image/mocks/gl2d_marker_symbols.json +++ b/test/image/mocks/gl2d_marker_symbols.json @@ -8,14 +8,18 @@ 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 4 + 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, + 7 ], "y": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, - 0, 1, 2, 3, 4, 5, 6, 7, 8 + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 ], "marker": { "symbol": [ @@ -63,7 +67,17 @@ "line-ew", "line-ns", "line-ne", - "line-nw" + "line-nw", + "arrow-up", + "arrow-down", + "arrow-left", + "arrow-right", + "arrow-bar-up", + "arrow-bar-down", + "arrow-bar-left", + "arrow-bar-right", + "arrow", + "arrow-wide" ], "color": "blue", "size": 20, @@ -117,7 +131,17 @@ "marker symbol: line-ew
number: 41", "marker symbol: line-ns
number: 42", "marker symbol: line-ne
number: 43", - "marker symbol: line-nw
number: 44" + "marker symbol: line-nw
number: 44", + "marker symbol: arrow-up
number: 45", + "marker symbol: arrow-down
number: 46", + "marker symbol: arrow-left
number: 47", + "marker symbol: arrow-right
number: 48", + "marker symbol: arrow-bar-up
number: 49", + "marker symbol: arrow-bar-down
number: 50", + "marker symbol: arrow-bar-left
number: 51", + "marker symbol: arrow-bar-right
number: 52", + "marker symbol: arrow
number: 53", + "marker symbol: arrow-wide
number: 54" ], "hoverinfo": "text" }, @@ -125,18 +149,22 @@ "type": "scattergl", "mode": "markers", "x": [ - 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, - 8, 8, 8, 8, 8, 8, 8, 8, 8, - 9, 9, 9, 9, 9, 9, 9, 9, 9 + 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, + 16 ], "y": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, - 0, 1, 2, 3, 4, 5, 6, 7, 8 + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 ], "marker": { "symbol": [ @@ -184,7 +212,17 @@ "line-ew-open", "line-ns-open", "line-ne-open", - "line-nw-open" + "line-nw-open", + "arrow-up-open", + "arrow-down-open", + "arrow-left-open", + "arrow-right-open", + "arrow-bar-up-open", + "arrow-bar-down-open", + "arrow-bar-left-open", + "arrow-bar-right-open", + "arrow-open", + "arrow-wide-open" ], "color": "blue", "size": 20, @@ -238,7 +276,17 @@ "marker symbol: line-ew-open
number: 141", "marker symbol: line-ns-open
number: 142", "marker symbol: line-ne-open
number: 143", - "marker symbol: line-nw-open
number: 144" + "marker symbol: line-nw-open
number: 144", + "marker symbol: arrow-up-open
number: 145", + "marker symbol: arrow-down-open
number: 146", + "marker symbol: arrow-left-open
number: 147", + "marker symbol: arrow-right-open
number: 148", + "marker symbol: arrow-bar-up-open
number: 149", + "marker symbol: arrow-bar-down-open
number: 150", + "marker symbol: arrow-bar-left-open
number: 151", + "marker symbol: arrow-bar-right-open
number: 152", + "marker symbol: arrow-open
number: 153", + "marker symbol: arrow-wide-open
number: 154" ], "hoverinfo": "text" }, @@ -246,18 +294,22 @@ "type": "scattergl", "mode": "markers", "x": [ - 10, 10, 10, 10, 10, 10, 10, 10, 10, - 11, 11, 11, 11, 11, 11, 11, 11, 11, - 12, 12, 12, 12, 12, 12, 12, 12, 12, - 13, 13, 13, 13, 13, 13, 13, 13, 13, - 14, 14, 14, 14, 14, 14, 14, 14, 14 + 18, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, 22, + 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, + 25 ], "y": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, - 0, 1, 2, 3, 4, 5, 6, 7, 8 + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 ], "marker": { "symbol": [ @@ -305,7 +357,17 @@ "line-ew-dot", "line-ns-dot", "line-ne-dot", - "line-nw-dot" + "line-nw-dot", + "arrow-up-dot", + "arrow-down-dot", + "arrow-left-dot", + "arrow-right-dot", + "arrow-bar-up-dot", + "arrow-bar-down-dot", + "arrow-bar-left-dot", + "arrow-bar-right-dot", + "arrow-dot", + "arrow-wide-dot" ], "color": "blue", "size": 20, @@ -359,7 +421,17 @@ "marker symbol: line-ew-dot
number: 241", "marker symbol: line-ns-dot
number: 242", "marker symbol: line-ne-dot
number: 243", - "marker symbol: line-nw-dot
number: 244" + "marker symbol: line-nw-dot
number: 244", + "marker symbol: arrow-up-dot
number: 245", + "marker symbol: arrow-down-dot
number: 246", + "marker symbol: arrow-left-dot
number: 247", + "marker symbol: arrow-right-dot
number: 248", + "marker symbol: arrow-bar-up-dot
number: 249", + "marker symbol: arrow-bar-down-dot
number: 250", + "marker symbol: arrow-bar-left-dot
number: 251", + "marker symbol: arrow-bar-right-dot
number: 252", + "marker symbol: arrow-dot
number: 253", + "marker symbol: arrow-wide-dot
number: 254" ], "hoverinfo": "text" }, @@ -367,18 +439,22 @@ "type": "scattergl", "mode": "markers", "x": [ - 15, 15, 15, 15, 15, 15, 15, 15, 15, - 16, 16, 16, 16, 16, 16, 16, 16, 16, - 17, 17, 17, 17, 17, 17, 17, 17, 17, - 18, 18, 18, 18, 18, 18, 18, 18, 18, - 19, 19, 19, 19, 19, 19, 19, 19, 19 + 27, 27, 27, 27, 27, 27, 27, 27, 27, + 28, 28, 28, 28, 28, 28, 28, 28, 28, + 29, 29, 29, 29, 29, 29, 29, 29, 29, + 30, 30, 30, 30, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, + 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, + 34 ], "y": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, - 0, 1, 2, 3, 4, 5, 6, 7, 8 + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 ], "marker": { "symbol": [ @@ -426,7 +502,17 @@ "line-ew-open-dot", "line-ns-open-dot", "line-ne-open-dot", - "line-nw-open-dot" + "line-nw-open-dot", + "arrow-up-open-dot", + "arrow-down-open-dot", + "arrow-left-open-dot", + "arrow-right-open-dot", + "arrow-bar-up-open-dot", + "arrow-bar-down-open-dot", + "arrow-bar-left-open-dot", + "arrow-bar-right-open-dot", + "arrow-open-dot", + "arrow-wide-open-dot" ], "color": "blue", "size": 20, @@ -480,7 +566,18 @@ "marker symbol: line-ew-open-dot
number: 341", "marker symbol: line-ns-open-dot
number: 342", "marker symbol: line-ne-open-dot
number: 343", - "marker symbol: line-nw-open-dot
number: 344" + "marker symbol: line-nw-open-dot
number: 344", + "marker symbol: line-nw-open-dot
number: 344", + "marker symbol: arrow-up-open-dot
number: 345", + "marker symbol: arrow-down-open-dot
number: 346", + "marker symbol: arrow-left-open-dot
number: 347", + "marker symbol: arrow-right-open-dot
number: 348", + "marker symbol: arrow-bar-up-open-dot
number: 349", + "marker symbol: arrow-bar-down-open-dot
number: 350", + "marker symbol: arrow-bar-left-open-dot
number: 351", + "marker symbol: arrow-bar-right-open-dot
number: 352", + "marker symbol: arrow-open-dot
number: 353", + "marker symbol: arrow-wide-open-dot
number: 354" ], "hoverinfo": "text" } @@ -492,7 +589,7 @@ "b": 0, "t": 0 }, - "width": 800, + "width": 1500, "height": 500, "xaxis": { "showgrid": false, diff --git a/test/image/mocks/glpolar_subplots.json b/test/image/mocks/glpolar_subplots.json index 78a58cbda8d..888d36ae69f 100644 --- a/test/image/mocks/glpolar_subplots.json +++ b/test/image/mocks/glpolar_subplots.json @@ -6,7 +6,7 @@ "theta": [0, 45, 180], "text": ["A0", "B0", "C0"], "hovertext": ["hover A0", "hover B0", "hover C0"], - "marker": {"symbol": "square", "size": 15}, + "marker": {"symbol": "arrow", "angle": [0, 135, 180], "size": 15}, "textfont": { "color": ["red", "green", "blue"], "size": 20 diff --git a/test/image/mocks/marker_symbols.json b/test/image/mocks/marker_symbols.json index 7509fa7547f..197af9d1f9b 100644 --- a/test/image/mocks/marker_symbols.json +++ b/test/image/mocks/marker_symbols.json @@ -7,14 +7,18 @@ 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 4 + 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, + 7 ], "y": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, - 0, 1, 2, 3, 4, 5, 6, 7, 8 + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 ], "marker": { "symbol": [ @@ -62,7 +66,17 @@ "line-ew", "line-ns", "line-ne", - "line-nw" + "line-nw", + "arrow-up", + "arrow-down", + "arrow-left", + "arrow-right", + "arrow-bar-up", + "arrow-bar-down", + "arrow-bar-left", + "arrow-bar-right", + "arrow", + "arrow-wide" ], "color": "blue", "size": 20, @@ -116,25 +130,39 @@ "marker symbol: line-ew
number: 41", "marker symbol: line-ns
number: 42", "marker symbol: line-ne
number: 43", - "marker symbol: line-nw
number: 44" + "marker symbol: line-nw
number: 44", + "marker symbol: arrow-up
number: 45", + "marker symbol: arrow-down
number: 46", + "marker symbol: arrow-left
number: 47", + "marker symbol: arrow-right
number: 48", + "marker symbol: arrow-bar-up
number: 49", + "marker symbol: arrow-bar-down
number: 50", + "marker symbol: arrow-bar-left
number: 51", + "marker symbol: arrow-bar-right
number: 52", + "marker symbol: arrow
number: 53", + "marker symbol: arrow-wide
number: 54" ], "hoverinfo": "text" }, { "mode": "markers", "x": [ - 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, - 8, 8, 8, 8, 8, 8, 8, 8, 8, - 9, 9, 9, 9, 9, 9, 9, 9, 9 + 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, + 16 ], "y": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, - 0, 1, 2, 3, 4, 5, 6, 7, 8 + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 ], "marker": { "symbol": [ @@ -182,7 +210,17 @@ "line-ew-open", "line-ns-open", "line-ne-open", - "line-nw-open" + "line-nw-open", + "arrow-up-open", + "arrow-down-open", + "arrow-left-open", + "arrow-right-open", + "arrow-bar-up-open", + "arrow-bar-down-open", + "arrow-bar-left-open", + "arrow-bar-right-open", + "arrow-open", + "arrow-wide-open" ], "color": "blue", "size": 20, @@ -236,25 +274,39 @@ "marker symbol: line-ew-open
number: 141", "marker symbol: line-ns-open
number: 142", "marker symbol: line-ne-open
number: 143", - "marker symbol: line-nw-open
number: 144" + "marker symbol: line-nw-open
number: 144", + "marker symbol: arrow-up-open
number: 145", + "marker symbol: arrow-down-open
number: 146", + "marker symbol: arrow-left-open
number: 147", + "marker symbol: arrow-right-open
number: 148", + "marker symbol: arrow-bar-up-open
number: 149", + "marker symbol: arrow-bar-down-open
number: 150", + "marker symbol: arrow-bar-left-open
number: 151", + "marker symbol: arrow-bar-right-open
number: 152", + "marker symbol: arrow-open
number: 153", + "marker symbol: arrow-wide-open
number: 154" ], "hoverinfo": "text" }, { "mode": "markers", "x": [ - 10, 10, 10, 10, 10, 10, 10, 10, 10, - 11, 11, 11, 11, 11, 11, 11, 11, 11, - 12, 12, 12, 12, 12, 12, 12, 12, 12, - 13, 13, 13, 13, 13, 13, 13, 13, 13, - 14, 14, 14, 14, 14, 14, 14, 14, 14 + 18, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, 22, + 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, + 25 ], "y": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, - 0, 1, 2, 3, 4, 5, 6, 7, 8 + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 ], "marker": { "symbol": [ @@ -302,7 +354,17 @@ "line-ew-dot", "line-ns-dot", "line-ne-dot", - "line-nw-dot" + "line-nw-dot", + "arrow-up-dot", + "arrow-down-dot", + "arrow-left-dot", + "arrow-right-dot", + "arrow-bar-up-dot", + "arrow-bar-down-dot", + "arrow-bar-left-dot", + "arrow-bar-right-dot", + "arrow-dot", + "arrow-wide-dot" ], "color": "blue", "size": 20, @@ -356,25 +418,39 @@ "marker symbol: line-ew-dot
number: 241", "marker symbol: line-ns-dot
number: 242", "marker symbol: line-ne-dot
number: 243", - "marker symbol: line-nw-dot
number: 244" + "marker symbol: line-nw-dot
number: 244", + "marker symbol: arrow-up-dot
number: 245", + "marker symbol: arrow-down-dot
number: 246", + "marker symbol: arrow-left-dot
number: 247", + "marker symbol: arrow-right-dot
number: 248", + "marker symbol: arrow-bar-up-dot
number: 249", + "marker symbol: arrow-bar-down-dot
number: 250", + "marker symbol: arrow-bar-left-dot
number: 251", + "marker symbol: arrow-bar-right-dot
number: 252", + "marker symbol: arrow-dot
number: 253", + "marker symbol: arrow-wide-dot
number: 254" ], "hoverinfo": "text" }, { "mode": "markers", "x": [ - 15, 15, 15, 15, 15, 15, 15, 15, 15, - 16, 16, 16, 16, 16, 16, 16, 16, 16, - 17, 17, 17, 17, 17, 17, 17, 17, 17, - 18, 18, 18, 18, 18, 18, 18, 18, 18, - 19, 19, 19, 19, 19, 19, 19, 19, 19 + 27, 27, 27, 27, 27, 27, 27, 27, 27, + 28, 28, 28, 28, 28, 28, 28, 28, 28, + 29, 29, 29, 29, 29, 29, 29, 29, 29, + 30, 30, 30, 30, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, + 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, + 34 ], "y": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, - 0, 1, 2, 3, 4, 5, 6, 7, 8 + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 ], "marker": { "symbol": [ @@ -422,7 +498,17 @@ "line-ew-open-dot", "line-ns-open-dot", "line-ne-open-dot", - "line-nw-open-dot" + "line-nw-open-dot", + "arrow-up-open-dot", + "arrow-down-open-dot", + "arrow-left-open-dot", + "arrow-right-open-dot", + "arrow-bar-up-open-dot", + "arrow-bar-down-open-dot", + "arrow-bar-left-open-dot", + "arrow-bar-right-open-dot", + "arrow-open-dot", + "arrow-wide-open-dot" ], "color": "blue", "size": 20, @@ -476,7 +562,18 @@ "marker symbol: line-ew-open-dot
number: 341", "marker symbol: line-ns-open-dot
number: 342", "marker symbol: line-ne-open-dot
number: 343", - "marker symbol: line-nw-open-dot
number: 344" + "marker symbol: line-nw-open-dot
number: 344", + "marker symbol: line-nw-open-dot
number: 344", + "marker symbol: arrow-up-open-dot
number: 345", + "marker symbol: arrow-down-open-dot
number: 346", + "marker symbol: arrow-left-open-dot
number: 347", + "marker symbol: arrow-right-open-dot
number: 348", + "marker symbol: arrow-bar-up-open-dot
number: 349", + "marker symbol: arrow-bar-down-open-dot
number: 350", + "marker symbol: arrow-bar-left-open-dot
number: 351", + "marker symbol: arrow-bar-right-open-dot
number: 352", + "marker symbol: arrow-open-dot
number: 353", + "marker symbol: arrow-wide-open-dot
number: 354" ], "hoverinfo": "text" } @@ -488,7 +585,7 @@ "b": 0, "t": 0 }, - "width": 800, + "width": 1500, "height": 500, "xaxis": { "showgrid": false, diff --git a/test/image/mocks/polar_direction.json b/test/image/mocks/polar_direction.json index bf044ab7011..6c017abba78 100644 --- a/test/image/mocks/polar_direction.json +++ b/test/image/mocks/polar_direction.json @@ -21,8 +21,9 @@ }, "marker": { "color": "#8090c7", - "symbol": "square", - "size": 8 + "symbol": "arrow", + "angleref": "previous", + "size": 12 }, "subplot": "polar", "text": "sector: 0->360
rotation: -90
direction: clockwise" @@ -48,8 +49,9 @@ }, "marker": { "color": "#8090c7", - "symbol": "square", - "size": 8 + "symbol": "arrow", + "angleref": "previous", + "size": 12 }, "subplot": "polar2", "text": "sector: 0->360
rotation: 90
direction: clockwise" @@ -75,8 +77,9 @@ }, "marker": { "color": "#8090c7", - "symbol": "square", - "size": 8 + "symbol": "arrow", + "angleref": "previous", + "size": 12 }, "subplot": "polar3", "text": "sector: 45->135
rotation: 90
direction: clockwise" @@ -102,8 +105,9 @@ }, "marker": { "color": "#8090c7", - "symbol": "square", - "size": 8 + "symbol": "arrow", + "angleref": "previous", + "size": 12 }, "subplot": "polar4", "text": "sector: 135->225
rotation: 90
direction: clockwise" @@ -129,8 +133,9 @@ }, "marker": { "color": "#8090c7", - "symbol": "square", - "size": 8 + "symbol": "arrow", + "angleref": "previous", + "size": 12 }, "subplot": "polar5", "text": "sector: 135->225
rotation: 90
direction: counterclockwise" @@ -156,8 +161,9 @@ }, "marker": { "color": "#8090c7", - "symbol": "square", - "size": 8 + "symbol": "arrow", + "angleref": "previous", + "size": 12 }, "subplot": "polar6", "text": "sector: 45->135
rotation: 90
direction: counterclockwise" @@ -183,8 +189,9 @@ }, "marker": { "color": "#8090c7", - "symbol": "square", - "size": 8 + "symbol": "arrow", + "angleref": "previous", + "size": 12 }, "subplot": "polar7", "text": "sector: 0->360
rotation: 90
direction: counterclockwise" @@ -210,8 +217,9 @@ }, "marker": { "color": "#8090c7", - "symbol": "square", - "size": 8 + "symbol": "arrow", + "angleref": "previous", + "size": 12 }, "subplot": "polar8", "text": "sector: 0->360
rotation: 0
direction: clockwise" @@ -237,8 +245,9 @@ }, "marker": { "color": "#8090c7", - "symbol": "square", - "size": 8 + "symbol": "arrow", + "angleref": "previous", + "size": 12 }, "subplot": "polar9", "text": "sector: 0->360
rotation: 0
direction: counterclockwise" @@ -271,12 +280,12 @@ }, "radialaxis": { "tickfont": { - "size": 8 + "size": 12 } }, "angularaxis": { "tickfont": { - "size": 8 + "size": 12 }, "rotation": -90, "direction": "clockwise" @@ -299,12 +308,12 @@ }, "radialaxis": { "tickfont": { - "size": 8 + "size": 12 } }, "angularaxis": { "tickfont": { - "size": 8 + "size": 12 }, "rotation": 90, "direction": "clockwise" @@ -327,12 +336,12 @@ }, "radialaxis": { "tickfont": { - "size": 8 + "size": 12 } }, "angularaxis": { "tickfont": { - "size": 8 + "size": 12 }, "rotation": 90, "direction": "clockwise" @@ -355,12 +364,12 @@ }, "radialaxis": { "tickfont": { - "size": 8 + "size": 12 } }, "angularaxis": { "tickfont": { - "size": 8 + "size": 12 }, "rotation": 90, "direction": "clockwise" @@ -383,12 +392,12 @@ }, "radialaxis": { "tickfont": { - "size": 8 + "size": 12 } }, "angularaxis": { "tickfont": { - "size": 8 + "size": 12 }, "rotation": 90, "direction": "counterclockwise" @@ -411,12 +420,12 @@ }, "radialaxis": { "tickfont": { - "size": 8 + "size": 12 } }, "angularaxis": { "tickfont": { - "size": 8 + "size": 12 }, "rotation": 90, "direction": "counterclockwise" @@ -439,12 +448,12 @@ }, "radialaxis": { "tickfont": { - "size": 8 + "size": 12 } }, "angularaxis": { "tickfont": { - "size": 8 + "size": 12 }, "rotation": 90, "direction": "counterclockwise" @@ -467,12 +476,12 @@ }, "radialaxis": { "tickfont": { - "size": 8 + "size": 12 } }, "angularaxis": { "tickfont": { - "size": 8 + "size": 12 }, "rotation": 0, "direction": "clockwise" @@ -495,12 +504,12 @@ }, "radialaxis": { "tickfont": { - "size": 8 + "size": 12 } }, "angularaxis": { "tickfont": { - "size": 8 + "size": 12 }, "rotation": 0, "direction": "counterclockwise" diff --git a/test/image/mocks/scattercarpet-on-two-carpets.json b/test/image/mocks/scattercarpet-on-two-carpets.json index dbac1ca27b8..2e52c1d5e90 100644 --- a/test/image/mocks/scattercarpet-on-two-carpets.json +++ b/test/image/mocks/scattercarpet-on-two-carpets.json @@ -47,7 +47,12 @@ "smoothing": 1, "color": "blue" }, - "mode": "lines", + "mode": "lines+markers", + "marker": { + "size": 15, + "symbol": "arrow", + "angleref": "previous" + }, "carpet": "c0" }, { @@ -59,7 +64,13 @@ "smoothing": 1, "color": "red" }, - "mode": "lines", + "mode": "lines+markers", + "marker": { + "size": 15, + "symbol": "arrow", + "angle": 90, + "angleref": "previous" + }, "carpet": "c1" } ] diff --git a/test/image/mocks/smith_gaps.json b/test/image/mocks/smith_gaps.json index f1c64de8cd1..11b5136f919 100644 --- a/test/image/mocks/smith_gaps.json +++ b/test/image/mocks/smith_gaps.json @@ -10,6 +10,7 @@ 0, 0.2, 0.4, null, 0.8, 1, null, 2, 3, null, 5 ], "mode": "markers+lines", + "marker": { "size": 10, "symbol": "arrow-wide", "angleref": "previous" }, "line": { "shape": "spline" } }, { @@ -23,6 +24,7 @@ 0, 0.2, 0.4, null, 0.8, 1, null, 2, 3, null, 5 ], "mode": "markers+lines", + "marker": { "size": 20, "symbol": "arrow", "angleref": "previous", "angle": -90 }, "line": { "shape": "spline" }, "subplot": "smith2" } diff --git a/test/image/mocks/splom_log.json b/test/image/mocks/splom_log.json index 8ec446f0c26..75618cd4fe2 100644 --- a/test/image/mocks/splom_log.json +++ b/test/image/mocks/splom_log.json @@ -9,6 +9,8 @@ "label": "B" }], "marker": { + "symbol": "arrow", + "angle": 180, "size": 20, "line": {"width": 2, "color": "#444"}, "color": [10, 40, 100], diff --git a/test/image/mocks/ternary_markers.json b/test/image/mocks/ternary_markers.json index 6283daa5d29..c7cd6f851a9 100644 --- a/test/image/mocks/ternary_markers.json +++ b/test/image/mocks/ternary_markers.json @@ -72,9 +72,10 @@ "color": "#DB7365" }, "marker": { - "symbol": 100, + "symbol": "arrow", + "angleref": "previous", "color": "#DB7365", - "size": 14, + "size": 10, "line": { "width": 2 } diff --git a/test/image/mocks/violin_ridgeplot.json b/test/image/mocks/violin_ridgeplot.json index 1cccf678e1b..988fe62eb70 100644 --- a/test/image/mocks/violin_ridgeplot.json +++ b/test/image/mocks/violin_ridgeplot.json @@ -11,7 +11,7 @@ "hovertemplate": "day=%{y}
total_bill=%{x}", "legendgroup": "day=Fri", "marker": { - "color": "#636efa" + "color": "#636efa", "symbol": "arrow", "angle": 180, "size": 20 }, "name": "day=Fri", "offsetgroup": "day=Fri", @@ -80,7 +80,7 @@ "hovertemplate": "day=%{y}
total_bill=%{x}", "legendgroup": "day=Sat", "marker": { - "color": "#EF553B" + "color": "#EF553B", "symbol": "arrow", "angle": 45, "size": 20 }, "name": "day=Sat", "offsetgroup": "day=Sat", @@ -285,7 +285,7 @@ "hovertemplate": "day=%{y}
total_bill=%{x}", "legendgroup": "day=Sun", "marker": { - "color": "#00cc96" + "color": "#00cc96", "symbol": "arrow", "angle": 0, "size": 20 }, "name": "day=Sun", "offsetgroup": "day=Sun", @@ -468,7 +468,7 @@ "hovertemplate": "day=%{y}
total_bill=%{x}", "legendgroup": "day=Thur", "marker": { - "color": "#ab63fa" + "color": "#ab63fa", "symbol": "arrow", "angle": -45, "size": 20 }, "name": "day=Thur", "offsetgroup": "day=Thur", diff --git a/test/image/mocks/z-gl2d_marker_symbols-angle.json b/test/image/mocks/z-gl2d_marker_symbols-angle.json new file mode 100644 index 00000000000..2b24b4d1eb7 --- /dev/null +++ b/test/image/mocks/z-gl2d_marker_symbols-angle.json @@ -0,0 +1,607 @@ +{ + "data": [ + { + "type": "scattergl", + "mode": "markers", + "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, + 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, + 7 + ], + "y": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 + ], + "marker": { "angle": 15, + "symbol": [ + "circle", + "square", + "diamond", + "cross", + "x", + "triangle-up", + "triangle-down", + "triangle-left", + "triangle-right", + "triangle-ne", + "triangle-se", + "triangle-sw", + "triangle-nw", + "pentagon", + "hexagon", + "hexagon2", + "octagon", + "star", + "hexagram", + "star-triangle-up", + "star-triangle-down", + "star-square", + "star-diamond", + "diamond-tall", + "diamond-wide", + "hourglass", + "bowtie", + "circle-cross", + "circle-x", + "square-cross", + "square-x", + "diamond-cross", + "diamond-x", + "cross-thin", + "x-thin", + "asterisk", + "hash", + "y-up", + "y-down", + "y-left", + "y-right", + "line-ew", + "line-ns", + "line-ne", + "line-nw", + "arrow-up", + "arrow-down", + "arrow-left", + "arrow-right", + "arrow-bar-up", + "arrow-bar-down", + "arrow-bar-left", + "arrow-bar-right", + "arrow", + "arrow-wide" + ], + "color": "blue", + "size": 20, + "line": { + "color": "orange", + "width": 1.5 + } + }, + "text": [ + "marker symbol: circle
number: 0", + "marker symbol: square
number: 1", + "marker symbol: diamond
number: 2", + "marker symbol: cross
number: 3", + "marker symbol: x
number: 4", + "marker symbol: triangle-up
number: 5", + "marker symbol: triangle-down
number: 6", + "marker symbol: triangle-left
number: 7", + "marker symbol: triangle-right
number: 8", + "marker symbol: triangle-ne
number: 9", + "marker symbol: triangle-se
number: 10", + "marker symbol: triangle-sw
number: 11", + "marker symbol: triangle-nw
number: 12", + "marker symbol: pentagon
number: 13", + "marker symbol: hexagon
number: 14", + "marker symbol: hexagon2
number: 15", + "marker symbol: octagon
number: 16", + "marker symbol: star
number: 17", + "marker symbol: hexagram
number: 18", + "marker symbol: star-triangle-up
number: 19", + "marker symbol: star-triangle-down
number: 20", + "marker symbol: star-square
number: 21", + "marker symbol: star-diamond
number: 22", + "marker symbol: diamond-tall
number: 23", + "marker symbol: diamond-wide
number: 24", + "marker symbol: hourglass
number: 25", + "marker symbol: bowtie
number: 26", + "marker symbol: circle-cross
number: 27", + "marker symbol: circle-x
number: 28", + "marker symbol: square-cross
number: 29", + "marker symbol: square-x
number: 30", + "marker symbol: diamond-cross
number: 31", + "marker symbol: diamond-x
number: 32", + "marker symbol: cross-thin
number: 33", + "marker symbol: x-thin
number: 34", + "marker symbol: asterisk
number: 35", + "marker symbol: hash
number: 36", + "marker symbol: y-up
number: 37", + "marker symbol: y-down
number: 38", + "marker symbol: y-left
number: 39", + "marker symbol: y-right
number: 40", + "marker symbol: line-ew
number: 41", + "marker symbol: line-ns
number: 42", + "marker symbol: line-ne
number: 43", + "marker symbol: line-nw
number: 44", + "marker symbol: arrow-up
number: 45", + "marker symbol: arrow-down
number: 46", + "marker symbol: arrow-left
number: 47", + "marker symbol: arrow-right
number: 48", + "marker symbol: arrow-bar-up
number: 49", + "marker symbol: arrow-bar-down
number: 50", + "marker symbol: arrow-bar-left
number: 51", + "marker symbol: arrow-bar-right
number: 52", + "marker symbol: arrow
number: 53", + "marker symbol: arrow-wide
number: 54" + ], + "hoverinfo": "text" + }, + { + "type": "scattergl", + "mode": "markers", + "x": [ + 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, + 16 + ], + "y": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 + ], + "marker": { "angle": 15, + "symbol": [ + "circle-open", + "square-open", + "diamond-open", + "cross-open", + "x-open", + "triangle-up-open", + "triangle-down-open", + "triangle-left-open", + "triangle-right-open", + "triangle-ne-open", + "triangle-se-open", + "triangle-sw-open", + "triangle-nw-open", + "pentagon-open", + "hexagon-open", + "hexagon2-open", + "octagon-open", + "star-open", + "hexagram-open", + "star-triangle-up-open", + "star-triangle-down-open", + "star-square-open", + "star-diamond-open", + "diamond-tall-open", + "diamond-wide-open", + "hourglass-open", + "bowtie-open", + "circle-cross-open", + "circle-x-open", + "square-cross-open", + "square-x-open", + "diamond-cross-open", + "diamond-x-open", + "cross-thin-open", + "x-thin-open", + "asterisk-open", + "hash-open", + "y-up-open", + "y-down-open", + "y-left-open", + "y-right-open", + "line-ew-open", + "line-ns-open", + "line-ne-open", + "line-nw-open", + "arrow-up-open", + "arrow-down-open", + "arrow-left-open", + "arrow-right-open", + "arrow-bar-up-open", + "arrow-bar-down-open", + "arrow-bar-left-open", + "arrow-bar-right-open", + "arrow-open", + "arrow-wide-open" + ], + "color": "blue", + "size": 20, + "line": { + "color": "orange", + "width": 1.5 + } + }, + "text": [ + "marker symbol: circle-open
number: 100", + "marker symbol: square-open
number: 101", + "marker symbol: diamond-open
number: 102", + "marker symbol: cross-open
number: 103", + "marker symbol: x-open
number: 104", + "marker symbol: triangle-up-open
number: 105", + "marker symbol: triangle-down-open
number: 106", + "marker symbol: triangle-left-open
number: 107", + "marker symbol: triangle-right-open
number: 108", + "marker symbol: triangle-ne-open
number: 109", + "marker symbol: triangle-se-open
number: 110", + "marker symbol: triangle-sw-open
number: 111", + "marker symbol: triangle-nw-open
number: 112", + "marker symbol: pentagon-open
number: 113", + "marker symbol: hexagon-open
number: 114", + "marker symbol: hexagon2-open
number: 115", + "marker symbol: octagon-open
number: 116", + "marker symbol: star-open
number: 117", + "marker symbol: hexagram-open
number: 118", + "marker symbol: star-triangle-up-open
number: 119", + "marker symbol: star-triangle-down-open
number: 120", + "marker symbol: star-square-open
number: 121", + "marker symbol: star-diamond-open
number: 122", + "marker symbol: diamond-tall-open
number: 123", + "marker symbol: diamond-wide-open
number: 124", + "marker symbol: hourglass-open
number: 125", + "marker symbol: bowtie-open
number: 126", + "marker symbol: circle-cross-open
number: 127", + "marker symbol: circle-x-open
number: 128", + "marker symbol: square-cross-open
number: 129", + "marker symbol: square-x-open
number: 130", + "marker symbol: diamond-cross-open
number: 131", + "marker symbol: diamond-x-open
number: 132", + "marker symbol: cross-thin-open
number: 133", + "marker symbol: x-thin-open
number: 134", + "marker symbol: asterisk-open
number: 135", + "marker symbol: hash-open
number: 136", + "marker symbol: y-up-open
number: 137", + "marker symbol: y-down-open
number: 138", + "marker symbol: y-left-open
number: 139", + "marker symbol: y-right-open
number: 140", + "marker symbol: line-ew-open
number: 141", + "marker symbol: line-ns-open
number: 142", + "marker symbol: line-ne-open
number: 143", + "marker symbol: line-nw-open
number: 144", + "marker symbol: arrow-up-open
number: 145", + "marker symbol: arrow-down-open
number: 146", + "marker symbol: arrow-left-open
number: 147", + "marker symbol: arrow-right-open
number: 148", + "marker symbol: arrow-bar-up-open
number: 149", + "marker symbol: arrow-bar-down-open
number: 150", + "marker symbol: arrow-bar-left-open
number: 151", + "marker symbol: arrow-bar-right-open
number: 152", + "marker symbol: arrow-open
number: 153", + "marker symbol: arrow-wide-open
number: 154" + ], + "hoverinfo": "text" + }, + { + "type": "scattergl", + "mode": "markers", + "x": [ + 18, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, 22, + 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, + 25 + ], + "y": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 + ], + "marker": { "angle": 15, + "symbol": [ + "circle-dot", + "square-dot", + "diamond-dot", + "cross-dot", + "x-dot", + "triangle-up-dot", + "triangle-down-dot", + "triangle-left-dot", + "triangle-right-dot", + "triangle-ne-dot", + "triangle-se-dot", + "triangle-sw-dot", + "triangle-nw-dot", + "pentagon-dot", + "hexagon-dot", + "hexagon2-dot", + "octagon-dot", + "star-dot", + "hexagram-dot", + "star-triangle-up-dot", + "star-triangle-down-dot", + "star-square-dot", + "star-diamond-dot", + "diamond-tall-dot", + "diamond-wide-dot", + "hourglass-dot", + "bowtie-dot", + "circle-cross-dot", + "circle-x-dot", + "square-cross-dot", + "square-x-dot", + "diamond-cross-dot", + "diamond-x-dot", + "cross-thin-dot", + "x-thin-dot", + "asterisk-dot", + "hash-dot", + "y-up-dot", + "y-down-dot", + "y-left-dot", + "y-right-dot", + "line-ew-dot", + "line-ns-dot", + "line-ne-dot", + "line-nw-dot", + "arrow-up-dot", + "arrow-down-dot", + "arrow-left-dot", + "arrow-right-dot", + "arrow-bar-up-dot", + "arrow-bar-down-dot", + "arrow-bar-left-dot", + "arrow-bar-right-dot", + "arrow-dot", + "arrow-wide-dot" + ], + "color": "blue", + "size": 20, + "line": { + "color": "orange", + "width": 1.5 + } + }, + "text": [ + "marker symbol: circle-dot
number: 200", + "marker symbol: square-dot
number: 201", + "marker symbol: diamond-dot
number: 202", + "marker symbol: cross-dot
number: 203", + "marker symbol: x-dot
number: 204", + "marker symbol: triangle-up-dot
number: 205", + "marker symbol: triangle-down-dot
number: 206", + "marker symbol: triangle-left-dot
number: 207", + "marker symbol: triangle-right-dot
number: 208", + "marker symbol: triangle-ne-dot
number: 209", + "marker symbol: triangle-se-dot
number: 210", + "marker symbol: triangle-sw-dot
number: 211", + "marker symbol: triangle-nw-dot
number: 212", + "marker symbol: pentagon-dot
number: 213", + "marker symbol: hexagon-dot
number: 214", + "marker symbol: hexagon2-dot
number: 215", + "marker symbol: octagon-dot
number: 216", + "marker symbol: star-dot
number: 217", + "marker symbol: hexagram-dot
number: 218", + "marker symbol: star-triangle-up-dot
number: 219", + "marker symbol: star-triangle-down-dot
number: 220", + "marker symbol: star-square-dot
number: 221", + "marker symbol: star-diamond-dot
number: 222", + "marker symbol: diamond-tall-dot
number: 223", + "marker symbol: diamond-wide-dot
number: 224", + "marker symbol: hourglass-dot
number: 225", + "marker symbol: bowtie-dot
number: 226", + "marker symbol: circle-cross-dot
number: 227", + "marker symbol: circle-x-dot
number: 228", + "marker symbol: square-cross-dot
number: 229", + "marker symbol: square-x-dot
number: 230", + "marker symbol: diamond-cross-dot
number: 231", + "marker symbol: diamond-x-dot
number: 232", + "marker symbol: cross-thin-dot
number: 233", + "marker symbol: x-thin-dot
number: 234", + "marker symbol: asterisk-dot
number: 235", + "marker symbol: hash-dot
number: 236", + "marker symbol: y-up-dot
number: 237", + "marker symbol: y-down-dot
number: 238", + "marker symbol: y-left-dot
number: 239", + "marker symbol: y-right-dot
number: 240", + "marker symbol: line-ew-dot
number: 241", + "marker symbol: line-ns-dot
number: 242", + "marker symbol: line-ne-dot
number: 243", + "marker symbol: line-nw-dot
number: 244", + "marker symbol: arrow-up-dot
number: 245", + "marker symbol: arrow-down-dot
number: 246", + "marker symbol: arrow-left-dot
number: 247", + "marker symbol: arrow-right-dot
number: 248", + "marker symbol: arrow-bar-up-dot
number: 249", + "marker symbol: arrow-bar-down-dot
number: 250", + "marker symbol: arrow-bar-left-dot
number: 251", + "marker symbol: arrow-bar-right-dot
number: 252", + "marker symbol: arrow-dot
number: 253", + "marker symbol: arrow-wide-dot
number: 254" + ], + "hoverinfo": "text" + }, + { + "type": "scattergl", + "mode": "markers", + "x": [ + 27, 27, 27, 27, 27, 27, 27, 27, 27, + 28, 28, 28, 28, 28, 28, 28, 28, 28, + 29, 29, 29, 29, 29, 29, 29, 29, 29, + 30, 30, 30, 30, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, + 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, + 34 + ], + "y": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 + ], + "marker": { "angle": 15, + "symbol": [ + "circle-open-dot", + "square-open-dot", + "diamond-open-dot", + "cross-open-dot", + "x-open-dot", + "triangle-up-open-dot", + "triangle-down-open-dot", + "triangle-left-open-dot", + "triangle-right-open-dot", + "triangle-ne-open-dot", + "triangle-se-open-dot", + "triangle-sw-open-dot", + "triangle-nw-open-dot", + "pentagon-open-dot", + "hexagon-open-dot", + "hexagon2-open-dot", + "octagon-open-dot", + "star-open-dot", + "hexagram-open-dot", + "star-triangle-up-open-dot", + "star-triangle-down-open-dot", + "star-square-open-dot", + "star-diamond-open-dot", + "diamond-tall-open-dot", + "diamond-wide-open-dot", + "hourglass-open-dot", + "bowtie-open-dot", + "circle-cross-open-dot", + "circle-x-open-dot", + "square-cross-open-dot", + "square-x-open-dot", + "diamond-cross-open-dot", + "diamond-x-open-dot", + "cross-thin-open-dot", + "x-thin-open-dot", + "asterisk-open-dot", + "hash-open-dot", + "y-up-open-dot", + "y-down-open-dot", + "y-left-open-dot", + "y-right-open-dot", + "line-ew-open-dot", + "line-ns-open-dot", + "line-ne-open-dot", + "line-nw-open-dot", + "arrow-up-open-dot", + "arrow-down-open-dot", + "arrow-left-open-dot", + "arrow-right-open-dot", + "arrow-bar-up-open-dot", + "arrow-bar-down-open-dot", + "arrow-bar-left-open-dot", + "arrow-bar-right-open-dot", + "arrow-open-dot", + "arrow-wide-open-dot" + ], + "color": "blue", + "size": 20, + "line": { + "color": "orange", + "width": 1.5 + } + }, + "text": [ + "marker symbol: circle-open-dot
number: 300", + "marker symbol: square-open-dot
number: 301", + "marker symbol: diamond-open-dot
number: 302", + "marker symbol: cross-open-dot
number: 303", + "marker symbol: x-open-dot
number: 304", + "marker symbol: triangle-up-open-dot
number: 305", + "marker symbol: triangle-down-open-dot
number: 306", + "marker symbol: triangle-left-open-dot
number: 307", + "marker symbol: triangle-right-open-dot
number: 308", + "marker symbol: triangle-ne-open-dot
number: 309", + "marker symbol: triangle-se-open-dot
number: 310", + "marker symbol: triangle-sw-open-dot
number: 311", + "marker symbol: triangle-nw-open-dot
number: 312", + "marker symbol: pentagon-open-dot
number: 313", + "marker symbol: hexagon-open-dot
number: 314", + "marker symbol: hexagon2-open-dot
number: 315", + "marker symbol: octagon-open-dot
number: 316", + "marker symbol: star-open-dot
number: 317", + "marker symbol: hexagram-open-dot
number: 318", + "marker symbol: star-triangle-up-open-dot
number: 319", + "marker symbol: star-triangle-down-open-dot
number: 320", + "marker symbol: star-square-open-dot
number: 321", + "marker symbol: star-diamond-open-dot
number: 322", + "marker symbol: diamond-tall-open-dot
number: 323", + "marker symbol: diamond-wide-open-dot
number: 324", + "marker symbol: hourglass-open-dot
number: 325", + "marker symbol: bowtie-open-dot
number: 326", + "marker symbol: circle-cross-open-dot
number: 327", + "marker symbol: circle-x-open-dot
number: 328", + "marker symbol: square-cross-open-dot
number: 329", + "marker symbol: square-x-open-dot
number: 330", + "marker symbol: diamond-cross-open-dot
number: 331", + "marker symbol: diamond-x-open-dot
number: 332", + "marker symbol: cross-thin-open-dot
number: 333", + "marker symbol: x-thin-open-dot
number: 334", + "marker symbol: asterisk-open-dot
number: 335", + "marker symbol: hash-open-dot
number: 336", + "marker symbol: y-up-open-dot
number: 337", + "marker symbol: y-down-open-dot
number: 338", + "marker symbol: y-left-open-dot
number: 339", + "marker symbol: y-right-open-dot
number: 340", + "marker symbol: line-ew-open-dot
number: 341", + "marker symbol: line-ns-open-dot
number: 342", + "marker symbol: line-ne-open-dot
number: 343", + "marker symbol: line-nw-open-dot
number: 344", + "marker symbol: line-nw-open-dot
number: 344", + "marker symbol: arrow-up-open-dot
number: 345", + "marker symbol: arrow-down-open-dot
number: 346", + "marker symbol: arrow-left-open-dot
number: 347", + "marker symbol: arrow-right-open-dot
number: 348", + "marker symbol: arrow-bar-up-open-dot
number: 349", + "marker symbol: arrow-bar-down-open-dot
number: 350", + "marker symbol: arrow-bar-left-open-dot
number: 351", + "marker symbol: arrow-bar-right-open-dot
number: 352", + "marker symbol: arrow-open-dot
number: 353", + "marker symbol: arrow-wide-open-dot
number: 354" + ], + "hoverinfo": "text" + } + ], + "layout": { + "margin": { + "l": 0, + "r": 0, + "b": 0, + "t": 0 + }, + "width": 1500, + "height": 500, + "xaxis": { + "showgrid": false, + "zeroline": false + }, + "yaxis": { + "showgrid": false, + "zeroline": false, + "autorange": "reversed" + }, + "showlegend": false, + "plot_bgcolor": "#d3d3d3", + "hovermode": "closest" + } +} diff --git a/test/image/mocks/z-line-shape-arrow.json b/test/image/mocks/z-line-shape-arrow.json new file mode 100644 index 00000000000..d4951d6b0f8 --- /dev/null +++ b/test/image/mocks/z-line-shape-arrow.json @@ -0,0 +1,176 @@ +{ + "data": [ + { + "line": { + "shape": "linear" + }, + "mode": "lines+markers", + "name": "'linear'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 10 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 1, + 3, + 2, + 3, + 1 + ] + }, + { + "line": { + "shape": "spline" + }, + "mode": "lines+markers", + "name": "'spline'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 10 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 6, + 8, + 7, + 8, + 6 + ] + }, + { + "line": { + "shape": "vhv" + }, + "mode": "lines+markers", + "name": "'vhv'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 10 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 11, + 13, + 12, + 13, + 11 + ] + }, + { + "line": { + "shape": "hvh" + }, + "mode": "lines+markers", + "name": "'hvh'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 10 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 16, + 18, + 17, + 18, + 16 + ] + }, + { + "line": { + "shape": "vh" + }, + "mode": "lines+markers", + "name": "'vh'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 10 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 21, + 23, + 22, + 23, + 21 + ] + }, + { + "line": { + "shape": "hv" + }, + "mode": "lines+markers", + "name": "'hv'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 10 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 26, + 28, + 27, + 28, + 26 + ] + } + ], + "layout": { + "title": { + "text" : "line shape and arrows" + }, + "legend": { + "orientation": "h" + }, + "height": 700, + "width": 700 + } +} diff --git a/test/image/mocks/z-line-shape-arrow_backoff-auto.json b/test/image/mocks/z-line-shape-arrow_backoff-auto.json new file mode 100644 index 00000000000..977cd481758 --- /dev/null +++ b/test/image/mocks/z-line-shape-arrow_backoff-auto.json @@ -0,0 +1,242 @@ +{ + "data": [ + { + "line": { + "width": 5, + "shape": "linear" + }, + "mode": "lines+markers", + "name": "'linear'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "x": [ + 1, + 2, + null, + 2, + 3, + null, + 3, + 4, + null, + 4, + 5 + ], + "y": [ + 1, + 3, + null, + 3, + 2, + null, + 2, + 3, + null, + 3, + 1 + ] + }, + { + "line": { + "width": 5, + "shape": "spline" + }, + "mode": "lines+markers", + "name": "'spline'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 6, + 8, + 7, + 8, + 6 + ] + }, + { + "line": { + "width": 5, + "shape": "vhv" + }, + "mode": "lines+markers", + "name": "'vhv'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "x": [ + 1, + 2, + null, + 2, + 3, + null, + 3, + 4, + null, + 4, + 5 + ], + "y": [ + 11, + 13, + null, + 13, + 12, + null, + 12, + 13, + null, + 13, + 11 + ] + }, + { + "line": { + "width": 5, + "shape": "hvh" + }, + "mode": "lines+markers", + "name": "'hvh'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "x": [ + 1, + 2, + null, + 2, + 3, + null, + 3, + 4, + null, + 4, + 5 + ], + "y": [ + 16, + 18, + null, + 18, + 17, + null, + 17, + 18, + null, + 18, + 16 + ] + }, + { + "line": { + "width": 5, + "shape": "vh" + }, + "mode": "lines+markers", + "name": "'vh'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "x": [ + 1, + 2, + null, + 2, + 3, + null, + 3, + 4, + null, + 4, + 5 + ], + "y": [ + 21, + 23, + null, + 23, + 22, + null, + 22, + 23, + null, + 23, + 21 + ] + }, + { + "line": { + "width": 5, + "shape": "hv" + }, + "mode": "lines+markers", + "name": "'hv'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "x": [ + 1, + 2, + null, + 2, + 3, + null, + 3, + 4, + null, + 4, + 5 + ], + "y": [ + 26, + 28, + null, + 28, + 27, + null, + 27, + 28, + null, + 28, + 26 + ] + } + ], + "layout": { + "title": { + "text" : "line shape and arrows with auto backoff" + }, + "legend": { + "orientation": "h" + }, + "height": 700, + "width": 700 + } +} diff --git a/test/image/mocks/z-line-shape-arrow_backoff.json b/test/image/mocks/z-line-shape-arrow_backoff.json new file mode 100644 index 00000000000..b10bd07187a --- /dev/null +++ b/test/image/mocks/z-line-shape-arrow_backoff.json @@ -0,0 +1,188 @@ +{ + "data": [ + { + "line": { + "backoff": 10, + "width": 5, + "shape": "linear" + }, + "mode": "lines+markers", + "name": "'linear'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 1, + 3, + 2, + 3, + 1 + ] + }, + { + "line": { + "backoff": 10, + "width": 5, + "shape": "spline" + }, + "mode": "lines+markers", + "name": "'spline'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 6, + 8, + 7, + 8, + 6 + ] + }, + { + "line": { + "backoff": 10, + "width": 5, + "shape": "vhv" + }, + "mode": "lines+markers", + "name": "'vhv'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 11, + 13, + 12, + 13, + 11 + ] + }, + { + "line": { + "backoff": 10, + "width": 5, + "shape": "hvh" + }, + "mode": "lines+markers", + "name": "'hvh'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 16, + 18, + 17, + 18, + 16 + ] + }, + { + "line": { + "backoff": 10, + "width": 5, + "shape": "vh" + }, + "mode": "lines+markers", + "name": "'vh'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 21, + 23, + 22, + 23, + 21 + ] + }, + { + "line": { + "backoff": 10, + "width": 5, + "shape": "hv" + }, + "mode": "lines+markers", + "name": "'hv'", + "type": "scatter", + "marker": { + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 26, + 28, + 27, + 28, + 26 + ] + } + ], + "layout": { + "title": { + "text" : "line shape and arrows with backoff" + }, + "legend": { + "orientation": "h" + }, + "height": 700, + "width": 700 + } +} diff --git a/test/image/mocks/z-marker-standoff.json b/test/image/mocks/z-marker-standoff.json new file mode 100644 index 00000000000..cd4925f7c9e --- /dev/null +++ b/test/image/mocks/z-marker-standoff.json @@ -0,0 +1,403 @@ +{ + "data": [ + { + "mode": "markers+lines", + "line": { + "width": 5, + "backoff": [30, 20] + }, + "marker": { + "standoff": [5, 20, null, 20, 10], + "angleref": "previous", + "symbol": "arrow", + "size": 20 + }, + "x": [ + 1, + 2, + null, + 2, + 3 + ], + "y": [ + 3, + 1, + null, + 1, + 2 + ] + }, + { + "mode": "markers+text", + "marker": { + "opacity": 1, + "size": [10, 40, 20] + }, + "text": [10, 40, 20], + "textposition": ["right", "center", "top"], + "x": [ + 1, + 2, + 3 + ], + "y": [ + 3, + 1, + 2 + ] + }, + { + "type": "scattergeo", + "mode": "markers+lines", + "line": { "width": 5 }, + "marker": { + "standoff": [5, 20, 10, 2.5], + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "lon": [ + -30, + 0, + 30, + 15 + ], + "lat": [ + 20, + -5, + 10, + -20 + ] + }, + { + "type": "scattergeo", + "mode": "markers+text", + "marker": { + "opacity": 1, + "size": [10, 40, 20, 5] + }, + "text": [10, 40, 20, 5], + "textposition": ["right", "center", "top", "bottom"], + "lon": [ + -30, + 0, + 30, + 15 + ], + "lat": [ + 20, + -5, + 10, + -20 + ] + }, + { + "type": "scatterternary", + "mode": "markers+lines", + "line": { + "width": 5, + "backoff": [30, 20] + }, + "marker": { + "standoff": [5, 20, null, 20, 10], + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "a": [ + 2, + 1, + null, + 1, + 1 + ], + "b": [ + 1, + 2, + null, + 2, + 1 + ], + "c": [ + 1, + 1, + null, + 1, + 2 + ] + }, + { + "type": "scatterternary", + "mode": "markers+text", + "marker": { + "opacity": 1, + "size": [10, 40, 20] + }, + "text": [10, 40, 20], + "textposition": ["right", "center", "top"], + "a": [ + 2, + 1, + 1 + ], + "b": [ + 1, + 2, + 1 + ], + "c": [ + 1, + 1, + 2 + ] + }, + { + "xaxis": "x2", + "yaxis": "y2", + "type": "carpet", + "a": [ 4, 4, 4, 4.5, 4.5, 4.5, 5, 5, 5, 6, 6, 6 ], + "b": [ 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3 ], + "y": [ 2, 3.5, 4, 3, 4.5, 5, 5.5, 6.5, 7.5, 8, 8.5, 10 ], + "aaxis": { + "tickprefix": "a = ", + "ticksuffix": "m", + "smoothing": 1, + "minorgridcount": 9 + }, + "baxis": { + "tickprefix": "b = ", + "ticksuffix": "Pa", + "smoothing": 1, + "minorgridcount": 9 + }, + "carpet": "c0" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "type": "scattercarpet", + "mode": "markers+lines", + "line": { + "width": 5, + "backoff": [30, 20] + }, + "marker": { + "standoff": [5, 20, null, 20, 10], + "angleref": "previous", + "symbol": "arrow", + "size": 20 + }, + "a": [ 4, 5, null, 5, 6 ], + "b": [ 2.5, 2.5, null, 2.5, 2.5 ], + "carpet": "c0" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "type": "scattercarpet", + "mode": "markers+text", + "marker": { + "opacity": 1, + "size": [10, 40, 20] + }, + "text": [10, 40, 20], + "textposition": ["right", "center", "top"], + "a": [ 4, 5, 6 ], + "b": [ 2.5, 2.5, 2.5 ], + "carpet": "c0" + }, + { + "subplot": "polar", + "type": "scatterpolar", + "mode": "markers+lines", + "line": { + "width": 5, + "backoff": [30, 20] + }, + "marker": { + "standoff": [5, 20, null, 20, 10], + "angleref": "previous", + "symbol": "arrow", + "size": 20 + }, + "r": [ + 1, + 2, + null, + 2, + 3 + ], + "theta": [ + 270, + 90, + null, + 90, + 180 + ] + }, + { + "subplot": "polar", + "type": "scatterpolar", + "mode": "markers+text", + "marker": { + "opacity": 1, + "size": [10, 40, 20] + }, + "text": [10, 40, 20], + "textposition": ["right", "center", "top"], + "r": [ + 1, + 2, + 3 + ], + "theta": [ + 270, + 90, + 180 + ] + }, + { + "subplot": "smith", + "type": "scattersmith", + "mode": "markers+lines", + "line": { + "width": 5, + "backoff": [30, 20] + }, + "marker": { + "standoff": [5, 20, null, 20, 10], + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "real": [ + 1, + 2, + null, + 2, + 0.5 + ], + "imag": [ + -0.5, + 1, + null, + 1, + 1 + ] + }, + { + "subplot": "smith", + "type": "scattersmith", + "mode": "markers+text", + "marker": { + "opacity": 1, + "size": [10, 40, 20] + }, + "text": [10, 40, 20], + "textposition": ["right", "center", "top"], + "real": [ + 1, + 2, + 0.5 + ], + "imag": [ + -0.5, + 1, + 1 + ] + } + ], + "layout": { + "xaxis": { + "domain": [ + 0, + 0.3 + ] + }, + "yaxis": { + "domain": [ + 0, + 0.45 + ] + }, + "xaxis2": { + "anchor": "y2", + "domain": [ + 0.35, + 0.65 + ] + }, + "yaxis2": { + "anchor": "x2", + "domain": [ + 0.55, + 1 + ] + }, + "geo": { + "projection": { + "type": "satellite" + }, + "domain": { + "x": [ + 0.35, + 0.65 + ], + "y": [ + 0, + 0.45 + ] + } + }, + "ternary": { + "domain": { + "x": [ + 0, + 0.3 + ], + "y": [ + 0.55, + 1 + ] + }, + "aaxis": { + "title": {"text": "A"} + }, + "baxis": { + "title": {"text": "B"} + }, + "caxis": { + "title": {"text": "C"} + } + }, + "polar": { + "domain": { + "x": [ + 0.7, + 1 + ], + "y": [ + 0, + 0.45 + ] + } + }, + "smith": { + "domain": { + "x": [ + 0.7, + 1 + ], + "y": [ + 0.55, + 1 + ] + } + }, + "height": 900, + "width": 1200, + "legend": { + "orientation": "h" + } + } +} diff --git a/test/image/mocks/z-marker-standoff_auto-backoff.json b/test/image/mocks/z-marker-standoff_auto-backoff.json new file mode 100644 index 00000000000..fad2bed639b --- /dev/null +++ b/test/image/mocks/z-marker-standoff_auto-backoff.json @@ -0,0 +1,402 @@ +{ + "data": [ + { + "mode": "markers+lines", + "line": { + "width": 5 + }, + "marker": { + "line": { + "width": 2, + "color": "darkblue" + }, + "standoff": [5, 20, null, 20, 10], + "angleref": "previous", + "symbol": "arrow-bar-up", + "size": 20 + }, + "x": [ + 1, + 2, + null, + 2, + 3 + ], + "y": [ + 3, + 1, + null, + 1, + 2 + ] + }, + { + "mode": "markers+text", + "marker": { + "opacity": 1, + "size": [10, 40, 20] + }, + "text": [10, 40, 20], + "textposition": ["right", "center", "top"], + "x": [ + 1, + 2, + 3 + ], + "y": [ + 3, + 1, + 2 + ] + }, + { + "type": "scattergeo", + "mode": "markers+lines", + "line": { "width": 5 }, + "marker": { + "standoff": [5, 20, null, 20, 10], + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "lon": [ + -30, + 0, + null, + 0, + 30 + ], + "lat": [ + 20, + -5, + null, + -5, + 10 + ] + }, + { + "type": "scattergeo", + "mode": "markers+text", + "marker": { + "opacity": 1, + "size": [10, 40, 20] + }, + "text": [10, 40, 20], + "textposition": ["right", "center", "top"], + "lon": [ + -30, + 0, + 30 + ], + "lat": [ + 20, + -5, + 10 + ] + }, + { + "type": "scatterternary", + "mode": "markers+lines", + "line": { + "width": 5 + }, + "marker": { + "standoff": [5, 20, null, 20, 10], + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "a": [ + 2, + 1, + null, + 1, + 1 + ], + "b": [ + 1, + 2, + null, + 2, + 1 + ], + "c": [ + 1, + 1, + null, + 1, + 2 + ] + }, + { + "type": "scatterternary", + "mode": "markers+text", + "marker": { + "opacity": 1, + "size": [10, 40, 20] + }, + "text": [10, 40, 20], + "textposition": ["right", "center", "top"], + "a": [ + 2, + 1, + 1 + ], + "b": [ + 1, + 2, + 1 + ], + "c": [ + 1, + 1, + 2 + ] + }, + { + "xaxis": "x2", + "yaxis": "y2", + "type": "carpet", + "a": [ 4, 4, 4, 4.5, 4.5, 4.5, 5, 5, 5, 6, 6, 6 ], + "b": [ 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3 ], + "y": [ 2, 3.5, 4, 3, 4.5, 5, 5.5, 6.5, 7.5, 8, 8.5, 10 ], + "aaxis": { + "tickprefix": "a = ", + "ticksuffix": "m", + "smoothing": 1, + "minorgridcount": 9 + }, + "baxis": { + "tickprefix": "b = ", + "ticksuffix": "Pa", + "smoothing": 1, + "minorgridcount": 9 + }, + "carpet": "c0" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "type": "scattercarpet", + "mode": "markers+lines", + "line": { + "width": 5 + }, + "marker": { + "standoff": [5, 20, null, 20, 10], + "angleref": "previous", + "symbol": "arrow", + "size": 20 + }, + "a": [ 4, 5, null, 5, 6 ], + "b": [ 2.5, 2.5, null, 2.5, 2.5 ], + "carpet": "c0" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "type": "scattercarpet", + "mode": "markers+text", + "marker": { + "opacity": 1, + "size": [10, 40, 20] + }, + "text": [10, 40, 20], + "textposition": ["right", "center", "top"], + "a": [ 4, 5, 6 ], + "b": [ 2.5, 2.5, 2.5 ], + "carpet": "c0" + }, + { + "subplot": "polar", + "type": "scatterpolar", + "mode": "markers+lines", + "line": { + "width": 5 + }, + "marker": { + "standoff": [5, 20, null, 20, 10], + "angleref": "previous", + "symbol": "arrow-up", + "size": 20 + }, + "r": [ + 1, + 2, + null, + 2, + 3 + ], + "theta": [ + 270, + 90, + null, + 90, + 180 + ] + }, + { + "subplot": "polar", + "type": "scatterpolar", + "mode": "markers+text", + "marker": { + "opacity": 1, + "size": [10, 40, 20] + }, + "text": [10, 40, 20], + "textposition": ["right", "center", "top"], + "r": [ + 1, + 2, + 3 + ], + "theta": [ + 270, + 90, + 180 + ] + }, + { + "subplot": "smith", + "type": "scattersmith", + "mode": "markers+lines", + "line": { + "width": 5 + }, + "marker": { + "standoff": [5, 20, null, 20, 10], + "angleref": "previous", + "symbol": "arrow-wide", + "size": 20 + }, + "real": [ + 1, + 2, + null, + 2, + 0.5 + ], + "imag": [ + -0.5, + 1, + null, + 1, + 1 + ] + }, + { + "subplot": "smith", + "type": "scattersmith", + "mode": "markers+text", + "marker": { + "opacity": 1, + "size": [10, 40, 20] + }, + "text": [10, 40, 20], + "textposition": ["right", "center", "top"], + "real": [ + 1, + 2, + 0.5 + ], + "imag": [ + -0.5, + 1, + 1 + ] + } + ], + "layout": { + "xaxis": { + "domain": [ + 0, + 0.3 + ] + }, + "yaxis": { + "domain": [ + 0, + 0.45 + ] + }, + "xaxis2": { + "anchor": "y2", + "domain": [ + 0.35, + 0.65 + ] + }, + "yaxis2": { + "anchor": "x2", + "domain": [ + 0.55, + 1 + ] + }, + "geo": { + "projection": { + "type": "satellite" + }, + "domain": { + "x": [ + 0.35, + 0.65 + ], + "y": [ + 0, + 0.45 + ] + } + }, + "ternary": { + "domain": { + "x": [ + 0, + 0.3 + ], + "y": [ + 0.55, + 1 + ] + }, + "aaxis": { + "title": {"text": "A"} + }, + "baxis": { + "title": {"text": "B"} + }, + "caxis": { + "title": {"text": "C"} + } + }, + "polar": { + "domain": { + "x": [ + 0.7, + 1 + ], + "y": [ + 0, + 0.45 + ] + } + }, + "smith": { + "domain": { + "x": [ + 0.7, + 1 + ], + "y": [ + 0.55, + 1 + ] + } + }, + "height": 900, + "width": 1200, + "legend": { + "orientation": "h" + } + } +} diff --git a/test/image/mocks/z-marker_symbols-angle.json b/test/image/mocks/z-marker_symbols-angle.json new file mode 100644 index 00000000000..57485c39cfa --- /dev/null +++ b/test/image/mocks/z-marker_symbols-angle.json @@ -0,0 +1,603 @@ +{ + "data": [ + { + "mode": "markers", + "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, + 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, + 7 + ], + "y": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 + ], + "marker": { "angle": 15, + "symbol": [ + "circle", + "square", + "diamond", + "cross", + "x", + "triangle-up", + "triangle-down", + "triangle-left", + "triangle-right", + "triangle-ne", + "triangle-se", + "triangle-sw", + "triangle-nw", + "pentagon", + "hexagon", + "hexagon2", + "octagon", + "star", + "hexagram", + "star-triangle-up", + "star-triangle-down", + "star-square", + "star-diamond", + "diamond-tall", + "diamond-wide", + "hourglass", + "bowtie", + "circle-cross", + "circle-x", + "square-cross", + "square-x", + "diamond-cross", + "diamond-x", + "cross-thin", + "x-thin", + "asterisk", + "hash", + "y-up", + "y-down", + "y-left", + "y-right", + "line-ew", + "line-ns", + "line-ne", + "line-nw", + "arrow-up", + "arrow-down", + "arrow-left", + "arrow-right", + "arrow-bar-up", + "arrow-bar-down", + "arrow-bar-left", + "arrow-bar-right", + "arrow", + "arrow-wide" + ], + "color": "blue", + "size": 20, + "line": { + "color": "orange", + "width": 1.5 + } + }, + "text": [ + "marker symbol: circle
number: 0", + "marker symbol: square
number: 1", + "marker symbol: diamond
number: 2", + "marker symbol: cross
number: 3", + "marker symbol: x
number: 4", + "marker symbol: triangle-up
number: 5", + "marker symbol: triangle-down
number: 6", + "marker symbol: triangle-left
number: 7", + "marker symbol: triangle-right
number: 8", + "marker symbol: triangle-ne
number: 9", + "marker symbol: triangle-se
number: 10", + "marker symbol: triangle-sw
number: 11", + "marker symbol: triangle-nw
number: 12", + "marker symbol: pentagon
number: 13", + "marker symbol: hexagon
number: 14", + "marker symbol: hexagon2
number: 15", + "marker symbol: octagon
number: 16", + "marker symbol: star
number: 17", + "marker symbol: hexagram
number: 18", + "marker symbol: star-triangle-up
number: 19", + "marker symbol: star-triangle-down
number: 20", + "marker symbol: star-square
number: 21", + "marker symbol: star-diamond
number: 22", + "marker symbol: diamond-tall
number: 23", + "marker symbol: diamond-wide
number: 24", + "marker symbol: hourglass
number: 25", + "marker symbol: bowtie
number: 26", + "marker symbol: circle-cross
number: 27", + "marker symbol: circle-x
number: 28", + "marker symbol: square-cross
number: 29", + "marker symbol: square-x
number: 30", + "marker symbol: diamond-cross
number: 31", + "marker symbol: diamond-x
number: 32", + "marker symbol: cross-thin
number: 33", + "marker symbol: x-thin
number: 34", + "marker symbol: asterisk
number: 35", + "marker symbol: hash
number: 36", + "marker symbol: y-up
number: 37", + "marker symbol: y-down
number: 38", + "marker symbol: y-left
number: 39", + "marker symbol: y-right
number: 40", + "marker symbol: line-ew
number: 41", + "marker symbol: line-ns
number: 42", + "marker symbol: line-ne
number: 43", + "marker symbol: line-nw
number: 44", + "marker symbol: arrow-up
number: 45", + "marker symbol: arrow-down
number: 46", + "marker symbol: arrow-left
number: 47", + "marker symbol: arrow-right
number: 48", + "marker symbol: arrow-bar-up
number: 49", + "marker symbol: arrow-bar-down
number: 50", + "marker symbol: arrow-bar-left
number: 51", + "marker symbol: arrow-bar-right
number: 52", + "marker symbol: arrow
number: 53", + "marker symbol: arrow-wide
number: 54" + ], + "hoverinfo": "text" + }, + { + "mode": "markers", + "x": [ + 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, 14.5, + 16 + ], + "y": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 + ], + "marker": { "angle": 15, + "symbol": [ + "circle-open", + "square-open", + "diamond-open", + "cross-open", + "x-open", + "triangle-up-open", + "triangle-down-open", + "triangle-left-open", + "triangle-right-open", + "triangle-ne-open", + "triangle-se-open", + "triangle-sw-open", + "triangle-nw-open", + "pentagon-open", + "hexagon-open", + "hexagon2-open", + "octagon-open", + "star-open", + "hexagram-open", + "star-triangle-up-open", + "star-triangle-down-open", + "star-square-open", + "star-diamond-open", + "diamond-tall-open", + "diamond-wide-open", + "hourglass-open", + "bowtie-open", + "circle-cross-open", + "circle-x-open", + "square-cross-open", + "square-x-open", + "diamond-cross-open", + "diamond-x-open", + "cross-thin-open", + "x-thin-open", + "asterisk-open", + "hash-open", + "y-up-open", + "y-down-open", + "y-left-open", + "y-right-open", + "line-ew-open", + "line-ns-open", + "line-ne-open", + "line-nw-open", + "arrow-up-open", + "arrow-down-open", + "arrow-left-open", + "arrow-right-open", + "arrow-bar-up-open", + "arrow-bar-down-open", + "arrow-bar-left-open", + "arrow-bar-right-open", + "arrow-open", + "arrow-wide-open" + ], + "color": "blue", + "size": 20, + "line": { + "color": "orange", + "width": 1.5 + } + }, + "text": [ + "marker symbol: circle-open
number: 100", + "marker symbol: square-open
number: 101", + "marker symbol: diamond-open
number: 102", + "marker symbol: cross-open
number: 103", + "marker symbol: x-open
number: 104", + "marker symbol: triangle-up-open
number: 105", + "marker symbol: triangle-down-open
number: 106", + "marker symbol: triangle-left-open
number: 107", + "marker symbol: triangle-right-open
number: 108", + "marker symbol: triangle-ne-open
number: 109", + "marker symbol: triangle-se-open
number: 110", + "marker symbol: triangle-sw-open
number: 111", + "marker symbol: triangle-nw-open
number: 112", + "marker symbol: pentagon-open
number: 113", + "marker symbol: hexagon-open
number: 114", + "marker symbol: hexagon2-open
number: 115", + "marker symbol: octagon-open
number: 116", + "marker symbol: star-open
number: 117", + "marker symbol: hexagram-open
number: 118", + "marker symbol: star-triangle-up-open
number: 119", + "marker symbol: star-triangle-down-open
number: 120", + "marker symbol: star-square-open
number: 121", + "marker symbol: star-diamond-open
number: 122", + "marker symbol: diamond-tall-open
number: 123", + "marker symbol: diamond-wide-open
number: 124", + "marker symbol: hourglass-open
number: 125", + "marker symbol: bowtie-open
number: 126", + "marker symbol: circle-cross-open
number: 127", + "marker symbol: circle-x-open
number: 128", + "marker symbol: square-cross-open
number: 129", + "marker symbol: square-x-open
number: 130", + "marker symbol: diamond-cross-open
number: 131", + "marker symbol: diamond-x-open
number: 132", + "marker symbol: cross-thin-open
number: 133", + "marker symbol: x-thin-open
number: 134", + "marker symbol: asterisk-open
number: 135", + "marker symbol: hash-open
number: 136", + "marker symbol: y-up-open
number: 137", + "marker symbol: y-down-open
number: 138", + "marker symbol: y-left-open
number: 139", + "marker symbol: y-right-open
number: 140", + "marker symbol: line-ew-open
number: 141", + "marker symbol: line-ns-open
number: 142", + "marker symbol: line-ne-open
number: 143", + "marker symbol: line-nw-open
number: 144", + "marker symbol: arrow-up-open
number: 145", + "marker symbol: arrow-down-open
number: 146", + "marker symbol: arrow-left-open
number: 147", + "marker symbol: arrow-right-open
number: 148", + "marker symbol: arrow-bar-up-open
number: 149", + "marker symbol: arrow-bar-down-open
number: 150", + "marker symbol: arrow-bar-left-open
number: 151", + "marker symbol: arrow-bar-right-open
number: 152", + "marker symbol: arrow-open
number: 153", + "marker symbol: arrow-wide-open
number: 154" + ], + "hoverinfo": "text" + }, + { + "mode": "markers", + "x": [ + 18, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, 22, + 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, 23.5, + 25 + ], + "y": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 + ], + "marker": { "angle": 15, + "symbol": [ + "circle-dot", + "square-dot", + "diamond-dot", + "cross-dot", + "x-dot", + "triangle-up-dot", + "triangle-down-dot", + "triangle-left-dot", + "triangle-right-dot", + "triangle-ne-dot", + "triangle-se-dot", + "triangle-sw-dot", + "triangle-nw-dot", + "pentagon-dot", + "hexagon-dot", + "hexagon2-dot", + "octagon-dot", + "star-dot", + "hexagram-dot", + "star-triangle-up-dot", + "star-triangle-down-dot", + "star-square-dot", + "star-diamond-dot", + "diamond-tall-dot", + "diamond-wide-dot", + "hourglass-dot", + "bowtie-dot", + "circle-cross-dot", + "circle-x-dot", + "square-cross-dot", + "square-x-dot", + "diamond-cross-dot", + "diamond-x-dot", + "cross-thin-dot", + "x-thin-dot", + "asterisk-dot", + "hash-dot", + "y-up-dot", + "y-down-dot", + "y-left-dot", + "y-right-dot", + "line-ew-dot", + "line-ns-dot", + "line-ne-dot", + "line-nw-dot", + "arrow-up-dot", + "arrow-down-dot", + "arrow-left-dot", + "arrow-right-dot", + "arrow-bar-up-dot", + "arrow-bar-down-dot", + "arrow-bar-left-dot", + "arrow-bar-right-dot", + "arrow-dot", + "arrow-wide-dot" + ], + "color": "blue", + "size": 20, + "line": { + "color": "orange", + "width": 1.5 + } + }, + "text": [ + "marker symbol: circle-dot
number: 200", + "marker symbol: square-dot
number: 201", + "marker symbol: diamond-dot
number: 202", + "marker symbol: cross-dot
number: 203", + "marker symbol: x-dot
number: 204", + "marker symbol: triangle-up-dot
number: 205", + "marker symbol: triangle-down-dot
number: 206", + "marker symbol: triangle-left-dot
number: 207", + "marker symbol: triangle-right-dot
number: 208", + "marker symbol: triangle-ne-dot
number: 209", + "marker symbol: triangle-se-dot
number: 210", + "marker symbol: triangle-sw-dot
number: 211", + "marker symbol: triangle-nw-dot
number: 212", + "marker symbol: pentagon-dot
number: 213", + "marker symbol: hexagon-dot
number: 214", + "marker symbol: hexagon2-dot
number: 215", + "marker symbol: octagon-dot
number: 216", + "marker symbol: star-dot
number: 217", + "marker symbol: hexagram-dot
number: 218", + "marker symbol: star-triangle-up-dot
number: 219", + "marker symbol: star-triangle-down-dot
number: 220", + "marker symbol: star-square-dot
number: 221", + "marker symbol: star-diamond-dot
number: 222", + "marker symbol: diamond-tall-dot
number: 223", + "marker symbol: diamond-wide-dot
number: 224", + "marker symbol: hourglass-dot
number: 225", + "marker symbol: bowtie-dot
number: 226", + "marker symbol: circle-cross-dot
number: 227", + "marker symbol: circle-x-dot
number: 228", + "marker symbol: square-cross-dot
number: 229", + "marker symbol: square-x-dot
number: 230", + "marker symbol: diamond-cross-dot
number: 231", + "marker symbol: diamond-x-dot
number: 232", + "marker symbol: cross-thin-dot
number: 233", + "marker symbol: x-thin-dot
number: 234", + "marker symbol: asterisk-dot
number: 235", + "marker symbol: hash-dot
number: 236", + "marker symbol: y-up-dot
number: 237", + "marker symbol: y-down-dot
number: 238", + "marker symbol: y-left-dot
number: 239", + "marker symbol: y-right-dot
number: 240", + "marker symbol: line-ew-dot
number: 241", + "marker symbol: line-ns-dot
number: 242", + "marker symbol: line-ne-dot
number: 243", + "marker symbol: line-nw-dot
number: 244", + "marker symbol: arrow-up-dot
number: 245", + "marker symbol: arrow-down-dot
number: 246", + "marker symbol: arrow-left-dot
number: 247", + "marker symbol: arrow-right-dot
number: 248", + "marker symbol: arrow-bar-up-dot
number: 249", + "marker symbol: arrow-bar-down-dot
number: 250", + "marker symbol: arrow-bar-left-dot
number: 251", + "marker symbol: arrow-bar-right-dot
number: 252", + "marker symbol: arrow-dot
number: 253", + "marker symbol: arrow-wide-dot
number: 254" + ], + "hoverinfo": "text" + }, + { + "mode": "markers", + "x": [ + 27, 27, 27, 27, 27, 27, 27, 27, 27, + 28, 28, 28, 28, 28, 28, 28, 28, 28, + 29, 29, 29, 29, 29, 29, 29, 29, 29, + 30, 30, 30, 30, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, + 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, 32.5, + 34 + ], + "y": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0 + ], + "marker": { "angle": 15, + "symbol": [ + "circle-open-dot", + "square-open-dot", + "diamond-open-dot", + "cross-open-dot", + "x-open-dot", + "triangle-up-open-dot", + "triangle-down-open-dot", + "triangle-left-open-dot", + "triangle-right-open-dot", + "triangle-ne-open-dot", + "triangle-se-open-dot", + "triangle-sw-open-dot", + "triangle-nw-open-dot", + "pentagon-open-dot", + "hexagon-open-dot", + "hexagon2-open-dot", + "octagon-open-dot", + "star-open-dot", + "hexagram-open-dot", + "star-triangle-up-open-dot", + "star-triangle-down-open-dot", + "star-square-open-dot", + "star-diamond-open-dot", + "diamond-tall-open-dot", + "diamond-wide-open-dot", + "hourglass-open-dot", + "bowtie-open-dot", + "circle-cross-open-dot", + "circle-x-open-dot", + "square-cross-open-dot", + "square-x-open-dot", + "diamond-cross-open-dot", + "diamond-x-open-dot", + "cross-thin-open-dot", + "x-thin-open-dot", + "asterisk-open-dot", + "hash-open-dot", + "y-up-open-dot", + "y-down-open-dot", + "y-left-open-dot", + "y-right-open-dot", + "line-ew-open-dot", + "line-ns-open-dot", + "line-ne-open-dot", + "line-nw-open-dot", + "arrow-up-open-dot", + "arrow-down-open-dot", + "arrow-left-open-dot", + "arrow-right-open-dot", + "arrow-bar-up-open-dot", + "arrow-bar-down-open-dot", + "arrow-bar-left-open-dot", + "arrow-bar-right-open-dot", + "arrow-open-dot", + "arrow-wide-open-dot" + ], + "color": "blue", + "size": 20, + "line": { + "color": "orange", + "width": 1.5 + } + }, + "text": [ + "marker symbol: circle-open-dot
number: 300", + "marker symbol: square-open-dot
number: 301", + "marker symbol: diamond-open-dot
number: 302", + "marker symbol: cross-open-dot
number: 303", + "marker symbol: x-open-dot
number: 304", + "marker symbol: triangle-up-open-dot
number: 305", + "marker symbol: triangle-down-open-dot
number: 306", + "marker symbol: triangle-left-open-dot
number: 307", + "marker symbol: triangle-right-open-dot
number: 308", + "marker symbol: triangle-ne-open-dot
number: 309", + "marker symbol: triangle-se-open-dot
number: 310", + "marker symbol: triangle-sw-open-dot
number: 311", + "marker symbol: triangle-nw-open-dot
number: 312", + "marker symbol: pentagon-open-dot
number: 313", + "marker symbol: hexagon-open-dot
number: 314", + "marker symbol: hexagon2-open-dot
number: 315", + "marker symbol: octagon-open-dot
number: 316", + "marker symbol: star-open-dot
number: 317", + "marker symbol: hexagram-open-dot
number: 318", + "marker symbol: star-triangle-up-open-dot
number: 319", + "marker symbol: star-triangle-down-open-dot
number: 320", + "marker symbol: star-square-open-dot
number: 321", + "marker symbol: star-diamond-open-dot
number: 322", + "marker symbol: diamond-tall-open-dot
number: 323", + "marker symbol: diamond-wide-open-dot
number: 324", + "marker symbol: hourglass-open-dot
number: 325", + "marker symbol: bowtie-open-dot
number: 326", + "marker symbol: circle-cross-open-dot
number: 327", + "marker symbol: circle-x-open-dot
number: 328", + "marker symbol: square-cross-open-dot
number: 329", + "marker symbol: square-x-open-dot
number: 330", + "marker symbol: diamond-cross-open-dot
number: 331", + "marker symbol: diamond-x-open-dot
number: 332", + "marker symbol: cross-thin-open-dot
number: 333", + "marker symbol: x-thin-open-dot
number: 334", + "marker symbol: asterisk-open-dot
number: 335", + "marker symbol: hash-open-dot
number: 336", + "marker symbol: y-up-open-dot
number: 337", + "marker symbol: y-down-open-dot
number: 338", + "marker symbol: y-left-open-dot
number: 339", + "marker symbol: y-right-open-dot
number: 340", + "marker symbol: line-ew-open-dot
number: 341", + "marker symbol: line-ns-open-dot
number: 342", + "marker symbol: line-ne-open-dot
number: 343", + "marker symbol: line-nw-open-dot
number: 344", + "marker symbol: line-nw-open-dot
number: 344", + "marker symbol: arrow-up-open-dot
number: 345", + "marker symbol: arrow-down-open-dot
number: 346", + "marker symbol: arrow-left-open-dot
number: 347", + "marker symbol: arrow-right-open-dot
number: 348", + "marker symbol: arrow-bar-up-open-dot
number: 349", + "marker symbol: arrow-bar-down-open-dot
number: 350", + "marker symbol: arrow-bar-left-open-dot
number: 351", + "marker symbol: arrow-bar-right-open-dot
number: 352", + "marker symbol: arrow-open-dot
number: 353", + "marker symbol: arrow-wide-open-dot
number: 354" + ], + "hoverinfo": "text" + } + ], + "layout": { + "margin": { + "l": 0, + "r": 0, + "b": 0, + "t": 0 + }, + "width": 1500, + "height": 500, + "xaxis": { + "showgrid": false, + "zeroline": false + }, + "yaxis": { + "showgrid": false, + "zeroline": false, + "autorange": "reversed" + }, + "showlegend": false, + "plot_bgcolor": "#d3d3d3", + "hovermode": "closest" + } +} diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js index f28ff0e7d70..11d0f7d9eab 100644 --- a/test/jasmine/tests/geo_test.js +++ b/test/jasmine/tests/geo_test.js @@ -2732,6 +2732,43 @@ describe('Test geo zoom/pan/drag interactions:', function() { }); }); +describe('Test geo interactions update marker angles:', function() { + var gd; + + beforeEach(function() { gd = createGraphDiv(); }); + + afterEach(destroyGraphDiv); + + function getPath() { + return d3Select('.scattergeo .point').node().getAttribute('d'); + } + + it('update angles when panning', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/geo_conic-conformal')); + fig.layout.width = 700; + fig.layout.height = 500; + fig.layout.dragmode = 'pan'; + + var initialPath, newPath; + + Plotly.newPlot(gd, fig) + .then(function() { + initialPath = getPath(); + + return drag({path: [[300, 200], [350, 250], [400, 300]], noCover: true}); + }) + .then(function() { + newPath = getPath(); + expect(newPath).toEqual('M0,0L18.238949513733473,8.206139204003389L19.579067718529352,-4.081679467234269Z'); + + expect(newPath).not.toEqual(initialPath); + expect(newPath).toEqual('M0,0L18.238949513733473,8.206139204003389L19.579067718529352,-4.081679467234269Z'); + expect(initialPath).toEqual('M0,0L-1.5033314641545745,19.94341982983066L10.506227353572104,17.01820163222463Z'); + }) + .then(done, done.fail); + }); +}); + describe('plotly_relayouting', function() { var gd; var events; diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index 66ea5a85b58..a2064d33db1 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -3999,6 +3999,9 @@ describe('hover distance', function() { describe('closest hovermode', function() { var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.hovermode = 'closest'; + // use simple markers here + delete mockCopy.data[0].marker; + delete mockCopy.data[1].marker; beforeEach(function(done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); diff --git a/test/jasmine/tests/scatterternary_test.js b/test/jasmine/tests/scatterternary_test.js index 3d947a02b8b..1b762f981ed 100644 --- a/test/jasmine/tests/scatterternary_test.js +++ b/test/jasmine/tests/scatterternary_test.js @@ -455,6 +455,8 @@ describe('Test scatterternary *cliponaxis*', function() { it('should show/hide point/text/errorbars in clipped and non-clipped layers', function(done) { var gd = createGraphDiv(); var fig = Lib.extendDeep({}, require('@mocks/ternary_markers.json')); + // use simple markers here + delete fig.data[0].marker; function _assert(layerClips, nodeDisplays, lineClips) { var frontLayer = d3Select('.frontplot'); diff --git a/test/plot-schema.json b/test/plot-schema.json index 353097973cb..de21dc77c47 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -435,7 +435,8 @@ "angle": { "description": "A number (in degree) between -180 and 180.", "otherOpts": [ - "dflt" + "dflt", + "arrayOk" ], "requiredOpts": [] }, @@ -15451,6 +15452,13 @@ "valType": "string" }, "marker": { + "angle": { + "arrayOk": false, + "description": "Sets the marker angle in respect to `angleref`.", + "dflt": 0, + "editType": "calc", + "valType": "angle" + }, "color": { "arrayOk": false, "description": "Sets the marker color. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.cmin` and `marker.cmax` if set.", @@ -15993,7 +16001,19 @@ "arrow-bar-right", 152, "152", - "arrow-bar-right-open" + "arrow-bar-right-open", + 53, + "53", + "arrow", + 153, + "153", + "arrow-open", + 54, + "54", + "arrow-wide", + 154, + "154", + "arrow-wide-open" ] } }, @@ -43252,6 +43272,19 @@ "valType": "number" }, "line": { + "backoff": { + "arrayOk": true, + "description": "Sets the line back off from the end point of the nth line segment (in px). This option is useful e.g. to avoid overlap with arrowhead markers. With *auto* the lines would trim before markers if `marker.angleref` is set to *previous*.", + "dflt": "auto", + "editType": "plot", + "min": 0, + "valType": "number" + }, + "backoffsrc": { + "description": "Sets the source reference on Chart Studio Cloud for `backoff`.", + "editType": "none", + "valType": "string" + }, "color": { "anim": true, "description": "Sets the line color.", @@ -43312,6 +43345,30 @@ } }, "marker": { + "angle": { + "anim": false, + "arrayOk": true, + "description": "Sets the marker angle in respect to `angleref`.", + "dflt": 0, + "editType": "plot", + "valType": "angle" + }, + "angleref": { + "anim": false, + "description": "Sets the reference for marker angle. With *previous*, angle 0 points along the line from the previous point to this one. With *up*, angle 0 points toward the top of the screen.", + "dflt": "up", + "editType": "plot", + "valType": "enumerated", + "values": [ + "previous", + "up" + ] + }, + "anglesrc": { + "description": "Sets the source reference on Chart Studio Cloud for `angle`.", + "editType": "none", + "valType": "string" + }, "autocolorscale": { "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", "dflt": true, @@ -44060,6 +44117,20 @@ "editType": "none", "valType": "string" }, + "standoff": { + "anim": true, + "arrayOk": true, + "description": "Moves the marker away from the data point in the direction of `angle` (in px). This can be useful for example if you have another marker at this location and you want to point an arrowhead marker at it.", + "dflt": 0, + "editType": "plot", + "min": 0, + "valType": "number" + }, + "standoffsrc": { + "description": "Sets the source reference on Chart Studio Cloud for `standoff`.", + "editType": "none", + "valType": "string" + }, "symbol": { "arrayOk": true, "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.", @@ -44540,7 +44611,19 @@ "arrow-bar-right", 152, "152", - "arrow-bar-right-open" + "arrow-bar-right-open", + 53, + "53", + "arrow", + 153, + "153", + "arrow-open", + 54, + "54", + "arrow-wide", + 154, + "154", + "arrow-wide-open" ] }, "symbolsrc": { @@ -47492,6 +47575,19 @@ "valType": "number" }, "line": { + "backoff": { + "arrayOk": true, + "description": "Sets the line back off from the end point of the nth line segment (in px). This option is useful e.g. to avoid overlap with arrowhead markers. With *auto* the lines would trim before markers if `marker.angleref` is set to *previous*.", + "dflt": "auto", + "editType": "plot", + "min": 0, + "valType": "number" + }, + "backoffsrc": { + "description": "Sets the source reference on Chart Studio Cloud for `backoff`.", + "editType": "none", + "valType": "string" + }, "color": { "description": "Sets the line color.", "editType": "style", @@ -47540,6 +47636,28 @@ } }, "marker": { + "angle": { + "arrayOk": true, + "description": "Sets the marker angle in respect to `angleref`.", + "dflt": 0, + "editType": "plot", + "valType": "angle" + }, + "angleref": { + "description": "Sets the reference for marker angle. With *previous*, angle 0 points along the line from the previous point to this one. With *up*, angle 0 points toward the top of the screen.", + "dflt": "up", + "editType": "plot", + "valType": "enumerated", + "values": [ + "previous", + "up" + ] + }, + "anglesrc": { + "description": "Sets the source reference on Chart Studio Cloud for `angle`.", + "editType": "none", + "valType": "string" + }, "autocolorscale": { "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", "dflt": true, @@ -48283,6 +48401,19 @@ "editType": "none", "valType": "string" }, + "standoff": { + "arrayOk": true, + "description": "Moves the marker away from the data point in the direction of `angle` (in px). This can be useful for example if you have another marker at this location and you want to point an arrowhead marker at it.", + "dflt": 0, + "editType": "plot", + "min": 0, + "valType": "number" + }, + "standoffsrc": { + "description": "Sets the source reference on Chart Studio Cloud for `standoff`.", + "editType": "none", + "valType": "string" + }, "symbol": { "arrayOk": true, "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.", @@ -48763,7 +48894,19 @@ "arrow-bar-right", 152, "152", - "arrow-bar-right-open" + "arrow-bar-right-open", + 53, + "53", + "arrow", + 153, + "153", + "arrow-open", + 54, + "54", + "arrow-wide", + 154, + "154", + "arrow-wide-open" ] }, "symbolsrc": { @@ -49375,6 +49518,29 @@ "valType": "string" }, "marker": { + "angle": { + "arrayOk": true, + "description": "Sets the marker angle in respect to `angleref`.", + "dflt": 0, + "editType": "calc", + "valType": "angle" + }, + "angleref": { + "description": "Sets the reference for marker angle. With *previous*, angle 0 points along the line from the previous point to this one. With *up*, angle 0 points toward the top of the screen. With *north*, angle 0 points north based on the current map projection.", + "dflt": "up", + "editType": "calc", + "valType": "enumerated", + "values": [ + "previous", + "up", + "north" + ] + }, + "anglesrc": { + "description": "Sets the source reference on Chart Studio Cloud for `angle`.", + "editType": "none", + "valType": "string" + }, "autocolorscale": { "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", "dflt": true, @@ -50111,6 +50277,19 @@ "editType": "none", "valType": "string" }, + "standoff": { + "arrayOk": true, + "description": "Moves the marker away from the data point in the direction of `angle` (in px). This can be useful for example if you have another marker at this location and you want to point an arrowhead marker at it.", + "dflt": 0, + "editType": "calc", + "min": 0, + "valType": "number" + }, + "standoffsrc": { + "description": "Sets the source reference on Chart Studio Cloud for `standoff`.", + "editType": "none", + "valType": "string" + }, "symbol": { "arrayOk": true, "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.", @@ -50591,7 +50770,19 @@ "arrow-bar-right", 152, "152", - "arrow-bar-right-open" + "arrow-bar-right-open", + 53, + "53", + "arrow", + 153, + "153", + "arrow-open", + 54, + "54", + "arrow-wide", + 154, + "154", + "arrow-wide-open" ] }, "symbolsrc": { @@ -51356,6 +51547,18 @@ } }, "marker": { + "angle": { + "arrayOk": true, + "description": "Sets the marker angle in respect to `angleref`.", + "dflt": 0, + "editType": "calc", + "valType": "angle" + }, + "anglesrc": { + "description": "Sets the source reference on Chart Studio Cloud for `angle`.", + "editType": "none", + "valType": "string" + }, "autocolorscale": { "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", "dflt": true, @@ -52539,7 +52742,19 @@ "arrow-bar-right", 152, "152", - "arrow-bar-right-open" + "arrow-bar-right-open", + 53, + "53", + "arrow", + 153, + "153", + "arrow-open", + 54, + "54", + "arrow-wide", + 154, + "154", + "arrow-wide-open" ] }, "symbolsrc": { @@ -54377,6 +54592,19 @@ "valType": "number" }, "line": { + "backoff": { + "arrayOk": true, + "description": "Sets the line back off from the end point of the nth line segment (in px). This option is useful e.g. to avoid overlap with arrowhead markers. With *auto* the lines would trim before markers if `marker.angleref` is set to *previous*.", + "dflt": "auto", + "editType": "plot", + "min": 0, + "valType": "number" + }, + "backoffsrc": { + "description": "Sets the source reference on Chart Studio Cloud for `backoff`.", + "editType": "none", + "valType": "string" + }, "color": { "description": "Sets the line color.", "editType": "style", @@ -54425,6 +54653,28 @@ } }, "marker": { + "angle": { + "arrayOk": true, + "description": "Sets the marker angle in respect to `angleref`.", + "dflt": 0, + "editType": "plot", + "valType": "angle" + }, + "angleref": { + "description": "Sets the reference for marker angle. With *previous*, angle 0 points along the line from the previous point to this one. With *up*, angle 0 points toward the top of the screen.", + "dflt": "up", + "editType": "plot", + "valType": "enumerated", + "values": [ + "previous", + "up" + ] + }, + "anglesrc": { + "description": "Sets the source reference on Chart Studio Cloud for `angle`.", + "editType": "none", + "valType": "string" + }, "autocolorscale": { "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", "dflt": true, @@ -55168,6 +55418,19 @@ "editType": "none", "valType": "string" }, + "standoff": { + "arrayOk": true, + "description": "Moves the marker away from the data point in the direction of `angle` (in px). This can be useful for example if you have another marker at this location and you want to point an arrowhead marker at it.", + "dflt": 0, + "editType": "plot", + "min": 0, + "valType": "number" + }, + "standoffsrc": { + "description": "Sets the source reference on Chart Studio Cloud for `standoff`.", + "editType": "none", + "valType": "string" + }, "symbol": { "arrayOk": true, "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.", @@ -55648,7 +55911,19 @@ "arrow-bar-right", 152, "152", - "arrow-bar-right-open" + "arrow-bar-right-open", + 53, + "53", + "arrow", + 153, + "153", + "arrow-open", + 54, + "54", + "arrow-wide", + 154, + "154", + "arrow-wide-open" ] }, "symbolsrc": { @@ -56263,6 +56538,18 @@ } }, "marker": { + "angle": { + "arrayOk": true, + "description": "Sets the marker angle in respect to `angleref`.", + "dflt": 0, + "editType": "calc", + "valType": "angle" + }, + "anglesrc": { + "description": "Sets the source reference on Chart Studio Cloud for `angle`.", + "editType": "none", + "valType": "string" + }, "autocolorscale": { "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", "dflt": true, @@ -57446,7 +57733,19 @@ "arrow-bar-right", 152, "152", - "arrow-bar-right-open" + "arrow-bar-right-open", + 53, + "53", + "arrow", + 153, + "153", + "arrow-open", + 54, + "54", + "arrow-wide", + 154, + "154", + "arrow-wide-open" ] }, "symbolsrc": { @@ -58030,6 +58329,19 @@ "valType": "number" }, "line": { + "backoff": { + "arrayOk": true, + "description": "Sets the line back off from the end point of the nth line segment (in px). This option is useful e.g. to avoid overlap with arrowhead markers. With *auto* the lines would trim before markers if `marker.angleref` is set to *previous*.", + "dflt": "auto", + "editType": "plot", + "min": 0, + "valType": "number" + }, + "backoffsrc": { + "description": "Sets the source reference on Chart Studio Cloud for `backoff`.", + "editType": "none", + "valType": "string" + }, "color": { "description": "Sets the line color.", "editType": "style", @@ -58078,6 +58390,28 @@ } }, "marker": { + "angle": { + "arrayOk": true, + "description": "Sets the marker angle in respect to `angleref`.", + "dflt": 0, + "editType": "plot", + "valType": "angle" + }, + "angleref": { + "description": "Sets the reference for marker angle. With *previous*, angle 0 points along the line from the previous point to this one. With *up*, angle 0 points toward the top of the screen.", + "dflt": "up", + "editType": "plot", + "valType": "enumerated", + "values": [ + "previous", + "up" + ] + }, + "anglesrc": { + "description": "Sets the source reference on Chart Studio Cloud for `angle`.", + "editType": "none", + "valType": "string" + }, "autocolorscale": { "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", "dflt": true, @@ -58821,6 +59155,19 @@ "editType": "none", "valType": "string" }, + "standoff": { + "arrayOk": true, + "description": "Moves the marker away from the data point in the direction of `angle` (in px). This can be useful for example if you have another marker at this location and you want to point an arrowhead marker at it.", + "dflt": 0, + "editType": "plot", + "min": 0, + "valType": "number" + }, + "standoffsrc": { + "description": "Sets the source reference on Chart Studio Cloud for `standoff`.", + "editType": "none", + "valType": "string" + }, "symbol": { "arrayOk": true, "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.", @@ -59301,7 +59648,19 @@ "arrow-bar-right", 152, "152", - "arrow-bar-right-open" + "arrow-bar-right-open", + 53, + "53", + "arrow", + 153, + "153", + "arrow-open", + 54, + "54", + "arrow-wide", + 154, + "154", + "arrow-wide-open" ] }, "symbolsrc": { @@ -59871,6 +60230,19 @@ "valType": "number" }, "line": { + "backoff": { + "arrayOk": true, + "description": "Sets the line back off from the end point of the nth line segment (in px). This option is useful e.g. to avoid overlap with arrowhead markers. With *auto* the lines would trim before markers if `marker.angleref` is set to *previous*.", + "dflt": "auto", + "editType": "plot", + "min": 0, + "valType": "number" + }, + "backoffsrc": { + "description": "Sets the source reference on Chart Studio Cloud for `backoff`.", + "editType": "none", + "valType": "string" + }, "color": { "description": "Sets the line color.", "editType": "style", @@ -59919,6 +60291,28 @@ } }, "marker": { + "angle": { + "arrayOk": true, + "description": "Sets the marker angle in respect to `angleref`.", + "dflt": 0, + "editType": "plot", + "valType": "angle" + }, + "angleref": { + "description": "Sets the reference for marker angle. With *previous*, angle 0 points along the line from the previous point to this one. With *up*, angle 0 points toward the top of the screen.", + "dflt": "up", + "editType": "plot", + "valType": "enumerated", + "values": [ + "previous", + "up" + ] + }, + "anglesrc": { + "description": "Sets the source reference on Chart Studio Cloud for `angle`.", + "editType": "none", + "valType": "string" + }, "autocolorscale": { "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", "dflt": true, @@ -60662,6 +61056,19 @@ "editType": "none", "valType": "string" }, + "standoff": { + "arrayOk": true, + "description": "Moves the marker away from the data point in the direction of `angle` (in px). This can be useful for example if you have another marker at this location and you want to point an arrowhead marker at it.", + "dflt": 0, + "editType": "plot", + "min": 0, + "valType": "number" + }, + "standoffsrc": { + "description": "Sets the source reference on Chart Studio Cloud for `standoff`.", + "editType": "none", + "valType": "string" + }, "symbol": { "arrayOk": true, "description": "Sets the marker symbol type. Adding 100 is equivalent to appending *-open* to a symbol name. Adding 200 is equivalent to appending *-dot* to a symbol name. Adding 300 is equivalent to appending *-open-dot* or *dot-open* to a symbol name.", @@ -61142,7 +61549,19 @@ "arrow-bar-right", 152, "152", - "arrow-bar-right-open" + "arrow-bar-right-open", + 53, + "53", + "arrow", + 153, + "153", + "arrow-open", + 54, + "54", + "arrow-wide", + 154, + "154", + "arrow-wide-open" ] }, "symbolsrc": { @@ -61714,6 +62133,18 @@ "valType": "number" }, "marker": { + "angle": { + "arrayOk": true, + "description": "Sets the marker angle in respect to `angleref`.", + "dflt": 0, + "editType": "plot", + "valType": "angle" + }, + "anglesrc": { + "description": "Sets the source reference on Chart Studio Cloud for `angle`.", + "editType": "none", + "valType": "string" + }, "autocolorscale": { "description": "Determines whether the colorscale is a default palette (`autocolorscale: true`) or the palette determined by `marker.colorscale`. Has an effect only if in `marker.color` is set to a numerical array. In case `colorscale` is unspecified or `autocolorscale` is true, the default palette will be chosen according to whether numbers in the `color` array are all positive, all negative or mixed.", "dflt": true, @@ -62897,7 +63328,19 @@ "arrow-bar-right", 152, "152", - "arrow-bar-right-open" + "arrow-bar-right-open", + 53, + "53", + "arrow", + 153, + "153", + "arrow-open", + 54, + "54", + "arrow-wide", + 154, + "154", + "arrow-wide-open" ] }, "symbolsrc": { @@ -69114,6 +69557,13 @@ } }, "marker": { + "angle": { + "arrayOk": false, + "description": "Sets the marker angle in respect to `angleref`.", + "dflt": 0, + "editType": "calc", + "valType": "angle" + }, "color": { "arrayOk": false, "description": "Sets the marker color. It accepts either a specific color or an array of numbers that are mapped to the colorscale relative to the max and min values of the array or relative to `marker.cmin` and `marker.cmax` if set.", @@ -69656,7 +70106,19 @@ "arrow-bar-right", 152, "152", - "arrow-bar-right-open" + "arrow-bar-right-open", + 53, + "53", + "arrow", + 153, + "153", + "arrow-open", + 54, + "54", + "arrow-wide", + 154, + "154", + "arrow-wide-open" ] } },