Skip to content

Commit 33e1acf

Browse files
authored
Merge pull request #2954 from plotly/barpolar-traces
Add barpolar traces
2 parents 8457b40 + 235fe5b commit 33e1acf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+2083
-522
lines changed

Diff for: lib/barpolar.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright 2012-2018, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
module.exports = require('../src/traces/barpolar');

Diff for: lib/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ Plotly.register([
5353
require('./candlestick'),
5454

5555
require('./scatterpolar'),
56-
require('./scatterpolargl')
56+
require('./scatterpolargl'),
57+
require('./barpolar')
5758
]);
5859

5960
// transforms

Diff for: src/lib/angles.js

+220-17
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,230 @@
88

99
'use strict';
1010

11+
var modModule = require('./mod');
12+
var mod = modModule.mod;
13+
var modHalf = modModule.modHalf;
14+
1115
var PI = Math.PI;
16+
var twoPI = 2 * PI;
1217

13-
exports.deg2rad = function(deg) {
14-
return deg / 180 * PI;
15-
};
18+
function deg2rad(deg) { return deg / 180 * PI; }
1619

17-
exports.rad2deg = function(rad) {
18-
return rad / PI * 180;
19-
};
20+
function rad2deg(rad) { return rad / PI * 180; }
2021

21-
exports.wrap360 = function(deg) {
22-
var out = deg % 360;
23-
return out < 0 ? out + 360 : out;
24-
};
22+
/**
23+
* is sector a full circle?
24+
* ... this comes up a lot in SVG path-drawing routines
25+
*
26+
* @param {2-item array} aBnds : angular bounds in *radians*
27+
* @return {boolean}
28+
*/
29+
function isFullCircle(aBnds) {
30+
return Math.abs(Math.abs(aBnds[1] - aBnds[0]) - twoPI) < 1e-15;
31+
}
2532

