@blueharford/scrypted-spatial-awareness 0.6.4 → 0.6.5

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/dist/plugin.zip CHANGED
Binary file
@@ -36602,7 +36602,9 @@ class TopologyDiscoveryEngine {
36602
36602
  type: landmark.type,
36603
36603
  description: landmark.description,
36604
36604
  visibleFromCameras: [analysis.cameraId],
36605
- },
36605
+ // Include bounding box for positioning (will be used by applyDiscoverySuggestion)
36606
+ boundingBox: landmark.boundingBox,
36607
+ }, // boundingBox is extra metadata not in Landmark interface
36606
36608
  };
36607
36609
  this.suggestions.set(suggestion.id, suggestion);
36608
36610
  }
@@ -40305,24 +40307,46 @@ class SpatialAwarenessPlugin extends sdk_1.ScryptedDeviceBase {
40305
40307
  const direction = fov.direction || 0;
40306
40308
  const range = fov.range || 80;
40307
40309
  const fovAngle = fov.angle || 90;
40308
- // Count existing landmarks from this camera to spread them out
40309
- const cameraDeviceId = camera.deviceId;
40310
- const existingFromCamera = (topology.landmarks || []).filter(l => l.visibleFromCameras?.includes(cameraDeviceId) ||
40311
- l.visibleFromCameras?.includes(camera.name)).length;
40312
40310
  // Calculate position in front of camera within its FOV
40313
40311
  // Convert direction to radians (0 = up/north, 90 = right/east)
40314
40312
  const dirRad = (direction - 90) * Math.PI / 180;
40315
40313
  const halfFov = (fovAngle / 2) * Math.PI / 180;
40316
- // Spread landmarks across the FOV cone at varying distances
40317
- const angleOffset = (existingFromCamera % 3 - 1) * halfFov * 0.6; // -0.6, 0, +0.6 of half FOV
40318
- const distanceMultiplier = 0.5 + (existingFromCamera % 2) * 0.3; // 50% or 80% of range
40314
+ // Use bounding box if available to position landmark accurately within FOV
40315
+ // boundingBox format: [x, y, width, height] normalized 0-1
40316
+ const bbox = suggestion.landmark.boundingBox;
40317
+ let angleOffset;
40318
+ let distanceMultiplier;
40319
+ if (bbox && bbox.length >= 2) {
40320
+ // Use bounding box center to determine position in FOV
40321
+ const bboxCenterX = bbox[0] + (bbox[2] || 0) / 2; // 0 = left edge, 1 = right edge
40322
+ const bboxCenterY = bbox[1] + (bbox[3] || 0) / 2; // 0 = top (far), 1 = bottom (close)
40323
+ // Map X position to angle within FOV
40324
+ // bboxCenterX 0 → left side of FOV (-halfFov)
40325
+ // bboxCenterX 0.5 → center of FOV (0)
40326
+ // bboxCenterX 1 → right side of FOV (+halfFov)
40327
+ angleOffset = (bboxCenterX - 0.5) * 2 * halfFov;
40328
+ // Map Y position to distance from camera
40329
+ // bboxCenterY 0 (top of frame) → far from camera (90% of range)
40330
+ // bboxCenterY 1 (bottom of frame) → close to camera (30% of range)
40331
+ distanceMultiplier = 0.9 - (bboxCenterY * 0.6);
40332
+ this.console.log(`[Discovery] Using bounding box [${bbox.join(',')}] → center (${bboxCenterX.toFixed(2)}, ${bboxCenterY.toFixed(2)})`);
40333
+ }
40334
+ else {
40335
+ // Fallback: spread landmarks across FOV if no bounding box
40336
+ const cameraDeviceId = camera.deviceId;
40337
+ const existingFromCamera = (topology.landmarks || []).filter(l => l.visibleFromCameras?.includes(cameraDeviceId) ||
40338
+ l.visibleFromCameras?.includes(camera.name)).length;
40339
+ angleOffset = (existingFromCamera % 3 - 1) * halfFov * 0.6;
40340
+ distanceMultiplier = 0.5 + (existingFromCamera % 2) * 0.3;
40341
+ this.console.log(`[Discovery] No bounding box, using fallback spread (existing: ${existingFromCamera})`);
40342
+ }
40319
40343
  const finalAngle = dirRad + angleOffset;
40320
40344
  const distance = range * distanceMultiplier;
40321
40345
  position = {
40322
40346
  x: camera.floorPlanPosition.x + Math.cos(finalAngle) * distance,
40323
40347
  y: camera.floorPlanPosition.y + Math.sin(finalAngle) * distance,
40324
40348
  };
40325
- this.console.log(`[Discovery] Placing landmark "${suggestion.landmark.name}" in ${camera.name}'s FOV: dir=${direction}°, dist=${distance.toFixed(0)}px`);
40349
+ this.console.log(`[Discovery] Placing landmark "${suggestion.landmark.name}" in ${camera.name}'s FOV: dir=${direction}°, angle=${(angleOffset * 180 / Math.PI).toFixed(1)}°, dist=${distance.toFixed(0)}px`);
40326
40350
  }
