@blueharford/scrypted-spatial-awareness 0.6.0 → 0.6.1

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/out/plugin.zip CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blueharford/scrypted-spatial-awareness",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Cross-camera object tracking for Scrypted NVR with spatial awareness",
5
5
  "author": "Joshua Seidel <blueharford>",
6
6
  "license": "Apache-2.0",
package/src/main.ts CHANGED
@@ -1824,8 +1824,7 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
1824
1824
  let updated = false;
1825
1825
 
1826
1826
  if (suggestion.type === 'landmark' && suggestion.landmark) {
1827
- // Calculate a reasonable position for the landmark
1828
- // Use the first visible camera's position as a starting point, or canvas center
1827
+ // Calculate position for the landmark WITHIN the camera's field of view
1829
1828
  let position = suggestion.landmark.position;
1830
1829
  if (!position || (position.x === 0 && position.y === 0)) {
1831
1830
  // Find a camera that can see this landmark
@@ -1833,12 +1832,35 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
1833
1832
  const camera = visibleCameraId ? topology.cameras.find(c => c.deviceId === visibleCameraId) : null;
1834
1833
 
1835
1834
  if (camera?.floorPlanPosition) {
1836
- // Position near the camera with some offset
1837
- const offset = (topology.landmarks?.length || 0) * 30;
1835
+ // Get camera's FOV direction and range (cast to any for flexible access)
1836
+ const fov = (camera.fov || { mode: 'simple', angle: 90, direction: 0, range: 80 }) as any;
1837
+ const direction = fov.direction || 0;
1838
+ const range = fov.range || 80;
1839
+ const fovAngle = fov.angle || 90;
1840
+
1841
+ // Count existing landmarks from this camera to spread them out
1842
+ const existingFromCamera = (topology.landmarks || []).filter(l =>
1843
+ l.visibleFromCameras?.includes(visibleCameraId)
1844
+ ).length;
1845
+
1846
+ // Calculate position in front of camera within its FOV
1847
+ // Convert direction to radians (0 = up/north, 90 = right/east)
1848
+ const dirRad = (direction - 90) * Math.PI / 180;
1849
+ const halfFov = (fovAngle / 2) * Math.PI / 180;
1850
+
1851
+ // Spread landmarks across the FOV cone at varying distances
1852
+ const angleOffset = (existingFromCamera % 3 - 1) * halfFov * 0.6; // -0.6, 0, +0.6 of half FOV
1853
+ const distanceMultiplier = 0.5 + (existingFromCamera % 2) * 0.3; // 50% or 80% of range
1854
+
1855
+ const finalAngle = dirRad + angleOffset;
1856
+ const distance = range * distanceMultiplier;
1857
+
1838
1858
  position = {
1839
- x: camera.floorPlanPosition.x + 50 + (offset % 100),
1840
- y: camera.floorPlanPosition.y + 50 + Math.floor(offset / 100) * 30,
1859
+ x: camera.floorPlanPosition.x + Math.cos(finalAngle) * distance,
1860
+ y: camera.floorPlanPosition.y + Math.sin(finalAngle) * distance,
1841
1861
  };
1862
+
1863
+ this.console.log(`[Discovery] Placing landmark "${suggestion.landmark.name}" in ${camera.name}'s FOV: dir=${direction}°, dist=${distance.toFixed(0)}px`);
1842
1864
  } else {
1843
1865
  // Position in a grid pattern starting from center
1844
1866
  const landmarkCount = topology.landmarks?.length || 0;
@@ -1882,17 +1904,69 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
1882
1904
  ? topology.cameras.find(c => c.deviceId === sourceCameras[0] || c.name === sourceCameras[0])
1883
1905
  : null;
1884
1906
 
1885
- // Create a default polygon near the camera or at a default location
1886
- let centerX = 300;
1887
- let centerY = 200;
1907
+ // Create zone polygon WITHIN the camera's field of view
1908
+ let polygon: { x: number; y: number }[] = [];
1909
+ const timestamp = Date.now();
1910
+
1888
1911
  if (camera?.floorPlanPosition) {
1889
- centerX = camera.floorPlanPosition.x;
1890
- centerY = camera.floorPlanPosition.y + 80;
1891
- }
1912
+ // Get camera's FOV direction and range (cast to any for flexible access)
1913
+ const fov = (camera.fov || { mode: 'simple', angle: 90, direction: 0, range: 80 }) as any;
1914
+ const direction = fov.direction || 0;
1915
+ const range = fov.range || 80;
1916
+ const fovAngle = fov.angle || 90;
1917
+
1918
+ // Convert direction to radians (0 = up/north, 90 = right/east)
1919
+ const dirRad = (direction - 90) * Math.PI / 180;
1920
+ const halfFov = (fovAngle / 2) * Math.PI / 180;
1921
+
1922
+ // Count existing zones from this camera to offset new ones
1923
+ const existingFromCamera = (topology.drawnZones || []).filter((z: any) =>
1924
+ z.linkedCameras?.includes(sourceCameras[0])
1925
+ ).length;
1926
+
1927
+ // Create a wedge-shaped zone within the camera's FOV
1928
+ // Offset based on existing zones to avoid overlap
1929
+ const innerRadius = range * 0.3 + existingFromCamera * 20;
1930
+ const outerRadius = range * 0.8 + existingFromCamera * 20;
1931
+
1932
+ // Use a portion of the FOV for each zone
1933
+ const zoneSpread = halfFov * 0.7; // 70% of half FOV
1934
+
1935
+ const camX = camera.floorPlanPosition.x;
1936
+ const camY = camera.floorPlanPosition.y;
1937
+
1938
+ // Create arc polygon (wedge shape)
1939
+ const steps = 8;
1940
+ // Inner arc (from left to right)
1941
+ for (let i = 0; i <= steps; i++) {
1942
+ const angle = dirRad - zoneSpread + (zoneSpread * 2 * i / steps);
1943
+ polygon.push({
1944
+ x: camX + Math.cos(angle) * innerRadius,
1945
+ y: camY + Math.sin(angle) * innerRadius,
1946
+ });
1947
+ }
1948
+ // Outer arc (from right to left)
1949
+ for (let i = steps; i >= 0; i--) {
1950
+ const angle = dirRad - zoneSpread + (zoneSpread * 2 * i / steps);
1951
+ polygon.push({
1952
+ x: camX + Math.cos(angle) * outerRadius,
1953
+ y: camY + Math.sin(angle) * outerRadius,
1954
+ });
1955
+ }
1892
1956
 
1893
- // Create a rectangular zone (user can edit later)
1894
- const size = 100;
1895
- const timestamp = Date.now();
1957
+ this.console.log(`[Discovery] Creating zone "${zone.name}" in ${camera.name}'s FOV: dir=${direction}°`);
1958
+ } else {
1959
+ // Fallback: rectangular zone at default location
1960
+ const centerX = 300 + (topology.drawnZones?.length || 0) * 120;
1961
+ const centerY = 200;
1962
+ const size = 100;
1963
+ polygon = [
1964
+ { x: centerX - size/2, y: centerY - size/2 },
1965
+ { x: centerX + size/2, y: centerY - size/2 },
1966
+ { x: centerX + size/2, y: centerY + size/2 },
1967
+ { x: centerX - size/2, y: centerY + size/2 },
1968
+ ];
1969
+ }
1896
1970
 
1897
1971
  // 1. Create DrawnZone (visual on floor plan)
1898
1972
  const drawnZone = {
@@ -1900,12 +1974,7 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
1900
1974
  name: zone.name,
1901
1975
  type: (zone.type || 'custom') as DrawnZoneType,
1902
1976
  description: zone.description,
1903
- polygon: [
1904
- { x: centerX - size/2, y: centerY - size/2 },
1905
- { x: centerX + size/2, y: centerY - size/2 },
1906
- { x: centerX + size/2, y: centerY + size/2 },
1907
- { x: centerX - size/2, y: centerY + size/2 },
1908
- ] as any,
1977
+ polygon: polygon as any,
1909
1978
  linkedCameras: sourceCameras,
1910
1979
  };
1911
1980