feat: basic navigation with bugs
This commit is contained in:
parent
e653010458
commit
3de71e2a5a
@ -19,8 +19,8 @@ Future<void> showFeatureBottomSheet(
|
|||||||
data: ThemeData.light(),
|
data: ThemeData.light(),
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(
|
constraints: const BoxConstraints(
|
||||||
minHeight: 300,
|
// minHeight: 300,
|
||||||
),
|
),
|
||||||
width: Get.mediaQuery.size.width,
|
width: Get.mediaQuery.size.width,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
@ -72,6 +72,28 @@ Future<void> showFeatureBottomSheet(
|
|||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
],
|
],
|
||||||
..._buildFeatureContent(feature),
|
..._buildFeatureContent(feature),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.share_location,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
"Start Navigation",
|
||||||
|
style: TextStyle(color: Colors.black),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onPressed: () => {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -114,7 +136,35 @@ List<Widget> _buildRoomContent(Feature feature) {
|
|||||||
|
|
||||||
/// Builds the content for the Door feature type.
|
/// Builds the content for the Door feature type.
|
||||||
List<Widget> _buildDoorContent(Feature feature, List<String> connects) {
|
List<Widget> _buildDoorContent(Feature feature, List<String> connects) {
|
||||||
return [Text('Door: ${feature.name}\nConnects: $connects')];
|
return [
|
||||||
|
Text(
|
||||||
|
feature.name,
|
||||||
|
style: const TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
if (connects.isNotEmpty) ...[
|
||||||
|
const Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(right: 4),
|
||||||
|
child: Text(
|
||||||
|
'Connects:',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 4,
|
||||||
|
children: connects.map((place) {
|
||||||
|
return ColorfulChip(label: place.toString());
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the content for the Toilet feature type.
|
/// Builds the content for the Toilet feature type.
|
||||||
|
@ -10,38 +10,39 @@ import 'package:uninav/map.dart';
|
|||||||
import 'package:uninav/util/geomath.dart';
|
import 'package:uninav/util/geomath.dart';
|
||||||
import 'package:uninav/util/util.dart';
|
import 'package:uninav/util/util.dart';
|
||||||
|
|
||||||
List<Widget> renderLevel(int level, {LayerHitNotifier? hitNotifier}) {
|
List<Widget> renderLevel(
|
||||||
|
int level,
|
||||||
|
) {
|
||||||
return <Widget>[
|
return <Widget>[
|
||||||
LevelLayer(
|
LevelLayer(
|
||||||
filter: (feature) =>
|
filter: (feature) =>
|
||||||
feature.level == level && feature.type is LectureHall,
|
feature.level == level && feature.type is LectureHall,
|
||||||
polyConstructor: (feature) => feature
|
polyConstructor: (feature) => feature
|
||||||
.getPolygon(
|
.getPolygon(
|
||||||
constructor: (pts) => Polygon(
|
constructor: (pts) => Polygon(
|
||||||
points: pts,
|
points: pts,
|
||||||
color: Colors.orange.withOpacity(0.2),
|
color: Colors.orange.withOpacity(0.2),
|
||||||
borderColor: Colors.orange,
|
borderColor: Colors.orange,
|
||||||
borderStrokeWidth: 2,
|
borderStrokeWidth: 2,
|
||||||
hitValue: feature,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
markerConstructor: (feature) => Marker(
|
|
||||||
width: 50,
|
|
||||||
height: 20,
|
|
||||||
point: feature.getPoint().unwrap(),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.class_,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
Text('${feature.name}'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
),
|
),
|
||||||
notifier: hitNotifier),
|
)
|
||||||
|
.unwrap(),
|
||||||
|
markerConstructor: (feature) => Marker(
|
||||||
|
width: 50,
|
||||||
|
height: 20,
|
||||||
|
point: feature.getPoint().unwrap(),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.class_,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
Text('${feature.name}'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
LevelLayer(
|
LevelLayer(
|
||||||
filter: (feature) => feature.level == level && feature.type is Room,
|
filter: (feature) => feature.level == level && feature.type is Room,
|
||||||
polyConstructor: (feature) => feature
|
polyConstructor: (feature) => feature
|
||||||
@ -51,11 +52,9 @@ List<Widget> renderLevel(int level, {LayerHitNotifier? hitNotifier}) {
|
|||||||
color: Colors.green.withOpacity(1.2),
|
color: Colors.green.withOpacity(1.2),
|
||||||
borderColor: Colors.green,
|
borderColor: Colors.green,
|
||||||
borderStrokeWidth: 2,
|
borderStrokeWidth: 2,
|
||||||
hitValue: feature,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
notifier: hitNotifier,
|
|
||||||
),
|
),
|
||||||
LevelLayer(
|
LevelLayer(
|
||||||
filter: (feature) => feature.level == level && feature.type is Door,
|
filter: (feature) => feature.level == level && feature.type is Door,
|
||||||
@ -72,7 +71,6 @@ List<Widget> renderLevel(int level, {LayerHitNotifier? hitNotifier}) {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
notifier: hitNotifier,
|
|
||||||
),
|
),
|
||||||
LevelLayer(
|
LevelLayer(
|
||||||
filter: (feature) => feature.level == level && feature.type is Toilet,
|
filter: (feature) => feature.level == level && feature.type is Toilet,
|
||||||
@ -91,7 +89,6 @@ List<Widget> renderLevel(int level, {LayerHitNotifier? hitNotifier}) {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
notifier: hitNotifier,
|
|
||||||
),
|
),
|
||||||
LevelLayer(
|
LevelLayer(
|
||||||
filter: (feature) =>
|
filter: (feature) =>
|
||||||
@ -110,7 +107,6 @@ List<Widget> renderLevel(int level, {LayerHitNotifier? hitNotifier}) {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
notifier: hitNotifier,
|
|
||||||
),
|
),
|
||||||
LevelLayer(
|
LevelLayer(
|
||||||
filter: (feature) =>
|
filter: (feature) =>
|
||||||
@ -129,7 +125,6 @@ List<Widget> renderLevel(int level, {LayerHitNotifier? hitNotifier}) {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
notifier: hitNotifier,
|
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -140,7 +135,6 @@ class LevelLayer extends StatelessWidget {
|
|||||||
final Marker Function(LatLng, String)? polyCenterMarkerConstructor;
|
final Marker Function(LatLng, String)? polyCenterMarkerConstructor;
|
||||||
final Marker Function(Feature)? markerConstructor;
|
final Marker Function(Feature)? markerConstructor;
|
||||||
final int? level;
|
final int? level;
|
||||||
final LayerHitNotifier? notifier;
|
|
||||||
|
|
||||||
const LevelLayer({
|
const LevelLayer({
|
||||||
this.level,
|
this.level,
|
||||||
@ -148,7 +142,6 @@ class LevelLayer extends StatelessWidget {
|
|||||||
this.polyConstructor,
|
this.polyConstructor,
|
||||||
this.polyCenterMarkerConstructor,
|
this.polyCenterMarkerConstructor,
|
||||||
this.markerConstructor,
|
this.markerConstructor,
|
||||||
this.notifier,
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -173,7 +166,6 @@ class LevelLayer extends StatelessWidget {
|
|||||||
points: points,
|
points: points,
|
||||||
borderColor: Colors.black26,
|
borderColor: Colors.black26,
|
||||||
borderStrokeWidth: 2.0,
|
borderStrokeWidth: 2.0,
|
||||||
hitValue: feature,
|
|
||||||
))
|
))
|
||||||
.unwrap());
|
.unwrap());
|
||||||
}
|
}
|
||||||
@ -241,14 +233,12 @@ class LevelLayer extends StatelessWidget {
|
|||||||
widgets.add(TranslucentPointer(
|
widgets.add(TranslucentPointer(
|
||||||
child: PolygonLayer(
|
child: PolygonLayer(
|
||||||
polygons: filteredPolygons,
|
polygons: filteredPolygons,
|
||||||
hitNotifier: notifier,
|
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
widgets.add(TranslucentPointer(
|
widgets.add(TranslucentPointer(
|
||||||
child: PolygonLayer(
|
child: PolygonLayer(
|
||||||
polygons: filteredPolygons,
|
polygons: filteredPolygons,
|
||||||
hitNotifier: notifier,
|
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -86,15 +86,15 @@ class MyMapController extends GetxController {
|
|||||||
|
|
||||||
final featuresList = <Feature>[];
|
final featuresList = <Feature>[];
|
||||||
|
|
||||||
print('doing');
|
// print('doing');
|
||||||
final geojson = GeoJSONFeatureCollection.fromJSON(geoJsonString);
|
final geojson = GeoJSONFeatureCollection.fromJSON(geoJsonString);
|
||||||
print('done');
|
// print('done');
|
||||||
|
|
||||||
for (final feature in geojson.features) {
|
for (final feature in geojson.features) {
|
||||||
print(feature);
|
// print(feature);
|
||||||
print(feature?.properties);
|
// print(feature?.properties);
|
||||||
if (feature == null) continue;
|
if (feature == null) continue;
|
||||||
print(feature.properties);
|
// print(feature.properties);
|
||||||
final parsed = parseFeature(
|
final parsed = parseFeature(
|
||||||
feature.properties ?? <String, dynamic>{}, feature.geometry);
|
feature.properties ?? <String, dynamic>{}, feature.geometry);
|
||||||
if (parsed case Ok(:final ok)) {
|
if (parsed case Ok(:final ok)) {
|
||||||
|
@ -36,7 +36,6 @@ class Feature with _$Feature {
|
|||||||
points: pts,
|
points: pts,
|
||||||
borderColor: Colors.black26,
|
borderColor: Colors.black26,
|
||||||
borderStrokeWidth: 2.0,
|
borderStrokeWidth: 2.0,
|
||||||
hitValue: 'test${pts.length}',
|
|
||||||
);
|
);
|
||||||
final polygon = geometry as GeoJSONPolygon;
|
final polygon = geometry as GeoJSONPolygon;
|
||||||
// print(polygon.geometry!.geoSeries[0].geoPoints);
|
// print(polygon.geometry!.geoSeries[0].geoPoints);
|
||||||
|
@ -40,7 +40,7 @@ Result<Feature> parseFeature(
|
|||||||
|
|
||||||
final building = yaml['building'] as String?;
|
final building = yaml['building'] as String?;
|
||||||
|
|
||||||
print("yaml: $yaml");
|
// print("yaml: $yaml");
|
||||||
|
|
||||||
var raw_type = yaml['type'] as String?;
|
var raw_type = yaml['type'] as String?;
|
||||||
if (raw_type == null && layer?.toLowerCase() == 'buildings') {
|
if (raw_type == null && layer?.toLowerCase() == 'buildings') {
|
||||||
@ -123,7 +123,7 @@ Result<List<String>> stringifyList(List<dynamic> tramLines) {
|
|||||||
|
|
||||||
Result<List<T>> getYamlList<T>(YamlMap yaml, String key) {
|
Result<List<T>> getYamlList<T>(YamlMap yaml, String key) {
|
||||||
try {
|
try {
|
||||||
print('yaml is ${yaml[key]}');
|
// print('yaml is ${yaml[key]}');
|
||||||
final val = (yaml[key] as YamlList?);
|
final val = (yaml[key] as YamlList?);
|
||||||
if (val == null) {
|
if (val == null) {
|
||||||
return bail("Key $key is missing in yaml");
|
return bail("Key $key is missing in yaml");
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:uninav/controllers/isar_controller.dart';
|
import 'package:uninav/controllers/isar_controller.dart';
|
||||||
import 'package:uninav/controllers/map_controller.dart';
|
import 'package:uninav/controllers/map_controller.dart';
|
||||||
@ -8,6 +9,9 @@ import 'package:uninav/settings.dart';
|
|||||||
// TODO: maybe make not async?
|
// TODO: maybe make not async?
|
||||||
void main() async {
|
void main() async {
|
||||||
Get.put(MyMapController());
|
Get.put(MyMapController());
|
||||||
|
await Get.find<MyMapController>()
|
||||||
|
.loadGeoJson(await rootBundle.loadString('assets/geo/uulm_beta.geojson'));
|
||||||
|
|
||||||
await Get.putAsync(() async {
|
await Get.putAsync(() async {
|
||||||
final controller = IsarController();
|
final controller = IsarController();
|
||||||
await controller.initializeIsar();
|
await controller.initializeIsar();
|
||||||
|
10
lib/map.dart
10
lib/map.dart
@ -19,7 +19,6 @@ class MapPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final LayerHitNotifier hitNotifier = ValueNotifier(null);
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
drawer: MyDrawer(),
|
drawer: MyDrawer(),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
@ -43,8 +42,6 @@ class MapPage extends StatelessWidget {
|
|||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
// Add onPressed logic here
|
// Add onPressed logic here
|
||||||
await Get.find<MyMapController>().loadGeoJson(
|
|
||||||
await rootBundle.loadString('assets/geo/uulm_beta.geojson'));
|
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
@ -70,7 +67,6 @@ class MapPage extends StatelessWidget {
|
|||||||
TranslucentPointer(
|
TranslucentPointer(
|
||||||
child: LevelLayer(
|
child: LevelLayer(
|
||||||
filter: (feature) => feature.type is Building,
|
filter: (feature) => feature.type is Building,
|
||||||
notifier: hitNotifier,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -97,10 +93,8 @@ class MapPage extends StatelessWidget {
|
|||||||
color: Colors.green.withOpacity(0.2),
|
color: Colors.green.withOpacity(0.2),
|
||||||
borderColor: Colors.green,
|
borderColor: Colors.green,
|
||||||
borderStrokeWidth: 1,
|
borderStrokeWidth: 1,
|
||||||
hitValue: feature,
|
|
||||||
))
|
))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
notifier: hitNotifier,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -108,8 +102,8 @@ class MapPage extends StatelessWidget {
|
|||||||
Obx(
|
Obx(
|
||||||
() => Stack(
|
() => Stack(
|
||||||
children: renderLevel(
|
children: renderLevel(
|
||||||
Get.find<MyMapController>().currentLevel.value,
|
Get.find<MyMapController>().currentLevel.value,
|
||||||
hitNotifier: hitNotifier)),
|
)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
425
lib/nav/graph.dart
Normal file
425
lib/nav/graph.dart
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
import 'package:anyhow/anyhow.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:directed_graph/directed_graph.dart';
|
||||||
|
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:geojson_vi/geojson_vi.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:latlong2/latlong.dart';
|
||||||
|
import 'package:uninav/data/geo/model.dart';
|
||||||
|
import 'package:uninav/util/geojson_util.dart';
|
||||||
|
import 'package:uninav/util/util.dart';
|
||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
|
part 'graph.freezed.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class GraphFeature with _$GraphFeature {
|
||||||
|
const factory GraphFeature.buildingFloor(int floor, Feature building) =
|
||||||
|
BuildingFloor;
|
||||||
|
const factory GraphFeature.portal(int fromFloor, String from, int toFloor,
|
||||||
|
String to, Feature baseFeature) = Portal;
|
||||||
|
const factory GraphFeature.basicFeature(
|
||||||
|
int floor, String building, Feature feature) = BasicFeature;
|
||||||
|
|
||||||
|
const GraphFeature._();
|
||||||
|
|
||||||
|
Result<LatLng> getCenter() {
|
||||||
|
return when(
|
||||||
|
buildingFloor: (floor, building) => building.getCenterPoint(),
|
||||||
|
portal: (fromFloor, from, toFloor, to, baseFeature) =>
|
||||||
|
baseFeature.getCenterPoint(),
|
||||||
|
basicFeature: (floor, building, feature) => feature.getCenterPoint(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
double distanceTo(GraphFeature other, String unit) => distanceBetweenLatLng(
|
||||||
|
getCenter().unwrap(), other.getCenter().unwrap(), unit);
|
||||||
|
|
||||||
|
double metersTo(GraphFeature other) => distanceTo(other, "meters");
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return when(
|
||||||
|
buildingFloor: (floor, building) => 'Floor (${building.name}:$floor)',
|
||||||
|
portal: (fromFloor, from, toFloor, to, _) =>
|
||||||
|
'Portal ($from:$fromFloor -> $to:$toFloor)',
|
||||||
|
basicFeature: (floor, building, feature) =>
|
||||||
|
'Feature (${formatFeatureTitle(feature)} ($building:$floor))',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool eq(String? a, String? b) => a?.toLowerCase() == b?.toLowerCase();
|
||||||
|
|
||||||
|
IList<GraphFeature> wrap(Feature feature, int floor, String buildingFrom) {
|
||||||
|
return feature.type
|
||||||
|
.maybeWhen(
|
||||||
|
building: () => [GraphFeature.buildingFloor(floor, feature)],
|
||||||
|
stairs: (floors) => stairPortalGenerator(floors, floor, feature),
|
||||||
|
lift: (floors) => stairPortalGenerator(floors, floor, feature, 99),
|
||||||
|
door: (connections) =>
|
||||||
|
doorPortalGenerator(connections, floor, buildingFrom, feature),
|
||||||
|
orElse: () => [GraphFeature.basicFeature(floor, buildingFrom, feature)],
|
||||||
|
)
|
||||||
|
.lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<GraphFeature> doorPortalGenerator(
|
||||||
|
List<String> connections, int floor, String from, Feature feature) {
|
||||||
|
final portals = <GraphFeature>[];
|
||||||
|
|
||||||
|
for (final connection in connections.where((c) => !eq(c, from))) {
|
||||||
|
portals.add(GraphFeature.portal(
|
||||||
|
floor, from, floor, connection.toLowerCase(), feature));
|
||||||
|
}
|
||||||
|
|
||||||
|
return portals;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<GraphFeature> stairPortalGenerator(
|
||||||
|
List<int> floors, int floor, Feature feature,
|
||||||
|
[int maxDist = 1]) {
|
||||||
|
final portals = <GraphFeature>[];
|
||||||
|
for (int i = 1; i <= maxDist; i++) {
|
||||||
|
if (floors.contains(floor - i)) {
|
||||||
|
portals.add(GraphFeature.portal(floor, feature.building!.toLowerCase(),
|
||||||
|
floor - i, feature.building!.toLowerCase(), feature));
|
||||||
|
}
|
||||||
|
if (floors.contains(floor + i)) {
|
||||||
|
portals.add(GraphFeature.portal(floor, feature.building!.toLowerCase(),
|
||||||
|
floor + i, feature.building!.toLowerCase(), feature));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return portals;
|
||||||
|
}
|
||||||
|
|
||||||
|
Feature unwrap(GraphFeature feature) {
|
||||||
|
return feature.when(
|
||||||
|
buildingFloor: (floor, building) => building,
|
||||||
|
portal: (fromFloor, from, toFloor, to, baseFeature) => baseFeature,
|
||||||
|
basicFeature: (floor, building, f) => f,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
double sum(double left, double right) => left + right;
|
||||||
|
|
||||||
|
// WeightedDirectedGraph createGraph(Feature origin, List<Feature> allFeatures) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
List<GraphFeature> findAdjacent(
|
||||||
|
GraphFeature feature, Iterable<Feature> allFeatures) {
|
||||||
|
List<GraphFeature> adjacentFeatures = [];
|
||||||
|
|
||||||
|
if (feature is BuildingFloor) {
|
||||||
|
// find all features in the building on the right floor
|
||||||
|
adjacentFeatures = allFeatures
|
||||||
|
.where((f) => eq(f.building, feature.building.name) || f.type is Door)
|
||||||
|
.where((f) => f.type.maybeWhen(
|
||||||
|
lift: (levels) => levels.contains(feature.floor),
|
||||||
|
stairs: (levels) => levels.contains(feature.floor),
|
||||||
|
door: (connections) =>
|
||||||
|
f.level == feature.floor &&
|
||||||
|
connections
|
||||||
|
.map((e) => e.toLowerCase())
|
||||||
|
.contains(feature.building.name.toLowerCase()),
|
||||||
|
orElse: () => f.level == feature.floor))
|
||||||
|
.mapMany((f) => wrap(f, feature.floor, feature.building.name))
|
||||||
|
.toList();
|
||||||
|
} else if (feature is Portal) {
|
||||||
|
adjacentFeatures = allFeatures
|
||||||
|
.where((f) => eq(f.name, feature.to) && f.type is Building)
|
||||||
|
.mapMany((f) => wrap(f, feature.toFloor, feature.to))
|
||||||
|
.toList();
|
||||||
|
} else if (feature is BasicFeature) {
|
||||||
|
adjacentFeatures = allFeatures
|
||||||
|
.where(
|
||||||
|
(f) => eq(f.name, feature.feature.building) && f.type is Building)
|
||||||
|
.mapMany((f) => wrap(f, feature.feature.level!, f.name))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
return adjacentFeatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<GraphFeature> createGraphList(
|
||||||
|
GraphFeature origin, List<Feature> allFeatures,
|
||||||
|
[Set<GraphFeature>? visited]) {
|
||||||
|
// final usedFeatures = <GraphFeature>[origin];
|
||||||
|
|
||||||
|
visited ??= <GraphFeature>{origin};
|
||||||
|
|
||||||
|
final adjacent = findAdjacent(origin, allFeatures);
|
||||||
|
for (final feature in adjacent.asSet()..removeAll(visited)) {
|
||||||
|
visited.add(feature);
|
||||||
|
final deeper = createGraphList(feature, allFeatures, visited);
|
||||||
|
visited.addAll(deeper);
|
||||||
|
}
|
||||||
|
|
||||||
|
return visited.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<GraphFeature, Map<GraphFeature, double>> createGraphMap(
|
||||||
|
GraphFeature origin, List<Feature> allFeatures) {
|
||||||
|
final graphList = createGraphList(origin, allFeatures);
|
||||||
|
final graphMap = <GraphFeature, Map<GraphFeature, double>>{};
|
||||||
|
for (final node in graphList) {
|
||||||
|
final adjacents = node.when(
|
||||||
|
buildingFloor: (floor, building) {
|
||||||
|
return graphList
|
||||||
|
.where((f) =>
|
||||||
|
f is Portal &&
|
||||||
|
eq(f.from, building.name) &&
|
||||||
|
f.fromFloor == floor ||
|
||||||
|
f is BasicFeature &&
|
||||||
|
eq(f.building, building.name) &&
|
||||||
|
f.floor == floor)
|
||||||
|
.map((f) => f.when(
|
||||||
|
portal: (fromFloor, from, toFloor, to, baseFeature) => (
|
||||||
|
f,
|
||||||
|
f.metersTo(node),
|
||||||
|
),
|
||||||
|
basicFeature: (floor, building, feature) =>
|
||||||
|
(f, f.metersTo(node)),
|
||||||
|
buildingFloor: (floor, building) => throw StateError(
|
||||||
|
"BUG: createGraphMap(): BuildingFloors shouldn't "
|
||||||
|
"be matched by BuildingFloors"),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
portal: (fromFloor, from, toFloor, to, baseFeature) {
|
||||||
|
return graphList
|
||||||
|
.where((f) =>
|
||||||
|
f is BuildingFloor &&
|
||||||
|
eq(f.building.name, to) &&
|
||||||
|
f.floor == toFloor)
|
||||||
|
.map((f) => f.when(
|
||||||
|
portal: (fromFloor, from, toFloor, to, baseFeature) =>
|
||||||
|
throw StateError(
|
||||||
|
"BUG: createGraphMap(): Portals shouldn't "
|
||||||
|
"be matched by Portals"),
|
||||||
|
basicFeature: (floor, building, feature) => throw StateError(
|
||||||
|
"BUG: createGraphMap(): BasicFeatures shouldn't "
|
||||||
|
"be matched by BasicFeatures"),
|
||||||
|
buildingFloor: (floor, building) => (
|
||||||
|
f,
|
||||||
|
f.metersTo(node) +
|
||||||
|
5 /* 5 extra meters for all portals. TODO: smarter!*/
|
||||||
|
),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
basicFeature: (floor, building, feature) {
|
||||||
|
return graphList
|
||||||
|
.where((f) =>
|
||||||
|
f is BuildingFloor &&
|
||||||
|
eq(f.building.name, building) &&
|
||||||
|
f.floor == floor)
|
||||||
|
.map((f) => f.when(
|
||||||
|
portal: (fromFloor, from, toFloor, to, baseFeature) =>
|
||||||
|
throw StateError(
|
||||||
|
"BUG: createGraphMap(): Portal shouldn't be matched "
|
||||||
|
"by BasicFeature"),
|
||||||
|
basicFeature: (floor, building, feature) => throw StateError(
|
||||||
|
"BUG: createGraphMap(): BasicFeatures shouldn't "
|
||||||
|
"be matched by BasicFeatures"),
|
||||||
|
buildingFloor: (floor, building) => (f, f.metersTo(node)),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
graphMap[node] =
|
||||||
|
Map.fromEntries(adjacents.map((tup) => MapEntry(tup.$1, tup.$2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return graphMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
WeightedDirectedGraph<GraphFeature, double> createGraph(
|
||||||
|
GraphFeature origin, List<Feature> allFeatures) {
|
||||||
|
final map = createGraphMap(origin, allFeatures);
|
||||||
|
final graph = WeightedDirectedGraph<GraphFeature, double>(
|
||||||
|
map,
|
||||||
|
summation: sum,
|
||||||
|
zero: 0.0,
|
||||||
|
comparator: (a, b) => compareGraphFeatures(a, b),
|
||||||
|
);
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<List<(GraphFeature, double)>> findShortestPath(
|
||||||
|
GraphFeature origin, GraphFeature destination, List<Feature> allFeatures,
|
||||||
|
[heuristicVariant = "zero", heuristicMultiplier = 0.2]) {
|
||||||
|
var graph = createGraphMap(origin, allFeatures);
|
||||||
|
|
||||||
|
if (!(graph.keys.contains(origin) &&
|
||||||
|
graph.values.firstWhereOrNull((vals) => vals.containsKey(destination)) !=
|
||||||
|
null)) {
|
||||||
|
return bail("Origin or destination not in graph");
|
||||||
|
}
|
||||||
|
|
||||||
|
// euclidean distance heuristic
|
||||||
|
|
||||||
|
double Function(GraphFeature) heuristic =
|
||||||
|
(GraphFeature node) => 0.0; // standard zero
|
||||||
|
if (heuristicVariant == "zero") {
|
||||||
|
heuristic = (GraphFeature node) => 0.0;
|
||||||
|
} else if (heuristicVariant == "euclidean") {
|
||||||
|
heuristic =
|
||||||
|
(GraphFeature node) => node.metersTo(destination) * heuristicMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
//heuristic(GraphFeature node) => 0.0;
|
||||||
|
|
||||||
|
// openlist
|
||||||
|
// format: (heuristic, g-val, parent?, node)
|
||||||
|
PriorityQueue<(double, double, GraphFeature?, GraphFeature)> openlist =
|
||||||
|
HeapPriorityQueue(
|
||||||
|
// reverse order (cmp b to a) because lower f-val (shorter distance) is better
|
||||||
|
(a, b) => (b.$1 + b.$2).compareTo((a.$1 + a.$2)),
|
||||||
|
);
|
||||||
|
|
||||||
|
final Map<GraphFeature, (GraphFeature?, double)> bestPathMap = {
|
||||||
|
origin: (null, 0.0)
|
||||||
|
};
|
||||||
|
|
||||||
|
openlist.add((heuristic(origin), 0.0, null, origin));
|
||||||
|
|
||||||
|
// closed list
|
||||||
|
Set<GraphFeature> closedlist = {};
|
||||||
|
|
||||||
|
var cost = 0.0;
|
||||||
|
|
||||||
|
while (openlist.isNotEmpty) {
|
||||||
|
final (f, g, parent, node) = openlist.removeFirst();
|
||||||
|
closedlist.add(node);
|
||||||
|
bestPathMap[node] = (parent, g);
|
||||||
|
if (node == destination) {
|
||||||
|
cost = g;
|
||||||
|
break;
|
||||||
|
// TODO: restore path
|
||||||
|
}
|
||||||
|
|
||||||
|
// expand node
|
||||||
|
final adjacents = graph[node]!;
|
||||||
|
for (final entry in adjacents.entries) {
|
||||||
|
final adjNode = entry.key;
|
||||||
|
final adjCost = entry.value;
|
||||||
|
|
||||||
|
if (closedlist.contains(adjNode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for (final open in openlist.unorderedElements) {
|
||||||
|
if (open.$4 == adjNode) {
|
||||||
|
found = true;
|
||||||
|
if (g + adjCost < open.$2) {
|
||||||
|
openlist.remove(open);
|
||||||
|
openlist.add((
|
||||||
|
open.$1 /* heuristic stays the same */,
|
||||||
|
g + adjCost,
|
||||||
|
adjNode,
|
||||||
|
open.$4
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
openlist.add((
|
||||||
|
f + heuristic(adjNode),
|
||||||
|
g + adjCost,
|
||||||
|
node,
|
||||||
|
adjNode,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bestPathMap.isNotEmpty) {
|
||||||
|
final path = <(GraphFeature, double)>[];
|
||||||
|
(GraphFeature?, double)? currentNode = (destination, cost);
|
||||||
|
while (currentNode?.$1 != null) {
|
||||||
|
final nextNode = bestPathMap[currentNode!.$1];
|
||||||
|
path.insert(
|
||||||
|
0, (currentNode!.$1!, currentNode.$2 - (nextNode?.$2 ?? 0.0)));
|
||||||
|
currentNode = nextNode;
|
||||||
|
}
|
||||||
|
return Ok(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bail("No path found");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compares two [GraphFeature] instances and determines their relative order.
|
||||||
|
///
|
||||||
|
/// The comparison is based on the specific subtypes and properties of the
|
||||||
|
/// [GraphFeature] instances. The comparison logic is as follows:
|
||||||
|
///
|
||||||
|
/// 1. If both instances are [BuildingFloor], they are compared first by the
|
||||||
|
/// building name and then by the floor number.
|
||||||
|
/// 2. If one instance is a [Portal] and the other is a [BuildingFloor] or
|
||||||
|
/// [BasicFeature], the [Portal] is considered greater.
|
||||||
|
/// 3. If both instances are [Portal], they are compared first by the `from`
|
||||||
|
/// property, then by the `to` property, and finally by the `baseFeature` name.
|
||||||
|
/// 4. If one instance is a [BasicFeature] and the other is a [BuildingFloor] or
|
||||||
|
/// [Portal], the [BasicFeature] is considered greater.
|
||||||
|
/// 5. If both instances are [BasicFeature], they are compared first by the
|
||||||
|
/// building name, then by the floor number, and finally by the feature name.
|
||||||
|
///
|
||||||
|
/// Returns a negative value if [a] is considered "less than" [b], a positive
|
||||||
|
/// value if [a] is considered "greater than" [b], and zero if they are considered
|
||||||
|
/// equal.
|
||||||
|
///
|
||||||
|
/// This function can be used as a comparator for sorting or ordering
|
||||||
|
/// [GraphFeature] instances.
|
||||||
|
int compareGraphFeatures(GraphFeature a, GraphFeature b) {
|
||||||
|
return a.when(
|
||||||
|
buildingFloor: (floorA, buildingA) {
|
||||||
|
return b.when(
|
||||||
|
buildingFloor: (floorB, buildingB) {
|
||||||
|
final buildingComparison = buildingA.name.compareTo(buildingB.name);
|
||||||
|
if (buildingComparison != 0) {
|
||||||
|
return buildingComparison;
|
||||||
|
}
|
||||||
|
return floorA.compareTo(floorB);
|
||||||
|
},
|
||||||
|
portal: (fromFloorB, fromB, toFloorB, toB, baseFeatureB) => -1,
|
||||||
|
basicFeature: (floorB, buildingB, featureB) => -1,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
portal: (fromFloorA, fromA, toFloorA, toA, baseFeatureA) {
|
||||||
|
return b.when(
|
||||||
|
buildingFloor: (floorB, buildingB) => 1,
|
||||||
|
portal: (fromFloorB, fromB, toFloorB, toB, baseFeatureB) {
|
||||||
|
final fromComparison = fromA.compareTo(fromB);
|
||||||
|
if (fromComparison != 0) {
|
||||||
|
return fromComparison;
|
||||||
|
}
|
||||||
|
final toComparison = toA.compareTo(toB);
|
||||||
|
if (toComparison != 0) {
|
||||||
|
return toComparison;
|
||||||
|
}
|
||||||
|
return baseFeatureA.name.compareTo(baseFeatureB.name);
|
||||||
|
},
|
||||||
|
basicFeature: (floorB, buildingB, featureB) => -1,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
basicFeature: (floorA, buildingA, featureA) {
|
||||||
|
return b.when(
|
||||||
|
buildingFloor: (floorB, buildingB) => 1,
|
||||||
|
portal: (fromFloorB, fromB, toFloorB, toB, baseFeatureB) => 1,
|
||||||
|
basicFeature: (floorB, buildingB, featureB) {
|
||||||
|
final buildingComparison = buildingA.compareTo(buildingB);
|
||||||
|
if (buildingComparison != 0) {
|
||||||
|
return buildingComparison;
|
||||||
|
}
|
||||||
|
final floorComparison = floorA.compareTo(floorB);
|
||||||
|
if (floorComparison != 0) {
|
||||||
|
return floorComparison;
|
||||||
|
}
|
||||||
|
return featureA.name.compareTo(featureB.name);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
646
lib/nav/graph.freezed.dart
Normal file
646
lib/nav/graph.freezed.dart
Normal file
@ -0,0 +1,646 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'graph.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$GraphFeature {
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function(int floor, Feature building) buildingFloor,
|
||||||
|
required TResult Function(int fromFloor, String from, int toFloor,
|
||||||
|
String to, Feature baseFeature)
|
||||||
|
portal,
|
||||||
|
required TResult Function(int floor, String building, Feature feature)
|
||||||
|
basicFeature,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(int floor, Feature building)? buildingFloor,
|
||||||
|
TResult? Function(int fromFloor, String from, int toFloor, String to,
|
||||||
|
Feature baseFeature)?
|
||||||
|
portal,
|
||||||
|
TResult? Function(int floor, String building, Feature feature)?
|
||||||
|
basicFeature,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function(int floor, Feature building)? buildingFloor,
|
||||||
|
TResult Function(int fromFloor, String from, int toFloor, String to,
|
||||||
|
Feature baseFeature)?
|
||||||
|
portal,
|
||||||
|
TResult Function(int floor, String building, Feature feature)? basicFeature,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(BuildingFloor value) buildingFloor,
|
||||||
|
required TResult Function(Portal value) portal,
|
||||||
|
required TResult Function(BasicFeature value) basicFeature,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(BuildingFloor value)? buildingFloor,
|
||||||
|
TResult? Function(Portal value)? portal,
|
||||||
|
TResult? Function(BasicFeature value)? basicFeature,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(BuildingFloor value)? buildingFloor,
|
||||||
|
TResult Function(Portal value)? portal,
|
||||||
|
TResult Function(BasicFeature value)? basicFeature,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $GraphFeatureCopyWith<$Res> {
|
||||||
|
factory $GraphFeatureCopyWith(
|
||||||
|
GraphFeature value, $Res Function(GraphFeature) then) =
|
||||||
|
_$GraphFeatureCopyWithImpl<$Res, GraphFeature>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$GraphFeatureCopyWithImpl<$Res, $Val extends GraphFeature>
|
||||||
|
implements $GraphFeatureCopyWith<$Res> {
|
||||||
|
_$GraphFeatureCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$BuildingFloorImplCopyWith<$Res> {
|
||||||
|
factory _$$BuildingFloorImplCopyWith(
|
||||||
|
_$BuildingFloorImpl value, $Res Function(_$BuildingFloorImpl) then) =
|
||||||
|
__$$BuildingFloorImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call({int floor, Feature building});
|
||||||
|
|
||||||
|
$FeatureCopyWith<$Res> get building;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$BuildingFloorImplCopyWithImpl<$Res>
|
||||||
|
extends _$GraphFeatureCopyWithImpl<$Res, _$BuildingFloorImpl>
|
||||||
|
implements _$$BuildingFloorImplCopyWith<$Res> {
|
||||||
|
__$$BuildingFloorImplCopyWithImpl(
|
||||||
|
_$BuildingFloorImpl _value, $Res Function(_$BuildingFloorImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? floor = null,
|
||||||
|
Object? building = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$BuildingFloorImpl(
|
||||||
|
null == floor
|
||||||
|
? _value.floor
|
||||||
|
: floor // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
null == building
|
||||||
|
? _value.building
|
||||||
|
: building // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Feature,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$FeatureCopyWith<$Res> get building {
|
||||||
|
return $FeatureCopyWith<$Res>(_value.building, (value) {
|
||||||
|
return _then(_value.copyWith(building: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$BuildingFloorImpl extends BuildingFloor {
|
||||||
|
const _$BuildingFloorImpl(this.floor, this.building) : super._();
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int floor;
|
||||||
|
@override
|
||||||
|
final Feature building;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'GraphFeature.buildingFloor(floor: $floor, building: $building)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$BuildingFloorImpl &&
|
||||||
|
(identical(other.floor, floor) || other.floor == floor) &&
|
||||||
|
(identical(other.building, building) ||
|
||||||
|
other.building == building));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, floor, building);
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$BuildingFloorImplCopyWith<_$BuildingFloorImpl> get copyWith =>
|
||||||
|
__$$BuildingFloorImplCopyWithImpl<_$BuildingFloorImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function(int floor, Feature building) buildingFloor,
|
||||||
|
required TResult Function(int fromFloor, String from, int toFloor,
|
||||||
|
String to, Feature baseFeature)
|
||||||
|
portal,
|
||||||
|
required TResult Function(int floor, String building, Feature feature)
|
||||||
|
basicFeature,
|
||||||
|
}) {
|
||||||
|
return buildingFloor(floor, building);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(int floor, Feature building)? buildingFloor,
|
||||||
|
TResult? Function(int fromFloor, String from, int toFloor, String to,
|
||||||
|
Feature baseFeature)?
|
||||||
|
portal,
|
||||||
|
TResult? Function(int floor, String building, Feature feature)?
|
||||||
|
basicFeature,
|
||||||
|
}) {
|
||||||
|
return buildingFloor?.call(floor, building);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function(int floor, Feature building)? buildingFloor,
|
||||||
|
TResult Function(int fromFloor, String from, int toFloor, String to,
|
||||||
|
Feature baseFeature)?
|
||||||
|
portal,
|
||||||
|
TResult Function(int floor, String building, Feature feature)? basicFeature,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (buildingFloor != null) {
|
||||||
|
return buildingFloor(floor, building);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(BuildingFloor value) buildingFloor,
|
||||||
|
required TResult Function(Portal value) portal,
|
||||||
|
required TResult Function(BasicFeature value) basicFeature,
|
||||||
|
}) {
|
||||||
|
return buildingFloor(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(BuildingFloor value)? buildingFloor,
|
||||||
|
TResult? Function(Portal value)? portal,
|
||||||
|
TResult? Function(BasicFeature value)? basicFeature,
|
||||||
|
}) {
|
||||||
|
return buildingFloor?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(BuildingFloor value)? buildingFloor,
|
||||||
|
TResult Function(Portal value)? portal,
|
||||||
|
TResult Function(BasicFeature value)? basicFeature,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (buildingFloor != null) {
|
||||||
|
return buildingFloor(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class BuildingFloor extends GraphFeature {
|
||||||
|
const factory BuildingFloor(final int floor, final Feature building) =
|
||||||
|
_$BuildingFloorImpl;
|
||||||
|
const BuildingFloor._() : super._();
|
||||||
|
|
||||||
|
int get floor;
|
||||||
|
Feature get building;
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$BuildingFloorImplCopyWith<_$BuildingFloorImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$PortalImplCopyWith<$Res> {
|
||||||
|
factory _$$PortalImplCopyWith(
|
||||||
|
_$PortalImpl value, $Res Function(_$PortalImpl) then) =
|
||||||
|
__$$PortalImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int fromFloor,
|
||||||
|
String from,
|
||||||
|
int toFloor,
|
||||||
|
String to,
|
||||||
|
Feature baseFeature});
|
||||||
|
|
||||||
|
$FeatureCopyWith<$Res> get baseFeature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$PortalImplCopyWithImpl<$Res>
|
||||||
|
extends _$GraphFeatureCopyWithImpl<$Res, _$PortalImpl>
|
||||||
|
implements _$$PortalImplCopyWith<$Res> {
|
||||||
|
__$$PortalImplCopyWithImpl(
|
||||||
|
_$PortalImpl _value, $Res Function(_$PortalImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? fromFloor = null,
|
||||||
|
Object? from = null,
|
||||||
|
Object? toFloor = null,
|
||||||
|
Object? to = null,
|
||||||
|
Object? baseFeature = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$PortalImpl(
|
||||||
|
null == fromFloor
|
||||||
|
? _value.fromFloor
|
||||||
|
: fromFloor // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
null == from
|
||||||
|
? _value.from
|
||||||
|
: from // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
null == toFloor
|
||||||
|
? _value.toFloor
|
||||||
|
: toFloor // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
null == to
|
||||||
|
? _value.to
|
||||||
|
: to // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
null == baseFeature
|
||||||
|
? _value.baseFeature
|
||||||
|
: baseFeature // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Feature,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$FeatureCopyWith<$Res> get baseFeature {
|
||||||
|
return $FeatureCopyWith<$Res>(_value.baseFeature, (value) {
|
||||||
|
return _then(_value.copyWith(baseFeature: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$PortalImpl extends Portal {
|
||||||
|
const _$PortalImpl(
|
||||||
|
this.fromFloor, this.from, this.toFloor, this.to, this.baseFeature)
|
||||||
|
: super._();
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int fromFloor;
|
||||||
|
@override
|
||||||
|
final String from;
|
||||||
|
@override
|
||||||
|
final int toFloor;
|
||||||
|
@override
|
||||||
|
final String to;
|
||||||
|
@override
|
||||||
|
final Feature baseFeature;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'GraphFeature.portal(fromFloor: $fromFloor, from: $from, toFloor: $toFloor, to: $to, baseFeature: $baseFeature)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$PortalImpl &&
|
||||||
|
(identical(other.fromFloor, fromFloor) ||
|
||||||
|
other.fromFloor == fromFloor) &&
|
||||||
|
(identical(other.from, from) || other.from == from) &&
|
||||||
|
(identical(other.toFloor, toFloor) || other.toFloor == toFloor) &&
|
||||||
|
(identical(other.to, to) || other.to == to) &&
|
||||||
|
(identical(other.baseFeature, baseFeature) ||
|
||||||
|
other.baseFeature == baseFeature));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
Object.hash(runtimeType, fromFloor, from, toFloor, to, baseFeature);
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$PortalImplCopyWith<_$PortalImpl> get copyWith =>
|
||||||
|
__$$PortalImplCopyWithImpl<_$PortalImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function(int floor, Feature building) buildingFloor,
|
||||||
|
required TResult Function(int fromFloor, String from, int toFloor,
|
||||||
|
String to, Feature baseFeature)
|
||||||
|
portal,
|
||||||
|
required TResult Function(int floor, String building, Feature feature)
|
||||||
|
basicFeature,
|
||||||
|
}) {
|
||||||
|
return portal(fromFloor, from, toFloor, to, baseFeature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(int floor, Feature building)? buildingFloor,
|
||||||
|
TResult? Function(int fromFloor, String from, int toFloor, String to,
|
||||||
|
Feature baseFeature)?
|
||||||
|
portal,
|
||||||
|
TResult? Function(int floor, String building, Feature feature)?
|
||||||
|
basicFeature,
|
||||||
|
}) {
|
||||||
|
return portal?.call(fromFloor, from, toFloor, to, baseFeature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function(int floor, Feature building)? buildingFloor,
|
||||||
|
TResult Function(int fromFloor, String from, int toFloor, String to,
|
||||||
|
Feature baseFeature)?
|
||||||
|
portal,
|
||||||
|
TResult Function(int floor, String building, Feature feature)? basicFeature,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (portal != null) {
|
||||||
|
return portal(fromFloor, from, toFloor, to, baseFeature);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(BuildingFloor value) buildingFloor,
|
||||||
|
required TResult Function(Portal value) portal,
|
||||||
|
required TResult Function(BasicFeature value) basicFeature,
|
||||||
|
}) {
|
||||||
|
return portal(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(BuildingFloor value)? buildingFloor,
|
||||||
|
TResult? Function(Portal value)? portal,
|
||||||
|
TResult? Function(BasicFeature value)? basicFeature,
|
||||||
|
}) {
|
||||||
|
return portal?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(BuildingFloor value)? buildingFloor,
|
||||||
|
TResult Function(Portal value)? portal,
|
||||||
|
TResult Function(BasicFeature value)? basicFeature,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (portal != null) {
|
||||||
|
return portal(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Portal extends GraphFeature {
|
||||||
|
const factory Portal(
|
||||||
|
final int fromFloor,
|
||||||
|
final String from,
|
||||||
|
final int toFloor,
|
||||||
|
final String to,
|
||||||
|
final Feature baseFeature) = _$PortalImpl;
|
||||||
|
const Portal._() : super._();
|
||||||
|
|
||||||
|
int get fromFloor;
|
||||||
|
String get from;
|
||||||
|
int get toFloor;
|
||||||
|
String get to;
|
||||||
|
Feature get baseFeature;
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$PortalImplCopyWith<_$PortalImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$BasicFeatureImplCopyWith<$Res> {
|
||||||
|
factory _$$BasicFeatureImplCopyWith(
|
||||||
|
_$BasicFeatureImpl value, $Res Function(_$BasicFeatureImpl) then) =
|
||||||
|
__$$BasicFeatureImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call({int floor, String building, Feature feature});
|
||||||
|
|
||||||
|
$FeatureCopyWith<$Res> get feature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$BasicFeatureImplCopyWithImpl<$Res>
|
||||||
|
extends _$GraphFeatureCopyWithImpl<$Res, _$BasicFeatureImpl>
|
||||||
|
implements _$$BasicFeatureImplCopyWith<$Res> {
|
||||||
|
__$$BasicFeatureImplCopyWithImpl(
|
||||||
|
_$BasicFeatureImpl _value, $Res Function(_$BasicFeatureImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? floor = null,
|
||||||
|
Object? building = null,
|
||||||
|
Object? feature = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$BasicFeatureImpl(
|
||||||
|
null == floor
|
||||||
|
? _value.floor
|
||||||
|
: floor // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
null == building
|
||||||
|
? _value.building
|
||||||
|
: building // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
null == feature
|
||||||
|
? _value.feature
|
||||||
|
: feature // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Feature,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$FeatureCopyWith<$Res> get feature {
|
||||||
|
return $FeatureCopyWith<$Res>(_value.feature, (value) {
|
||||||
|
return _then(_value.copyWith(feature: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$BasicFeatureImpl extends BasicFeature {
|
||||||
|
const _$BasicFeatureImpl(this.floor, this.building, this.feature) : super._();
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int floor;
|
||||||
|
@override
|
||||||
|
final String building;
|
||||||
|
@override
|
||||||
|
final Feature feature;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'GraphFeature.basicFeature(floor: $floor, building: $building, feature: $feature)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$BasicFeatureImpl &&
|
||||||
|
(identical(other.floor, floor) || other.floor == floor) &&
|
||||||
|
(identical(other.building, building) ||
|
||||||
|
other.building == building) &&
|
||||||
|
(identical(other.feature, feature) || other.feature == feature));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, floor, building, feature);
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$BasicFeatureImplCopyWith<_$BasicFeatureImpl> get copyWith =>
|
||||||
|
__$$BasicFeatureImplCopyWithImpl<_$BasicFeatureImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function(int floor, Feature building) buildingFloor,
|
||||||
|
required TResult Function(int fromFloor, String from, int toFloor,
|
||||||
|
String to, Feature baseFeature)
|
||||||
|
portal,
|
||||||
|
required TResult Function(int floor, String building, Feature feature)
|
||||||
|
basicFeature,
|
||||||
|
}) {
|
||||||
|
return basicFeature(floor, building, feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(int floor, Feature building)? buildingFloor,
|
||||||
|
TResult? Function(int fromFloor, String from, int toFloor, String to,
|
||||||
|
Feature baseFeature)?
|
||||||
|
portal,
|
||||||
|
TResult? Function(int floor, String building, Feature feature)?
|
||||||
|
basicFeature,
|
||||||
|
}) {
|
||||||
|
return basicFeature?.call(floor, building, feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function(int floor, Feature building)? buildingFloor,
|
||||||
|
TResult Function(int fromFloor, String from, int toFloor, String to,
|
||||||
|
Feature baseFeature)?
|
||||||
|
portal,
|
||||||
|
TResult Function(int floor, String building, Feature feature)? basicFeature,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (basicFeature != null) {
|
||||||
|
return basicFeature(floor, building, feature);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(BuildingFloor value) buildingFloor,
|
||||||
|
required TResult Function(Portal value) portal,
|
||||||
|
required TResult Function(BasicFeature value) basicFeature,
|
||||||
|
}) {
|
||||||
|
return basicFeature(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(BuildingFloor value)? buildingFloor,
|
||||||
|
TResult? Function(Portal value)? portal,
|
||||||
|
TResult? Function(BasicFeature value)? basicFeature,
|
||||||
|
}) {
|
||||||
|
return basicFeature?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(BuildingFloor value)? buildingFloor,
|
||||||
|
TResult Function(Portal value)? portal,
|
||||||
|
TResult Function(BasicFeature value)? basicFeature,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (basicFeature != null) {
|
||||||
|
return basicFeature(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class BasicFeature extends GraphFeature {
|
||||||
|
const factory BasicFeature(
|
||||||
|
final int floor, final String building, final Feature feature) =
|
||||||
|
_$BasicFeatureImpl;
|
||||||
|
const BasicFeature._() : super._();
|
||||||
|
|
||||||
|
int get floor;
|
||||||
|
String get building;
|
||||||
|
Feature get feature;
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$BasicFeatureImplCopyWith<_$BasicFeatureImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:uninav/components/drawer.dart';
|
|
||||||
import 'package:uninav/components/hamburger_menu.dart';
|
|
||||||
|
|
||||||
class SettingsPage extends StatelessWidget {
|
|
||||||
const SettingsPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text('Settings'),
|
|
||||||
leading: HamburgerMenu(),
|
|
||||||
),
|
|
||||||
drawer: MyDrawer(),
|
|
||||||
body: const Center(
|
|
||||||
child: Text('TODO'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
54
pubspec.lock
54
pubspec.lock
@ -154,7 +154,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "4.10.0"
|
version: "4.10.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||||
@ -185,14 +185,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.8"
|
version: "1.0.8"
|
||||||
dart_earcut:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: dart_earcut
|
|
||||||
sha256: "41b493147e30a051efb2da1e3acb7f38fe0db60afba24ac1ea5684cee272721e"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.0"
|
|
||||||
dart_style:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -209,6 +201,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
|
directed_graph:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: directed_graph
|
||||||
|
sha256: fcb45029b4a5089d383b79056b6716d6bf5af79b8e7dbac4b61d26af46410548
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.3"
|
||||||
|
exception_templates:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: exception_templates
|
||||||
|
sha256: "517f7c770da690073663f867ee2057ae2f4ffb28edae9da9faa624aa29ac76eb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.1"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -217,6 +225,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
|
fast_immutable_collections:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: fast_immutable_collections
|
||||||
|
sha256: "38fbc50df5b219dcfb83ebbc3275ec09872530ca1153858fc56fceadb310d037"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "10.2.2"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -258,10 +274,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_map
|
name: flutter_map
|
||||||
sha256: bee8c5bacb49a68aabcf6009c050a8b3b07ac75403f29f741d8c00d4a725e086
|
sha256: cda8d72135b697f519287258b5294a57ce2f2a5ebf234f0e406aad4dc14c9399
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.0-dev.1"
|
version: "6.1.0"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -424,6 +440,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.1"
|
version: "0.9.1"
|
||||||
|
lazy_memo:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lazy_memo
|
||||||
|
sha256: dcb30b4184a6d767e1d779d74ce784d752d38313b8fb4bad6b659ae7af4bb34d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.3"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -640,6 +664,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.3"
|
version: "1.2.3"
|
||||||
|
quote_buffer:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: quote_buffer
|
||||||
|
sha256: c4cd07e55ed1b1645a1cc74278a03b2a642c9f6ea3c0528d51827fdd320acf87
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.6"
|
||||||
rust_core:
|
rust_core:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -39,7 +39,8 @@ dependencies:
|
|||||||
yaml: ^3.1.2
|
yaml: ^3.1.2
|
||||||
surrealdb: ^0.8.0
|
surrealdb: ^0.8.0
|
||||||
# geojson: ^1.0.0
|
# geojson: ^1.0.0
|
||||||
flutter_map: 7.0.0-dev.1
|
# flutter_map: 7.0.0-dev.1
|
||||||
|
flutter_map: ^6.0.0
|
||||||
# flutter_map: ^4.0.0
|
# flutter_map: ^4.0.0
|
||||||
latlong2: ^0.9.0
|
latlong2: ^0.9.0
|
||||||
# latlong2: ^0.8.0
|
# latlong2: ^0.8.0
|
||||||
@ -53,6 +54,9 @@ dependencies:
|
|||||||
isar: ^3.1.0+1
|
isar: ^3.1.0+1
|
||||||
isar_flutter_libs: ^3.1.0+1
|
isar_flutter_libs: ^3.1.0+1
|
||||||
path_provider: ^2.1.3
|
path_provider: ^2.1.3
|
||||||
|
directed_graph: ^0.4.3
|
||||||
|
fast_immutable_collections: ^10.2.2
|
||||||
|
collection: ^1.18.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
176
test/graph_tests.dart
Normal file
176
test/graph_tests.dart
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:uninav/controllers/map_controller.dart';
|
||||||
|
import 'package:uninav/data/geo/model.dart';
|
||||||
|
import 'package:uninav/nav/graph.dart';
|
||||||
|
import 'package:uninav/util/util.dart';
|
||||||
|
|
||||||
|
String formatGraphFeature(GraphFeature feature) {
|
||||||
|
return feature.when(
|
||||||
|
buildingFloor: (floor, building) => "(bfl ${building.name}:$floor)",
|
||||||
|
portal: (fromFloor, from, toFloor, to, baseFeat) {
|
||||||
|
return "(p ${formatFeatureTitle(baseFeat)} $from:$fromFloor -> $to:$toFloor)";
|
||||||
|
},
|
||||||
|
basicFeature: (lv, building, bf) =>
|
||||||
|
"(bf ${formatFeatureTitle(bf)} $building:$lv)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
group('findAdjacent', () {
|
||||||
|
late MyMapController mapController;
|
||||||
|
late List<Feature> allFeatures;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
// Initialize the MyMapController and load the GeoJSON data
|
||||||
|
mapController = MyMapController();
|
||||||
|
await mapController.loadGeoJson(
|
||||||
|
await rootBundle.loadString('assets/geo/uulm_beta.geojson'));
|
||||||
|
allFeatures = mapController.features;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('generates a graph', () {
|
||||||
|
// Find a building feature
|
||||||
|
// final buildingFeature = allFeatures.firstWhere((f) => f.type is Building);
|
||||||
|
final buildingFeature = allFeatures
|
||||||
|
.firstWhere((f) => f.type is Building && eq(f.name, 'o28'));
|
||||||
|
|
||||||
|
final graph = createGraph(
|
||||||
|
wrap(buildingFeature, 2, buildingFeature.name).first, allFeatures);
|
||||||
|
// print(graph);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('generates a graph map', () {
|
||||||
|
// Find a building feature
|
||||||
|
// final buildingFeature = allFeatures.firstWhere((f) => f.type is Building);
|
||||||
|
final buildingFeature = allFeatures
|
||||||
|
.firstWhere((f) => f.type is Building && eq(f.name, 'o28'));
|
||||||
|
|
||||||
|
final graph = createGraphMap(
|
||||||
|
wrap(buildingFeature, 2, buildingFeature.name).first, allFeatures);
|
||||||
|
|
||||||
|
print(graph.entries
|
||||||
|
.map((entry) => "${formatGraphFeature(entry.key)}: "
|
||||||
|
"{${entry.value.entries.map((e) => "${formatGraphFeature(entry.key)}: ${entry.value.map((key, value) => MapEntry(formatGraphFeature(key), value))}").join(', ')}")
|
||||||
|
.join('\n'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('generates a graph list', () {
|
||||||
|
// Find a building feature
|
||||||
|
// final buildingFeature = allFeatures.firstWhere((f) => f.type is Building);
|
||||||
|
final buildingFeature = allFeatures
|
||||||
|
.firstWhere((f) => f.type is Building && eq(f.name, 'o28'));
|
||||||
|
|
||||||
|
final graph = createGraphList(
|
||||||
|
wrap(buildingFeature, 2, buildingFeature.name).first, allFeatures);
|
||||||
|
var text = graph.map(formatGraphFeature).join(' ');
|
||||||
|
print(text);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('finds adjacent features for a building', () {
|
||||||
|
// Find a building feature
|
||||||
|
// final buildingFeature = allFeatures.firstWhere((f) => f.type is Building);
|
||||||
|
final buildingFeature = allFeatures
|
||||||
|
.firstWhere((f) => f.type is Building && eq(f.name, 'o28'));
|
||||||
|
|
||||||
|
// Find adjacent features for the building
|
||||||
|
final adjacentFeatures = findAdjacent(
|
||||||
|
wrap(buildingFeature, 2, buildingFeature.name).first, allFeatures);
|
||||||
|
|
||||||
|
final doorPortal = adjacentFeatures
|
||||||
|
.firstWhere((f) => f is Portal && f.baseFeature.type is Door);
|
||||||
|
print(
|
||||||
|
"adjacent for building ${buildingFeature.name}: \n${adjacentFeatures.map((e) => "${e.toString()}\n")}");
|
||||||
|
print(adjacentFeatures.map(unwrap).map(formatFeatureTitle));
|
||||||
|
|
||||||
|
final doorAdjacentFeatures = findAdjacent(doorPortal, allFeatures);
|
||||||
|
|
||||||
|
print("\n\ndoor $doorPortal : ");
|
||||||
|
print(doorAdjacentFeatures.map((e) => "${e.toString()}\n"));
|
||||||
|
print(doorAdjacentFeatures.map(unwrap).map(formatFeatureTitle));
|
||||||
|
|
||||||
|
final stairsPortal = adjacentFeatures
|
||||||
|
.firstWhere((f) => f is Portal && f.baseFeature.type is Stairs);
|
||||||
|
|
||||||
|
final stairsAdjacentFeatures = findAdjacent(stairsPortal, allFeatures);
|
||||||
|
|
||||||
|
print("\n\nstairs $stairsPortal : ");
|
||||||
|
print(stairsAdjacentFeatures.map((e) => "${e.toString()}\n"));
|
||||||
|
print(stairsAdjacentFeatures.map(unwrap).map(formatFeatureTitle));
|
||||||
|
|
||||||
|
final baseFeature = adjacentFeatures.firstWhere((f) => f is BasicFeature);
|
||||||
|
|
||||||
|
final baseAdjacentFeatures = findAdjacent(baseFeature, allFeatures);
|
||||||
|
|
||||||
|
print("\n\nbase $baseFeature : ");
|
||||||
|
print(baseAdjacentFeatures.map((e) => "${e.toString()}\n"));
|
||||||
|
print(baseAdjacentFeatures.map(unwrap).map(formatFeatureTitle));
|
||||||
|
|
||||||
|
// Check if all adjacent features are in the same building
|
||||||
|
expect(
|
||||||
|
true, // TODO
|
||||||
|
true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('tries to find a path through the graph using own method', () async {
|
||||||
|
// Find a building feature
|
||||||
|
// final buildingFeature = allFeatures.firstWhere((f) => f.type is Building);
|
||||||
|
final startFeature = allFeatures
|
||||||
|
.firstWhere((f) => f.type is Building && eq(f.name, 'o28'));
|
||||||
|
|
||||||
|
// final endFeature = allFeatures
|
||||||
|
// .firstWhere((f) => f.type is Building && eq(f.name, 'o25'));
|
||||||
|
|
||||||
|
final endFeature = allFeatures
|
||||||
|
.firstWhere((f) => f.type is LectureHall && eq(f.name, 'H1'));
|
||||||
|
|
||||||
|
print(endFeature);
|
||||||
|
|
||||||
|
final path = findShortestPath(
|
||||||
|
wrap(startFeature, 2, startFeature.name).first,
|
||||||
|
// wrap(endFeature, 2, endFeature.name).first,
|
||||||
|
wrap(endFeature, endFeature.level!, endFeature.building!).first,
|
||||||
|
allFeatures,
|
||||||
|
);
|
||||||
|
print(path
|
||||||
|
.unwrap()
|
||||||
|
.map((e) => "${formatGraphFeature(e.$1)} (${e.$2}m)")
|
||||||
|
.join(' -> '));
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
test('tries to find a path through the graph', () async {
|
||||||
|
// Find a building feature
|
||||||
|
// final buildingFeature = allFeatures.firstWhere((f) => f.type is Building);
|
||||||
|
final startFeature = allFeatures
|
||||||
|
.firstWhere((f) => f.type is Building && eq(f.name, 'o28'));
|
||||||
|
|
||||||
|
final endFeature = allFeatures
|
||||||
|
.firstWhere((f) => f.type is Building && eq(f.name, 'o25'));
|
||||||
|
|
||||||
|
// final endFeature = allFeatures
|
||||||
|
// .firstWhere((f) => f.type is LectureHall && eq(f.name, 'H1'));
|
||||||
|
|
||||||
|
final graph = createGraph(
|
||||||
|
wrap(startFeature, 2, startFeature.name).first, allFeatures);
|
||||||
|
|
||||||
|
final path = await Future.any([
|
||||||
|
Future.delayed(const Duration(seconds: 5),
|
||||||
|
() => throw TimeoutException('Test timed out after 5 seconds')),
|
||||||
|
Future(() => graph.shortestPath(
|
||||||
|
wrap(startFeature, 1, startFeature.name).first,
|
||||||
|
wrap(endFeature, 2, endFeature.name).first,
|
||||||
|
//wrap(endFeature, endFeature.level!, endFeature.building!).first,
|
||||||
|
)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
print(path.map(formatGraphFeature).join('\n'));
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user