Add ellipse changes no angle

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
pull/8539/merge^2
Mark Tolmacs
parent d9ea7190ec
commit 7b4e989d65
No known key found for this signature in database

@ -1,236 +1,5 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Test Transform > Test arrow bindings > should bind arrows to existing shapes when start / end provided with ids 1`] = `
{
"angle": 0,
"backgroundColor": "#d8f5a2",
"boundElements": [
{
"id": "id45",
"type": "arrow",
},
{
"id": "id46",
"type": "arrow",
},
],
"customData": undefined,
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 300,
"id": Any<String>,
"index": "a0",
"isDeleted": false,
"link": null,
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": null,
"seed": Any<Number>,
"strokeColor": "#66a80f",
"strokeStyle": "solid",
"strokeWidth": 2,
"type": "ellipse",
"updated": 1,
"version": 4,
"versionNonce": Any<Number>,
"width": 300,
"x": 630,
"y": 316,
}
`;
exports[`Test Transform > Test arrow bindings > should bind arrows to existing shapes when start / end provided with ids 2`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": [
{
"id": "id46",
"type": "arrow",
},
],
"customData": undefined,
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 100,
"id": Any<String>,
"index": "a1",
"isDeleted": false,
"link": null,
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": null,
"seed": Any<Number>,
"strokeColor": "#9c36b5",
"strokeStyle": "solid",
"strokeWidth": 2,
"type": "diamond",
"updated": 1,
"version": 3,
"versionNonce": Any<Number>,
"width": 140,
"x": 96,
"y": 400,
}
`;
exports[`Test Transform > Test arrow bindings > should bind arrows to existing shapes when start / end provided with ids 3`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"customData": undefined,
"elbowed": false,
"endArrowhead": "arrow",
"endBinding": {
"elementId": "ellipse-1",
"fixedPoint": null,
"focus": -0.008153707962747813,
"gap": 1,
},
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 35,
"id": Any<String>,
"index": "a2",
"isDeleted": false,
"lastCommittedPoint": null,
"link": null,
"locked": false,
"opacity": 100,
"points": [
[
0.5,
0.5,
],
[
394.5,
34.5,
],
],
"roughness": 1,
"roundness": null,
"seed": Any<Number>,
"startArrowhead": null,
"startBinding": {
"elementId": "id47",
"fixedPoint": null,
"focus": -0.08139534883720931,
"gap": 1,
},
"strokeColor": "#1864ab",
"strokeStyle": "solid",
"strokeWidth": 2,
"type": "arrow",
"updated": 1,
"version": 4,
"versionNonce": Any<Number>,
"width": 395,
"x": 247,
"y": 420,
}
`;
exports[`Test Transform > Test arrow bindings > should bind arrows to existing shapes when start / end provided with ids 4`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"customData": undefined,
"elbowed": false,
"endArrowhead": "arrow",
"endBinding": {
"elementId": "ellipse-1",
"fixedPoint": null,
"focus": 0.10666666666666667,
"gap": 3.834326468444573,
},
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 0,
"id": Any<String>,
"index": "a3",
"isDeleted": false,
"lastCommittedPoint": null,
"link": null,
"locked": false,
"opacity": 100,
"points": [
[
0.5,
0,
],
[
399.5,
0,
],
],
"roughness": 1,
"roundness": null,
"seed": Any<Number>,
"startArrowhead": null,
"startBinding": {
"elementId": "diamond-1",
"fixedPoint": null,
"focus": 0,
"gap": 1,
},
"strokeColor": "#e67700",
"strokeStyle": "solid",
"strokeWidth": 2,
"type": "arrow",
"updated": 1,
"version": 4,
"versionNonce": Any<Number>,
"width": 400,
"x": 227,
"y": 450,
}
`;
exports[`Test Transform > Test arrow bindings > should bind arrows to existing shapes when start / end provided with ids 5`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": [
{
"id": "id45",
"type": "arrow",
},
],
"customData": undefined,
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 300,
"id": Any<String>,
"index": "a4",
"isDeleted": false,
"link": null,
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": null,
"seed": Any<Number>,
"strokeColor": "#1e1e1e",
"strokeStyle": "solid",
"strokeWidth": 2,
"type": "rectangle",
"updated": 1,
"version": 3,
"versionNonce": Any<Number>,
"width": 300,
"x": -53,
"y": 270,
}
`;
exports[`Test Transform > Test arrow bindings > should bind arrows to existing text elements when start / end provided with ids 1`] = ` exports[`Test Transform > Test arrow bindings > should bind arrows to existing text elements when start / end provided with ids 1`] = `
{ {
"angle": 0, "angle": 0,
@ -1551,7 +1320,7 @@ exports[`Test Transform > should transform the elements correctly when linear el
"elementId": "B", "elementId": "B",
"fixedPoint": null, "fixedPoint": null,
"focus": 0, "focus": 0,
"gap": 1, "gap": 77.017,
}, },
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,

