| | | 1 | | //======================================================================= |
| | | 2 | | // GridCellPrism.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 System.Runtime.CompilerServices; |
| | | 10 | | using FixedMathSharp; |
| | | 11 | | using FixedMathSharp.Geometry; |
| | | 12 | | using GridForge.Spatial; |
| | | 13 | | |
| | | 14 | | namespace GridForge.Grids.Topology; |
| | | 15 | | |
| | | 16 | | /// <summary> |
| | | 17 | | /// Describes one exact topology cell as an ordered convex XZ footprint and a closed vertical interval. |
| | | 18 | | /// </summary> |
| | | 19 | | /// <remarks> |
| | | 20 | | /// Construction fails when a metric cannot be bisected exactly in the fixed-point scalar domain. |
| | | 21 | | /// </remarks> |
| | | 22 | | public readonly struct GridCellPrism |
| | | 23 | | { |
| | | 24 | | private readonly Vector2d _vertex0; |
| | | 25 | | private readonly Vector2d _vertex1; |
| | | 26 | | private readonly Vector2d _vertex2; |
| | | 27 | | private readonly Vector2d _vertex3; |
| | | 28 | | private readonly Vector2d _vertex4; |
| | | 29 | | private readonly Vector2d _vertex5; |
| | | 30 | | |
| | | 31 | | /// <summary> |
| | | 32 | | /// The exact runtime cell identity represented by this prism. |
| | | 33 | | /// </summary> |
| | | 34 | | public WorldVoxelIndex Cell { get; } |
| | | 35 | | |
| | | 36 | | /// <summary> |
| | | 37 | | /// The topology that produced the footprint. |
| | | 38 | | /// </summary> |
| | | 39 | | public GridTopologyKind TopologyKind { get; } |
| | | 40 | | |
| | | 41 | | /// <summary> |
| | | 42 | | /// The world-space cell center. |
| | | 43 | | /// </summary> |
| | | 44 | | public Vector3d Center { get; } |
| | | 45 | | |
| | | 46 | | /// <summary> |
| | | 47 | | /// The inclusive lower Y bound. |
| | | 48 | | /// </summary> |
| | | 49 | | public Fixed64 VerticalMin { get; } |
| | | 50 | | |
| | | 51 | | /// <summary> |
| | | 52 | | /// The inclusive upper Y bound. |
| | | 53 | | /// </summary> |
| | | 54 | | public Fixed64 VerticalMax { get; } |
| | | 55 | | |
| | | 56 | | /// <summary> |
| | | 57 | | /// The largest radius that can be inset from every horizontal footprint edge. |
| | | 58 | | /// </summary> |
| | | 59 | | public Fixed64 PlanarInradius { get; } |
| | | 60 | | |
| | | 61 | | /// <summary> |
| | | 62 | | /// The number of boundary-ordered footprint vertices. |
| | | 63 | | /// </summary> |
| | | 64 | | public int FootprintVertexCount { get; } |
| | | 65 | | |
| | | 66 | | internal GridCellPrism( |
| | | 67 | | WorldVoxelIndex cell, |
| | | 68 | | GridTopologyKind topologyKind, |
| | | 69 | | Vector3d center, |
| | | 70 | | Fixed64 verticalMin, |
| | | 71 | | Fixed64 verticalMax, |
| | | 72 | | Fixed64 planarInradius, |
| | | 73 | | ReadOnlySpan<Vector2d> footprint) |
| | | 74 | | { |
| | 7637 | 75 | | if (footprint.Length != 4 && footprint.Length != 6) |
| | 1 | 76 | | throw new ArgumentOutOfRangeException(nameof(footprint)); |
| | | 77 | | |
| | 7636 | 78 | | Cell = cell; |
| | 7636 | 79 | | TopologyKind = topologyKind; |
| | 7636 | 80 | | Center = center; |
| | 7636 | 81 | | VerticalMin = verticalMin; |
| | 7636 | 82 | | VerticalMax = verticalMax; |
| | 7636 | 83 | | PlanarInradius = planarInradius; |
| | 7636 | 84 | | FootprintVertexCount = footprint.Length; |
| | 7636 | 85 | | _vertex0 = footprint[0]; |
| | 7636 | 86 | | _vertex1 = footprint[1]; |
| | 7636 | 87 | | _vertex2 = footprint[2]; |
| | 7636 | 88 | | _vertex3 = footprint[3]; |
| | 7636 | 89 | | _vertex4 = footprint.Length > 4 ? footprint[4] : default; |
| | 7636 | 90 | | _vertex5 = footprint.Length > 5 ? footprint[5] : default; |
| | 7636 | 91 | | } |
| | | 92 | | |
| | | 93 | | /// <summary> |
| | | 94 | | /// Gets one boundary-ordered XZ footprint vertex. |
| | | 95 | | /// </summary> |
| | | 96 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 129297 | 97 | | public Vector2d GetFootprintVertex(int index) => index switch |
| | 129297 | 98 | | { |
| | 59656 | 99 | | 0 when FootprintVertexCount > 0 => _vertex0, |
| | 65100 | 100 | | 1 when FootprintVertexCount > 1 => _vertex1, |
| | 65044 | 101 | | 2 when FootprintVertexCount > 2 => _vertex2, |
| | 64992 | 102 | | 3 when FootprintVertexCount > 3 => _vertex3, |
| | 1901 | 103 | | 4 when FootprintVertexCount > 4 => _vertex4, |
| | 1898 | 104 | | 5 when FootprintVertexCount > 5 => _vertex5, |
| | 2 | 105 | | _ => throw new ArgumentOutOfRangeException(nameof(index)) |
| | 129297 | 106 | | }; |
| | | 107 | | |
| | | 108 | | /// <summary> |
| | | 109 | | /// Copies the boundary-ordered XZ footprint into caller-owned storage. |
| | | 110 | | /// </summary> |
| | | 111 | | public void CopyFootprintTo(Span<Vector2d> destination) |
| | | 112 | | { |
| | 5760 | 113 | | if (destination.Length < FootprintVertexCount) |
| | 1 | 114 | | throw new ArgumentException("The destination is smaller than the footprint.", nameof(destination)); |
| | | 115 | | |
| | 58862 | 116 | | for (int i = 0; i < FootprintVertexCount; i++) |
| | 23672 | 117 | | destination[i] = GetFootprintVertex(i); |
| | 5759 | 118 | | } |
| | | 119 | | |
| | | 120 | | /// <summary> |
| | | 121 | | /// Determines whether a world-space point lies inside or on this closed prism. |
| | | 122 | | /// </summary> |
| | | 123 | | public bool Contains(Vector3d point) |
| | | 124 | | { |
| | 8945 | 125 | | if (FootprintVertexCount is not 4 and not 6 |
| | 8945 | 126 | | || point.Y < VerticalMin |
| | 8945 | 127 | | || point.Y > VerticalMax) |
| | 7 | 128 | | return false; |
| | | 129 | | |
| | 8938 | 130 | | Span<Vector2d> offsets = stackalloc Vector2d[6]; |
| | 8938 | 131 | | Vector2d origin = new(Center.X, Center.Z); |
| | 89812 | 132 | | for (int i = 0; i < FootprintVertexCount; i++) |
| | 35968 | 133 | | offsets[i] = GetFootprintVertex(i) - origin; |
| | | 134 | | |
| | 8938 | 135 | | return FixedConvex2dRelations.ContainsPoint( |
| | 8938 | 136 | | new Vector2d(point.X, point.Z), |
| | 8938 | 137 | | origin, |
| | 8938 | 138 | | offsets[..FootprintVertexCount]); |
| | | 139 | | } |
| | | 140 | | |
| | | 141 | | internal TopologyVoxelAabb GetAabb() |
| | | 142 | | { |
| | 2709 | 143 | | Vector2d first = _vertex0; |
| | 2709 | 144 | | Fixed64 minX = first.X; |
| | 2709 | 145 | | Fixed64 maxX = first.X; |
| | 2709 | 146 | | Fixed64 minZ = first.Y; |
| | 2709 | 147 | | Fixed64 maxZ = first.Y; |
| | | 148 | | |
| | 22464 | 149 | | for (int i = 1; i < FootprintVertexCount; i++) |
| | | 150 | | { |
| | 8523 | 151 | | Vector2d vertex = GetFootprintVertex(i); |
| | 8523 | 152 | | minX = FixedMath.Min(minX, vertex.X); |
| | 8523 | 153 | | maxX = FixedMath.Max(maxX, vertex.X); |
| | 8523 | 154 | | minZ = FixedMath.Min(minZ, vertex.Y); |
| | 8523 | 155 | | maxZ = FixedMath.Max(maxZ, vertex.Y); |
| | | 156 | | } |
| | | 157 | | |
| | 2709 | 158 | | return new TopologyVoxelAabb( |
| | 2709 | 159 | | new Vector3d(minX, VerticalMin, minZ), |
| | 2709 | 160 | | new Vector3d(maxX, VerticalMax, maxZ)); |
| | | 161 | | } |
| | | 162 | | } |