public void Test_SubGridTree_InvalidCreation_TreeLevels() { // Test creating invalid subgrid trees ISubGridTree invalid = null; try { invalid = new SubGridTree(0, 1.0, new SubGridFactory <NodeSubGrid, LeafSubGrid>()); Assert.True(false, "SubGridTree permitted creation with invalid subgrid tree level"); } catch (Exception e) { Assert.True(e is ArgumentException, "Invalid exception raised for invalid argument to SubGridTree constructor"); } try { invalid = new SubGridTree(10, 1.0, new SubGridFactory <NodeSubGrid, LeafSubGrid>()); Assert.True(false, "SubGridTree permitted creation with invalid subgrid tree level"); } catch (Exception e) { Assert.True(e is ArgumentException, "Invalid exception raised for invalid argument to SubGridTree constructor"); } try { invalid = new SubGridTree(10, 1.0, new SubGridFactory <NodeSubGrid, LeafSubGrid>()); Assert.True(false, "SubGridTree permitted creation with invalid subgrid tree level"); } catch (Exception e) { Assert.True(e is ArgumentException, "Invalid exception raised for invalid argument to SubGridTree constructor"); } }
/// <summary> /// Integrates the cell passes processed from TAG files into sub grids within the live site model /// </summary> /// <param name="siteModelFromDatamodel">The site model to perform the change notifications for</param> /// <param name="task">The 'seed' task used as a hold all for aggregated machines</param> /// <param name="subGridIntegrator">The integrator to use to insert the new cell passes into the live site model</param> /// <param name="groupedAggregatedCellPasses">The set of all cell passes from all TAG files, grouped in to a single intermediary site model</param> /// <param name="numTagFilesRepresented">The number of TAG files represented in the data set being integrated</param> /// <param name="totalPassCountInAggregation">The sum total number of cell passes integrated in the live site model</param> private bool IntegrateCellPassesIntoLiveSiteModel(ISiteModel siteModelFromDatamodel, AggregatedDataIntegratorTask task, SubGridIntegrator subGridIntegrator, ISubGridTree groupedAggregatedCellPasses, int numTagFilesRepresented, out long totalPassCountInAggregation) { _log.LogInformation($"Aggregation Task Process --> Labeling aggregated cell pass with correct machine ID for {siteModelFromDatamodel.ID}"); totalPassCountInAggregation = 0; // This is a dirty map for the leaf sub grids and is stored as a bitmap grid // with one level fewer that the sub grid tree it is representing, and // with cells the size of the leaf sub grids themselves. As the cell coordinates // we have been given are with respect to the sub grid, we must transform them // into coordinates relevant to the dirty bitmap sub grid tree. _workingModelUpdateMap = new SubGridTreeSubGridExistenceBitMask { CellSize = SubGridTreeConsts.SubGridTreeDimension * siteModelFromDatamodel.CellSize, ID = siteModelFromDatamodel.ID }; // Integrate the cell pass data into the main site model and commit each sub grid as it is updated // ... first relabel the passes with the machine IDs from the persistent datamodel // Compute the vector of internal site model machine indexes between the intermediary site model constructed from the TAG files, // and the persistent site model the data us being processed into (short taskInternalMachineIndex, short datamodelInternalMachineIndex)[] internalMachineIndexMap = task.IntermediaryTargetMachines
/// <summary> /// Computes a tight bounding extent around the elevation values stored in the sub grid tree /// </summary> private static BoundingWorldExtent3D DataStoreExtents(ISubGridTree dataStore) { var computedGridExtent = BoundingWorldExtent3D.Inverted(); dataStore.ScanAllSubGrids(subGrid => { var items = ((GenericLeafSubGrid <float>)subGrid).Items; SubGridUtilities.SubGridDimensionalIterator((x, y) => { var elev = items[x, y]; if (elev != Common.Consts.NullHeight) { computedGridExtent.Include(subGrid.OriginX + x, subGrid.OriginY + y, elev); } }); return(true); }); if (computedGridExtent.IsValidPlanExtent) { computedGridExtent.Offset(-SubGridTreeConsts.DefaultIndexOriginOffset, -SubGridTreeConsts.DefaultIndexOriginOffset); } // Convert the grid rectangle to a world rectangle, padding out the 3D bound by a small margin to avoid edge effects in calculations var computedWorldExtent = new BoundingWorldExtent3D ((computedGridExtent.MinX - 1.01) * dataStore.CellSize, (computedGridExtent.MinY - 1.01) * dataStore.CellSize, (computedGridExtent.MaxX + 1.01) * dataStore.CellSize, (computedGridExtent.MaxY + 1.01) * dataStore.CellSize, computedGridExtent.MinZ - 0.01, computedGridExtent.MaxZ + 0.01); return(computedWorldExtent); }
public LeafSubGrid(ISubGridTree owner, ISubGrid parent, byte level) : base(owner, parent, level) { // Assert level = tree.NumLevels (leaves are only at the tips) if (owner != null && level != owner.NumLevels) { throw new ArgumentException("Requested level for leaf sub grid <> number of levels in tree", nameof(level)); } }
/// <summary> /// Constructor the the base client sub grid. This decorates the standard (owner, parent, level) /// constructor from the base with the cell size and index origin offset parameters from the sub grid tree /// this leaf is derived from. /// </summary> protected ClientLeafSubGrid(ISubGridTree owner, ISubGrid parent, byte level, double cellSize, int indexOriginOffset) : base(owner, parent, level) { CellSize = cellSize; IndexOriginOffset = indexOriginOffset; _gridDataType = GridDataType.All; // Default to 'all', descendant specialized classes will set appropriately TopLayerOnly = false; ProfileDisplayMode = DisplayMode.Height; }
/// <summary> /// Provides Read() semantics for a sub grid tree against a BinaryReader /// </summary> /// <param name="tree"></param> /// <param name="header"></param> /// <param name="version"></param> /// <param name="reader"></param> /// <returns></returns> public static bool Read(ISubGridTree tree, string header, int version, BinaryReader reader) { string Header = reader.ReadString(); int Version = reader.ReadInt32(); long Size = reader.ReadInt64(); if (Header != header || Version != version || Size == 0 || Size != reader.BaseStream.Length) { Log.LogError($"Header, version or stream size mismatch reading spatial sub grid index. Header={Header} (expected {header}), Version={Version} (expected {version}), Size={reader.BaseStream.Length} (expected {Size})"); return(false); } return(SerialiseIn(tree, reader)); }
public void Test_SubGridTree_InvalidCreation_SubgridFactory() { // Test creating with invalid factory ISubGridTree invalid = null; try { invalid = new SubGridTree(SubGridTreeConsts.SubGridTreeLevels, 1.0, null); Assert.True(false, "SubGridTree permitted creation with invalid subgrid tree cell size"); } catch (Exception e) { Assert.True(e is ArgumentException, "Invalid exception raised for invalid argument to SubGridTree constructor"); } }
/// <summary> /// Serializes all the sub grids in the tree out to the writer /// </summary> /// <param name="tree"></param> /// <param name="writer"></param> /// <returns></returns> static bool SerializeOut(ISubGridTree tree, BinaryWriter writer) { long SubGridCount = tree.CountLeafSubGridsInMemory(); writer.Write(tree.ID.ToByteArray()); writer.Write(SubGridCount); return(tree.ScanAllSubGrids(subGrid => { // Write out the origin for the node writer.Write(subGrid.OriginX); writer.Write(subGrid.OriginY); subGrid.Write(writer); return true; // keep scanning })); }
/// <summary> /// Constructs a mask using polygonal and positional spatial filtering aspects of a filter. /// </summary> private static void ConstructSubGridSpatialAndPositionalMask(SubGridCellAddress currentSubGridOrigin, InterceptList intercepts, int fromProfileCellIndex, SubGridTreeBitmapSubGridBits mask, ICellSpatialFilter cellFilter, ISubGridTree subGridTree) { var cellFilterHasSpatialOrPositionalFilters = cellFilter.HasSpatialOrPositionalFilters; var interceptsCount = intercepts.Count; mask.Clear(); for (var interceptIdx = fromProfileCellIndex; interceptIdx < interceptsCount; interceptIdx++) { // Determine the on-the-ground cell underneath the midpoint of each cell on the intercept line subGridTree.CalculateIndexOfCellContainingPosition(intercepts.Items[interceptIdx].MidPointX, intercepts.Items[interceptIdx].MidPointY, out var otgCellX, out var otgCellY); var thisSubGridOrigin = new SubGridCellAddress(otgCellX & ~SubGridTreeConsts.SubGridLocalKeyMask, otgCellY & ~SubGridTreeConsts.SubGridLocalKeyMask); if (!currentSubGridOrigin.Equals(thisSubGridOrigin)) { break; } var cellX = otgCellX & SubGridTreeConsts.SubGridLocalKeyMask; var cellY = otgCellY & SubGridTreeConsts.SubGridLocalKeyMask; if (cellFilterHasSpatialOrPositionalFilters) { subGridTree.GetCellCenterPosition(otgCellX, otgCellY, out var cellCenterX, out var cellCenterY); if (cellFilter.IsCellInSelection(cellCenterX, cellCenterY)) { mask.SetBit(cellX, cellY); } } else { mask.SetBit(cellX, cellY); } } }
/// <summary> /// Serializes the content of all the sub grids in the sub grid tree from the BinaryReader instance /// </summary> /// <param name="tree"></param> /// <param name="reader"></param> /// <returns></returns> static bool SerialiseIn(ISubGridTree tree, BinaryReader reader) { tree.ID = reader.ReadGuid(); // Read in the number of sub grids long SubGridCount = reader.ReadInt64(); // Read in each sub grid and add it to the tree for (long I = 0; I < SubGridCount; I++) { // Read in the the origin for the node int OriginX = reader.ReadInt32(); int OriginY = reader.ReadInt32(); // Create a node to hold the bits ISubGrid SubGrid = tree.ConstructPathToCell(OriginX, OriginY, Types.SubGridPathConstructionType.CreateLeaf); SubGrid.Read(reader); } return(true); }
private static void ConstructSubGridSpatialAndPositionalMask(ISubGridTree tree, SubGridCellAddress currentSubGridOrigin, List <T> profileCells, SubGridTreeBitmapSubGridBits mask, int fromProfileCellIndex, ICellSpatialFilter cellFilter) { mask.Clear(); // From current position to end... for (var cellIdx = fromProfileCellIndex; cellIdx < profileCells.Count; cellIdx++) { var profileCell = profileCells[cellIdx]; var thisSubGridOrigin = new SubGridCellAddress( profileCell.OTGCellX & ~SubGridTreeConsts.SubGridLocalKeyMask, profileCell.OTGCellY & ~SubGridTreeConsts.SubGridLocalKeyMask); if (!currentSubGridOrigin.Equals(thisSubGridOrigin)) { break; } var cellX = (byte)(profileCell.OTGCellX & SubGridTreeConsts.SubGridLocalKeyMask); var cellY = (byte)(profileCell.OTGCellY & SubGridTreeConsts.SubGridLocalKeyMask); if (cellFilter.HasSpatialOrPositionalFilters) { tree.GetCellCenterPosition(profileCell.OTGCellX, profileCell.OTGCellY, out var cellCenterX, out var cellCenterY); if (cellFilter.IsCellInSelection(cellCenterX, cellCenterY)) { mask.SetBit(cellX, cellY); } } else { mask.SetBit(cellX, cellY); } } }
/// <summary> /// Provides Write() semantics for a sub grid tree against a BinaryWriter /// </summary> /// <param name="tree"></param> /// <param name="header"></param> /// <param name="version"></param> /// <param name="writer"></param> /// <returns></returns> public static bool Write(ISubGridTree tree, string header, int version, BinaryWriter writer) { writer.Write(header); writer.Write(version); // Write place holder for stream size long SizePosition = writer.BaseStream.Position; writer.Write(0L); bool serializationResult = SerializeOut(tree, writer); if (serializationResult) { // Write the size of the stream in to the header long Size = writer.BaseStream.Position; writer.BaseStream.Seek(SizePosition, SeekOrigin.Begin); writer.Write(Size); } return(serializationResult); }
/// <summary> /// Construct either a node or a leaf sub grid for the given sub grid tree at the given level using the generic /// types Node and Leaf. /// </summary> /// <param name="tree"></param> /// <param name="treeLevel"></param> /// <returns>An ISubGrid interface representing the newly created node or leaf sub grid</returns> public virtual ISubGrid GetSubGrid(ISubGridTree tree, byte treeLevel) { // Ensure the requested tree level is valid for the given tree if (treeLevel < 1 || treeLevel > tree.NumLevels) { throw new ArgumentException($"Invalid treeLevel in sub grid factory: {treeLevel}, range is 1-{tree.NumLevels}", nameof(treeLevel)); } if (treeLevel < tree.NumLevels) { return(new Node { Owner = tree, Level = treeLevel }); } return(new Leaf { Owner = tree, Level = treeLevel }); }
/// <summary> /// Base constructor for a Node type sub grid. /// </summary> public NodeSubGrid(ISubGridTree owner, ISubGrid parent, byte level) : base(owner, parent, level) { Initialise(); }
/// <summary> /// Main constructor. Creates the local generic Items[,] array and delegates to base(...) /// </summary> /// <param name="owner"></param> /// <param name="parent"></param> /// <param name="level"></param> public GenericLeafSubGrid(ISubGridTree owner, ISubGrid parent, byte level) : base(owner, parent, level) { AllocateItems(); }
public void ScanCellsOverTriangle(ISubGridTree tree, int triIndex, Func <ISubGridTree, int, int, bool> leafSatisfied, Action <ISubGridTree, int, int, int> includeTriangleInLeaf, Action <ISubGridTree, int, // sourceTriangle Func <ISubGridTree, int, int, bool>, // leafSatisfied Action <ISubGridTree, int, int, int>, // includeTriangleInLeaf XYZ, XYZ, XYZ, bool> ProcessTrianglePiece) { Triangle Tri = TriangleItems[triIndex]; // Split triangle into two pieces, a 'top' piece and a 'bottom' piece to simplify // scanning across the triangle. Split is always with a horizontal line XYZ[] SortVertices = new XYZ[] { VertexItems[Tri.Vertex0], VertexItems[Tri.Vertex1], VertexItems[Tri.Vertex2] }; if (SortVertices[0].Y > SortVertices[1].Y) { DesignGeometry.SwapVertices(ref SortVertices[0], ref SortVertices[1]); } if (SortVertices[1].Y > SortVertices[2].Y) { DesignGeometry.SwapVertices(ref SortVertices[1], ref SortVertices[2]); } if (SortVertices[0].Y > SortVertices[1].Y) { DesignGeometry.SwapVertices(ref SortVertices[0], ref SortVertices[1]); } XYZ TopVertex = SortVertices[2]; XYZ CentralVertex = SortVertices[1]; XYZ BottomVertex = SortVertices[0]; // now make sure leftmost vertex in in first array item if (SortVertices[0].X > SortVertices[1].X) { DesignGeometry.SwapVertices(ref SortVertices[0], ref SortVertices[1]); } if (SortVertices[1].X > SortVertices[2].X) { DesignGeometry.SwapVertices(ref SortVertices[1], ref SortVertices[2]); } if (SortVertices[0].X > SortVertices[1].X) { DesignGeometry.SwapVertices(ref SortVertices[0], ref SortVertices[1]); } XYZ LeftMostVertex = SortVertices[0]; XYZ RightMostVertex = SortVertices[2]; // Are top or bottom vertices coincident with the middle vertex bool BottomPieceOnly = Math.Abs(TopVertex.Y - CentralVertex.Y) < 0.0001; bool TopPieceOnly = Math.Abs(BottomVertex.Y - CentralVertex.Y) < 0.0001; if (TopPieceOnly && BottomPieceOnly) // It's a thin horizontal triangle { ProcessTrianglePiece(tree, triIndex, leafSatisfied, includeTriangleInLeaf, LeftMostVertex, RightMostVertex, CentralVertex, true); } else { if (!(TopPieceOnly || BottomPieceOnly)) { // Divide triangle in two with a horizontal line // Find intersection point of triangle edge between top most and bottom most vertices if (LineIntersection.LinesIntersect(LeftMostVertex.X - 1, CentralVertex.Y, RightMostVertex.X + 1, CentralVertex.Y, TopVertex.X, TopVertex.Y, BottomVertex.X, BottomVertex.Y, out double IntersectX, out double IntersectY, true, out _)) { XYZ IntersectionVertex = new XYZ(IntersectX, IntersectY, 0); ProcessTrianglePiece(tree, triIndex, leafSatisfied, includeTriangleInLeaf, CentralVertex, IntersectionVertex, TopVertex, false); ProcessTrianglePiece(tree, triIndex, leafSatisfied, includeTriangleInLeaf, CentralVertex, IntersectionVertex, BottomVertex, false); } else { Log.LogWarning($"Triangle {Tri} failed to have intersection line calculated for it"); } }
/// <summary> /// Overloaded Write() method that does not accept a header or version to include into the serialized /// stream. Header will be set to string.Empty and version will be set to 0. /// This should only be used in contexts where the existence of the stream is transient and never written /// to a persistent location that may be sensitive to version considerations. /// </summary> /// <param name="tree"></param> /// <param name="writer"></param> /// <returns></returns> public static bool Write(ISubGridTree tree, BinaryWriter writer) => Write(tree, string.Empty, 0, writer);
/// <summary> /// Main constructor. Creates the local generic Items[,] array and delegates to base(...) /// </summary> /// <param name="owner"></param> /// <param name="parent"></param> /// <param name="level"></param> /// <param name="cellSize"></param> /// <param name="indexOriginOffset"></param> protected GenericClientLeafSubGrid(ISubGridTree owner, ISubGrid parent, byte level, double cellSize, int indexOriginOffset) : base(owner, parent, level, cellSize, indexOriginOffset) { }
/// <summary> /// Constructor taking the tree reference, parent and level of the sub grid to be created /// </summary> public SubGridTreeLeafBitmapSubGrid(ISubGridTree owner, ISubGrid parent, byte level) : base(owner, parent, level) { }
/// <summary> /// Constructor. Set the grid to HeightAndTime. /// </summary> /// <param name="owner"></param> /// <param name="parent"></param> /// <param name="level"></param> /// <param name="cellSize"></param> /// <param name="indexOriginOffset"></param> public ClientHeightLeafSubGrid(ISubGridTree owner, ISubGrid parent, byte level, double cellSize, int indexOriginOffset) : base(owner, parent, level, cellSize, indexOriginOffset) { Initialise(); }
public LeafSubGridBase(ISubGridTree owner, ISubGrid parent, byte level) : base(owner, parent, level) { }
/// <summary> /// Overloaded Read() method that does not accept a header or version to verify in the deserialized /// stream. Header will be expected to be string.Empty and version will be expected to be 0. /// This should only be used in contexts where the existence of the stream is transient and never read from /// a persistent location that may be sensitive to version considerations. /// </summary> /// <param name="tree"></param> /// <param name="reader"></param> /// <returns></returns> public static bool Read(ISubGridTree tree, BinaryReader reader) => Read(tree, string.Empty, 0, reader);