diff --git a/src/pages/Route/route-preview/Constants.ts b/src/pages/Route/route-preview/Constants.ts
index 1c4add7..498a2ae 100644
--- a/src/pages/Route/route-preview/Constants.ts
+++ b/src/pages/Route/route-preview/Constants.ts
@@ -5,5 +5,5 @@ export const STATION_OUTLINE_WIDTH = 4;
export const SIGHT_SIZE = 40;
export const SCALE_FACTOR = 50;
-export const BACKGROUND_COLOR = 0x111111;
+export const BACKGROUND_COLOR = 0x000;
export const PATH_COLOR = 0xff4d4d;
diff --git a/src/pages/Route/route-preview/webgl-prototype/WebGLRouteMapPrototype.tsx b/src/pages/Route/route-preview/webgl-prototype/WebGLRouteMapPrototype.tsx
index 8db9dcf..b9b2c32 100644
--- a/src/pages/Route/route-preview/webgl-prototype/WebGLRouteMapPrototype.tsx
+++ b/src/pages/Route/route-preview/webgl-prototype/WebGLRouteMapPrototype.tsx
@@ -2,8 +2,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { flushSync } from "react-dom";
import type { PointerEvent as ReactPointerEvent, CSSProperties } from "react";
import { observer } from "mobx-react-lite";
-
import { useMapData } from "../MapDataContext";
+import { AlignLeft, AlignCenter, AlignRight } from "lucide-react";
import { useTransform } from "../TransformContext";
import { coordinatesToLocal, localToCoordinates } from "../utils";
import { BACKGROUND_COLOR, SCALE_FACTOR, UP_SCALE } from "../Constants";
@@ -12,6 +12,16 @@ import { SightData } from "../types";
const SIGHT_ICON_URL = "/sight_icon.svg";
+const buttons = [
+ { label: , value: 1, align: "left" },
+ {
+ label: ,
+ value: 2,
+ align: "center",
+ },
+ { label: , value: 3, align: "right" },
+];
+
type Vec2 = { x: number; y: number };
type Transform = {
@@ -412,6 +422,7 @@ export const WebGLRouteMapPrototype = observer(() => {
y: centerY - worldCenterY * clampedScale,
},
};
+
lastTransformRef.current = adjusted;
return adjusted;
}, []);
@@ -534,9 +545,12 @@ export const WebGLRouteMapPrototype = observer(() => {
return;
}
+ const roundedLat = Math.round(latitude * 1e6) / 1e6;
+ const roundedLon = Math.round(longitude * 1e6) / 1e6;
+
lastCenterRef.current = {
- latitude: Math.round(latitude * 1e6) / 1e6,
- longitude: Math.round(longitude * 1e6) / 1e6,
+ latitude: roundedLat,
+ longitude: roundedLon,
};
},
[rotationAngle]
@@ -634,8 +648,28 @@ export const WebGLRouteMapPrototype = observer(() => {
}, [cancelScheduledCenterCommit]);
const updateTransform = useCallback(
- (next: Transform, options?: { immediate?: boolean }) => {
- const adjusted = clampTransformScale(next);
+ (
+ next: Transform,
+ options?: { immediate?: boolean; skipClamp?: boolean }
+ ) => {
+ console.log("🔄 updateTransform вызван", {
+ inputTransform: next,
+ options,
+ stackTrace: new Error().stack?.split("\n").slice(1, 4).join("\n"),
+ });
+
+ const adjusted = options?.skipClamp ? next : clampTransformScale(next);
+
+ if (adjusted !== next && !options?.skipClamp) {
+ console.log(
+ "🔄 updateTransform: transform изменен после clampTransformScale",
+ {
+ before: next,
+ after: adjusted,
+ }
+ );
+ }
+
transformRef.current = adjusted;
if (options?.immediate) {
flushSync(() => {
@@ -1122,6 +1156,10 @@ export const WebGLRouteMapPrototype = observer(() => {
let transform = transformRef.current;
if (!transform || !Number.isFinite(transform.scale)) {
+ if (canvasSize.width === 0 || canvasSize.height === 0) {
+ return;
+ }
+
transform = computeViewTransform(
fallbackVertices,
canvas.width,
@@ -1166,9 +1204,25 @@ export const WebGLRouteMapPrototype = observer(() => {
latitude: centerLat as number,
longitude: centerLon as number,
};
+ const clamped = clampTransformScale(transform);
+ if (clamped.scale !== transform.scale) {
+ const clampedScale = clamped.scale;
+ transform = {
+ scale: clampedScale,
+ translation: {
+ x: canvas.width / 2 - rotatedX * clampedScale,
+ y: canvas.height / 2 - rotatedY * clampedScale,
+ },
+ };
+
+ updateTransform(transform, { skipClamp: true });
+ } else {
+ updateTransform(clamped, { skipClamp: true });
+ }
+ } else {
+ transform = clampTransformScale(transform);
+ updateTransform(transform);
}
- transform = clampTransformScale(transform);
- updateTransform(transform);
} else {
const clamped = clampTransformScale(transform);
if (clamped !== transform) {
@@ -1886,6 +1940,14 @@ export const WebGLRouteMapPrototype = observer(() => {
? { right: 0, transform: "none" }
: { left: "50%", transform: "translateX(-50%)" };
+ const secondaryLineHeight = 1.2;
+ const secondaryHeight = showSecondary
+ ? secondaryFontSize * secondaryLineHeight
+ : 0;
+ const menuPaddingTop = showSecondary
+ ? Math.max(0, secondaryHeight - secondaryMarginTop) + 3
+ : 3;
+
return (
{
marginTop: -1 * secondaryMarginTop,
fontWeight: 400,
fontSize: secondaryFontSize,
+ lineHeight: secondaryLineHeight,
color: "#CBCBCB",
textShadow: "0 0 3px rgba(0,0,0,0.4)",
whiteSpace: "nowrap",
@@ -1972,6 +2035,65 @@ export const WebGLRouteMapPrototype = observer(() => {
+ {hoveredStationId === station.id && (
+ e.stopPropagation()}
+ >
+
+ {buttons.map((btn) => (
+
{
+ e.stopPropagation();
+ setStationAlignments((prev) => {
+ const next = new Map(prev);
+ next.set(
+ station.id,
+ btn.align as StationAlignment
+ );
+ return next;
+ });
+ setStationAlign(station.id, btn.value);
+ }}
+ style={{
+ padding: "4px 8px",
+ fontSize: 12,
+ cursor: "pointer",
+ backgroundColor:
+ alignment === btn.align
+ ? "#e0e0e0"
+ : "transparent",
+ borderRadius: 4,
+ color: "black",
+ fontWeight: 500,
+ userSelect: "none",
+ }}
+ >
+ {btn.label}
+
+ ))}
+
+
+ )}
);
})}