fix: Orientation

This commit is contained in:
Mark Tolmacs
2025-12-02 21:33:08 +00:00
parent 6f4081e371
commit fdb8aaf44e
2 changed files with 220 additions and 44 deletions

View File

@@ -1,7 +1,10 @@
import {
type GlobalPoint,
type LineSegment,
lineSegment,
lineSegmentIntersectionPoints,
type LocalPoint,
pointDistance,
pointFrom,
pointFromVector,
vectorAntiNormal,
@@ -12,69 +15,148 @@ import {
} from "@excalidraw/math";
import { type ExcalidrawFreeDrawElement } from "./types";
import { doLineSegmentsIntersect } from "@excalidraw/utils";
import { debugDrawLine, debugDrawPoint, distance } from "@excalidraw/common";
const offset = (
x: number,
y: number,
pressure: number,
direction: "left" | "right",
origin: LocalPoint,
) => {
const p = pointFrom<LocalPoint>(x, y);
const v = vectorNormalize(vectorFromPoint(p, origin));
const normal = direction === "left" ? vectorNormal(v) : vectorAntiNormal(v);
const scaled = vectorScale(normal, pressure / 2);
return pointFromVector(scaled, origin);
};
function generateSegments(
input:
| readonly [x: number, y: number, pressure: number][]
| readonly [x: number, y: number][],
element: ExcalidrawFreeDrawElement,
): LineSegment<LocalPoint>[] {
if (input.length < 2) {
if (input.length < 3) {
return [];
}
const offset = (
x: number,
y: number,
pressure: number,
direction: "left" | "right",
) => {
const p = pointFrom<LocalPoint>(x, y);
const v = vectorNormalize(vectorFromPoint(p));
const normal = direction === "left" ? vectorAntiNormal(v) : vectorNormal(v);
const scaled = vectorScale(normal, pressure / 2);
return pointFromVector(scaled, p);
};
let idx = 0;
const segments = Array(input.length * 2 - 1);
//const segments: LineSegment<LocalPoint>[] = [];
const segments = Array(input.length * 4 - 4);
segments[idx++] = lineSegment(
offset(input[0][0], input[0][1], input[0][2] ?? 5, "left"),
offset(input[1][0], input[1][1], input[0][2] ?? 5, "left"),
);
for (let i = 2; i < input.length; i++) {
const point = input[i];
const prev = segments[idx - 1][1];
segments[idx++] = lineSegment(
prev,
offset(point[0], point[1], point[2] ?? 5, "left"),
);
}
const prev = segments[idx - 1][1];
segments[idx++] = lineSegment(
prev,
offset(
input[input.length - 1][0],
input[input.length - 1][1],
input[input.length - 1][2] ?? 5,
input[1][0],
input[1][1],
input[1][2] ?? 5,
"left",
pointFrom<LocalPoint>(input[0][0], input[0][1]),
),
offset(
input[0][0],
input[0][1],
input[0][2] ?? 5,
"right",
pointFrom<LocalPoint>(input[1][0], input[1][1]),
),
);
for (let i = input.length - 2; i >= 0; i--) {
const point = input[i];
const prev = segments[idx - 1][1];
segments[idx++] = lineSegment(
prev,
offset(point[0], point[1], point[2] ?? 5, "right"),
for (let i = 2; i < input.length; i++) {
const a = segments[idx - 1][1];
const b = offset(
input[i][0],
input[i][1],
input[i][2] ?? 5,
"left",
pointFrom<LocalPoint>(input[i - 1][0], input[i - 1][1]),
);
const c = offset(
input[i - 1][0],
input[i - 1][1],
input[i - 1][2] ?? 5,
"right",
pointFrom<LocalPoint>(input[i][0], input[i][1]),
);
segments[idx++] = lineSegment(a, b); // Bridge segment
segments[idx++] = lineSegment(b, c); // Main segment
}
// Turnaround segments
const prev = segments[idx - 1][1];
segments[idx++] = lineSegment(
prev,
pointFrom<LocalPoint>(
input[input.length - 1][0],
input[input.length - 1][1],
),
);
segments[idx++] = lineSegment(
pointFrom<LocalPoint>(
input[input.length - 1][0],
input[input.length - 1][1],
),
offset(
input[input.length - 2][0],
input[input.length - 2][1],
input[input.length - 2][2] ?? 5,
"left",
pointFrom<LocalPoint>(
input[input.length - 1][0],
input[input.length - 1][1],
),
),
);
for (let i = input.length - 2; i > 0; i--) {
const a = segments[idx - 1][1];
const b = offset(
input[i + 1][0],
input[i + 1][1],
input[i + 1][2] ?? 5,
"right",
pointFrom<LocalPoint>(input[i][0], input[i][1]),
);
const c = offset(
input[i - 1][0],
input[i - 1][1],
input[i - 1][2] ?? 5,
"left",
pointFrom<LocalPoint>(input[i][0], input[i][1]),
);
segments[idx++] = lineSegment(a, b); // Main segment
segments[idx++] = lineSegment(b, c); // Bridge segment
}
const last = segments[idx - 1][1];
segments[idx++] = lineSegment(
last,
offset(
input[1][0],
input[1][1],
input[1][2] ?? 5,
"right",
pointFrom<LocalPoint>(input[0][0], input[0][1]),
),
);
// Closing cap
segments[idx++] = lineSegment(
segments[idx - 2][1],
pointFrom<LocalPoint>(input[0][0], input[0][1]),
);
// debugDrawPoint(
// pointFrom<LocalPoint>(element.x + segments[0], input[0][1]),
// );
segments[idx++] = lineSegment(
pointFrom<LocalPoint>(input[0][0], input[0][1]),
segments[0][0],
);
return segments;
}
@@ -85,7 +167,92 @@ export function getStroke(
options: any,
element: ExcalidrawFreeDrawElement,
): LocalPoint[] {
const segments = generateSegments(input);
const segments = generateSegments(input, element);
// for (let j = 0; j < segments.length; j++) {
// segments.forEach((s, i) => {
// 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++) {
// if (i < 2 || i > segments.length - 3) {
// continue;
// }
// const intersection1 = lineSegmentIntersectionPoints(
// segments[i - 2],
// segments[i],
// );
// // if (intersection1) {
// // segments[i][0] = intersection1;
// // }
// const intersection2 = lineSegmentIntersectionPoints(
// segments[i + 2],
// segments[i],
// );
// // if (intersection2) {
// // segments[i][1] = intersection2;
// // }
// if (!!intersection1 !== !!intersection2) {
// console.log("??", intersection1, intersection2);
// }
// }
const colors = [
"#FF0000",
"#00FF00",
"#0000FF",
// "#FFFF00",
// "#00FFFF",
// "#FF00FF",
// "#C0C0C0",
// "#800000",
// "#808000",
// "#008000",
// "#800080",
// "#008080",
// "#000080",
];
segments.forEach((s, i) => {
debugDrawLine(
lineSegment(
pointFrom<GlobalPoint>(element.x + s[0][0], element.y + s[0][1]),
pointFrom<GlobalPoint>(element.x + s[1][0], element.y + s[1][1]),
),
{ color: colors[i % colors.length], permanent: true },
);
});
input.forEach((p, i) => {
if (i === 0) {
return;
}
debugDrawLine(
lineSegment(
pointFrom<GlobalPoint>(
element.x + input[i - 1][0],
element.y + input[i - 1][1],
),
pointFrom<GlobalPoint>(element.x + p[0], element.y + p[1]),
),
{ color: "#000000", permanent: true },
);
});
return [segments[0][0], ...segments.map((s) => s[1])];
}

View File

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