Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/renderers/draw-features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function featurePathRenderer(feature: PackedGraphFeature): string {
}

const simplifiedPoints = simplify(points, 0.3);
const clippedPoints = clipPoly(simplifiedPoints, graphWidth, graphHeight);
const clippedPoints = clipPoly(simplifiedPoints, graphWidth, graphHeight, 1);

const lineGen = line().curve(curveBasisClosed);
const path = `${round(lineGen(clippedPoints) || "")}Z`;
Expand Down
25 changes: 23 additions & 2 deletions src/utils/commonUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,41 @@ import { rand } from "./probabilityUtils";
* @param points - Array of points [[x1, y1], [x2, y2], ...]
* @param graphWidth - Width of the graph
* @param graphHeight - Height of the graph
* @param secure - When truthy, duplicate boundary-crossing points to prevent B-spline
* curves from arcing away from map edges (restores original "secure clipping" behavior)
* @returns Clipped polygon points
*/
export const clipPoly = (
points: [number, number][],
graphWidth: number,
graphHeight: number,
secure?: number,
) => {
if (points.length < 2) return points;
if (points.some((point) => point === undefined)) {
window.ERROR && console.error("Undefined point in clipPoly", points);
return points;
}

return clipPolygon(points, [0, 0, graphWidth, graphHeight]);
const clipped = clipPolygon(points, [0, 0, graphWidth, graphHeight]);

if (!secure || !clipped.length) return clipped;

// Duplicate each boundary point twice so the B-spline passes through it
// rather than arcing away from the map edge (replicates polygonclip secure=1)
const secured: [number, number][] = [];
for (const point of clipped) {
secured.push(point);
if (
point[0] === 0 ||
point[0] === graphWidth ||
point[1] === 0 ||
point[1] === graphHeight
) {
secured.push(point, point);
}
Comment on lines +12 to +44
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation duplicates all points that lie on the map boundary, but the new JSDoc (and PR description) describe duplicating only boundary-crossing intersection points. If the intent is to match the original polygonclip secure=1 semantics more closely, consider detecting only the points introduced by clipping (or update the doc/PR description to reflect that all boundary points are triplicated).

Copilot uses AI. Check for mistakes.
}
return secured;
};

/**
Expand Down Expand Up @@ -375,7 +396,7 @@ declare global {
interface Window {
ERROR: boolean;

clipPoly: typeof clipPoly;
clipPoly: (points: [number, number][], secure?: number) => [number, number][];
getSegmentId: typeof getSegmentId;
debounce: typeof debounce;
throttle: typeof throttle;
Expand Down
4 changes: 2 additions & 2 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@ import {
wiki,
} from "./commonUtils";

window.clipPoly = (points: [number, number][]) =>
clipPoly(points, graphWidth, graphHeight);
window.clipPoly = (points: [number, number][], secure?: number) =>
clipPoly(points, graphWidth, graphHeight, secure);
window.getSegmentId = getSegmentId;
window.debounce = debounce;
window.throttle = throttle;
Expand Down