Skip to content

Commit 68b489d

Browse files
committed
continue lines off the edge toward invalid log values
1 parent 2fde3dc commit 68b489d

File tree

5 files changed

+79
-18
lines changed

5 files changed

+79
-18
lines changed

src/constants/numerical.js

+6
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ module.exports = {
4949
*/
5050
ALMOST_EQUAL: 1 - 1e-6,
5151

52+
/*
53+
* If we're asked to clip a non-positive log value, how far off-screen
54+
* do we put it?
55+
*/
56+
LOG_CLIP: 10,
57+
5258
/*
5359
* not a number, but for displaying numbers: the "minus sign" symbol is
5460
* wider than the regular ascii dash "-"

src/plots/cartesian/set_convert.js

+3-7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var ensureNumber = Lib.ensureNumber;
2121
var numConstants = require('../../constants/numerical');
2222
var FP_SAFE = numConstants.FP_SAFE;
2323
var BADNUM = numConstants.BADNUM;
24+
var LOG_CLIP = numConstants.LOG_CLIP;
2425

2526
var constants = require('./constants');
2627
var axisIds = require('./axis_ids');
@@ -59,20 +60,15 @@ module.exports = function setConvert(ax, fullLayout) {
5960

6061
var axLetter = (ax._id || 'x').charAt(0);
6162

62-
// clipMult: how many axis lengths past the edge do we render?
63-
// for panning, 1-2 would suffice, but for zooming more is nice.
64-
// also, clipping can affect the direction of lines off the edge...
65-
var clipMult = 10;
66-
6763
function toLog(v, clip) {
6864
if(v > 0) return Math.log(v) / Math.LN10;
6965

7066
else if(v <= 0 && clip && ax.range && ax.range.length === 2) {
71-
// clip NaN (ie past negative infinity) to clipMult axis
67+
// clip NaN (ie past negative infinity) to LOG_CLIP axis
7268
// length past the negative edge
7369
var r0 = ax.range[0],
7470
r1 = ax.range[1];
75-
return 0.5 * (r0 + r1 - 3 * clipMult * Math.abs(r0 - r1));
71+
return 0.5 * (r0 + r1 - 2 * LOG_CLIP * Math.abs(r0 - r1));
7672
}
7773

7874
else return BADNUM;

src/traces/scatter/line_points.js

+37-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99

1010
'use strict';
1111

12-
var BADNUM = require('../../constants/numerical').BADNUM;
12+
var numConstants = require('../../constants/numerical');
13+
var BADNUM = numConstants.BADNUM;
14+
var LOG_CLIP = numConstants.LOG_CLIP;
15+
var LOG_CLIP_PLUS = LOG_CLIP + 0.5;
16+
var LOG_CLIP_MINUS = LOG_CLIP - 0.5;
1317
var Lib = require('../../lib');
1418
var segmentsIntersect = Lib.segmentsIntersect;
1519
var constrain = Lib.constrain;
@@ -19,6 +23,10 @@ var constants = require('./constants');
1923
module.exports = function linePoints(d, opts) {
2024
var xa = opts.xaxis;
2125
var ya = opts.yaxis;
26+
var xLog = xa.type === 'log';
27+
var yLog = ya.type === 'log';
28+
var xLen = xa._length;
29+
var yLen = ya._length;
2230
var connectGaps = opts.connectGaps;
2331
var baseTolerance = opts.baseTolerance;
2432
var shape = opts.shape;
@@ -59,7 +67,25 @@ module.exports = function linePoints(d, opts) {
5967
if(!di) return false;
6068
var x = xa.c2p(di.x);
6169
var y = ya.c2p(di.y);
62-
if(x === BADNUM || y === BADNUM) return false;
70+
71+
// if non-positive log values, set them VERY far off-screen
72+
// so the line looks essentially straight from the previous point.
73+
if(x === BADNUM) {
74+
if(xLog) x = xa.c2p(di.x, true);
75+
if(x === BADNUM) return false;
76+
// If BOTH were bad log values, make the line follow a constant
77+
// exponent rather than a constant slope
78+
if(yLog && y === BADNUM) {
79+
x *= Math.abs(xa._m * yLen * (xa._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS) /
80+
(ya._m * xLen * (ya._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS)));
81+
}
82+
x *= 1000;
83+
}
84+
if(y === BADNUM) {
85+
if(yLog) y = ya.c2p(di.y, true);
86+
if(y === BADNUM) return false;
87+
y *= 1000;
88+
}
6389
return [x, y];
6490
}
6591

@@ -79,16 +105,16 @@ module.exports = function linePoints(d, opts) {
79105
var latestXFrac, latestYFrac;
80106
// if we're off-screen, increase tolerance over baseTolerance
81107
function getTolerance(pt, nextPt) {
82-
var xFrac = pt[0] / xa._length;
83-
var yFrac = pt[1] / ya._length;
108+
var xFrac = pt[0] / xLen;
109+
var yFrac = pt[1] / yLen;
84110
var offScreenFraction = Math.max(0, -xFrac, xFrac - 1, -yFrac, yFrac - 1);
85111
if(offScreenFraction && (latestXFrac !== undefined) &&
86112
crossesViewport(xFrac, yFrac, latestXFrac, latestYFrac)
87113
) {
88114
offScreenFraction = 0;
89115
}
90116
if(offScreenFraction && nextPt &&
91-
crossesViewport(xFrac, yFrac, nextPt[0] / xa._length, nextPt[1] / ya._length)
117+
crossesViewport(xFrac, yFrac, nextPt[0] / xLen, nextPt[1] / yLen)
92118
) {
93119
offScreenFraction = 0;
94120
}
@@ -114,10 +140,10 @@ module.exports = function linePoints(d, opts) {
114140
// if both are outside there will be 0 or 2 intersections
115141
// (or 1 if it's right at a corner - we'll treat that like 0)
116142
// returns an array of intersection pts
117-
var xEdge0 = -xa._length * maxScreensAway;
118-
var xEdge1 = xa._length * (1 + maxScreensAway);
119-
var yEdge0 = -ya._length * maxScreensAway;
120-
var yEdge1 = ya._length * (1 + maxScreensAway);
143+
var xEdge0 = -xLen * maxScreensAway;
144+
var xEdge1 = xLen * (1 + maxScreensAway);
145+
var yEdge0 = -yLen * maxScreensAway;
146+
var yEdge1 = yLen * (1 + maxScreensAway);
121147
var edges = [
122148
[xEdge0, yEdge0, xEdge1, yEdge0],
123149
[xEdge1, yEdge0, xEdge1, yEdge1],
@@ -261,8 +287,8 @@ module.exports = function linePoints(d, opts) {
261287
}
262288

263289
function addPt(pt) {
264-
latestXFrac = pt[0] / xa._length;
265-
latestYFrac = pt[1] / ya._length;
290+
latestXFrac = pt[0] / xLen;
291+
latestYFrac = pt[1] / yLen;
266292
// Are we more than maxScreensAway off-screen any direction?
267293
// if so, clip to this box, but in such a way that on-screen
268294
// drawing is unchanged
55.5 KB
Loading

test/image/mocks/log_lines_fills.json

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"data": [{
3+
"x": [-1, 1.5, 2, 0, 1, 3, 4, 5, 6, 1, -1, -1, 3, 3, 0, 7, 7, -1, -1],
4+
"y": [-1, 1.5, 1, -1, -1, 1, 1, 0, 2, 2, 0, 2, 3, 4, 5, 6, -2, -2, -1],
5+
"mode": "markers+lines", "fill": "toself"
6+
}, {
7+
"x": [-1, 1.5, 2, 0, 1, 3, 4, 5, 6, 1, -1, -1, 3, 3, 0, 7, 7, -1, -1],
8+
"y": [-1, 1.5, 1, -1, -1, 1, 1, 0, 2, 2, 0, 2, 3, 4, 5, 6, -2, -2, -1],
9+
"mode": "markers+lines", "fill": "toself", "xaxis": "x2"
10+
}, {
11+
"x": [-1, 1.5, 2, 0, 1, 3, 4, 5, 6, 1, -1, -1, 3, 3, 0, 7, 7, -1, -1],
12+
"y": [-1, 1.5, 1, -1, -1, 1, 1, 0, 2, 2, 0, 2, 3, 4, 5, 6, -2, -2, -1],
13+
"mode": "markers+lines", "fill": "toself", "yaxis": "y2"
14+
}, {
15+
"x": [-1, 1.5, 2, 0, 1, 3, 4, 5, 6, 1, -1, -1, 3, 3, 0, 7, 7, -1, -1],
16+
"y": [-1, 1.5, 1, -1, -1, 1, 1, 0, 2, 2, 0, 2, 3, 4, 5, 6, -2, -2, -1],
17+
"mode": "markers+lines", "fill": "toself", "xaxis": "x2", "yaxis": "y2"
18+
}, {
19+
"x": [0.01, 0.1], "y": [0.01, 0.1],
20+
"mode": "markers", "xaxis": "x2", "yaxis": "y2"
21+
}],
22+
"layout": {
23+
"width": 800,
24+
"height": 600,
25+
"xaxis": {"title": "linear"},
26+
"xaxis2": {"title": "log", "type": "log"},
27+
"yaxis": {"title": "linear"},
28+
"yaxis2": {"title": "log", "type": "log"},
29+
"grid": {"columns": 2, "rows": 2},
30+
"showlegend": false,
31+
"title": "Lines to invalid log values"
32+
}
33+
}

0 commit comments

Comments
 (0)