feat: path closure

This commit is contained in:
hypercross 2026-03-15 18:28:44 +08:00
parent 2d4b719e10
commit a840261ae0
1 changed files with 83 additions and 14 deletions

View File

@ -22,15 +22,8 @@ export function pts2plotter(
let str = init(width * px2mm, height * px2mm);
// 按 X 轴然后 Y 轴排序路径
const sorted = pts.slice();
sorted.sort(function (a, b) {
const [ax, ay] = topleft(a);
const [bx, by] = topleft(b);
if (ax !== bx) return ax - bx;
return ay - by;
});
// 使用最近邻算法排序路径
const sorted = sortPathsByNearestNeighbor(pts, start);
// 从起点到第一个路径
if (sorted.length > 0) {
@ -44,6 +37,11 @@ export function pts2plotter(
str += ` D${plu(pt[0] * px2mm)},${plu((height - pt[1]) * px2mm)}`;
}
// 如果第一个路径未闭合,添加闭合命令
if (!isPathClosed(firstPath)) {
str += ` D${plu(firstPath[0][0] * px2mm)},${plu((height - firstPath[0][1]) * px2mm)}`;
}
// 路径之间移动
for (let i = 1; i < sorted.length; i++) {
const prevPath = sorted[i - 1];
@ -58,6 +56,11 @@ export function pts2plotter(
const pt = currPath[j];
str += ` D${plu(pt[0] * px2mm)},${plu((height - pt[1]) * px2mm)}`;
}
// 如果当前路径未闭合,添加闭合命令
if (!isPathClosed(currPath)) {
str += ` D${plu(currPath[0][0] * px2mm)},${plu((height - currPath[0][1]) * px2mm)}`;
}
}
}
@ -88,6 +91,72 @@ function topleft(pts: [number, number][]) {
return [minx, miny] as [number, number];
}
/**
*
* @param path
* @param threshold 0.01
*/
function isPathClosed(path: [number, number][], threshold = 0.01): boolean {
if (path.length < 2) return true;
const [sx, sy] = path[0];
const [ex, ey] = path[path.length - 1];
const dist = Math.sqrt((sx - ex) ** 2 + (sy - ey) ** 2);
return dist < threshold;
}
/**
*
* @param pt
* @param path
*/
function distanceToPath(pt: [number, number], path: [number, number][]): number {
let minDist = Infinity;
for (const p of path) {
const dist = Math.sqrt((pt[0] - p[0]) ** 2 + (pt[1] - p[1]) ** 2);
if (dist < minDist) minDist = dist;
}
return minDist;
}
/**
* 使
* @param paths
* @param startPos
*/
function sortPathsByNearestNeighbor(
paths: [number, number][][],
startPos: [number, number]
): [number, number][][] {
if (paths.length === 0) return [];
const result: [number, number][][] = [];
const remaining = paths.slice();
let currentPos = startPos;
while (remaining.length > 0) {
// 找到距离当前位置最近的路径
let nearestIndex = 0;
let nearestDist = Infinity;
for (let i = 0; i < remaining.length; i++) {
const dist = distanceToPath(currentPos, remaining[i]);
if (dist < nearestDist) {
nearestDist = dist;
nearestIndex = i;
}
}
// 将最近的路径添加到结果中
const nearestPath = remaining.splice(nearestIndex, 1)[0];
result.push(nearestPath);
// 更新当前位置为该路径的终点
currentPos = nearestPath[nearestPath.length - 1];
}
return result;
}
function init(w: number, h: number) {
return ` IN TB26,${plu(w)},${plu(h)} CT1`;
}