@ -705,11 +705,7 @@ export const getHeadingForElbowArrowSnap = (
return otherPointHeading; return otherPointHeading;
} }
const distance = getDistanceForBinding( const distance = getDistanceForBinding(origPoint, bindableElement);
origPoint,
bindableElement,
elementsMap,
);
if (!distance) { if (!distance) {
return vectorToHeading( return vectorToHeading(
@ -731,7 +727,6 @@ export const getHeadingForElbowArrowSnap = (
const getDistanceForBinding = ( const getDistanceForBinding = (
point: Readonly<GlobalPoint>, point: Readonly<GlobalPoint>,
bindableElement: ExcalidrawBindableElement, bindableElement: ExcalidrawBindableElement,
elementsMap: ElementsMap,
) => { ) => {
const distance = distanceToBindableElement(bindableElement, point); const distance = distanceToBindableElement(bindableElement, point);
const bindDistance = maxBindingGap( const bindDistance = maxBindingGap(

@ -123,8 +123,8 @@ const roundedCutoffSegment = (
const t = (4 * r) / Math.sqrt(2); const t = (4 * r) / Math.sqrt(2);
return segment( return segment(
ellipseSegmentInterceptPoints(ellipse(s[0], radians(0), t, t), s)[0], ellipseSegmentInterceptPoints(ellipse(s[0], t, t), s)[0],
ellipseSegmentInterceptPoints(ellipse(s[1], radians(0), t, t), s)[0], ellipseSegmentInterceptPoints(ellipse(s[1], t, t), s)[0],
); );
}; };
@ -198,13 +198,12 @@ export const distanceToEllipseElement = (
element: ExcalidrawEllipseElement, element: ExcalidrawEllipseElement,
p: GlobalPoint, p: GlobalPoint,
): number => { ): number => {
const center = point(
element.x + element.width / 2,
element.y + element.height / 2,
);
return ellipseDistanceFromPoint( return ellipseDistanceFromPoint(
p, pointRotateRads(p, center, radians(-element.angle)),
ellipse( ellipse(center, element.width / 2, element.height / 2),
point(element.x + element.width / 2, element.y + element.height / 2),
element.angle,
element.width / 2,
element.height / 2,
),
); );
}; };

@ -17,7 +17,6 @@ import { aabbForElement, pointInsideBounds } from "../shapes";
import { isAnyTrue, toBrandedType } from "../utils"; import { isAnyTrue, toBrandedType } from "../utils";
import { import {
bindPointToSnapToElementOutline, bindPointToSnapToElementOutline,
distanceToBindableElement,
avoidRectangularCorner, avoidRectangularCorner,
getHoveredElementForBinding, getHoveredElementForBinding,
FIXED_BINDING_DISTANCE, FIXED_BINDING_DISTANCE,
@ -26,6 +25,7 @@ import {
snapToMid, snapToMid,
} from "./binding"; } from "./binding";
import type { Bounds } from "./bounds"; import type { Bounds } from "./bounds";
import { distanceToBindableElement } from "./distance";
import type { Heading } from "./heading"; import type { Heading } from "./heading";
import { import {
compareHeading, compareHeading,
@ -1023,7 +1023,7 @@ const getGlobalPoint = (
// NOTE: Resize scales the binding position point too, so we need to update it // NOTE: Resize scales the binding position point too, so we need to update it
return Math.abs( return Math.abs(
distanceToBindableElement(boundElement, fixedGlobalPoint, elementsMap) - distanceToBindableElement(boundElement, fixedGlobalPoint) -
FIXED_BINDING_DISTANCE, FIXED_BINDING_DISTANCE,
) > 0.01 ) > 0.01
? getSnapPoint(initialPoint, otherPoint, boundElement, elementsMap) ? getSnapPoint(initialPoint, otherPoint, boundElement, elementsMap)
@ -1060,9 +1060,12 @@ const getBindPointHeading = (
hoveredElement && hoveredElement &&
aabbForElement( aabbForElement(
hoveredElement, hoveredElement,
Array(4).fill( Array(4).fill(distanceToBindableElement(hoveredElement, p)) as [
distanceToBindableElement(hoveredElement, p, elementsMap), number,
) as [number, number, number, number], number,
number,
number,
],
), ),
elementsMap, elementsMap,
origPoint, origPoint,

@ -817,8 +817,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
"strokeWidth": 2, "strokeWidth": 2,
"type": "arrow", "type": "arrow",
"updated": 1, "updated": 1,
"version": 28, "version": 30,
"width": 50, "width": 0,
"x": 200, "x": 200,
"y": 0, "y": 0,
} }
@ -852,7 +852,7 @@ History {
0, 0,
], ],
[ [
50, 0,
0, 0,
], ],
], ],
@ -938,7 +938,7 @@ History {
0, 0,
], ],
[ [
50, 0,
0, 0,
], ],
], ],

@ -225,7 +225,7 @@ exports[`move element > rectangles with binding arrow 7`] = `
"elementId": "id0", "elementId": "id0",
"fixedPoint": null, "fixedPoint": null,
"focus": "-0.60000", "focus": "-0.60000",
"gap": 10, "gap": 9,
}, },
"strokeColor": "#1e1e1e", "strokeColor": "#1e1e1e",
"strokeStyle": "solid", "strokeStyle": "solid",

@ -1,5 +1,5 @@
import { radians } from "./angle"; import { radians } from "./angle";
import { arc, arcIncludesPoint, arcSegmentInterceptPoint } from "./arc"; import { arc, arcIncludesPoint, arcSegmentInterceptPoints } from "./arc";
import { point } from "./point"; import { point } from "./point";
import { segment } from "./segment"; import { segment } from "./segment";
@ -33,7 +33,7 @@ describe("point on arc", () => {
describe("intersection", () => { describe("intersection", () => {
it("should report correct interception point", () => { it("should report correct interception point", () => {
expect( expect(
arcSegmentInterceptPoint( arcSegmentInterceptPoints(
arc(point(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)), arc(point(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)),
segment(point(2, 1), point(0, 0)), segment(point(2, 1), point(0, 0)),
), ),
@ -42,7 +42,7 @@ describe("intersection", () => {
it("should report both interception points when present", () => { it("should report both interception points when present", () => {
expect( expect(
arcSegmentInterceptPoint( arcSegmentInterceptPoints(
arc(point(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)), arc(point(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)),
segment(point(0.9, -2), point(0.9, 2)), segment(point(0.9, -2), point(0.9, 2)),
), ),

@ -1,8 +1,10 @@
import { invariant } from "../excalidraw/utils"; import { cartesian2Polar, normalizeRadians, radians } from "./angle";
import { cartesian2Polar, radians } from "./angle"; import {
import { ellipse, ellipseSegmentInterceptPoints } from "./ellipse"; ellipse,
ellipseDistanceFromPoint,
ellipseSegmentInterceptPoints,
} from "./ellipse";
import { point, pointDistance } from "./point"; import { point, pointDistance } from "./point";
import { segment } from "./segment";
import type { GenericPoint, Segment, Radians, Arc } from "./types"; import type { GenericPoint, Segment, Radians, Arc } from "./types";
import { PRECISION } from "./utils"; import { PRECISION } from "./utils";
@ -53,26 +55,44 @@ export function arcDistanceFromPoint<Point extends GenericPoint>(
a: Arc<Point>, a: Arc<Point>,
p: Point, p: Point,
) { ) {
const intersectPoint = arcSegmentInterceptPoint(a, segment(p, a.center)); const theta = normalizeRadians(
radians(Math.atan2(p[0] - a.center[0], p[1] - a.center[1])),
invariant(
intersectPoint.length !== 1,
"Arc distance intersector cannot have multiple intersections",
); );
return pointDistance(intersectPoint[0], p); if (a.startAngle <= theta && a.endAngle >= theta) {
return ellipseDistanceFromPoint(
p,
ellipse(a.center, 2 * a.radius, 2 * a.radius),
);
}
return Math.min(
pointDistance(
p,
point(
a.center[0] + a.radius + Math.cos(a.startAngle),
a.center[1] + a.radius + Math.sin(a.startAngle),
),
),
pointDistance(
p,
point(
a.center[0] + a.radius + Math.cos(a.endAngle),
a.center[1] + a.radius + Math.sin(a.endAngle),
),
),
);
} }
/** /**
* Returns the intersection point(s) of a line segment represented by a start * Returns the intersection point(s) of a line segment represented by a start
* point and end point and a symmetric arc. * point and end point and a symmetric arc.
*/ */
export function arcSegmentInterceptPoint<Point extends GenericPoint>( export function arcSegmentInterceptPoints<Point extends GenericPoint>(
a: Readonly<Arc<Point>>, a: Readonly<Arc<Point>>,
s: Readonly<Segment<Point>>, s: Readonly<Segment<Point>>,
): Point[] { ): Point[] {
return ellipseSegmentInterceptPoints( return ellipseSegmentInterceptPoints(
ellipse(a.center, radians(0), a.radius, a.radius), ellipse(a.center, a.radius, a.radius),
s, s,
).filter((candidate) => { ).filter((candidate) => {
const [candidateRadius, candidateAngle] = cartesian2Polar( const [candidateRadius, candidateAngle] = cartesian2Polar(

@ -1,4 +1,3 @@
import { radians } from "./angle";
import { import {
ellipse, ellipse,
ellipseSegmentInterceptPoints, ellipseSegmentInterceptPoints,
@ -10,29 +9,29 @@ import { segment } from "./segment";
import type { Ellipse, GlobalPoint } from "./types"; import type { Ellipse, GlobalPoint } from "./types";
describe("point and ellipse", () => { describe("point and ellipse", () => {
const target: Ellipse<GlobalPoint> = ellipse(point(0, 0), radians(0), 2, 1);
it("point on ellipse", () => { it("point on ellipse", () => {
[point(0, 1), point(0, -1), point(2, 0), point(-2, 0)].forEach((p) => { const target: Ellipse<GlobalPoint> = ellipse(point(1, 2), 2, 1);
[point(1, 3), point(1, 1), point(3, 2), point(-1, 2)].forEach((p) => {
expect(ellipseTouchesPoint(p, target)).toBe(true); expect(ellipseTouchesPoint(p, target)).toBe(true);
}); });
expect(ellipseTouchesPoint(point(-1.4, 0.7), target, 0.1)).toBe(true); expect(ellipseTouchesPoint(point(-0.4, 2.7), target, 0.1)).toBe(true);
expect(ellipseTouchesPoint(point(-1.4, 0.71), target, 0.01)).toBe(true); expect(ellipseTouchesPoint(point(-0.4, 2.71), target, 0.01)).toBe(true);
expect(ellipseTouchesPoint(point(1.4, 0.7), target, 0.1)).toBe(true); expect(ellipseTouchesPoint(point(2.4, 2.7), target, 0.1)).toBe(true);
expect(ellipseTouchesPoint(point(1.4, 0.71), target, 0.01)).toBe(true); expect(ellipseTouchesPoint(point(2.4, 2.71), target, 0.01)).toBe(true);
expect(ellipseTouchesPoint(point(1, -0.86), target, 0.1)).toBe(true); expect(ellipseTouchesPoint(point(2, 1.14), target, 0.1)).toBe(true);
expect(ellipseTouchesPoint(point(1, -0.86), target, 0.01)).toBe(true); expect(ellipseTouchesPoint(point(2, 1.14), target, 0.01)).toBe(true);
expect(ellipseTouchesPoint(point(-1, -0.86), target, 0.1)).toBe(true); expect(ellipseTouchesPoint(point(0, 1.14), target, 0.1)).toBe(true);
expect(ellipseTouchesPoint(point(-1, -0.86), target, 0.01)).toBe(true); expect(ellipseTouchesPoint(point(0, 1.14), target, 0.01)).toBe(true);
expect(ellipseTouchesPoint(point(-1, 0.8), target)).toBe(false); expect(ellipseTouchesPoint(point(0, 2.8), target)).toBe(false);
expect(ellipseTouchesPoint(point(1, -0.8), target)).toBe(false); expect(ellipseTouchesPoint(point(2, 1.2), target)).toBe(false);
}); });
it("point in ellipse", () => { it("point in ellipse", () => {
const target: Ellipse<GlobalPoint> = ellipse(point(0, 0), 2, 1);
[point(0, 1), point(0, -1), point(2, 0), point(-2, 0)].forEach((p) => { [point(0, 1), point(0, -1), point(2, 0), point(-2, 0)].forEach((p) => {
expect(ellipseIncludesPoint(p, target)).toBe(true); expect(ellipseIncludesPoint(p, target)).toBe(true);
}); });
@ -50,7 +49,7 @@ describe("point and ellipse", () => {
describe("line and ellipse", () => { describe("line and ellipse", () => {
it("detects outside segment", () => { it("detects outside segment", () => {
const e = ellipse(point(0, 0), radians(0), 2, 2); const e = ellipse(point(0, 0), 2, 2);
expect( expect(
ellipseSegmentInterceptPoints( ellipseSegmentInterceptPoints(

@ -1,12 +1,5 @@
import { radians } from "./angle"; import { point, pointDistance, pointFromVector } from "./point";
import { line } from "./line"; import type { Ellipse, GenericPoint, Segment } from "./types";
import {
point,
pointDistance,
pointFromVector,
pointRotateRads,
} from "./point";
import type { Ellipse, GenericPoint, Segment, Radians } from "./types";
import { PRECISION } from "./utils"; import { PRECISION } from "./utils";
import { import {
vector, vector,
@ -27,13 +20,11 @@ import {
*/ */
export function ellipse<Point extends GenericPoint>( export function ellipse<Point extends GenericPoint>(
center: Point, center: Point,
angle: Radians,
halfWidth: number, halfWidth: number,
halfHeight: number, halfHeight: number,
): Ellipse<Point> { ): Ellipse<Point> {
return { return {
center, center,
angle,
halfWidth, halfWidth,
halfHeight, halfHeight,
} as Ellipse<Point>; } as Ellipse<Point>;
@ -50,22 +41,11 @@ export const ellipseIncludesPoint = <Point extends GenericPoint>(
p: Point, p: Point,
ellipse: Ellipse<Point>, ellipse: Ellipse<Point>,
) => { ) => {
const { center, angle, halfWidth, halfHeight } = ellipse; const { center, halfWidth, halfHeight } = ellipse;
const translatedPoint = vectorAdd( const normalizedX = (p[0] - center[0]) / halfWidth;
vectorFromPoint(p), const normalizedY = (p[1] - center[1]) / halfHeight;
vectorScale(vectorFromPoint(center), -1),
);
const [rotatedPointX, rotatedPointY] = pointRotateRads(
pointFromVector(translatedPoint),
point(0, 0),
radians(-angle),
);
return ( return normalizedX * normalizedX + normalizedY * normalizedY <= 1;
(rotatedPointX / halfWidth) * (rotatedPointX / halfWidth) +
(rotatedPointY / halfHeight) * (rotatedPointY / halfHeight) <=
1
);
}; };
/** /**
@ -97,21 +77,16 @@ export const ellipseDistanceFromPoint = <Point extends GenericPoint>(
p: Point, p: Point,
ellipse: Ellipse<Point>, ellipse: Ellipse<Point>,
): number => { ): number => {
const { angle, halfWidth, halfHeight, center } = ellipse; const { halfWidth, halfHeight, center } = ellipse;
const a = halfWidth; const a = halfWidth;
const b = halfHeight; const b = halfHeight;
const translatedPoint = vectorAdd( const translatedPoint = vectorAdd(
vectorFromPoint(p), vectorFromPoint(p),
vectorScale(vectorFromPoint(center), -1), vectorScale(vectorFromPoint(center), -1),
); );
const [rotatedPointX, rotatedPointY] = pointRotateRads(
pointFromVector(translatedPoint),
point(0, 0),
radians(-angle),
);
const px = Math.abs(rotatedPointX); const px = Math.abs(translatedPoint[0]);
const py = Math.abs(rotatedPointY); const py = Math.abs(translatedPoint[1]);
let tx = 0.707; let tx = 0.707;
let ty = 0.707; let ty = 0.707;
@ -140,11 +115,11 @@ export const ellipseDistanceFromPoint = <Point extends GenericPoint>(
} }
const [minX, minY] = [ const [minX, minY] = [
a * tx * Math.sign(rotatedPointX), a * tx * Math.sign(translatedPoint[0]),
b * ty * Math.sign(rotatedPointY), b * ty * Math.sign(translatedPoint[1]),
]; ];
return pointDistance(point(rotatedPointX, rotatedPointY), point(minX, minY)); return pointDistance(pointFromVector(translatedPoint), point(minX, minY));
}; };
/** /**
@ -153,19 +128,13 @@ export const ellipseDistanceFromPoint = <Point extends GenericPoint>(
*/ */
export function ellipseSegmentInterceptPoints<Point extends GenericPoint>( export function ellipseSegmentInterceptPoints<Point extends GenericPoint>(
e: Readonly<Ellipse<Point>>, e: Readonly<Ellipse<Point>>,
l: Readonly<Segment<Point>>, s: Readonly<Segment<Point>>,
): Point[] { ): Point[] {
const rx = e.halfWidth; const rx = e.halfWidth;
const ry = e.halfHeight; const ry = e.halfHeight;
const nonRotatedLine = line(
pointRotateRads(l[0], e.center, radians(-e.angle)), const dir = vectorFromPoint(s[1], s[0]);
pointRotateRads(l[1], e.center, radians(-e.angle)), const diff = vector(s[0][0] - e.center[0], s[0][1] - e.center[1]);
);
const dir = vectorFromPoint(nonRotatedLine[1], nonRotatedLine[0]);
const diff = vector(
nonRotatedLine[0][0] - e.center[0],
nonRotatedLine[0][1] - e.center[1],
);
const mDir = vector(dir[0] / (rx * rx), dir[1] / (ry * ry)); const mDir = vector(dir[0] / (rx * rx), dir[1] / (ry * ry));
const mDiff = vector(diff[0] / (rx * rx), diff[1] / (ry * ry)); const mDiff = vector(diff[0] / (rx * rx), diff[1] / (ry * ry));
@ -183,10 +152,8 @@ export function ellipseSegmentInterceptPoints<Point extends GenericPoint>(
if (0 <= t_a && t_a <= 1) { if (0 <= t_a && t_a <= 1) {
intersections.push( intersections.push(
point( point(
nonRotatedLine[0][0] + s[0][0] + (s[1][0] - s[0][0]) * t_a,
(nonRotatedLine[1][0] - nonRotatedLine[0][0]) * t_a, s[0][1] + (s[1][1] - s[0][1]) * t_a,
nonRotatedLine[0][1] +
(nonRotatedLine[1][1] - nonRotatedLine[0][1]) * t_a,
), ),
); );
} }
@ -194,10 +161,8 @@ export function ellipseSegmentInterceptPoints<Point extends GenericPoint>(
if (0 <= t_b && t_b <= 1) { if (0 <= t_b && t_b <= 1) {
intersections.push( intersections.push(
point( point(
nonRotatedLine[0][0] + s[0][0] + (s[1][0] - s[0][0]) * t_b,
(nonRotatedLine[1][0] - nonRotatedLine[0][0]) * t_b, s[0][1] + (s[1][1] - s[0][1]) * t_b,
nonRotatedLine[0][1] +
(nonRotatedLine[1][1] - nonRotatedLine[0][1]) * t_b,
), ),
); );
} }
@ -206,16 +171,12 @@ export function ellipseSegmentInterceptPoints<Point extends GenericPoint>(
if (0 <= t && t <= 1) { if (0 <= t && t <= 1) {
intersections.push( intersections.push(
point( point(
nonRotatedLine[0][0] + s[0][0] + (s[1][0] - s[0][0]) * t,
(nonRotatedLine[1][0] - nonRotatedLine[0][0]) * t, s[0][1] + (s[1][1] - s[0][1]) * t,
nonRotatedLine[0][1] +
(nonRotatedLine[1][1] - nonRotatedLine[0][1]) * t,
), ),
); );
} }
} }
return intersections.map((point) => return intersections;
pointRotateRads(point, e.center, e.angle),
);
} }

@ -137,7 +137,6 @@ export type Extent = {
// in replace of semi major and semi minor axes // in replace of semi major and semi minor axes
export type Ellipse<Point extends GenericPoint> = { export type Ellipse<Point extends GenericPoint> = {
center: Point; center: Point;
angle: Radians;
halfWidth: number; halfWidth: number;
halfHeight: number; halfHeight: number;
} & { } & {

@ -175,16 +175,11 @@ export const getSelectionBoxShape = <Point extends GlobalPoint | LocalPoint>(
export const getEllipseShape = <Point extends GlobalPoint | LocalPoint>( export const getEllipseShape = <Point extends GlobalPoint | LocalPoint>(
element: ExcalidrawEllipseElement, element: ExcalidrawEllipseElement,
): GeometricShape<Point> => { ): GeometricShape<Point> => {
const { width, height, angle, x, y } = element; const { width, height, x, y } = element;
return { return {
type: "ellipse", type: "ellipse",
data: ellipse( data: ellipse(point(x + width / 2, y + height / 2), width / 2, height / 2),
point(x + width / 2, y + height / 2),
angle,
width / 2,
height / 2,
),
}; };
}; };

Loading…
Cancel
Save