From 03809e56abd0f2ae39566d51e1bc1129c9e82475 Mon Sep 17 00:00:00 2001 From: max Date: Tue, 23 Aug 2022 21:34:33 +0300 Subject: [PATCH] refactor: curve and shorten 2-points routes --- src/layers/renderers/drawRoutes.ts | 41 ++++++++++++++++++++++++++++-- src/utils/debugUtils.ts | 10 ++++---- src/utils/lineUtils.ts | 4 +++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/layers/renderers/drawRoutes.ts b/src/layers/renderers/drawRoutes.ts index 2c9751f4..1518dd8d 100644 --- a/src/layers/renderers/drawRoutes.ts +++ b/src/layers/renderers/drawRoutes.ts @@ -1,11 +1,12 @@ import * as d3 from "d3"; +import {getNormal} from "utils/lineUtils"; import {round} from "utils/stringUtils"; const lineGenTypeMap: {[key in IRoute["type"]]: d3.CurveFactory | d3.CurveFactoryLineOnly} = { road: d3.curveCatmullRom.alpha(0.1), trail: d3.curveCatmullRom.alpha(0.1), - sea: d3.curveBundle.beta(1) + sea: d3.curveBasis }; export function drawRoutes() { @@ -22,7 +23,7 @@ export function drawRoutes() { for (const {i, type, cells} of pack.routes) { if (type !== "sea") straightenPathAngles(cells); // mutates points - const pathPoints = cells.map(cellId => points[cellId]); + const pathPoints = getPathPoints(cells); lineGen.curve(lineGenTypeMap[type]); const path = round(lineGen(pathPoints)!, 1); @@ -79,4 +80,40 @@ export function drawRoutes() { } } } + + function getPathPoints(cellIds: number[]): TPoints { + const pathPoints = cellIds.map(cellId => points[cellId]); + + if (pathPoints.length === 2) { + // curve and shorten 2-points line + const [[x1, y1], [x2, y2]] = pathPoints; + + const middleX = (x1 + x2) / 2; + const middleY = (y1 + y2) / 2; + + // add shifted point at the middle to curve the line a bit + const NORMAL_LENGTH = 0.3; + const normal = getNormal([x1, y1], [x2, y2]); + const sign = cellIds[0] % 2 ? 1 : -1; + const normalX = middleX + NORMAL_LENGTH * Math.cos(normal) * sign; + const normalY = middleY + NORMAL_LENGTH * Math.sin(normal) * sign; + + // make line shorter to avoid overlapping with other lines + const SHORT_LINE_LENGTH_MODIFIER = 0.8; + const distX = x2 - x1; + const distY = y2 - y1; + const nx1 = x1 + distX * SHORT_LINE_LENGTH_MODIFIER; + const ny1 = y1 + distY * SHORT_LINE_LENGTH_MODIFIER; + const nx2 = x2 - distX * SHORT_LINE_LENGTH_MODIFIER; + const ny2 = y2 - distY * SHORT_LINE_LENGTH_MODIFIER; + + return [ + [nx1, ny1], + [normalX, normalY], + [nx2, ny2] + ]; + } + + return pathPoints; + } } diff --git a/src/utils/debugUtils.ts b/src/utils/debugUtils.ts index 20a3113c..623d66a5 100644 --- a/src/utils/debugUtils.ts +++ b/src/utils/debugUtils.ts @@ -1,5 +1,7 @@ // utils to be used for debugging (not in PROD) +import {getNormal} from "./lineUtils"; + export function drawPoint([x, y]: TPoint, {radius = 1, color = "red"} = {}) { debug.append("circle").attr("cx", x).attr("cy", y).attr("r", radius).attr("fill", color); } @@ -10,7 +12,7 @@ export function drawPolygon( ) { debug .append("polyline") - .attr("points", [...points, points[0]]) + .attr("points", [...points, points[0]].join(" ")) .attr("fill", fill) .attr("fill-opacity", fillOpacity) .attr("stroke", stroke) @@ -28,10 +30,8 @@ export function drawLine([x1, y1]: TPoint, [x2, y2]: TPoint, {stroke = "#444", s .attr("stroke-width", strokeWidth); } -export function drawArrow([x1, y1]: TPoint, [x2, y2]: TPoint, {width = 1, color = "#444"} = {}): void { - const angle = Math.atan2(y2 - y1, x2 - x1); - const normal = angle + Math.PI / 2; - +export function drawArrow([x1, y1]: TPoint, [x2, y2]: TPoint, {width = 1, color = "#444"} = {}) { + const normal = getNormal([x1, y1], [x2, y2]); const [xMid, yMid] = [(x1 + x2) / 2, (y1 + y2) / 2]; const [xLeft, yLeft] = [xMid + width * Math.cos(normal), yMid + width * Math.sin(normal)]; diff --git a/src/utils/lineUtils.ts b/src/utils/lineUtils.ts index bb0f6008..0d837f12 100644 --- a/src/utils/lineUtils.ts +++ b/src/utils/lineUtils.ts @@ -74,3 +74,7 @@ export function filterOutOfCanvasPoints(points: TPoints) { return points.filter((_, i) => filterOutCanvasPoint(i)); } + +export function getNormal([x1, y1]: TPoint, [x2, y2]: TPoint) { + return Math.atan2(y1 - y2, x1 - x2) + Math.PI / 2; +}