Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Circle): Add counterclockwise parameter to Circle class #9670

Merged
merged 1 commit into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [next]

- feat(Circle): Add counterclockwise parameter to Circle class [#9670](https://github.com/fabricjs/fabric.js/pull/9670)

## [6.0.0-beta19]

- feat(LayoutManager): Expose objects registration [#9661](https://github.com/fabricjs/fabric.js/pull/9661)
Expand Down
39 changes: 24 additions & 15 deletions src/shapes/Circle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,26 @@ interface UniqueCircleProps {
radius: number;

/**
* degrees of start of the circle.
* probably will change to degrees in next major version
* @type Number 0 - 359
* Angle for the start of the circle, in degrees.
* @type TDegree 0 - 359
* @default 0
*/
startAngle: number;

/**
* End angle of the circle
* probably will change to degrees in next major version
* @type Number 1 - 360
* Angle for the end of the circle, in degrees
* @type TDegree 1 - 360
* @default 360
*/
endAngle: number;

/**
* Orientation for the direction of the circle.
* Setting to true will switch the arc of the circle to traverse from startAngle to endAngle in a counter-clockwise direction.
* Note: this will only change how the circle is drawn, and does not affect rotational transformation.
* @default false
*/
lbordowitz marked this conversation as resolved.
Show resolved Hide resolved
counterClockwise: boolean;
}

export interface SerializedCircleProps
Expand All @@ -41,12 +47,18 @@ export interface SerializedCircleProps

export interface CircleProps extends FabricObjectProps, UniqueCircleProps {}

const CIRCLE_PROPS = ['radius', 'startAngle', 'endAngle'] as const;
const CIRCLE_PROPS = [
'radius',
'startAngle',
'endAngle',
'counterClockwise',
] as const;

export const circleDefaultValues: UniqueCircleProps = {
radius: 0,
startAngle: 0,
endAngle: 360,
counterClockwise: false,
};

export class Circle<
Expand All @@ -60,6 +72,7 @@ export class Circle<
declare radius: number;
declare startAngle: number;
declare endAngle: number;
declare counterClockwise: boolean;

static type = 'Circle';

Expand Down Expand Up @@ -101,7 +114,7 @@ export class Circle<
this.radius,
degreesToRadians(this.startAngle),
degreesToRadians(this.endAngle),
false
this.counterClockwise
);
this._renderPaintInOrder(ctx);
}
Expand Down Expand Up @@ -169,14 +182,10 @@ export class Circle<
startY = sin(start) * radius,
endX = cos(end) * radius,
endY = sin(end) * radius,
largeFlag = angle > 180 ? '1' : '0';
largeFlag = angle > 180 ? 1 : 0,
sweepFlag = this.counterClockwise ? 0 : 1;
return [
`<path d="M ${startX} ${startY}`,
` A ${radius} ${radius}`,
' 0 ',
`${largeFlag} 1`,
` ${endX} ${endY}`,
'" ',
`<path d="M ${startX} ${startY} A ${radius} ${radius} 0 ${largeFlag} ${sweepFlag} ${endX} ${endY}" `,
'COMMON_PARTS',
' />\n',
];
Expand Down
11 changes: 10 additions & 1 deletion test/unit/circle.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
radius: 0,
startAngle: 0,
endAngle: 360,
counterClockwise: false,
skewX: 0,
skewY: 0,
strokeUniform: false
Expand Down Expand Up @@ -159,13 +160,21 @@
});

QUnit.test('toSVG with half circle', function(assert) {
var circle = new fabric.Circle({ width: 100, height: 100, radius: 10, endAngle: fabric.util.radiansToDegrees(Math.PI) });
var circle = new fabric.Circle({ width: 100, height: 100, radius: 10, endAngle: 180 });
var svg = circle.toSVG();
var svgClipPath = circle.toClipPathSVG();
assert.equal(svg, '<g transform=\"matrix(1 0 0 1 10.5 10.5)\" >\n<path d=\"M 10 0 A 10 10 0 0 1 -10 0\" style=\"stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;\" />\n</g>\n');
assert.equal(svgClipPath, '\t<path d=\"M 10 0 A 10 10 0 0 1 -10 0\" transform=\"matrix(1 0 0 1 10.5 10.5)\" />\n', 'half circle as clipPath');
});

QUnit.test('toSVG with counterclockwise half circle', function (assert) {
var circle = new fabric.Circle({ width: 100, height: 100, radius: 10, endAngle: 180, counterClockwise: true });
var svg = circle.toSVG();
var svgClipPath = circle.toClipPathSVG();
assert.equal(svg, '<g transform=\"matrix(1 0 0 1 10.5 10.5)\" >\n<path d=\"M 10 0 A 10 10 0 0 0 -10 0\" style=\"stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;\" />\n</g>\n');
assert.equal(svgClipPath, '\t<path d=\"M 10 0 A 10 10 0 0 0 -10 0\" transform=\"matrix(1 0 0 1 10.5 10.5)\" />\n', 'half circle as clipPath');
});

QUnit.test('fromElement', function(assert) {
var done = assert.async();
assert.ok(typeof fabric.Circle.fromElement === 'function');
Expand Down
Loading