refactor: curve and shorten 2-points routes

This commit is contained in:
max 2022-08-23 21:34:33 +03:00
parent bbf8871e70
commit 03809e56ab
3 changed files with 48 additions and 7 deletions

View file

@ -1,11 +1,12 @@
import * as d3 from "d3"; import * as d3 from "d3";
import {getNormal} from "utils/lineUtils";
import {round} from "utils/stringUtils"; import {round} from "utils/stringUtils";
const lineGenTypeMap: {[key in IRoute["type"]]: d3.CurveFactory | d3.CurveFactoryLineOnly} = { const lineGenTypeMap: {[key in IRoute["type"]]: d3.CurveFactory | d3.CurveFactoryLineOnly} = {
road: d3.curveCatmullRom.alpha(0.1), road: d3.curveCatmullRom.alpha(0.1),
trail: d3.curveCatmullRom.alpha(0.1), trail: d3.curveCatmullRom.alpha(0.1),
sea: d3.curveBundle.beta(1) sea: d3.curveBasis
}; };
export function drawRoutes() { export function drawRoutes() {
@ -22,7 +23,7 @@ export function drawRoutes() {
for (const {i, type, cells} of pack.routes) { for (const {i, type, cells} of pack.routes) {
if (type !== "sea") straightenPathAngles(cells); // mutates points if (type !== "sea") straightenPathAngles(cells); // mutates points
const pathPoints = cells.map(cellId => points[cellId]); const pathPoints = getPathPoints(cells);
lineGen.curve(lineGenTypeMap[type]); lineGen.curve(lineGenTypeMap[type]);
const path = round(lineGen(pathPoints)!, 1); 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;
}
} }

View file

@ -1,5 +1,7 @@
// utils to be used for debugging (not in PROD) // utils to be used for debugging (not in PROD)
import {getNormal} from "./lineUtils";
export function drawPoint([x, y]: TPoint, {radius = 1, color = "red"} = {}) { 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); debug.append("circle").attr("cx", x).attr("cy", y).attr("r", radius).attr("fill", color);
} }
@ -10,7 +12,7 @@ export function drawPolygon(
) { ) {
debug debug
.append("polyline") .append("polyline")
.attr("points", [...points, points[0]]) .attr("points", [...points, points[0]].join(" "))
.attr("fill", fill) .attr("fill", fill)
.attr("fill-opacity", fillOpacity) .attr("fill-opacity", fillOpacity)
.attr("stroke", stroke) .attr("stroke", stroke)
@ -28,10 +30,8 @@ export function drawLine([x1, y1]: TPoint, [x2, y2]: TPoint, {stroke = "#444", s
.attr("stroke-width", strokeWidth); .attr("stroke-width", strokeWidth);
} }
export function drawArrow([x1, y1]: TPoint, [x2, y2]: TPoint, {width = 1, color = "#444"} = {}): void { export function drawArrow([x1, y1]: TPoint, [x2, y2]: TPoint, {width = 1, color = "#444"} = {}) {
const angle = Math.atan2(y2 - y1, x2 - x1); const normal = getNormal([x1, y1], [x2, y2]);
const normal = angle + Math.PI / 2;
const [xMid, yMid] = [(x1 + x2) / 2, (y1 + y2) / 2]; const [xMid, yMid] = [(x1 + x2) / 2, (y1 + y2) / 2];
const [xLeft, yLeft] = [xMid + width * Math.cos(normal), yMid + width * Math.sin(normal)]; const [xLeft, yLeft] = [xMid + width * Math.cos(normal), yMid + width * Math.sin(normal)];

View file

@ -74,3 +74,7 @@ export function filterOutOfCanvasPoints(points: TPoints) {
return points.filter((_, i) => filterOutCanvasPoint(i)); 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;
}