| | | 1 | | //======================================================================= |
| | | 2 | | // NormalizedGridConfiguration.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.Runtime.CompilerServices; |
| | | 9 | | using FixedMathSharp; |
| | | 10 | | using GridForge.Grids.Topology; |
| | | 11 | | using GridForge.Spatial; |
| | | 12 | | |
| | | 13 | | namespace GridForge.Configuration; |
| | | 14 | | |
| | | 15 | | /// <summary> |
| | | 16 | | /// Describes a validated grid configuration after topology-specific bounds |
| | | 17 | | /// normalization, without registering a live grid. |
| | | 18 | | /// </summary> |
| | | 19 | | public readonly struct NormalizedGridConfiguration |
| | | 20 | | { |
| | | 21 | | internal IGridTopology? Topology { get; } |
| | | 22 | | |
| | | 23 | | internal GridDimensions Dimensions { get; } |
| | | 24 | | |
| | | 25 | | /// <summary> |
| | | 26 | | /// The normalized configuration. Its bounds are topology-aligned, while |
| | | 27 | | /// scan-cell and storage settings retain the caller's requested values. |
| | | 28 | | /// </summary> |
| | | 29 | | public GridConfiguration Configuration { get; } |
| | | 30 | | |
| | | 31 | | /// <summary> |
| | | 32 | | /// The exact normalized bounds-and-topology key used to bind equivalent grids. |
| | | 33 | | /// Storage kind and scan-cell size are deliberately excluded from this identity. |
| | | 34 | | /// </summary> |
| | | 35 | | public GridConfigurationKey Key { get; } |
| | | 36 | | |
| | | 37 | | /// <summary> |
| | | 38 | | /// The number of valid topology-local X or axial-Q addresses. |
| | | 39 | | /// </summary> |
| | | 40 | | public int Width { get; } |
| | | 41 | | |
| | | 42 | | /// <summary> |
| | | 43 | | /// The number of valid topology-local vertical layers. |
| | | 44 | | /// </summary> |
| | | 45 | | public int Height { get; } |
| | | 46 | | |
| | | 47 | | /// <summary> |
| | | 48 | | /// The number of valid topology-local Z or axial-R addresses. |
| | | 49 | | /// </summary> |
| | | 50 | | public int Length { get; } |
| | | 51 | | |
| | | 52 | | /// <summary> |
| | | 53 | | /// The total number of topology-local addresses in the normalized grid. |
| | | 54 | | /// Sparse storage may materialize only a subset of these addresses. |
| | | 55 | | /// </summary> |
| | | 56 | | public int AddressCount { get; } |
| | | 57 | | |
| | | 58 | | /// <summary> |
| | | 59 | | /// Indicates whether this descriptor contains a valid normalized address space. |
| | | 60 | | /// </summary> |
| | 10 | 61 | | public bool IsValid => Width > 0 && Height > 0 && Length > 0; |
| | | 62 | | |
| | | 63 | | internal NormalizedGridConfiguration( |
| | | 64 | | GridConfiguration configuration, |
| | | 65 | | IGridTopology topology, |
| | | 66 | | GridDimensions dimensions) |
| | | 67 | | { |
| | 955 | 68 | | Topology = topology; |
| | 955 | 69 | | Dimensions = dimensions; |
| | 955 | 70 | | Configuration = configuration; |
| | 955 | 71 | | Key = configuration.ToGridKey(); |
| | 955 | 72 | | Width = dimensions.Width; |
| | 955 | 73 | | Height = dimensions.Height; |
| | 955 | 74 | | Length = dimensions.Length; |
| | 955 | 75 | | AddressCount = checked(dimensions.Width * dimensions.Height * dimensions.Length); |
| | 955 | 76 | | } |
| | | 77 | | |
| | | 78 | | /// <summary> |
| | | 79 | | /// Determines whether an index belongs to this topology-local address space. |
| | | 80 | | /// Validation is independent of sparse physical-voxel presence. |
| | | 81 | | /// </summary> |
| | | 82 | | /// <remarks> |
| | | 83 | | /// Validation uses coordinate values only; <see cref="VoxelIndex.IsAllocated"/> |
| | | 84 | | /// does not change whether the address is in range. |
| | | 85 | | /// </remarks> |
| | | 86 | | /// <param name="index">The topology-local index to validate.</param> |
| | | 87 | | /// <returns>True when each coordinate is inside the normalized dimensions.</returns> |
| | | 88 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 89 | | public bool IsValidIndex(VoxelIndex index) => |
| | 33 | 90 | | (uint)index.x < (uint)Width |
| | 33 | 91 | | && (uint)index.y < (uint)Height |
| | 33 | 92 | | && (uint)index.z < (uint)Length; |
| | | 93 | | |
| | | 94 | | /// <summary> |
| | | 95 | | /// Attempts to derive the exact offline prism for a topology-local address. |
| | | 96 | | /// </summary> |
| | | 97 | | /// <remarks> |
| | | 98 | | /// The returned prism has no runtime world/grid identity. It is suitable for |
| | | 99 | | /// authoring validation before a matching physical grid exists. |
| | | 100 | | /// </remarks> |
| | | 101 | | /// <param name="index">The topology-local address.</param> |
| | | 102 | | /// <param name="prism">The exact cell prism when the address and metrics are representable.</param> |
| | | 103 | | /// <returns>True when the address is valid and its prism is exactly representable.</returns> |
| | | 104 | | public bool TryGetCellPrism(VoxelIndex index, out GridCellPrism prism) |
| | | 105 | | { |
| | 22 | 106 | | if (!IsValidIndex(index) || Topology == null) |
| | | 107 | | { |
| | 6 | 108 | | prism = default; |
| | 6 | 109 | | return false; |
| | | 110 | | } |
| | | 111 | | |
| | 16 | 112 | | Vector3d center = Topology.GetWorldPosition(Configuration.BoundsMin, index); |
| | 16 | 113 | | return GridCellGeometry.TryCreatePrism( |
| | 16 | 114 | | Configuration.TopologyKind, |
| | 16 | 115 | | Configuration.TopologyMetrics, |
| | 16 | 116 | | center, |
| | 16 | 117 | | default, |
| | 16 | 118 | | out prism); |
| | | 119 | | } |
| | | 120 | | } |