40327
40351
  else {
40328
40352
  // Position in a grid pattern starting from center
@@ -40383,29 +40407,54 @@ class SpatialAwarenessPlugin extends sdk_1.ScryptedDeviceBase {
40383
40407
  // Convert direction to radians (0 = up/north, 90 = right/east)
40384
40408
  const dirRad = (direction - 90) * Math.PI / 180;
40385
40409
  const halfFov = (fovAngle / 2) * Math.PI / 180;
40386
- // Count existing zones from this camera to offset new ones
40387
- const existingFromCamera = (topology.drawnZones || []).filter((z) => z.linkedCameras?.includes(sourceCameras[0])).length;
40388
- // Create a wedge-shaped zone within the camera's FOV
40389
- // Offset based on existing zones to avoid overlap
40390
- const innerRadius = range * 0.3 + existingFromCamera * 20;
40391
- const outerRadius = range * 0.8 + existingFromCamera * 20;
40392
- // Use a portion of the FOV for each zone
40393
- const zoneSpread = halfFov * 0.7; // 70% of half FOV
40394
40410
  const camX = camera.floorPlanPosition.x;
40395
40411
  const camY = camera.floorPlanPosition.y;
40396
- // Create arc polygon (wedge shape)
40412
+ // Use bounding box if available to position zone accurately within FOV
40413
+ const bbox = zone.boundingBox; // [x, y, width, height] normalized 0-1
40414
+ let innerRadius;
40415
+ let outerRadius;
40416
+ let angleStart;
40417
+ let angleEnd;
40418
+ if (bbox && bbox.length >= 4) {
40419
+ // Map bounding box to position within FOV
40420
+ const bboxLeft = bbox[0];
40421
+ const bboxRight = bbox[0] + bbox[2];
40422
+ const bboxTop = bbox[1];
40423
+ const bboxBottom = bbox[1] + bbox[3];
40424
+ // Map X to angle within FOV (0 = left edge, 1 = right edge)
40425
+ angleStart = dirRad + (bboxLeft - 0.5) * 2 * halfFov;
40426
+ angleEnd = dirRad + (bboxRight - 0.5) * 2 * halfFov;
40427
+ // Map Y to distance (0 = far, 1 = close)
40428
+ innerRadius = range * (0.9 - bboxBottom * 0.6);
40429
+ outerRadius = range * (0.9 - bboxTop * 0.6);
40430
+ // Ensure min size
40431
+ if (outerRadius - innerRadius < 20) {
40432
+ outerRadius = innerRadius + 20;
40433
+ }
40434
+ this.console.log(`[Discovery] Zone "${zone.name}" using bbox [${bbox.join(',')}] → angles ${(angleStart * 180 / Math.PI).toFixed(1)}° to ${(angleEnd * 180 / Math.PI).toFixed(1)}°`);
40435
+ }
40436
+ else {
40437
+ // Fallback: wedge-shaped zone offset by existing count
40438
+ const existingFromCamera = (topology.drawnZones || []).filter((z) => z.linkedCameras?.includes(sourceCameras[0])).length;
40439
+ innerRadius = range * 0.3 + existingFromCamera * 20;
40440
+ outerRadius = range * 0.8 + existingFromCamera * 20;
40441
+ angleStart = dirRad - halfFov * 0.7;
40442
+ angleEnd = dirRad + halfFov * 0.7;
40443
+ this.console.log(`[Discovery] Zone "${zone.name}" using fallback spread (existing: ${existingFromCamera})`);
40444
+ }
40445
+ // Create arc polygon
40397
40446
  const steps = 8;
40398
- // Inner arc (from left to right)
40447
+ // Inner arc (from start angle to end angle)
40399
40448
  for (let i = 0; i <= steps; i++) {
40400
- const angle = dirRad - zoneSpread + (zoneSpread * 2 * i / steps);
40449
+ const angle = angleStart + (angleEnd - angleStart) * i / steps;
40401
40450
  polygon.push({
40402
40451
  x: camX + Math.cos(angle) * innerRadius,
40403
40452
  y: camY + Math.sin(angle) * innerRadius,
40404
40453
  });
40405
40454
  }
40406
- // Outer arc (from right to left)
40455
+ // Outer arc (from end angle to start angle)
40407
40456
  for (let i = steps; i >= 0; i--) {
40408
- const angle = dirRad - zoneSpread + (zoneSpread * 2 * i / steps);
40457
+ const angle = angleStart + (angleEnd - angleStart) * i / steps;
40409
40458
  polygon.push({
40410
40459
  x: camX + Math.cos(angle) * outerRadius,
40411
40460
  y: camY + Math.sin(angle) * outerRadius,