From 81ff960bd542ab892f0d0fd609b794888db463d0 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Tue, 5 Feb 2019 22:59:24 +0200 Subject: [PATCH 01/16] allow any borders to be skipped --- src/elements/element.rectangle.js | 42 +++++++------------- test/specs/element.rectangle.tests.js | 55 +++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 36 deletions(-) diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index fe3702cda4e..11929724ced 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -88,21 +88,11 @@ module.exports = Element.extend({ var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom)); borderWidth = borderWidth > barSize ? barSize : borderWidth; var halfStroke = borderWidth / 2; - // Adjust borderWidth when bar top position is near vm.base(zero). - var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0); - var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0); - var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0); - var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0); - // not become a vertical line? - if (borderLeft !== borderRight) { - top = borderTop; - bottom = borderBottom; - } - // not become a horizontal line? - if (borderTop !== borderBottom) { - left = borderLeft; - right = borderRight; - } + // Adjust coordinates so borders stay inside + left = left + (borderSkipped.indexOf('left') < 0 ? halfStroke * signX : 0); + right = right + (borderSkipped.indexOf('right') < 0 ? -halfStroke * signX : 0); + top = top + (borderSkipped.indexOf('top') < 0 ? halfStroke * signY : 0); + bottom = bottom + (borderSkipped.indexOf('bottom') < 0 ? -halfStroke * signY : 0); } ctx.beginPath(); @@ -122,25 +112,21 @@ module.exports = Element.extend({ // Find first (starting) corner with fallback to 'bottom' var borders = ['bottom', 'left', 'top', 'right']; - var startCorner = borders.indexOf(borderSkipped, 0); - if (startCorner === -1) { - startCorner = 0; - } - - function cornerAt(index) { - return corners[(startCorner + index) % 4]; - } // Draw rectangle from 'startCorner' - var corner = cornerAt(0); + var corner = corners[3]; ctx.moveTo(corner[0], corner[1]); - for (var i = 1; i < 4; i++) { - corner = cornerAt(i); - ctx.lineTo(corner[0], corner[1]); + for (var i = 0; i < 4; ++i) { + corner = corners[i]; + if (borderSkipped.indexOf(borders[i]) > -1) { + ctx.moveTo(corner[0], corner[1]); + } else { + ctx.lineTo(corner[0], corner[1]); + } } - ctx.fill(); + ctx.fillRect(left, bottom, right - left, top - bottom); if (borderWidth) { ctx.stroke(); } diff --git a/test/specs/element.rectangle.tests.js b/test/specs/element.rectangle.tests.js index d8d08476f49..293f75ffbb4 100644 --- a/test/specs/element.rectangle.tests.js +++ b/test/specs/element.rectangle.tests.js @@ -219,6 +219,9 @@ describe('Rectangle element tests', function() { }, { name: 'setLineWidth', args: [1] + }, { + name: 'moveTo', + args: [11.5, 0] }, { name: 'moveTo', args: [8.5, 0] @@ -232,8 +235,8 @@ describe('Rectangle element tests', function() { name: 'lineTo', args: [11.5, 0] }, { - name: 'fill', - args: [], + name: 'fillRect', + args: [8.5, 0, 3, 14.5], }, { name: 'stroke', args: [] @@ -275,6 +278,9 @@ describe('Rectangle element tests', function() { }, { name: 'setLineWidth', args: [undefined] + }, { + name: 'moveTo', + args: [12, 0] }, { name: 'moveTo', args: [8, 0] @@ -288,8 +294,8 @@ describe('Rectangle element tests', function() { name: 'lineTo', args: [12, 0] }, { - name: 'fill', - args: [], + name: 'fillRect', + args: [8, 0, 4, 15], }]); }); @@ -311,12 +317,13 @@ describe('Rectangle element tests', function() { rectangle.draw(); - var drawCalls = rectangle._view.ctx.getCalls().splice(4, 4); + var drawCalls = rectangle._view.ctx.getCalls().splice(4, 5); expect(drawCalls).toEqual(expectedDrawCalls); } it ('should draw correctly respecting "borderSkipped" == "bottom"', function() { testBorderSkipped ('bottom', [ + {name: 'moveTo', args: [12, 0]}, {name: 'moveTo', args: [8, 0]}, {name: 'lineTo', args: [8, 15]}, {name: 'lineTo', args: [12, 15]}, @@ -326,19 +333,21 @@ describe('Rectangle element tests', function() { it ('should draw correctly respecting "borderSkipped" == "left"', function() { testBorderSkipped ('left', [ + {name: 'moveTo', args: [12, 0]}, + {name: 'lineTo', args: [8, 0]}, {name: 'moveTo', args: [8, 15]}, {name: 'lineTo', args: [12, 15]}, {name: 'lineTo', args: [12, 0]}, - {name: 'lineTo', args: [8, 0]}, ]); }); it ('should draw correctly respecting "borderSkipped" == "top"', function() { testBorderSkipped ('top', [ - {name: 'moveTo', args: [12, 15]}, - {name: 'lineTo', args: [12, 0]}, + {name: 'moveTo', args: [12, 0]}, {name: 'lineTo', args: [8, 0]}, {name: 'lineTo', args: [8, 15]}, + {name: 'moveTo', args: [12, 15]}, + {name: 'lineTo', args: [12, 0]}, ]); }); @@ -348,7 +357,37 @@ describe('Rectangle element tests', function() { {name: 'lineTo', args: [8, 0]}, {name: 'lineTo', args: [8, 15]}, {name: 'lineTo', args: [12, 15]}, + {name: 'moveTo', args: [12, 0]}, ]); }); + it ('should draw correctly respecting "borderSkipped" == "none"', function() { + testBorderSkipped ('none', [ + {name: 'moveTo', args: [12, 0]}, + {name: 'lineTo', args: [8, 0]}, + {name: 'lineTo', args: [8, 15]}, + {name: 'lineTo', args: [12, 15]}, + {name: 'lineTo', args: [12, 0]}, + ]); + }); + + it ('should draw correctly respecting "borderSkipped" == "left right"', function() { + testBorderSkipped ('left right', [ + {name: 'moveTo', args: [12, 0]}, + {name: 'lineTo', args: [8, 0]}, + {name: 'moveTo', args: [8, 15]}, + {name: 'lineTo', args: [12, 15]}, + {name: 'moveTo', args: [12, 0]}, + ]); + }); + + it ('should draw correctly respecting "borderSkipped" == "top bottom"', function() { + testBorderSkipped ('top bottom', [ + {name: 'moveTo', args: [12, 0]}, + {name: 'moveTo', args: [8, 0]}, + {name: 'lineTo', args: [8, 15]}, + {name: 'moveTo', args: [12, 15]}, + {name: 'lineTo', args: [12, 0]}, + ]); + }); }); From 9c993a6f6cb1171b16a552494788dd85304dc8b8 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Wed, 6 Feb 2019 13:09:28 +0200 Subject: [PATCH 02/16] object instead of array, support borderWidth object as well --- src/elements/element.rectangle.js | 141 ++++++++++++++-------- test/specs/element.rectangle.tests.js | 162 +++++++++++++++----------- 2 files changed, 187 insertions(+), 116 deletions(-) diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index 11929724ced..623043a232a 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -2,8 +2,10 @@ var defaults = require('../core/core.defaults'); var Element = require('../core/core.element'); +var helpers = require('../helpers/index'); var defaultColor = defaults.global.defaultColor; +var valueOrDefault = helpers.valueOrDefault; defaults._set('global', { elements: { @@ -58,8 +60,8 @@ module.exports = Element.extend({ draw: function() { var ctx = this._chart.ctx; var vm = this._view; - var left, right, top, bottom, signX, signY, borderSkipped; var borderWidth = vm.borderWidth; + var left, right, top, bottom, signX, signY, borderSkipped, x, y; if (!vm.horizontal) { // bar @@ -69,7 +71,7 @@ module.exports = Element.extend({ bottom = vm.base; signX = 1; signY = bottom > top ? 1 : -1; - borderSkipped = vm.borderSkipped || 'bottom'; + borderSkipped = valueOrDefault(vm.borderSkipped, {bottom: true}) || {}; } else { // horizontal bar left = vm.base; @@ -78,57 +80,102 @@ module.exports = Element.extend({ bottom = vm.y + vm.height / 2; signX = right > left ? 1 : -1; signY = 1; - borderSkipped = vm.borderSkipped || 'left'; + borderSkipped = valueOrDefault(vm.borderSkipped, {left: true}) || {}; } - // Canvas doesn't allow us to stroke inside the width so we can - // adjust the sizes to fit if we're setting a stroke on the line - if (borderWidth) { - // borderWidth shold be less than bar width and bar height. - var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom)); - borderWidth = borderWidth > barSize ? barSize : borderWidth; - var halfStroke = borderWidth / 2; - // Adjust coordinates so borders stay inside - left = left + (borderSkipped.indexOf('left') < 0 ? halfStroke * signX : 0); - right = right + (borderSkipped.indexOf('right') < 0 ? -halfStroke * signX : 0); - top = top + (borderSkipped.indexOf('top') < 0 ? halfStroke * signY : 0); - bottom = bottom + (borderSkipped.indexOf('bottom') < 0 ? -halfStroke * signY : 0); - } - - ctx.beginPath(); ctx.fillStyle = vm.backgroundColor; ctx.strokeStyle = vm.borderColor; - ctx.lineWidth = borderWidth; - - // Corner points, from bottom-left to bottom-right clockwise - // | 1 2 | - // | 0 3 | - var corners = [ - [left, bottom], - [left, top], - [right, top], - [right, bottom] - ]; - - // Find first (starting) corner with fallback to 'bottom' - var borders = ['bottom', 'left', 'top', 'right']; - - // Draw rectangle from 'startCorner' - var corner = corners[3]; - ctx.moveTo(corner[0], corner[1]); - - for (var i = 0; i < 4; ++i) { - corner = corners[i]; - if (borderSkipped.indexOf(borders[i]) > -1) { - ctx.moveTo(corner[0], corner[1]); - } else { - ctx.lineTo(corner[0], corner[1]); - } - } - ctx.fillRect(left, bottom, right - left, top - bottom); if (borderWidth) { - ctx.stroke(); + if (typeof borderSkipped === 'string') { + borderSkipped = {}; + borderSkipped[vm.borderSkipped] = true; + } + + var maxWidth = Math.abs(left - right); + var maxHeight = Math.abs(top - bottom); + if (!helpers.isObject(borderWidth)) { + borderWidth = { + bottom: borderSkipped.bottom ? 0 : Math.min(borderWidth, maxHeight), + left: borderSkipped.left ? 0 : Math.min(borderWidth, maxWidth), + top: borderSkipped.top ? 0 : Math.min(borderWidth, maxHeight), + right: borderSkipped.right ? 0 : Math.min(borderWidth, maxWidth) + }; + } + ctx.fillRect( + left + borderWidth.left * signX, + bottom - borderWidth.bottom * signY, + right - left - signX * (borderWidth.left + borderWidth.right), + top - bottom + signY * (borderWidth.top + borderWidth.bottom)); + + ctx.beginPath(); + if (borderWidth.bottom) { + y = bottom - signY * borderWidth.bottom / 2; + ctx.moveTo(right, y); + if (borderWidth.left) { + ctx.lineTo(left + signX * borderWidth.left / 2, y); + } else { + ctx.lineTo(left, y); + } + if (borderWidth.bottom !== (borderWidth.left || borderWidth.top || borderWidth.right)) { + ctx.lineWidth = borderWidth.bottom; + ctx.stroke(); + if (borderWidth.left || borderWidth.top || borderWidth.right) { + ctx.beginPath(); + } + } + } + if (borderWidth.left) { + x = left + signX * borderWidth.left / 2; + if (borderWidth.left !== borderWidth.bottom) { + ctx.moveTo(x, bottom); + } + if (borderWidth.top) { + ctx.lineTo(x, top + signY * borderWidth.top / 2); + } else { + ctx.lineTo(x, top); + } + if (borderWidth.left !== (borderWidth.top || borderWidth.right)) { + ctx.lineWidth = borderWidth.left; + ctx.stroke(); + if (borderWidth.top || borderWidth.right) { + ctx.beginPath(); + } + } + } + if (borderWidth.top) { + y = top + signY * borderWidth.top / 2; + if (borderWidth.top !== borderWidth.left) { + ctx.moveTo(left, y); + } + if (borderWidth.right) { + ctx.lineTo(right - signX * borderWidth.right / 2, y); + } else { + ctx.lineTo(right, y); + } + if (borderWidth.top !== borderWidth.right) { + ctx.lineWidth = borderWidth.top; + ctx.stroke(); + if (borderWidth.right) { + ctx.beginPath(); + } + } + } + if (borderWidth.right) { + x = right - signX * borderWidth.right / 2; + if (borderWidth.right !== borderWidth.top) { + ctx.moveTo(x, top); + } + if (borderWidth.bottom) { + ctx.lineTo(x, bottom - signY * borderWidth.bottom / 2); + } else { + ctx.lineTo(x, bottom); + } + ctx.lineWidth = borderWidth.right; + ctx.stroke(); + } + } else { + ctx.fillRect(left, bottom, right - left, top - bottom); } }, diff --git a/test/specs/element.rectangle.tests.js b/test/specs/element.rectangle.tests.js index 293f75ffbb4..a270ee96422 100644 --- a/test/specs/element.rectangle.tests.js +++ b/test/specs/element.rectangle.tests.js @@ -208,20 +208,17 @@ describe('Rectangle element tests', function() { rectangle.draw(); expect(mockContext.getCalls()).toEqual([{ - name: 'beginPath', - args: [], - }, { name: 'setFillStyle', args: ['rgb(255, 0, 0)'] }, { name: 'setStrokeStyle', args: ['rgb(0, 0, 255)'], }, { - name: 'setLineWidth', - args: [1] + name: 'fillRect', + args: [9, 0, 2, 14], }, { - name: 'moveTo', - args: [11.5, 0] + name: 'beginPath', + args: [], }, { name: 'moveTo', args: [8.5, 0] @@ -235,8 +232,8 @@ describe('Rectangle element tests', function() { name: 'lineTo', args: [11.5, 0] }, { - name: 'fillRect', - args: [8.5, 0, 3, 14.5], + name: 'setLineWidth', + args: [1] }, { name: 'stroke', args: [] @@ -267,32 +264,11 @@ describe('Rectangle element tests', function() { rectangle.draw(); expect(mockContext.getCalls()).toEqual([{ - name: 'beginPath', - args: [], - }, { name: 'setFillStyle', args: ['rgb(255, 0, 0)'] }, { name: 'setStrokeStyle', args: ['rgb(0, 0, 255)'], - }, { - name: 'setLineWidth', - args: [undefined] - }, { - name: 'moveTo', - args: [12, 0] - }, { - name: 'moveTo', - args: [8, 0] - }, { - name: 'lineTo', - args: [8, 15] - }, { - name: 'lineTo', - args: [12, 15] - }, { - name: 'lineTo', - args: [12, 0] }, { name: 'fillRect', args: [8, 0, 4, 15], @@ -307,6 +283,7 @@ describe('Rectangle element tests', function() { // Attach a view object as if we were the controller rectangle._view = { + borderWidth: 2, borderSkipped: borderSkipped, // set tested 'borderSkipped' parameter ctx: mockContext, base: 0, @@ -323,71 +300,118 @@ describe('Rectangle element tests', function() { it ('should draw correctly respecting "borderSkipped" == "bottom"', function() { testBorderSkipped ('bottom', [ - {name: 'moveTo', args: [12, 0]}, - {name: 'moveTo', args: [8, 0]}, - {name: 'lineTo', args: [8, 15]}, - {name: 'lineTo', args: [12, 15]}, - {name: 'lineTo', args: [12, 0]}, + {name: 'moveTo', args: [9, 0]}, + {name: 'lineTo', args: [9, 14]}, + {name: 'lineTo', args: [11, 14]}, + {name: 'lineTo', args: [11, 0]}, + {name: 'setLineWidth', args: [2]}, ]); }); it ('should draw correctly respecting "borderSkipped" == "left"', function() { testBorderSkipped ('left', [ - {name: 'moveTo', args: [12, 0]}, - {name: 'lineTo', args: [8, 0]}, - {name: 'moveTo', args: [8, 15]}, - {name: 'lineTo', args: [12, 15]}, - {name: 'lineTo', args: [12, 0]}, + {name: 'moveTo', args: [12, 1]}, + {name: 'lineTo', args: [8, 1]}, + {name: 'moveTo', args: [8, 14]}, + {name: 'lineTo', args: [11, 14]}, + {name: 'lineTo', args: [11, 1]}, ]); }); it ('should draw correctly respecting "borderSkipped" == "top"', function() { testBorderSkipped ('top', [ - {name: 'moveTo', args: [12, 0]}, - {name: 'lineTo', args: [8, 0]}, - {name: 'lineTo', args: [8, 15]}, - {name: 'moveTo', args: [12, 15]}, - {name: 'lineTo', args: [12, 0]}, + {name: 'moveTo', args: [12, 1]}, + {name: 'lineTo', args: [9, 1]}, + {name: 'lineTo', args: [9, 15]}, + {name: 'moveTo', args: [11, 15]}, + {name: 'lineTo', args: [11, 1]}, ]); }); it ('should draw correctly respecting "borderSkipped" == "right"', function() { testBorderSkipped ('right', [ - {name: 'moveTo', args: [12, 0]}, - {name: 'lineTo', args: [8, 0]}, - {name: 'lineTo', args: [8, 15]}, - {name: 'lineTo', args: [12, 15]}, - {name: 'moveTo', args: [12, 0]}, + {name: 'moveTo', args: [12, 1]}, + {name: 'lineTo', args: [9, 1]}, + {name: 'lineTo', args: [9, 14]}, + {name: 'lineTo', args: [12, 14]}, + {name: 'setLineWidth', args: [2]}, ]); }); it ('should draw correctly respecting "borderSkipped" == "none"', function() { testBorderSkipped ('none', [ - {name: 'moveTo', args: [12, 0]}, - {name: 'lineTo', args: [8, 0]}, - {name: 'lineTo', args: [8, 15]}, - {name: 'lineTo', args: [12, 15]}, - {name: 'lineTo', args: [12, 0]}, + {name: 'moveTo', args: [12, 1]}, + {name: 'lineTo', args: [9, 1]}, + {name: 'lineTo', args: [9, 14]}, + {name: 'lineTo', args: [11, 14]}, + {name: 'lineTo', args: [11, 1]}, ]); }); - it ('should draw correctly respecting "borderSkipped" == "left right"', function() { - testBorderSkipped ('left right', [ - {name: 'moveTo', args: [12, 0]}, - {name: 'lineTo', args: [8, 0]}, - {name: 'moveTo', args: [8, 15]}, - {name: 'lineTo', args: [12, 15]}, - {name: 'moveTo', args: [12, 0]}, + it ('should draw correctly respecting "borderSkipped" == {left: true, right: true}', function() { + testBorderSkipped({left: true, right: true}, [ + {name: 'moveTo', args: [12, 1]}, + {name: 'lineTo', args: [8, 1]}, + {name: 'moveTo', args: [8, 14]}, + {name: 'lineTo', args: [12, 14]}, + {name: 'setLineWidth', args: [2]}, ]); }); - it ('should draw correctly respecting "borderSkipped" == "top bottom"', function() { - testBorderSkipped ('top bottom', [ - {name: 'moveTo', args: [12, 0]}, - {name: 'moveTo', args: [8, 0]}, - {name: 'lineTo', args: [8, 15]}, - {name: 'moveTo', args: [12, 15]}, - {name: 'lineTo', args: [12, 0]}, + it ('should draw correctly respecting "borderSkipped" == {top: true, bottom: true}', function() { + testBorderSkipped({top: true, bottom: true}, [ + {name: 'moveTo', args: [9, 0]}, + {name: 'lineTo', args: [9, 15]}, + {name: 'moveTo', args: [11, 15]}, + {name: 'lineTo', args: [11, 0]}, + {name: 'setLineWidth', args: [2]}, ]); }); + + function testBorderWidth(borderWidth, expectedDrawCalls) { + var mockContext = window.createMockContext(); + var rectangle = new Chart.elements.Rectangle({ + _chart: {ctx: mockContext} + }); + + // Attach a view object as if we were the controller + rectangle._view = { + borderWidth: borderWidth, + ctx: mockContext, + base: 0, + width: 4, + x: 10, + y: 15, + }; + + rectangle.draw(); + + var drawCalls = rectangle._view.ctx.getCalls().slice(4); + expect(drawCalls).toEqual(expectedDrawCalls); + } + + it ('should draw correctly respecting "borderWidth" == {bottom: 4, left: 1, top: 2, right: 3}', function() { + testBorderWidth({bottom: 4, left: 1, top: 2, right: 3}, [ + {name: 'moveTo', args: [12, 2]}, + {name: 'lineTo', args: [8.5, 2]}, + {name: 'setLineWidth', args: [4]}, + {name: 'stroke', args: []}, + {name: 'beginPath', args: []}, + {name: 'moveTo', args: [8.5, 0]}, + {name: 'lineTo', args: [8.5, 14]}, + {name: 'setLineWidth', args: [1]}, + {name: 'stroke', args: []}, + {name: 'beginPath', args: []}, + {name: 'moveTo', args: [8, 14]}, + {name: 'lineTo', args: [10.5, 14]}, + {name: 'setLineWidth', args: [2]}, + {name: 'stroke', args: []}, + {name: 'beginPath', args: []}, + {name: 'moveTo', args: [10.5, 15]}, + {name: 'lineTo', args: [10.5, 2]}, + {name: 'setLineWidth', args: [3]}, + {name: 'stroke', args: []}, + ]); + }); + }); From 8fc2481c3c8219584ad4166df537d342753236e9 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Wed, 6 Feb 2019 20:29:30 +0200 Subject: [PATCH 03/16] remove overlapping of borders, generalization --- src/elements/element.rectangle.js | 157 ++++++++++++-------------- test/specs/element.rectangle.tests.js | 61 ++++++---- 2 files changed, 114 insertions(+), 104 deletions(-) diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index 623043a232a..dcba94bd793 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -61,7 +61,10 @@ module.exports = Element.extend({ var ctx = this._chart.ctx; var vm = this._view; var borderWidth = vm.borderWidth; - var left, right, top, bottom, signX, signY, borderSkipped, x, y; + var lineCount = 0; + var width = 0; + var left, right, top, bottom, signX, signY, borderSkipped; + var maxWidth, maxHeight, prevWidth, nextWidth; if (!vm.horizontal) { // bar @@ -86,96 +89,82 @@ module.exports = Element.extend({ ctx.fillStyle = vm.backgroundColor; ctx.strokeStyle = vm.borderColor; - if (borderWidth) { - if (typeof borderSkipped === 'string') { - borderSkipped = {}; - borderSkipped[vm.borderSkipped] = true; - } + if (!borderWidth) { + ctx.fillRect(left, bottom, right - left, top - bottom); + return; + } - var maxWidth = Math.abs(left - right); - var maxHeight = Math.abs(top - bottom); - if (!helpers.isObject(borderWidth)) { - borderWidth = { - bottom: borderSkipped.bottom ? 0 : Math.min(borderWidth, maxHeight), - left: borderSkipped.left ? 0 : Math.min(borderWidth, maxWidth), - top: borderSkipped.top ? 0 : Math.min(borderWidth, maxHeight), - right: borderSkipped.right ? 0 : Math.min(borderWidth, maxWidth) - }; - } - ctx.fillRect( - left + borderWidth.left * signX, - bottom - borderWidth.bottom * signY, - right - left - signX * (borderWidth.left + borderWidth.right), - top - bottom + signY * (borderWidth.top + borderWidth.bottom)); - - ctx.beginPath(); - if (borderWidth.bottom) { - y = bottom - signY * borderWidth.bottom / 2; - ctx.moveTo(right, y); - if (borderWidth.left) { - ctx.lineTo(left + signX * borderWidth.left / 2, y); - } else { - ctx.lineTo(left, y); - } - if (borderWidth.bottom !== (borderWidth.left || borderWidth.top || borderWidth.right)) { - ctx.lineWidth = borderWidth.bottom; - ctx.stroke(); - if (borderWidth.left || borderWidth.top || borderWidth.right) { - ctx.beginPath(); - } - } + if (typeof borderSkipped === 'string') { + borderSkipped = {}; + borderSkipped[vm.borderSkipped] = true; + } + + maxWidth = Math.abs(left - right) / 2; + maxHeight = Math.abs(top - bottom) / 2; + if (!helpers.isObject(borderWidth)) { + borderWidth = { + bottom: borderSkipped.bottom ? 0 : Math.min(borderWidth, maxHeight), + left: borderSkipped.left ? 0 : Math.min(borderWidth, maxWidth), + top: borderSkipped.top ? 0 : Math.min(borderWidth, maxHeight), + right: borderSkipped.right ? 0 : Math.min(borderWidth, maxWidth) + }; + } else { + borderWidth = { + bottom: Math.min(borderWidth.bottom || 0, maxHeight), + left: Math.min(borderWidth.left || 0, maxWidth), + top: Math.min(borderWidth.top || 0, maxHeight), + right: Math.min(borderWidth.right || 0, maxWidth) + }; + } + + ctx.fillRect( + left + borderWidth.left * signX, + bottom - borderWidth.bottom * signY, + right - left - signX * (borderWidth.left + borderWidth.right), + top - bottom + signY * (borderWidth.top + borderWidth.bottom)); + + function drawBorder(w, x1, y1, x2, y2, x1x, y1x, x2x, y2x) { + prevWidth = width; + width = nextWidth; + nextWidth = w; + + if (!width) { + return; } - if (borderWidth.left) { - x = left + signX * borderWidth.left / 2; - if (borderWidth.left !== borderWidth.bottom) { - ctx.moveTo(x, bottom); - } - if (borderWidth.top) { - ctx.lineTo(x, top + signY * borderWidth.top / 2); - } else { - ctx.lineTo(x, top); - } - if (borderWidth.left !== (borderWidth.top || borderWidth.right)) { - ctx.lineWidth = borderWidth.left; + + if (ctx.lineWidth !== width) { + if (lineCount) { ctx.stroke(); - if (borderWidth.top || borderWidth.right) { - ctx.beginPath(); - } + ctx.beginPath(); + lineCount = 0; } + ctx.lineWidth = width; } - if (borderWidth.top) { - y = top + signY * borderWidth.top / 2; - if (borderWidth.top !== borderWidth.left) { - ctx.moveTo(left, y); - } - if (borderWidth.right) { - ctx.lineTo(right - signX * borderWidth.right / 2, y); - } else { - ctx.lineTo(right, y); - } - if (borderWidth.top !== borderWidth.right) { - ctx.lineWidth = borderWidth.top; - ctx.stroke(); - if (borderWidth.right) { - ctx.beginPath(); - } - } + + x1 += x1x * signX * width / 2; + y1 += y1x * signY * width / 2; + if (prevWidth !== width) { + ctx.moveTo(x1, y1); } - if (borderWidth.right) { - x = right - signX * borderWidth.right / 2; - if (borderWidth.right !== borderWidth.top) { - ctx.moveTo(x, top); - } - if (borderWidth.bottom) { - ctx.lineTo(x, bottom - signY * borderWidth.bottom / 2); - } else { - ctx.lineTo(x, bottom); - } - ctx.lineWidth = borderWidth.right; - ctx.stroke(); + if (width === nextWidth) { + x2 = x2x === 0 ? x1 : x2 + x2x * signX * nextWidth / 2; + y2 = y2x === 0 ? y1 : y2 + y2x * signY * nextWidth / 2; + } else { + x2 = x2x === 0 ? x1 : x2 + x2x * signX * nextWidth; + y2 = y2x === 0 ? y1 : y2 + y2x * signY * nextWidth; } - } else { - ctx.fillRect(left, bottom, right - left, top - bottom); + ctx.lineTo(x2, y2); + lineCount++; + } + + ctx.beginPath(); + nextWidth = borderWidth.bottom; + drawBorder(borderWidth.left, right, bottom, left, bottom, 0, -1, 1, 0); + drawBorder(borderWidth.top, left, bottom, left, top, 1, 0, 0, 1); + drawBorder(borderWidth.right, left, top, right, top, 0, 1, -1, 0); + drawBorder(borderWidth.bottom, right, top, right, bottom, -1, 0, 0, -1); + if (lineCount) { + ctx.stroke(); } }, diff --git a/test/specs/element.rectangle.tests.js b/test/specs/element.rectangle.tests.js index a270ee96422..d98a8a839ca 100644 --- a/test/specs/element.rectangle.tests.js +++ b/test/specs/element.rectangle.tests.js @@ -219,6 +219,9 @@ describe('Rectangle element tests', function() { }, { name: 'beginPath', args: [], + }, { + name: 'setLineWidth', + args: [1] }, { name: 'moveTo', args: [8.5, 0] @@ -231,9 +234,6 @@ describe('Rectangle element tests', function() { }, { name: 'lineTo', args: [11.5, 0] - }, { - name: 'setLineWidth', - args: [1] }, { name: 'stroke', args: [] @@ -294,77 +294,87 @@ describe('Rectangle element tests', function() { rectangle.draw(); - var drawCalls = rectangle._view.ctx.getCalls().splice(4, 5); + var drawCalls = rectangle._view.ctx.getCalls().slice(4); expect(drawCalls).toEqual(expectedDrawCalls); } it ('should draw correctly respecting "borderSkipped" == "bottom"', function() { testBorderSkipped ('bottom', [ + {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [9, 0]}, {name: 'lineTo', args: [9, 14]}, {name: 'lineTo', args: [11, 14]}, {name: 'lineTo', args: [11, 0]}, - {name: 'setLineWidth', args: [2]}, + {name: 'stroke', args: []}, ]); }); it ('should draw correctly respecting "borderSkipped" == "left"', function() { testBorderSkipped ('left', [ + {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [12, 1]}, {name: 'lineTo', args: [8, 1]}, {name: 'moveTo', args: [8, 14]}, {name: 'lineTo', args: [11, 14]}, {name: 'lineTo', args: [11, 1]}, + {name: 'stroke', args: []}, ]); }); it ('should draw correctly respecting "borderSkipped" == "top"', function() { testBorderSkipped ('top', [ + {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [12, 1]}, {name: 'lineTo', args: [9, 1]}, {name: 'lineTo', args: [9, 15]}, {name: 'moveTo', args: [11, 15]}, {name: 'lineTo', args: [11, 1]}, + {name: 'stroke', args: []}, ]); }); it ('should draw correctly respecting "borderSkipped" == "right"', function() { testBorderSkipped ('right', [ + {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [12, 1]}, {name: 'lineTo', args: [9, 1]}, {name: 'lineTo', args: [9, 14]}, {name: 'lineTo', args: [12, 14]}, - {name: 'setLineWidth', args: [2]}, + {name: 'stroke', args: []}, ]); }); it ('should draw correctly respecting "borderSkipped" == "none"', function() { testBorderSkipped ('none', [ + {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [12, 1]}, {name: 'lineTo', args: [9, 1]}, {name: 'lineTo', args: [9, 14]}, {name: 'lineTo', args: [11, 14]}, {name: 'lineTo', args: [11, 1]}, + {name: 'stroke', args: []}, ]); }); it ('should draw correctly respecting "borderSkipped" == {left: true, right: true}', function() { testBorderSkipped({left: true, right: true}, [ + {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [12, 1]}, {name: 'lineTo', args: [8, 1]}, {name: 'moveTo', args: [8, 14]}, {name: 'lineTo', args: [12, 14]}, - {name: 'setLineWidth', args: [2]}, + {name: 'stroke', args: []}, ]); }); it ('should draw correctly respecting "borderSkipped" == {top: true, bottom: true}', function() { testBorderSkipped({top: true, bottom: true}, [ + {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [9, 0]}, {name: 'lineTo', args: [9, 15]}, {name: 'moveTo', args: [11, 15]}, {name: 'lineTo', args: [11, 0]}, - {name: 'setLineWidth', args: [2]}, + {name: 'stroke', args: []}, ]); }); @@ -390,26 +400,37 @@ describe('Rectangle element tests', function() { expect(drawCalls).toEqual(expectedDrawCalls); } - it ('should draw correctly respecting "borderWidth" == {bottom: 4, left: 1, top: 2, right: 3}', function() { - testBorderWidth({bottom: 4, left: 1, top: 2, right: 3}, [ - {name: 'moveTo', args: [12, 2]}, - {name: 'lineTo', args: [8.5, 2]}, + it ('should draw correctly respecting "borderWidth" == {left: 2, right: 2}', function() { + testBorderWidth({left: 2, right: 2}, [ + {name: 'setLineWidth', args: [2]}, + {name: 'moveTo', args: [9, 0]}, + {name: 'lineTo', args: [9, 15]}, + {name: 'moveTo', args: [11, 15]}, + {name: 'lineTo', args: [11, 0]}, + {name: 'stroke', args: []}, + ]); + }); + + it ('should draw correctly respecting "borderWidth" == {bottom: 4, left: 1, top: 3, right: 2}', function() { + testBorderWidth({bottom: 4, left: 1, top: 3, right: 2}, [ {name: 'setLineWidth', args: [4]}, + {name: 'moveTo', args: [12, 2]}, + {name: 'lineTo', args: [9, 2]}, {name: 'stroke', args: []}, {name: 'beginPath', args: []}, - {name: 'moveTo', args: [8.5, 0]}, - {name: 'lineTo', args: [8.5, 14]}, {name: 'setLineWidth', args: [1]}, + {name: 'moveTo', args: [8.5, 0]}, + {name: 'lineTo', args: [8.5, 12]}, {name: 'stroke', args: []}, {name: 'beginPath', args: []}, - {name: 'moveTo', args: [8, 14]}, - {name: 'lineTo', args: [10.5, 14]}, - {name: 'setLineWidth', args: [2]}, + {name: 'setLineWidth', args: [3]}, + {name: 'moveTo', args: [8, 13.5]}, + {name: 'lineTo', args: [10, 13.5]}, {name: 'stroke', args: []}, {name: 'beginPath', args: []}, - {name: 'moveTo', args: [10.5, 15]}, - {name: 'lineTo', args: [10.5, 2]}, - {name: 'setLineWidth', args: [3]}, + {name: 'setLineWidth', args: [2]}, + {name: 'moveTo', args: [11, 15]}, + {name: 'lineTo', args: [11, 4]}, {name: 'stroke', args: []}, ]); }); From 817970ccda1492a8d81b319cebe6d248bcc5e9c5 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Wed, 6 Feb 2019 23:27:31 +0200 Subject: [PATCH 04/16] no object for borderSkipped, some cleanup, more tests --- src/elements/element.rectangle.js | 65 ++++++----- test/specs/element.rectangle.tests.js | 159 +++++++++++++++++++------- 2 files changed, 151 insertions(+), 73 deletions(-) diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index dcba94bd793..c2dfc87fab8 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -74,7 +74,7 @@ module.exports = Element.extend({ bottom = vm.base; signX = 1; signY = bottom > top ? 1 : -1; - borderSkipped = valueOrDefault(vm.borderSkipped, {bottom: true}) || {}; + borderSkipped = valueOrDefault(vm.borderSkipped, 'bottom') || ''; } else { // horizontal bar left = vm.base; @@ -83,7 +83,7 @@ module.exports = Element.extend({ bottom = vm.y + vm.height / 2; signX = right > left ? 1 : -1; signY = 1; - borderSkipped = valueOrDefault(vm.borderSkipped, {left: true}) || {}; + borderSkipped = valueOrDefault(vm.borderSkipped, 'left') || ''; } ctx.fillStyle = vm.backgroundColor; @@ -94,27 +94,22 @@ module.exports = Element.extend({ return; } - if (typeof borderSkipped === 'string') { - borderSkipped = {}; - borderSkipped[vm.borderSkipped] = true; - } - maxWidth = Math.abs(left - right) / 2; maxHeight = Math.abs(top - bottom) / 2; - if (!helpers.isObject(borderWidth)) { - borderWidth = { - bottom: borderSkipped.bottom ? 0 : Math.min(borderWidth, maxHeight), - left: borderSkipped.left ? 0 : Math.min(borderWidth, maxWidth), - top: borderSkipped.top ? 0 : Math.min(borderWidth, maxHeight), - right: borderSkipped.right ? 0 : Math.min(borderWidth, maxWidth) - }; - } else { + if (helpers.isObject(borderWidth)) { borderWidth = { bottom: Math.min(borderWidth.bottom || 0, maxHeight), left: Math.min(borderWidth.left || 0, maxWidth), top: Math.min(borderWidth.top || 0, maxHeight), right: Math.min(borderWidth.right || 0, maxWidth) }; + } else { + borderWidth = { + bottom: borderSkipped === 'bottom' ? 0 : Math.min(borderWidth, maxHeight), + left: borderSkipped === 'left' ? 0 : Math.min(borderWidth, maxWidth), + top: borderSkipped === 'top' ? 0 : Math.min(borderWidth, maxHeight), + right: borderSkipped === 'right' ? 0 : Math.min(borderWidth, maxWidth) + }; } ctx.fillRect( @@ -123,7 +118,7 @@ module.exports = Element.extend({ right - left - signX * (borderWidth.left + borderWidth.right), top - bottom + signY * (borderWidth.top + borderWidth.bottom)); - function drawBorder(w, x1, y1, x2, y2, x1x, y1x, x2x, y2x) { + function drawBorder(w, x1, y1, x2, y2) { prevWidth = width; width = nextWidth; nextWidth = w; @@ -132,6 +127,17 @@ module.exports = Element.extend({ return; } + if (lineCount === 0) { + ctx.beginPath(); + } + + var vertical = x1 === x2; + var sign1 = vertical ? y1 === top ? -1 : 1 : x1 === left ? 1 : -1; + var sign2 = vertical ? y1 > y2 ? 1 : -1 : x1 > x2 ? 1 : -1; + if (width === nextWidth) { + sign2 /= 2; + } + if (ctx.lineWidth !== width) { if (lineCount) { ctx.stroke(); @@ -141,28 +147,27 @@ module.exports = Element.extend({ ctx.lineWidth = width; } - x1 += x1x * signX * width / 2; - y1 += y1x * signY * width / 2; + if (vertical) { + x2 = x1 = x1 + sign1 * signX * width / 2; + y2 += sign2 * nextWidth; + } else { + y1 = y2 = y1 + sign1 * signY * width / 2; + x2 += sign2 * nextWidth; + } + if (prevWidth !== width) { ctx.moveTo(x1, y1); } - if (width === nextWidth) { - x2 = x2x === 0 ? x1 : x2 + x2x * signX * nextWidth / 2; - y2 = y2x === 0 ? y1 : y2 + y2x * signY * nextWidth / 2; - } else { - x2 = x2x === 0 ? x1 : x2 + x2x * signX * nextWidth; - y2 = y2x === 0 ? y1 : y2 + y2x * signY * nextWidth; - } + ctx.lineTo(x2, y2); lineCount++; } - ctx.beginPath(); nextWidth = borderWidth.bottom; - drawBorder(borderWidth.left, right, bottom, left, bottom, 0, -1, 1, 0); - drawBorder(borderWidth.top, left, bottom, left, top, 1, 0, 0, 1); - drawBorder(borderWidth.right, left, top, right, top, 0, 1, -1, 0); - drawBorder(borderWidth.bottom, right, top, right, bottom, -1, 0, 0, -1); + drawBorder(borderWidth.left, right, bottom, left, bottom); + drawBorder(borderWidth.top, left, bottom, left, top); + drawBorder(borderWidth.right, left, top, right, top); + drawBorder(borderWidth.bottom, right, top, right, bottom); if (lineCount) { ctx.stroke(); } diff --git a/test/specs/element.rectangle.tests.js b/test/specs/element.rectangle.tests.js index d98a8a839ca..dca62167639 100644 --- a/test/specs/element.rectangle.tests.js +++ b/test/specs/element.rectangle.tests.js @@ -1,7 +1,7 @@ // Test the rectangle element describe('Rectangle element tests', function() { - it ('Should be constructed', function() { + it('Should be constructed', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -12,7 +12,7 @@ describe('Rectangle element tests', function() { expect(rectangle._index).toBe(1); }); - it ('Should correctly identify as in range', function() { + it('Should correctly identify as in range', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -61,7 +61,7 @@ describe('Rectangle element tests', function() { expect(negativeRectangle.inRange(10, -5)).toBe(true); }); - it ('should get the correct height', function() { + it('should get the correct height', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -93,7 +93,7 @@ describe('Rectangle element tests', function() { expect(negativeRectangle.height()).toBe(5); }); - it ('should get the correct tooltip position', function() { + it('should get the correct tooltip position', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -132,7 +132,7 @@ describe('Rectangle element tests', function() { }); }); - it ('should get the correct vertical area', function() { + it('should get the correct vertical area', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -149,7 +149,7 @@ describe('Rectangle element tests', function() { expect(rectangle.getArea()).toEqual(60); }); - it ('should get the correct horizontal area', function() { + it('should get the correct horizontal area', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -166,7 +166,7 @@ describe('Rectangle element tests', function() { expect(rectangle.getArea()).toEqual(40); }); - it ('should get the center', function() { + it('should get the center', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -183,7 +183,7 @@ describe('Rectangle element tests', function() { expect(rectangle.getCenterPoint()).toEqual({x: 10, y: 7.5}); }); - it ('should draw correctly', function() { + it('should draw correctly', function() { var mockContext = window.createMockContext(); var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, @@ -240,7 +240,7 @@ describe('Rectangle element tests', function() { }]); }); - it ('should draw correctly with no stroke', function() { + it('should draw correctly with no stroke', function() { var mockContext = window.createMockContext(); var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, @@ -275,6 +275,101 @@ describe('Rectangle element tests', function() { }]); }); + it('should draw borders correctly when horizontal and pointing right', function() { + var mockContext = window.createMockContext(); + var rectangle = new Chart.elements.Rectangle({ + _chart: {ctx: mockContext} + }); + + // Attach a view object as if we were the controller + rectangle._view = { + horizontal: true, + borderWidth: 2, + borderSkipped: false, + ctx: mockContext, + base: 0, + height: 5, + x: 15, + y: 10, + }; + + rectangle.draw(); + + var drawCalls = rectangle._view.ctx.getCalls().slice(4); + expect(drawCalls).toEqual([ + {name: 'setLineWidth', args: [2]}, + {name: 'moveTo', args: [15, 11.5]}, + {name: 'lineTo', args: [1, 11.5]}, + {name: 'lineTo', args: [1, 8.5]}, + {name: 'lineTo', args: [14, 8.5]}, + {name: 'lineTo', args: [14, 11.5]}, + {name: 'stroke', args: []}, + ]); + }); + + it('should draw borders correctly when horizontal and pointing left', function() { + var mockContext = window.createMockContext(); + var rectangle = new Chart.elements.Rectangle({ + _chart: {ctx: mockContext} + }); + + // Attach a view object as if we were the controller + rectangle._view = { + horizontal: true, + borderWidth: 2, + borderSkipped: false, + ctx: mockContext, + base: 15, + height: 5, + x: 0, + y: 10, + }; + + rectangle.draw(); + + var drawCalls = rectangle._view.ctx.getCalls().slice(4); + expect(drawCalls).toEqual([ + {name: 'setLineWidth', args: [2]}, + {name: 'moveTo', args: [0, 11.5]}, + {name: 'lineTo', args: [14, 11.5]}, + {name: 'lineTo', args: [14, 8.5]}, + {name: 'lineTo', args: [1, 8.5]}, + {name: 'lineTo', args: [1, 11.5]}, + {name: 'stroke', args: []}, + ]); + }); + + it('should draw borders correctly when vertical and pointing down', function() { + var mockContext = window.createMockContext(); + var rectangle = new Chart.elements.Rectangle({ + _chart: {ctx: mockContext} + }); + + // Attach a view object as if we were the controller + rectangle._view = { + borderWidth: 2, + borderSkipped: false, + ctx: mockContext, + base: 15, + width: 4, + x: 10, + y: 0, + }; + + rectangle.draw(); + + var drawCalls = rectangle._view.ctx.getCalls().slice(4); + expect(drawCalls).toEqual([ + {name: 'setLineWidth', args: [2]}, + {name: 'moveTo', args: [12, 14]}, + {name: 'lineTo', args: [9, 14]}, + {name: 'lineTo', args: [9, 1]}, + {name: 'lineTo', args: [11, 1]}, + {name: 'lineTo', args: [11, 14]}, + {name: 'stroke', args: []}, + ]); + }); + function testBorderSkipped(borderSkipped, expectedDrawCalls) { var mockContext = window.createMockContext(); var rectangle = new Chart.elements.Rectangle({ @@ -298,8 +393,8 @@ describe('Rectangle element tests', function() { expect(drawCalls).toEqual(expectedDrawCalls); } - it ('should draw correctly respecting "borderSkipped" == "bottom"', function() { - testBorderSkipped ('bottom', [ + it('should draw correctly respecting "borderSkipped" == "bottom"', function() { + testBorderSkipped('bottom', [ {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [9, 0]}, {name: 'lineTo', args: [9, 14]}, @@ -309,8 +404,8 @@ describe('Rectangle element tests', function() { ]); }); - it ('should draw correctly respecting "borderSkipped" == "left"', function() { - testBorderSkipped ('left', [ + it('should draw correctly respecting "borderSkipped" == "left"', function() { + testBorderSkipped('left', [ {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [12, 1]}, {name: 'lineTo', args: [8, 1]}, @@ -321,8 +416,8 @@ describe('Rectangle element tests', function() { ]); }); - it ('should draw correctly respecting "borderSkipped" == "top"', function() { - testBorderSkipped ('top', [ + it('should draw correctly respecting "borderSkipped" == "top"', function() { + testBorderSkipped('top', [ {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [12, 1]}, {name: 'lineTo', args: [9, 1]}, @@ -333,8 +428,8 @@ describe('Rectangle element tests', function() { ]); }); - it ('should draw correctly respecting "borderSkipped" == "right"', function() { - testBorderSkipped ('right', [ + it('should draw correctly respecting "borderSkipped" == "right"', function() { + testBorderSkipped('right', [ {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [12, 1]}, {name: 'lineTo', args: [9, 1]}, @@ -344,8 +439,8 @@ describe('Rectangle element tests', function() { ]); }); - it ('should draw correctly respecting "borderSkipped" == "none"', function() { - testBorderSkipped ('none', [ + it('should draw correctly respecting "borderSkipped" == false', function() { + testBorderSkipped(false, [ {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [12, 1]}, {name: 'lineTo', args: [9, 1]}, @@ -356,28 +451,6 @@ describe('Rectangle element tests', function() { ]); }); - it ('should draw correctly respecting "borderSkipped" == {left: true, right: true}', function() { - testBorderSkipped({left: true, right: true}, [ - {name: 'setLineWidth', args: [2]}, - {name: 'moveTo', args: [12, 1]}, - {name: 'lineTo', args: [8, 1]}, - {name: 'moveTo', args: [8, 14]}, - {name: 'lineTo', args: [12, 14]}, - {name: 'stroke', args: []}, - ]); - }); - - it ('should draw correctly respecting "borderSkipped" == {top: true, bottom: true}', function() { - testBorderSkipped({top: true, bottom: true}, [ - {name: 'setLineWidth', args: [2]}, - {name: 'moveTo', args: [9, 0]}, - {name: 'lineTo', args: [9, 15]}, - {name: 'moveTo', args: [11, 15]}, - {name: 'lineTo', args: [11, 0]}, - {name: 'stroke', args: []}, - ]); - }); - function testBorderWidth(borderWidth, expectedDrawCalls) { var mockContext = window.createMockContext(); var rectangle = new Chart.elements.Rectangle({ @@ -400,7 +473,7 @@ describe('Rectangle element tests', function() { expect(drawCalls).toEqual(expectedDrawCalls); } - it ('should draw correctly respecting "borderWidth" == {left: 2, right: 2}', function() { + it('should draw correctly respecting "borderWidth" == {left: 2, right: 2}', function() { testBorderWidth({left: 2, right: 2}, [ {name: 'setLineWidth', args: [2]}, {name: 'moveTo', args: [9, 0]}, @@ -411,7 +484,7 @@ describe('Rectangle element tests', function() { ]); }); - it ('should draw correctly respecting "borderWidth" == {bottom: 4, left: 1, top: 3, right: 2}', function() { + it('should draw correctly respecting "borderWidth" == {bottom: 4, left: 1, top: 3, right: 2}', function() { testBorderWidth({bottom: 4, left: 1, top: 3, right: 2}, [ {name: 'setLineWidth', args: [4]}, {name: 'moveTo', args: [12, 2]}, From 01b4c24171d65c92bc0ff7bf47c2260c617f5400 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 7 Feb 2019 09:31:52 +0200 Subject: [PATCH 05/16] cache repeated Math.min --- src/elements/element.rectangle.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index c2dfc87fab8..27b90883787 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -104,11 +104,13 @@ module.exports = Element.extend({ right: Math.min(borderWidth.right || 0, maxWidth) }; } else { + maxWidth = Math.min(borderWidth, maxWidth); + maxHeight = Math.min(borderWidth, maxHeight); borderWidth = { - bottom: borderSkipped === 'bottom' ? 0 : Math.min(borderWidth, maxHeight), - left: borderSkipped === 'left' ? 0 : Math.min(borderWidth, maxWidth), - top: borderSkipped === 'top' ? 0 : Math.min(borderWidth, maxHeight), - right: borderSkipped === 'right' ? 0 : Math.min(borderWidth, maxWidth) + bottom: borderSkipped === 'bottom' ? 0 : maxHeight, + left: borderSkipped === 'left' ? 0 : maxWidth, + top: borderSkipped === 'top' ? 0 : maxHeight, + right: borderSkipped === 'right' ? 0 : maxWidth }; } From 0823e9dee1fb47fd99dda8349705bab6816200cd Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 7 Feb 2019 10:09:13 +0200 Subject: [PATCH 06/16] add documentation --- docs/charts/bar.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/charts/bar.md b/docs/charts/bar.md index 88e2744c713..5d286022089 100644 --- a/docs/charts/bar.md +++ b/docs/charts/bar.md @@ -71,7 +71,7 @@ the color of the bars is generally set this way. | [`backgroundColor`](#styling) | [`Color`](../general/colors.md) | Yes | Yes | `'rgba(0, 0, 0, 0.1)'` | [`borderColor`](#styling) | [`Color`](../general/colors.md) | Yes | Yes | `'rgba(0, 0, 0, 0.1)'` | [`borderSkipped`](#borderskipped) | `string` | Yes | Yes | `'bottom'` -| [`borderWidth`](#styling) | `number` | Yes | Yes | `0` +| [`borderWidth`](#borderwidth) | number|object | Yes | Yes | `0` | [`data`](#data-structure) | `object[]` | - | - | **required** | [`hoverBackgroundColor`](#interactions) | [`Color`](../general/colors.md) | - | Yes | `undefined` | [`hoverBorderColor`](#interactions) | [`Color`](../general/colors.md) | - | Yes | `undefined` @@ -97,7 +97,7 @@ The style of each bar can be controlled with the following properties: | `backgroundColor` | The bar background color. | `borderColor` | The bar border color. | [`borderSkipped`](#borderskipped) | The edge to skip when drawing bar. -| `borderWidth` | The bar border width (in pixels). +| [`borderWidth`](#borderwidth) | The bar border width (in pixels). All these values, if `undefined`, fallback to the associated [`elements.rectangle.*`](../configuration/elements.md#rectangle-configuration) options. @@ -112,6 +112,13 @@ Options are: * `'left'` * `'top'` * `'right'` +* `false` + +#### borderWidth + +If this value is a number, it is applied to all sides of the rectangle (left, top, right, bottom), except [`borderSkipped`](#borderskipped). If this value is an object, the `left` property defines the left border width. Similarly the `right`, `top` and `bottom` properties can also be specified. Omitted borders are skipped. + +**Note:** for negative bars in vertical chart, `top` and `bottom` are flipped. Same goes for `left` and `right` in horizontal chart. ### Interactions From c5f267c6f28b5b0f72e1b157b1115d858ba4fefb Mon Sep 17 00:00:00 2001 From: kurkle Date: Fri, 8 Feb 2019 19:08:20 +0200 Subject: [PATCH 07/16] refactor for simplicity, remove last overlap --- src/elements/element.rectangle.js | 199 +++++++++++++++----------- test/specs/element.rectangle.tests.js | 12 +- 2 files changed, 124 insertions(+), 87 deletions(-) diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index 27b90883787..6db39346401 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -56,15 +56,104 @@ function getBarBounds(bar) { }; } +function drawLines(ctx, lines) { + var i = 0; + var ilen = lines.length; + var line, prevLine; + + for (; i < ilen; ++i) { + line = lines[i]; + if (i === 0 || prevLine.w !== line.w) { + if (i > 0) { + ctx.stroke(); + } + ctx.beginPath(); + ctx.lineWidth = line.w; + } + if (i === 0 || prevLine.x2 !== line.x1 || prevLine.y2 !== line.y1) { + ctx.moveTo(line.x1, line.y1); + } + ctx.lineTo(line.x2, line.y2); + prevLine = line; + } + if (ilen) { + ctx.stroke(); + } +} + +// eslint-disable-next-line complexity +function buildBorderLines(rect, width, offset) { + var lines = []; + var halfOffsetLeft = offset.left / 2; + var halfOffsetRight = offset.right / 2; + var halfOffsetTop = offset.top / 2; + var halfOffsetBottom = offset.bottom / 2; + + if (width.bottom) { + lines.push({ + w: width.bottom, + x1: rect.right, + y1: rect.bottom + halfOffsetBottom, + x2: rect.left + (width.bottom === width.left ? halfOffsetLeft : offset.left), + y2: rect.bottom + halfOffsetBottom + }); + } + if (width.left) { + lines.push({ + w: width.left, + x1: rect.left + halfOffsetLeft, + y1: rect.bottom + (width.bottom === width.left ? halfOffsetBottom : 0), + x2: rect.left + halfOffsetLeft, + y2: rect.top + (width.left === width.top ? halfOffsetTop : offset.top) + }); + } + if (width.top) { + lines.push({ + w: width.top, + x1: rect.left + (width.left === width.top ? halfOffsetLeft : 0), + y1: rect.top + halfOffsetTop, + x2: rect.right + (width.top === width.right ? halfOffsetRight : offset.right), + y2: rect.top + halfOffsetTop + }); + } + if (width.right) { + lines.push({ + w: width.right, + x1: rect.right + halfOffsetRight, + y1: rect.top + (width.top === width.right ? halfOffsetTop : 0), + x2: rect.right + halfOffsetRight, + y2: rect.bottom + offset.bottom + }); + } + return lines; +} + +function parseBorderWidth(borderWidth, borderSkipped, maxWidth, maxHeight) { + if (helpers.isObject(borderWidth)) { + return { + bottom: Math.min(borderWidth.bottom || 0, maxHeight), + left: Math.min(borderWidth.left || 0, maxWidth), + top: Math.min(borderWidth.top || 0, maxHeight), + right: Math.min(borderWidth.right || 0, maxWidth) + }; + } + + maxWidth = Math.min(borderWidth, maxWidth); + maxHeight = Math.min(borderWidth, maxHeight); + return { + bottom: borderSkipped === 'bottom' ? 0 : maxHeight, + left: borderSkipped === 'left' ? 0 : maxWidth, + top: borderSkipped === 'top' ? 0 : maxHeight, + right: borderSkipped === 'right' ? 0 : maxWidth + }; +} + module.exports = Element.extend({ draw: function() { var ctx = this._chart.ctx; var vm = this._view; var borderWidth = vm.borderWidth; - var lineCount = 0; - var width = 0; - var left, right, top, bottom, signX, signY, borderSkipped; - var maxWidth, maxHeight, prevWidth, nextWidth; + var left, right, top, bottom, signX, signY, borderSkipped, offset; if (!vm.horizontal) { // bar @@ -94,85 +183,33 @@ module.exports = Element.extend({ return; } - maxWidth = Math.abs(left - right) / 2; - maxHeight = Math.abs(top - bottom) / 2; - if (helpers.isObject(borderWidth)) { - borderWidth = { - bottom: Math.min(borderWidth.bottom || 0, maxHeight), - left: Math.min(borderWidth.left || 0, maxWidth), - top: Math.min(borderWidth.top || 0, maxHeight), - right: Math.min(borderWidth.right || 0, maxWidth) - }; - } else { - maxWidth = Math.min(borderWidth, maxWidth); - maxHeight = Math.min(borderWidth, maxHeight); - borderWidth = { - bottom: borderSkipped === 'bottom' ? 0 : maxHeight, - left: borderSkipped === 'left' ? 0 : maxWidth, - top: borderSkipped === 'top' ? 0 : maxHeight, - right: borderSkipped === 'right' ? 0 : maxWidth - }; - } + borderWidth = parseBorderWidth( + borderWidth, + borderSkipped, + Math.abs(left - right) / 2, + Math.abs(top - bottom) / 2); + + offset = { + left: borderWidth.left * signX, + right: borderWidth.right * -signX, + top: borderWidth.top * signY, + bottom: borderWidth.bottom * -signY, + }; ctx.fillRect( - left + borderWidth.left * signX, - bottom - borderWidth.bottom * signY, - right - left - signX * (borderWidth.left + borderWidth.right), - top - bottom + signY * (borderWidth.top + borderWidth.bottom)); - - function drawBorder(w, x1, y1, x2, y2) { - prevWidth = width; - width = nextWidth; - nextWidth = w; - - if (!width) { - return; - } - - if (lineCount === 0) { - ctx.beginPath(); - } - - var vertical = x1 === x2; - var sign1 = vertical ? y1 === top ? -1 : 1 : x1 === left ? 1 : -1; - var sign2 = vertical ? y1 > y2 ? 1 : -1 : x1 > x2 ? 1 : -1; - if (width === nextWidth) { - sign2 /= 2; - } - - if (ctx.lineWidth !== width) { - if (lineCount) { - ctx.stroke(); - ctx.beginPath(); - lineCount = 0; - } - ctx.lineWidth = width; - } - - if (vertical) { - x2 = x1 = x1 + sign1 * signX * width / 2; - y2 += sign2 * nextWidth; - } else { - y1 = y2 = y1 + sign1 * signY * width / 2; - x2 += sign2 * nextWidth; - } - - if (prevWidth !== width) { - ctx.moveTo(x1, y1); - } - - ctx.lineTo(x2, y2); - lineCount++; - } - - nextWidth = borderWidth.bottom; - drawBorder(borderWidth.left, right, bottom, left, bottom); - drawBorder(borderWidth.top, left, bottom, left, top); - drawBorder(borderWidth.right, left, top, right, top); - drawBorder(borderWidth.bottom, right, top, right, bottom); - if (lineCount) { - ctx.stroke(); - } + left + offset.left, + bottom + offset.bottom, + right + offset.right - left - offset.left, + top + offset.top - bottom - offset.bottom); + + drawLines(ctx, + buildBorderLines({ + left: left, + top: top, + right: right, + bottom: bottom + }, borderWidth, offset) + ); }, height: function() { diff --git a/test/specs/element.rectangle.tests.js b/test/specs/element.rectangle.tests.js index dca62167639..2862ada693c 100644 --- a/test/specs/element.rectangle.tests.js +++ b/test/specs/element.rectangle.tests.js @@ -302,7 +302,7 @@ describe('Rectangle element tests', function() { {name: 'lineTo', args: [1, 11.5]}, {name: 'lineTo', args: [1, 8.5]}, {name: 'lineTo', args: [14, 8.5]}, - {name: 'lineTo', args: [14, 11.5]}, + {name: 'lineTo', args: [14, 10.5]}, {name: 'stroke', args: []}, ]); }); @@ -334,7 +334,7 @@ describe('Rectangle element tests', function() { {name: 'lineTo', args: [14, 11.5]}, {name: 'lineTo', args: [14, 8.5]}, {name: 'lineTo', args: [1, 8.5]}, - {name: 'lineTo', args: [1, 11.5]}, + {name: 'lineTo', args: [1, 10.5]}, {name: 'stroke', args: []}, ]); }); @@ -365,7 +365,7 @@ describe('Rectangle element tests', function() { {name: 'lineTo', args: [9, 14]}, {name: 'lineTo', args: [9, 1]}, {name: 'lineTo', args: [11, 1]}, - {name: 'lineTo', args: [11, 14]}, + {name: 'lineTo', args: [11, 13]}, {name: 'stroke', args: []}, ]); }); @@ -411,7 +411,7 @@ describe('Rectangle element tests', function() { {name: 'lineTo', args: [8, 1]}, {name: 'moveTo', args: [8, 14]}, {name: 'lineTo', args: [11, 14]}, - {name: 'lineTo', args: [11, 1]}, + {name: 'lineTo', args: [11, 2]}, {name: 'stroke', args: []}, ]); }); @@ -423,7 +423,7 @@ describe('Rectangle element tests', function() { {name: 'lineTo', args: [9, 1]}, {name: 'lineTo', args: [9, 15]}, {name: 'moveTo', args: [11, 15]}, - {name: 'lineTo', args: [11, 1]}, + {name: 'lineTo', args: [11, 2]}, {name: 'stroke', args: []}, ]); }); @@ -446,7 +446,7 @@ describe('Rectangle element tests', function() { {name: 'lineTo', args: [9, 1]}, {name: 'lineTo', args: [9, 14]}, {name: 'lineTo', args: [11, 14]}, - {name: 'lineTo', args: [11, 1]}, + {name: 'lineTo', args: [11, 2]}, {name: 'stroke', args: []}, ]); }); From 05c8ab6d4c950165e2a9df693b64f959c98590db Mon Sep 17 00:00:00 2001 From: kurkle Date: Sun, 10 Feb 2019 10:41:59 +0200 Subject: [PATCH 08/16] image based tests --- .../borderWidth/indexable-object.js | 54 ++++++++++++++++++ .../borderWidth/indexable-object.png | Bin 0 -> 1975 bytes .../controller.bar/borderWidth/object.js | 40 +++++++++++++ .../controller.bar/borderWidth/object.png | Bin 0 -> 2069 bytes .../borderWidth/scriptable-object.js | 52 +++++++++++++++++ .../borderWidth/scriptable-object.png | Bin 0 -> 4664 bytes .../controller.bar/horizontal-borders.js | 40 +++++++++++++ .../controller.bar/horizontal-borders.png | Bin 0 -> 1489 bytes 8 files changed, 186 insertions(+) create mode 100644 test/fixtures/controller.bar/borderWidth/indexable-object.js create mode 100644 test/fixtures/controller.bar/borderWidth/indexable-object.png create mode 100644 test/fixtures/controller.bar/borderWidth/object.js create mode 100644 test/fixtures/controller.bar/borderWidth/object.png create mode 100644 test/fixtures/controller.bar/borderWidth/scriptable-object.js create mode 100644 test/fixtures/controller.bar/borderWidth/scriptable-object.png create mode 100644 test/fixtures/controller.bar/horizontal-borders.js create mode 100644 test/fixtures/controller.bar/horizontal-borders.png diff --git a/test/fixtures/controller.bar/borderWidth/indexable-object.js b/test/fixtures/controller.bar/borderWidth/indexable-object.js new file mode 100644 index 00000000000..368fd28346c --- /dev/null +++ b/test/fixtures/controller.bar/borderWidth/indexable-object.js @@ -0,0 +1,54 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderWidth: [ + {}, + {bottom: 1, left: 1, top: 1, right: 1}, + {bottom: 1, left: 2, top: 1, right: 2}, + {bottom: 1, left: 3, top: 1, right: 3}, + {bottom: 1, left: 4, top: 1, right: 4}, + {bottom: 1, left: 5, top: 1, right: 5} + ] + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#80808080', + borderWidth: [ + {bottom: 1, left: 5, top: 1, right: 5}, + {bottom: 1, left: 4, top: 1, right: 4}, + {bottom: 1, left: 3, top: 1, right: 3}, + {bottom: 1, left: 2, top: 1, right: 2}, + {bottom: 1, left: 1, top: 1, right: 1}, + {} + ] + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [{display: false}] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderWidth/indexable-object.png b/test/fixtures/controller.bar/borderWidth/indexable-object.png new file mode 100644 index 0000000000000000000000000000000000000000..693806a18da69eb54ecc5c03f5fd37aaafb404fc GIT binary patch literal 1975 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is@o>pWc?Ln`LHy=mAl%_`9L z(4-_}iHZ=fr?2Q!^$8&>mANw3Jrj{izW*+#^V9Oo9Y?Gzl$Lz7I{e+{YE|r+ir6>V z?n(-;I87+8dWPHS*ra$sN(@L=EoI+PRWP6q`>1qKEV6$Tat zhDMeKpyCOmib)BLeHEc!*Q~O;Qd(2I?RfXzwtIK){{8jf%IRm>TEH+pFn#T+xX`Qp z;WK^*ht}5r|5ajTTVD`dSHAM*=}mjY82$p4C@?uNln62~G;=U8UT|e#;89{=aS&%@ zFz5irMC_=7;TjGhS2vf&+FvQPkzxR*h5Lp5-7E(-=^U4}KfKqSIU#ad>2ce-cYBsG zFqo`dHU0V3Z91pl_D%fpk^R8Kf{I=1UT=H+rg-`<_796?>v{J4{nN&9Wc7ab1FyNy zRh8Sl1|~E6>i4d(A2#0K|K`iGGLD8_2Ob~z_2q^?1H*^=z=SrM@o7;E?q+q^oqId) z|HCZ|3=ckgF>GOHV5s>xTC6HCD$uBsS+J@q)cWSOx8F80Sjh1CUoXA;?bg$KyVljq h^dHY(|5J9!Z{ckRR*M98+e9(|fv2mV%Q~loCIC19ei8rx literal 0 HcmV?d00001 diff --git a/test/fixtures/controller.bar/borderWidth/object.js b/test/fixtures/controller.bar/borderWidth/object.js new file mode 100644 index 00000000000..fb39473a627 --- /dev/null +++ b/test/fixtures/controller.bar/borderWidth/object.js @@ -0,0 +1,40 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderWidth: {bottom: 1, left: 2, top: 3, right: 4} + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#888', + borderWidth: {bottom: 4, left: 3, top: 2, right: 1} + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [{display: false}] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderWidth/object.png b/test/fixtures/controller.bar/borderWidth/object.png new file mode 100644 index 0000000000000000000000000000000000000000..bec6da56919b50bb26a6e5be27ab483e86d2dd75 GIT binary patch literal 2069 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 literal 0 HcmV?d00001 From 630e891365b2848a80d71ddb2e07778c240fb879 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Mon, 11 Feb 2019 12:07:57 +0200 Subject: [PATCH 09/16] remove mock tests alter horizontal-borders fixture to detect overlapping draws add borderSkipped: false to borderSkipped/value fixture --- .../controller.bar/borderSkipped/value.js | 5 + .../controller.bar/borderSkipped/value.png | Bin 4889 -> 2058 bytes .../controller.bar/horizontal-borders.js | 3 +- .../controller.bar/horizontal-borders.png | Bin 1489 -> 1613 bytes test/specs/element.rectangle.tests.js | 326 ------------------ 5 files changed, 7 insertions(+), 327 deletions(-) diff --git a/test/fixtures/controller.bar/borderSkipped/value.js b/test/fixtures/controller.bar/borderSkipped/value.js index 139a2c68905..fed9592636b 100644 --- a/test/fixtures/controller.bar/borderSkipped/value.js +++ b/test/fixtures/controller.bar/borderSkipped/value.js @@ -22,6 +22,11 @@ module.exports = { { // option in element (fallback) data: [0, 5, -10, null], + }, + { + // option in dataset + data: [0, 5, -10, null], + borderSkipped: false } ] }, diff --git a/test/fixtures/controller.bar/borderSkipped/value.png b/test/fixtures/controller.bar/borderSkipped/value.png index 56ef05a41fe65a38ffee53a6b4a25964e0eee2e7..80c7df6be2f497c4820735534adcc51c9c239723 100644 GIT binary patch literal 2058 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is@oFM7H-hE&XXd(AK_CQ`)p z;;aeD%a1XC*zoHAM+-5xmC5`k=0+@7QX6tNyrcSG)Q3&(bHiI5ewQ%=O~3~p)LZvG zK3zF)|Npl#$3Omg^{#lw@26$E@1Fbo=il~f{yEFH@A)|K^QU89VOdH%ghnPHDK zqXGlN4h15}1^pZizyJPwyZFAv-oF#`SsL0o8)oJ*G91uiN=S=lVA#;bFyp2e1H&36 zhO}+03=FF|Mx}>GuuS?_Xj^noiUE><_WvpN{Vw<8-1+xkt(gw&76c~m25upOB*Tsw zpKa`U+fHA%pKmGE@csAS;vL^VU;X~`=l0z|&gc2n{?7S->UQ7FJ6>2b_m}MX&wsuM z)^jt&bT9}oFvLtCf(+nlcx+L(ch~P^C5Ct3e{Xlc_d|~H!+~$x7c%_#_-W?x!aqU` z3^f{z0^5PfvzEo-4luQT5o9O?#?l3Mh9khV>>xiXIT{F~DPhE9hv&*n4;nw0?B1)% zaIYqg>4DApa_h#T=V9@g*NLu}2cHHHf)Gy=Q&Hx0Su6{1-oD!M< DiX%&` literal 4889 zcmeHLe^3*57Js{4=mvPjx)5>9LDQ#i#pklRzUaO^6Nw5|b5V%tz2?Cp< zpyvtpYz>t|&})kfsMwLh90>7%k+yPzs89kVskJ48A>oH4m_Y8k@&CKInRcdsB>!Z; zFQ0ku?fbs>c`xr+V!{T#ho1)k@HfWC{t*C2j~I~d=&S4Gzjp(8>o&&5ye&&M4!@E1 zo3DM?zUR&byduhI_5a7YzHOrJA$AkKuWL{Ji0@+m)iH^S+p9LJ>g~04-Fd-cab0-u z_noV))EAbQ9ZP721q`nc3}#JqF51jJ6XZjJ4SXW^UrK?0>wyg+P86>8qByyd??JNs zHPh84KWFW(QJK$3%iiEWd@jBTnZ4(w-;k-dVK0aPy zv)KkLQPIoG+GlO^k`=4$ANUqa%F4=eYx)g0&hty7XTrTYjHb&0Wj9pw1&)LEkLI)y zl?k~e9r`(g!SJm{qq#MgRX)=>9_IDpsW!tV4XrVPQRAH<8qyH8c!W z6uemOX%KY2Clwn+8Kw18U5VUqEKxRQ+ujG?}C1&i}rvhamNkZ_Icm_x|uIPRsPTVF*R`kxB z>A^Sycg`fWtYmamIIiBj%9{dQ+NYBp1*Hhb>7FfjhK>RSjUo3kd6ZcQYbm6iV7h5J z55wztR!?=>Ory{Tc{59BUd50Oum7PV3vMz;S)?|{=)*59A*uLa z8g6foyLf_&N%~IXX}}!rYOlk{7Dk1~C$DGKY4`%(;FoVa;>N;-SFh)Yoi%~7iG?Il z#PT49KEXo#{$-8pf=~c19&3CLC+z;NfVcwkE6$Mjv26X`Hm?rz_~hi|#DU29X7i3h z1xL0T?TY13xb;!d!OW5rncOs8Qd08MNT$UozI8UCrlmt~w$IGWJTMxKKZRC4IOtnk zAyzaEc+fPecECM?09R_YLo1S-YMWw4@SY{uFCE6CYYObMb zuj5x8;ZfugRQG??jYm~fzJ|&_wIdaAQ&b!Shdk09YdDT`Je#{7 z&qo~BX0r4rgd$@#e3ei@_r@|HO8JUZwh=@%JWM~|B=^G_dh&A9a3K%b&+Y?x#1T}m zf8wu;sXoo|4aZ?x0R=yP2+Hp`^0>L&Z}DeHV7Sy-O#UE5%tt#u9@3Yy;47GV3Bz}D zAnUt#&@Ycny*yIHL8|9^-woT2nCFxtP`TiS?tZNB8(Z`_7;}_Xo@AQUNw8fa(GF|1 z?QKowNtwM-YjQl6z9lJ6u|Bd|Wx0`+1$ILoYIAeh5yLwa&G#p*UusS51?IDw6yM}g zn_aKh%bPU$Hfb5x?JT)I4$HjFQc+h|*B&jq&h@+FLbEOvb??;6qt|(>kXO@)y0$Bt z1fUv|&jw?dG>W9kS0kcUKRh?dL}J*%IhQ-ah7a9tx%SojXfrYSAkt5H3;kd3-{o9lY}>#<{r$`>_~&RcygDx678dCN zXlqm2vW54t55ip%bIXZ~2hj>4a$@Vs#BB_0Zi#YkO75Y#e7yO+Pnf+J+KN0z8!lz52WZ^)Hvj+t diff --git a/test/fixtures/controller.bar/horizontal-borders.js b/test/fixtures/controller.bar/horizontal-borders.js index 3e361d56918..b6bbc51aec7 100644 --- a/test/fixtures/controller.bar/horizontal-borders.js +++ b/test/fixtures/controller.bar/horizontal-borders.js @@ -1,4 +1,5 @@ module.exports = { + threshold: 0.01, config: { type: 'horizontalBar', data: { @@ -22,7 +23,7 @@ module.exports = { rectangle: { backgroundColor: '#AAAAAA80', borderColor: '#80808080', - borderWidth: 4 + borderWidth: {bottom: 6, left: 15, top: 6, right: 15} } }, scales: { diff --git a/test/fixtures/controller.bar/horizontal-borders.png b/test/fixtures/controller.bar/horizontal-borders.png index 99f76ac4bb979fb4a25aee37e100252fa2c8e861..406c23f1ea536012ec010a0200b99c66c88d2516 100644 GIT binary patch literal 1613 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is@oL_A#_Ln`LHy?ZcICQ`)V z;;DnDZ(0a8I~6h<+Qt&s;r2*hi&2x|t@i0io+d+{o?WNDl)j$NY~K9*`}~9NqQrm( z(Gc`WB-dQqbM-U3LyE!Hs(%a}YhG8a|5?Pq;FEWI+d?p7U0uf$1_p%ydl#UvzzTUK zO9lpxEBq5>7#LVywR`XZ{ruHgg`I(+@t5KxW`+ibzXB&285|a%Xp6Z1HSF_K28J1T zs`pDBKOS;ko?+rg_L|9Yv0wi%3ape@kbVE~;ls=KUj1ZNFq#o%ThHL~ioGKYAAL+> zd-dx2Gj99;t>_0@TWiGI=*BKcO?6zcBXAg8#dND&xJkaAIuh)Xd!#*!!`{m) zm^5Tw{mss?ch8 z!$M_thJw4V{xY|`Vr418M;~jfcdYqY%KFC-=myL{q=kq_uc4jytj!>2GWx8(XYcSL zR}h%O^)uIj9D0whA@{b5Zx}G}CNMk90{KRfy}=|O6hwRsnfE|yWEd{(0fnF?!;(s1 z*a{Reczg!M^Am;%&(;DBJi(}NHX54&|9^=cSi5BBDxjXK`{sum`d0u|XZ&HjuQL5g z2+)8pA3l7@%>VjnQpp;G^H#(;I4lRM!$rS1@o7c<5tmC<*2iwdg5rE4^MZep!JfA# z+8`=VJ9fR zbucHq1Ld+u3^R6vN*W7>XX}WU1{(O`*RQHeHDIgO=pX;^@CvBZ>iHP@ISg2uFfdI2 z^y9~m)O=vdk1YokDv~~6$9-jXSq-uV7wst_%9}nJhOh5$yq@2; z0_^AqzkqqRV4LBw{=K=k%a#K(E<-x7s44i1uf#wqQT~YS{;%#1G#CyJ%>2h@uteqV U_vN2~Ml&#Yy85}Sb4q9e03%>>TL1t6 literal 1489 zcmcIjZA?>V6h7AryG*3FMz=EUXl<@bjEg1FXlt^yLW$fgAQ}Z%l{EgS5{+g+#5EHx zx@nCGfx9f0`LP*MinT^mB#LOGBluNNLWw9aH=#h8X=8T-1zLRH+aK28k}a9{&vSBa z&Ni}k;kbr~_5VTabZ3Bq6v4&Oa_a1-QTVEhwJ6HdB zQOgcDDA8QI<9Vjq0a_-u%RyWxi$izFFPnAe|Bq@Gf{L}8`sKa~wBCW=eBi`J0 z1yr?c7bXd5_h>jPSO_qyMBdtQLk@7qLsub)^Bu~RpuL22h5{%bU%->V>BG`{1n7S) zwe|xjb94q&D77n8T7ZPkx5_4NmU+Z(;gP%-W5<0VQhDi1*NS1!20VJ4z};M1uUH~r z?WCp7w{WWBtoQdRk%}(!w3p(dtayf_$X^`EOt?2pEX}coTeYzw#_N13d}-;NJ~t1^y&0NmgY$1vc(i!{Is>6ao;Ck zr=a4COC?}($f|0BMF5+ct;~sENONm=;aFyL1c1rvAx234z0%6I6n;A%CI!e4h@$Ro!J&^X6Q?LV1O&_l3#teerR{ zvAE>ugB9#jqyjCdd3L3mfuaau^h3RuOkG2lj9uy&rKLvKMy$bT29~qtYY)0h zFU`HsFiBXHyM)s1{2y_saX0bZPs<#ZDL7R&VNMQwf(JV3Kvp4}g?R`w_|6UgEW=4z zT6j0hw}}rX1okqUq2pWJLJKw5{tJc&N}!K%+tF@AApox(o#LEK`lv1jjf&^T`uU&H zGnlSHYP)eHqsTExb9bZW>QU?;5ysPl=Wr?vnu>H1|Fx2)J=p!?L+}U z+odkc;~vrb$vC4DNP{C5+v(c_g`u-rJi8y?tvb8(Ln$}s-^~v_AOc&8#La?)>vOzM hokD4R2fjI!|L?@HyyEKlrRC)~$!265E~J_EzXrkdAAtY> diff --git a/test/specs/element.rectangle.tests.js b/test/specs/element.rectangle.tests.js index 2862ada693c..d6932359ed9 100644 --- a/test/specs/element.rectangle.tests.js +++ b/test/specs/element.rectangle.tests.js @@ -182,330 +182,4 @@ describe('Rectangle element tests', function() { expect(rectangle.getCenterPoint()).toEqual({x: 10, y: 7.5}); }); - - it('should draw correctly', function() { - var mockContext = window.createMockContext(); - var rectangle = new Chart.elements.Rectangle({ - _datasetIndex: 2, - _index: 1, - _chart: { - ctx: mockContext, - } - }); - - // Attach a view object as if we were the controller - rectangle._view = { - backgroundColor: 'rgb(255, 0, 0)', - base: 0, - borderColor: 'rgb(0, 0, 255)', - borderWidth: 1, - ctx: mockContext, - width: 4, - x: 10, - y: 15, - }; - - rectangle.draw(); - - expect(mockContext.getCalls()).toEqual([{ - name: 'setFillStyle', - args: ['rgb(255, 0, 0)'] - }, { - name: 'setStrokeStyle', - args: ['rgb(0, 0, 255)'], - }, { - name: 'fillRect', - args: [9, 0, 2, 14], - }, { - name: 'beginPath', - args: [], - }, { - name: 'setLineWidth', - args: [1] - }, { - name: 'moveTo', - args: [8.5, 0] - }, { - name: 'lineTo', - args: [8.5, 14.5] // This is a minus bar. Not 15.5 - }, { - name: 'lineTo', - args: [11.5, 14.5] - }, { - name: 'lineTo', - args: [11.5, 0] - }, { - name: 'stroke', - args: [] - }]); - }); - - it('should draw correctly with no stroke', function() { - var mockContext = window.createMockContext(); - var rectangle = new Chart.elements.Rectangle({ - _datasetIndex: 2, - _index: 1, - _chart: { - ctx: mockContext, - } - }); - - // Attach a view object as if we were the controller - rectangle._view = { - backgroundColor: 'rgb(255, 0, 0)', - base: 0, - borderColor: 'rgb(0, 0, 255)', - ctx: mockContext, - width: 4, - x: 10, - y: 15, - }; - - rectangle.draw(); - - expect(mockContext.getCalls()).toEqual([{ - name: 'setFillStyle', - args: ['rgb(255, 0, 0)'] - }, { - name: 'setStrokeStyle', - args: ['rgb(0, 0, 255)'], - }, { - name: 'fillRect', - args: [8, 0, 4, 15], - }]); - }); - - it('should draw borders correctly when horizontal and pointing right', function() { - var mockContext = window.createMockContext(); - var rectangle = new Chart.elements.Rectangle({ - _chart: {ctx: mockContext} - }); - - // Attach a view object as if we were the controller - rectangle._view = { - horizontal: true, - borderWidth: 2, - borderSkipped: false, - ctx: mockContext, - base: 0, - height: 5, - x: 15, - y: 10, - }; - - rectangle.draw(); - - var drawCalls = rectangle._view.ctx.getCalls().slice(4); - expect(drawCalls).toEqual([ - {name: 'setLineWidth', args: [2]}, - {name: 'moveTo', args: [15, 11.5]}, - {name: 'lineTo', args: [1, 11.5]}, - {name: 'lineTo', args: [1, 8.5]}, - {name: 'lineTo', args: [14, 8.5]}, - {name: 'lineTo', args: [14, 10.5]}, - {name: 'stroke', args: []}, - ]); - }); - - it('should draw borders correctly when horizontal and pointing left', function() { - var mockContext = window.createMockContext(); - var rectangle = new Chart.elements.Rectangle({ - _chart: {ctx: mockContext} - }); - - // Attach a view object as if we were the controller - rectangle._view = { - horizontal: true, - borderWidth: 2, - borderSkipped: false, - ctx: mockContext, - base: 15, - height: 5, - x: 0, - y: 10, - }; - - rectangle.draw(); - - var drawCalls = rectangle._view.ctx.getCalls().slice(4); - expect(drawCalls).toEqual([ - {name: 'setLineWidth', args: [2]}, - {name: 'moveTo', args: [0, 11.5]}, - {name: 'lineTo', args: [14, 11.5]}, - {name: 'lineTo', args: [14, 8.5]}, - {name: 'lineTo', args: [1, 8.5]}, - {name: 'lineTo', args: [1, 10.5]}, - {name: 'stroke', args: []}, - ]); - }); - - it('should draw borders correctly when vertical and pointing down', function() { - var mockContext = window.createMockContext(); - var rectangle = new Chart.elements.Rectangle({ - _chart: {ctx: mockContext} - }); - - // Attach a view object as if we were the controller - rectangle._view = { - borderWidth: 2, - borderSkipped: false, - ctx: mockContext, - base: 15, - width: 4, - x: 10, - y: 0, - }; - - rectangle.draw(); - - var drawCalls = rectangle._view.ctx.getCalls().slice(4); - expect(drawCalls).toEqual([ - {name: 'setLineWidth', args: [2]}, - {name: 'moveTo', args: [12, 14]}, - {name: 'lineTo', args: [9, 14]}, - {name: 'lineTo', args: [9, 1]}, - {name: 'lineTo', args: [11, 1]}, - {name: 'lineTo', args: [11, 13]}, - {name: 'stroke', args: []}, - ]); - }); - - function testBorderSkipped(borderSkipped, expectedDrawCalls) { - var mockContext = window.createMockContext(); - var rectangle = new Chart.elements.Rectangle({ - _chart: {ctx: mockContext} - }); - - // Attach a view object as if we were the controller - rectangle._view = { - borderWidth: 2, - borderSkipped: borderSkipped, // set tested 'borderSkipped' parameter - ctx: mockContext, - base: 0, - width: 4, - x: 10, - y: 15, - }; - - rectangle.draw(); - - var drawCalls = rectangle._view.ctx.getCalls().slice(4); - expect(drawCalls).toEqual(expectedDrawCalls); - } - - it('should draw correctly respecting "borderSkipped" == "bottom"', function() { - testBorderSkipped('bottom', [ - {name: 'setLineWidth', args: [2]}, - {name: 'moveTo', args: [9, 0]}, - {name: 'lineTo', args: [9, 14]}, - {name: 'lineTo', args: [11, 14]}, - {name: 'lineTo', args: [11, 0]}, - {name: 'stroke', args: []}, - ]); - }); - - it('should draw correctly respecting "borderSkipped" == "left"', function() { - testBorderSkipped('left', [ - {name: 'setLineWidth', args: [2]}, - {name: 'moveTo', args: [12, 1]}, - {name: 'lineTo', args: [8, 1]}, - {name: 'moveTo', args: [8, 14]}, - {name: 'lineTo', args: [11, 14]}, - {name: 'lineTo', args: [11, 2]}, - {name: 'stroke', args: []}, - ]); - }); - - it('should draw correctly respecting "borderSkipped" == "top"', function() { - testBorderSkipped('top', [ - {name: 'setLineWidth', args: [2]}, - {name: 'moveTo', args: [12, 1]}, - {name: 'lineTo', args: [9, 1]}, - {name: 'lineTo', args: [9, 15]}, - {name: 'moveTo', args: [11, 15]}, - {name: 'lineTo', args: [11, 2]}, - {name: 'stroke', args: []}, - ]); - }); - - it('should draw correctly respecting "borderSkipped" == "right"', function() { - testBorderSkipped('right', [ - {name: 'setLineWidth', args: [2]}, - {name: 'moveTo', args: [12, 1]}, - {name: 'lineTo', args: [9, 1]}, - {name: 'lineTo', args: [9, 14]}, - {name: 'lineTo', args: [12, 14]}, - {name: 'stroke', args: []}, - ]); - }); - - it('should draw correctly respecting "borderSkipped" == false', function() { - testBorderSkipped(false, [ - {name: 'setLineWidth', args: [2]}, - {name: 'moveTo', args: [12, 1]}, - {name: 'lineTo', args: [9, 1]}, - {name: 'lineTo', args: [9, 14]}, - {name: 'lineTo', args: [11, 14]}, - {name: 'lineTo', args: [11, 2]}, - {name: 'stroke', args: []}, - ]); - }); - - function testBorderWidth(borderWidth, expectedDrawCalls) { - var mockContext = window.createMockContext(); - var rectangle = new Chart.elements.Rectangle({ - _chart: {ctx: mockContext} - }); - - // Attach a view object as if we were the controller - rectangle._view = { - borderWidth: borderWidth, - ctx: mockContext, - base: 0, - width: 4, - x: 10, - y: 15, - }; - - rectangle.draw(); - - var drawCalls = rectangle._view.ctx.getCalls().slice(4); - expect(drawCalls).toEqual(expectedDrawCalls); - } - - it('should draw correctly respecting "borderWidth" == {left: 2, right: 2}', function() { - testBorderWidth({left: 2, right: 2}, [ - {name: 'setLineWidth', args: [2]}, - {name: 'moveTo', args: [9, 0]}, - {name: 'lineTo', args: [9, 15]}, - {name: 'moveTo', args: [11, 15]}, - {name: 'lineTo', args: [11, 0]}, - {name: 'stroke', args: []}, - ]); - }); - - it('should draw correctly respecting "borderWidth" == {bottom: 4, left: 1, top: 3, right: 2}', function() { - testBorderWidth({bottom: 4, left: 1, top: 3, right: 2}, [ - {name: 'setLineWidth', args: [4]}, - {name: 'moveTo', args: [12, 2]}, - {name: 'lineTo', args: [9, 2]}, - {name: 'stroke', args: []}, - {name: 'beginPath', args: []}, - {name: 'setLineWidth', args: [1]}, - {name: 'moveTo', args: [8.5, 0]}, - {name: 'lineTo', args: [8.5, 12]}, - {name: 'stroke', args: []}, - {name: 'beginPath', args: []}, - {name: 'setLineWidth', args: [3]}, - {name: 'moveTo', args: [8, 13.5]}, - {name: 'lineTo', args: [10, 13.5]}, - {name: 'stroke', args: []}, - {name: 'beginPath', args: []}, - {name: 'setLineWidth', args: [2]}, - {name: 'moveTo', args: [11, 15]}, - {name: 'lineTo', args: [11, 4]}, - {name: 'stroke', args: []}, - ]); - }); - }); From ea51b3a27f5e46c6b85b44335bd36e60650ed16b Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Mon, 11 Feb 2019 12:13:17 +0200 Subject: [PATCH 10/16] remove unnecessary falsy fallbacks --- src/elements/element.rectangle.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index 6db39346401..6b10d5706ae 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -163,7 +163,7 @@ module.exports = Element.extend({ bottom = vm.base; signX = 1; signY = bottom > top ? 1 : -1; - borderSkipped = valueOrDefault(vm.borderSkipped, 'bottom') || ''; + borderSkipped = valueOrDefault(vm.borderSkipped, 'bottom'); } else { // horizontal bar left = vm.base; @@ -172,7 +172,7 @@ module.exports = Element.extend({ bottom = vm.y + vm.height / 2; signX = right > left ? 1 : -1; signY = 1; - borderSkipped = valueOrDefault(vm.borderSkipped, 'left') || ''; + borderSkipped = valueOrDefault(vm.borderSkipped, 'left'); } ctx.fillStyle = vm.backgroundColor; From 0299def3c47664caf345b0e51dbabb192d74778a Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Tue, 12 Feb 2019 13:06:01 +0200 Subject: [PATCH 11/16] simplify drawLines --- src/elements/element.rectangle.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index 6b10d5706ae..4e93b365423 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -61,24 +61,28 @@ function drawLines(ctx, lines) { var ilen = lines.length; var line, prevLine; + if (!ilen) { + return; + } + + prevLine = lines[0]; + ctx.beginPath(); + ctx.lineWidth = prevLine.w; + for (; i < ilen; ++i) { line = lines[i]; - if (i === 0 || prevLine.w !== line.w) { - if (i > 0) { - ctx.stroke(); - } + if (prevLine.w !== line.w) { + ctx.stroke(); ctx.beginPath(); ctx.lineWidth = line.w; } - if (i === 0 || prevLine.x2 !== line.x1 || prevLine.y2 !== line.y1) { + if (prevLine.x2 !== line.x1 || prevLine.y2 !== line.y1) { ctx.moveTo(line.x1, line.y1); } ctx.lineTo(line.x2, line.y2); prevLine = line; } - if (ilen) { - ctx.stroke(); - } + ctx.stroke(); } // eslint-disable-next-line complexity From a2f483728147b240a4b54a69c262832677b0c91a Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 14 Feb 2019 01:05:22 +0200 Subject: [PATCH 12/16] update per comments - borderWidth not inverted - borderSkipped on top of borderWidth --- src/elements/element.rectangle.js | 165 ++++++++---------- .../borderWidth/indexable-object.js | 2 + .../controller.bar/borderWidth/object.js | 2 + .../controller.bar/borderWidth/object.png | Bin 2069 -> 2129 bytes .../borderWidth/scriptable-object.js | 2 + .../borderWidth/scriptable-object.png | Bin 4664 -> 1601 bytes .../controller.bar/horizontal-borders.js | 1 + .../controller.bar/horizontal-borders.png | Bin 1613 -> 1611 bytes 8 files changed, 84 insertions(+), 88 deletions(-) diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index 4e93b365423..a64319c7224 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -30,22 +30,22 @@ function isVertical(bar) { */ function getBarBounds(bar) { var vm = bar._view; - var x1, x2, y1, y2; + var x1, x2, y1, y2, half; if (isVertical(bar)) { // vertical - var halfWidth = vm.width / 2; - x1 = vm.x - halfWidth; - x2 = vm.x + halfWidth; + half = vm.width / 2; + x1 = vm.x - half; + x2 = vm.x + half; y1 = Math.min(vm.y, vm.base); y2 = Math.max(vm.y, vm.base); } else { // horizontal bar - var halfHeight = vm.height / 2; + half = vm.height / 2; x1 = Math.min(vm.x, vm.base); x2 = Math.max(vm.x, vm.base); - y1 = vm.y - halfHeight; - y2 = vm.y + halfHeight; + y1 = vm.y - half; + y2 = vm.y + half; } return { @@ -86,134 +86,123 @@ function drawLines(ctx, lines) { } // eslint-disable-next-line complexity -function buildBorderLines(rect, width, offset) { +function buildBorderLines(rect, width) { var lines = []; - var halfOffsetLeft = offset.left / 2; - var halfOffsetRight = offset.right / 2; - var halfOffsetTop = offset.top / 2; - var halfOffsetBottom = offset.bottom / 2; + var halfLeft = width.left / 2; + var halfRight = width.right / 2; + var halfTop = width.top / 2; + var halfBottom = width.bottom / 2; if (width.bottom) { lines.push({ w: width.bottom, x1: rect.right, - y1: rect.bottom + halfOffsetBottom, - x2: rect.left + (width.bottom === width.left ? halfOffsetLeft : offset.left), - y2: rect.bottom + halfOffsetBottom + y1: rect.bottom - halfBottom, + x2: rect.left + (width.bottom === width.left ? halfLeft : width.left), + y2: rect.bottom - halfBottom }); } if (width.left) { lines.push({ w: width.left, - x1: rect.left + halfOffsetLeft, - y1: rect.bottom + (width.bottom === width.left ? halfOffsetBottom : 0), - x2: rect.left + halfOffsetLeft, - y2: rect.top + (width.left === width.top ? halfOffsetTop : offset.top) + x1: rect.left + halfLeft, + y1: rect.bottom - (width.bottom === width.left ? halfBottom : 0), + x2: rect.left + halfLeft, + y2: rect.top + (width.left === width.top ? halfTop : width.top) }); } if (width.top) { lines.push({ w: width.top, - x1: rect.left + (width.left === width.top ? halfOffsetLeft : 0), - y1: rect.top + halfOffsetTop, - x2: rect.right + (width.top === width.right ? halfOffsetRight : offset.right), - y2: rect.top + halfOffsetTop + x1: rect.left + (width.left === width.top ? halfLeft : 0), + y1: rect.top + halfTop, + x2: rect.right - (width.top === width.right ? halfRight : width.right), + y2: rect.top + halfTop }); } if (width.right) { lines.push({ w: width.right, - x1: rect.right + halfOffsetRight, - y1: rect.top + (width.top === width.right ? halfOffsetTop : 0), - x2: rect.right + halfOffsetRight, - y2: rect.bottom + offset.bottom + x1: rect.right - halfRight, + y1: rect.top + (width.top === width.right ? halfTop : 0), + x2: rect.right - halfRight, + y2: rect.bottom - width.bottom }); } return lines; } -function parseBorderWidth(borderWidth, borderSkipped, maxWidth, maxHeight) { - if (helpers.isObject(borderWidth)) { - return { - bottom: Math.min(borderWidth.bottom || 0, maxHeight), - left: Math.min(borderWidth.left || 0, maxWidth), - top: Math.min(borderWidth.top || 0, maxHeight), - right: Math.min(borderWidth.right || 0, maxWidth) - }; +function parseBorderWidth(value, skipped, maxWidth, maxHeight) { + var t, r, b, l; + + if (helpers.isObject(value)) { + t = +value.top || 0; + r = +value.right || 0; + b = +value.bottom || 0; + l = +value.left || 0; + } else { + t = r = b = l = +value || 0; } - maxWidth = Math.min(borderWidth, maxWidth); - maxHeight = Math.min(borderWidth, maxHeight); return { - bottom: borderSkipped === 'bottom' ? 0 : maxHeight, - left: borderSkipped === 'left' ? 0 : maxWidth, - top: borderSkipped === 'top' ? 0 : maxHeight, - right: borderSkipped === 'right' ? 0 : maxWidth + top: Math.min(maxHeight, skipped === 'top' ? 0 : t), + right: Math.min(maxWidth, skipped === 'right' ? 0 : r), + bottom: Math.min(maxHeight, skipped === 'bottom' ? 0 : b), + left: Math.min(maxWidth, skipped === 'left' ? 0 : l) }; } +function flip(orig, v1, v2) { + return orig === v1 ? v2 : orig === v2 ? v1 : orig; +} + +function parseBorderSkipped(bar) { + var vm = bar._view; + var vertical = isVertical(bar); + var borderSkipped = valueOrDefault(vm.borderSkipped, vertical ? 'bottom' : 'left'); + + if (vertical && vm.base < vm.y) { + borderSkipped = flip(borderSkipped, 'bottom', 'top'); + } + if (!vertical && vm.base > vm.x) { + borderSkipped = flip(borderSkipped, 'left', 'right'); + } + return borderSkipped; +} + module.exports = Element.extend({ draw: function() { - var ctx = this._chart.ctx; - var vm = this._view; + var me = this; + var ctx = me._chart.ctx; + var vm = me._view; var borderWidth = vm.borderWidth; - var left, right, top, bottom, signX, signY, borderSkipped, offset; - - if (!vm.horizontal) { - // bar - left = vm.x - vm.width / 2; - right = vm.x + vm.width / 2; - top = vm.y; - bottom = vm.base; - signX = 1; - signY = bottom > top ? 1 : -1; - borderSkipped = valueOrDefault(vm.borderSkipped, 'bottom'); - } else { - // horizontal bar - left = vm.base; - right = vm.x; - top = vm.y - vm.height / 2; - bottom = vm.y + vm.height / 2; - signX = right > left ? 1 : -1; - signY = 1; - borderSkipped = valueOrDefault(vm.borderSkipped, 'left'); - } + var bounds = getBarBounds(me); + var left = bounds.left; + var top = bounds.top; + var width = bounds.right - left; + var height = bounds.bottom - top; ctx.fillStyle = vm.backgroundColor; ctx.strokeStyle = vm.borderColor; if (!borderWidth) { - ctx.fillRect(left, bottom, right - left, top - bottom); + ctx.fillRect(left, top, width, height); return; } borderWidth = parseBorderWidth( borderWidth, - borderSkipped, - Math.abs(left - right) / 2, - Math.abs(top - bottom) / 2); - - offset = { - left: borderWidth.left * signX, - right: borderWidth.right * -signX, - top: borderWidth.top * signY, - bottom: borderWidth.bottom * -signY, - }; + parseBorderSkipped(me), + width / 2, + height / 2); ctx.fillRect( - left + offset.left, - bottom + offset.bottom, - right + offset.right - left - offset.left, - top + offset.top - bottom - offset.bottom); - - drawLines(ctx, - buildBorderLines({ - left: left, - top: top, - right: right, - bottom: bottom - }, borderWidth, offset) - ); + left + borderWidth.left, + top + borderWidth.top, + width - borderWidth.left - borderWidth.right, + height - borderWidth.top - borderWidth.bottom); + + drawLines(ctx, buildBorderLines(bounds, borderWidth)); }, height: function() { diff --git a/test/fixtures/controller.bar/borderWidth/indexable-object.js b/test/fixtures/controller.bar/borderWidth/indexable-object.js index 368fd28346c..97f9af5d46f 100644 --- a/test/fixtures/controller.bar/borderWidth/indexable-object.js +++ b/test/fixtures/controller.bar/borderWidth/indexable-object.js @@ -7,6 +7,7 @@ module.exports = { { // option in dataset data: [0, 5, 10, null, -10, -5], + borderSkipped: false, borderWidth: [ {}, {bottom: 1, left: 1, top: 1, right: 1}, @@ -29,6 +30,7 @@ module.exports = { rectangle: { backgroundColor: 'transparent', borderColor: '#80808080', + borderSkipped: false, borderWidth: [ {bottom: 1, left: 5, top: 1, right: 5}, {bottom: 1, left: 4, top: 1, right: 4}, diff --git a/test/fixtures/controller.bar/borderWidth/object.js b/test/fixtures/controller.bar/borderWidth/object.js index fb39473a627..9133b30aec5 100644 --- a/test/fixtures/controller.bar/borderWidth/object.js +++ b/test/fixtures/controller.bar/borderWidth/object.js @@ -7,6 +7,7 @@ module.exports = { { // option in dataset data: [0, 5, 10, null, -10, -5], + borderSkipped: false, borderWidth: {bottom: 1, left: 2, top: 3, right: 4} }, { @@ -22,6 +23,7 @@ module.exports = { rectangle: { backgroundColor: 'transparent', borderColor: '#888', + borderSkipped: false, borderWidth: {bottom: 4, left: 3, top: 2, right: 1} } }, diff --git a/test/fixtures/controller.bar/borderWidth/object.png b/test/fixtures/controller.bar/borderWidth/object.png index bec6da56919b50bb26a6e5be27ab483e86d2dd75..fdd58a91e879bce2ee74d589c49c1b4c1772de3c 100644 GIT binary patch literal 2129 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is>yBs^UlLn`LHy={0-I#r@I zFw5*fk;cRuEYb;rnVhPu69%jcedp3lO-V8g(uz`!7(!T@A9 zvotU;BurosU|`@80(zpsk;wt5!GnQ=fq{(^=%WJ)j0&TQ34{j!dyBsO^=1=hpDmQh z*Sl|TYg_xbPS)SwKmLBUY=VD$X&2F6S#6T^arQF=&*!+~bD1Fu*6zFoFi zh~aqQp1n^i_sk04Zdb-&2Z~W9NY(>V7hDSO;;OUE(XNv`|HFV66NR{R|no(~V+Nir;7 zz5g4_f%fx1YyNoxQ`mgl42Bx5en$Hch6)~u27B8Vz6a)eG1y%3Jpjz@_P{JYL5+cd zi(`}?n&Hqe7nE4uUQN%pT%T9_hM|T5SY89m83hZsBlCl=PoL-60&|ollLNf|jAc2nx;k&y`)8}azuv*q@Yv!WM}urV Y!@)A#f8r!oM6r>mdKI;Vst03d?D7XSbN literal 2069 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is@o?|8a6hE&XXduQ)SnRJQP z#G`o!o?4i!;51*LH08zY*w!+}JSWx{>XVd86X$4e7v{DT`dGs%SU(L_S3=9e97Bl>KeCxW^{PX)c7#MaiGdVCY7MJTLepSly^Yi=XZ|&f_jvc&cAi_ zIWGp8;~!tn`dwj|zpnN@!ykSo2VnjK=PnS{&-CD|uKce5XU@;R{?US=@9~chb6F0| zzn|d$-;_bGkClPpfRa8#9K#-9w*16-LEV87l7SJ_3qR%q{RjRj+cPmgSk1w}I6;kp zfoqf=qT$f6n(e^V=QVcwMS-P&A>#&N2J<=nObidsaW=eTu$b%j9#~Yz`!GCVW?*u}kIN{r*yT&xGB-YviB5*56$7 z|Kq3I+w<#duEa4sSUQ)1p}|(2fg#~MCj-NcVxVo`=3o_Ku$u%FcLXY61FDoz0cx2r z`~(`7zk7GOcEa(;AHThq=|BGX?fl34?TX~rKVxBFI6sG>A`=)?70*}>cv~|v957aA zh`R+0jCq!fA1?VZFkJ9qNa0{$SjE}E!^q%Z$~ZxhfnkLjgAq{gRThUf28IQ*7)}T< zFoXy*NHQ@rWHBi?GcW}Bj%uQAKrs9`dAD{a|ECI@dv!A&pTA%8Z-1r4ox9I}eEN6$ zdVJc?f4k-1+3+wt14d^#Fgi2JnI7B)W`hNHIUCB$f$>tN&T#K8Fj{ZTB@o{q&TuwN uF9YgGqpk-=(+rNxGyKaR;tSWZzr4By`O^H73vGc_1B0ilpUXO@geCx8n^b53 literal 4664 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 NApCtvNLi3H^G_wWkfZx z{rA^mJ3a=6d%5Qy2mYV4w(f6aJUau!`)3S0{>d;f#GGL)_-pCPz_8=SyyMBS*Q>wU z+S=aDT|3X7L238DnkTQI_GjRupOjer-MY1K@>@o;ddvCeoiD{$GBhlWbD7k4o{>T4 ztJ0*B8a{@EmEseZJT7Kn@ZQ^@B0Ha%A>x&*%F7=z3XJD9CCFp5o$Ih_f zO1HU%y`6v-l}c3HN?krW%E>{}`Q;d)7~Sw&tEa1H*xd$0W=rt=oS5_;G!X z=+)o(1*8sanJmaGGg+3IBm0=d{cYd={P`0bG@F^>M#}k5E9%e3T>AO{|3WK%2+Y*nDUwieJx#b(sS*0M8R*4H-Sc!+0vi`^Z@#AZ*A9I1)fYBa5 zv4LG}f>d(NG?R3YE1746?X7RPa9b(j`q#KiF;{=HbL^F6c>X!^Qp)6K%%+oX0aM49 z_4$lZNd#y3gj}DL%K+#|1=?gOCE7PPc>ml)v>N&S#65FF!*B^1YRP~yH z!R+Q{n^*G|b#OXp<$wlGo=L8FJvBocDC&y4QP`Y+Js3`R=tKx4ixJnQ=$f zU#&&S;C^XJdszY0t;50a|o`hS(b`RWf$;(D+&o%5WrAaVIV z!(;txK_;f0XFTu~lyf}(GF)7Rhdy!Rqy35g`gK3<0&^|G|9W4{AIhoRH9G9j3KH~m L^>bP0l+XkKj4Im% literal 1613 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is@oL_A#_Ln`LHy?ZcICQ`)V z;;DnDZ(0a8I~6h<+Qt&s;r2*hi&2x|t@i0io+d+{o?WNDl)j$NY~K9*`}~9NqQrm( z(Gc`WB-dQqbM-U3LyE!Hs(%a}YhG8a|5?Pq;FEWI+d?p7U0uf$1_p%ydl#UvzzTUK zO9lpxEBq5>7#LVywR`XZ{ruHgg`I(+@t5KxW`+ibzXB&285|a%Xp6Z1HSF_K28J1T zs`pDBKOS;ko?+rg_L|9Yv0wi%3ape@kbVE~;ls=KUj1ZNFq#o%ThHL~ioGKYAAL+> zd-dx2Gj99;t>_0@TWiGI=*BKcO?6zcBXAg8#dND&xJkaAIuh)Xd!#*!!`{m) zm^5Tw{mss?ch8 z!$M_thJw4V{xY|`Vr418M;~jfcdYqY%KFC-=myL{q=kq_uc4jytj!>2GWx8(XYcSL zR}h%O^)uIj9D0whA@{b5Zx}G}CNMk90{KRfy}=|O6hwRsnfE|yWEd{(0fnF?!;(s1 z*a{Reczg!M^Am;%&(;DBJi(}NHX54&|9^=cSi5BBDxjXK`{sum`d0u|XZ&HjuQL5g z2+)8pA3l7@%>VjnQpp;G^H#(;I4lRM!$rS1@o7c<5tmC<*2iwdg5rE4^MZep!JfA# z+8`=VJ9fR zbucHq1Ld+u3^R6vN*W7>XX}WU1{(O`*RQHeHDIgO=pX;^@CvBZ>iHP@ISg2uFfdI2 z^y9~m)O=vdk1YokDv~~6$9-jXSq-uV7wst_%9}nJhOh5$yq@2; z0_^AqzkqqRV4LBw{=K=k%a#K(E<-x7s44i1uf#wqQT~YS{;%#1G#CyJ%>2h@uteqV U_vN2~Ml&#Yy85}Sb4q9e03%>>TL1t6 From 66d84ec9e163b4cf1c532702de28f5c6a359cc87 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 14 Feb 2019 01:12:57 +0200 Subject: [PATCH 13/16] update docs --- docs/charts/bar.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/charts/bar.md b/docs/charts/bar.md index 5d286022089..a4bd86dd84f 100644 --- a/docs/charts/bar.md +++ b/docs/charts/bar.md @@ -107,6 +107,8 @@ This setting is used to avoid drawing the bar stroke at the base of the fill. In general, this does not need to be changed except when creating chart types that derive from a bar chart. +**Note:** for negative bars in vertical chart, `top` and `bottom` are flipped. Same goes for `left` and `right` in horizontal chart. + Options are: * `'bottom'` * `'left'` @@ -116,9 +118,7 @@ Options are: #### borderWidth -If this value is a number, it is applied to all sides of the rectangle (left, top, right, bottom), except [`borderSkipped`](#borderskipped). If this value is an object, the `left` property defines the left border width. Similarly the `right`, `top` and `bottom` properties can also be specified. Omitted borders are skipped. - -**Note:** for negative bars in vertical chart, `top` and `bottom` are flipped. Same goes for `left` and `right` in horizontal chart. +If this value is a number, it is applied to all sides of the rectangle (left, top, right, bottom), except [`borderSkipped`](#borderskipped). If this value is an object, the `left` property defines the left border width. Similarly the `right`, `top` and `bottom` properties can also be specified. Omitted borders and [`borderSkipped`](#borderskipped) are skipped. ### Interactions From 40b4327045f227c50ff65e086034d66ba5b258ae Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Sun, 17 Feb 2019 20:47:54 +0200 Subject: [PATCH 14/16] fill border instead of drawing lines, add borderRadius --- docs/charts/bar.md | 2 + src/controllers/controller.bar.js | 4 +- src/elements/element.rectangle.js | 199 ++++++++++-------- .../controller.bar/borderRadius/indexable.js | 56 +++++ .../controller.bar/borderRadius/indexable.png | Bin 0 -> 5210 bytes .../controller.bar/borderRadius/scriptable.js | 54 +++++ .../borderRadius/scriptable.png | Bin 0 -> 5997 bytes .../controller.bar/borderRadius/value.js | 42 ++++ .../controller.bar/borderRadius/value.png | Bin 0 -> 5824 bytes .../controller.bar/borderWidth/object.png | Bin 2129 -> 2113 bytes .../controller.bar/horizontal-borders.png | Bin 1611 -> 1524 bytes 11 files changed, 272 insertions(+), 85 deletions(-) create mode 100644 test/fixtures/controller.bar/borderRadius/indexable.js create mode 100644 test/fixtures/controller.bar/borderRadius/indexable.png create mode 100644 test/fixtures/controller.bar/borderRadius/scriptable.js create mode 100644 test/fixtures/controller.bar/borderRadius/scriptable.png create mode 100644 test/fixtures/controller.bar/borderRadius/value.js create mode 100644 test/fixtures/controller.bar/borderRadius/value.png diff --git a/docs/charts/bar.md b/docs/charts/bar.md index a4bd86dd84f..5cee8d359a6 100644 --- a/docs/charts/bar.md +++ b/docs/charts/bar.md @@ -72,6 +72,7 @@ the color of the bars is generally set this way. | [`borderColor`](#styling) | [`Color`](../general/colors.md) | Yes | Yes | `'rgba(0, 0, 0, 0.1)'` | [`borderSkipped`](#borderskipped) | `string` | Yes | Yes | `'bottom'` | [`borderWidth`](#borderwidth) | number|object | Yes | Yes | `0` +| [`borderRadius`](#styling) | `number` | Yes | Yes | `0` | [`data`](#data-structure) | `object[]` | - | - | **required** | [`hoverBackgroundColor`](#interactions) | [`Color`](../general/colors.md) | - | Yes | `undefined` | [`hoverBorderColor`](#interactions) | [`Color`](../general/colors.md) | - | Yes | `undefined` @@ -98,6 +99,7 @@ The style of each bar can be controlled with the following properties: | `borderColor` | The bar border color. | [`borderSkipped`](#borderskipped) | The edge to skip when drawing bar. | [`borderWidth`](#borderwidth) | The bar border width (in pixels). +| `borderRadius` | Rounding corners of outer border edge. All these values, if `undefined`, fallback to the associated [`elements.rectangle.*`](../configuration/elements.md#rectangle-configuration) options. diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index aef53b68afe..f0cdd1ddc7e 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -158,6 +158,7 @@ module.exports = DatasetController.extend({ borderColor: options.borderColor, borderSkipped: options.borderSkipped, borderWidth: options.borderWidth, + borderRadius: options.borderRadius, datasetLabel: dataset.label, label: me.chart.data.labels[index] }; @@ -399,7 +400,8 @@ module.exports = DatasetController.extend({ 'backgroundColor', 'borderColor', 'borderSkipped', - 'borderWidth' + 'borderWidth', + 'borderRadius' ]; for (i = 0, ilen = keys.length; i < ilen; ++i) { diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index a64319c7224..197bdddc6c8 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -56,82 +56,6 @@ function getBarBounds(bar) { }; } -function drawLines(ctx, lines) { - var i = 0; - var ilen = lines.length; - var line, prevLine; - - if (!ilen) { - return; - } - - prevLine = lines[0]; - ctx.beginPath(); - ctx.lineWidth = prevLine.w; - - for (; i < ilen; ++i) { - line = lines[i]; - if (prevLine.w !== line.w) { - ctx.stroke(); - ctx.beginPath(); - ctx.lineWidth = line.w; - } - if (prevLine.x2 !== line.x1 || prevLine.y2 !== line.y1) { - ctx.moveTo(line.x1, line.y1); - } - ctx.lineTo(line.x2, line.y2); - prevLine = line; - } - ctx.stroke(); -} - -// eslint-disable-next-line complexity -function buildBorderLines(rect, width) { - var lines = []; - var halfLeft = width.left / 2; - var halfRight = width.right / 2; - var halfTop = width.top / 2; - var halfBottom = width.bottom / 2; - - if (width.bottom) { - lines.push({ - w: width.bottom, - x1: rect.right, - y1: rect.bottom - halfBottom, - x2: rect.left + (width.bottom === width.left ? halfLeft : width.left), - y2: rect.bottom - halfBottom - }); - } - if (width.left) { - lines.push({ - w: width.left, - x1: rect.left + halfLeft, - y1: rect.bottom - (width.bottom === width.left ? halfBottom : 0), - x2: rect.left + halfLeft, - y2: rect.top + (width.left === width.top ? halfTop : width.top) - }); - } - if (width.top) { - lines.push({ - w: width.top, - x1: rect.left + (width.left === width.top ? halfLeft : 0), - y1: rect.top + halfTop, - x2: rect.right - (width.top === width.right ? halfRight : width.right), - y2: rect.top + halfTop - }); - } - if (width.right) { - lines.push({ - w: width.right, - x1: rect.right - halfRight, - y1: rect.top + (width.top === width.right ? halfTop : 0), - x2: rect.right - halfRight, - y2: rect.bottom - width.bottom - }); - } - return lines; -} - function parseBorderWidth(value, skipped, maxWidth, maxHeight) { var t, r, b, l; @@ -170,6 +94,103 @@ function parseBorderSkipped(bar) { return borderSkipped; } +function buildBorderSections(bounds, inner, border, radius) { + var top = 'top'; + var left = 'left'; + var right = 'right'; + var bottom = 'bottom'; + var borders = [top, right, bottom, left]; + var corners = [[left, top], [right, top], [right, bottom], [left, bottom]]; + var sections = []; + var current = []; + var count = 0; + var startIdx = 0; + var i, b, n, p, c1, c2; + + radius = radius || 0; + + for (i = 0; i < 4; ++i) { + if (!border[borders[i]]) { + startIdx = i; + break; + } + } + + for (i = 0; i < 4; ++i) { + b = borders[(i + startIdx) % 4]; + if (border[b]) { + n = borders[(i + startIdx + 1) % 4]; + p = borders[(i + startIdx + 3) % 4]; + c1 = corners[(i + startIdx) % 4]; + c2 = corners[(i + startIdx + 1) % 4]; + if (i === 0 || !border[p]) { + if (current.length) { + sections.push({count: count, corners: current}); + current = []; + count = 0; + } + current.push({x1: bounds[c1[0]], y1: bounds[c1[1]], x2: inner[c1[0]], y2: inner[c1[1]], r: radius}); + } + if (i < 3 || !border[n]) { + current.push({x1: bounds[c2[0]], y1: bounds[c2[1]], x2: inner[c2[0]], y2: inner[c2[1]], r: radius}); + } + count++; + } + } + if (current.length) { + sections.push({count: count, corners: current}); + } + return sections; +} + +function drawBorderSection(ctx, section) { + ctx.beginPath(); + + var c = section.corners; + var p1 = c[c.length - 1]; + var p2 = c[0]; + var r = p2.r; + var ilen = section.count === 4 ? 5 : section.count; + var i; + + // if all borders are drawn and we have a border radius, move starting point + if (section.count === 4 && p2.r) { + p2 = {x1: p2.x1, y1: p2.y1, x2: p2.x2, y2: p2.y2, r: p2.r}; + if (p2.x1 === p1.x1) { + p2.x1 += p2.x2 > p2.x1 ? p2.r : -p2.r; + } + if (p2.y1 === p1.y1) { + p2.y1 += p2.y2 > p2.y1 ? p2.r : -p2.r; + } + } + + ctx.moveTo(p2.x1, p2.y1); + for (i = 0; i < ilen; i++) { + p1 = p2; + p2 = c[(i + 1) % c.length]; + ctx.arcTo(p1.x1, p1.y1, p2.x1, p2.y1, Math.max(p1.r, r)); + r = p1.r; + } + if (section.count < 4) { + ctx.lineTo(p2.x1, p2.y1); + ctx.lineTo(p2.x2, p2.y2); + } else { + p1 = p2; + p2 = c[0]; + ctx.closePath(); + ctx.moveTo(p2.x2, p2.y2); + } + while (i > 0) { + p1 = p2; + p2 = c[(i - 1) % 4]; + ctx.arcTo(p1.x2, p1.y2, p2.x2, p2.y2, 0); + i--; + } + ctx.lineTo(p2.x2, p2.y2); + ctx.closePath(); + ctx.fill('evenodd'); +} + module.exports = Element.extend({ draw: function() { var me = this; @@ -179,11 +200,14 @@ module.exports = Element.extend({ var bounds = getBarBounds(me); var left = bounds.left; var top = bounds.top; - var width = bounds.right - left; + var right = bounds.right; + var bottom = bounds.bottom; + var width = right - left; var height = bounds.bottom - top; + var borderRadius = vm.borderRadius || 0; + var inner, i, sections; ctx.fillStyle = vm.backgroundColor; - ctx.strokeStyle = vm.borderColor; if (!borderWidth) { ctx.fillRect(left, top, width, height); @@ -196,13 +220,20 @@ module.exports = Element.extend({ width / 2, height / 2); - ctx.fillRect( - left + borderWidth.left, - top + borderWidth.top, - width - borderWidth.left - borderWidth.right, - height - borderWidth.top - borderWidth.bottom); + inner = { + left: left + borderWidth.left, + top: top + borderWidth.top, + right: right - borderWidth.right, + bottom: bottom - borderWidth.bottom + }; + + ctx.fillRect(inner.left, inner.top, inner.right - inner.left, inner.bottom - inner.top); - drawLines(ctx, buildBorderLines(bounds, borderWidth)); + ctx.fillStyle = vm.borderColor; + sections = buildBorderSections(bounds, inner, borderWidth, borderRadius); + for (i = 0; i < sections.length; i++) { + drawBorderSection(ctx, sections[i]); + } }, height: function() { diff --git a/test/fixtures/controller.bar/borderRadius/indexable.js b/test/fixtures/controller.bar/borderRadius/indexable.js new file mode 100644 index 00000000000..60f65f0850b --- /dev/null +++ b/test/fixtures/controller.bar/borderRadius/indexable.js @@ -0,0 +1,56 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderWidth: 5, + borderRadius: [ + 0, + 1, + 2, + 3, + 4, + 5 + ] + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#888', + borderWidth: 5, + borderRadius: [ + 5, + 4, + 3, + 2, + 1, + 0 + ] + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [{display: false}] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderRadius/indexable.png b/test/fixtures/controller.bar/borderRadius/indexable.png new file mode 100644 index 0000000000000000000000000000000000000000..4ad7ed05c32abfa1f877a1354440e4fd6184fa3f GIT binary patch literal 5210 zcmeI0TU1k58proJJ;6gniQ*VnOEC&IGnPs^wSeW4qtXVnTsrD7MNGh2kdYE-1Powu zqE4~P7I4||!o^@)ys(tOLWW8b5+~zehZ&+~DhlL+$j#JTln_V^nVrr{ALeBq=3&-K zUXr!fx3j-|+28N{|KH~sX`Ch0m#F}NB`L{CzX1S+9}$2Dz_(j<1Ni`~Sd)^pdFLVS z)1fzSe?0$TpIxxw$1m2<@Q&k>oaB?+XglzZ2V0h`YFghE5V3_w((nDXP{u?Vu^;XST?P|Mj;b3T=Y(!pe z#_^B5=i|(BYnfae)@MJ}CpuG5zp$_KVU_)Xv!@nFbjxYr(%Vt|$}LE{(JE#IX{u`y zt{2oB4298AVQ#K-8KXE0MNQWy#y_E`1?2>YKXW^n#$W{<1?>U(>ue$~#!A(x_|Xlu z+W8A|y`_IFcHyp0TQw^M1}h(gC05OiJ}q<4@dNVayNuS=-V|?~R#B}&&b~Xi6>sb& zL9BKDPwsaRR?tE~vwp~(gT}z5V)hF5UqMj5`T}oNP(`i~B1KBf&4$A6iFZEEQ=*!4xp7Y;rs5}pGX@58&AWPYeM1hBo6nPVgeB~GlT{*177fHve%|b+gJ+flI-S0H_qE>|(=*=J%{+v}?H1_YfRxCVKi-55>>&Wg zR5XLRk>7W@ijF_pZg#jD{!_qromtlmK_4itb7efKxd>r^iJ^7cHN_SbWyFpKJg#M>!;!+y}nZ`VG^xo zCTKk}5Hj^T103mu)bxk=>*Bu*|EEpTZ2j@@CIsD#h=FYp`NXx$3t)?f6rTqO8bgBs zFa;cVzLkR!yY2{4499$$=yF>VxJ-Q~odgmnm1y9Ht`M+m^JE~6LG$fs?kW2T83>A@ zKE<|kFb(XaG%*R1Abe8n$xtBK+WZ{DX}C{LW$cMVGJbK;cOZiJ_;TRid|?m_2GmwW z=~MsV{<8Zo9Bj3-Y+yJv_9fjQoNTHdi*2h(xK~hbfnLjTsuDikgFnli%P=MUnC4D^sllZTrTnVbq;W0))N>X5Ajy zQjN_K&cDw#^pu*JR)f-QL`}D!wS_~ia0R+74W=HuWO2IPP)s=dE` zwJ?e179WSEp@5kV{7woar+wV{md*p;GEE{B%c4Tg)N!l)oBajzUy}c#G1QANbw{M- z>Y%whj8|4tQj*`5r4(L0A-yr#;;8UcOj%|JspRbI07`*lzG>IJ*cxN<2=rHz7QNh= zi83yz8sjMHPxD4oaoYX}o9X6JEtkt(D1CH_ukUemz+qT#I9TrLzp(>E=C3py1Cl%x z&a|}9sonTUG`lgCH(VI4vOi9PTzTK~v3?ym{dO>aD@lY)AURGBck}r$v;~gE1eepL z7pF&I0??o}KvVT-x)R#DOAv|;zNy(|H`{c>TPhUJ&u4Ol<59I$GV?;ZNepuDeehut S75xIhSIWC-No`wlj{FNX;hH!A literal 0 HcmV?d00001 diff --git a/test/fixtures/controller.bar/borderRadius/scriptable.js b/test/fixtures/controller.bar/borderRadius/scriptable.js new file mode 100644 index 00000000000..2ac4d612129 --- /dev/null +++ b/test/fixtures/controller.bar/borderRadius/scriptable.js @@ -0,0 +1,54 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderWidth: 10, + borderRadius: function(ctx) { + var value = ctx.dataset.data[ctx.dataIndex] || 0; + return Math.abs(value); + } + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5] + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#888', + borderWidth: 10, + borderRadius: function(ctx) { + return ctx.dataIndex * 2; + } + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [ + { + display: false, + ticks: { + beginAtZero: true + } + } + ] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderRadius/scriptable.png b/test/fixtures/controller.bar/borderRadius/scriptable.png new file mode 100644 index 0000000000000000000000000000000000000000..ee3951e5b25b225fcc49df8485fafa8b360a3a51 GIT binary patch literal 5997 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 literal 0 HcmV?d00001 diff --git a/test/fixtures/controller.bar/borderRadius/value.js b/test/fixtures/controller.bar/borderRadius/value.js new file mode 100644 index 00000000000..21c9e4a6862 --- /dev/null +++ b/test/fixtures/controller.bar/borderRadius/value.js @@ -0,0 +1,42 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderWidth: 2, + borderRadius: 4 + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#888', + borderWidth: 10, + borderRadius: 8 + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [{display: false}] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderRadius/value.png b/test/fixtures/controller.bar/borderRadius/value.png new file mode 100644 index 0000000000000000000000000000000000000000..45b2e7bdb60a49b9721ca023724073ff5548d169 GIT binary patch literal 5824 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 Uhu+BJ{s?~dnA@7=?_~e^AJ4N+NB{r; literal 0 HcmV?d00001 diff --git a/test/fixtures/controller.bar/borderWidth/object.png b/test/fixtures/controller.bar/borderWidth/object.png index fdd58a91e879bce2ee74d589c49c1b4c1772de3c..04576006af463429104cd7595d23318d113cf6cd 100644 GIT binary patch literal 2113 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is>yI6Pe(Ln`LHy|pnjHC&`M z@#wk(XCnT@ zzVF|&pJ9b61B2KPj)qtL5BTSO)nmxwWMKFw&Zxk!ij$#%m8F4Ug&I(S00Yy4Squyj zz^H1-Vq!>O991xM!lAK~@xzPBLJa(Nz?d@v#vBVIqXDS}{R}%^9R6Li{qb_{hVFNt zzSea!Ffcrb&)a_c`Rb&vWxH#4-TwIUknDWvd#^+78SbgSV@QyIn6RLevEba#Z!3P! zFW$F=#o?=L{gXMLbr=}heseSwpPT+JeQxo-rK}EL@Bh8`{l7IZmHqH%X<&G9Lj|Zr z7?_-rCj+y-A22=j`2w9~0W{Rg44C8|jw%}NLEzxWVDpIs64k&$2~^O7DTjQ9n7p#~W!Hm(3=AoPh`RKJGJ~EKQ^WFlhNn_zA9%NJ23ETa N44$rjF6*2UngBaj#$^Bi literal 2129 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is>yBs^UlLn`LHy={0-I#r@I zFw5*fk;cRuEYb;rnVhPu69%jcedp3lO-V8g(uz`!7(!T@A9 zvotU;BurosU|`@80(zpsk;wt5!GnQ=fq{(^=%WJ)j0&TQ34{j!dyBsO^=1=hpDmQh z*Sl|TYg_xbPS)SwKmLBUY=VD$X&2F6S#6T^arQF=&*!+~bD1Fu*6zFoFi zh~aqQp1n^i_sk04Zdb-&2Z~W9NY(>V7hDSO;;OUE(XNv`|HFV66NR{R|no(~V+Nir;7 zz5g4_f%fx1YyNoxQ`mgl42Bx5en$Hch6)~u27B8Vz6a)eG1y%3Jpjz@_P{JYL5+cd zi(`}?n&Hqe7nE4uUQN%pT%T9_hM|T5SY89m83hZsBlCl=PoL-60&|ollLNf|jAc2nx;k&y`)8}azuv*q@Yv!WM}urV Y!@)A#f8r!oM6r>mdKI;Vst03d?D7XSbN diff --git a/test/fixtures/controller.bar/horizontal-borders.png b/test/fixtures/controller.bar/horizontal-borders.png index 0dbb171a115dd2b8882e5985c35b340bc2dedd17..85b881cebd2c2d8062a7ea46f9684537414ee841 100644 GIT binary patch literal 1524 zcmeAS@N?(olHy`uVBq!ia0y~yU;;83893O0)X@p&(is?7cYC@xhE&XXd-q_bOtgT* zMZTj8HAL7I48$s)o{cVH3S!v&UT5zQj(ragU-vm2)B8$hhvbzjtFM09#t1ZncA(%` z)a%*L!p_SxOgx(L^$(-K&F_C*KNm4D@RZ9OzqCh&f#L8~{)s?}mPGV+gaQG{5l99n-L0!ia1_p%ydl#Taffe#fmJAFWS5UP5-e;d3$H&m{(C*h& zi+#(g{xNi{@&DjkReJR^yTg*Y29W*@5^Lw#Gbnv!c3F*&PM7Qdbw20I_xZmp7#MzR zFz$E%TfXhr@BCU>28Nm$%nv>mGcXjGvLAT-oPps%7EpEV{rD?THosiFZ9Rj_EB1~s zeDpQ-A8LDdz52aG>nzcIO8DB|s}P)_#q>bmrAxp!4orj(Y70 zX19nLgzc?w2-w@u;D?94F!SlhU%!4i2hC1pcP@GSzkUTD!-M@63`>rm zV`RAV+zyNBIVYqaMn)JV~x((@`n^%437x;C<{BXni6(Bd%Hsw$9&H@Rp z5}&YS9VnPsdqUsKD}?APEC|L!m!wr*sZUbMtS@6=Ft^-n^J>50)RH+rIoAV?P-JiT zas!yGX2WCU%%>Z`+(}}*eC6Edskd_FJ$o$i@OjPFc_!OInNi1W^>hA)6Th85eEasz z`|~O=hp|AWoefkr2(lk|1I!Hyj~Hgeg7S?F!)B234(5b1P?=H4aAqAS$MQAY1ZC$V zj0U?wW?C?$g(GSHRL2mbH~s#saF7Y>eiiY=`+-=%m^1}Bfq`Mi^sUz>BbP-h33 z4h!l!o-i;d1lYS2F)#?MkXHh#<+#E>QHFtmx!^#m!Vl%|zy1a~5HmUw?2>z=J&VKM%PW{P zWM2Kv&arpTo2ZavvjIh1+4Hs@J zMO^ zGELgD9+KusiJ|S$tLu-rT&jA_z+iTBv(2mdhEq%KefF=o0`bJ_bBqi*@R*wSbmO|$ zdzWlmzI^%awIEl%{q>n~M;63-CM$gO^2;yRukE#e5dC7(w($4zn6cCFv&S$G7#6^s zD(M5xPx<@@PoF;Re>NPHx$p5ceEzwH6O^Ajm=oTCN|;9sGj><~W9YD8c(x9d|M(hq zk|6!5jA75dee+D>K{oDuaoVuP0&I73&DA}?%nMBQfxuM1>z$mu{QexUdzx3`%hbR! z2b}3sdOqf6pFe;8{#9U(dyrHu*Z-^h%~yY5iJ}K9LUNun7A)Uqc&vXd$iS5Ij0e7g y9PaU#;o>Sh^obiE?N9XAulsQq=zBx}>U}YPD5rAQ=&(a8NYK;O&t;ucLK6U0L3ru_ From 23859af0914e3d3f03d537d31052af05633baff5 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Mon, 18 Feb 2019 09:11:55 +0200 Subject: [PATCH 15/16] longer variable names, comments, small improvements --- src/elements/element.rectangle.js | 119 ++++++++++++++++-------------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index 197bdddc6c8..d72494b82a8 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -33,14 +33,12 @@ function getBarBounds(bar) { var x1, x2, y1, y2, half; if (isVertical(bar)) { - // vertical half = vm.width / 2; x1 = vm.x - half; x2 = vm.x + half; y1 = Math.min(vm.y, vm.base); y2 = Math.max(vm.y, vm.base); } else { - // horizontal bar half = vm.height / 2; x1 = Math.min(vm.x, vm.base); x2 = Math.max(vm.x, vm.base); @@ -94,99 +92,108 @@ function parseBorderSkipped(bar) { return borderSkipped; } -function buildBorderSections(bounds, inner, border, radius) { +// border can be in 1 or 2 sections. build corners for each border in each section +function buildBorderSections(bounds, inner, border) { var top = 'top'; var left = 'left'; var right = 'right'; var bottom = 'bottom'; - var borders = [top, right, bottom, left]; + var borderKeys = [top, right, bottom, left]; var corners = [[left, top], [right, top], [right, bottom], [left, bottom]]; var sections = []; var current = []; - var count = 0; + var borderCount = 0; var startIdx = 0; - var i, b, n, p, c1, c2; - - radius = radius || 0; + var i, idx, nextIdx, c1, c2; + // start from a skipped border, so we can close the path for (i = 0; i < 4; ++i) { - if (!border[borders[i]]) { + if (!border[borderKeys[i]]) { startIdx = i; break; } } for (i = 0; i < 4; ++i) { - b = borders[(i + startIdx) % 4]; - if (border[b]) { - n = borders[(i + startIdx + 1) % 4]; - p = borders[(i + startIdx + 3) % 4]; - c1 = corners[(i + startIdx) % 4]; - c2 = corners[(i + startIdx + 1) % 4]; - if (i === 0 || !border[p]) { + idx = (i + startIdx) % 4; + if (border[borderKeys[idx]]) { + nextIdx = (i + startIdx + 1) % 4; + c1 = corners[idx]; + c2 = corners[nextIdx]; + // if this is first border, or previous border is skipped, add start corner + // previous index is found by adding 3 to keep the index positive in all cases + if (i === 0 || !border[borderKeys[(i + startIdx + 3) % 4]]) { if (current.length) { - sections.push({count: count, corners: current}); + sections.push({count: borderCount, corners: current}); current = []; - count = 0; + borderCount = 0; } - current.push({x1: bounds[c1[0]], y1: bounds[c1[1]], x2: inner[c1[0]], y2: inner[c1[1]], r: radius}); + current.push({x1: bounds[c1[0]], y1: bounds[c1[1]], x2: inner[c1[0]], y2: inner[c1[1]]}); } - if (i < 3 || !border[n]) { - current.push({x1: bounds[c2[0]], y1: bounds[c2[1]], x2: inner[c2[0]], y2: inner[c2[1]], r: radius}); + // if this is not last border or next border is skipped, add end corner + if (i < 3 || !border[borderKeys[nextIdx]]) { + current.push({x1: bounds[c2[0]], y1: bounds[c2[1]], x2: inner[c2[0]], y2: inner[c2[1]]}); } - count++; + borderCount++; } } if (current.length) { - sections.push({count: count, corners: current}); + sections.push({count: borderCount, corners: current}); } return sections; } -function drawBorderSection(ctx, section) { +function drawBorderSection(ctx, section, borderRadius) { ctx.beginPath(); - var c = section.corners; - var p1 = c[c.length - 1]; - var p2 = c[0]; - var r = p2.r; - var ilen = section.count === 4 ? 5 : section.count; + var corners = section.corners; + var borderCount = section.count; + var cornerCount = corners.length; + var startCorner = corners[corners.length - 1]; + var endCorner = corners[0]; + var ilen = borderCount === 4 ? 5 : borderCount; var i; - // if all borders are drawn and we have a border radius, move starting point - if (section.count === 4 && p2.r) { - p2 = {x1: p2.x1, y1: p2.y1, x2: p2.x2, y2: p2.y2, r: p2.r}; - if (p2.x1 === p1.x1) { - p2.x1 += p2.x2 > p2.x1 ? p2.r : -p2.r; + // if all borders are drawn and we have a border radius, + // move starting point out ouf the rounded section + if (borderRadius && borderCount === 4) { + // clone so original corner stays intact + endCorner = {x1: endCorner.x1, y1: endCorner.y1, x2: endCorner.x2, y2: endCorner.y2}; + if (endCorner.x1 === startCorner.x1) { + endCorner.x1 += endCorner.x2 > endCorner.x1 ? borderRadius : -borderRadius; } - if (p2.y1 === p1.y1) { - p2.y1 += p2.y2 > p2.y1 ? p2.r : -p2.r; + if (endCorner.y1 === startCorner.y1) { + endCorner.y1 += endCorner.y2 > endCorner.y1 ? borderRadius : -borderRadius; } } - ctx.moveTo(p2.x1, p2.y1); + // draw outer edge first + ctx.moveTo(endCorner.x1, endCorner.y1); for (i = 0; i < ilen; i++) { - p1 = p2; - p2 = c[(i + 1) % c.length]; - ctx.arcTo(p1.x1, p1.y1, p2.x1, p2.y1, Math.max(p1.r, r)); - r = p1.r; + startCorner = endCorner; + endCorner = corners[(i + 1) % cornerCount]; + ctx.arcTo(startCorner.x1, startCorner.y1, endCorner.x1, endCorner.y1, borderRadius); } - if (section.count < 4) { - ctx.lineTo(p2.x1, p2.y1); - ctx.lineTo(p2.x2, p2.y2); + + // in between edges + if (borderCount < 4) { + ctx.lineTo(endCorner.x1, endCorner.y1); + ctx.lineTo(endCorner.x2, endCorner.y2); } else { - p1 = p2; - p2 = c[0]; + // all corners are drawn, close the outer edge + startCorner = endCorner; + endCorner = corners[0]; ctx.closePath(); - ctx.moveTo(p2.x2, p2.y2); + ctx.moveTo(endCorner.x2, endCorner.y2); } - while (i > 0) { - p1 = p2; - p2 = c[(i - 1) % 4]; - ctx.arcTo(p1.x2, p1.y2, p2.x2, p2.y2, 0); - i--; + + // draw the inner edge in reverse direction + for (; i > 0; --i) { + startCorner = endCorner; + endCorner = corners[(i - 1) % cornerCount]; + ctx.arcTo(startCorner.x2, startCorner.y2, endCorner.x2, endCorner.y2, 0); } - ctx.lineTo(p2.x2, p2.y2); + ctx.lineTo(endCorner.x2, endCorner.y2); ctx.closePath(); ctx.fill('evenodd'); } @@ -203,7 +210,7 @@ module.exports = Element.extend({ var right = bounds.right; var bottom = bounds.bottom; var width = right - left; - var height = bounds.bottom - top; + var height = bottom - top; var borderRadius = vm.borderRadius || 0; var inner, i, sections; @@ -230,9 +237,9 @@ module.exports = Element.extend({ ctx.fillRect(inner.left, inner.top, inner.right - inner.left, inner.bottom - inner.top); ctx.fillStyle = vm.borderColor; - sections = buildBorderSections(bounds, inner, borderWidth, borderRadius); + sections = buildBorderSections(bounds, inner, borderWidth); for (i = 0; i < sections.length; i++) { - drawBorderSection(ctx, sections[i]); + drawBorderSection(ctx, sections[i], borderRadius); } }, From 700ae2c91d509d6e9c9626b545e734c82dc52ed9 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Mon, 18 Feb 2019 11:29:38 +0200 Subject: [PATCH 16/16] limit borderRadius to smallest non zero borderWidth --- src/elements/element.rectangle.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index d72494b82a8..741110e29d3 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -227,6 +227,14 @@ module.exports = Element.extend({ width / 2, height / 2); + // limit borderRadius to smallest non zero borderWidth + borderRadius = Math.min( + borderWidth.left || borderRadius, + borderWidth.top || borderRadius, + borderWidth.right || borderRadius, + borderWidth.bottom || borderRadius + ); + inner = { left: left + borderWidth.left, top: top + borderWidth.top,