This commit is contained in:
@@ -61,6 +61,10 @@
|
||||
yamlMeta: null,
|
||||
/** Pending ROS metadata from upload dialog (set before PNG picker). */
|
||||
uploadMeta: null,
|
||||
/** Dotted path overlay when opened from Setup → Paths (view). */
|
||||
pathPreview: null,
|
||||
/** Fingerprint of preferred zones when map was loaded (for save warning). */
|
||||
initialPreferredFp: "",
|
||||
};
|
||||
|
||||
const titleEl = el("mapEditorTitle");
|
||||
@@ -500,6 +504,46 @@
|
||||
return mapMetaForOriginDisplay() || state.map || {};
|
||||
}
|
||||
|
||||
function preferredZonesFingerprint(zones) {
|
||||
return zones
|
||||
.filter((z) => z && z.type === "preferred")
|
||||
.map((z) =>
|
||||
JSON.stringify({
|
||||
id: z.id,
|
||||
points: z.points,
|
||||
geometry: z.geometry,
|
||||
line_width_cm: z.line_width_cm,
|
||||
}),
|
||||
)
|
||||
.sort()
|
||||
.join("|");
|
||||
}
|
||||
|
||||
function renderPathPreview() {
|
||||
if (!objectsSvgEl || !state.pathPreview?.points?.length) return;
|
||||
const geo = Geo();
|
||||
const meta = mapMetaForEditor();
|
||||
const { width, height } = floorPlanSize();
|
||||
if (!geo || !width) return;
|
||||
const pts = state.pathPreview.points
|
||||
.map((p) => {
|
||||
const px = geo.worldToPixel(meta, width, height, p.x, p.y);
|
||||
return `${px.x},${px.y}`;
|
||||
})
|
||||
.join(" ");
|
||||
const poly = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
|
||||
poly.setAttribute("points", pts);
|
||||
poly.setAttribute("fill", "none");
|
||||
poly.setAttribute("stroke", "#2563eb");
|
||||
poly.setAttribute("stroke-width", "3");
|
||||
poly.setAttribute("stroke-dasharray", "8 6");
|
||||
poly.setAttribute("stroke-linecap", "round");
|
||||
poly.setAttribute("stroke-linejoin", "round");
|
||||
poly.classList.add("mapEditorPathPreview");
|
||||
poly.setAttribute("pointer-events", "none");
|
||||
objectsSvgEl.appendChild(poly);
|
||||
}
|
||||
|
||||
function renderObjects() {
|
||||
const obj = Objects();
|
||||
if (!objectsSvgEl || !obj) return;
|
||||
@@ -530,6 +574,7 @@
|
||||
draft: state.draft,
|
||||
selectionRect: state.selectionRect,
|
||||
});
|
||||
renderPathPreview();
|
||||
}
|
||||
|
||||
function imagePointFromEvent(evt) {
|
||||
@@ -1769,6 +1814,7 @@
|
||||
state.map = await api(`/api/maps/${encodeURIComponent(state.mapId)}`);
|
||||
await loadYamlMeta();
|
||||
syncZonesFromMap();
|
||||
state.initialPreferredFp = preferredZonesFingerprint(state.zones);
|
||||
updateHeader();
|
||||
renderMapImage();
|
||||
fillSettingsForm();
|
||||
@@ -1796,6 +1842,8 @@
|
||||
state.selectionRect = null;
|
||||
state.positionDrag = null;
|
||||
state.pendingPosition = null;
|
||||
state.pathPreview = callbacks.pathPreview || null;
|
||||
state.initialPreferredFp = "";
|
||||
updateObjectTypePickerUi();
|
||||
state.view = Geo()?.createView(1, 0, 0) || { scale: 1, panX: 0, panY: 0 };
|
||||
if (tipEl) {
|
||||
@@ -1829,6 +1877,8 @@
|
||||
state.selectionRect = null;
|
||||
state.positionDrag = null;
|
||||
state.pendingPosition = null;
|
||||
state.pathPreview = null;
|
||||
state.initialPreferredFp = "";
|
||||
drawLineDialogEl?.close();
|
||||
menuDialogEl?.close();
|
||||
settingsDialogEl?.close();
|
||||
@@ -2062,6 +2112,19 @@
|
||||
if (imageUpdated) state.map = { ...state.map, ...imageUpdated };
|
||||
state.rasterDirty = false;
|
||||
}
|
||||
const preferredChanged =
|
||||
preferredZonesFingerprint(state.zones) !== state.initialPreferredFp;
|
||||
if (preferredChanged && state.map?.id) {
|
||||
try {
|
||||
const pathsData = await api(
|
||||
`/api/paths?site_id=${encodeURIComponent(state.map.site_id || "")}`,
|
||||
);
|
||||
const onMap = (pathsData.paths || []).filter((p) => p.map_id === state.map.id);
|
||||
if (onMap.length && !confirm(t("maps.editor.preferredPathWarning"))) return;
|
||||
} catch {
|
||||
/* continue save */
|
||||
}
|
||||
}
|
||||
const updated = await api(`/api/maps/${encodeURIComponent(state.map.id)}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
@@ -2069,6 +2132,7 @@
|
||||
});
|
||||
state.map = updated;
|
||||
syncZonesFromMap();
|
||||
state.initialPreferredFp = preferredZonesFingerprint(state.zones);
|
||||
state.callbacks.onMapUpdated?.(updated);
|
||||
setDirty(false);
|
||||
updateHeader();
|
||||
|
||||
Reference in New Issue
Block a user