//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;
            }
        }