feat: complete working navigation functions
This commit is contained in:
		| @ -2,7 +2,6 @@ import 'dart:convert'; | ||||
|  | ||||
| import 'package:anyhow/anyhow.dart'; | ||||
| import 'package:flutter_map/flutter_map.dart'; | ||||
| import 'package:flutter_map/src/gestures/positioned_tap_detector_2.dart'; | ||||
| import 'package:geojson_vi/geojson_vi.dart'; | ||||
| import 'package:get/get.dart'; | ||||
| import 'package:latlong2/latlong.dart'; | ||||
| @ -95,8 +94,8 @@ class MyMapController extends GetxController { | ||||
|         // print(feature?.properties); | ||||
|         if (feature == null) continue; | ||||
|         // print(feature.properties); | ||||
|         final parsed = parseFeature( | ||||
|             feature.properties ?? <String, dynamic>{}, feature.geometry); | ||||
|         final parsed = parseFeature(feature.properties ?? <String, dynamic>{}, | ||||
|             feature.geometry, feature.id); | ||||
|         if (parsed case Ok(:final ok)) { | ||||
|           featuresList.add(ok); | ||||
|         } | ||||
|  | ||||
| @ -20,6 +20,7 @@ class Feature with _$Feature { | ||||
|     required GeoJSONGeometry geometry, | ||||
|     int? level, | ||||
|     String? building, | ||||
|     required String id, | ||||
|   }) = _Feature; | ||||
|  | ||||
|   bool isPolygon() { | ||||
|  | ||||
| @ -22,6 +22,7 @@ mixin _$Feature { | ||||
|   GeoJSONGeometry get geometry => throw _privateConstructorUsedError; | ||||
|   int? get level => throw _privateConstructorUsedError; | ||||
|   String? get building => throw _privateConstructorUsedError; | ||||
|   String get id => throw _privateConstructorUsedError; | ||||
|  | ||||
|   @JsonKey(ignore: true) | ||||
|   $FeatureCopyWith<Feature> get copyWith => throw _privateConstructorUsedError; | ||||
| @ -38,7 +39,8 @@ abstract class $FeatureCopyWith<$Res> { | ||||
|       String? description, | ||||
|       GeoJSONGeometry geometry, | ||||
|       int? level, | ||||
|       String? building}); | ||||
|       String? building, | ||||
|       String id}); | ||||
|  | ||||
|   $FeatureTypeCopyWith<$Res> get type; | ||||
| } | ||||
| @ -62,6 +64,7 @@ class _$FeatureCopyWithImpl<$Res, $Val extends Feature> | ||||
|     Object? geometry = null, | ||||
|     Object? level = freezed, | ||||
|     Object? building = freezed, | ||||
|     Object? id = null, | ||||
|   }) { | ||||
|     return _then(_value.copyWith( | ||||
|       name: null == name | ||||
| @ -88,6 +91,10 @@ class _$FeatureCopyWithImpl<$Res, $Val extends Feature> | ||||
|           ? _value.building | ||||
|           : building // ignore: cast_nullable_to_non_nullable | ||||
|               as String?, | ||||
|       id: null == id | ||||
|           ? _value.id | ||||
|           : id // ignore: cast_nullable_to_non_nullable | ||||
|               as String, | ||||
|     ) as $Val); | ||||
|   } | ||||
|  | ||||
| @ -113,7 +120,8 @@ abstract class _$$FeatureImplCopyWith<$Res> implements $FeatureCopyWith<$Res> { | ||||
|       String? description, | ||||
|       GeoJSONGeometry geometry, | ||||
|       int? level, | ||||
|       String? building}); | ||||
|       String? building, | ||||
|       String id}); | ||||
|  | ||||
|   @override | ||||
|   $FeatureTypeCopyWith<$Res> get type; | ||||
| @ -136,6 +144,7 @@ class __$$FeatureImplCopyWithImpl<$Res> | ||||
|     Object? geometry = null, | ||||
|     Object? level = freezed, | ||||
|     Object? building = freezed, | ||||
|     Object? id = null, | ||||
|   }) { | ||||
|     return _then(_$FeatureImpl( | ||||
|       name: null == name | ||||
| @ -162,6 +171,10 @@ class __$$FeatureImplCopyWithImpl<$Res> | ||||
|           ? _value.building | ||||
|           : building // ignore: cast_nullable_to_non_nullable | ||||
|               as String?, | ||||
|       id: null == id | ||||
|           ? _value.id | ||||
|           : id // ignore: cast_nullable_to_non_nullable | ||||
|               as String, | ||||
|     )); | ||||
|   } | ||||
| } | ||||
| @ -175,7 +188,8 @@ class _$FeatureImpl extends _Feature { | ||||
|       this.description, | ||||
|       required this.geometry, | ||||
|       this.level, | ||||
|       this.building}) | ||||
|       this.building, | ||||
|       required this.id}) | ||||
|       : super._(); | ||||
|  | ||||
|   @override | ||||
| @ -190,10 +204,12 @@ class _$FeatureImpl extends _Feature { | ||||
|   final int? level; | ||||
|   @override | ||||
|   final String? building; | ||||
|   @override | ||||
|   final String id; | ||||
|  | ||||
|   @override | ||||
|   String toString() { | ||||
|     return 'Feature(name: $name, type: $type, description: $description, geometry: $geometry, level: $level, building: $building)'; | ||||
|     return 'Feature(name: $name, type: $type, description: $description, geometry: $geometry, level: $level, building: $building, id: $id)'; | ||||
|   } | ||||
|  | ||||
|   @override | ||||
| @ -209,12 +225,13 @@ class _$FeatureImpl extends _Feature { | ||||
|                 other.geometry == geometry) && | ||||
|             (identical(other.level, level) || other.level == level) && | ||||
|             (identical(other.building, building) || | ||||
|                 other.building == building)); | ||||
|                 other.building == building) && | ||||
|             (identical(other.id, id) || other.id == id)); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   int get hashCode => Object.hash( | ||||
|       runtimeType, name, type, description, geometry, level, building); | ||||
|       runtimeType, name, type, description, geometry, level, building, id); | ||||
|  | ||||
|   @JsonKey(ignore: true) | ||||
|   @override | ||||
| @ -230,7 +247,8 @@ abstract class _Feature extends Feature { | ||||
|       final String? description, | ||||
|       required final GeoJSONGeometry geometry, | ||||
|       final int? level, | ||||
|       final String? building}) = _$FeatureImpl; | ||||
|       final String? building, | ||||
|       required final String id}) = _$FeatureImpl; | ||||
|   const _Feature._() : super._(); | ||||
|  | ||||
|   @override | ||||
| @ -246,6 +264,8 @@ abstract class _Feature extends Feature { | ||||
|   @override | ||||
|   String? get building; | ||||
|   @override | ||||
|   String get id; | ||||
|   @override | ||||
|   @JsonKey(ignore: true) | ||||
|   _$$FeatureImplCopyWith<_$FeatureImpl> get copyWith => | ||||
|       throw _privateConstructorUsedError; | ||||
|  | ||||
| @ -4,7 +4,7 @@ import 'package:uninav/data/geo/model.dart'; | ||||
| import 'package:yaml/yaml.dart'; | ||||
|  | ||||
| Result<Feature> parseFeature( | ||||
|     Map<String, dynamic> properties, GeoJSONGeometry geometry) { | ||||
|     Map<String, dynamic> properties, GeoJSONGeometry geometry, String id) { | ||||
|   final name = properties['name'] as String?; | ||||
|   final description_yaml = properties['description'] as String? ?? ''; | ||||
|   final layer = properties['layer'] as String?; | ||||
| @ -110,6 +110,7 @@ Result<Feature> parseFeature( | ||||
|     geometry: geometry, | ||||
|     level: level, | ||||
|     building: building, | ||||
|     id: id, | ||||
|   )); | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -1,11 +1,11 @@ | ||||
| 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:flutter/foundation.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:rust_core/iter.dart'; | ||||
| import 'package:uninav/data/geo/model.dart'; | ||||
| import 'package:uninav/util/geojson_util.dart'; | ||||
| import 'package:uninav/util/util.dart'; | ||||
| @ -38,6 +38,12 @@ class GraphFeature with _$GraphFeature { | ||||
|  | ||||
|   double metersTo(GraphFeature other) => distanceTo(other, "meters"); | ||||
|  | ||||
|   String get id => when( | ||||
|         buildingFloor: (floor, building) => building.id, | ||||
|         portal: (fromFloor, from, toFloor, to, baseFeature) => baseFeature.id, | ||||
|         basicFeature: (floor, building, feature) => feature.id, | ||||
|       ); | ||||
|  | ||||
|   @override | ||||
|   String toString() { | ||||
|     return when( | ||||
| @ -48,9 +54,102 @@ class GraphFeature with _$GraphFeature { | ||||
|           'Feature (${formatFeatureTitle(feature)} ($building:$floor))', | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   int get hashCode { | ||||
|     return when( | ||||
|       buildingFloor: (floor, building) => Object.hash(floor, building), | ||||
|       portal: (fromFloor, from, toFloor, to, baseFeature) => | ||||
|           Object.hash(fromFloor, from, toFloor, to, baseFeature), | ||||
|       basicFeature: (floor, building, feature) => | ||||
|           Object.hash(floor, building, feature), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   bool operator ==(Object other) { | ||||
|     if (identical(this, other)) return true; | ||||
|  | ||||
|     return other is GraphFeature && | ||||
|         other.when( | ||||
|           buildingFloor: (floor, building) => | ||||
|               this is BuildingFloor && | ||||
|               (this as BuildingFloor).floor == floor && | ||||
|               (this as BuildingFloor).building == building, | ||||
|           portal: (fromFloor, from, toFloor, to, baseFeature) => | ||||
|               this is Portal && | ||||
|               (this as Portal).fromFloor == fromFloor && | ||||
|               (this as Portal).from == from && | ||||
|               (this as Portal).toFloor == toFloor && | ||||
|               (this as Portal).to == to && | ||||
|               (this as Portal).baseFeature == baseFeature, | ||||
|           basicFeature: (floor, building, feature) => | ||||
|               this is BasicFeature && | ||||
|               (this as BasicFeature).floor == floor && | ||||
|               (this as BasicFeature).building == building && | ||||
|               (this as BasicFeature).feature == feature, | ||||
|         ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool eq(String? a, String? b) => a?.toLowerCase() == b?.toLowerCase(); | ||||
| class Graph { | ||||
|   final List<(GraphFeature, double, GraphFeature)> _edges = []; | ||||
|   final HashSet<GraphFeature> _nodes = HashSet(); | ||||
|   final HashSet<(GraphFeature, GraphFeature)> _edgesSet = HashSet(); | ||||
|  | ||||
|   Iterable<GraphFeature> get nodes => _nodes.iter(); | ||||
|  | ||||
|   void addNode(GraphFeature node) { | ||||
|     _nodes.add(node); | ||||
|     if (node is BasicFeature && node.feature.name == 'H22') { | ||||
|       print(node); | ||||
|       print(node.hashCode); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void addEdge(GraphFeature from, GraphFeature to, double weight) { | ||||
|     addNode(from); | ||||
|     addNode(to); | ||||
|     if (!_edgesSet.contains((from, to))) { | ||||
|       _edgesSet.add((from, to)); | ||||
|       _edges.add((from, weight, to)); | ||||
|     } | ||||
|     if (!_edgesSet.contains((to, from))) { | ||||
|       _edgesSet.add((to, from)); | ||||
|       _edges.add((to, weight, from)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   List<(GraphFeature, double, GraphFeature)> getEdges(GraphFeature node) { | ||||
|     return _edges.where((edge) => edge.$1 == node).toList(); | ||||
|   } | ||||
|  | ||||
|   bool contains(GraphFeature node) { | ||||
|     return _nodes.contains(node); | ||||
|   } | ||||
|  | ||||
|   bool containsEdge(GraphFeature from, GraphFeature to) { | ||||
|     return _edgesSet.contains((from, to)); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   String toString() { | ||||
|     return 'Graph(_edges: $_edges, _nodes: $_nodes, _edgesSet: $_edgesSet)'; | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   bool operator ==(Object other) { | ||||
|     if (identical(this, other)) return true; | ||||
|  | ||||
|     return other is Graph && | ||||
|         listEquals(other._edges, _edges) && | ||||
|         setEquals(other._nodes, _nodes) && | ||||
|         setEquals(other._edgesSet, _edgesSet); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   int get hashCode => _edges.hashCode ^ _nodes.hashCode ^ _edgesSet.hashCode; | ||||
| } | ||||
|  | ||||
| IList<GraphFeature> wrap(Feature feature, int floor, String buildingFrom) { | ||||
|   return feature.type | ||||
| @ -60,7 +159,10 @@ IList<GraphFeature> wrap(Feature feature, int floor, String buildingFrom) { | ||||
|         lift: (floors) => stairPortalGenerator(floors, floor, feature, 99), | ||||
|         door: (connections) => | ||||
|             doorPortalGenerator(connections, floor, buildingFrom, feature), | ||||
|         orElse: () => [GraphFeature.basicFeature(floor, buildingFrom, feature)], | ||||
|         orElse: () => [ | ||||
|           GraphFeature.basicFeature( | ||||
|               floor, feature.building ?? buildingFrom, feature) | ||||
|         ], | ||||
|       ) | ||||
|       .lock; | ||||
| } | ||||
| @ -70,8 +172,7 @@ List<GraphFeature> doorPortalGenerator( | ||||
|   final portals = <GraphFeature>[]; | ||||
|  | ||||
|   for (final connection in connections.where((c) => !eq(c, from))) { | ||||
|     portals.add(GraphFeature.portal( | ||||
|         floor, from, floor, connection.toLowerCase(), feature)); | ||||
|     portals.add(GraphFeature.portal(floor, from, floor, connection, feature)); | ||||
|   } | ||||
|  | ||||
|   return portals; | ||||
| @ -83,12 +184,12 @@ List<GraphFeature> stairPortalGenerator( | ||||
|   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)); | ||||
|       portals.add(GraphFeature.portal( | ||||
|           floor, feature.building!, floor - i, feature.building!, feature)); | ||||
|     } | ||||
|     if (floors.contains(floor + i)) { | ||||
|       portals.add(GraphFeature.portal(floor, feature.building!.toLowerCase(), | ||||
|           floor + i, feature.building!.toLowerCase(), feature)); | ||||
|       portals.add(GraphFeature.portal( | ||||
|           floor, feature.building!, floor + i, feature.building!, feature)); | ||||
|     } | ||||
|   } | ||||
|   return portals; | ||||
| @ -142,117 +243,34 @@ List<GraphFeature> findAdjacent( | ||||
|   return adjacentFeatures; | ||||
| } | ||||
|  | ||||
| List<GraphFeature> createGraphList( | ||||
|     GraphFeature origin, List<Feature> allFeatures, | ||||
|     [Set<GraphFeature>? visited]) { | ||||
| Graph makeGraph(GraphFeature origin, List<Feature> allFeatures, | ||||
|     [Graph? graph]) { | ||||
|   // final usedFeatures = <GraphFeature>[origin]; | ||||
|  | ||||
|   visited ??= <GraphFeature>{origin}; | ||||
|   graph ??= Graph(); | ||||
|   graph.addNode(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); | ||||
|   for (final feature in adjacent.asSet()..removeAll(graph.nodes)) { | ||||
|     graph.addEdge(origin, feature, origin.metersTo(feature)); | ||||
|     final _ = makeGraph(feature, allFeatures, graph); | ||||
|     // graph.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); | ||||
| Result<List<(GraphFeature, double)>> findShortestPath(GraphFeature origin, | ||||
|     bool Function(GraphFeature) destinationSelector, List<Feature> allFeatures, | ||||
|     {heuristicVariant = "zero", heuristicMultiplier = 0.2}) { | ||||
|   Graph graph = makeGraph(origin, allFeatures); | ||||
|  | ||||
|   if (!(graph.keys.contains(origin) && | ||||
|       graph.values.firstWhereOrNull((vals) => vals.containsKey(destination)) != | ||||
|           null)) { | ||||
|   final GraphFeature? destination = | ||||
|       graph.nodes.firstWhereOrNull(destinationSelector); | ||||
|  | ||||
|   if (!(graph.contains(origin) && | ||||
|       destination != null && | ||||
|       graph.contains(destination))) { | ||||
|     return bail("Origin or destination not in graph"); | ||||
|   } | ||||
|  | ||||
| @ -299,10 +317,10 @@ Result<List<(GraphFeature, double)>> findShortestPath( | ||||
|     } | ||||
|  | ||||
|     // expand node | ||||
|     final adjacents = graph[node]!; | ||||
|     for (final entry in adjacents.entries) { | ||||
|       final adjNode = entry.key; | ||||
|       final adjCost = entry.value; | ||||
|     final edges = graph.getEdges(node); | ||||
|     for (final entry in edges) { | ||||
|       final adjNode = entry.$3; | ||||
|       final adjCost = entry.$2; | ||||
|  | ||||
|       if (closedlist.contains(adjNode)) { | ||||
|         continue; | ||||
| @ -349,77 +367,3 @@ Result<List<(GraphFeature, double)>> findShortestPath( | ||||
|  | ||||
|   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); | ||||
|         }, | ||||
|       ); | ||||
|     }, | ||||
|   ); | ||||
| } | ||||
|  | ||||
| @ -144,24 +144,6 @@ class _$BuildingFloorImpl extends BuildingFloor { | ||||
|   @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') | ||||
| @ -341,29 +323,6 @@ class _$PortalImpl extends Portal { | ||||
|   @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') | ||||
| @ -529,25 +488,6 @@ class _$BasicFeatureImpl extends BasicFeature { | ||||
|   @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') | ||||
|  | ||||
| @ -1,7 +1,8 @@ | ||||
| import 'package:flutter/cupertino.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:uninav/data/geo/model.dart'; | ||||
|  | ||||
| bool eq(String? a, String? b) => a?.toLowerCase() == b?.toLowerCase(); | ||||
|  | ||||
| String formatDuration(Duration duration) { | ||||
|   final days = duration.inDays; | ||||
|   final hours = duration.inHours.remainder(24); | ||||
|  | ||||
| @ -22,6 +22,7 @@ String formatGraphFeature(GraphFeature feature) { | ||||
|  | ||||
| void main() { | ||||
|   TestWidgetsFlutterBinding.ensureInitialized(); | ||||
|  | ||||
|   group('findAdjacent', () { | ||||
|     late MyMapController mapController; | ||||
|     late List<Feature> allFeatures; | ||||
| @ -34,6 +35,69 @@ void main() { | ||||
|       allFeatures = mapController.features; | ||||
|     }); | ||||
|  | ||||
|     test('generates a graph (new)', () { | ||||
|       // 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 endFeature = allFeatures | ||||
|       //     .firstWhere((f) => f.type is Building && eq(f.name, 'o25')); | ||||
|  | ||||
|       // final targetFeature = allFeatures | ||||
|       //     .firstWhere((f) => f.type is LectureHall && eq(f.name, 'H22')); | ||||
|  | ||||
|       // final wrapped = | ||||
|       //     wrap(targetFeature, targetFeature.level!, targetFeature.building!); | ||||
|       // print(wrapped); | ||||
|  | ||||
|       final graph = makeGraph( | ||||
|           wrap(buildingFeature, 2, buildingFeature.name).first, allFeatures); | ||||
|  | ||||
|       final wrapped = [ | ||||
|         graph.nodes.firstWhere((element) => | ||||
|             element is BasicFeature && eq(element.feature.name, "H22")) | ||||
|       ]; | ||||
|  | ||||
|       print(graph.contains(wrapped.first)); // print(graph); | ||||
|       print(wrapped.first.hashCode); // print(graph); | ||||
|       // print(wrap(targetFeature, targetFeature.level!, targetFeature.building!) | ||||
|       //     .first | ||||
|       //     .hashCode); | ||||
|       // print(wrapped.first == | ||||
|       //     wrap(targetFeature, targetFeature.level!, targetFeature.building!) | ||||
|       //         .first); | ||||
|  | ||||
|       // print(graph.toString()); | ||||
|     }); | ||||
|  | ||||
|     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, 'o25')); | ||||
|  | ||||
|       // 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, 'H22')); | ||||
|  | ||||
|       print(endFeature); | ||||
|  | ||||
|       final path = findShortestPath( | ||||
|         wrap(startFeature, 4, startFeature.name).first, | ||||
|         (f) => f is BasicFeature && eq(f.feature.name, 'H1'), | ||||
|         // wrap(endFeature, 2, "o28").first, | ||||
|         allFeatures, | ||||
|       ); | ||||
|       print(path | ||||
|           .unwrap() | ||||
|           .map((e) => "${formatGraphFeature(e.$1)} (${e.$2}m)") | ||||
|           .join(' -> ')); | ||||
|     }); | ||||
|  | ||||
| /* | ||||
|     test('generates a graph', () { | ||||
|       // Find a building feature | ||||
|       // final buildingFeature = allFeatures.firstWhere((f) => f.type is Building); | ||||
| @ -172,5 +236,6 @@ void main() { | ||||
|       print(path.map(formatGraphFeature).join('\n')); | ||||
|     }); | ||||
|     */ | ||||
|     */ | ||||
|   }); | ||||
| } | ||||
|  | ||||
							
								
								
									
										5
									
								
								test/scratch_1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								test/scratch_1
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Reference in New Issue
	
	Block a user