26-
exports.wrap180 = function(deg) {
27-
if(Math.abs(deg) > 180) deg -= Math.round(deg / 360) * 360;
28-
return deg;
29-
};
33+
/**
34+
* angular delta between angle 'a' and 'b'
35+
* solution taken from: https://stackoverflow.com/a/2007279
36+
*
37+
* @param {number} a : first angle in *radians*
38+
* @param {number} b : second angle in *radians*
39+
* @return {number} angular delta in *radians*
40+
*/
41+
function angleDelta(a, b) {
42+
return modHalf(b - a, twoPI);
43+
}
44+
45+
/**
46+
* angular distance between angle 'a' and 'b'
47+
*
48+
* @param {number} a : first angle in *radians*
49+
* @param {number} b : second angle in *radians*
50+
* @return {number} angular distance in *radians*
51+
*/
52+
function angleDist(a, b) {
53+
return Math.abs(angleDelta(a, b));
54+
}
55+
56+
/**
57+
* is angle inside sector?
58+
*
59+
* @param {number} a : angle to test in *radians*
60+
* @param {2-item array} aBnds : sector's angular bounds in *radians*
61+
* @param {boolean}
62+
*/
63+
function isAngleInsideSector(a, aBnds) {
64+
if(isFullCircle(aBnds)) return true;
65+
66+
var s0, s1;
67+
68+
if(aBnds[0] < aBnds[1]) {
69+
s0 = aBnds[0];
70+
s1 = aBnds[1];
71+
} else {
72+
s0 = aBnds[1];
73+
s1 = aBnds[0];
74+
}
75+
76+
s0 = mod(s0, twoPI);
77+
s1 = mod(s1, twoPI);
78+
if(s0 > s1) s1 += twoPI;
79+
80+
var a0 = mod(a, twoPI);
81+
var a1 = a0 + twoPI;
82+
83+
return (a0 >= s0 && a0 <= s1) || (a1 >= s0 && a1 <= s1);
84+
}
85+
86+
/**
87+
* is pt (r,a) inside sector?
88+
*
89+
* @param {number} r : pt's radial coordinate
90+
* @param {number} a : pt's angular coordinate in *radians*
91+
* @param {2-item array} rBnds : sector's radial bounds
92+
* @param {2-item array} aBnds : sector's angular bounds in *radians*
93+
* @return {boolean}
94+
*/
95+
function isPtInsideSector(r, a, rBnds, aBnds) {
96+
if(!isAngleInsideSector(a, aBnds)) return false;
97+
98+
var r0, r1;
99+
100+
if(rBnds[0] < rBnds[1]) {
101+
r0 = rBnds[0];
102+
r1 = rBnds[1];
103+
} else {
104+
r0 = rBnds[1];
105+
r1 = rBnds[0];
106+
}
107+
108+
return r >= r0 && r <= r1;
109+
}
110+
111+
// common to pathArc, pathSector and pathAnnulus
112+
function _path(r0, r1, a0, a1, cx, cy, isClosed) {
113+
cx = cx || 0;
114+
cy = cy || 0;
115+
116+
var isCircle = isFullCircle([a0, a1]);
117+
var aStart, aMid, aEnd;
118+
var rStart, rEnd;
119+
120+
if(isCircle) {
121+
aStart = 0;
122+
aMid = PI;
123+
aEnd = twoPI;
124+
} else {
125+
if(a0 < a1) {
126+
aStart = a0;
127+
aEnd = a1;
128+
} else {
129+
aStart = a1;
130+
aEnd = a0;
131+
}
132+
}
133+
134+
if(r0 < r1) {
135+
rStart = r0;
136+
rEnd = r1;
137+
} else {
138+
rStart = r1;
139+
rEnd = r0;
140+
}
141+
142+
// N.B. svg coordinates here, where y increases downward
143+
function pt(r, a) {
144+
return [r * Math.cos(a) + cx, cy - r * Math.sin(a)];
145+
}
146+
147+
var largeArc = Math.abs(aEnd - aStart) <= PI ? 0 : 1;
148+
function arc(r, a, cw) {
149+
return 'A' + [r, r] + ' ' + [0, largeArc, cw] + ' ' + pt(r, a);
150+
}
151+
152+
var p;
153+
154+
if(isCircle) {
155+
if(rStart === null) {
156+
p = 'M' + pt(rEnd, aStart) +
157+
arc(rEnd, aMid, 0) +
158+
arc(rEnd, aEnd, 0) + 'Z';
159+
} else {
160+
p = 'M' + pt(rStart, aStart) +
161+
arc(rStart, aMid, 0) +
162+
arc(rStart, aEnd, 0) + 'Z' +
163+
'M' + pt(rEnd, aStart) +
164+
arc(rEnd, aMid, 1) +
165+
arc(rEnd, aEnd, 1) + 'Z';
166+
}
167+
} else {
168+
if(rStart === null) {
169+
p = 'M' + pt(rEnd, aStart) + arc(rEnd, aEnd, 0);
170+
if(isClosed) p += 'L0,0Z';
171+
} else {
172+
p = 'M' + pt(rStart, aStart) +
173+
'L' + pt(rEnd, aStart) +
174+
arc(rEnd, aEnd, 0) +
175+
'L' + pt(rStart, aEnd) +
176+
arc(rStart, aStart, 1) + 'Z';
177+
}
178+
}
179+
180+
return p;
181+
}
182+
183+
/**
184+
* path an arc
185+
*
186+
* @param {number} r : radius
187+
* @param {number} a0 : first angular coordinate in *radians*
188+
* @param {number} a1 : second angular coordinate in *radians*
189+
* @param {number (optional)} cx : x coordinate of center
190+
* @param {number (optional)} cy : y coordinate of center
191+
* @return {string} svg path
192+
*/
193+
function pathArc(r, a0, a1, cx, cy) {
194+
return _path(null, r, a0, a1, cx, cy, 0);
195+
}
196+
197+
/**
198+
* path a sector
199+
*
200+
* @param {number} r : radius
201+
* @param {number} a0 : first angular coordinate in *radians*
202+
* @param {number} a1 : second angular coordinate in *radians*
203+
* @param {number (optional)} cx : x coordinate of center
204+
* @param {number (optional)} cy : y coordinate of center
205+
* @return {string} svg path
206+
*/
207+
function pathSector(r, a0, a1, cx, cy) {
208+
return _path(null, r, a0, a1, cx, cy, 1);
209+
}
210+
211+
/**
212+
* path an annulus
213+
*
214+
* @param {number} r0 : first radial coordinate
215+
* @param {number} r1 : second radial coordinate
216+
* @param {number} a0 : first angular coordinate in *radians*
217+
* @param {number} a1 : second angular coordinate in *radians*
218+
* @param {number (optional)} cx : x coordinate of center
219+
* @param {number (optional)} cy : y coordinate of center
220+
* @return {string} svg path
221+
*/
222+
function pathAnnulus(r0, r1, a0, a1, cx, cy) {
223+
return _path(r0, r1, a0, a1, cx, cy, 1);
224+
}
30225

