Skip to content

Commit

Permalink
fix(g-math): arc util should consider sweepFlag, close #222
Browse files Browse the repository at this point in the history
  • Loading branch information
dengfuping committed Oct 22, 2019
1 parent f342413 commit 011bd39
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 32 deletions.
26 changes: 17 additions & 9 deletions packages/g-math/src/arc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,21 @@ function yExtrema(rx, ry, xRotation) {
}

// 根据角度求 x 坐标
function xAt(cx, cy, rx, ry, xRotation, angle) {
return rx * Math.cos(xRotation) * Math.cos(angle) - ry * Math.sin(xRotation) * Math.sin(angle) + cx;
function xAt(cx, cy, rx, ry, xRotation, sweepFlag, angle) {
return (
cx +
(sweepFlag === 1 ? 1 : -1) *
(rx * Math.cos(xRotation) * Math.cos(angle) - ry * Math.sin(xRotation) * Math.sin(angle))
);
}

// 根据角度求 y 坐标
function yAt(cx, cy, rx, ry, xRotation, angle) {
return rx * Math.sin(xRotation) * Math.cos(angle) + ry * Math.cos(xRotation) * Math.sin(angle) + cy;
function yAt(cx, cy, rx, ry, xRotation, sweepFlag, angle) {
return (
cy +
(sweepFlag === 1 ? 1 : -1) *
(rx * Math.sin(xRotation) * Math.cos(angle) + ry * Math.cos(xRotation) * Math.sin(angle))
);
}

// 获取点在椭圆上的角度
Expand Down Expand Up @@ -85,7 +93,7 @@ export default {
}

for (let i = 0; i < xs.length; i++) {
const x = xAt(cx, cy, rx, ry, xRotation, xs[i]);
const x = xAt(cx, cy, rx, ry, xRotation, sweepFlag, xs[i]);
if (x < minX) {
minX = x;
}
Expand All @@ -112,7 +120,7 @@ export default {
}

for (let i = 0; i < ys.length; i++) {
const y = yAt(cx, cy, rx, ry, xRotation, ys[i]);
const y = yAt(cx, cy, rx, ry, xRotation, sweepFlag, ys[i]);
if (y < minY) {
minY = y;
}
Expand Down Expand Up @@ -178,11 +186,11 @@ export default {
const nearestPoint = this.nearestPoint(cx, cy, rx, ry, x0, y0);
return distance(nearestPoint.x, nearestPoint.y, x0, y0);
},
pointAt(cx, cy, rx, ry, xRotation, startAngle, endAngle, t) {
pointAt(cx, cy, rx, ry, xRotation, sweepFlag, startAngle, endAngle, t) {
const angle = (endAngle - startAngle) * t + startAngle;
return {
x: xAt(cx, cy, rx, ry, xRotation, angle),
y: yAt(cx, cy, rx, ry, xRotation, angle),
x: xAt(cx, cy, rx, ry, xRotation, sweepFlag, angle),
y: yAt(cx, cy, rx, ry, xRotation, sweepFlag, angle),
};
},
tangentAngle(cx, cy, rx, ry, xRotation, startAngle, endAngle, t) {
Expand Down
70 changes: 47 additions & 23 deletions packages/g-math/tests/unit/ellipse-arc-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,36 @@ function rotation(center, p, angle) {
const relativeX = p.x - center.x;
const relativeY = p.y - center.y;
return {
x: relativeX * Math.cos(angle) - relativeY * Math.sin(angle) + center.x,
y: relativeX * Math.sin(angle) + relativeY * Math.cos(angle) + center.y,
x: center.x + relativeX * Math.cos(angle) - relativeY * Math.sin(angle),
y: center.y + relativeX * Math.sin(angle) + relativeY * Math.cos(angle),
};
}

describe('ellipse arc test', () => {
it('point at, circle', () => {
const sweepFlag = 1;
// 1/2 圆弧,未旋转
expect(equalPoint(arc.pointAt(0, 0, 10, 10, 0, 0, Math.PI, 0), { x: 10, y: 0 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 10, 10, 0, 0, Math.PI, 1), { x: -10, y: 0 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 10, 10, 0, 0, Math.PI, 0.5), { x: 0, y: 10 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 10, 10, 0, sweepFlag, 0, Math.PI, 0), { x: 10, y: 0 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 10, 10, 0, sweepFlag, 0, Math.PI, 1), { x: -10, y: 0 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 10, 10, 0, sweepFlag, 0, Math.PI, 0.5), { x: 0, y: 10 })).eqls(true);

// 1/2 圆弧,旋转90度
expect(equalPoint(arc.pointAt(0, 0, 10, 10, Math.PI / 2, 0, Math.PI, 0), { x: 0, y: 10 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 10, 10, Math.PI / 2, 0, Math.PI, 1), { x: 0, y: -10 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 10, 10, Math.PI / 2, 0, Math.PI, 0.5), { x: -10, y: 0 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 10, 10, Math.PI / 2, sweepFlag, 0, Math.PI, 0), { x: 0, y: 10 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 10, 10, Math.PI / 2, sweepFlag, 0, Math.PI, 1), { x: 0, y: -10 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 10, 10, Math.PI / 2, sweepFlag, 0, Math.PI, 0.5), { x: -10, y: 0 })).eqls(true);
});

it('point at, ellipse', () => {
const sweepFlag = 1;
// 1/2 椭圆圆弧,未旋转
expect(equalPoint(arc.pointAt(0, 0, 20, 10, 0, 0, Math.PI, 0), { x: 20, y: 0 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 20, 10, 0, 0, Math.PI, 1), { x: -20, y: 0 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 20, 10, 0, 0, Math.PI, 0.5), { x: 0, y: 10 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 20, 10, 0, sweepFlag, 0, Math.PI, 0), { x: 20, y: 0 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 20, 10, 0, sweepFlag, 0, Math.PI, 1), { x: -20, y: 0 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 20, 10, 0, sweepFlag, 0, Math.PI, 0.5), { x: 0, y: 10 })).eqls(true);

// 1/2 椭圆圆弧,旋转90度
expect(equalPoint(arc.pointAt(0, 0, 20, 10, Math.PI / 2, 0, Math.PI, 0), { x: 0, y: 20 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 20, 10, Math.PI / 2, 0, Math.PI, 1), { x: 0, y: -20 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 20, 10, Math.PI / 2, 0, Math.PI, 0.5), { x: -10, y: 0 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 20, 10, Math.PI / 2, sweepFlag, 0, Math.PI, 0), { x: 0, y: 20 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 20, 10, Math.PI / 2, sweepFlag, 0, Math.PI, 1), { x: 0, y: -20 })).eqls(true);
expect(equalPoint(arc.pointAt(0, 0, 20, 10, Math.PI / 2, sweepFlag, 0, Math.PI, 0.5), { x: -10, y: 0 })).eqls(true);
});

it('box, circle', () => {
Expand All @@ -64,7 +66,7 @@ describe('ellipse arc test', () => {
width: 20,
height: 10,
});
// 1/2 圆弧,旋转 90
// 1/2 圆弧,顺时针旋转 90
expect(arc.box(0, 0, 10, 10, Math.PI / 2, 1, 0, Math.PI)).eqls({
x: -10,
y: -10,
Expand All @@ -73,7 +75,7 @@ describe('ellipse arc test', () => {
});

const box = arc.box(0, 0, 10, 10, Math.PI / 2, 1, 0, Math.PI / 2);
// 1 / 4 圆弧,旋转 90
// 1 / 4 圆弧,顺时针旋转 90
expect(
equalBox(box, {
x: -10,
Expand All @@ -84,7 +86,7 @@ describe('ellipse arc test', () => {
).eqls(true);
});
it('box circle 45', () => {
// 1 / 4 圆弧,旋转 45
// 1 / 4 圆弧,顺时针旋转 45
const xRotation = Math.PI / 4;
const box1 = arc.box(0, 0, 10, 10, xRotation, 1, 0, Math.PI / 2);
expect(
Expand All @@ -104,7 +106,7 @@ describe('ellipse arc test', () => {
width: 40,
height: 10,
});
// 1/2 圆弧,旋转 90
// 1/2 圆弧,顺时针旋转 90
expect(
equalBox(arc.box(0, 0, 20, 10, Math.PI / 2, 1, 0, Math.PI), {
x: -10,
Expand All @@ -116,16 +118,17 @@ describe('ellipse arc test', () => {
});

it('box, ellipse 45', () => {
// 1 / 4 圆弧,旋转 45
// 1 / 4 圆弧,顺时针旋转 45
const xRotation = Math.PI / 4;
const box = arc.box(100, 100, 20, 10, xRotation, 1, 0, Math.PI / 2);
const p1 = arc.pointAt(100, 100, 20, 10, 0, 0, Math.PI / 2, 0);
const p2 = arc.pointAt(100, 100, 20, 10, 0, 0, Math.PI / 2, 1);
const sweepFlag = 1;
const box = arc.box(100, 100, 20, 10, xRotation, sweepFlag, 0, Math.PI / 2);
const p1 = arc.pointAt(100, 100, 20, 10, 0, sweepFlag, 0, Math.PI / 2, 0);
const p2 = arc.pointAt(100, 100, 20, 10, 0, sweepFlag, 0, Math.PI / 2, 1);
const p11 = rotation({ x: 100, y: 100 }, p1, xRotation);
const p21 = rotation({ x: 100, y: 100 }, p2, xRotation);

ctx.beginPath();
ctx.ellipse(100, 100, 20, 10, xRotation, 0, Math.PI);
ctx.ellipse(100, 100, 20, 10, xRotation, 0, Math.PI, !!sweepFlag);
ctx.stroke();

ctx.beginPath();
Expand All @@ -135,6 +138,27 @@ describe('ellipse arc test', () => {
expect(box.width).eqls(p11.x - p21.x);
});

it('box, ellipse 45 when sweepFlag = 0', () => {
// 1 / 4 椭圆弧,旋转 45,逆时针方向绘制
const xRotation = Math.PI / 4;
const sweepFlag = 0;
const box = arc.box(100, 100, 20, 10, xRotation, sweepFlag, 0, Math.PI / 2);
const p1 = arc.pointAt(100, 100, 20, 10, 0, sweepFlag, 0, Math.PI / 2, 0);
const p2 = arc.pointAt(100, 100, 20, 10, 0, sweepFlag, 0, Math.PI / 2, 1);
const p11 = rotation({ x: 100, y: 100 }, p1, xRotation);
const p21 = rotation({ x: 100, y: 100 }, p2, xRotation);

ctx.beginPath();
ctx.ellipse(100, 100, 20, 10, xRotation, 0, Math.PI, !!sweepFlag);
ctx.stroke();

ctx.beginPath();
ctx.strokeRect(box.x, box.y, box.width, box.height);
expect(box.x).eqls(p11.x);
expect(box.y).eqls(p11.y);
expect(box.width).eqls(p21.x - p11.x);
});

it('tangent angle', () => {
expect(arc.tangentAngle(0, 0, 10, 10, 0, 0, Math.PI, 0)).eqls(Math.PI / 2);
expect(arc.tangentAngle(0, 0, 10, 10, 0, 0, Math.PI, 1)).eqls(Math.PI / 2 + Math.PI);
Expand Down

0 comments on commit 011bd39

Please sign in to comment.