feat: Cross-stitch algo

This commit is contained in:
Mark Tolmacs
2025-12-03 09:20:02 +00:00
parent fdb8aaf44e
commit 4a67c3e9b7
2 changed files with 66 additions and 33 deletions

View File

@@ -1,10 +1,12 @@
import { import {
type GlobalPoint, type GlobalPoint,
Line,
type LineSegment, type LineSegment,
lineSegment, lineSegment,
lineSegmentIntersectionPoints, lineSegmentIntersectionPoints,
type LocalPoint, type LocalPoint,
pointDistance, pointDistance,
pointDistanceSq,
pointFrom, pointFrom,
pointFromVector, pointFromVector,
vectorAntiNormal, vectorAntiNormal,
@@ -149,9 +151,6 @@ function generateSegments(
segments[idx - 2][1], segments[idx - 2][1],
pointFrom<LocalPoint>(input[0][0], input[0][1]), pointFrom<LocalPoint>(input[0][0], input[0][1]),
); );
// debugDrawPoint(
// pointFrom<LocalPoint>(element.x + segments[0], input[0][1]),
// );
segments[idx++] = lineSegment( segments[idx++] = lineSegment(
pointFrom<LocalPoint>(input[0][0], input[0][1]), pointFrom<LocalPoint>(input[0][0], input[0][1]),
segments[0][0], segments[0][0],
@@ -167,25 +166,51 @@ export function getStroke(
options: any, options: any,
element: ExcalidrawFreeDrawElement, element: ExcalidrawFreeDrawElement,
): LocalPoint[] { ): LocalPoint[] {
const segments = generateSegments(input, element); const segments: (LineSegment<LocalPoint> | undefined)[] = generateSegments(
input,
element,
);
// for (let j = 0; j < segments.length; j++) { const MIN_DIST_SQ = 1 ** 2;
// segments.forEach((s, i) => { for (let j = 0; j < segments.length; j++) {
// if (j !== i && j + 1 !== i && j !== i + 1) { for (let i = j + 1; i < segments.length; i++) {
// const intersection = lineSegmentIntersectionPoints(segments[j], s); const a = segments[j];
// if (intersection?.length) { const b = segments[i];
// console.log( if (!a || !b) {
// "intersection", continue;
// j, }
// i,
// pointDistance(segments[j][0], segments[j][1]), const intersection = lineSegmentIntersectionPoints(a, b);
// pointDistance(s[0], s[1]),
// //lineSegmentIntersectionPoints(segments[j], s), if (
// ); intersection &&
// } pointDistanceSq(a[0], intersection) > MIN_DIST_SQ &&
// } pointDistanceSq(a[1], intersection) > MIN_DIST_SQ
// }); ) {
// } debugDrawPoint(
pointFrom<GlobalPoint>(
element.x + intersection[0],
element.y + intersection[1],
),
{ color: "#FF00FF", permanent: true },
);
console.log("intersection", j, i, intersection);
}
// if (j !== i && j + 1 !== i && j !== i + 1) {
// const intersection = lineSegmentIntersectionPoints(segments[j], s);
// if (intersection?.length) {
// console.log(
// "intersection",
// j,
// i,
// pointDistance(segments[j][0], segments[j][1]),
// pointDistance(s[0], s[1]),
// //lineSegmentIntersectionPoints(segments[j], s),
// );
// }
// }
}
}
// for (let i = 0; i < segments.length; i++) { // for (let i = 0; i < segments.length; i++) {
// if (i < 2 || i > segments.length - 3) { // if (i < 2 || i > segments.length - 3) {
@@ -213,6 +238,16 @@ export function getStroke(
// } // }
// } // }
debugSegments(segments, input, element);
return [segments[0][0], ...segments.map((s) => s[1])];
}
function debugSegments(
segments: LineSegment<LocalPoint>[],
input: readonly [number, number, number][] | readonly [number, number][],
element: ExcalidrawFreeDrawElement,
): void {
const colors = [ const colors = [
"#FF0000", "#FF0000",
"#00FF00", "#00FF00",
@@ -253,6 +288,4 @@ export function getStroke(
{ color: "#000000", permanent: true }, { color: "#000000", permanent: true },
); );
}); });
return [segments[0][0], ...segments.map((s) => s[1])];
} }

View File

@@ -1119,16 +1119,16 @@ export function getFreedrawOutlinePoints(element: ExcalidrawFreeDrawElement) {
last: true, last: true,
}; };
// return getStroke( return getStroke(
// [ [
// [0, 0], [0, 0],
// [30, -30], [30, -30],
// [60, -30], [60, -30],
// ], ],
// options, options,
// element, element,
// ); );
return getStroke(inputPoints, options, element) as [number, number][]; //return getStroke(inputPoints, options, element) as [number, number][];
} }
function med(A: number[], B: number[]) { function med(A: number[], B: number[]) {