//Based on http://www.holoborodko.com/pavel/downloads/NoiseRobustSecondDerivative.pdf unsafe void CalculateCrossSectionSecondDerivs(VoxelCrossSection[] vehicleCrossSection, int oneSidedFilterLength, int frontIndex, int backIndex, double sectionThickness) { int M, N; if (oneSidedFilterLength < 2) { oneSidedFilterLength = 2; ThreadSafeDebugLogger.Instance.RegisterMessage("Needed to adjust filter length up"); } else if (oneSidedFilterLength > 40) { oneSidedFilterLength = 40; ThreadSafeDebugLogger.Instance.RegisterMessage("Reducing filter length to prevent overflow"); } M = oneSidedFilterLength; N = M * 2 + 1; long* sK = stackalloc long[M + 1]; //double* areas = stackalloc double[N + 2]; for (int i = 0; i <= M; i++) { sK[i] = CalculateSk(i, M, N); } double denom = Math.Pow(2, N - 3); denom *= sectionThickness * sectionThickness; denom = 1 / denom; int lowIndex, highIndex; lowIndex = Math.Max(frontIndex - 1, 0); highIndex = Math.Min(backIndex + 1, vehicleCrossSection.Length - 1); for (int i = lowIndex + M; i <= highIndex - M; i++) { double secondDeriv = 0; if(i >= frontIndex && i <= backIndex) secondDeriv = sK[0] * vehicleCrossSection[i].area; for (int k = 1; k <= M; k++) { double forwardArea, backwardArea; if (i + k <= backIndex) backwardArea = vehicleCrossSection[i + k].area; else backwardArea = 0;// (vehicleCrossSection[backIndex].area - vehicleCrossSection[backIndex - 1].area) * (i + k - backIndex) + vehicleCrossSection[backIndex].area; if (i - k >= frontIndex) forwardArea = vehicleCrossSection[i - k].area; else forwardArea = 0;// (vehicleCrossSection[frontIndex].area - vehicleCrossSection[frontIndex + 1].area) * (i - k - frontIndex) + vehicleCrossSection[frontIndex].area; ;// vehicleCrossSection[frontIndex].area; secondDeriv += sK[k] * (forwardArea + backwardArea); } vehicleCrossSection[i].secondAreaDeriv = secondDeriv * denom; //ThreadSafeDebugLogger.Instance.RegisterMessage(vehicleCrossSection[i].secondAreaDeriv.ToString()); } //forward difference for (int i = frontIndex; i < lowIndex + M; i++) { double secondDeriv = 0; secondDeriv += vehicleCrossSection[i].area; if(i + 2 <= backIndex) secondDeriv += vehicleCrossSection[i + 2].area; if (i + 1 <= backIndex) secondDeriv -= 2 * vehicleCrossSection[i + 1].area; secondDeriv /= sectionThickness * sectionThickness; vehicleCrossSection[i].secondAreaDeriv = secondDeriv; } //backward difference for (int i = highIndex - M + 1; i <= backIndex; i++) { double secondDeriv = 0; secondDeriv += vehicleCrossSection[i].area; if (i - 2 >= frontIndex) secondDeriv += vehicleCrossSection[i - 2].area; if (i - 1 >= frontIndex) secondDeriv -= 2 * vehicleCrossSection[i - 1].area; secondDeriv /= sectionThickness * sectionThickness; vehicleCrossSection[i].secondAreaDeriv = secondDeriv; } }
unsafe void AdjustCrossSectionForAirDucting(VoxelCrossSection[] vehicleCrossSection, List<GeometryPartModule> geometryModules, int front, int back, ref double maxCrossSectionArea) { //double* areaAdjustment = stackalloc double[vehicleCrossSection.Length]; for (int i = 0; i < geometryModules.Count; i++) { GeometryPartModule g = geometryModules[i]; g.GetICrossSectionAdjusters(activeAdjusters, _worldToLocalMatrix, _vehicleMainAxis); } double intakeArea = 0; double engineExitArea = 0; for (int i = 0; i < activeAdjusters.Count; i++) //get all forward facing engines / intakes { ICrossSectionAdjuster adjuster = activeAdjusters[i]; if (adjuster is AirbreathingEngineCrossSectonAdjuster) engineExitArea += Math.Abs(adjuster.AreaRemovedFromCrossSection()); if (adjuster is IntakeCrossSectionAdjuster) intakeArea += Math.Abs(adjuster.AreaRemovedFromCrossSection()); if(adjuster is IntegratedIntakeEngineCrossSectionAdjuster) { engineExitArea += Math.Abs(adjuster.AreaRemovedFromCrossSection()); intakeArea += Math.Abs(adjuster.AreaRemovedFromCrossSection()); } } //ThreadSafeDebugLogger.Instance.RegisterMessage(intakeArea + " " + engineExitArea); if (intakeArea != 0 && engineExitArea != 0) //if they exist, go through the calculations { if (_ductedAreaAdjustment.Length != vehicleCrossSection.Length) _ductedAreaAdjustment = new double[vehicleCrossSection.Length]; int frontMostIndex = -1, backMostIndex = -1; //sweep through entire vehicle for (int i = 0; i < _ductedAreaAdjustment.Length; i++) { double ductedArea = 0; //area based on the voxel size //double actualArea = 0; //area based on intake and engine data double voxelCountScale = _voxelElementSize * _voxelElementSize; //and all the intakes / engines if (i >= front && i <= back) { for (int j = 0; j < activeAdjusters.Count; j++) { ICrossSectionAdjuster adjuster = activeAdjusters[j]; if (adjuster.IntegratedCrossSectionIncreaseDecrease()) continue; if (adjuster.AreaRemovedFromCrossSection() == 0) continue; VoxelCrossSection.SideAreaValues val; Part p = adjuster.GetPart(); //see if you can find that in this section if (vehicleCrossSection[i].partSideAreaValues.TryGetValue(p, out val)) { if (adjuster.AreaRemovedFromCrossSection() > 0) { //actualArea += adjuster.AreaRemovedFromCrossSection(); ductedArea += Math.Max(0, val.crossSectionalAreaCount * voxelCountScale + adjuster.AreaThreshold()); } else { //actualArea -= adjuster.AreaRemovedFromCrossSection(); ductedArea -= Math.Max(0, val.crossSectionalAreaCount * voxelCountScale + adjuster.AreaThreshold()); } } } ductedArea *= 0.75; //if (Math.Abs(actualArea) < Math.Abs(ductedArea)) // ductedArea = actualArea; if (ductedArea != 0) if (frontMostIndex < 0) frontMostIndex = i; else backMostIndex = i; } _ductedAreaAdjustment[i] = ductedArea; } double tmpArea = _ductedAreaAdjustment[0]; for (int i = 1; i < _ductedAreaAdjustment.Length; i++) { double areaAdjustment = _ductedAreaAdjustment[i]; double prevAreaAdjustment = tmpArea; tmpArea = areaAdjustment; //store for next iteration if (areaAdjustment > 0 && prevAreaAdjustment > 0) { double areaChange = areaAdjustment - prevAreaAdjustment; if (areaChange > 0) _ductedAreaAdjustment[i] = areaChange; //this transforms this into a change in area, but only for increases (intakes) else { tmpArea = prevAreaAdjustment; _ductedAreaAdjustment[i] = 0; } } } tmpArea = _ductedAreaAdjustment[_ductedAreaAdjustment.Length - 1]; for (int i = _ductedAreaAdjustment.Length - 1; i >= 0; i--) { double areaAdjustment = _ductedAreaAdjustment[i]; double prevAreaAdjustment = tmpArea; tmpArea = areaAdjustment; //store for next iteration if (areaAdjustment < 0 && prevAreaAdjustment < 0) { double areaChange = areaAdjustment - prevAreaAdjustment; if (areaChange < 0) _ductedAreaAdjustment[i] = areaChange; //this transforms this into a change in area, but only for decreases (engines) else { tmpArea = prevAreaAdjustment; _ductedAreaAdjustment[i] = 0; } } } for (int i = _ductedAreaAdjustment.Length - 1; i >= 0; i--) { double areaAdjustment = 0; for (int j = 0; j <= i; j++) areaAdjustment += _ductedAreaAdjustment[j]; _ductedAreaAdjustment[i] = areaAdjustment; //ThreadSafeDebugLogger.Instance.RegisterMessage(areaAdjustment.ToString()); } for (int i = 0; i < vehicleCrossSection.Length; i++) { double ductedArea = 0; //area based on the voxel size double actualArea = 0; //area based on intake and engine data //and all the intakes / engines for (int j = 0; j < activeAdjusters.Count; j++) { ICrossSectionAdjuster adjuster = activeAdjusters[j]; if (!adjuster.IntegratedCrossSectionIncreaseDecrease()) continue; VoxelCrossSection.SideAreaValues val; Part p = adjuster.GetPart(); //see if you can find that in this section if (vehicleCrossSection[i].partSideAreaValues.TryGetValue(p, out val)) { ductedArea += val.crossSectionalAreaCount; actualArea += adjuster.AreaRemovedFromCrossSection(); } //ThreadSafeDebugLogger.Instance.RegisterMessage(ductedArea.ToString()); } ductedArea *= _voxelElementSize * _voxelElementSize * 0.75; if (Math.Abs(actualArea) < Math.Abs(ductedArea)) ductedArea = actualArea; if (ductedArea != 0) if (i < frontMostIndex) frontMostIndex = i; else if (i > backMostIndex) backMostIndex = i; _ductedAreaAdjustment[i] += ductedArea; } int index = _ductedAreaAdjustment.Length - 1; double endVoxelArea = _ductedAreaAdjustment[index]; double currentArea = endVoxelArea; while(currentArea > 0) { currentArea -= endVoxelArea; _ductedAreaAdjustment[index] = currentArea; --index; if (index < 0) break; currentArea = _ductedAreaAdjustment[index]; } maxCrossSectionArea = 0; //put upper limit on area lost for (int i = 0; i < vehicleCrossSection.Length; i++) { double areaUnchanged = vehicleCrossSection[i].area; double areaChanged = -_ductedAreaAdjustment[i]; if (areaChanged > 0) areaChanged = 0; areaChanged += areaUnchanged; double tmpTotalArea = Math.Max(0.15 * areaUnchanged, areaChanged); if (tmpTotalArea > maxCrossSectionArea) maxCrossSectionArea = tmpTotalArea; vehicleCrossSection[i].area = tmpTotalArea; } } activeAdjusters.Clear(); }
private double CriticalMachFactorForUnsmoothCrossSection(VoxelCrossSection[] crossSections, double finenessRatio, double sectionThickness) { double maxAbsRateOfChange = 0; double maxSecondDeriv = 0; double prevArea = 0; double invSectionThickness = 1/ sectionThickness; for(int i = 0; i < crossSections.Length; i++) { double currentArea = crossSections[i].area; double absRateOfChange = Math.Abs(currentArea - prevArea) * invSectionThickness; if (absRateOfChange > maxAbsRateOfChange) maxAbsRateOfChange = absRateOfChange; prevArea = currentArea; maxSecondDeriv = Math.Max(maxSecondDeriv, Math.Abs(crossSections[i].secondAreaDeriv)); } //double normalizedRateOfChange = maxAbsRateOfChange / _maxCrossSectionArea; double maxCritMachAdjustmentFactor = 2 * _maxCrossSectionArea + 5 * (0.5 * maxAbsRateOfChange + 0.3 * maxSecondDeriv); maxCritMachAdjustmentFactor = 0.5 + (_maxCrossSectionArea - 0.5 * (0.5 * maxAbsRateOfChange + 0.3 * maxSecondDeriv)) / maxCritMachAdjustmentFactor; //will vary based on x = maxAbsRateOfChange / _maxCrossSectionArea from 1 @ x = 0 to 0.5 as x -> infinity double critAdjustmentFactor = 4 + finenessRatio; critAdjustmentFactor = 2 * (1 - maxCritMachAdjustmentFactor) / critAdjustmentFactor; critAdjustmentFactor += maxCritMachAdjustmentFactor; if (critAdjustmentFactor > 1) critAdjustmentFactor = 1; return critAdjustmentFactor; }
//Smooths out area and area 2nd deriv distributions to deal with noise in the representation unsafe void GaussianSmoothCrossSections(VoxelCrossSection[] vehicleCrossSection, double stdDevCutoff, double lengthPercentFactor, double sectionThickness, double length, int frontIndex, int backIndex, int areaSmoothingIterations, int derivSmoothingIterations) { double stdDev = length * lengthPercentFactor; int numVals = (int)Math.Ceiling(stdDevCutoff * stdDev / sectionThickness); if (numVals <= 1) return; double* gaussianFactors = stackalloc double[numVals]; double* prevUncorrectedVals = stackalloc double[numVals]; double* futureUncorrectedVals = stackalloc double[numVals - 1]; /* double[] gaussianFactors = new double[numVals]; double[] prevUncorrectedVals = new double[numVals]; double[] futureUncorrectedVals = new double[numVals - 1];*/ double invVariance = 1 / (stdDev * stdDev); //calculate Gaussian factors for each of the points that will be hit for (int i = 0; i < numVals; i++) { double factor = (i * sectionThickness); factor *= factor; gaussianFactors[i] = Math.Exp(-0.5 * factor * invVariance); } //then sum them up... double sum = 0; for (int i = 0; i < numVals; i++) if (i == 0) sum += gaussianFactors[i]; else sum += 2 * gaussianFactors[i]; double invSum = 1 / sum; //and then use that to normalize the factors for (int i = 0; i < numVals; i++) { gaussianFactors[i] *= invSum; } //first smooth the area itself. This has a greater effect on the 2nd deriv due to the effect of noise on derivatives for (int j = 0; j < areaSmoothingIterations; j++) { for (int i = 0; i < numVals; i++) prevUncorrectedVals[i] = vehicleCrossSection[frontIndex].area; //set all the vals to 0 to prevent screwups between iterations for (int i = frontIndex; i <= backIndex; i++) //area smoothing pass { for (int k = numVals - 1; k > 0; k--) { prevUncorrectedVals[k] = prevUncorrectedVals[k - 1]; //shift prev vals down } double curValue = vehicleCrossSection[i].area; prevUncorrectedVals[0] = curValue; //and set the central value for (int k = 0; k < numVals - 1; k++) //update future vals { if (i + k < backIndex) futureUncorrectedVals[k] = vehicleCrossSection[i + k + 1].area; else futureUncorrectedVals[k] = vehicleCrossSection[backIndex].area; } curValue = 0; //zero for coming calculations... double borderScaling = 1; //factor to correct for the 0s lurking at the borders of the curve... for (int k = 0; k < numVals; k++) { double val = prevUncorrectedVals[k]; double gaussianFactor = gaussianFactors[k]; curValue += gaussianFactor * val; //central and previous values; if (val == 0) borderScaling -= gaussianFactor; } for (int k = 0; k < numVals - 1; k++) { double val = futureUncorrectedVals[k]; double gaussianFactor = gaussianFactors[k + 1]; curValue += gaussianFactor * val; //future values if (val == 0) borderScaling -= gaussianFactor; } if (borderScaling > 0) curValue /= borderScaling; //and now all of the 0s beyond the edge have been removed vehicleCrossSection[i].area = curValue; } } CalculateCrossSectionSecondDerivs(vehicleCrossSection, numVals, frontIndex, backIndex, sectionThickness); //and now smooth the derivs for (int j = 0; j < derivSmoothingIterations; j++) { for (int i = 0; i < numVals; i++) prevUncorrectedVals[i] = vehicleCrossSection[frontIndex].secondAreaDeriv; //set all the vals to 0 to prevent screwups between iterations for (int i = frontIndex; i <= backIndex; i++) //deriv smoothing pass { for (int k = numVals - 1; k > 0; k--) { prevUncorrectedVals[k] = prevUncorrectedVals[k - 1]; //shift prev vals down } double curValue = vehicleCrossSection[i].secondAreaDeriv; prevUncorrectedVals[0] = curValue; //and set the central value for (int k = 0; k < numVals - 1; k++) //update future vals { if (i + k < backIndex) futureUncorrectedVals[k] = vehicleCrossSection[i + k + 1].secondAreaDeriv; else futureUncorrectedVals[k] = vehicleCrossSection[backIndex].secondAreaDeriv; } curValue = 0; //zero for coming calculations... double borderScaling = 1; //factor to correct for the 0s lurking at the borders of the curve... for (int k = 0; k < numVals; k++) { double val = prevUncorrectedVals[k]; double gaussianFactor = gaussianFactors[k]; curValue += gaussianFactor * val; //central and previous values; if (val == 0) borderScaling -= gaussianFactor; } for (int k = 0; k < numVals - 1; k++) { double val = futureUncorrectedVals[k]; double gaussianFactor = gaussianFactors[k + 1]; curValue += gaussianFactor * val; //future values if (val == 0) borderScaling -= gaussianFactor; } if (borderScaling > 0) curValue /= borderScaling; //and now all of the 0s beyond the edge have been removed vehicleCrossSection[i].secondAreaDeriv = curValue; } } }
private double CalcAllTransonicWaveDrag(VoxelCrossSection[] sections, int front, int numSections, double sectionThickness) { double drag = 0; double Lj; for(int j = 0; j < numSections; j++) { double accumulator = 0; Lj = (j + 0.5) * Math.Log(j + 0.5); if (j > 0) Lj -= (j - 0.5) * Math.Log(j - 0.5); for(int i = j; i < numSections; i++) { accumulator += sections[front + i].secondAreaDeriv * sections[front + i - j].secondAreaDeriv; } drag += accumulator * Lj; } drag *= -sectionThickness * sectionThickness / Math.PI; return drag; }
private double CalculateCpLinearBackward(VoxelCrossSection[] vehicleCrossSection, int index, int back, double beta, double sectionThickness, double maxCrossSection) { double cP = 0; double cutoff = maxCrossSection * 2;//Math.Min(maxCrossSection * 0.1, vehicleCrossSection[index].area * 0.25); double tmp1, tmp2; tmp1 = indexSqrt[back + 2 - index]; for (int i = back + 1; i >= index; i--) { double tmp; //tmp2 = Math.Sqrt(tmp); tmp2 = indexSqrt[i - index]; tmp = tmp1 - tmp2; tmp1 = tmp2; if (i < vehicleCrossSection.Length) tmp *= MathClampAbs(vehicleCrossSection[i].secondAreaDeriv, cutoff); else tmp *= 0; cP += tmp; } double avgArea = vehicleCrossSection[index].area; /*if (index < vehicleCrossSection.Length - 1) { avgArea += vehicleCrossSection[index + 1].area; } avgArea *= 0.5;*/ cP *= Math.Sqrt(0.5 * sectionThickness / (beta * Math.Sqrt(Math.PI * avgArea))); return cP; }
//Taken from Appendix A of NASA TR R-213 private double CalculateCpLinearForward(VoxelCrossSection[] vehicleCrossSection, int index, int front, double beta, double sectionThickness, double maxCrossSection) { double cP = 0; double cutoff = maxCrossSection * 2;//Math.Min(maxCrossSection * 0.1, vehicleCrossSection[index].area * 0.25); double tmp1, tmp2; tmp1 = indexSqrt[index - (front - 2)]; for (int i = front - 1; i <= index; i++) { double tmp; //tmp2 = Math.Sqrt(tmp); tmp2 = indexSqrt[index - i]; tmp = tmp1 - tmp2; tmp1 = tmp2; if (i >= 0) tmp *= MathClampAbs(vehicleCrossSection[i].secondAreaDeriv, cutoff); else tmp *= 0; cP += tmp; } double avgArea = vehicleCrossSection[index].area; /*if(index > 0) { avgArea += vehicleCrossSection[index - 1].area; } avgArea *= 0.5;*/ cP *= Math.Sqrt(0.5 * sectionThickness / (beta * Math.Sqrt(Math.PI * avgArea))); return cP; }
private void CalculateSonicPressure(VoxelCrossSection[] vehicleCrossSection, int front, int back, double sectionThickness, double maxCrossSection) { lock (_commonLocker) if (vehicleCrossSection.Length > indexSqrt.Length + 1) indexSqrt = GenerateIndexSqrtLookup(vehicleCrossSection.Length + 2); double machTest = 1.2; double beta = Math.Sqrt(machTest * machTest - 1); double cP90 = CalcMaxCp(machTest); //double noseAreaSlope = (vehicleCrossSection[front].area) / sectionThickness; for (int i = front; i <= back; i++) { double cP = CalculateCpLinearForward(vehicleCrossSection, i, front, beta, sectionThickness, double.PositiveInfinity); //cP += CalculateCpNoseDiscont(i - front, noseAreaSlope, sectionThickness); cP *= -0.5; cP = AdjustVelForFinitePressure(cP); cP *= -2; if (cP < 0) cP = AdjustCpForNonlinearEffects(cP, beta, machTest); if (cP > cP90) cP = cP90; vehicleCrossSection[i].cpSonicForward = cP; } //noseAreaSlope = (vehicleCrossSection[back].area) / sectionThickness; for (int i = back; i >= front; i--) { double cP = CalculateCpLinearBackward(vehicleCrossSection, i, back, beta, sectionThickness, double.PositiveInfinity); //cP += CalculateCpNoseDiscont(back - i, noseAreaSlope, sectionThickness); cP *= -0.5; cP = AdjustVelForFinitePressure(cP); cP *= -2; if (cP < 0) cP = AdjustCpForNonlinearEffects(cP, beta, machTest); if (cP > cP90) cP = cP90; vehicleCrossSection[i].cpSonicBackward = cP; } }
public void CrossSectionData(VoxelCrossSection[] crossSections, Vector3 orientationVector, out int frontIndex, out int backIndex, out double sectionThickness, out double maxCrossSectionArea) { //TODO: Look into setting better limits for iterating over sweep plane to improve off-axis performance double wInc; Vector4d plane = CalculateEquationOfSweepPlane(orientationVector, out wInc); double x, y, z; x = Math.Abs(plane.x); y = Math.Abs(plane.y); z = Math.Abs(plane.z); double elementArea = elementSize * elementSize; bool frontIndexFound = false; frontIndex = 0; backIndex = crossSections.Length - 1; sectionThickness = elementSize; Matrix4x4 sectionNormalToVesselCoords = Matrix4x4.TRS(Vector3.zero, Quaternion.FromToRotation(new Vector3(0, 0, 1), orientationVector), Vector3.one); Matrix4x4 vesselToSectionNormal = sectionNormalToVesselCoords.inverse; //Code has multiple optimizations to take advantage of the limited rnage of values that are included. They are listed below //(int)Math.Ceiling(x) -> (int)(x + 1) for x > 0 //(int)Math.Round(x) -> (int)(x + 0.5f) for x > 0 //(int)Math.Floor(x) -> (int)(x) for x > 0 //Check y first, since it is most likely to be the flow direction if (y >= z && y >= x) { int sectionCount = yCellLength + (int)(xCellLength * x / y + 1) + (int)(zCellLength * z / y + 1); sectionCount = Math.Min(sectionCount, crossSections.Length); double angleSizeIncreaseFactor = Math.Sqrt((x + y + z) / y); elementArea *= angleSizeIncreaseFactor; //account for different angles effects on voxel cube's projected area double invMag = 1 / Math.Sqrt(x * x + y * y + z * z); sectionThickness *= y * invMag; double xNorm, yNorm, zNorm; xNorm = plane.x * invMag; yNorm = plane.y * invMag; zNorm = plane.z * invMag; double yAbsNorm = Math.Abs(yNorm); int xSectArrayLength = (int)(xCellLength * yAbsNorm + yCellLength * Math.Abs(xNorm) + 1); //Stack allocation of array allows removal of garbage collection issues //bool* sectionArray = stackalloc bool[xSectArrayLength * (int)(zCellLength * yAbsNorm + yCellLength * Math.Abs(zNorm) + 1)]; //bool[,] sectionRepresentation = new bool[(int)Math.Ceiling(xCellLength * Math.Abs(yNorm) + yCellLength * Math.Abs(xNorm)), (int)Math.Ceiling(zCellLength * Math.Abs(yNorm) + yCellLength * Math.Abs(zNorm))]; double invYPlane = 1 / plane.y; plane.x *= invYPlane; plane.z *= invYPlane; plane.w *= invYPlane; wInc *= invYPlane; plane.w -= 0.5; //shift the plane to auto-account for midpoint rounding, allowing it to be simple casts to int for (int m = 0; m < sectionCount; m++) { double areaCount = 0; //Vector3d centroid = Vector3d.zero; double centx, centy, centz; centx = centy = centz = 0; //int unshadowedAreaCount = 0; //Vector3d unshadowedCentroid = Vector3d.zero; //int unshadowedCentx, unshadowedCenty, unshadowedCentz; //unshadowedCentx = unshadowedCenty = unshadowedCentz = 0; double i_xx = 0, i_xy = 0, i_yy = 0; Dictionary<Part, VoxelCrossSection.SideAreaValues> partSideAreas = crossSections[m].partSideAreaValues; partSideAreas.Clear(); for (int iOverall = 0; iOverall < xCellLength; iOverall += 8) //Overall ones iterate over the actual voxel indices (to make use of the equation of the plane) but are used to get chunk indices for (int kOverall = 0; kOverall < zCellLength; kOverall += 8) { int jSect1, jSect2, jSect3; if (plane.x * plane.z > 0.0) //Determine high and low points on this quad of the plane { jSect1 = (int)(-(plane.x * iOverall + plane.z * kOverall + plane.w)); //End points of the plane jSect3 = (int)(-(plane.x * (iOverall + 7) + plane.z * (kOverall + 7) + plane.w)); } else { jSect1 = (int)(-(plane.x * (iOverall + 7) + plane.z * kOverall + plane.w)); jSect3 = (int)(-(plane.x * iOverall + plane.z * (kOverall + 7) + plane.w)); } jSect2 = (int)((jSect1 + jSect3) * 0.5); //Central point int jMin = Math.Min(jSect1, jSect3); int jMax = Math.Max(jSect1, jSect3) + 1; jSect1 = jMin >> 3; jSect2 = jSect2 >> 3; jSect3 = (jMax - 1) >> 3; if (jSect1 >= yLength) //if the smallest sect is above the limit, they all are continue; if (jSect3 < 0) //if the largest sect is below the limit, they all are continue; if (jSect1 == jSect3) //If chunk indices are identical, only get that one and it's very simple; only need to check 1 and 3, because if any are different, it must be those { if (jSect1 < 0) continue; VoxelChunk sect = voxelChunks[iOverall >> 3, jSect1, kOverall >> 3]; if (sect == null) continue; for (int i = iOverall; i < iOverall + 8; i++) //Finally, iterate over the chunk { double tmp = plane.x * i + plane.w; for (int k = kOverall; k < kOverall + 8; k++) { int j = (int)(-(plane.z * k + tmp)); if (j < jMin || j > jMax) continue; int index = i + 8 * j + 64 * k; VoxelChunk.PartSizePair pair = sect.GetVoxelPartSizePairGlobalIndex(index); if ((object)pair.part != null) { DetermineIfPartGetsForcesAndAreas(partSideAreas, pair, i, j, k); double size = pair.GetSize(); if (size > 1.0) size = 1.0; areaCount += size; centx += i * size; centy += j * size; centz += k * size; Vector3 location = vesselToSectionNormal.MultiplyVector(new Vector3(i, j, k)); i_xx += location.x * location.x * size; i_xy += location.x * location.y * size; i_yy += location.y * location.y * size; } } } } else //Two or three different indices requires separate handling { VoxelChunk sect1 = null, sect2 = null, sect3 = null; int iSect = iOverall >> 3; int kSect = kOverall >> 3; bool validSects = false; if (!(jSect1 < 0)) //this block ensures that there are sections here to check { sect1 = voxelChunks[iSect, jSect1, kSect]; if (sect1 != null) validSects = true; } if (!(jSect2 < 0 || jSect2 >= yLength)) { sect2 = voxelChunks[iSect, jSect2, kSect]; if (sect2 != null && sect2 != sect1) validSects |= true; } if (!(jSect3 >= yLength)) { sect3 = voxelChunks[iSect, jSect3, kSect]; if (sect3 != null && (sect3 != sect2 && sect3 != sect1)) validSects |= true; } if (!validSects) continue; for (int i = iOverall; i < iOverall + 8; i++) { double tmp = plane.x * i + plane.w; for (int k = kOverall; k < kOverall + 8; k++) { int j = (int)(-(plane.z * k + tmp)); if (j < jMin || j > jMax) continue; int index = i + 8 * j + 64 * k; VoxelChunk.PartSizePair pair; int jSect = j >> 3; if (jSect == jSect1 && sect1 != null) pair = sect1.GetVoxelPartSizePairGlobalIndex(index); else if (jSect == jSect2 && sect2 != null) pair = sect2.GetVoxelPartSizePairGlobalIndex(index); else if (jSect == jSect3 && sect3 != null) pair = sect3.GetVoxelPartSizePairGlobalIndex(index); else continue; if ((object)pair.part != null) { DetermineIfPartGetsForcesAndAreas(partSideAreas, pair, i, j, k); double size = pair.GetSize(); if (size > 1.0) size = 1.0; areaCount += size; centx += i * size; centy += j * size; centz += k * size; Vector3 location = vesselToSectionNormal.MultiplyVector(new Vector3(i, j, k)); i_xx += location.x * location.x * size; i_xy += location.x * location.y * size; i_yy += location.y * location.y * size; } } } } } Vector3d centroid = new Vector3d(centx, centy, centz); //Vector3d unshadowedCentroid = new Vector3d(unshadowedCentx, unshadowedCenty, unshadowedCentz); if (areaCount > 0) { if (frontIndexFound) backIndex = m; else { frontIndexFound = true; frontIndex = m; } centroid /= areaCount; /*if (unshadowedAreaCount != 0) unshadowedCentroid /= unshadowedAreaCount; else unshadowedCentroid = centroid;*/ } Vector3 localCentroid = vesselToSectionNormal.MultiplyVector(centroid); i_xx -= areaCount * localCentroid.x * localCentroid.x; i_xy -= areaCount * localCentroid.x * localCentroid.y; i_yy -= areaCount * localCentroid.y * localCentroid.y; double tanPrinAngle = TanPrincipalAxisAngle(i_xx, i_yy, i_xy); Vector3 axis1 = new Vector3(1, 0, 0), axis2 = new Vector3(0, 0, 1); double flatnessRatio = 1; if (tanPrinAngle != 0) { axis1 = new Vector3(1, 0, (float)tanPrinAngle); axis1.Normalize(); axis2 = new Vector3(axis1.z, 0, -axis1.x); flatnessRatio = i_xy * axis2.z / axis2.x + i_xx; flatnessRatio = (i_xy * tanPrinAngle + i_xx) / flatnessRatio; flatnessRatio = Math.Sqrt(Math.Sqrt(flatnessRatio)); } if (double.IsNaN(flatnessRatio)) flatnessRatio = 1; Vector3 principalAxis; if (flatnessRatio > 1) principalAxis = axis1; else { flatnessRatio = 1 / flatnessRatio; principalAxis = axis2; } if (flatnessRatio > 10) flatnessRatio = 10; principalAxis = sectionNormalToVesselCoords.MultiplyVector(principalAxis); crossSections[m].centroid = centroid * elementSize + lowerRightCorner; crossSections[m].area = areaCount * elementArea; crossSections[m].flatnessRatio = flatnessRatio; crossSections[m].flatNormalVector = principalAxis; plane.w += wInc; } } else if (x > y && x > z) { int sectionCount = xCellLength + (int)(yCellLength * y / x + 1) + (int)(zCellLength * z / x + 1); sectionCount = Math.Min(sectionCount, crossSections.Length); double angleSizeIncreaseFactor = Math.Sqrt((x + y + z) / x); elementArea *= angleSizeIncreaseFactor; //account for different angles effects on voxel cube's projected area double invMag = 1 / Math.Sqrt(x * x + y * y + z * z); sectionThickness *= x * invMag; double xNorm, yNorm, zNorm; xNorm = plane.x * invMag; yNorm = plane.y * invMag; zNorm = plane.z * invMag; double xAbsNorm = Math.Abs(xNorm); double i_xx = 0, i_xy = 0, i_yy = 0; int ySectArrayLength = (int)(xCellLength * Math.Abs(yNorm) + yCellLength * xAbsNorm + 1); //Stack allocation of array allows removal of garbage collection issues //bool* sectionArray = stackalloc bool[ySectArrayLength * (int)(zCellLength * xAbsNorm + xCellLength * Math.Abs(zNorm) + 1)]; //bool[,] sectionRepresentation = new bool[(int)Math.Ceiling(xCellLength * Math.Abs(yNorm) + yCellLength * Math.Abs(xNorm)), (int)Math.Ceiling(zCellLength * Math.Abs(xNorm) + xCellLength * Math.Abs(zNorm))]; double invXPlane = 1 / plane.x; plane.y *= invXPlane; plane.z *= invXPlane; plane.w *= invXPlane; wInc *= invXPlane; for (int m = 0; m < sectionCount; m++) { double areaCount = 0; //Vector3d centroid = Vector3d.zero; double centx, centy, centz; centx = centy = centz = 0; //int unshadowedAreaCount = 0; //Vector3d unshadowedCentroid = Vector3d.zero; //int unshadowedCentx, unshadowedCenty, unshadowedCentz; //unshadowedCentx = unshadowedCenty = unshadowedCentz = 0; Dictionary<Part, VoxelCrossSection.SideAreaValues> partSideAreas = crossSections[m].partSideAreaValues; partSideAreas.Clear(); for (int jOverall = 0; jOverall < yCellLength; jOverall += 8) for (int kOverall = 0; kOverall < zCellLength; kOverall += 8) { int iSect1, iSect2, iSect3; if (plane.y * plane.z > 0) { iSect1 = (int)(-(plane.y * jOverall + plane.z * kOverall + plane.w) + 0.5); iSect3 = (int)(-(plane.y * (jOverall + 7) + plane.z * (kOverall + 7) + plane.w) + 0.5); } else { iSect1 = (int)(-(plane.y * (jOverall + 7) + plane.z * kOverall + plane.w) + 0.5); iSect3 = (int)(-(plane.y * jOverall + plane.z * (kOverall + 7) + plane.w) + 0.5); } iSect2 = (int)((iSect1 + iSect3) * 0.5); int iMin = Math.Min(iSect1, iSect3); int iMax = Math.Max(iSect1, iSect3) + 1; iSect1 = iMin >> 3; iSect2 = iSect2 >> 3; iSect3 = (iMax - 1) >> 3; if (iSect1 >= xLength) //if the smallest sect is above the limit, they all are continue; if (iSect3 < 0) //if the largest sect is below the limit, they all are continue; if (iSect1 == iSect3) { if (iSect1 < 0) continue; VoxelChunk sect = voxelChunks[iSect1, jOverall >> 3, kOverall >> 3]; if (sect == null) continue; for (int j = jOverall; j < jOverall + 8; j++) { double tmp = plane.y * j + plane.w; for (int k = kOverall; k < kOverall + 8; k++) { int i = (int)(-(plane.z * k + tmp) + 0.5); if (i < iMin || i > iMax) continue; int index = i + 8 * j + 64 * k; VoxelChunk.PartSizePair pair = sect.GetVoxelPartSizePairGlobalIndex(index); if ((object)pair.part != null) { DetermineIfPartGetsForcesAndAreas(partSideAreas, pair, i, j, k); double size = pair.GetSize(); if (size > 1.0) size = 1.0; areaCount += size; centx += i * size; centy += j * size; centz += k * size; Vector3 location = vesselToSectionNormal.MultiplyVector(new Vector3(i, j, k)); i_xx += location.x * location.x * size; i_xy += location.x * location.y * size; i_yy += location.y * location.y * size; } } } } else { VoxelChunk sect1 = null, sect2 = null, sect3 = null; int jSect = jOverall >> 3; int kSect = kOverall >> 3; bool validSects = false; if (!(iSect1 < 0)) { sect1 = voxelChunks[iSect1, jSect, kSect]; if (sect1 != null) validSects = true; } if (!(iSect2 < 0 || iSect2 >= xLength)) { sect2 = voxelChunks[iSect2, jSect, kSect]; if (sect2 != null && sect2 != sect1) validSects |= true; } if (!(iSect3 >= xLength)) { sect3 = voxelChunks[iSect3, jSect, kSect]; if (sect3 != null && (sect3 != sect2 && sect3 != sect1)) validSects |= true; } if (!validSects) continue; for (int j = jOverall; j < jOverall + 8; j++) { double tmp = plane.y * j + plane.w; for (int k = kOverall; k < kOverall + 8; k++) { int i = (int)(-(plane.z * k + tmp) + 0.5); if (i < iMin || i > iMax) continue; int index = i + 8 * j + 64 * k; VoxelChunk.PartSizePair pair; int iSect = i >> 3; if (iSect == iSect1 && sect1 != null) pair = sect1.GetVoxelPartSizePairGlobalIndex(index); else if (iSect == iSect2 && sect2 != null) pair = sect2.GetVoxelPartSizePairGlobalIndex(index); else if (iSect == iSect3 && sect3 != null) pair = sect3.GetVoxelPartSizePairGlobalIndex(index); else continue; if ((object)pair.part != null) { DetermineIfPartGetsForcesAndAreas(partSideAreas, pair, i, j, k); double size = pair.GetSize(); if (size > 1.0) size = 1.0; areaCount += size; centx += i * size; centy += j * size; centz += k * size; Vector3 location = vesselToSectionNormal.MultiplyVector(new Vector3(i, j, k)); i_xx += location.x * location.x * size; i_xy += location.x * location.y * size; i_yy += location.y * location.y * size; } } } } } Vector3d centroid = new Vector3d(centx, centy, centz); //Vector3d unshadowedCentroid = new Vector3d(unshadowedCentx, unshadowedCenty, unshadowedCentz); if (areaCount > 0) { if (frontIndexFound) backIndex = m; else { frontIndexFound = true; frontIndex = m; } centroid /= areaCount; /*if (unshadowedAreaCount != 0) unshadowedCentroid /= unshadowedAreaCount; else unshadowedCentroid = centroid;*/ } Vector3 localCentroid = vesselToSectionNormal.MultiplyVector(centroid); i_xx -= areaCount * localCentroid.x * localCentroid.x; i_xy -= areaCount * localCentroid.x * localCentroid.y; i_yy -= areaCount * localCentroid.y * localCentroid.y; double tanPrinAngle = TanPrincipalAxisAngle(i_xx, i_yy, i_xy); Vector3 axis1 = new Vector3(1, 0, 0), axis2 = new Vector3(0, 0, 1); double flatnessRatio = 1; if (tanPrinAngle != 0) { axis1 = new Vector3(1, 0, (float)tanPrinAngle); axis1.Normalize(); axis2 = new Vector3(axis1.z, 0, -axis1.x); flatnessRatio = i_xy * axis2.z / axis2.x + i_xx; flatnessRatio = (i_xy * tanPrinAngle + i_xx) / flatnessRatio; flatnessRatio = Math.Sqrt(Math.Sqrt(flatnessRatio)); } if (double.IsNaN(flatnessRatio)) flatnessRatio = 1; Vector3 principalAxis; if (flatnessRatio > 1) principalAxis = axis1; else { flatnessRatio = 1 / flatnessRatio; principalAxis = axis2; } if (flatnessRatio > 10) flatnessRatio = 10; principalAxis = sectionNormalToVesselCoords.MultiplyVector(principalAxis); crossSections[m].centroid = centroid * elementSize + lowerRightCorner; //crossSections[m].additonalUnshadowedCentroid = unshadowedCentroid * elementSize + lowerRightCorner; crossSections[m].area = areaCount * elementArea; crossSections[m].flatnessRatio = flatnessRatio; crossSections[m].flatNormalVector = principalAxis; //crossSections[m].additionalUnshadowedArea = unshadowedAreaCount * elementArea; plane.w += wInc; } } else { int sectionCount = zCellLength + (int)(xCellLength * x / z + 1) + (int)(yCellLength * y / z + 1); sectionCount = Math.Min(sectionCount, crossSections.Length); double angleSizeIncreaseFactor = Math.Sqrt((x + y + z) / z); //account for different angles effects on voxel cube's projected area elementArea *= angleSizeIncreaseFactor; //account for different angles effects on voxel cube's projected area double invMag = 1 / Math.Sqrt(x * x + y * y + z * z); sectionThickness *= z * invMag; double xNorm, yNorm, zNorm; xNorm = plane.x * invMag; yNorm = plane.y * invMag; zNorm = plane.z * invMag; double zAbsNorm = Math.Abs(zNorm); double i_xx = 0, i_xy = 0, i_yy = 0; int xSectArrayLength = (int)(xCellLength * zAbsNorm + zCellLength * Math.Abs(xNorm) + 1); //Stack allocation of array allows removal of garbage collection issues //bool* sectionArray = stackalloc bool[xSectArrayLength * (int)(yCellLength * zAbsNorm + zCellLength * Math.Abs(yNorm) + 1)]; //bool[,] sectionRepresentation = new bool[(int)Math.Ceiling(xCellLength * Math.Abs(zNorm) + zCellLength * Math.Abs(xNorm)), (int)Math.Ceiling(yCellLength * Math.Abs(zNorm) + zCellLength * Math.Abs(yNorm))]; double invZPlane = 1 / plane.z; plane.y *= invZPlane; plane.x *= invZPlane; plane.w *= invZPlane; wInc *= invZPlane; for (int m = 0; m < sectionCount; m++) { double areaCount = 0; //Vector3d centroid = Vector3d.zero; double centx, centy, centz; centx = centy = centz = 0; //int unshadowedAreaCount = 0; //Vector3d unshadowedCentroid = Vector3d.zero; //int unshadowedCentx, unshadowedCenty, unshadowedCentz; //unshadowedCentx = unshadowedCenty = unshadowedCentz = 0; Dictionary<Part, VoxelCrossSection.SideAreaValues> partSideAreas = crossSections[m].partSideAreaValues; partSideAreas.Clear(); for (int iOverall = 0; iOverall < xCellLength; iOverall += 8) //Overall ones iterate over the actual voxel indices (to make use of the equation of the plane) but are used to get chunk indices for (int jOverall = 0; jOverall < yCellLength; jOverall += 8) { int kSect1, kSect2, kSect3; if (plane.x * plane.y > 0) //Determine high and low points on this quad of the plane { kSect1 = (int)(-(plane.x * iOverall + plane.y * jOverall + plane.w) + 0.5); kSect3 = (int)(-(plane.x * (iOverall + 7) + plane.y * (jOverall + 7) + plane.w) + 0.5); } else { kSect1 = (int)(-(plane.x * (iOverall + 7) + plane.y * jOverall + plane.w) + 0.5); kSect3 = (int)(-(plane.x * iOverall + plane.y * (jOverall + 7) + plane.w) + 0.5); } kSect2 = (int)((kSect1 + kSect3) * 0.5); int kMin = Math.Min(kSect1, kSect3); int kMax = Math.Max(kSect1, kSect3) + 1; kSect1 = kMin >> 3; kSect2 = kSect2 >> 3; kSect3 = (kMax - 1) >> 3; if (kSect1 >= zLength) //if the smallest sect is above the limit, they all are continue; if (kSect3 < 0) //if the largest sect is below the limit, they all are continue; if (kSect1 == kSect3) //If chunk indices are identical, only get that one and it's very simple { if (kSect1 < 0) continue; VoxelChunk sect = voxelChunks[iOverall >> 3, jOverall >> 3, kSect1]; if (sect == null) continue; for (int i = iOverall; i < iOverall + 8; i++) //Finally, iterate over the chunk { double tmp = plane.x * i + plane.w; for (int j = jOverall; j < jOverall + 8; j++) { int k = (int)(-(plane.y * j + tmp) + 0.5); if (k < kMin || k > kMax) continue; int index = i + 8 * j + 64 * k; VoxelChunk.PartSizePair pair = sect.GetVoxelPartSizePairGlobalIndex(index); if ((object)pair.part != null) { DetermineIfPartGetsForcesAndAreas(partSideAreas, pair, i, j, k); double size = pair.GetSize(); if (size > 1.0) size = 1.0; areaCount += size; centx += i * size; centy += j * size; centz += k * size; Vector3 location = vesselToSectionNormal.MultiplyVector(new Vector3(i, j, k)); i_xx += location.x * location.x * size; i_xy += location.x * location.y * size; i_yy += location.y * location.y * size; } } } } else { VoxelChunk sect1 = null, sect2 = null, sect3 = null; int iSect = iOverall >> 3; int jSect = jOverall >> 3; bool validSects = false; if (!(kSect1 < 0)) //If indices are different, this section of the plane crosses two chunks { sect1 = voxelChunks[iSect, jSect, kSect1]; if (sect1 != null) validSects = true; } if (!(kSect2 < 0 || kSect2 >= zLength)) { sect2 = voxelChunks[iSect, jSect, kSect2]; if (sect2 != null && sect2 != sect1) validSects |= true; } if (!(kSect3 >= zLength)) { sect3 = voxelChunks[iSect, jSect, kSect3]; if (sect3 != null && (sect3 != sect2 && sect3 != sect1)) validSects |= true; } if (!validSects) continue; for (int i = iOverall; i < iOverall + 8; i++) { double tmp = plane.x * i + plane.w; for (int j = jOverall; j < jOverall + 8; j++) { int k = (int)(-(plane.y * j + tmp) + 0.5); if (k < kMin || k > kMax) continue; int index = i + 8 * j + 64 * k; VoxelChunk.PartSizePair pair; int kSect = k >> 3; if (kSect == kSect1 && sect1 != null) pair = sect1.GetVoxelPartSizePairGlobalIndex(index); else if (kSect == kSect2 && sect2 != null) pair = sect2.GetVoxelPartSizePairGlobalIndex(index); else if (kSect == kSect3 && sect3 != null) pair = sect3.GetVoxelPartSizePairGlobalIndex(index); else continue; if ((object)pair.part != null) { DetermineIfPartGetsForcesAndAreas(partSideAreas, pair, i, j, k); double size = pair.GetSize(); if (size > 1.0) size = 1.0; areaCount += size; centx += i * size; centy += j * size; centz += k * size; Vector3 location = vesselToSectionNormal.MultiplyVector(new Vector3(i, j, k)); i_xx += location.x * location.x * size; i_xy += location.x * location.y * size; i_yy += location.y * location.y * size; } } } } } Vector3d centroid = new Vector3d(centx, centy, centz); //Vector3d unshadowedCentroid = new Vector3d(unshadowedCentx, unshadowedCenty, unshadowedCentz); if (areaCount > 0) { if (frontIndexFound) backIndex = m; else { frontIndexFound = true; frontIndex = m; } centroid /= areaCount; /*if (unshadowedAreaCount != 0) unshadowedCentroid /= unshadowedAreaCount; else unshadowedCentroid = centroid;*/ } Vector3 localCentroid = vesselToSectionNormal.MultiplyVector(centroid); i_xx -= areaCount * localCentroid.x * localCentroid.x; i_xy -= areaCount * localCentroid.x * localCentroid.y; i_yy -= areaCount * localCentroid.y * localCentroid.y; double tanPrinAngle = TanPrincipalAxisAngle(i_xx, i_yy, i_xy); Vector3 axis1 = new Vector3(1, 0, 0), axis2 = new Vector3(0, 0, 1); double flatnessRatio = 1; if (tanPrinAngle != 0) { axis1 = new Vector3(1, 0, (float)tanPrinAngle); axis1.Normalize(); axis2 = new Vector3(axis1.z, 0, -axis1.x); flatnessRatio = i_xy * axis2.z / axis2.x + i_xx; flatnessRatio = (i_xy * tanPrinAngle + i_xx) / flatnessRatio; flatnessRatio = Math.Sqrt(Math.Sqrt(flatnessRatio)); } if (double.IsNaN(flatnessRatio)) flatnessRatio = 1; Vector3 principalAxis; if (flatnessRatio > 1) principalAxis = axis1; else { flatnessRatio = 1 / flatnessRatio; principalAxis = axis2; } if (flatnessRatio > 10) flatnessRatio = 10; principalAxis = sectionNormalToVesselCoords.MultiplyVector(principalAxis); crossSections[m].centroid = centroid * elementSize + lowerRightCorner; //crossSections[m].additonalUnshadowedCentroid = unshadowedCentroid * elementSize + lowerRightCorner; crossSections[m].area = areaCount * elementArea; crossSections[m].flatnessRatio = flatnessRatio; crossSections[m].flatNormalVector = principalAxis; //crossSections[m].additionalUnshadowedArea = unshadowedAreaCount * elementArea; plane.w += wInc; } } double denom = 1 / (sectionThickness * sectionThickness); maxCrossSectionArea = 0; for (int i = frontIndex; i <= backIndex; i++) //calculate 2nd derivs, raw { double areaM1, area0, areaP1; if(i == frontIndex) //forward difference for frontIndex { areaM1 = crossSections[i].area; area0 = crossSections[i + 1].area; areaP1 = crossSections[i + 2].area; } else if (i == backIndex) //backward difference for backIndex { areaM1 = crossSections[i - 2].area; area0 = crossSections[i - 1].area; areaP1 = crossSections[i].area; } else //central difference for all others { areaM1 = crossSections[i - 1].area; area0 = crossSections[i].area; areaP1 = crossSections[i + 1].area; } double areaSecondDeriv = (areaM1 + areaP1) - 2 * area0; areaSecondDeriv *= denom; crossSections[i].secondAreaDeriv = areaSecondDeriv; if (crossSections[i].area > maxCrossSectionArea) maxCrossSectionArea = crossSections[i].area; } }