| | | 1 | | //======================================================================= |
| | | 2 | | // NavigationTokenLegend.cs |
| | | 3 | | //======================================================================= |
| | | 4 | | // MIT License, Copyright (c) 2024-present David Oravsky (mrdav30) |
| | | 5 | | // See LICENSE file in the project root for full license information. |
| | | 6 | | //======================================================================= |
| | | 7 | | |
| | | 8 | | using System; |
| | | 9 | | using FixedMathSharp; |
| | | 10 | | using SwiftCollections; |
| | | 11 | | |
| | | 12 | | namespace Trailblazer.Pathing; |
| | | 13 | | |
| | | 14 | | /// <summary> |
| | | 15 | | /// Maps trimmed authoring tokens to complete navigation-cell payloads. |
| | | 16 | | /// </summary> |
| | | 17 | | public sealed class NavigationTokenLegend |
| | | 18 | | { |
| | 27 | 19 | | private readonly SwiftDictionary<string, NavigationTokenLegendEntry> _entries = |
| | 27 | 20 | | new(8, StringComparer.Ordinal); |
| | | 21 | | |
| | | 22 | | /// <summary> |
| | | 23 | | /// Creates the built-in solid, volume, mixed-media, and climb token set. |
| | | 24 | | /// </summary> |
| | | 25 | | public static NavigationTokenLegend CreateBuiltIn( |
| | | 26 | | Fixed64 radiusClearance = default, |
| | | 27 | | Fixed64 heightClearance = default) |
| | | 28 | | { |
| | 22 | 29 | | SwiftThrowHelper.ThrowIfArgument( |
| | 22 | 30 | | radiusClearance < Fixed64.Zero, |
| | 22 | 31 | | nameof(radiusClearance), |
| | 22 | 32 | | "Radius clearance must be non-negative."); |
| | 22 | 33 | | SwiftThrowHelper.ThrowIfArgument( |
| | 22 | 34 | | heightClearance < Fixed64.Zero, |
| | 22 | 35 | | nameof(heightClearance), |
| | 22 | 36 | | "Height clearance must be non-negative."); |
| | | 37 | | |
| | 22 | 38 | | var legend = new NavigationTokenLegend(); |
| | 22 | 39 | | legend.Register(string.Empty, NavigationTokenLegendEntry.SkipCell()); |
| | 22 | 40 | | legend.Register(".", NavigationTokenLegendEntry.SkipCell()); |
| | 22 | 41 | | legend.Register("X", NavigationTokenLegendEntry.SkipCell()); |
| | 22 | 42 | | legend.Register("S", CreateEntry( |
| | 22 | 43 | | TraversalMedia.Solid, |
| | 22 | 44 | | radiusClearance, |
| | 22 | 45 | | heightClearance, |
| | 22 | 46 | | NavigationCellFlags.None, |
| | 22 | 47 | | TraversalMedia.Solid)); |
| | 22 | 48 | | legend.Register("SC", CreateEntry( |
| | 22 | 49 | | TraversalMedia.Solid, |
| | 22 | 50 | | radiusClearance, |
| | 22 | 51 | | heightClearance, |
| | 22 | 52 | | NavigationCellFlags.ClimbSurfaceHint)); |
| | 22 | 53 | | legend.Register("G", CreateEntry( |
| | 22 | 54 | | TraversalMedia.Gas, |
| | 22 | 55 | | radiusClearance, |
| | 22 | 56 | | heightClearance, |
| | 22 | 57 | | NavigationCellFlags.None, |
| | 22 | 58 | | TraversalMedia.Gas)); |
| | 22 | 59 | | legend.Register("L", CreateEntry( |
| | 22 | 60 | | TraversalMedia.Liquid, |
| | 22 | 61 | | radiusClearance, |
| | 22 | 62 | | heightClearance, |
| | 22 | 63 | | NavigationCellFlags.None, |
| | 22 | 64 | | TraversalMedia.Liquid)); |
| | 22 | 65 | | legend.Register("LC", CreateEntry( |
| | 22 | 66 | | TraversalMedia.Solid | TraversalMedia.Liquid, |
| | 22 | 67 | | radiusClearance, |
| | 22 | 68 | | heightClearance, |
| | 22 | 69 | | NavigationCellFlags.ClimbSurfaceHint, |
| | 22 | 70 | | TraversalMedia.Solid | TraversalMedia.Liquid)); |
| | 22 | 71 | | legend.Register("SG", CreateEntry( |
| | 22 | 72 | | TraversalMedia.Solid | TraversalMedia.Gas, |
| | 22 | 73 | | radiusClearance, |
| | 22 | 74 | | heightClearance, |
| | 22 | 75 | | NavigationCellFlags.None, |
| | 22 | 76 | | TraversalMedia.Solid | TraversalMedia.Gas)); |
| | 22 | 77 | | legend.Register("SL", CreateEntry( |
| | 22 | 78 | | TraversalMedia.Solid | TraversalMedia.Liquid, |
| | 22 | 79 | | radiusClearance, |
| | 22 | 80 | | heightClearance, |
| | 22 | 81 | | NavigationCellFlags.None, |
| | 22 | 82 | | TraversalMedia.Solid | TraversalMedia.Liquid)); |
| | 22 | 83 | | return legend; |
| | | 84 | | } |
| | | 85 | | |
| | | 86 | | /// <summary> |
| | | 87 | | /// Registers one trimmed token. A duplicate token returns false without replacement. |
| | | 88 | | /// </summary> |
| | | 89 | | public bool Register(string token, NavigationTokenLegendEntry entry) |
| | | 90 | | { |
| | 233 | 91 | | SwiftThrowHelper.ThrowIfNull(token, nameof(token)); |
| | 232 | 92 | | string normalizedToken = token.Trim(); |
| | 232 | 93 | | SwiftThrowHelper.ThrowIfArgument( |
| | 232 | 94 | | normalizedToken.IndexOf('!') >= 0 || normalizedToken.IndexOf('_') >= 0, |
| | 232 | 95 | | nameof(token), |
| | 232 | 96 | | "Legend tokens cannot contain reserved marker or inline-cost characters."); |
| | | 97 | | |
| | 230 | 98 | | if (_entries.ContainsKey(normalizedToken)) |
| | 1 | 99 | | return false; |
| | | 100 | | |
| | 229 | 101 | | _entries.Add(normalizedToken, entry); |
| | 229 | 102 | | return true; |
| | | 103 | | } |
| | | 104 | | |
| | | 105 | | /// <summary> |
| | | 106 | | /// Attempts to resolve one token after ordinal whitespace trimming. |
| | | 107 | | /// </summary> |
| | | 108 | | public bool TryGetEntry(string token, out NavigationTokenLegendEntry entry) |
| | | 109 | | { |
| | 46 | 110 | | SwiftThrowHelper.ThrowIfNull(token, nameof(token)); |
| | 45 | 111 | | return _entries.TryGetValue(token.Trim(), out entry); |
| | | 112 | | } |
| | | 113 | | |
| | | 114 | | private static NavigationTokenLegendEntry CreateEntry( |
| | | 115 | | TraversalMedia media, |
| | | 116 | | Fixed64 radiusClearance, |
| | | 117 | | Fixed64 heightClearance, |
| | | 118 | | NavigationCellFlags flags, |
| | | 119 | | TraversalMedia transitionMedia = TraversalMedia.None) => |
| | 154 | 120 | | new( |
| | 154 | 121 | | new NavigationCell( |
| | 154 | 122 | | media, |
| | 154 | 123 | | TraversalCapability.None, |
| | 154 | 124 | | default, |
| | 154 | 125 | | Fixed64.Zero, |
| | 154 | 126 | | radiusClearance, |
| | 154 | 127 | | heightClearance, |
| | 154 | 128 | | flags), |
| | 154 | 129 | | transitionMedia); |
| | | 130 | | } |