Eliipse and rounded rectangle with one edge case
Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
parent
946d88be36
commit
b97aee6092
|
@ -63,7 +63,11 @@ import {
|
||||||
rotatePoint,
|
rotatePoint,
|
||||||
vectorToHeading,
|
vectorToHeading,
|
||||||
} from "../math";
|
} from "../math";
|
||||||
import { debugDrawPoint } from "../visualdebug";
|
import {
|
||||||
|
debugDrawBounds,
|
||||||
|
debugDrawPoint,
|
||||||
|
debugDrawSegments,
|
||||||
|
} from "../visualdebug";
|
||||||
import {
|
import {
|
||||||
interceptPointsOfLineAndEllipse,
|
interceptPointsOfLineAndEllipse,
|
||||||
interceptPointsOfSegmentAndRoundedRectangle,
|
interceptPointsOfSegmentAndRoundedRectangle,
|
||||||
|
@ -743,36 +747,35 @@ export const bindPointToSnapToElementOutline = (
|
||||||
bindableElement,
|
bindableElement,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
);
|
);
|
||||||
|
//debugDrawBounds(aabb);
|
||||||
debugDrawPoint(point, "green");
|
//debugDrawPoint(point, "green");
|
||||||
if (heading) {
|
if (heading) {
|
||||||
const headingIsVertical =
|
const headingIsVertical =
|
||||||
compareHeading(heading, HEADING_UP) ||
|
compareHeading(heading, HEADING_UP) ||
|
||||||
compareHeading(heading, HEADING_DOWN);
|
compareHeading(heading, HEADING_DOWN);
|
||||||
const intersections = _intersectElementWithLine(
|
const intersections = _intersectElementWithLine(
|
||||||
bindableElement,
|
bindableElement,
|
||||||
rotatePoint(
|
headingIsVertical
|
||||||
headingIsVertical
|
? [point[0], point[1] - 2 * bindableElement.height]
|
||||||
? [point[0], aabb[1] - bindableElement.height]
|
: [point[0] - 2 * bindableElement.width, point[1]],
|
||||||
: [aabb[0] - bindableElement.width, point[1]],
|
headingIsVertical
|
||||||
center,
|
? [point[0], point[1] + 2 * bindableElement.height]
|
||||||
-bindableElement.angle,
|
: [point[0] + 2 * bindableElement.width, point[1]],
|
||||||
),
|
|
||||||
rotatePoint(
|
|
||||||
headingIsVertical
|
|
||||||
? [point[0], aabb[3] + bindableElement.height]
|
|
||||||
: [aabb[2] + bindableElement.width, point[1]],
|
|
||||||
center,
|
|
||||||
-bindableElement.angle,
|
|
||||||
),
|
|
||||||
FIXED_BINDING_DISTANCE,
|
FIXED_BINDING_DISTANCE,
|
||||||
);
|
);
|
||||||
intersections.sort(
|
intersections.sort(
|
||||||
(a, b) => distanceSq2d(a, point) - distanceSq2d(b, point),
|
(a, b) => distanceSq2d(a, point) - distanceSq2d(b, point),
|
||||||
);
|
);
|
||||||
|
debugDrawSegments([
|
||||||
|
headingIsVertical
|
||||||
|
? [point[0], point[1] - 2 * bindableElement.height]
|
||||||
|
: [point[0] - 2 * bindableElement.width, point[1]],
|
||||||
|
headingIsVertical
|
||||||
|
? [point[0], point[1] + 2 * bindableElement.height]
|
||||||
|
: [point[0] + 2 * bindableElement.width, point[1]],
|
||||||
|
]);
|
||||||
if (intersections.length > 0) {
|
if (intersections.length > 0) {
|
||||||
debugDrawPoint(intersections[0], "red");
|
intersections.forEach((point) => debugDrawPoint(point, "red"));
|
||||||
return intersections[0];
|
return intersections[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -825,10 +828,13 @@ export const _intersectElementWithLine = (
|
||||||
const halfHeight = element.height / 2;
|
const halfHeight = element.height / 2;
|
||||||
return interceptPointsOfLineAndEllipse(
|
return interceptPointsOfLineAndEllipse(
|
||||||
{
|
{
|
||||||
center: [element.x + element.width, element.y + element.height],
|
center: [
|
||||||
|
element.x + element.width / 2,
|
||||||
|
element.y + element.height / 2,
|
||||||
|
],
|
||||||
angle: element.angle,
|
angle: element.angle,
|
||||||
halfWidth,
|
halfWidth: halfWidth + gap,
|
||||||
halfHeight,
|
halfHeight: halfHeight + gap,
|
||||||
},
|
},
|
||||||
[a, b] as LineSegment,
|
[a, b] as LineSegment,
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,6 +12,10 @@ import {
|
||||||
scaleVector,
|
scaleVector,
|
||||||
subtractVectors,
|
subtractVectors,
|
||||||
} from "../../excalidraw/math";
|
} from "../../excalidraw/math";
|
||||||
|
import {
|
||||||
|
debugDrawPoint,
|
||||||
|
debugDrawSegments,
|
||||||
|
} from "../../excalidraw/visualdebug";
|
||||||
import type {
|
import type {
|
||||||
Point,
|
Point,
|
||||||
Line,
|
Line,
|
||||||
|
@ -984,40 +988,6 @@ export const pointInEllipse = (point: Point, ellipse: Ellipse) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the intersection point(s) of a line segment represented by a start
|
|
||||||
* point and end point and a symmetric arc.
|
|
||||||
*/
|
|
||||||
export const interceptOfSymmetricArcAndSegment = (
|
|
||||||
arc: CircularArc,
|
|
||||||
line: Readonly<LineSegment>,
|
|
||||||
): Point[] => {
|
|
||||||
const radius = distancePoints(arc.from, arc.center);
|
|
||||||
const [, fromAngle] = carthesian2Polar(arc.from, arc.center);
|
|
||||||
const [, toAngle] = carthesian2Polar(arc.to, arc.center);
|
|
||||||
|
|
||||||
return interceptPointsOfLineAndEllipse(
|
|
||||||
{
|
|
||||||
center: arc.center,
|
|
||||||
angle: 0,
|
|
||||||
halfHeight: radius,
|
|
||||||
halfWidth: radius,
|
|
||||||
},
|
|
||||||
line,
|
|
||||||
).filter((candidate) => {
|
|
||||||
const [candidateRadius, candidateAngle] = carthesian2Polar(
|
|
||||||
candidate,
|
|
||||||
arc.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
return fromAngle < toAngle
|
|
||||||
? Math.abs(radius - candidateRadius) < 0.0000001 &&
|
|
||||||
fromAngle <= candidateAngle &&
|
|
||||||
toAngle >= candidateAngle
|
|
||||||
: fromAngle <= candidateAngle || toAngle >= candidateAngle;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate a maximum of two intercept points for a line going throug an
|
* Calculate a maximum of two intercept points for a line going throug an
|
||||||
* ellipse.
|
* ellipse.
|
||||||
|
@ -1080,7 +1050,43 @@ export const interceptPointsOfLineAndEllipse = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return intersections;
|
return intersections.map((point) =>
|
||||||
|
rotatePoint(point, ellipse.center, ellipse.angle),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the intersection point(s) of a line segment represented by a start
|
||||||
|
* point and end point and a symmetric arc.
|
||||||
|
*/
|
||||||
|
export const interceptOfSymmetricArcAndSegment = (
|
||||||
|
arc: CircularArc,
|
||||||
|
line: Readonly<LineSegment>,
|
||||||
|
): Point[] => {
|
||||||
|
const radius = distancePoints(arc.from, arc.center);
|
||||||
|
const [, fromAngle] = carthesian2Polar(arc.from, arc.center);
|
||||||
|
const [, toAngle] = carthesian2Polar(arc.to, arc.center);
|
||||||
|
|
||||||
|
return interceptPointsOfLineAndEllipse(
|
||||||
|
{
|
||||||
|
center: arc.center,
|
||||||
|
angle: 0,
|
||||||
|
halfHeight: radius,
|
||||||
|
halfWidth: radius,
|
||||||
|
},
|
||||||
|
line,
|
||||||
|
).filter((candidate) => {
|
||||||
|
const [candidateRadius, candidateAngle] = carthesian2Polar(
|
||||||
|
candidate,
|
||||||
|
arc.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
return fromAngle < toAngle
|
||||||
|
? Math.abs(radius - candidateRadius) < 0.0000001 &&
|
||||||
|
fromAngle <= candidateAngle &&
|
||||||
|
toAngle >= candidateAngle
|
||||||
|
: fromAngle <= candidateAngle || toAngle >= candidateAngle;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1167,10 +1173,8 @@ export const interceptPointsOfSegmentAndRoundedRectangle = (
|
||||||
center,
|
center,
|
||||||
rectangle.angle,
|
rectangle.angle,
|
||||||
);
|
);
|
||||||
const rotatedSegment = [
|
|
||||||
rotatePoint(segment[0], center, rectangle.angle),
|
debugDrawSegments([segment]);
|
||||||
rotatePoint(segment[1], center, rectangle.angle),
|
|
||||||
] as LineSegment;
|
|
||||||
|
|
||||||
const candidates = interceptPointsOfSegmentAndPolygon(
|
const candidates = interceptPointsOfSegmentAndPolygon(
|
||||||
[
|
[
|
||||||
|
@ -1180,7 +1184,7 @@ export const interceptPointsOfSegmentAndRoundedRectangle = (
|
||||||
nonRotatedBottomLeft,
|
nonRotatedBottomLeft,
|
||||||
nonRotatedTopLeft,
|
nonRotatedTopLeft,
|
||||||
],
|
],
|
||||||
rotatedSegment,
|
segment,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check if candidate points are in rounded corner territory
|
// Check if candidate points are in rounded corner territory
|
||||||
|
@ -1238,20 +1242,32 @@ export const interceptPointsOfSegmentAndRoundedRectangle = (
|
||||||
...result,
|
...result,
|
||||||
...interceptOfSymmetricArcAndSegment(
|
...interceptOfSymmetricArcAndSegment(
|
||||||
{
|
{
|
||||||
center: [
|
center: rotatePoint(
|
||||||
nonRotatedTopLeft[0] + rectangle.roundness.topLeft,
|
[
|
||||||
nonRotatedTopLeft[1] + rectangle.roundness.topLeft,
|
rectangle.points.topLeft[0] + rectangle.roundness.topLeft,
|
||||||
],
|
rectangle.points.topLeft[1] + rectangle.roundness.topLeft,
|
||||||
from: [
|
],
|
||||||
nonRotatedTopLeft[0],
|
center,
|
||||||
nonRotatedTopLeft[1] + rectangle.roundness.topLeft,
|
rectangle.angle,
|
||||||
],
|
),
|
||||||
to: [
|
from: rotatePoint(
|
||||||
nonRotatedTopLeft[0] + rectangle.roundness.topLeft,
|
[
|
||||||
nonRotatedTopLeft[1],
|
rectangle.points.topLeft[0],
|
||||||
],
|
rectangle.points.topLeft[1] + rectangle.roundness.topLeft,
|
||||||
|
],
|
||||||
|
center,
|
||||||
|
rectangle.angle,
|
||||||
|
),
|
||||||
|
to: rotatePoint(
|
||||||
|
[
|
||||||
|
rectangle.points.topLeft[0] + rectangle.roundness.topLeft,
|
||||||
|
rectangle.points.topLeft[1],
|
||||||
|
],
|
||||||
|
center,
|
||||||
|
rectangle.angle,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
rotatedSegment,
|
segment,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -1261,20 +1277,32 @@ export const interceptPointsOfSegmentAndRoundedRectangle = (
|
||||||
...result,
|
...result,
|
||||||
...interceptOfSymmetricArcAndSegment(
|
...interceptOfSymmetricArcAndSegment(
|
||||||
{
|
{
|
||||||
center: [
|
center: rotatePoint(
|
||||||
nonRotatedTopRight[0] - rectangle.roundness.topRight,
|
[
|
||||||
nonRotatedTopRight[1] + rectangle.roundness.topRight,
|
rectangle.points.topRight[0] - rectangle.roundness.topRight,
|
||||||
],
|
rectangle.points.topRight[1] + rectangle.roundness.topRight,
|
||||||
from: [
|
],
|
||||||
nonRotatedTopRight[0] - rectangle.roundness.topRight,
|
center,
|
||||||
nonRotatedTopRight[1],
|
rectangle.angle,
|
||||||
],
|
),
|
||||||
to: [
|
from: rotatePoint(
|
||||||
nonRotatedTopRight[0],
|
[
|
||||||
nonRotatedTopRight[1] + rectangle.roundness.topRight,
|
rectangle.points.topRight[0] - rectangle.roundness.topRight,
|
||||||
],
|
rectangle.points.topRight[1],
|
||||||
|
],
|
||||||
|
center,
|
||||||
|
rectangle.angle,
|
||||||
|
),
|
||||||
|
to: rotatePoint(
|
||||||
|
[
|
||||||
|
rectangle.points.topRight[0],
|
||||||
|
rectangle.points.topRight[1] + rectangle.roundness.topRight,
|
||||||
|
],
|
||||||
|
center,
|
||||||
|
rectangle.angle,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
rotatedSegment,
|
segment,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -1284,20 +1312,32 @@ export const interceptPointsOfSegmentAndRoundedRectangle = (
|
||||||
...result,
|
...result,
|
||||||
...interceptOfSymmetricArcAndSegment(
|
...interceptOfSymmetricArcAndSegment(
|
||||||
{
|
{
|
||||||
center: [
|
center: rotatePoint(
|
||||||
nonRotatedBottomRight[0] - rectangle.roundness.bottomRight,
|
[
|
||||||
nonRotatedBottomRight[1] - rectangle.roundness.bottomRight,
|
rectangle.points.bottomRight[0] - rectangle.roundness.bottomRight,
|
||||||
],
|
rectangle.points.bottomRight[1] - rectangle.roundness.bottomRight,
|
||||||
from: [
|
],
|
||||||
nonRotatedBottomRight[0],
|
center,
|
||||||
nonRotatedBottomRight[1] - rectangle.roundness.bottomRight,
|
rectangle.angle,
|
||||||
],
|
),
|
||||||
to: [
|
from: rotatePoint(
|
||||||
nonRotatedBottomRight[0] - rectangle.roundness.bottomRight,
|
[
|
||||||
nonRotatedBottomRight[1],
|
rectangle.points.bottomRight[0],
|
||||||
],
|
rectangle.points.bottomRight[1] - rectangle.roundness.bottomRight,
|
||||||
|
],
|
||||||
|
center,
|
||||||
|
rectangle.angle,
|
||||||
|
),
|
||||||
|
to: rotatePoint(
|
||||||
|
[
|
||||||
|
rectangle.points.bottomRight[0] - rectangle.roundness.bottomRight,
|
||||||
|
rectangle.points.bottomRight[1],
|
||||||
|
],
|
||||||
|
center,
|
||||||
|
rectangle.angle,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
rotatedSegment,
|
segment,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -1307,20 +1347,32 @@ export const interceptPointsOfSegmentAndRoundedRectangle = (
|
||||||
...result,
|
...result,
|
||||||
...interceptOfSymmetricArcAndSegment(
|
...interceptOfSymmetricArcAndSegment(
|
||||||
{
|
{
|
||||||
center: [
|
center: rotatePoint(
|
||||||
nonRotatedBottomLeft[0] + rectangle.roundness.bottomLeft,
|
[
|
||||||
nonRotatedBottomLeft[1] - rectangle.roundness.bottomLeft,
|
rectangle.points.bottomLeft[0] + rectangle.roundness.bottomLeft,
|
||||||
],
|
rectangle.points.bottomLeft[1] - rectangle.roundness.bottomLeft,
|
||||||
from: [
|
],
|
||||||
nonRotatedBottomLeft[0] + rectangle.roundness.bottomLeft,
|
center,
|
||||||
nonRotatedBottomLeft[1],
|
rectangle.angle,
|
||||||
],
|
),
|
||||||
to: [
|
from: rotatePoint(
|
||||||
nonRotatedBottomLeft[0],
|
[
|
||||||
nonRotatedBottomLeft[1] - rectangle.roundness.bottomLeft,
|
rectangle.points.bottomLeft[0] + rectangle.roundness.bottomLeft,
|
||||||
],
|
rectangle.points.bottomLeft[1],
|
||||||
|
],
|
||||||
|
center,
|
||||||
|
rectangle.angle,
|
||||||
|
),
|
||||||
|
to: rotatePoint(
|
||||||
|
[
|
||||||
|
rectangle.points.bottomLeft[0],
|
||||||
|
rectangle.points.bottomLeft[1] - rectangle.roundness.bottomLeft,
|
||||||
|
],
|
||||||
|
center,
|
||||||
|
rectangle.angle,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
rotatedSegment,
|
segment,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue