iobroker.proscenic 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +93 -0
- package/admin/i18n/de.json +9 -0
- package/admin/i18n/en.json +9 -0
- package/admin/i18n/es.json +9 -0
- package/admin/i18n/fr.json +9 -0
- package/admin/i18n/it.json +9 -0
- package/admin/i18n/nl.json +9 -0
- package/admin/i18n/pl.json +9 -0
- package/admin/i18n/pt.json +9 -0
- package/admin/i18n/ru.json +9 -0
- package/admin/i18n/uk.json +9 -0
- package/admin/i18n/zh-cn.json +9 -0
- package/admin/jsonConfig.json +85 -0
- package/admin/proscenic-m7-pro.png +0 -0
- package/admin/proscenic.png +0 -0
- package/build/domain/commands.js +129 -0
- package/build/domain/commands.js.map +7 -0
- package/build/domain/consumables.js +63 -0
- package/build/domain/consumables.js.map +7 -0
- package/build/domain/live-map.js +829 -0
- package/build/domain/live-map.js.map +7 -0
- package/build/domain/maintenance-message.js +128 -0
- package/build/domain/maintenance-message.js.map +7 -0
- package/build/domain/map.js +58 -0
- package/build/domain/map.js.map +7 -0
- package/build/domain/reconnect-policy.js +45 -0
- package/build/domain/reconnect-policy.js.map +7 -0
- package/build/domain/status.js +108 -0
- package/build/domain/status.js.map +7 -0
- package/build/main.js +529 -0
- package/build/main.js.map +7 -0
- package/build/objects/object-definitions.js +214 -0
- package/build/objects/object-definitions.js.map +7 -0
- package/build/objects/projector.js +231 -0
- package/build/objects/projector.js.map +7 -0
- package/build/protocol/crypto.js +81 -0
- package/build/protocol/crypto.js.map +7 -0
- package/build/protocol/gateway-client.js +136 -0
- package/build/protocol/gateway-client.js.map +7 -0
- package/build/protocol/rest-client.js +184 -0
- package/build/protocol/rest-client.js.map +7 -0
- package/build/protocol/types.js +17 -0
- package/build/protocol/types.js.map +7 -0
- package/io-package.json +125 -0
- package/package.json +80 -0
|
@@ -0,0 +1,829 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var live_map_exports = {};
|
|
20
|
+
__export(live_map_exports, {
|
|
21
|
+
countLiveMapAreas20002: () => countLiveMapAreas20002,
|
|
22
|
+
extractLiveMapCoordinateMetadata20002: () => extractLiveMapCoordinateMetadata20002,
|
|
23
|
+
extractRobotPose20001: () => extractRobotPose20001,
|
|
24
|
+
mergeLiveMapCoordinateMetadata20002: () => mergeLiveMapCoordinateMetadata20002,
|
|
25
|
+
mergeLiveMapCoordinateMetadataCache: () => mergeLiveMapCoordinateMetadataCache,
|
|
26
|
+
renderLiveMapImage20002: () => renderLiveMapImage20002
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(live_map_exports);
|
|
29
|
+
var import_node_zlib = require("node:zlib");
|
|
30
|
+
const PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
31
|
+
const CRC_TABLE = createCrcTable();
|
|
32
|
+
const MAX_LIVE_MAP_PIXELS = 512 * 512;
|
|
33
|
+
const MAX_LIVE_MAP_DATA_URL_BYTES = 256 * 1024;
|
|
34
|
+
const COLOR_UNKNOWN = [184, 204, 216];
|
|
35
|
+
const COLOR_ROOM = [255, 255, 255];
|
|
36
|
+
const COLOR_WALL = [56, 130, 188];
|
|
37
|
+
const COLOR_OBSTACLE = [82, 82, 82];
|
|
38
|
+
const COLOR_FORBIDDEN_AREA = [209, 106, 133];
|
|
39
|
+
const COLOR_FORBIDDEN_OUTLINE = [175, 68, 103];
|
|
40
|
+
const FORBIDDEN_AREA_ALPHA = 0.45;
|
|
41
|
+
const COLOR_ROOM_AREA = [112, 125, 236];
|
|
42
|
+
const COLOR_ROOM_AREA_OUTLINE = [68, 84, 210];
|
|
43
|
+
const ROOM_AREA_ALPHA = 0.3;
|
|
44
|
+
const COLOR_DOCK = [92, 92, 92];
|
|
45
|
+
const COLOR_ROBOT = [39, 139, 61];
|
|
46
|
+
const COLOR_PATH = [126, 216, 96];
|
|
47
|
+
const COLOR_HEADING = [20, 20, 20];
|
|
48
|
+
const MIN_PATH_SEGMENT_DISTANCE_SQUARED = 1.5 ** 2;
|
|
49
|
+
const MAX_PATH_SEGMENT_DISTANCE_SQUARED = 90 ** 2;
|
|
50
|
+
const ROUTED_PATH_PADDING_PIXELS = 24;
|
|
51
|
+
const MAX_ROUTED_PATH_CELLS = 2e4;
|
|
52
|
+
const PATH_LINE_RADIUS = 1;
|
|
53
|
+
function extractRobotPose20001(data) {
|
|
54
|
+
const record = getRecord(data);
|
|
55
|
+
const pos = parsePoint(record == null ? void 0 : record.pos);
|
|
56
|
+
if (!pos) {
|
|
57
|
+
return void 0;
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
pos,
|
|
61
|
+
phi: typeof (record == null ? void 0 : record.phi) === "number" ? record.phi : void 0
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function renderLiveMapImage20002(data, poses = []) {
|
|
65
|
+
const sample = parseMapSample(data);
|
|
66
|
+
if (!sample) {
|
|
67
|
+
return void 0;
|
|
68
|
+
}
|
|
69
|
+
const compressed = decodeMapPayload(sample.encoded);
|
|
70
|
+
const occupancy = decompressLz4Block(compressed, sample.width * sample.height);
|
|
71
|
+
if (occupancy.length !== sample.width * sample.height) {
|
|
72
|
+
return void 0;
|
|
73
|
+
}
|
|
74
|
+
const pixels = renderOccupancy(sample.width, sample.height, occupancy);
|
|
75
|
+
const coordinateMetadata = drawCoordinateMetadata(sample, pixels);
|
|
76
|
+
const runtime = drawRobotRuntime(sample, occupancy, pixels, poses);
|
|
77
|
+
const png = encodeRgbPng(sample.width, sample.height, pixels);
|
|
78
|
+
const dataUrl = `data:image/png;base64,${png.toString("base64")}`;
|
|
79
|
+
if (Buffer.byteLength(dataUrl, "utf8") > MAX_LIVE_MAP_DATA_URL_BYTES) {
|
|
80
|
+
return void 0;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
dataUrl,
|
|
84
|
+
width: sample.width,
|
|
85
|
+
height: sample.height,
|
|
86
|
+
areas: summarizeRenderedAreas(sample),
|
|
87
|
+
poseCount: runtime.projectedPoseCount,
|
|
88
|
+
rawPoseCount: poses.length,
|
|
89
|
+
pathLineSegments: runtime.pathLineSegments,
|
|
90
|
+
skippedPathSegments: runtime.skippedPathSegments,
|
|
91
|
+
renderedForbiddenAreaCount: coordinateMetadata.renderedForbiddenAreaCount,
|
|
92
|
+
renderedRoomAreaCount: coordinateMetadata.renderedRoomAreaCount,
|
|
93
|
+
orientation: "flip-y",
|
|
94
|
+
decompressedBytes: occupancy.length
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function extractLiveMapCoordinateMetadata20002(data) {
|
|
98
|
+
const record = getRecord(data);
|
|
99
|
+
if (!record) {
|
|
100
|
+
return void 0;
|
|
101
|
+
}
|
|
102
|
+
const metadata = {};
|
|
103
|
+
if (typeof record.mapId === "number") {
|
|
104
|
+
metadata.mapId = record.mapId;
|
|
105
|
+
}
|
|
106
|
+
const areas = normalizeMapAreaMetadata(record.area);
|
|
107
|
+
if (areas.length > 0) {
|
|
108
|
+
metadata.area = areas;
|
|
109
|
+
}
|
|
110
|
+
const chargeHandlePos = parsePoint(record.chargeHandlePos);
|
|
111
|
+
if (chargeHandlePos) {
|
|
112
|
+
metadata.chargeHandlePos = chargeHandlePos;
|
|
113
|
+
}
|
|
114
|
+
return metadata.mapId !== void 0 || metadata.area !== void 0 || metadata.chargeHandlePos !== void 0 ? metadata : void 0;
|
|
115
|
+
}
|
|
116
|
+
function mergeLiveMapCoordinateMetadata20002(data, metadata) {
|
|
117
|
+
const record = getRecord(data);
|
|
118
|
+
if (!record || !metadata) {
|
|
119
|
+
return data;
|
|
120
|
+
}
|
|
121
|
+
if (metadata.mapId !== void 0 && typeof record.mapId === "number" && metadata.mapId !== record.mapId) {
|
|
122
|
+
return data;
|
|
123
|
+
}
|
|
124
|
+
const merged = { ...record };
|
|
125
|
+
if (metadata.area && metadata.area.length > 0) {
|
|
126
|
+
merged.area = metadata.area.map((area) => ({
|
|
127
|
+
...area.source,
|
|
128
|
+
__proscenicKind: area.kind
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
if (metadata.chargeHandlePos && !parsePoint(record.chargeHandlePos)) {
|
|
132
|
+
merged.chargeHandlePos = metadata.chargeHandlePos;
|
|
133
|
+
}
|
|
134
|
+
return merged;
|
|
135
|
+
}
|
|
136
|
+
function countLiveMapAreas20002(data) {
|
|
137
|
+
const record = getRecord(data);
|
|
138
|
+
return Array.isArray(record == null ? void 0 : record.area) ? record.area.length : 0;
|
|
139
|
+
}
|
|
140
|
+
function mergeLiveMapCoordinateMetadataCache(previous, next) {
|
|
141
|
+
var _a, _b, _c, _d;
|
|
142
|
+
const sameMap = (previous == null ? void 0 : previous.mapId) === void 0 || next.mapId === void 0 || previous.mapId === next.mapId;
|
|
143
|
+
const previousAreas = sameMap ? (_a = previous == null ? void 0 : previous.area) != null ? _a : [] : [];
|
|
144
|
+
return {
|
|
145
|
+
mapId: (_b = next.mapId) != null ? _b : previous == null ? void 0 : previous.mapId,
|
|
146
|
+
area: mergeAreaMetadata(previousAreas, (_c = next.area) != null ? _c : []),
|
|
147
|
+
chargeHandlePos: (_d = next.chargeHandlePos) != null ? _d : sameMap ? previous == null ? void 0 : previous.chargeHandlePos : void 0
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function parseMapSample(data) {
|
|
151
|
+
const record = getRecord(data);
|
|
152
|
+
if (!record) {
|
|
153
|
+
return void 0;
|
|
154
|
+
}
|
|
155
|
+
const encoded = record.map;
|
|
156
|
+
const width = record.width;
|
|
157
|
+
const height = record.height;
|
|
158
|
+
if (typeof encoded !== "string" || typeof width !== "number" || typeof height !== "number") {
|
|
159
|
+
return void 0;
|
|
160
|
+
}
|
|
161
|
+
if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0) {
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
if (width * height > MAX_LIVE_MAP_PIXELS) {
|
|
165
|
+
return void 0;
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
encoded,
|
|
169
|
+
width,
|
|
170
|
+
height,
|
|
171
|
+
resolution: typeof record.resolution === "number" ? record.resolution : void 0,
|
|
172
|
+
xMin: typeof record.x_min === "number" ? record.x_min : void 0,
|
|
173
|
+
yMin: typeof record.y_min === "number" ? record.y_min : void 0,
|
|
174
|
+
areas: parseMapAreas(record.area),
|
|
175
|
+
chargeHandlePos: parsePoint(record.chargeHandlePos)
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function renderOccupancy(width, height, occupancy) {
|
|
179
|
+
const pixels = Buffer.alloc(width * height * 3);
|
|
180
|
+
for (let index = 0; index < occupancy.length; index++) {
|
|
181
|
+
const sourceX = index % width;
|
|
182
|
+
const sourceY = Math.floor(index / width);
|
|
183
|
+
const targetY = height - 1 - sourceY;
|
|
184
|
+
const value = occupancy[index];
|
|
185
|
+
if (value === 0) {
|
|
186
|
+
setPixel(pixels, width, height, sourceX, targetY, COLOR_WALL);
|
|
187
|
+
} else if (value === 127) {
|
|
188
|
+
setPixel(pixels, width, height, sourceX, targetY, COLOR_ROOM);
|
|
189
|
+
} else if (value === 255) {
|
|
190
|
+
setPixel(pixels, width, height, sourceX, targetY, COLOR_UNKNOWN);
|
|
191
|
+
} else {
|
|
192
|
+
setPixel(pixels, width, height, sourceX, targetY, value < 127 ? COLOR_OBSTACLE : COLOR_ROOM);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return pixels;
|
|
196
|
+
}
|
|
197
|
+
function drawCoordinateMetadata(sample, pixels) {
|
|
198
|
+
let renderedForbiddenAreaCount = 0;
|
|
199
|
+
let renderedRoomAreaCount = 0;
|
|
200
|
+
for (const area of sample.areas) {
|
|
201
|
+
const projected = area.vertices.map((vertex) => projectRobotCoordinate(sample, vertex)).filter((vertex) => vertex !== void 0);
|
|
202
|
+
if (projected.length >= 3) {
|
|
203
|
+
if (area.kind === "room") {
|
|
204
|
+
fillPolygon(pixels, sample.width, sample.height, projected, COLOR_ROOM_AREA, ROOM_AREA_ALPHA);
|
|
205
|
+
drawPolygon(pixels, sample.width, sample.height, projected, COLOR_ROOM_AREA_OUTLINE);
|
|
206
|
+
renderedRoomAreaCount += 1;
|
|
207
|
+
} else {
|
|
208
|
+
fillPolygon(pixels, sample.width, sample.height, projected, COLOR_FORBIDDEN_AREA, FORBIDDEN_AREA_ALPHA);
|
|
209
|
+
drawPolygon(pixels, sample.width, sample.height, projected, COLOR_FORBIDDEN_OUTLINE);
|
|
210
|
+
renderedForbiddenAreaCount += 1;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (sample.chargeHandlePos) {
|
|
215
|
+
const charge = projectRobotCoordinate(sample, sample.chargeHandlePos);
|
|
216
|
+
if (charge) {
|
|
217
|
+
plotMarker(pixels, sample.width, sample.height, charge[0], charge[1], 5, COLOR_DOCK);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return { renderedForbiddenAreaCount, renderedRoomAreaCount };
|
|
221
|
+
}
|
|
222
|
+
function drawRobotRuntime(sample, occupancy, pixels, poses) {
|
|
223
|
+
const projected = poses.map((pose) => ({
|
|
224
|
+
point: projectRobotCoordinate(sample, pose.pos),
|
|
225
|
+
phi: pose.phi
|
|
226
|
+
})).filter((pose) => pose.point !== void 0);
|
|
227
|
+
let pathLineSegments = 0;
|
|
228
|
+
let skippedPathSegments = 0;
|
|
229
|
+
for (let index = 1; index < projected.length; index++) {
|
|
230
|
+
const previous = projected[index - 1].point;
|
|
231
|
+
const current = projected[index].point;
|
|
232
|
+
const segmentDistanceSquared = distanceSquared(previous, current);
|
|
233
|
+
if (segmentDistanceSquared >= MIN_PATH_SEGMENT_DISTANCE_SQUARED && segmentDistanceSquared <= MAX_PATH_SEGMENT_DISTANCE_SQUARED) {
|
|
234
|
+
const routedPath = findDrawablePath(sample, occupancy, previous, current);
|
|
235
|
+
if (routedPath) {
|
|
236
|
+
drawAdaptivePathPolyline(sample, occupancy, pixels, sample.width, sample.height, routedPath);
|
|
237
|
+
pathLineSegments += 1;
|
|
238
|
+
} else {
|
|
239
|
+
skippedPathSegments += 1;
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
skippedPathSegments += 1;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const latest = projected.at(-1);
|
|
246
|
+
if (!latest) {
|
|
247
|
+
return {
|
|
248
|
+
projectedPoseCount: 0,
|
|
249
|
+
pathLineSegments,
|
|
250
|
+
skippedPathSegments
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
plotMarker(pixels, sample.width, sample.height, latest.point[0], latest.point[1], 5, COLOR_ROBOT);
|
|
254
|
+
if (latest.phi !== void 0) {
|
|
255
|
+
const angle = latest.phi / 1e3;
|
|
256
|
+
const headingLength = 10;
|
|
257
|
+
const endX = Math.round(latest.point[0] + Math.cos(angle) * headingLength);
|
|
258
|
+
const endY = Math.round(latest.point[1] - Math.sin(angle) * headingLength);
|
|
259
|
+
drawLine(pixels, sample.width, sample.height, latest.point[0], latest.point[1], endX, endY, COLOR_HEADING);
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
projectedPoseCount: projected.length,
|
|
263
|
+
pathLineSegments,
|
|
264
|
+
skippedPathSegments
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function drawAdaptivePathLine(pixels, width, height, x1, y1, x2, y2, canDraw) {
|
|
268
|
+
drawLineWithPixelColor(
|
|
269
|
+
pixels,
|
|
270
|
+
width,
|
|
271
|
+
height,
|
|
272
|
+
x1,
|
|
273
|
+
y1,
|
|
274
|
+
x2,
|
|
275
|
+
y2,
|
|
276
|
+
(x, y) => canDraw && !canDraw(x, y) ? void 0 : COLOR_PATH
|
|
277
|
+
);
|
|
278
|
+
drawLineNeighbors(pixels, width, height, x1, y1, x2, y2, canDraw);
|
|
279
|
+
}
|
|
280
|
+
function drawAdaptivePathPolyline(sample, occupancy, pixels, width, height, points) {
|
|
281
|
+
const canDraw = (x, y) => isTraversableMapPixel(sample, occupancy, x, y);
|
|
282
|
+
for (let index = 1; index < points.length; index++) {
|
|
283
|
+
const [x1, y1] = points[index - 1];
|
|
284
|
+
const [x2, y2] = points[index];
|
|
285
|
+
drawAdaptivePathLine(pixels, width, height, x1, y1, x2, y2, canDraw);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function findDrawablePath(sample, occupancy, start, end) {
|
|
289
|
+
if (isDirectPathTraversable(sample, occupancy, start, end)) {
|
|
290
|
+
return [start, end];
|
|
291
|
+
}
|
|
292
|
+
return findRoutedPath(sample, occupancy, start, end);
|
|
293
|
+
}
|
|
294
|
+
function isDirectPathTraversable(sample, occupancy, start, end) {
|
|
295
|
+
let traversedCells = 0;
|
|
296
|
+
let blockedCells = 0;
|
|
297
|
+
forEachLinePoint(start[0], start[1], end[0], end[1], (x, y) => {
|
|
298
|
+
traversedCells += 1;
|
|
299
|
+
if (!isTraversableMapPixel(sample, occupancy, x, y)) {
|
|
300
|
+
blockedCells += 1;
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
return traversedCells > 0 && blockedCells === 0;
|
|
304
|
+
}
|
|
305
|
+
function findRoutedPath(sample, occupancy, start, end) {
|
|
306
|
+
var _a, _b;
|
|
307
|
+
const routeStart = nearestTraversablePoint(sample, occupancy, start);
|
|
308
|
+
const routeEnd = nearestTraversablePoint(sample, occupancy, end);
|
|
309
|
+
if (!routeStart || !routeEnd) {
|
|
310
|
+
return void 0;
|
|
311
|
+
}
|
|
312
|
+
const minX = Math.max(0, Math.min(routeStart[0], routeEnd[0]) - ROUTED_PATH_PADDING_PIXELS);
|
|
313
|
+
const maxX = Math.min(sample.width - 1, Math.max(routeStart[0], routeEnd[0]) + ROUTED_PATH_PADDING_PIXELS);
|
|
314
|
+
const minY = Math.max(0, Math.min(routeStart[1], routeEnd[1]) - ROUTED_PATH_PADDING_PIXELS);
|
|
315
|
+
const maxY = Math.min(sample.height - 1, Math.max(routeStart[1], routeEnd[1]) + ROUTED_PATH_PADDING_PIXELS);
|
|
316
|
+
const searchWidth = maxX - minX + 1;
|
|
317
|
+
const searchHeight = maxY - minY + 1;
|
|
318
|
+
if (searchWidth * searchHeight > MAX_ROUTED_PATH_CELLS) {
|
|
319
|
+
return void 0;
|
|
320
|
+
}
|
|
321
|
+
const distance = /* @__PURE__ */ new Map();
|
|
322
|
+
const previous = /* @__PURE__ */ new Map();
|
|
323
|
+
const queue = [];
|
|
324
|
+
const startKey = localPointKey(routeStart[0], routeStart[1], minX, minY, searchWidth);
|
|
325
|
+
const endKey = localPointKey(routeEnd[0], routeEnd[1], minX, minY, searchWidth);
|
|
326
|
+
distance.set(startKey, 0);
|
|
327
|
+
queue.push({ key: startKey, score: heuristic(routeStart, routeEnd) });
|
|
328
|
+
while (queue.length > 0) {
|
|
329
|
+
queue.sort((left, right) => left.score - right.score);
|
|
330
|
+
const current = queue.shift();
|
|
331
|
+
if (!current) {
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
if (current.key === endKey) {
|
|
335
|
+
return restoreRoutedPath(previous, current.key, minX, minY, searchWidth);
|
|
336
|
+
}
|
|
337
|
+
const [currentX, currentY] = pointFromLocalKey(current.key, minX, minY, searchWidth);
|
|
338
|
+
const currentDistance = (_a = distance.get(current.key)) != null ? _a : Number.POSITIVE_INFINITY;
|
|
339
|
+
for (let dy = -1; dy <= 1; dy++) {
|
|
340
|
+
for (let dx = -1; dx <= 1; dx++) {
|
|
341
|
+
if (dx === 0 && dy === 0) {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
const nextX = currentX + dx;
|
|
345
|
+
const nextY = currentY + dy;
|
|
346
|
+
if (nextX < minX || nextX > maxX || nextY < minY || nextY > maxY || !isTraversableMapPixel(sample, occupancy, nextX, nextY)) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const nextKey = localPointKey(nextX, nextY, minX, minY, searchWidth);
|
|
350
|
+
const nextDistance = currentDistance + (dx !== 0 && dy !== 0 ? Math.SQRT2 : 1);
|
|
351
|
+
if (nextDistance >= ((_b = distance.get(nextKey)) != null ? _b : Number.POSITIVE_INFINITY)) {
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
distance.set(nextKey, nextDistance);
|
|
355
|
+
previous.set(nextKey, current.key);
|
|
356
|
+
queue.push({ key: nextKey, score: nextDistance + heuristic([nextX, nextY], routeEnd) });
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return void 0;
|
|
361
|
+
}
|
|
362
|
+
function restoreRoutedPath(previous, endKey, minX, minY, width) {
|
|
363
|
+
var _a;
|
|
364
|
+
const keys = [endKey];
|
|
365
|
+
let current = endKey;
|
|
366
|
+
while (previous.has(current)) {
|
|
367
|
+
current = (_a = previous.get(current)) != null ? _a : current;
|
|
368
|
+
keys.push(current);
|
|
369
|
+
}
|
|
370
|
+
return simplifyPath(keys.reverse().map((key) => pointFromLocalKey(key, minX, minY, width)));
|
|
371
|
+
}
|
|
372
|
+
function simplifyPath(points) {
|
|
373
|
+
var _a;
|
|
374
|
+
if (points.length <= 2) {
|
|
375
|
+
return points;
|
|
376
|
+
}
|
|
377
|
+
const simplified = [points[0]];
|
|
378
|
+
let previousDirection;
|
|
379
|
+
for (let index = 1; index < points.length; index++) {
|
|
380
|
+
const direction = [
|
|
381
|
+
Math.sign(points[index][0] - points[index - 1][0]),
|
|
382
|
+
Math.sign(points[index][1] - points[index - 1][1])
|
|
383
|
+
];
|
|
384
|
+
if (previousDirection && (direction[0] !== previousDirection[0] || direction[1] !== previousDirection[1])) {
|
|
385
|
+
simplified.push(points[index - 1]);
|
|
386
|
+
}
|
|
387
|
+
previousDirection = direction;
|
|
388
|
+
}
|
|
389
|
+
simplified.push((_a = points.at(-1)) != null ? _a : points[0]);
|
|
390
|
+
return simplified;
|
|
391
|
+
}
|
|
392
|
+
function nearestTraversablePoint(sample, occupancy, point) {
|
|
393
|
+
if (isTraversableMapPixel(sample, occupancy, point[0], point[1])) {
|
|
394
|
+
return point;
|
|
395
|
+
}
|
|
396
|
+
for (let radius = 1; radius <= 3; radius++) {
|
|
397
|
+
for (let y = point[1] - radius; y <= point[1] + radius; y++) {
|
|
398
|
+
for (let x = point[0] - radius; x <= point[0] + radius; x++) {
|
|
399
|
+
if (isTraversableMapPixel(sample, occupancy, x, y)) {
|
|
400
|
+
return [x, y];
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return void 0;
|
|
406
|
+
}
|
|
407
|
+
function isTraversableMapPixel(sample, occupancy, x, y) {
|
|
408
|
+
if (x < 0 || x >= sample.width || y < 0 || y >= sample.height) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
const sourceY = sample.height - 1 - y;
|
|
412
|
+
const value = occupancy[sourceY * sample.width + x];
|
|
413
|
+
return value >= 127;
|
|
414
|
+
}
|
|
415
|
+
function localPointKey(x, y, minX, minY, width) {
|
|
416
|
+
return (y - minY) * width + (x - minX);
|
|
417
|
+
}
|
|
418
|
+
function pointFromLocalKey(key, minX, minY, width) {
|
|
419
|
+
return [minX + key % width, minY + Math.floor(key / width)];
|
|
420
|
+
}
|
|
421
|
+
function heuristic(left, right) {
|
|
422
|
+
return Math.hypot(left[0] - right[0], left[1] - right[1]);
|
|
423
|
+
}
|
|
424
|
+
function drawLineNeighbors(pixels, width, height, x1, y1, x2, y2, canDraw) {
|
|
425
|
+
for (let dy = -PATH_LINE_RADIUS; dy <= PATH_LINE_RADIUS; dy++) {
|
|
426
|
+
for (let dx = -PATH_LINE_RADIUS; dx <= PATH_LINE_RADIUS; dx++) {
|
|
427
|
+
if (dx === 0 && dy === 0 || dx * dx + dy * dy > PATH_LINE_RADIUS * PATH_LINE_RADIUS) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
drawLineWithPixelColor(
|
|
431
|
+
pixels,
|
|
432
|
+
width,
|
|
433
|
+
height,
|
|
434
|
+
x1 + dx,
|
|
435
|
+
y1 + dy,
|
|
436
|
+
x2 + dx,
|
|
437
|
+
y2 + dy,
|
|
438
|
+
(x, y) => canDraw && !canDraw(x, y) ? void 0 : COLOR_PATH
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function projectRobotCoordinate(sample, point) {
|
|
444
|
+
if (sample.resolution === void 0 || sample.xMin === void 0 || sample.yMin === void 0) {
|
|
445
|
+
return void 0;
|
|
446
|
+
}
|
|
447
|
+
const xMeters = point[0] / 1e3;
|
|
448
|
+
const yMeters = point[1] / 1e3;
|
|
449
|
+
const x = Math.round((xMeters - sample.xMin) / sample.resolution);
|
|
450
|
+
const y = sample.height - 1 - Math.round((yMeters - sample.yMin) / sample.resolution);
|
|
451
|
+
if (x < 0 || x >= sample.width || y < 0 || y >= sample.height) {
|
|
452
|
+
return void 0;
|
|
453
|
+
}
|
|
454
|
+
return [x, y];
|
|
455
|
+
}
|
|
456
|
+
function parseMapAreas(value) {
|
|
457
|
+
return normalizeMapAreaMetadata(value).map((area) => ({
|
|
458
|
+
key: area.key,
|
|
459
|
+
vertices: parseVertices(area.source.vertexs),
|
|
460
|
+
kind: area.kind,
|
|
461
|
+
source: area.source
|
|
462
|
+
}));
|
|
463
|
+
}
|
|
464
|
+
function summarizeRenderedAreas(sample) {
|
|
465
|
+
return sample.areas.map((area) => {
|
|
466
|
+
const projected = area.vertices.map((point) => projectRobotCoordinate(sample, point)).filter((point) => point !== void 0);
|
|
467
|
+
const id = typeof area.source.id === "number" || typeof area.source.id === "string" ? area.source.id : void 0;
|
|
468
|
+
const label = firstUsefulLabel(area.source.tag, area.source.name);
|
|
469
|
+
return {
|
|
470
|
+
key: area.key,
|
|
471
|
+
kind: area.kind,
|
|
472
|
+
...id !== void 0 ? { id } : {},
|
|
473
|
+
...label !== void 0 ? { label } : {},
|
|
474
|
+
...projected.length > 0 ? { bounds: boundsFor(projected) } : {}
|
|
475
|
+
};
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
function firstUsefulLabel(...values) {
|
|
479
|
+
for (const value of values) {
|
|
480
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
481
|
+
return value;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return void 0;
|
|
485
|
+
}
|
|
486
|
+
function boundsFor(points) {
|
|
487
|
+
const xs = points.map(([x]) => x);
|
|
488
|
+
const ys = points.map(([, y]) => y);
|
|
489
|
+
return {
|
|
490
|
+
minX: Math.min(...xs),
|
|
491
|
+
minY: Math.min(...ys),
|
|
492
|
+
maxX: Math.max(...xs),
|
|
493
|
+
maxY: Math.max(...ys)
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
function normalizeMapAreaMetadata(value) {
|
|
497
|
+
var _a, _b, _c;
|
|
498
|
+
if (!Array.isArray(value)) {
|
|
499
|
+
return [];
|
|
500
|
+
}
|
|
501
|
+
const candidates = value.map((entry) => {
|
|
502
|
+
const record = getRecord(entry);
|
|
503
|
+
if (!record) {
|
|
504
|
+
return void 0;
|
|
505
|
+
}
|
|
506
|
+
const vertices = parseVertices(record.vertexs);
|
|
507
|
+
if (vertices.length < 3) {
|
|
508
|
+
return void 0;
|
|
509
|
+
}
|
|
510
|
+
const key = mapAreaKey(record, vertices);
|
|
511
|
+
const explicitKind = parseAreaKind(record.__proscenicKind);
|
|
512
|
+
return {
|
|
513
|
+
key,
|
|
514
|
+
explicitKind,
|
|
515
|
+
source: record
|
|
516
|
+
};
|
|
517
|
+
}).filter(
|
|
518
|
+
(entry) => entry !== void 0
|
|
519
|
+
);
|
|
520
|
+
const counts = /* @__PURE__ */ new Map();
|
|
521
|
+
for (const candidate of candidates) {
|
|
522
|
+
counts.set(candidate.key, ((_a = counts.get(candidate.key)) != null ? _a : 0) + 1);
|
|
523
|
+
}
|
|
524
|
+
const normalized = /* @__PURE__ */ new Map();
|
|
525
|
+
for (const candidate of candidates) {
|
|
526
|
+
const kind = (_c = candidate.explicitKind) != null ? _c : counts.get(candidate.key) !== void 0 && ((_b = counts.get(candidate.key)) != null ? _b : 0) > 1 ? "forbidden" : candidates.length === 1 ? "forbidden" : "room";
|
|
527
|
+
const existing = normalized.get(candidate.key);
|
|
528
|
+
const next = {
|
|
529
|
+
key: candidate.key,
|
|
530
|
+
kind,
|
|
531
|
+
source: withoutInternalAreaMetadata(candidate.source)
|
|
532
|
+
};
|
|
533
|
+
normalized.set(candidate.key, existing ? mergeAreaMetadataEntry(existing, next) : next);
|
|
534
|
+
}
|
|
535
|
+
return [...normalized.values()];
|
|
536
|
+
}
|
|
537
|
+
function mergeAreaMetadata(previous, next) {
|
|
538
|
+
const merged = /* @__PURE__ */ new Map();
|
|
539
|
+
for (const area of [...previous, ...next]) {
|
|
540
|
+
const existing = merged.get(area.key);
|
|
541
|
+
merged.set(area.key, existing ? mergeAreaMetadataEntry(existing, area) : area);
|
|
542
|
+
}
|
|
543
|
+
return [...merged.values()];
|
|
544
|
+
}
|
|
545
|
+
function mergeAreaMetadataEntry(left, right) {
|
|
546
|
+
return {
|
|
547
|
+
key: right.key,
|
|
548
|
+
kind: strongestAreaKind(left.kind, right.kind),
|
|
549
|
+
source: right.source
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
function strongestAreaKind(left, right) {
|
|
553
|
+
if (left === "forbidden" || right === "forbidden") {
|
|
554
|
+
return "forbidden";
|
|
555
|
+
}
|
|
556
|
+
if (left === "room" || right === "room") {
|
|
557
|
+
return "room";
|
|
558
|
+
}
|
|
559
|
+
return "unknown";
|
|
560
|
+
}
|
|
561
|
+
function parseAreaKind(value) {
|
|
562
|
+
return value === "forbidden" || value === "room" || value === "unknown" ? value : void 0;
|
|
563
|
+
}
|
|
564
|
+
function withoutInternalAreaMetadata(record) {
|
|
565
|
+
const clone = { ...record };
|
|
566
|
+
delete clone.__proscenicKind;
|
|
567
|
+
return clone;
|
|
568
|
+
}
|
|
569
|
+
function parseVertices(value) {
|
|
570
|
+
if (!Array.isArray(value)) {
|
|
571
|
+
return [];
|
|
572
|
+
}
|
|
573
|
+
return value.map((vertex) => parsePoint(vertex)).filter((vertex) => vertex !== void 0);
|
|
574
|
+
}
|
|
575
|
+
function mapAreaKey(record, vertices) {
|
|
576
|
+
const id = record.id;
|
|
577
|
+
if (typeof id === "number" || typeof id === "string") {
|
|
578
|
+
return `id:${String(id)}`;
|
|
579
|
+
}
|
|
580
|
+
const xs = vertices.map(([x]) => x);
|
|
581
|
+
const ys = vertices.map(([, y]) => y);
|
|
582
|
+
return `shape:${vertices.length}:${Math.min(...xs)}:${Math.min(...ys)}:${Math.max(...xs)}:${Math.max(...ys)}`;
|
|
583
|
+
}
|
|
584
|
+
function parsePoint(value) {
|
|
585
|
+
if (Array.isArray(value) && value.length >= 2 && typeof value[0] === "number" && typeof value[1] === "number") {
|
|
586
|
+
return [value[0], value[1]];
|
|
587
|
+
}
|
|
588
|
+
return void 0;
|
|
589
|
+
}
|
|
590
|
+
function getRecord(value) {
|
|
591
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
592
|
+
}
|
|
593
|
+
function decodeMapPayload(encodedMap) {
|
|
594
|
+
return Buffer.from(encodedMap.replace(/\s/gu, "+"), "base64");
|
|
595
|
+
}
|
|
596
|
+
function decompressLz4Block(input, maxOutputBytes) {
|
|
597
|
+
const output = Buffer.alloc(maxOutputBytes);
|
|
598
|
+
let inputOffset = 0;
|
|
599
|
+
let outputOffset = 0;
|
|
600
|
+
while (inputOffset < input.length) {
|
|
601
|
+
const token = input[inputOffset++];
|
|
602
|
+
let literalLength = token >> 4;
|
|
603
|
+
if (literalLength === 15) {
|
|
604
|
+
let next;
|
|
605
|
+
do {
|
|
606
|
+
if (inputOffset >= input.length) {
|
|
607
|
+
throw new Error("literal length overflow");
|
|
608
|
+
}
|
|
609
|
+
next = input[inputOffset++];
|
|
610
|
+
literalLength += next;
|
|
611
|
+
} while (next === 255);
|
|
612
|
+
}
|
|
613
|
+
if (inputOffset + literalLength > input.length || outputOffset + literalLength > output.length) {
|
|
614
|
+
throw new Error("literal overrun");
|
|
615
|
+
}
|
|
616
|
+
input.copy(output, outputOffset, inputOffset, inputOffset + literalLength);
|
|
617
|
+
inputOffset += literalLength;
|
|
618
|
+
outputOffset += literalLength;
|
|
619
|
+
if (inputOffset >= input.length) {
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
622
|
+
if (inputOffset + 2 > input.length) {
|
|
623
|
+
throw new Error("offset overrun");
|
|
624
|
+
}
|
|
625
|
+
const matchOffset = input[inputOffset] | input[inputOffset + 1] << 8;
|
|
626
|
+
inputOffset += 2;
|
|
627
|
+
if (matchOffset === 0 || matchOffset > outputOffset) {
|
|
628
|
+
throw new Error("bad match offset");
|
|
629
|
+
}
|
|
630
|
+
let matchLength = token & 15;
|
|
631
|
+
if (matchLength === 15) {
|
|
632
|
+
let next;
|
|
633
|
+
do {
|
|
634
|
+
if (inputOffset >= input.length) {
|
|
635
|
+
throw new Error("match length overflow");
|
|
636
|
+
}
|
|
637
|
+
next = input[inputOffset++];
|
|
638
|
+
matchLength += next;
|
|
639
|
+
} while (next === 255);
|
|
640
|
+
}
|
|
641
|
+
matchLength += 4;
|
|
642
|
+
if (outputOffset + matchLength > output.length) {
|
|
643
|
+
throw new Error("match overrun");
|
|
644
|
+
}
|
|
645
|
+
for (let index = 0; index < matchLength; index++) {
|
|
646
|
+
output[outputOffset + index] = output[outputOffset - matchOffset + index];
|
|
647
|
+
}
|
|
648
|
+
outputOffset += matchLength;
|
|
649
|
+
}
|
|
650
|
+
return output.subarray(0, outputOffset);
|
|
651
|
+
}
|
|
652
|
+
function fillPolygon(pixels, width, height, polygon, color, alpha = 1) {
|
|
653
|
+
const minY = Math.max(0, Math.min(...polygon.map(([, y]) => y)));
|
|
654
|
+
const maxY = Math.min(height - 1, Math.max(...polygon.map(([, y]) => y)));
|
|
655
|
+
for (let y = minY; y <= maxY; y++) {
|
|
656
|
+
const intersections = [];
|
|
657
|
+
for (let index = 0; index < polygon.length; index++) {
|
|
658
|
+
const [x1, y1] = polygon[index];
|
|
659
|
+
const [x2, y2] = polygon[(index + 1) % polygon.length];
|
|
660
|
+
if (y1 <= y && y2 > y || y2 <= y && y1 > y) {
|
|
661
|
+
const x = x1 + (y - y1) * (x2 - x1) / (y2 - y1);
|
|
662
|
+
intersections.push(Math.round(x));
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
intersections.sort((left, right) => left - right);
|
|
666
|
+
for (let index = 0; index + 1 < intersections.length; index += 2) {
|
|
667
|
+
const start = Math.max(0, intersections[index]);
|
|
668
|
+
const end = Math.min(width - 1, intersections[index + 1]);
|
|
669
|
+
for (let x = start; x <= end; x++) {
|
|
670
|
+
blendPixel(pixels, width, height, x, y, color, alpha);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
function drawPolygon(pixels, width, height, polygon, color) {
|
|
676
|
+
for (let index = 0; index < polygon.length; index++) {
|
|
677
|
+
const [x1, y1] = polygon[index];
|
|
678
|
+
const [x2, y2] = polygon[(index + 1) % polygon.length];
|
|
679
|
+
drawLine(pixels, width, height, x1, y1, x2, y2, color);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
function drawLine(pixels, width, height, x1, y1, x2, y2, color) {
|
|
683
|
+
drawLineWithPixelColor(pixels, width, height, x1, y1, x2, y2, () => color);
|
|
684
|
+
}
|
|
685
|
+
function forEachLinePoint(x1, y1, x2, y2, callback) {
|
|
686
|
+
const dx = Math.abs(x2 - x1);
|
|
687
|
+
const sx = x1 < x2 ? 1 : -1;
|
|
688
|
+
const dy = -Math.abs(y2 - y1);
|
|
689
|
+
const sy = y1 < y2 ? 1 : -1;
|
|
690
|
+
let error = dx + dy;
|
|
691
|
+
let x = x1;
|
|
692
|
+
let y = y1;
|
|
693
|
+
while (true) {
|
|
694
|
+
callback(x, y);
|
|
695
|
+
if (x === x2 && y === y2) {
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
698
|
+
const error2 = 2 * error;
|
|
699
|
+
if (error2 >= dy) {
|
|
700
|
+
error += dy;
|
|
701
|
+
x += sx;
|
|
702
|
+
}
|
|
703
|
+
if (error2 <= dx) {
|
|
704
|
+
error += dx;
|
|
705
|
+
y += sy;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function drawLineWithPixelColor(pixels, width, height, x1, y1, x2, y2, pickColor) {
|
|
710
|
+
const dx = Math.abs(x2 - x1);
|
|
711
|
+
const sx = x1 < x2 ? 1 : -1;
|
|
712
|
+
const dy = -Math.abs(y2 - y1);
|
|
713
|
+
const sy = y1 < y2 ? 1 : -1;
|
|
714
|
+
let error = dx + dy;
|
|
715
|
+
let x = x1;
|
|
716
|
+
let y = y1;
|
|
717
|
+
while (true) {
|
|
718
|
+
if (x >= 0 && x < width && y >= 0 && y < height) {
|
|
719
|
+
const color = pickColor(x, y);
|
|
720
|
+
if (color) {
|
|
721
|
+
setPixel(pixels, width, height, x, y, color);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (x === x2 && y === y2) {
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
const error2 = 2 * error;
|
|
728
|
+
if (error2 >= dy) {
|
|
729
|
+
error += dy;
|
|
730
|
+
x += sx;
|
|
731
|
+
}
|
|
732
|
+
if (error2 <= dx) {
|
|
733
|
+
error += dx;
|
|
734
|
+
y += sy;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function plotMarker(pixels, width, height, x, y, radius, color) {
|
|
739
|
+
for (let dy = -radius; dy <= radius; dy++) {
|
|
740
|
+
for (let dx = -radius; dx <= radius; dx++) {
|
|
741
|
+
const px = x + dx;
|
|
742
|
+
const py = y + dy;
|
|
743
|
+
if (px >= 0 && px < width && py >= 0 && py < height && dx * dx + dy * dy <= radius * radius) {
|
|
744
|
+
setPixel(pixels, width, height, px, py, color);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
function setPixel(pixels, width, height, x, y, color) {
|
|
750
|
+
if (x < 0 || x >= width || y < 0 || y >= height) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const offset = (y * width + x) * 3;
|
|
754
|
+
pixels[offset] = color[0];
|
|
755
|
+
pixels[offset + 1] = color[1];
|
|
756
|
+
pixels[offset + 2] = color[2];
|
|
757
|
+
}
|
|
758
|
+
function blendPixel(pixels, width, height, x, y, color, alpha) {
|
|
759
|
+
if (alpha >= 1) {
|
|
760
|
+
setPixel(pixels, width, height, x, y, color);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
if (x < 0 || x >= width || y < 0 || y >= height) {
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
const offset = (y * width + x) * 3;
|
|
767
|
+
pixels[offset] = Math.round(pixels[offset] * (1 - alpha) + color[0] * alpha);
|
|
768
|
+
pixels[offset + 1] = Math.round(pixels[offset + 1] * (1 - alpha) + color[1] * alpha);
|
|
769
|
+
pixels[offset + 2] = Math.round(pixels[offset + 2] * (1 - alpha) + color[2] * alpha);
|
|
770
|
+
}
|
|
771
|
+
function distanceSquared(left, right) {
|
|
772
|
+
return (left[0] - right[0]) ** 2 + (left[1] - right[1]) ** 2;
|
|
773
|
+
}
|
|
774
|
+
function encodeRgbPng(width, height, pixels) {
|
|
775
|
+
if (pixels.length !== width * height * 3) {
|
|
776
|
+
throw new Error(`Expected ${width * height * 3} RGB pixels but received ${pixels.length}`);
|
|
777
|
+
}
|
|
778
|
+
const header = Buffer.alloc(13);
|
|
779
|
+
header.writeUInt32BE(width, 0);
|
|
780
|
+
header.writeUInt32BE(height, 4);
|
|
781
|
+
header[8] = 8;
|
|
782
|
+
header[9] = 2;
|
|
783
|
+
const rowBytes = width * 3;
|
|
784
|
+
const scanlines = Buffer.alloc((rowBytes + 1) * height);
|
|
785
|
+
for (let y = 0; y < height; y++) {
|
|
786
|
+
scanlines[y * (rowBytes + 1)] = 0;
|
|
787
|
+
pixels.copy(scanlines, y * (rowBytes + 1) + 1, y * rowBytes, (y + 1) * rowBytes);
|
|
788
|
+
}
|
|
789
|
+
return Buffer.concat([
|
|
790
|
+
PNG_SIGNATURE,
|
|
791
|
+
pngChunk("IHDR", header),
|
|
792
|
+
pngChunk("IDAT", (0, import_node_zlib.deflateSync)(scanlines)),
|
|
793
|
+
pngChunk("IEND", Buffer.alloc(0))
|
|
794
|
+
]);
|
|
795
|
+
}
|
|
796
|
+
function pngChunk(type, data) {
|
|
797
|
+
const typeBuffer = Buffer.from(type, "ascii");
|
|
798
|
+
const length = Buffer.alloc(4);
|
|
799
|
+
length.writeUInt32BE(data.length, 0);
|
|
800
|
+
const checksum = Buffer.alloc(4);
|
|
801
|
+
checksum.writeUInt32BE(crc32(Buffer.concat([typeBuffer, data])), 0);
|
|
802
|
+
return Buffer.concat([length, typeBuffer, data, checksum]);
|
|
803
|
+
}
|
|
804
|
+
function crc32(data) {
|
|
805
|
+
let crc = 4294967295;
|
|
806
|
+
for (const byte of data) {
|
|
807
|
+
crc = CRC_TABLE[(crc ^ byte) & 255] ^ crc >>> 8;
|
|
808
|
+
}
|
|
809
|
+
return (crc ^ 4294967295) >>> 0;
|
|
810
|
+
}
|
|
811
|
+
function createCrcTable() {
|
|
812
|
+
return Array.from({ length: 256 }, (_, index) => {
|
|
813
|
+
let value = index;
|
|
814
|
+
for (let bit = 0; bit < 8; bit++) {
|
|
815
|
+
value = value & 1 ? 3988292384 ^ value >>> 1 : value >>> 1;
|
|
816
|
+
}
|
|
817
|
+
return value >>> 0;
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
821
|
+
0 && (module.exports = {
|
|
822
|
+
countLiveMapAreas20002,
|
|
823
|
+
extractLiveMapCoordinateMetadata20002,
|
|
824
|
+
extractRobotPose20001,
|
|
825
|
+
mergeLiveMapCoordinateMetadata20002,
|
|
826
|
+
mergeLiveMapCoordinateMetadataCache,
|
|
827
|
+
renderLiveMapImage20002
|
|
828
|
+
});
|
|
829
|
+
//# sourceMappingURL=live-map.js.map
|