diff --git a/services/pcp-tgu-ops-ui/src/features/tgu-map-2d/canvas/drawRequests.ts b/services/pcp-tgu-ops-ui/src/features/tgu-map-2d/canvas/drawRequests.ts index 1839716..59671b6 100644 --- a/services/pcp-tgu-ops-ui/src/features/tgu-map-2d/canvas/drawRequests.ts +++ b/services/pcp-tgu-ops-ui/src/features/tgu-map-2d/canvas/drawRequests.ts @@ -8,33 +8,53 @@ const SURVEY_COLORS: Record = { }; const DEFAULT_COLOR = "#FFA040"; +type Pt = { x: number; y: number }; + +function unwrapX(points: Pt[], worldSize: number): Pt[] { + if (points.length === 0) return []; + const out: Pt[] = [points[0]]; + let prevX = points[0].x; + for (let i = 1; i < points.length; i++) { + let x = points[i].x; + while (x - prevX > worldSize / 2) x -= worldSize; + while (prevX - x > worldSize / 2) x += worldSize; + out.push({ x, y: points[i].y }); + prevX = x; + } + return out; +} + export function drawRequests( ctx: CanvasRenderingContext2D, polygons: MapPolygon[], projection: MapProjection, - _viewport: { width: number; height: number } + viewport: { width: number; height: number } ) { ctx.save(); for (const polygon of polygons) { if (polygon.points.length < 3) continue; - const screenPoints = polygon.points.map((point) => projection.project(point)); - + const raw = polygon.points.map((p) => projection.project(p)); + const pts = unwrapX(raw, projection.worldSize); const color = SURVEY_COLORS[polygon.surveyType ?? ""] ?? DEFAULT_COLOR; - ctx.beginPath(); - screenPoints.forEach((point, index) => { - if (index === 0) { - ctx.moveTo(point.x, point.y); - } else { - ctx.lineTo(point.x, point.y); - } - }); - ctx.closePath(); ctx.fillStyle = hexToRgba(color, 0.18); ctx.strokeStyle = hexToRgba(color, 0.85); ctx.lineWidth = 1.5; ctx.setLineDash([4, 3]); - ctx.fill(); - ctx.stroke(); + for (const dx of [0, projection.worldSize, -projection.worldSize]) { + const visible = pts.some((p) => { + const x = p.x + dx; + return x >= -64 && x <= viewport.width + 64 && p.y >= -64 && p.y <= viewport.height + 64; + }); + if (!visible) continue; + ctx.beginPath(); + for (let i = 0; i < pts.length; i++) { + if (i === 0) ctx.moveTo(pts[i].x + dx, pts[i].y); + else ctx.lineTo(pts[i].x + dx, pts[i].y); + } + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } ctx.setLineDash([]); } ctx.restore(); diff --git a/services/pcp-tgu-ops-ui/src/features/tgu-map-2d/canvas/drawSwath.ts b/services/pcp-tgu-ops-ui/src/features/tgu-map-2d/canvas/drawSwath.ts index 2a5125a..c2dee16 100644 --- a/services/pcp-tgu-ops-ui/src/features/tgu-map-2d/canvas/drawSwath.ts +++ b/services/pcp-tgu-ops-ui/src/features/tgu-map-2d/canvas/drawSwath.ts @@ -1,6 +1,38 @@ import type { MapProjection } from "../geometry/mapProjection"; import type { MapPolygon } from "../model/mapTypes"; +type Pt = { x: number; y: number }; + +function unwrapX(points: Pt[], worldSize: number): Pt[] { + if (points.length === 0) return []; + const out: Pt[] = [points[0]]; + let prevX = points[0].x; + for (let i = 1; i < points.length; i++) { + let x = points[i].x; + while (x - prevX > worldSize / 2) x -= worldSize; + while (prevX - x > worldSize / 2) x += worldSize; + out.push({ x, y: points[i].y }); + prevX = x; + } + return out; +} + +function inView(points: Pt[], dx: number, vw: number, vh: number): boolean { + return points.some((p) => { + const x = p.x + dx; + return x >= -64 && x <= vw + 64 && p.y >= -64 && p.y <= vh + 64; + }); +} + +function tracePath(ctx: CanvasRenderingContext2D, points: Pt[], dx: number) { + ctx.beginPath(); + for (let i = 0; i < points.length; i++) { + if (i === 0) ctx.moveTo(points[i].x + dx, points[i].y); + else ctx.lineTo(points[i].x + dx, points[i].y); + } + ctx.closePath(); +} + export function drawSwath( ctx: CanvasRenderingContext2D, polygons: MapPolygon[], @@ -13,18 +45,14 @@ export function drawSwath( ctx.lineWidth = 1; for (const polygon of polygons) { if (polygon.points.length < 3) continue; - const screenPoints = polygon.points.map((point) => projection.project(point)); - if (!screenPoints.some((p) => p.x >= -32 && p.x <= viewport.width + 32 && p.y >= -32 && p.y <= viewport.height + 32)) { - continue; + const raw = polygon.points.map((p) => projection.project(p)); + const pts = unwrapX(raw, projection.worldSize); + for (const dx of [0, projection.worldSize, -projection.worldSize]) { + if (!inView(pts, dx, viewport.width, viewport.height)) continue; + tracePath(ctx, pts, dx); + ctx.fill(); + ctx.stroke(); } - ctx.beginPath(); - screenPoints.forEach((p, i) => { - if (i === 0) ctx.moveTo(p.x, p.y); - else ctx.lineTo(p.x, p.y); - }); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); } ctx.restore(); }