private ISiteModel BuildModelForSingleCellProgressiveVolume(int numCellPasses, DateTime baseTime, TimeSpan timeIncrement, float baseHeight, float heightIncrement) { var dummy = new ClientHeightLeafSubGrid(); // Ensure NullCells is intialised for this class, var siteModel = DITAGFileAndSubGridRequestsWithIgniteFixture.NewEmptyModel(); var bulldozerMachineIndex = siteModel.Machines.Locate("Bulldozer", false).InternalSiteModelMachineIndex; var cellPasses = Enumerable.Range(0, numCellPasses).Select(x => new CellPass { InternalSiteModelMachineIndex = bulldozerMachineIndex, Time = baseTime + x * timeIncrement, Height = baseHeight + x * heightIncrement, PassType = PassType.Front }).ToList(); // Ensure the machine the cell passes are being added to has start and stop evens bracketing the cell passes siteModel.MachinesTargetValues[bulldozerMachineIndex].StartEndRecordedDataEvents.PutValueAtDate(baseTime, ProductionEventType.StartEvent); siteModel.MachinesTargetValues[bulldozerMachineIndex].StartEndRecordedDataEvents.PutValueAtDate(baseTime + (numCellPasses - 1) * timeIncrement, ProductionEventType.EndEvent); siteModel.MachinesTargetValues[bulldozerMachineIndex].SaveMachineEventsToPersistentStore(siteModel.PrimaryStorageProxy); DITAGFileAndSubGridRequestsFixture.AddSingleCellWithPasses (siteModel, SubGridTreeConsts.DefaultIndexOriginOffset, SubGridTreeConsts.DefaultIndexOriginOffset, cellPasses, 1, cellPasses.Count()); DITAGFileAndSubGridRequestsFixture.ConvertSiteModelToImmutable(siteModel); return(siteModel); }
private ClientLeafSubGrid NewClientSubGrid() { var subGrid = new ClientHeightLeafSubGrid { OriginX = SubGridTreeConsts.DefaultIndexOriginOffset, OriginY = SubGridTreeConsts.DefaultIndexOriginOffset }; subGrid.FillWithTestPattern(); return(subGrid); }
public void Test_HeightClientLeafSubGridTests_SetToZero() { ClientHeightLeafSubGrid subgrid = new ClientHeightLeafSubGrid(null, null, SubGridTreeConsts.SubGridTreeLevels, 1, SubGridTreeConsts.DefaultIndexOriginOffset); subgrid.SetToZeroHeight(); Assert.Equal((int)subgrid.CountNonNullCells(), SubGridTreeConsts.CellsPerSubGrid); int NumEqualZero = 0; ClientHeightLeafSubGrid.ForEachStatic((x, y) => { if (subgrid.Cells[x, y] == 0.0) { NumEqualZero++; } }); Assert.Equal((int)NumEqualZero, SubGridTreeConsts.CellsPerSubGrid); }
public void Test_PatchTask_TransferResponse() { var pipeLine = new Mock <ISubGridPipelineBase>(); pipeLine.Setup(mk => mk.Aborted).Returns(false); PatchTask task = new PatchTask { PipeLine = pipeLine.Object }; ClientHeightLeafSubGrid transferSubgrid = new ClientHeightLeafSubGrid(null, null, 0, 1, 0); task.TransferResponse(new IClientLeafSubGrid[] { transferSubgrid }); Assert.True(task.PatchSubGrids.Count == 1, $"Count of transferred subgrids not 1 as expected (= {task.PatchSubGrids.Count}"); Assert.True(task.PatchSubGrids[0] == transferSubgrid, $"Transferred subgrid is not the same as the one passed into the task."); }
public void Test_ElevationStatisticsAggregator_ProcessResult_NoAggregation() { var aggregator = new ElevationStatisticsAggregator(); var clientGrid = new ClientHeightLeafSubGrid(); clientGrid.FillWithTestPattern(); var dLength = clientGrid.Cells.Length; aggregator.CellSize = TestConsts.CELL_SIZE; IClientLeafSubGrid[][] subGrids = new[] { new[] { clientGrid } }; aggregator.ProcessSubGridResult(subGrids); Assert.True(aggregator.CellsScanned == dLength, "Invalid value for CellsScanned."); Assert.True(aggregator.CellsUsed == dLength, "Invalid value for CellsUsed."); Assert.True(Math.Abs(aggregator.CoverageArea - dLength * Math.Pow(aggregator.CellSize, 2)) < Consts.TOLERANCE_AREA, "Invalid value for CoverageArea."); Assert.True(Math.Abs(aggregator.MinElevation) < Consts.TOLERANCE_HEIGHT, "Invalid value for MinElevation."); Assert.True(Math.Abs(aggregator.MaxElevation - MAX_ELEVATION) < Consts.TOLERANCE_HEIGHT, "Invalid value for MaxElevation."); }
/// <summary> /// Populate result grid from processed subgrid /// </summary> /// <param name="subGrid"></param> private void ExtractRequiredValues(ClientHeightLeafSubGrid subGrid) { var worldExtents = subGrid.WorldExtents(); var subGridWorldOriginY = worldExtents.MinY; var subGridWorldOriginX = worldExtents.MinX; var topX = worldExtents.MaxX; var topY = worldExtents.MaxY; float elev; // Work out the x/y range across our grid we will lookup values double rangeMinX = (subGridWorldOriginX - TileMinX) / GridIntervalX; var posX = (int)(Math.Truncate(rangeMinX)); TileRangeMinX = (posX == (int)rangeMinX) ? (int)rangeMinX : posX + 1; if (TileRangeMinX < 0) { TileRangeMinX = 0; } double rangeMinY = (subGridWorldOriginY - TileMinY) / GridIntervalY; var posY = (int)(Math.Truncate(rangeMinY)); TileRangeMinY = (posY == (int)rangeMinY) ? (int)rangeMinY : posY + 1; if (TileRangeMinY < 0) { TileRangeMinY = 0; } double rangeMaxX = (topX - TileMinX) / GridIntervalX; TileRangeMaxX = (int)(Math.Truncate(rangeMaxX)); if (TileRangeMaxX > GridSize - 1) { TileRangeMaxX = GridSize - 1; } double rangeMaxY = (topY - TileMinY) / GridIntervalY; TileRangeMaxY = (int)(Math.Truncate(rangeMaxY)); if (TileRangeMaxY > GridSize - 1) { TileRangeMaxY = GridSize - 1; } // Iterate over our grid and extract cell heights from subgrid for (int y = TileRangeMinY; y <= TileRangeMaxY; y++) { for (int x = TileRangeMinX; x <= TileRangeMaxX; x++) { // based on grid position lookup cell value // use grid's easting northing value to find subgrid cell we are interested in SubGridTree.CalculateIndexOfCellContainingPosition(GriddedElevDataArray[x, y].Easting, GriddedElevDataArray[x, y].Northing, subGrid.CellSize, subGrid.IndexOriginOffset, out int CellX, out int CellY); subGrid.GetOTGLeafSubGridCellIndex(CellX, CellY, out var subGridX, out var subGridY); // if we have a valid height add it to our data grid if (subGrid.Cells[subGridX, subGridY] != CellPassConsts.NullHeight) { elev = subGrid.Cells[subGridX, subGridY]; GriddedElevDataArray[x, y].Elevation = elev; if (elev < MinElevation) { MinElevation = elev; } if (elev > MaxElevation) { MaxElevation = elev; } } } } }
protected void ProcessVolumeInformationForSubGrid(ClientHeightLeafSubGrid baseScanSubGrid, ClientHeightLeafSubGrid topScanSubGrid) { _log.LogDebug("In ProcessVolumeInformationForSubGrid"); try { // DesignHeights represents all the valid spot elevations for the cells in the // sub grid being processed (IClientHeightLeafSubGrid designHeights, DesignProfilerRequestResult profilerRequestResult)getDesignHeightsResult = (null, DesignProfilerRequestResult.UnknownError); // FCellArea is a handy place to store the cell area, rather than calculate it all the time (value wont change); var cellArea = CellSize * CellSize; // Query the patch of elevations from the surface model for this sub grid if (ActiveDesign?.Design != null) { _log.LogDebug("About to call ActiveDesign.Design.GetDesignHeightsViaLocalCompute()"); getDesignHeightsResult = ActiveDesign.Design.GetDesignHeightsViaLocalCompute(SiteModel, ActiveDesign.Offset, baseScanSubGrid.OriginAsCellAddress(), CellSize); if (getDesignHeightsResult.profilerRequestResult != DesignProfilerRequestResult.OK && getDesignHeightsResult.profilerRequestResult != DesignProfilerRequestResult.NoElevationsInRequestedPatch) { _log.LogError($"Design profiler sub grid elevation request for {baseScanSubGrid.OriginAsCellAddress()} failed with error {getDesignHeightsResult.profilerRequestResult}"); return; } } var bits = new SubGridTreeBitmapSubGridBits(SubGridBitsCreationOptions.Unfilled); // ReSharper disable once CompareOfFloatsByEqualityOperator var standardVolumeProcessing = LiftParams.TargetLiftThickness == Consts.NullHeight || LiftParams.TargetLiftThickness <= 0; var localCellsScanned = 0; var localCellsUsed = 0; var localCellsDiscarded = 0; var localCellsUsedCut = 0; var localCellsUsedFill = 0; var localVolume = 0.0d; var localCutFillVolume = new CutFillVolume(0, 0); // If we are interested in standard volume processing use this cycle if (standardVolumeProcessing) { localCellsScanned += SubGridTreeConsts.SubGridTreeCellsPerSubGrid; for (var i = 0; i < SubGridTreeConsts.SubGridTreeDimension; i++) { for (var j = 0; j < SubGridTreeConsts.SubGridTreeDimension; j++) { float topZ; var baseZ = baseScanSubGrid.Cells[i, j]; // If the user has configured a first pass thickness, then we need to subtract this height // difference from the BaseZ retrieved from the current cell if this measured height was // the first pass made in the cell. if (LiftParams.FirstPassThickness > 0) { baseZ -= LiftParams.FirstPassThickness; } if (VolumeType == VolumeComputationType.BetweenFilterAndDesign || VolumeType == VolumeComputationType.BetweenDesignAndFilter) { topZ = getDesignHeightsResult.designHeights?.Cells[i, j] ?? Consts.NullHeight; if (VolumeType == VolumeComputationType.BetweenDesignAndFilter) { MinMax.Swap(ref baseZ, ref topZ); } } else { topZ = topScanSubGrid.Cells[i, j]; } switch (VolumeType) { case VolumeComputationType.None: break; case VolumeComputationType.AboveLevel: { // ReSharper disable once CompareOfFloatsByEqualityOperator if (baseZ != Consts.NullHeight) { localCellsUsed++; if (baseZ > BaseLevel) { localVolume += cellArea * (baseZ - BaseLevel); } } else { localCellsDiscarded++; } break; } case VolumeComputationType.Between2Levels: { // ReSharper disable once CompareOfFloatsByEqualityOperator if (baseZ != Consts.NullHeight) { localCellsUsed++; if (baseZ > BaseLevel) { localVolume += cellArea * (baseZ < TopLevel ? (baseZ - BaseLevel) : (TopLevel - BaseLevel)); } } else { localCellsDiscarded++; } break; } case VolumeComputationType.AboveFilter: case VolumeComputationType.Between2Filters: case VolumeComputationType.BetweenFilterAndDesign: case VolumeComputationType.BetweenDesignAndFilter: { // ReSharper disable once CompareOfFloatsByEqualityOperator if (baseZ != Consts.NullHeight && // ReSharper disable once CompareOfFloatsByEqualityOperator topZ != Consts.NullHeight) { localCellsUsed++; // Note the fact we have processed this cell in the coverage map bits.SetBit(i, j); var cellUsedInVolumeCalc = (topZ - baseZ >= FillTolerance) || (baseZ - topZ >= CutTolerance); // Accumulate volumes if (cellUsedInVolumeCalc) { var volumeDifference = cellArea * (topZ - baseZ); // Accumulate the 'surplus' volume. Ie: the simple summation of // all cuts and fills. localVolume += volumeDifference; // Accumulate the cuts and fills into discrete cut and fill quantities if (topZ < baseZ) { localCellsUsedCut++; localCutFillVolume.AddCutVolume(Math.Abs(volumeDifference)); } else { localCellsUsedFill++; localCutFillVolume.AddFillVolume(Math.Abs(volumeDifference)); } } else { // Note the fact there was no volume change in this cell // NoChangeMap.Cells[BaseScanSubGrid.OriginX + I, BaseScanSubGrid.OriginY + J] := True; } } else { localCellsDiscarded++; } } break; default: _log.LogError($"Unknown volume type {VolumeType} in ProcessVolumeInformationForSubGrid()"); break; } } } } // ReSharper disable once CompareOfFloatsByEqualityOperator var targetLiftThicknessCalculationsRequired = LiftParams.TargetLiftThickness != Consts.NullHeight && LiftParams.TargetLiftThickness > 0; //If we are interested in thickness calculations do them if (targetLiftThicknessCalculationsRequired) { var belowToleranceToCheck = LiftParams.TargetLiftThickness - LiftParams.BelowToleranceLiftThickness; var aboveToleranceToCheck = LiftParams.TargetLiftThickness + LiftParams.AboveToleranceLiftThickness; SubGridUtilities.SubGridDimensionalIterator((i, j) => { var baseZ = baseScanSubGrid.Cells[i, j]; var topZ = topScanSubGrid.Cells[i, j]; // ReSharper disable once CompareOfFloatsByEqualityOperator if (baseZ != Consts.NullHeight || // ReSharper disable once CompareOfFloatsByEqualityOperator topZ != Consts.NullHeight) { localCellsScanned++; } //Test if we don't have NULL values and carry on // ReSharper disable once CompareOfFloatsByEqualityOperator if (baseZ != Consts.NullHeight && // ReSharper disable once CompareOfFloatsByEqualityOperator topZ != Consts.NullHeight) { bits.SetBit(i, j); double elevationDiff = topZ - baseZ; if (elevationDiff <= aboveToleranceToCheck && elevationDiff >= belowToleranceToCheck) { localCellsUsed++; } else if (elevationDiff > aboveToleranceToCheck) { localCellsUsedFill++; } else if (elevationDiff < belowToleranceToCheck) { localCellsUsedCut++; } } else { localCellsDiscarded++; } }); } // Update the quantities in the aggregator proper // Note: the lock is not asynchronous as this will be highly non-contended _lock.Wait(); try { CellsScanned += localCellsScanned; CellsUsed += localCellsUsed; CellsDiscarded += localCellsDiscarded; CellsUsedCut += localCellsUsedCut; CellsUsedFill += localCellsUsedFill; Volume += localVolume; CutFillVolume.AddCutFillVolume(localCutFillVolume.CutVolume, localCutFillVolume.FillVolume); // Record the bits for this sub grid in the coverage map by requesting the whole sub grid // of bits from the leaf level and setting it in one operation under an exclusive lock if (!bits.IsEmpty()) { var coverageMapSubGrid = CoverageMap.ConstructPathToCell(baseScanSubGrid.OriginX, baseScanSubGrid.OriginY, SubGridPathConstructionType.CreateLeaf); ((SubGridTreeLeafBitmapSubGrid)coverageMapSubGrid).Bits = bits; } } finally { _lock.Release(); } } catch (Exception e) { _log.LogError(e, "Exception thrown by ProcessVolumeInformationForSubGrid - rethrowing"); throw; } finally { _log.LogDebug("Out ProcessVolumeInformationForSubGrid"); } }
/// <summary> /// Performs the donkey work of the elevation patch calculation /// </summary> private IClientHeightLeafSubGrid Calc(ISiteModelBase siteModel, DesignOffset referenceDesign, double cellSize, int originX, int originY, out DesignProfilerRequestResult calcResult) { calcResult = DesignProfilerRequestResult.UnknownError; if (_isTraceLoggingEnabled) { _log.LogTrace("About to lock design"); } var design = Designs.Lock(referenceDesign.DesignID, siteModel, cellSize, out var lockResult); if (design == null) { _log.LogWarning($"Failed to read design file for design {referenceDesign.DesignID}"); calcResult = lockResult == DesignLoadResult.DesignDoesNotExist ? DesignProfilerRequestResult.DesignDoesNotExist : DesignProfilerRequestResult.FailedToLoadDesignFile; return(null); } try { if (_isTraceLoggingEnabled) { _log.LogTrace("Computing sub grid elevation patch"); } // Check to see if this sub grid has any design surface underlying it // from which to calculate an elevation patch. If not, don't bother... if (!design.HasElevationDataForSubGridPatch(originX >> SubGridTreeConsts.SubGridIndexBitsPerLevel, originY >> SubGridTreeConsts.SubGridIndexBitsPerLevel)) { calcResult = DesignProfilerRequestResult.NoElevationsInRequestedPatch; return(null); } var result = new ClientHeightLeafSubGrid(null, null, SubGridTreeConsts.SubGridTreeLevels, cellSize, SubGridTreeConsts.DefaultIndexOriginOffset); result.SetAbsoluteOriginPosition(originX & ~SubGridTreeConsts.SubGridLocalKeyMask, originY & ~SubGridTreeConsts.SubGridLocalKeyMask); result.CalculateWorldOrigin(out var worldOriginX, out var worldOriginY); calcResult = design.InterpolateHeights(result.Cells, worldOriginX, worldOriginY, cellSize, referenceDesign.Offset) ? DesignProfilerRequestResult.OK : DesignProfilerRequestResult.NoElevationsInRequestedPatch; if (_isTraceLoggingEnabled) { _log.LogTrace("Computed sub grid elevation patch"); } return(result); } finally { if (_isTraceLoggingEnabled) { _log.LogTrace("Unlocking design"); } Designs.UnLock(referenceDesign.DesignID, design); if (_isTraceLoggingEnabled) { _log.LogTrace("Completed calculating design elevations"); } } }