public void KDTree() { var tree = new List <Sorta>(); for (int j = 0; j != 10; ++j) { for (int i = 0; i != 10; ++i) { tree.Add(new Sorta { pos = new v2(i, j) }); } } KDTree_.Build(tree, 2); var result = KDTree_.Search(tree, 2, new[] { 5.0, 5.0 }, 1.1).Select(x => x.pos).ToList(); result.Sort((l, r) => { if (l.x != r.x) { return(l.x < r.x ? -1 : 1); } return(l.y < r.y ? -1 : 1); }); Assert.Equal(result.Count, 5); Assert.Equal(result[0], new v2(4f, 5f)); Assert.Equal(result[1], new v2(5f, 4f)); Assert.Equal(result[2], new v2(5f, 5f)); Assert.Equal(result[3], new v2(5f, 6f)); Assert.Equal(result[4], new v2(6f, 5f)); }
/// <summary>Rebuild the map</summary> public void BuildSystemMap(IEnumerable <StarSystem> stars) { Log.Write(ELogLevel.Info, $"Building systems map"); using (StatusStack.NewStatusMessage($"Updating star map...")) { m_tree.Clear(); m_tree.AddRange(stars.Select(x => new StarSystemRef(x))); KDTree_.Build(m_tree, 3); } }
/// <summary>Search for trades</summary> private async Task FindTradeRoutes(Settings settings, int issue) // worker thread context { if (!CanFindTradeRoutes(settings)) { // Settings not valid, ignore until they are. RunOnMainThread(() => { if (issue != m_trade_routes_issue) { return; } m_trade_routes_issue_current = issue; }); return; } else { // Settings are valid, reset the trade routes before starting RunOnMainThread(() => { if (issue != m_trade_routes_issue || TradeRoutes.Count == 0) { return; } TradeRoutes.Clear(); TradeRoutesChanged?.Invoke(this, EventArgs.Empty); }); } // Get the start point var origin_system = await Src.GetStarSystem(settings.Origin.StarSystemID.Value); var origin_station = await Src.GetStation(settings.Origin.StationID.Value); var origin = new Location(origin_system, origin_station); // If a specific destination is given, find trades between the origin and that destination if (!settings.AnyDestination) { var destination_system = await Src.GetStarSystem(settings.Destination.StarSystemID.Value); var destination_station = settings.Destination.StationID != null ? await Src.GetStation(settings.Destination.StationID.Value) : null; Log.Write(ELogLevel.Info, $"Find trade routes {origin_system.Name}/{origin_station.Name} → {destination_system.Name}/{destination_station?.Name ?? "Any"}"); var stations = destination_station != null ? new[] { destination_station } : Src.EnumStations( system_id: destination_system.ID, max_station_distance: settings.MaxStationDistance, required_pad_size: settings.RequiredPadSize, facilities_incl: EFacilities.Market | EFacilities.Docking, ignore_planetary: settings.IgnorePlanetBases); // Look for trade routes between the given stations foreach (var station in stations) { // Abort if the issue number changes if (m_trade_routes_issue != issue) { return; } var routes = await FindTradeRoutes(settings, origin, new Location(destination_system, station), issue); if (routes.Count == 0) { continue; } RunOnMainThread(() => { if (issue != m_trade_routes_issue) { return; } MergeTradeRoutes(origin, routes); }); } } else { Log.Write(ELogLevel.Info, $"Find trade routes {origin_system.Name}/{origin_station.Name} within {settings.MaxTradeRouteDistance} LY"); int considered_systems = 0, last_considered_systems = 0; var considered = new HashSet <StarSystemRef>(); // A queue of systems to consider var queue = new Queue <StarSystem>(); using (var msg = StatusStack.NewStatusMessage("Finding Trade Routes...")) { var candidates = Src.Search(origin_system.Position, settings.MaxTradeRouteDistance.Value).ToList(); KDTree_.Build(candidates, 3); // Find all systems within the maximum trade route distance. for (queue.Enqueue(origin_system); queue.Count != 0 && !Shutdown.IsCancellationRequested;) { // Abort if the issue number changes if (m_trade_routes_issue != issue) { return; } // Get the next system to consider var hop = queue.Dequeue(); // Find the systems within the maximum jump range that have not been considered already var system_refs = KDTree_.Search(candidates, 3, (double[])hop.Position, settings.MaxJumpRange).Where(x => !considered.Contains(x)).ToList(); if (system_refs.Count == 0) { continue; } // When the considered number gets large enough, remove them from the map and regenerate it const int RebuildTreeThreshold = 256; considered.AddRange(system_refs); if (considered.Count > RebuildTreeThreshold) { candidates.RemoveSet(considered); KDTree_.Build(candidates, 3); considered.Clear(); } // Check the stations in each system foreach (var system_ref in system_refs) { // Abort if the issue number changes if (m_trade_routes_issue != issue) { return; } // Get the system var destination_system = await Src.GetStarSystem(system_ref.ID); // Update status if (++considered_systems - last_considered_systems > 100) { var distance = (destination_system.Position - origin_system.Position).Length; msg.Message = $"Finding Trade Routes... (checked:{considered_systems} queued:{queue.Count} distance:{distance})"; last_considered_systems = considered_systems; } // Needs a permit? if (destination_system.NeedPermit && settings.IgnorePermitSystems) { continue; } // Check the suitable stations var stations = Src.EnumStations( system_id: destination_system.ID, max_station_distance: settings.MaxStationDistance, required_pad_size: settings.RequiredPadSize, facilities_incl: EFacilities.Market | EFacilities.Docking, ignore_planetary: settings.IgnorePlanetBases); foreach (var station in stations) { // Abort if the issue number changes if (m_trade_routes_issue != issue) { return; } var routes = await FindTradeRoutes(settings, origin, new Location(destination_system, station), issue); if (routes.Count == 0) { continue; } RunOnMainThread(() => { if (issue != m_trade_routes_issue) { return; } MergeTradeRoutes(origin, routes); }); } // Add this system to be considered for the next hop queue.Enqueue(destination_system); } } } } m_trade_routes_issue_current = issue; }
/// <summary>Search the map for nearby systems</summary> public IEnumerable <StarSystemRef> Search(v4 position, double radius) { return(KDTree_.Search(m_tree, 3, (double[])position, radius)); }