31-
exports.isFullCircle = function(sector) {
32-
var arc = Math.abs(sector[1] - sector[0]);
33-
return arc === 360;
226+
module.exports = {
227+
deg2rad: deg2rad,
228+
rad2deg: rad2deg,
229+
angleDelta: angleDelta,
230+
angleDist: angleDist,
231+
isFullCircle: isFullCircle,
232+
isAngleInsideSector: isAngleInsideSector,
233+
isPtInsideSector: isPtInsideSector,
234+
pathArc: pathArc,
235+
pathSector: pathSector,
236+
pathAnnulus: pathAnnulus
34237
};

Diff for: src/lib/coerce.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var colorscaleNames = Object.keys(require('../components/colorscale/scales'));
1818
var nestedProperty = require('./nested_property');
1919
var counterRegex = require('./regex').counter;
2020
var DESELECTDIM = require('../constants/interactions').DESELECTDIM;
21-
var wrap180 = require('./angles').wrap180;
21+
var modHalf = require('./mod').modHalf;
2222
var isArrayOrTypedArray = require('./is_array').isArrayOrTypedArray;
2323

2424
exports.valObjectMeta = {
@@ -186,7 +186,7 @@ exports.valObjectMeta = {
186186
coerceFunction: function(v, propOut, dflt) {
187187
if(v === 'auto') propOut.set('auto');
188188
else if(!isNumeric(v)) propOut.set(dflt);
189-
else propOut.set(wrap180(+v));
189+
else propOut.set(modHalf(+v, 360));
190190
}
191191
},
192192
subplotid: {

Diff for: src/lib/dates.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ var d3 = require('d3');
1313
var isNumeric = require('fast-isnumeric');
1414

1515
var Loggers = require('./loggers');
16-
var mod = require('./mod');
16+
var mod = require('./mod').mod;
1717

1818
var constants = require('../constants/numerical');
1919
var BADNUM = constants.BADNUM;

Diff for: src/lib/geometry2d.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
'use strict';
1010

11-
var mod = require('./mod');
11+
var mod = require('./mod').mod;
1212

1313
/*
1414
* look for intersection of two line segments

Diff for: src/lib/index.js

+15-5
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@ lib.nestedProperty = require('./nested_property');
2222
lib.keyedContainer = require('./keyed_container');
2323
lib.relativeAttr = require('./relative_attr');
2424
lib.isPlainObject = require('./is_plain_object');
25-
lib.mod = require('./mod');
2625
lib.toLogRange = require('./to_log_range');
2726
lib.relinkPrivateKeys = require('./relink_private');
2827
lib.ensureArray = require('./ensure_array');
2928

29+
var modModule = require('./mod');
30+
lib.mod = modModule.mod;
31+
lib.modHalf = modModule.modHalf;
32+
3033
var isArrayModule = require('./is_array');
3134
lib.isTypedArray = isArrayModule.isTypedArray;
3235
lib.isArrayOrTypedArray = isArrayModule.isArrayOrTypedArray;
@@ -62,6 +65,7 @@ lib.sorterAsc = searchModule.sorterAsc;
6265
lib.sorterDes = searchModule.sorterDes;
6366
lib.distinctVals = searchModule.distinctVals;
6467
lib.roundUp = searchModule.roundUp;
68+
lib.findIndexOfMin = searchModule.findIndexOfMin;
6569

6670
var statsModule = require('./stats');
6771
lib.aggNums = statsModule.aggNums;
@@ -85,9 +89,14 @@ lib.apply2DTransform2 = matrixModule.apply2DTransform2;
8589
var anglesModule = require('./angles');
8690
lib.deg2rad = anglesModule.deg2rad;
8791
lib.rad2deg = anglesModule.rad2deg;
88-
lib.wrap360 = anglesModule.wrap360;
89-
lib.wrap180 = anglesModule.wrap180;
92+
lib.angleDelta = anglesModule.angleDelta;
93+
lib.angleDist = anglesModule.angleDist;
9094
lib.isFullCircle = anglesModule.isFullCircle;
95+
lib.isAngleInsideSector = anglesModule.isAngleInsideSector;
96+
lib.isPtInsideSector = anglesModule.isPtInsideSector;
97+
lib.pathArc = anglesModule.pathArc;
98+
lib.pathSector = anglesModule.pathSector;
99+
lib.pathAnnulus = anglesModule.pathAnnulus;
91100

92101
var geom2dModule = require('./geometry2d');
93102
lib.segmentsIntersect = geom2dModule.segmentsIntersect;
@@ -702,7 +711,7 @@ lib.isD3Selection = function(obj) {
702711
*
703712
* @param {d3 selection} parent : parent selection of the element in question
704713
* @param {string} nodeType : node type of element to append
705-
* @param {string} className : class name of element in question
714+
* @param {string} className (optional) : class name of element in question
706715
* @param {fn} enterFn (optional) : optional fn applied to entering elements only
707716
* @return {d3 selection} selection of new layer
708717
*
@@ -729,7 +738,8 @@ lib.ensureSingle = function(parent, nodeType, className, enterFn) {
729738
var sel = parent.select(nodeType + (className ? '.' + className : ''));
730739
if(sel.size()) return sel;
731740

732-
var layer = parent.append(nodeType).classed(className, true);
741+
var layer = parent.append(nodeType);
742+
if(className) layer.classed(className, true);
733743
if(enterFn) layer.call(enterFn);
734744

735745
return layer;

Diff for: src/lib/mod.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,22 @@
1212
* sanitized modulus function that always returns in the range [0, d)
1313
* rather than (-d, 0] if v is negative
1414
*/
15-
module.exports = function mod(v, d) {
15+
function mod(v, d) {
1616
var out = v % d;
1717
return out < 0 ? out + d : out;
18+
}
19+
20+
/**
21+
* sanitized modulus function that always returns in the range [-d/2, d/2]
22+
* rather than (-d, 0] if v is negative
23+
*/
24+
function modHalf(v, d) {
25+
return Math.abs(v) > (d / 2) ?
26+
v - Math.round(v / d) * d :
27+
v;
28+
}
29+
30+
module.exports = {
31+
mod: mod,
32+
modHalf: modHalf
1833
};

0 commit comments

Comments
 (0)