mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-02-12 16:52:51 +00:00
Fixes for 360-deg FOVs
This commit is contained in:
@@ -104,7 +104,8 @@ cp lib/keys.dart.example lib/keys.dart
|
||||
## Roadmap
|
||||
|
||||
### Needed Bugfixes
|
||||
- Fix rendering of 0-360 FOV ring
|
||||
- Make submission guide scarier
|
||||
- "More..." button in profiles dropdown -> identify page
|
||||
- Node data fetching super slow; retries not working?
|
||||
- Clean up tile cache; implement some max size or otherwise trim unused / old tiles to prevent infinite memory growth
|
||||
- Filter NSI suggestions based on what has already been typed in
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
{
|
||||
"2.4.3": {
|
||||
"content": [
|
||||
"• Fixed 360° FOV rendering - devices with full circle coverage now render as complete rings instead of having a wedge cut out or being a line",
|
||||
"• Fixed 360° FOV submission - now correctly submits '0-360' to OpenStreetMap instead of incorrect '180-180' values, disables direction slider"
|
||||
]
|
||||
},
|
||||
"2.4.1": {
|
||||
"content": [
|
||||
"• Save button moved to top-right corner of profile editor screens",
|
||||
|
||||
@@ -107,6 +107,11 @@ class OsmNode {
|
||||
start = ((start % 360) + 360) % 360;
|
||||
end = ((end % 360) + 360) % 360;
|
||||
|
||||
// Special case: if start equals end, this represents 360° FOV
|
||||
if (start == end) {
|
||||
return DirectionFov(start, 360.0);
|
||||
}
|
||||
|
||||
double width, center;
|
||||
|
||||
if (start > end) {
|
||||
|
||||
@@ -743,6 +743,11 @@ class UploadQueueState extends ChangeNotifier {
|
||||
|
||||
// Convert a center direction and FOV to range notation (e.g., 180° center with 90° FOV -> "135-225")
|
||||
String _formatDirectionWithFov(double center, double fov) {
|
||||
// Handle 360-degree FOV as special case
|
||||
if (fov >= 360) {
|
||||
return '0-360';
|
||||
}
|
||||
|
||||
final halfFov = fov / 2;
|
||||
final start = (center - halfFov + 360) % 360;
|
||||
final end = (center + halfFov) % 360;
|
||||
|
||||
@@ -170,7 +170,10 @@ class DirectionConesBuilder {
|
||||
bool isActiveDirection = true,
|
||||
}) {
|
||||
// Handle full circle case (360-degree FOV)
|
||||
if (halfAngleDeg >= 180) {
|
||||
// Use 179.5 threshold to account for floating point precision
|
||||
print("DEBUG: halfAngleDeg = $halfAngleDeg, bearing = $bearingDeg");
|
||||
if (halfAngleDeg >= 179.5) {
|
||||
print("DEBUG: Using full circle for 360° FOV");
|
||||
return _buildFullCircle(
|
||||
origin: origin,
|
||||
zoom: zoom,
|
||||
@@ -179,6 +182,7 @@ class DirectionConesBuilder {
|
||||
isActiveDirection: isActiveDirection,
|
||||
);
|
||||
}
|
||||
print("DEBUG: Using normal cone for FOV = ${halfAngleDeg * 2}°");
|
||||
|
||||
// Calculate pixel-based radii
|
||||
final outerRadiusPx = kNodeIconDiameter + (kNodeIconDiameter * kDirectionConeBaseLength);
|
||||
@@ -232,6 +236,7 @@ class DirectionConesBuilder {
|
||||
}
|
||||
|
||||
/// Build a full circle for 360-degree FOV cases
|
||||
/// Returns just the outer circle - we'll handle the donut effect differently
|
||||
static Polygon _buildFullCircle({
|
||||
required LatLng origin,
|
||||
required double zoom,
|
||||
@@ -239,17 +244,19 @@ class DirectionConesBuilder {
|
||||
bool isSession = false,
|
||||
bool isActiveDirection = true,
|
||||
}) {
|
||||
// Calculate pixel-based radii
|
||||
print("DEBUG: Building full circle - isSession: $isSession, isActiveDirection: $isActiveDirection");
|
||||
|
||||
// Calculate pixel-based radii
|
||||
final outerRadiusPx = kNodeIconDiameter + (kNodeIconDiameter * kDirectionConeBaseLength);
|
||||
final innerRadiusPx = kNodeIconDiameter + (2 * getNodeRingThickness(context));
|
||||
|
||||
// Convert pixels to coordinate distances with zoom scaling
|
||||
final pixelToCoordinate = 0.00001 * math.pow(2, 15 - zoom);
|
||||
final outerRadius = outerRadiusPx * pixelToCoordinate;
|
||||
final innerRadius = innerRadiusPx * pixelToCoordinate;
|
||||
|
||||
// Create full circle with many points for smooth rendering
|
||||
const int circlePoints = 36;
|
||||
print("DEBUG: Outer radius: $outerRadius, zoom: $zoom");
|
||||
|
||||
// Create simple filled circle - no donut complexity
|
||||
const int circlePoints = 60;
|
||||
final points = <LatLng>[];
|
||||
|
||||
LatLng project(double deg, double distance) {
|
||||
@@ -260,17 +267,13 @@ class DirectionConesBuilder {
|
||||
return LatLng(origin.latitude + dLat, origin.longitude + dLon);
|
||||
}
|
||||
|
||||
// Add outer circle points
|
||||
for (int i = 0; i < circlePoints; i++) {
|
||||
final angle = i * 360.0 / circlePoints;
|
||||
// Add outer circle points - simple complete circle
|
||||
for (int i = 0; i <= circlePoints; i++) { // Note: <= to ensure closure
|
||||
final angle = (i * 360.0 / circlePoints) % 360.0;
|
||||
points.add(project(angle, outerRadius));
|
||||
}
|
||||
|
||||
// Add inner circle points in reverse order to create donut
|
||||
for (int i = circlePoints - 1; i >= 0; i--) {
|
||||
final angle = i * 360.0 / circlePoints;
|
||||
points.add(project(angle, innerRadius));
|
||||
}
|
||||
|
||||
print("DEBUG: Created ${points.length} points for full circle");
|
||||
|
||||
// Adjust opacity based on direction state
|
||||
double opacity = kDirectionConeOpacity;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: deflockapp
|
||||
description: Map public surveillance infrastructure with OpenStreetMap
|
||||
publish_to: "none"
|
||||
version: 2.4.1+39 # The thing after the + is the version code, incremented with each release
|
||||
version: 2.4.3+41 # The thing after the + is the version code, incremented with each release
|
||||
|
||||
environment:
|
||||
sdk: ">=3.5.0 <4.0.0" # oauth2_client 4.x needs Dart 3.5+
|
||||
|
||||
91
test/models/osm_node_test.dart
Normal file
91
test/models/osm_node_test.dart
Normal file
@@ -0,0 +1,91 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:deflockapp/models/osm_node.dart';
|
||||
|
||||
void main() {
|
||||
group('OsmNode Direction Parsing', () {
|
||||
test('should parse 360-degree FOV from X-X notation', () {
|
||||
final node = OsmNode(
|
||||
id: 1,
|
||||
coord: const LatLng(0, 0),
|
||||
tags: {'direction': '180-180'},
|
||||
);
|
||||
|
||||
final directionFovPairs = node.directionFovPairs;
|
||||
|
||||
expect(directionFovPairs, hasLength(1));
|
||||
expect(directionFovPairs[0].centerDegrees, equals(180.0));
|
||||
expect(directionFovPairs[0].fovDegrees, equals(360.0));
|
||||
});
|
||||
|
||||
test('should parse 360-degree FOV from 0-0 notation', () {
|
||||
final node = OsmNode(
|
||||
id: 1,
|
||||
coord: const LatLng(0, 0),
|
||||
tags: {'direction': '0-0'},
|
||||
);
|
||||
|
||||
final directionFovPairs = node.directionFovPairs;
|
||||
|
||||
expect(directionFovPairs, hasLength(1));
|
||||
expect(directionFovPairs[0].centerDegrees, equals(0.0));
|
||||
expect(directionFovPairs[0].fovDegrees, equals(360.0));
|
||||
});
|
||||
|
||||
test('should parse 360-degree FOV from 270-270 notation', () {
|
||||
final node = OsmNode(
|
||||
id: 1,
|
||||
coord: const LatLng(0, 0),
|
||||
tags: {'direction': '270-270'},
|
||||
);
|
||||
|
||||
final directionFovPairs = node.directionFovPairs;
|
||||
|
||||
expect(directionFovPairs, hasLength(1));
|
||||
expect(directionFovPairs[0].centerDegrees, equals(270.0));
|
||||
expect(directionFovPairs[0].fovDegrees, equals(360.0));
|
||||
});
|
||||
|
||||
test('should parse normal range notation correctly', () {
|
||||
final node = OsmNode(
|
||||
id: 1,
|
||||
coord: const LatLng(0, 0),
|
||||
tags: {'direction': '90-270'},
|
||||
);
|
||||
|
||||
final directionFovPairs = node.directionFovPairs;
|
||||
|
||||
expect(directionFovPairs, hasLength(1));
|
||||
expect(directionFovPairs[0].centerDegrees, equals(180.0));
|
||||
expect(directionFovPairs[0].fovDegrees, equals(180.0));
|
||||
});
|
||||
|
||||
test('should parse wrapping range notation correctly', () {
|
||||
final node = OsmNode(
|
||||
id: 1,
|
||||
coord: const LatLng(0, 0),
|
||||
tags: {'direction': '270-90'},
|
||||
);
|
||||
|
||||
final directionFovPairs = node.directionFovPairs;
|
||||
|
||||
expect(directionFovPairs, hasLength(1));
|
||||
expect(directionFovPairs[0].centerDegrees, equals(0.0));
|
||||
expect(directionFovPairs[0].fovDegrees, equals(180.0));
|
||||
});
|
||||
|
||||
test('should parse single direction correctly', () {
|
||||
final node = OsmNode(
|
||||
id: 1,
|
||||
coord: const LatLng(0, 0),
|
||||
tags: {'direction': '90'},
|
||||
);
|
||||
|
||||
final directionFovPairs = node.directionFovPairs;
|
||||
|
||||
expect(directionFovPairs, hasLength(1));
|
||||
expect(directionFovPairs[0].centerDegrees, equals(90.0));
|
||||
// Default FOV from dev_config (kDirectionConeHalfAngle * 2)
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user