@blueharford/scrypted-spatial-awareness 0.6.4 → 0.6.6
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/main.nodejs.js +1 -1
- package/dist/main.nodejs.js.map +1 -1
- package/dist/plugin.zip +0 -0
- package/out/main.nodejs.js +90 -22
- package/out/main.nodejs.js.map +1 -1
- package/out/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/core/topology-discovery.ts +3 -1
- package/src/main.ts +103 -28
package/out/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -659,7 +659,9 @@ export class TopologyDiscoveryEngine {
|
|
|
659
659
|
type: landmark.type,
|
|
660
660
|
description: landmark.description,
|
|
661
661
|
visibleFromCameras: [analysis.cameraId],
|
|
662
|
-
|
|
662
|
+
// Include bounding box for positioning (will be used by applyDiscoverySuggestion)
|
|
663
|
+
boundingBox: landmark.boundingBox,
|
|
664
|
+
} as any, // boundingBox is extra metadata not in Landmark interface
|
|
663
665
|
};
|
|
664
666
|
this.suggestions.set(suggestion.id, suggestion);
|
|
665
667
|
}
|
package/src/main.ts
CHANGED
|
@@ -1861,21 +1861,66 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
|
|
|
1861
1861
|
const range = fov.range || 80;
|
|
1862
1862
|
const fovAngle = fov.angle || 90;
|
|
1863
1863
|
|
|
1864
|
-
// Count existing landmarks from this camera to spread them out
|
|
1865
|
-
const cameraDeviceId = camera.deviceId;
|
|
1866
|
-
const existingFromCamera = (topology.landmarks || []).filter(l =>
|
|
1867
|
-
l.visibleFromCameras?.includes(cameraDeviceId) ||
|
|
1868
|
-
l.visibleFromCameras?.includes(camera.name)
|
|
1869
|
-
).length;
|
|
1870
|
-
|
|
1871
1864
|
// Calculate position in front of camera within its FOV
|
|
1872
1865
|
// Convert direction to radians (0 = up/north, 90 = right/east)
|
|
1873
1866
|
const dirRad = (direction - 90) * Math.PI / 180;
|
|
1874
1867
|
const halfFov = (fovAngle / 2) * Math.PI / 180;
|
|
1875
1868
|
|
|
1876
|
-
//
|
|
1877
|
-
|
|
1878
|
-
const
|
|
1869
|
+
// Use landmark TYPE to determine distance - some things are inherently far away
|
|
1870
|
+
// regardless of where they appear in the camera frame
|
|
1871
|
+
const landmarkType = suggestion.landmark.type;
|
|
1872
|
+
const farTypes = ['neighbor', 'boundary', 'street']; // Always place at edge of FOV
|
|
1873
|
+
const isFarType = farTypes.includes(landmarkType || '');
|
|
1874
|
+
|
|
1875
|
+
// Use bounding box if available for horizontal positioning
|
|
1876
|
+
// boundingBox format: [x, y, width, height] normalized 0-1
|
|
1877
|
+
const bbox = (suggestion.landmark as any).boundingBox as [number, number, number, number] | undefined;
|
|
1878
|
+
|
|
1879
|
+
let angleOffset: number;
|
|
1880
|
+
let distanceMultiplier: number;
|
|
1881
|
+
|
|
1882
|
+
if (bbox && bbox.length >= 2) {
|
|
1883
|
+
// Use bounding box X for horizontal position in FOV
|
|
1884
|
+
const bboxCenterX = bbox[0] + (bbox[2] || 0) / 2; // 0 = left edge, 1 = right edge
|
|
1885
|
+
|
|
1886
|
+
// Map X position to angle within FOV
|
|
1887
|
+
angleOffset = (bboxCenterX - 0.5) * 2 * halfFov;
|
|
1888
|
+
|
|
1889
|
+
// For distance: use TYPE first, then bbox Y as hint
|
|
1890
|
+
if (isFarType) {
|
|
1891
|
+
// Neighbors, boundaries, streets are BEYOND the camera's normal range
|
|
1892
|
+
// Place at 150-200% of range to indicate they're distant background features
|
|
1893
|
+
distanceMultiplier = 1.5 + Math.random() * 0.5; // 150-200% of range
|
|
1894
|
+
this.console.log(`[Discovery] Far-type landmark "${landmarkType}" placed BEYOND FOV (${(distanceMultiplier * 100).toFixed(0)}% of range)`);
|
|
1895
|
+
} else {
|
|
1896
|
+
// For other types, use bbox Y as a hint (but cap minimum distance)
|
|
1897
|
+
const bboxCenterY = bbox[1] + (bbox[3] || 0) / 2;
|
|
1898
|
+
// Map Y to distance: 0 (top) = far, 1 (bottom) = closer (but not too close)
|
|
1899
|
+
distanceMultiplier = Math.max(0.4, 0.9 - (bboxCenterY * 0.5));
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
this.console.log(`[Discovery] Using bounding box [${bbox.join(',')}] for horizontal position`);
|
|
1903
|
+
} else {
|
|
1904
|
+
// No bounding box - use type and spread pattern
|
|
1905
|
+
const cameraDeviceId = camera.deviceId;
|
|
1906
|
+
const existingFromCamera = (topology.landmarks || []).filter(l =>
|
|
1907
|
+
l.visibleFromCameras?.includes(cameraDeviceId) ||
|
|
1908
|
+
l.visibleFromCameras?.includes(camera.name)
|
|
1909
|
+
).length;
|
|
1910
|
+
|
|
1911
|
+
// Spread horizontally across FOV
|
|
1912
|
+
angleOffset = (existingFromCamera % 3 - 1) * halfFov * 0.6;
|
|
1913
|
+
|
|
1914
|
+
// Distance based on type
|
|
1915
|
+
if (isFarType) {
|
|
1916
|
+
// Neighbors, boundaries, streets are BEYOND the camera's normal range
|
|
1917
|
+
distanceMultiplier = 1.5 + Math.random() * 0.5; // 150-200% of range
|
|
1918
|
+
this.console.log(`[Discovery] Far-type landmark "${landmarkType}" (no bbox) placed BEYOND FOV`);
|
|
1919
|
+
} else {
|
|
1920
|
+
distanceMultiplier = 0.5 + (existingFromCamera % 2) * 0.3;
|
|
1921
|
+
this.console.log(`[Discovery] No bounding box, using fallback spread (existing: ${existingFromCamera})`);
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1879
1924
|
|
|
1880
1925
|
const finalAngle = dirRad + angleOffset;
|
|
1881
1926
|
const distance = range * distanceMultiplier;
|
|
@@ -1885,7 +1930,7 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
|
|
|
1885
1930
|
y: camera.floorPlanPosition.y + Math.sin(finalAngle) * distance,
|
|
1886
1931
|
};
|
|
1887
1932
|
|
|
1888
|
-
this.console.log(`[Discovery] Placing landmark "${suggestion.landmark.name}" in ${camera.name}'s FOV: dir=${direction}°, dist=${distance.toFixed(0)}px`);
|
|
1933
|
+
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`);
|
|
1889
1934
|
} else {
|
|
1890
1935
|
// Position in a grid pattern starting from center
|
|
1891
1936
|
const landmarkCount = topology.landmarks?.length || 0;
|
|
@@ -1956,35 +2001,65 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
|
|
|
1956
2001
|
const dirRad = (direction - 90) * Math.PI / 180;
|
|
1957
2002
|
const halfFov = (fovAngle / 2) * Math.PI / 180;
|
|
1958
2003
|
|
|
1959
|
-
|
|
1960
|
-
const
|
|
1961
|
-
z.linkedCameras?.includes(sourceCameras[0])
|
|
1962
|
-
).length;
|
|
2004
|
+
const camX = camera.floorPlanPosition.x;
|
|
2005
|
+
const camY = camera.floorPlanPosition.y;
|
|
1963
2006
|
|
|
1964
|
-
//
|
|
1965
|
-
|
|
1966
|
-
const innerRadius = range * 0.3 + existingFromCamera * 20;
|
|
1967
|
-
const outerRadius = range * 0.8 + existingFromCamera * 20;
|
|
2007
|
+
// Use bounding box if available to position zone accurately within FOV
|
|
2008
|
+
const bbox = zone.boundingBox; // [x, y, width, height] normalized 0-1
|
|
1968
2009
|
|
|
1969
|
-
|
|
1970
|
-
|
|
2010
|
+
let innerRadius: number;
|
|
2011
|
+
let outerRadius: number;
|
|
2012
|
+
let angleStart: number;
|
|
2013
|
+
let angleEnd: number;
|
|
1971
2014
|
|
|
1972
|
-
|
|
1973
|
-
|
|
2015
|
+
if (bbox && bbox.length >= 4) {
|
|
2016
|
+
// Map bounding box to position within FOV
|
|
2017
|
+
const bboxLeft = bbox[0];
|
|
2018
|
+
const bboxRight = bbox[0] + bbox[2];
|
|
2019
|
+
const bboxTop = bbox[1];
|
|
2020
|
+
const bboxBottom = bbox[1] + bbox[3];
|
|
2021
|
+
|
|
2022
|
+
// Map X to angle within FOV (0 = left edge, 1 = right edge)
|
|
2023
|
+
angleStart = dirRad + (bboxLeft - 0.5) * 2 * halfFov;
|
|
2024
|
+
angleEnd = dirRad + (bboxRight - 0.5) * 2 * halfFov;
|
|
2025
|
+
|
|
2026
|
+
// Map Y to distance (0 = far, 1 = close)
|
|
2027
|
+
innerRadius = range * (0.9 - bboxBottom * 0.6);
|
|
2028
|
+
outerRadius = range * (0.9 - bboxTop * 0.6);
|
|
2029
|
+
|
|
2030
|
+
// Ensure min size
|
|
2031
|
+
if (outerRadius - innerRadius < 20) {
|
|
2032
|
+
outerRadius = innerRadius + 20;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
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)}°`);
|
|
2036
|
+
} else {
|
|
2037
|
+
// Fallback: wedge-shaped zone offset by existing count
|
|
2038
|
+
const existingFromCamera = (topology.drawnZones || []).filter((z: any) =>
|
|
2039
|
+
z.linkedCameras?.includes(sourceCameras[0])
|
|
2040
|
+
).length;
|
|
2041
|
+
|
|
2042
|
+
innerRadius = range * 0.3 + existingFromCamera * 20;
|
|
2043
|
+
outerRadius = range * 0.8 + existingFromCamera * 20;
|
|
2044
|
+
angleStart = dirRad - halfFov * 0.7;
|
|
2045
|
+
angleEnd = dirRad + halfFov * 0.7;
|
|
2046
|
+
|
|
2047
|
+
this.console.log(`[Discovery] Zone "${zone.name}" using fallback spread (existing: ${existingFromCamera})`);
|
|
2048
|
+
}
|
|
1974
2049
|
|
|
1975
|
-
// Create arc polygon
|
|
2050
|
+
// Create arc polygon
|
|
1976
2051
|
const steps = 8;
|
|
1977
|
-
// Inner arc (from
|
|
2052
|
+
// Inner arc (from start angle to end angle)
|
|
1978
2053
|
for (let i = 0; i <= steps; i++) {
|
|
1979
|
-
const angle =
|
|
2054
|
+
const angle = angleStart + (angleEnd - angleStart) * i / steps;
|
|
1980
2055
|
polygon.push({
|
|
1981
2056
|
x: camX + Math.cos(angle) * innerRadius,
|
|
1982
2057
|
y: camY + Math.sin(angle) * innerRadius,
|
|
1983
2058
|
});
|
|
1984
2059
|
}
|
|
1985
|
-
// Outer arc (from
|
|
2060
|
+
// Outer arc (from end angle to start angle)
|
|
1986
2061
|
for (let i = steps; i >= 0; i--) {
|
|
1987
|
-
const angle =
|
|
2062
|
+
const angle = angleStart + (angleEnd - angleStart) * i / steps;
|
|
1988
2063
|
polygon.push({
|
|
1989
2064
|
x: camX + Math.cos(angle) * outerRadius,
|
|
1990
2065
|
y: camY + Math.sin(angle) * outerRadius,
|