diff --git a/src/traces/parcoords/axisbrush.js b/src/traces/parcoords/axisbrush.js index 1e7cc8fcc65..7a47db672a0 100644 --- a/src/traces/parcoords/axisbrush.js +++ b/src/traces/parcoords/axisbrush.js @@ -266,12 +266,6 @@ function attachDragBehavior(selection) { s.newExtent = [s.startExtent, d.unitToPaddedPx.invert(y)].sort(sortAsc); } - // take care of the parcoords axis height constraint: bar can't breach it - var bottomViolation = Math.max(0, -s.newExtent[0]); - var topViolation = Math.max(0, s.newExtent[1] - 1); - s.newExtent[0] += bottomViolation; - s.newExtent[1] -= topViolation; - d.brush.filterSpecified = true; s.extent = s.stayingIntervals.concat([s.newExtent]); s.brushCallback(d); diff --git a/src/traces/parcoords/lines.js b/src/traces/parcoords/lines.js index 08d6cafd814..2646165aa87 100644 --- a/src/traces/parcoords/lines.js +++ b/src/traces/parcoords/lines.js @@ -225,9 +225,11 @@ function makeItem(model, leftmost, rightmost, itemNumber, i0, i1, x, y, panelSiz function expandedPixelRange(bounds) { var dh = maskHeight - 1; + var a = Math.max(0, Math.floor(bounds[0] * dh), 0); + var b = Math.min(dh, Math.ceil(bounds[1] * dh), dh); return [ - Math.max(0, Math.floor(bounds[0] * dh), 0), - Math.min(dh, Math.ceil(bounds[1] * dh), dh) + Math.min(a, b), + Math.max(a, b) ]; } diff --git a/test/image/baselines/gl2d_parcoords_out-of-range_selected-above-below.png b/test/image/baselines/gl2d_parcoords_out-of-range_selected-above-below.png new file mode 100644 index 00000000000..0dccfb563c8 Binary files /dev/null and b/test/image/baselines/gl2d_parcoords_out-of-range_selected-above-below.png differ diff --git a/test/image/mocks/gl2d_parcoords_out-of-range_selected-above-below.json b/test/image/mocks/gl2d_parcoords_out-of-range_selected-above-below.json new file mode 100644 index 00000000000..0c50eaab3d2 --- /dev/null +++ b/test/image/mocks/gl2d_parcoords_out-of-range_selected-above-below.json @@ -0,0 +1,821 @@ +{ + "data": [ + { + "type": "parcoords", + "dimensions": [ + { + "label": "A", + "constraintrange": [ + [ + 20, + 40 + ], + [ + 80, + 100 + ], + [ + 200, + 220 + ] + ], + "range": [ + 30, + 210 + ], + "values": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255 + ] + }, + { + "label": "B", + "values": [ + "1.00000", + "0.99992", + "0.99970", + "0.99932", + "0.99880", + "0.99812", + "0.99729", + "0.99631", + "0.99518", + "0.99391", + "0.99248", + "0.99090", + "0.98918", + "0.98730", + "0.98528", + "0.98311", + "0.98079", + "0.97832", + "0.97570", + "0.97294", + "0.97003", + "0.96698", + "0.96378", + "0.96043", + "0.95694", + "0.95331", + "0.94953", + "0.94561", + "0.94154", + "0.93734", + "0.93299", + "0.92851", + "0.92388", + "0.91911", + "0.91421", + "0.90917", + "0.90399", + "0.89867", + "0.89322", + "0.88764", + "0.88192", + "0.87607", + "0.87009", + "0.86397", + "0.85773", + "0.85136", + "0.84485", + "0.83822", + "0.83147", + "0.82459", + "0.81758", + "0.81046", + "0.80321", + "0.79584", + "0.78835", + "0.78074", + "0.77301", + "0.76517", + "0.75721", + "0.74914", + "0.74095", + "0.73265", + "0.72425", + "0.71573", + "0.70711", + "0.69838", + "0.68954", + "0.68060", + "0.67156", + "0.66242", + "0.65317", + "0.64383", + "0.63439", + "0.62486", + "0.61523", + "0.60551", + "0.59570", + "0.58580", + "0.57581", + "0.56573", + "0.55557", + "0.54532", + "0.53500", + "0.52459", + "0.51410", + "0.50354", + "0.49290", + "0.48218", + "0.47140", + "0.46054", + "0.44961", + "0.43862", + "0.42756", + "0.41643", + "0.40524", + "0.39399", + "0.38268", + "0.37132", + "0.35990", + "0.34842", + "0.33689", + "0.32531", + "0.31368", + "0.30201", + "0.29028", + "0.27852", + "0.26671", + "0.25487", + "0.24298", + "0.23106", + "0.21910", + "0.20711", + "0.19509", + "0.18304", + "0.17096", + "0.15886", + "0.14673", + "0.13458", + "0.12241", + "0.11022", + "0.09802", + "0.08580", + "0.07356", + "0.06132", + "0.04907", + "0.03681", + "0.02454", + "0.01227", + "0.00000", + "-0.01227", + "-0.02454", + "-0.03681", + "-0.04907", + "-0.06132", + "-0.07356", + "-0.08580", + "-0.09802", + "-0.11022", + "-0.12241", + "-0.13458", + "-0.14673", + "-0.15886", + "-0.17096", + "-0.18304", + "-0.19509", + "-0.20711", + "-0.21910", + "-0.23106", + "-0.24298", + "-0.25487", + "-0.26671", + "-0.27852", + "-0.29028", + "-0.30201", + "-0.31368", + "-0.32531", + "-0.33689", + "-0.34842", + "-0.35990", + "-0.37132", + "-0.38268", + "-0.39399", + "-0.40524", + "-0.41643", + "-0.42756", + "-0.43862", + "-0.44961", + "-0.46054", + "-0.47140", + "-0.48218", + "-0.49290", + "-0.50354", + "-0.51410", + "-0.52459", + "-0.53500", + "-0.54532", + "-0.55557", + "-0.56573", + "-0.57581", + "-0.58580", + "-0.59570", + "-0.60551", + "-0.61523", + "-0.62486", + "-0.63439", + "-0.64383", + "-0.65317", + "-0.66242", + "-0.67156", + "-0.68060", + "-0.68954", + "-0.69838", + "-0.70711", + "-0.71573", + "-0.72425", + "-0.73265", + "-0.74095", + "-0.74914", + "-0.75721", + "-0.76517", + "-0.77301", + "-0.78074", + "-0.78835", + "-0.79584", + "-0.80321", + "-0.81046", + "-0.81758", + "-0.82459", + "-0.83147", + "-0.83822", + "-0.84485", + "-0.85136", + "-0.85773", + "-0.86397", + "-0.87009", + "-0.87607", + "-0.88192", + "-0.88764", + "-0.89322", + "-0.89867", + "-0.90399", + "-0.90917", + "-0.91421", + "-0.91911", + "-0.92388", + "-0.92851", + "-0.93299", + "-0.93734", + "-0.94154", + "-0.94561", + "-0.94953", + "-0.95331", + "-0.95694", + "-0.96043", + "-0.96378", + "-0.96698", + "-0.97003", + "-0.97294", + "-0.97570", + "-0.97832", + "-0.98079", + "-0.98311", + "-0.98528", + "-0.98730", + "-0.98918", + "-0.99090", + "-0.99248", + "-0.99391", + "-0.99518", + "-0.99631", + "-0.99729", + "-0.99812", + "-0.99880", + "-0.99932", + "-0.99970", + "-1.00000" + ] + } + ], + "line": { + "colorscale": "Portland", + "color": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255 + ] + } + } + ], + "layout": { + "width": 800, + "height": 800, + "margin": { + "t": 200, + "b": 200 + }, + "title": { + "text": "Should select constraintranges outside range" + } + } +} diff --git a/test/jasmine/tests/parcoords_test.js b/test/jasmine/tests/parcoords_test.js index 270143137dc..98858b374bc 100644 --- a/test/jasmine/tests/parcoords_test.js +++ b/test/jasmine/tests/parcoords_test.js @@ -40,6 +40,12 @@ function mouseTo(x, y) { mouseEvent('mouseover', x, y); } +function mostOfDrag(x1, y1, x2, y2) { + mouseTo(x1, y1); + mouseEvent('mousedown', x1, y1); + mouseEvent('mousemove', x2, y2); +} + function purgeGraphDiv(done) { var gd = d3.select('.js-plotly-plot').node(); if(gd) Plotly.purge(gd); @@ -1301,7 +1307,7 @@ describe('parcoords react more attributes', function() { }); }); -describe('parcoords constraint interactions', function() { +describe('parcoords constraint interactions - without defined axis ranges', function() { var gd, initialDashArray0, initialDashArray1; function initialFigure() { @@ -1365,11 +1371,6 @@ describe('parcoords constraint interactions', function() { return highlight.attributes['stroke-dasharray'].value.split(',').map(Number); } - function mostOfDrag(x1, y1, x2, y2) { - mouseTo(x1, y1); - mouseEvent('mousedown', x1, y1); - mouseEvent('mousemove', x2, y2); - } function checkDashCount(dashArray, intervals) { // no-selection dasharrays have 2 entries: @@ -1496,8 +1497,7 @@ describe('parcoords constraint interactions', function() { .then(delay(noSnapDelay)) .then(function() { expect(getDashArray(1)).toBeCloseToArray(newDashArray); - // TODO: ideally this would get clipped to [0, 9]... - expect(gd.data[0].dimensions[1].constraintrange).toBeCloseToArray([-0.1020, 9]); + expect(gd.data[0].dimensions[1].constraintrange).toBeCloseToArray([-2.913369429404415, 9]); }) .catch(failTest) .then(done); @@ -1540,3 +1540,88 @@ describe('parcoords constraint interactions', function() { .then(done); }); }); + +describe('parcoords constraint interactions - with defined axis ranges', function() { + function initialFigure() { + return { + data: [{ + type: 'parcoords', + dimensions: [{ + values: [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + + }, { + values: [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9], + tickvals: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + ticktext: ['a', 'b', 'c', 'd', 'e', 'f', 'i', 'j', 'k', 'l'], + range: [3, 7], + constraintrange: [4, 6] + }] + }], + layout: { + width: 400, + height: 400, + margin: {t: 100, b: 100, l: 100, r: 100} + } + }; + } + + var gd; + var initialSnapDuration; + var shortenedSnapDuration = 20; + var noSnapDelay = 20; + beforeAll(function() { + initialSnapDuration = PC.bar.snapDuration; + PC.bar.snapDuration = shortenedSnapDuration; + }); + + afterAll(function() { + purgeGraphDiv(); + PC.bar.snapDuration = initialSnapDuration; + }); + + beforeEach(function(done) { + var hasGD = !!gd; + if(!hasGD) gd = createGraphDiv(); + + Plotly.react(gd, initialFigure()) + .catch(failTest) + .then(done); + }); + + it('@noCI @gl updates constraints above and below axis ranges', function(done) { + expect(gd.data[0].dimensions[1].constraintrange).toBeCloseToArray([4, 6]); + + var x = 295; + + // first: move above range + mostOfDrag(x, 200, x, 100); + mouseEvent('mouseup', x, 100); + delay(noSnapDelay)() + .then(function() { + expect(gd.data[0].dimensions[1].constraintrange).toBeCloseToArray([5.75, 8.25]); + // move back + mostOfDrag(x, 110, x, 210); + mouseEvent('mouseup', x, 210); + }) + .then(delay(noSnapDelay)) + .then(function() { + expect(gd.data[0].dimensions[1].constraintrange).toBeCloseToArray([3.75, 6.25]); + // move below range + mostOfDrag(x, 200, x, 300); + mouseEvent('mouseup', x, 300); + }) + .then(delay(noSnapDelay)) + .then(function() { + expect(gd.data[0].dimensions[1].constraintrange).toBeCloseToArray([1.75, 4.25]); + // move back + mostOfDrag(x, 290, x, 190); + mouseEvent('mouseup', x, 190); + }) + .then(delay(noSnapDelay)) + .then(function() { + expect(gd.data[0].dimensions[1].constraintrange).toBeCloseToArray([3.75, 6.25]); + }) + .catch(failTest) + .then(done); + }); +});