Skip to content

Commit e34212b

Browse files
committed
Draw bar instead of anchor cross when moving hybrid-sized shapes [2038]
- When moving a shape that is pixel-sized in one dimension and data-sized or paper-sized in the other dimension a centered vertical or horizontal bar is rendered instead of the anchor cross. - Thereby fix a bug in `extractPathCoords` to return numerical values instead of numbers represented as strings. - Also introduce the statistical `midRange` function in lib.
1 parent c686473 commit e34212b

File tree

5 files changed

+74
-23
lines changed

5 files changed

+74
-23
lines changed

src/components/shapes/draw.js

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -447,24 +447,39 @@ function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer) {
447447
.classed('visual-cue', true);
448448

449449
// Update
450-
var anchorX = x2p(xPixelSized ?
451-
shapeOptions.xanchor :
452-
isNotPath ?
453-
shapeOptions.x0 :
454-
helpers.extractPathCoords(shapeOptions.path, constants.paramIsX)[0]);
455-
var anchorY = y2p(yPixelSized ?
456-
shapeOptions.yanchor :
457-
isNotPath ?
458-
shapeOptions.y0 :
459-
helpers.extractPathCoords(shapeOptions.path, constants.paramIsY)[0]);
460-
461-
anchorX = helpers.transformPosForSharpStrokeRendering(anchorX, strokeWidth);
462-
anchorY = helpers.transformPosForSharpStrokeRendering(anchorY, strokeWidth);
463-
464-
var crossHairPath = 'M' + (anchorX - 1) + ',' + (anchorY - 1) +
465-
'h-8v2h8 v8h2v-8 h8v-2h-8 v-8h-2 Z';
466-
467-
visualCues.attr('d', crossHairPath);
450+
var posX = x2p(
451+
xPixelSized ?
452+
shapeOptions.xanchor :
453+
Lib.midRange(
454+
isNotPath ?
455+
[shapeOptions.x0, shapeOptions.x1] :
456+
helpers.extractPathCoords(shapeOptions.path, constants.paramIsX))
457+
);
458+
var posY = y2p(
459+
yPixelSized ?
460+
shapeOptions.yanchor :
461+
Lib.midRange(
462+
isNotPath ?
463+
[shapeOptions.y0, shapeOptions.y1] :
464+
helpers.extractPathCoords(shapeOptions.path, constants.paramIsY))
465+
);
466+
467+
posX = helpers.roundPositionForSharpStrokeRendering(posX, strokeWidth);
468+
posY = helpers.roundPositionForSharpStrokeRendering(posY, strokeWidth);
469+
470+
if(xPixelSized && yPixelSized) {
471+
var crossPath = 'M' + (posX - 1 - strokeWidth) + ',' + (posY - 1 - strokeWidth) +
472+
'h-8v2h8 v8h2v-8 h8v-2h-8 v-8h-2 Z';
473+
visualCues.attr('d', crossPath);
474+
} else if(xPixelSized) {
475+
var vBarPath = 'M' + (posX - 1 - strokeWidth) + ',' + (posY - 9 - strokeWidth) +
476+
'v18 h2 v-18 Z';
477+
visualCues.attr('d', vBarPath);
478+
} else {
479+
var hBarPath = 'M' + (posX - 9 - strokeWidth) + ',' + (posY - 1 - strokeWidth) +
480+
'h18 v2 h-18 Z';
481+
visualCues.attr('d', hBarPath);
482+
}
468483
}
469484
}
470485

src/components/shapes/helpers.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
var constants = require('./constants');
1313

14+
var Lib = require('../../lib');
15+
1416
// special position conversion functions... category axis positions can't be
1517
// specified by their data values, because they don't make a continuous mapping.
1618
// so these have to be specified in terms of the category serial numbers,
@@ -50,7 +52,7 @@ exports.extractPathCoords = function(path, paramsToUse) {
5052
var params = segment.substr(1).match(constants.paramRE);
5153
if(!params || params.length < relevantParamIdx) return;
5254

53-
extractedCoordinates.push(params[relevantParamIdx]);
55+
extractedCoordinates.push(Lib.cleanNumber(params[relevantParamIdx]));
5456
});
5557

