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"); } }