Files
2026-03-25 18:20:24 +08:00

432 lines
18 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CaeMesh;
using CaeGlobals;
using DynamicTypeDescriptor;
using System.Collections.Concurrent;
using Priority_Queue;
namespace CaeResults
{
public class CloudInterpolator
{
// Variables
private int _nx;
private int _ny;
private int _nz;
private int _nxy;
private int _numOfValues;
private double _deltaX;
private double _deltaY;
private double _deltaZ;
private CloudPoint[] _cloudPoints;
private BoundingBox _sourceBox;
private BoundingBox[] _regionBoxes; // Tag of each bounding box contains dictionary<triangleId, boundingBox>
// Variables
public int NumOfValues { get { return _numOfValues; } } // number of values/dimensions per point
public CloudPoint[] CloudPoints { get { return _cloudPoints; } }
// Constructor
public CloudInterpolator(CloudPoint[] cloudPoints)
{
_cloudPoints = cloudPoints;
//
_sourceBox = ComputeAllPointsBoundingBox(_cloudPoints);
_sourceBox.InflateIfThinn(1E-6);
//
double l = _sourceBox.GetDiagonal() / 100;
_nx = (int)Math.Ceiling(_sourceBox.GetXSize() / l);
_ny = (int)Math.Ceiling(_sourceBox.GetYSize() / l);
_nz = (int)Math.Ceiling(_sourceBox.GetZSize() / l);
//
int currNumBoxes = _nx * _ny * _nz;
int maxNumBoxes = 10_000_000;
if (currNumBoxes > maxNumBoxes)
{
double factor = Math.Pow((double)maxNumBoxes / currNumBoxes, 0.333333);
l /= factor;
}
//
_nx = (int)Math.Ceiling(_sourceBox.GetXSize() / l);
_ny = (int)Math.Ceiling(_sourceBox.GetYSize() / l);
_nz = (int)Math.Ceiling(_sourceBox.GetZSize() / l);
//
_nxy = _nx * _ny;
_deltaX = _sourceBox.GetXSize() / _nx;
_deltaY = _sourceBox.GetYSize() / _ny;
_deltaZ = _sourceBox.GetZSize() / _nz;
//
_regionBoxes = AssignPointsToRegions(_cloudPoints, _sourceBox, _nx, _ny, _nz);
}
public void InterpolateAt(double[] point, CloudInterpolatorEnum interpolator, double radius,
out double[] distance, out double[] values)
{
if (interpolator == CloudInterpolatorEnum.ClosestPoint)
{
InterpolateByClosestPoint(point, out distance, out values);
}
else if (interpolator == CloudInterpolatorEnum.Gauss)
{
InterpolateByGauss(point, radius, out distance, out values);
}
else if (interpolator == CloudInterpolatorEnum.Shepard)
{
InterpolateByShepard(point, radius, out distance, out values);
}
else throw new NotSupportedException();
//
values = values.ToArray(); // copy
}
public void InterpolateByClosestPoint(double[] point, out double[] distance, out double[] values)
{
Dictionary<int, BoundingBox> regions = GetClosestRegions(point, -1);
//
double absX;
double absY;
double absZ;
double d;
double minD = double.MaxValue;
CloudPoint bestPoint = new CloudPoint();
foreach (var regionEntry in regions)
{
if (regionEntry.Value.IsMaxOutsideDistance2SmallerThan(point, minD * minD))
{
foreach (var cloudPoint in (HashSet<CloudPoint>)regionEntry.Value.Tag)
{
absX = Math.Abs(cloudPoint.Coor[0] - point[0]);
if (absX < minD)
{
absY = Math.Abs(cloudPoint.Coor[1] - point[1]);
if (absY < minD)
{
absZ = Math.Abs(cloudPoint.Coor[2] - point[2]);
if (absZ < minD)
{
d = Math.Sqrt(absX * absX + absY * absY + absZ * absZ);
//
if (d < minD)
{
minD = d;
bestPoint = cloudPoint;
}
}
}
}
}
}
}
//
distance = new double[] { bestPoint.Coor[0] - point[0],
bestPoint.Coor[1] - point[1],
bestPoint.Coor[2] - point[2]};
//
values = bestPoint.Values; // copy values since multiply with magnitude
}
public void InterpolateByGauss(double[] point, double radius, out double[] distance, out double[] values)
{
Dictionary<int, BoundingBox> regions = GetClosestRegions(point, radius);
//
double absX;
double absY;
double absZ;
double d;
double s = 1;
double w;
double radius2 = radius * radius;
Dictionary<CloudPoint, double> cloudPointWeight = new Dictionary<CloudPoint, double>();
foreach (var regionEntry in regions)
{
if (regionEntry.Value.IsMaxOutsideDistance2SmallerThan(point, radius2))
{
foreach (var cloudPoint in (HashSet<CloudPoint>)regionEntry.Value.Tag)
{
absX = Math.Abs(cloudPoint.Coor[0] - point[0]);
if (absX < radius)
{
absY = Math.Abs(cloudPoint.Coor[1] - point[1]);
if (absY < radius)
{
absZ = Math.Abs(cloudPoint.Coor[2] - point[2]);
if (absZ < radius)
{
d = Math.Sqrt(absX * absX + absY * absY + absZ * absZ);
//
w = Math.Exp(Math.Pow(-s * d / radius, 2));
cloudPointWeight.Add(cloudPoint, w);
}
}
}
}
}
}
//
if (cloudPointWeight.Count > 0)
{
double wSum = 0;
foreach (var entry in cloudPointWeight) wSum += entry.Value;
//
values = new double[_numOfValues];
//
CloudPoint cp;
foreach (var entry in cloudPointWeight)
{
cp = entry.Key;
for (int i = 0; i < _numOfValues; i++)
{
values[i] += (entry.Value / wSum) * cp.Values[i];
}
}
//
distance = new double[] { 0, 0, 0 };
}
else
{
values = new double[_numOfValues];
distance = new double[] { radius, radius, radius };
}
//
}
public void InterpolateByShepard(double[] point, double radius, out double[] distance, out double[] values)
{
Dictionary<int, BoundingBox> regions = GetClosestRegions(point, radius);
//
bool foundExactPosition = false;
double absX;
double absY;
double absZ;
double d;
double w;
double radius2 = radius * radius;
Dictionary<CloudPoint, double> cloudPointWeight = new Dictionary<CloudPoint, double>();
foreach (var regionEntry in regions)
{
if (regionEntry.Value.IsMaxOutsideDistance2SmallerThan(point, radius2))
{
foreach (var cloudPoint in (HashSet<CloudPoint>)regionEntry.Value.Tag)
{
absX = Math.Abs(cloudPoint.Coor[0] - point[0]);
if (absX < radius)
{
absY = Math.Abs(cloudPoint.Coor[1] - point[1]);
if (absY < radius)
{
absZ = Math.Abs(cloudPoint.Coor[2] - point[2]);
if (absZ < radius)
{
d = Math.Sqrt(absX * absX + absY * absY + absZ * absZ);
//
if (d == 0)
{
cloudPointWeight.Clear();
cloudPointWeight.Add(cloudPoint, 1);
foundExactPosition = true;
break;
}
else
{
w = 1 / Math.Pow(d, 2);
cloudPointWeight.Add(cloudPoint, w);
}
}
}
}
}
}
if (foundExactPosition) break;
}
//
if (cloudPointWeight.Count > 0)
{
double wSum = 0;
foreach (var entry in cloudPointWeight) wSum += entry.Value;
//
values = new double[_numOfValues];
//
CloudPoint cp;
foreach (var entry in cloudPointWeight)
{
cp = entry.Key;
for (int i = 0; i < _numOfValues; i++)
{
values[i] += (entry.Value / wSum) * cp.Values[i];
}
}
//
distance = new double[] { 0, 0, 0 };
}
else
{
values = new double[_numOfValues];
distance = new double[] { radius, radius, radius };
}
//
}
private Dictionary<int, BoundingBox> GetClosestRegions(double[] point, double radius)
{
int i;
int j;
int k;
int mini;
int maxi;
int minj;
int maxj;
int mink;
int maxk;
int index;
BoundingBox bb;
Dictionary<int, BoundingBox> regions = new Dictionary<int, BoundingBox>();
int num;
int layer;
//
i = (int)Math.Floor((point[0] - _sourceBox.MinX) / _deltaX);
j = (int)Math.Floor((point[1] - _sourceBox.MinY) / _deltaY);
k = (int)Math.Floor((point[2] - _sourceBox.MinZ) / _deltaZ);
if (i < 0) i = 0;
else if (i >= _nx) i = _nx - 1;
if (j < 0) j = 0;
else if (j >= _ny) j = _ny - 1;
if (k < 0) k = 0;
else if (k >= _nz) k = _nz - 1;
index = k * _nxy + j * _nx + i;
bb = _regionBoxes[index];
if (bb != null) regions.Add(index, bb);
//
layer = 0;
num = bb == null ? 0 : ((HashSet<CloudPoint>)bb.Tag).Count;
// Add next layer of regions - at least one
while (num == 0 || layer < 1)
{
layer++;
if (radius <= 0)
{
mini = i - layer;
maxi = i + layer;
minj = j - layer;
maxj = j + layer;
mink = k - layer;
maxk = k + layer;
}
else
{
mini = (int)Math.Floor((point[0] - radius - _sourceBox.MinX) / _deltaX);
maxi = (int)Math.Ceiling((point[0] + radius - _sourceBox.MinX) / _deltaX);
minj = (int)Math.Floor((point[1] - radius - _sourceBox.MinY) / _deltaY);
maxj = (int)Math.Ceiling((point[1] + radius - _sourceBox.MinY) / _deltaY);
mink = (int)Math.Floor((point[2] - radius - _sourceBox.MinZ) / _deltaZ);
maxk = (int)Math.Ceiling((point[2] + radius - _sourceBox.MinZ) / _deltaZ);
num = 1;
}
//
if (mini < 0) mini = 0;
if (mini >= _nx) mini = _nx - 1;
if (maxi < 0) maxi = 0;
if (maxi >= _nx) maxi = _nx - 1;
//
if (minj < 0) minj = 0;
if (minj >= _ny) minj = _ny - 1;
if (maxj < 0) maxj = 0;
if (maxj >= _ny) maxj = _ny - 1;
//
if (mink < 0) mink = 0;
if (mink >= _nz) mink = _nz - 1;
if (maxk < 0) maxk = 0;
if (maxk >= _nz) maxk = _nz - 1;
//
for (int kk = mink; kk <= maxk; kk++)
{
for (int jj = minj; jj <= maxj; jj++)
{
for (int ii = mini; ii <= maxi; ii++)
{
index = kk * _nxy + jj * _nx + ii;
if (!regions.ContainsKey(index))
{
bb = _regionBoxes[index];
//
if (bb != null && ((HashSet<CloudPoint>)bb.Tag).Count > 0)
{
regions.Add(index, bb);
num += ((HashSet<CloudPoint>)bb.Tag).Count;
}
}
}
}
}
}
//
return regions;
}
//
private BoundingBox ComputeAllPointsBoundingBox(CloudPoint[] cloudPoints)
{
_numOfValues = -1;
BoundingBox bb = new BoundingBox();
bb.IncludeFirstCoor(cloudPoints[0].Coor);
for (int i = 1; i < cloudPoints.Length; i++)
{
bb.IncludeCoorFast(cloudPoints[i].Coor);
if (_numOfValues == -1) _numOfValues = cloudPoints[i].Values.Length;
if (_numOfValues != cloudPoints[i].Values.Length)
throw new CaeException("The interpolator does not contain the same number of values at each cloud point.");
}
return bb;
}
private BoundingBox[] AssignPointsToRegions(CloudPoint[] cloudPoints, BoundingBox sourceBox, int nx, int ny, int nz)
{
int nxy = nx * ny;
double deltaX = sourceBox.GetXSize() / nx;
double deltaY = sourceBox.GetYSize() / ny;
double deltaZ = sourceBox.GetZSize() / nz;
//
BoundingBox bb;
BoundingBox[] regions = new BoundingBox[nxy * nz];
//
int pointI;
int pointJ;
int pointK;
int regionIndex;
// If cell box max value is on the border of the region division, the cell will be a member of both space regions
//foreach (var cloudPoint in cloudPoints)
for (int i = 0; i < cloudPoints.Length; i++)
{
pointI = (int)Math.Floor((cloudPoints[i].Coor[0] - sourceBox.MinX) / deltaX);
pointJ = (int)Math.Floor((cloudPoints[i].Coor[1] - sourceBox.MinY) / deltaY);
pointK = (int)Math.Floor((cloudPoints[i].Coor[2] - sourceBox.MinZ) / deltaZ);
//
if (pointI < 0) pointI = 0;
else if (pointI >= _nx) pointI = _nx - 1;
if (pointJ < 0) pointJ = 0;
else if (pointJ >= _ny) pointJ = _ny - 1;
if (pointK < 0) pointK = 0;
else if (pointK >= _nz) pointK = _nz - 1;
//
regionIndex = pointK * nxy + pointJ * nx + pointI;
bb = regions[regionIndex];
if (bb == null)
{
bb = new BoundingBox();
bb.MinX = sourceBox.MinX + pointI * deltaX;
bb.MaxX = bb.MinX + deltaX;
bb.MinY = sourceBox.MinY + pointJ * deltaY;
bb.MaxY = bb.MinY + deltaY;
bb.MinZ = sourceBox.MinZ + pointK * deltaZ;
bb.MaxZ = bb.MinZ + deltaZ;
bb.Tag = new HashSet<CloudPoint>();
regions[regionIndex] = bb;
}
((HashSet<CloudPoint>)bb.Tag).Add(cloudPoints[i]);
}
//
return regions;
}
}
}