5658
return extractedCoordinates;
@@ -98,7 +100,7 @@ exports.getPixelToData = function(gd, axis, isVertical) {
98100
};
99101

100102
/**
101-
* Based on the given stroke width, transforms the passed
103+
* Based on the given stroke width, rounds the passed
102104
* position value to represent either a full or half pixel.
103105
*
104106
* In case of an odd stroke width (e.g. 1), this measure ensures
@@ -113,7 +115,7 @@ exports.getPixelToData = function(gd, axis, isVertical) {
113115
* @param {number} strokeWidth The stroke width
114116
* @returns {number} either an integer or a .5 decimal number
115117
*/
116-
exports.transformPosForSharpStrokeRendering = function(pos, strokeWidth) {
118+
exports.roundPositionForSharpStrokeRendering = function(pos, strokeWidth) {
117119
var strokeWidthIsOdd = Math.round(strokeWidth % 2) === 1;
118120
var posValAsInt = Math.round(pos);
119121

src/lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ var statsModule = require('./stats');
6767
lib.aggNums = statsModule.aggNums;
6868
lib.len = statsModule.len;
6969
lib.mean = statsModule.mean;
70+
lib.midRange = statsModule.midRange;
7071
lib.variance = statsModule.variance;
7172
lib.stdev = statsModule.stdev;
7273
lib.interp = statsModule.interp;

src/lib/stats.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ exports.mean = function(data, len) {
5656
return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len;
5757
};
5858

59+
exports.midRange = function(numArr) {
60+
if(numArr === undefined || numArr.length === 0) return undefined;
61+
return (exports.aggNums(Math.max, null, numArr) + exports.aggNums(Math.min, null, numArr)) / 2;
62+
};
63+
5964
exports.variance = function(data, len, mean) {
6065
if(!len) len = exports.len(data);
6166
if(!isNumeric(mean)) mean = exports.mean(data, len);

test/jasmine/tests/lib_test.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,12 @@ describe('Test lib.js:', function() {
108108
});
109109

110110
describe('mean() should', function() {
111-
it('toss out non-numerics (strings):', function() {
111+
it('toss out non-numerics (strings)', function() {
112112
var input = [1, 2, 'apple', 'orange'],
113113
res = Lib.mean(input);
114114
expect(res).toEqual(1.5);
115115
});
116-
it('toss out non-numerics (NaN):', function() {
116+
it('toss out non-numerics (NaN)', function() {
117117
var input = [1, 2, NaN],
118118
res = Lib.mean(input);
119119
expect(res).toEqual(1.5);
@@ -125,6 +125,34 @@ describe('Test lib.js:', function() {
125125
});
126126
});
127127

128+
describe('midRange() should', function() {
129+
it('should calculate the arithmetic mean of the maximum and minimum value of a given array', function() {
130+
var input = [1, 5.5, 6, 15, 10, 13],
131+
res = Lib.midRange(input);
132+
expect(res).toEqual(8);
133+
});
134+
it('toss out non-numerics (strings)', function() {
135+
var input = [1, 2, 'apple', 'orange'],
136+
res = Lib.midRange(input);
137+
expect(res).toEqual(1.5);
138+
});
139+
it('toss out non-numerics (NaN)', function() {
140+
var input = [1, 2, NaN],
141+
res = Lib.midRange(input);
142+
expect(res).toEqual(1.5);
143+
});
144+
it('should be able to deal with array of length 1', function() {
145+
var input = [10],
146+
res = Lib.midRange(input);
147+
expect(res).toEqual(10);
148+
});
149+
it('should return undefined for an empty array', function() {
150+
var input = [],
151+
res = Lib.midRange(input);
152+
expect(res).toBeUndefined();
153+
});
154+
});
155+
128156
describe('variance() should', function() {
129157
it('return 0 on input [2, 2, 2, 2, 2]:', function() {
130158
var input = [2, 2, 2, 2],

0 commit comments

Comments
 (0)