]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Add circular prop to arc element (#10405)
authorSlava Terekhov <thabarbados@gmail.com>
Sat, 30 Jul 2022 15:08:38 +0000 (19:08 +0400)
committerGitHub <noreply@github.com>
Sat, 30 Jul 2022 15:08:38 +0000 (11:08 -0400)
* feat: add circular prop to arc element draw actions
* test: add test for arc element with circular:false prop
* feat: add circular prop to Arc element options
* docs: add decriptiption for new Polar area chart prop
* docs: fix circular prop description
* docs: add info about arc element circular prop to elements docs
* docs: move circular prop from general options to styling

docs/charts/polar.md
docs/configuration/elements.md
src/elements/element.arc.js
test/specs/element.arc.tests.js
types/index.esm.d.ts

index 5870dfd2f63b53ec0fd6a50f1a27e6ce275c1144..0f9c8fcee1b4c9d2cb88f71033206715b1d8de10 100644 (file)
@@ -66,6 +66,7 @@ The following options can be included in a polar area chart dataset to configure
 | [`hoverBorderColor`](#interactions) | [`Color`](../general/colors.md) | Yes | Yes | `undefined`
 | [`hoverBorderJoinStyle`](#interactions) | `'round'`\|`'bevel'`\|`'miter'` | Yes | Yes | `undefined`
 | [`hoverBorderWidth`](#interactions) | `number` | Yes | Yes | `undefined`
+| [`circular`](#styling) | `boolean` | Yes | Yes | `true`
 
 All these values, if `undefined`, fallback to the scopes described in [option resolution](../general/options)
 
@@ -85,6 +86,7 @@ The style of each arc can be controlled with the following properties:
 | `borderColor` | arc border color.
 | `borderJoinStyle` | arc border join style. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin).
 | `borderWidth` | arc border width (in pixels).
+| `circular` | By default the Arc is curved. If `circular: false` the Arc will be flat.
 
 All these values, if `undefined`, fallback to the associated [`elements.arc.*`](../configuration/elements.md#arc-configuration) options.
 
index ec5fe9659d02d6fb8584f24e5aa22f1edadbc5be..e37a12e27ec0ab8cd31a5a1667478bab7fd77e97 100644 (file)
@@ -101,3 +101,4 @@ Namespace: `options.elements.arc`, global arc options: `Chart.defaults.elements.
 | `borderColor` | [`Color`](/general/colors.md) | `'#fff'` | Arc stroke color.
 | `borderJoinStyle` | `'round'`\|`'bevel'`\|`'miter'` | `'bevel'`\|`'round'` | Line join style. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin). The default is `'round'` when `borderAlign` is `'inner'`
 | `borderWidth`| `number` | `2` | Arc stroke width.
+| `circular` | `boolean` | `true` | By default the Arc is curved. If `circular: false` the Arc will be flat
index 1e03f9e1a7b5e97fe46d6596b3d97cb76c2517ab..3ec9091b77ffcb2ab68b16d1ed60b6b63e8e8295 100644 (file)
@@ -93,7 +93,7 @@ function rThetaToXY(r, theta, x, y) {
  * @param {CanvasRenderingContext2D} ctx
  * @param {ArcElement} element
  */
-function pathArc(ctx, element, offset, spacing, end) {
+function pathArc(ctx, element, offset, spacing, end, circular) {
   const {x, y, startAngle: start, pixelMargin, innerRadius: innerR} = element;
 
   const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0);
@@ -131,52 +131,64 @@ function pathArc(ctx, element, offset, spacing, end) {
 
   ctx.beginPath();
 
-  // The first arc segment from point 1 to point 2
-  ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerEndAdjustedAngle);
+  if (circular) {
+    // The first arc segment from point 1 to point 2
+    ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerEndAdjustedAngle);
 
-  // The corner segment from point 2 to point 3
-  if (outerEnd > 0) {
-    const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y);
-    ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI);
-  }
+    // The corner segment from point 2 to point 3
+    if (outerEnd > 0) {
+      const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y);
+      ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI);
+    }
 
-  // The line from point 3 to point 4
-  const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y);
-  ctx.lineTo(p4.x, p4.y);
+    // The line from point 3 to point 4
+    const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y);
+    ctx.lineTo(p4.x, p4.y);
 
-  // The corner segment from point 4 to point 5
-  if (innerEnd > 0) {
-    const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y);
-    ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI);
-  }
+    // The corner segment from point 4 to point 5
+    if (innerEnd > 0) {
+      const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y);
+      ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI);
+    }
 
-  // The inner arc from point 5 to point 6
-  ctx.arc(x, y, innerRadius, endAngle - (innerEnd / innerRadius), startAngle + (innerStart / innerRadius), true);
+    // The inner arc from point 5 to point 6
+    ctx.arc(x, y, innerRadius, endAngle - (innerEnd / innerRadius), startAngle + (innerStart / innerRadius), true);
 
-  // The corner segment from point 6 to point 7
-  if (innerStart > 0) {
-    const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y);
-    ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI);
-  }
+    // The corner segment from point 6 to point 7
+    if (innerStart > 0) {
+      const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y);
+      ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI);
+    }
 
-  // The line from point 7 to point 8
-  const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y);
-  ctx.lineTo(p8.x, p8.y);
+    // The line from point 7 to point 8
+    const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y);
+    ctx.lineTo(p8.x, p8.y);
 
-  // The corner segment from point 8 to point 1
-  if (outerStart > 0) {
-    const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y);
-    ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle);
+    // The corner segment from point 8 to point 1
+    if (outerStart > 0) {
+      const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y);
+      ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle);
+    }
+  } else {
+    ctx.moveTo(x, y);
+
+    const outerStartX = Math.cos(outerStartAdjustedAngle) * outerRadius + x;
+    const outerStartY = Math.sin(outerStartAdjustedAngle) * outerRadius + y;
+    ctx.lineTo(outerStartX, outerStartY);
+
+    const outerEndX = Math.cos(outerEndAdjustedAngle) * outerRadius + x;
+    const outerEndY = Math.sin(outerEndAdjustedAngle) * outerRadius + y;
+    ctx.lineTo(outerEndX, outerEndY);
   }
 
   ctx.closePath();
 }
 
-function drawArc(ctx, element, offset, spacing) {
+function drawArc(ctx, element, offset, spacing, circular) {
   const {fullCircles, startAngle, circumference} = element;
   let endAngle = element.endAngle;
   if (fullCircles) {
-    pathArc(ctx, element, offset, spacing, startAngle + TAU);
+    pathArc(ctx, element, offset, spacing, startAngle + TAU, circular);
 
     for (let i = 0; i < fullCircles; ++i) {
       ctx.fill();
@@ -189,8 +201,7 @@ function drawArc(ctx, element, offset, spacing) {
       }
     }
   }
-
-  pathArc(ctx, element, offset, spacing, endAngle);
+  pathArc(ctx, element, offset, spacing, endAngle, circular);
   ctx.fill();
   return endAngle;
 }
@@ -219,7 +230,7 @@ function drawFullCircleBorders(ctx, element, inner) {
   }
 }
 
-function drawBorder(ctx, element, offset, spacing, endAngle) {
+function drawBorder(ctx, element, offset, spacing, endAngle, circular) {
   const {options} = element;
   const {borderWidth, borderJoinStyle} = options;
   const inner = options.borderAlign === 'inner';
@@ -244,7 +255,7 @@ function drawBorder(ctx, element, offset, spacing, endAngle) {
     clipArc(ctx, element, endAngle);
   }
 
-  pathArc(ctx, element, offset, spacing, endAngle);
+  pathArc(ctx, element, offset, spacing, endAngle, circular);
   ctx.stroke();
 }
 
@@ -323,6 +334,7 @@ export default class ArcElement extends Element {
     const {options, circumference} = this;
     const offset = (options.offset || 0) / 2;
     const spacing = (options.spacing || 0) / 2;
+    const circular = options.circular;
     this.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0;
     this.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0;
 
@@ -345,8 +357,8 @@ export default class ArcElement extends Element {
     ctx.fillStyle = options.backgroundColor;
     ctx.strokeStyle = options.borderColor;
 
-    const endAngle = drawArc(ctx, this, radiusOffset, spacing);
-    drawBorder(ctx, this, radiusOffset, spacing, endAngle);
+    const endAngle = drawArc(ctx, this, radiusOffset, spacing, circular);
+    drawBorder(ctx, this, radiusOffset, spacing, endAngle, circular);
 
     ctx.restore();
   }
@@ -366,6 +378,7 @@ ArcElement.defaults = {
   offset: 0,
   spacing: 0,
   angle: undefined,
+  circular: true,
 };
 
 /**
index e2ec0788b4b93fe2fcd550528d0d9f45219ea252..fdfddab9315230765f0ba107d85099805f9d9a0a 100644 (file)
@@ -218,4 +218,36 @@ describe('Arc element tests', function() {
 
     expect(ctx.getCalls().length).toBe(0);
   });
+
+  it('should draw when circular: false', function() {
+    var arc = new Chart.elements.ArcElement({
+      startAngle: 0,
+      endAngle: Math.PI * 2,
+      x: 2,
+      y: 2,
+      innerRadius: 0,
+      outerRadius: 2,
+      options: {
+        spacing: 0,
+        offset: 0,
+        scales: {
+          r: {
+            grid: {
+              circular: false,
+            },
+          },
+        },
+        elements: {
+          arc: {
+            circular: false
+          },
+        },
+      }
+    });
+
+    var ctx = window.createMockContext();
+    arc.draw(ctx);
+
+    expect(ctx.getCalls().length).toBeGreaterThan(0);
+  });
 });
index 75304c1f2e2b3de668e3f4c9d3f1be2104be5e16..4284e2d1019e7a69af76cad7e2e6b5a37bba5464 100644 (file)
@@ -1746,6 +1746,12 @@ export interface ArcOptions extends CommonElementOptions {
    * Arc offset (in pixels).
    */
   offset: number;
+
+  /**
+   * If false, Arc will be flat.
+   * @default true
+   */
+  circular: boolean;
 }
 
 export interface ArcHoverOptions extends CommonHoverOptions {