585 lines
20 KiB
C#
585 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using CaeGlobals;
|
|
|
|
namespace CaeMesh
|
|
{
|
|
public class BoundingBoxVolumeComparer : IComparer<BoundingBox>
|
|
{
|
|
public int Compare(BoundingBox bb1, BoundingBox bb2)
|
|
{
|
|
// Use rounding to eliminate numerical error - different exploded view
|
|
double v1 = Tools.RoundToSignificantDigits(bb1.GetVolume(), 6);
|
|
double v2 = Tools.RoundToSignificantDigits(bb2.GetVolume(), 6);
|
|
//
|
|
if (v1 < v2) return 1;
|
|
else if (v1 > v2) return -1;
|
|
else return 0;
|
|
}
|
|
}
|
|
public class BoundingBoxDistanceVolumeComparer : IComparer<BoundingBox>
|
|
{
|
|
public static double[] Center;
|
|
public int Compare(BoundingBox bb1, BoundingBox bb2)
|
|
{
|
|
if (Center == null || Center.Length != 3)
|
|
throw new NotSupportedException();
|
|
double[] c1 = bb1.GetCenter();
|
|
double[] c2 = bb2.GetCenter();
|
|
double dist1 = Math.Pow(c1[0] - Center[0], 2) + Math.Pow(c1[1] - Center[1], 2) + Math.Pow(c1[2] - Center[2], 2);
|
|
double dist2 = Math.Pow(c2[0] - Center[0], 2) + Math.Pow(c2[1] - Center[1], 2) + Math.Pow(c2[2] - Center[2], 2);
|
|
// Use rounding to eliminate numerical error - different exploded view
|
|
dist1 = Tools.RoundToSignificantDigits(dist1, 6);
|
|
dist2 = Tools.RoundToSignificantDigits(dist2, 6);
|
|
//
|
|
if (dist1 > dist2) return 1;
|
|
else if (dist1 < dist2) return -1;
|
|
else
|
|
{
|
|
BoundingBoxVolumeComparer boundingBoxVolumeComparer = new BoundingBoxVolumeComparer();
|
|
return boundingBoxVolumeComparer.Compare(bb1, bb2);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public class BoundingBox
|
|
{
|
|
// Variables
|
|
public double MinX;
|
|
public double MinY;
|
|
public double MinZ;
|
|
//
|
|
public double MaxX;
|
|
public double MaxY;
|
|
public double MaxZ;
|
|
//
|
|
public object Tag;
|
|
|
|
|
|
// Constructors
|
|
public BoundingBox()
|
|
{
|
|
Reset();
|
|
}
|
|
public BoundingBox(BoundingBox box)
|
|
{
|
|
MinX = box.MinX;
|
|
MinY = box.MinY;
|
|
MinZ = box.MinZ;
|
|
//
|
|
MaxX = box.MaxX;
|
|
MaxY = box.MaxY;
|
|
MaxZ = box.MaxZ;
|
|
//
|
|
if (box.Tag != null) Tag = box.Tag.DeepClone();
|
|
}
|
|
|
|
|
|
// Static methods
|
|
public static double[] GetCenter(IEnumerable<BoundingBox> boxes)
|
|
{
|
|
BoundingBox bb = new BoundingBox();
|
|
foreach (var box in boxes) bb.IncludeBox(box);
|
|
return bb.GetCenter();
|
|
}
|
|
public static double[][] GetExplodedBBOffsets(int explodedDirection, double scaleFactor, BoundingBox[] boxes,
|
|
BoundingBox[] fixedBoxes = null)
|
|
{
|
|
//
|
|
// https://stackoverflow.com/questions/3265986/an-algorithm-to-space-out-overlapping-rectangles
|
|
//
|
|
// Renumber and add scale
|
|
for (int i = 0; i < boxes.Length; i++)
|
|
{
|
|
boxes[i].Tag = i;
|
|
boxes[i].Scale(1.2);
|
|
}
|
|
// Sort by size
|
|
Array.Sort(boxes, new BoundingBoxVolumeComparer());
|
|
//
|
|
int firstBoxId = 0;
|
|
BoundingBox globalBox = new BoundingBox();
|
|
double[][] offsets = new double[boxes.Length][];
|
|
List<BoundingBox> nonIntersectingBBs = new List<BoundingBox>();
|
|
// Add fixed boxes
|
|
if (fixedBoxes != null && fixedBoxes.Length > 0)
|
|
{
|
|
nonIntersectingBBs.AddRange(fixedBoxes);
|
|
foreach (var fixedBox in fixedBoxes) globalBox.IncludeBox(fixedBox);
|
|
}
|
|
else
|
|
{
|
|
// Add the largest box
|
|
firstBoxId = 1;
|
|
globalBox.IncludeBox(boxes[0]);
|
|
nonIntersectingBBs.Add(boxes[0]);
|
|
offsets[(int)boxes[0].Tag] = new double[] { 0, 0, 0 };
|
|
}
|
|
int count;
|
|
Vec3D center;
|
|
Vec3D offset;
|
|
Vec3D direction;
|
|
BoundingBox box;
|
|
for (int i = firstBoxId; i < boxes.Length; i++)
|
|
{
|
|
box = boxes[i];
|
|
center = new Vec3D(globalBox.GetCenter());
|
|
offset = new Vec3D();
|
|
direction = new Vec3D(box.GetCenter()) - center;
|
|
// Set the offset direction
|
|
if (explodedDirection == 1) direction.Y = direction.Z = 0;
|
|
else if (explodedDirection == 2) direction.X = direction.Z = 0;
|
|
else if (explodedDirection == 3) direction.X = direction.Y = 0;
|
|
else if (explodedDirection == 4) direction.Z = 0;
|
|
else if (explodedDirection == 5) direction.Y = 0;
|
|
else if (explodedDirection == 6) direction.X = 0;
|
|
// Fix the 0 length direction
|
|
if (direction.Len2 < 1E-6 * globalBox.GetDiagonal()) direction.Coor = new double[] { 1, 1, 1 };
|
|
// Set the offset direction
|
|
if (explodedDirection == 1) direction.Y = direction.Z = 0;
|
|
else if (explodedDirection == 2) direction.X = direction.Z = 0;
|
|
else if (explodedDirection == 3) direction.X = direction.Y = 0;
|
|
else if (explodedDirection == 4) direction.Z = 0;
|
|
else if (explodedDirection == 5) direction.Y = 0;
|
|
else if (explodedDirection == 6) direction.X = 0;
|
|
//
|
|
direction.Normalize();
|
|
direction *= (0.01 * globalBox.GetDiagonal());
|
|
//
|
|
count = 0;
|
|
while (box.Intersects(nonIntersectingBBs) && count++ < 10000)
|
|
{
|
|
box.AddOffset(direction.Coor);
|
|
offset += direction;
|
|
}
|
|
nonIntersectingBBs.Add(box);
|
|
globalBox.IncludeBox(box);
|
|
//
|
|
offsets[(int)box.Tag]= (offset * scaleFactor).Coor;
|
|
}
|
|
//
|
|
return offsets;
|
|
}
|
|
public static double[][] GetExplodedBBbyCPOffsets(double[] centerPoint, int explodedDirection, double scaleFactor,
|
|
BoundingBox[] boxes)
|
|
{
|
|
BoundingBox assemblyBB = new BoundingBox();
|
|
// Renumber and add scale
|
|
for (int i = 0; i < boxes.Length; i++)
|
|
{
|
|
assemblyBB.IncludeBox(boxes[i]);
|
|
//
|
|
boxes[i].Tag = i;
|
|
boxes[i].Scale(1.2);
|
|
}
|
|
// Sort by distance and size
|
|
BoundingBoxDistanceVolumeComparer.Center = centerPoint.ToArray();
|
|
Array.Sort(boxes, new BoundingBoxDistanceVolumeComparer());
|
|
//
|
|
double[][] offsets = new double[boxes.Length][];
|
|
//
|
|
Vec3D center;
|
|
Vec3D offset;
|
|
Vec3D direction;
|
|
double distance;
|
|
BoundingBox box;
|
|
for (int i = 0; i < boxes.Length; i++)
|
|
{
|
|
box = boxes[i];
|
|
center = new Vec3D(centerPoint);
|
|
direction = new Vec3D(box.GetCenter()) - center;
|
|
distance = direction.Len;
|
|
// Set the offset type
|
|
if (explodedDirection == 1) direction.Y = direction.Z = 0;
|
|
else if (explodedDirection == 2) direction.X = direction.Z = 0;
|
|
else if (explodedDirection == 3) direction.X = direction.Y = 0;
|
|
else if (explodedDirection == 4) direction.Z = 0;
|
|
else if (explodedDirection == 5) direction.Y = 0;
|
|
else if (explodedDirection == 6) direction.X = 0;
|
|
// Normalize after truncation
|
|
direction.Normalize();
|
|
// Compute offset
|
|
offset = distance * direction * 2;
|
|
offsets[(int)box.Tag] = (offset * scaleFactor).Coor;
|
|
}
|
|
//
|
|
return offsets;
|
|
}
|
|
|
|
|
|
// Methods
|
|
public void Reset()
|
|
{
|
|
MinX = double.MaxValue;
|
|
MinY = double.MaxValue;
|
|
MinZ = double.MaxValue;
|
|
MaxX = -double.MaxValue;
|
|
MaxY = -double.MaxValue;
|
|
MaxZ = -double.MaxValue;
|
|
}
|
|
public void AddOffset(double[] offset)
|
|
{
|
|
MinX += offset[0];
|
|
MaxX += offset[0];
|
|
MinY += offset[1];
|
|
MaxY += offset[1];
|
|
MinZ += offset[2];
|
|
MaxZ += offset[2];
|
|
}
|
|
public void RemoveOffset(double[] offset)
|
|
{
|
|
MinX -= offset[0];
|
|
MaxX -= offset[0];
|
|
MinY -= offset[1];
|
|
MaxY -= offset[1];
|
|
MinZ -= offset[2];
|
|
MaxZ -= offset[2];
|
|
}
|
|
public void Inflate(double offset)
|
|
{
|
|
MinX -= offset;
|
|
MaxX += offset;
|
|
MinY -= offset;
|
|
MaxY += offset;
|
|
MinZ -= offset;
|
|
MaxZ += offset;
|
|
}
|
|
public void InflateByDiagonal(double offsetFactor)
|
|
{
|
|
Inflate(GetDiagonal() * offsetFactor);
|
|
}
|
|
public void InflateIfThinn(double offsetFactor)
|
|
{
|
|
if (IsThinnInAnyDirection())
|
|
Inflate(GetDiagonal() * offsetFactor);
|
|
}
|
|
public void Scale(double scaleFactor)
|
|
{
|
|
double delta = 0.5 * (MaxX - MinX) * (scaleFactor - 1);
|
|
MinX -= delta;
|
|
MaxX += delta;
|
|
//
|
|
delta = 0.5 * (MaxY - MinY) * (scaleFactor - 1);
|
|
MinY -= delta;
|
|
MaxY += delta;
|
|
//
|
|
delta = 0.5 * (MaxZ - MinZ) * (scaleFactor - 1);
|
|
MinZ -= delta;
|
|
MaxZ += delta;
|
|
}
|
|
|
|
//
|
|
public void IncludeCoor(double[] coor)
|
|
{
|
|
if (coor[0] > MaxX) MaxX = coor[0];
|
|
if (coor[0] < MinX) MinX = coor[0];
|
|
//
|
|
if (coor[1] > MaxY) MaxY = coor[1];
|
|
if (coor[1] < MinY) MinY = coor[1];
|
|
//
|
|
if (coor[2] > MaxZ) MaxZ = coor[2];
|
|
if (coor[2] < MinZ) MinZ = coor[2];
|
|
}
|
|
public void IncludeFirstCoor(double[] coor)
|
|
{
|
|
MaxX = coor[0];
|
|
MinX = coor[0];
|
|
//
|
|
MaxY = coor[1];
|
|
MinY = coor[1];
|
|
//
|
|
MaxZ = coor[2];
|
|
MinZ = coor[2];
|
|
}
|
|
public void IncludeCoorFast(double[] coor)
|
|
{
|
|
if (coor[0] > MaxX) MaxX = coor[0];
|
|
else if (coor[0] < MinX) MinX = coor[0];
|
|
//
|
|
if (coor[1] > MaxY) MaxY = coor[1];
|
|
else if (coor[1] < MinY) MinY = coor[1];
|
|
//
|
|
if (coor[2] > MaxZ) MaxZ = coor[2];
|
|
else if (coor[2] < MinZ) MinZ = coor[2];
|
|
}
|
|
public void IncludeCoors(double[][] coors)
|
|
{
|
|
if (coors.Length > 0) IncludeFirstCoor(coors[0]);
|
|
for (int i = 0; i < coors.Length; i++) IncludeCoorFast(coors[i]);
|
|
}
|
|
public void IncludeBox(BoundingBox box)
|
|
{
|
|
if (box.MaxX > MaxX) MaxX = box.MaxX;
|
|
if (box.MinX < MinX) MinX = box.MinX;
|
|
//
|
|
if (box.MaxY > MaxY ) MaxY = box.MaxY;
|
|
if (box.MinY < MinY) MinY = box.MinY;
|
|
//
|
|
if (box.MaxZ > MaxZ) MaxZ = box.MaxZ;
|
|
if (box.MinZ < MinZ) MinZ = box.MinZ;
|
|
}
|
|
public void IncludeNode(FeNode node)
|
|
{
|
|
if (node.X > MaxX) MaxX = node.X;
|
|
if (node.X < MinX) MinX = node.X;
|
|
//
|
|
if (node.Y > MaxY) MaxY = node.Y;
|
|
if (node.Y < MinY) MinY = node.Y;
|
|
//
|
|
if (node.Z > MaxZ) MaxZ = node.Z;
|
|
if (node.Z < MinZ) MinZ = node.Z;
|
|
}
|
|
//
|
|
public bool Intersects(BoundingBox box, double relativeOffset = 0)
|
|
{
|
|
double off1 = 0;
|
|
double off2 = 0;
|
|
if (relativeOffset > 0)
|
|
{
|
|
off1 = GetDiagonal() * relativeOffset;
|
|
off2 = box.GetDiagonal() * relativeOffset;
|
|
}
|
|
//
|
|
if (box.MaxX + off2 < MinX - off1) return false;
|
|
if (box.MinX - off2 > MaxX + off1) return false;
|
|
if (box.MaxY + off2 < MinY - off1) return false;
|
|
if (box.MinY - off2 > MaxY + off1) return false;
|
|
if (box.MaxZ + off2 < MinZ - off1) return false;
|
|
if (box.MinZ - off2 > MaxZ + off1) return false;
|
|
return true;
|
|
}
|
|
public bool Intersects(List<BoundingBox> boxes)
|
|
{
|
|
foreach (var box in boxes)
|
|
{
|
|
if (Intersects(box)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
public BoundingBox GetIntersection(BoundingBox box)
|
|
{
|
|
BoundingBox intersection = new BoundingBox();
|
|
//
|
|
intersection.MinX = Math.Max(MinX, box.MinX);
|
|
intersection.MaxX = Math.Min(MaxX, box.MaxX);
|
|
//
|
|
intersection.MinY = Math.Max(MinY, box.MinY);
|
|
intersection.MaxY = Math.Min(MaxY, box.MaxY);
|
|
//
|
|
intersection.MinZ = Math.Max(MinZ, box.MinZ);
|
|
intersection.MaxZ = Math.Min(MaxZ, box.MaxZ);
|
|
//
|
|
return intersection;
|
|
}
|
|
//
|
|
public double MaxOutsideAxialDistance(double[] coor)
|
|
{
|
|
double distX = 0;
|
|
double distY = 0;
|
|
double distZ = 0;
|
|
if (coor[0] < MinX) distX = MinX - coor[0];
|
|
else if (coor[0] > MaxX) distX = coor[0] - MaxX;
|
|
//
|
|
if (coor[1] < MinY) distY = MinY - coor[1];
|
|
else if (coor[1] > MaxY) distY = coor[1] - MaxY;
|
|
//
|
|
if (coor[2] < MinZ) distZ = MinZ - coor[2];
|
|
else if (coor[2] > MaxZ) distZ = coor[2] - MaxZ;
|
|
//
|
|
return Math.Max(Math.Max(distX, distY), distZ);
|
|
}
|
|
public double MinOutsideDistance2(double[] coor)
|
|
{
|
|
double d;
|
|
double dist = 0;
|
|
//
|
|
if (coor[0] < MinX)
|
|
{
|
|
d = MinX - coor[0];
|
|
dist = d * d;
|
|
}
|
|
else if (coor[0] > MaxX)
|
|
{
|
|
d = coor[0] - MaxX;
|
|
dist = d * d;
|
|
}
|
|
//
|
|
if (coor[1] < MinY)
|
|
{
|
|
d = MinY - coor[1];
|
|
dist += d * d;
|
|
}
|
|
else if (coor[1] > MaxY)
|
|
{
|
|
d = coor[1] - MaxY;
|
|
dist += d * d;
|
|
}
|
|
//
|
|
if (coor[2] < MinZ)
|
|
{
|
|
d = MinZ - coor[2];
|
|
dist += d * d;
|
|
}
|
|
else if (coor[2] > MaxZ)
|
|
{
|
|
d = coor[2] - MaxZ;
|
|
dist += d * d;
|
|
}
|
|
//
|
|
return dist;
|
|
}
|
|
public double MaxOutsideDistance2(double[] coor)
|
|
{
|
|
double d;
|
|
double dist = 0;
|
|
//
|
|
if (coor[0] < MinX)
|
|
{
|
|
d = MaxX - coor[0];
|
|
dist = d * d;
|
|
}
|
|
else if (coor[0] > MaxX)
|
|
{
|
|
d = coor[0] - MinX;
|
|
dist = d * d;
|
|
}
|
|
//
|
|
if (coor[1] < MinY)
|
|
{
|
|
d = MaxY - coor[1];
|
|
dist += d * d;
|
|
}
|
|
else if (coor[1] > MaxY)
|
|
{
|
|
d = coor[1] - MinY;
|
|
dist += d * d;
|
|
}
|
|
//
|
|
if (coor[2] < MinZ)
|
|
{
|
|
d = MaxZ - coor[2];
|
|
dist += d * d;
|
|
}
|
|
else if (coor[2] > MaxZ)
|
|
{
|
|
d = coor[2] - MinZ;
|
|
dist += d * d;
|
|
}
|
|
//
|
|
return dist;
|
|
}
|
|
public bool IsMaxOutsideDistance2SmallerThan(double[] coor, double limit)
|
|
{
|
|
double d;
|
|
double dist = 0;
|
|
//
|
|
if (coor[0] < MinX)
|
|
{
|
|
d = MinX - coor[0];
|
|
dist = d * d;
|
|
if (dist >= limit) return false;
|
|
}
|
|
else if (coor[0] > MaxX)
|
|
{
|
|
d = coor[0] - MaxX;
|
|
dist = d * d;
|
|
if (dist >= limit) return false;
|
|
}
|
|
//
|
|
if (coor[1] < MinY)
|
|
{
|
|
d = MinY - coor[1];
|
|
dist += d * d;
|
|
if (dist >= limit) return false;
|
|
}
|
|
else if (coor[1] > MaxY)
|
|
{
|
|
d = coor[1] - MaxY;
|
|
dist += d * d;
|
|
if (dist >= limit) return false;
|
|
}
|
|
//
|
|
if (coor[2] < MinZ)
|
|
{
|
|
d = MinZ - coor[2];
|
|
dist += d * d;
|
|
if (dist >= limit) return false;
|
|
}
|
|
else if (coor[2] > MaxZ)
|
|
{
|
|
d = coor[2] - MaxZ;
|
|
dist += d * d;
|
|
if (dist >= limit) return false;
|
|
}
|
|
//
|
|
return true;
|
|
}
|
|
//
|
|
public double[] GetCenter()
|
|
{
|
|
return new double[] { (MinX + MaxX) / 2, (MinY + MaxY) / 2, (MinZ + MaxZ) / 2 };
|
|
}
|
|
public double GetXSize()
|
|
{
|
|
return MaxX - MinX;
|
|
}
|
|
public double GetYSize()
|
|
{
|
|
return MaxY - MinY;
|
|
}
|
|
public double GetZSize()
|
|
{
|
|
return MaxZ - MinZ;
|
|
}
|
|
public double GetDiagonal()
|
|
{
|
|
return Math.Sqrt(Math.Pow(MaxX - MinX, 2) + Math.Pow(MaxY - MinY, 2) + Math.Pow(MaxZ - MinZ, 2));
|
|
}
|
|
public double GetVolume()
|
|
{
|
|
return (MaxX - MinX) * (MaxY - MinY) * (MaxZ - MinZ);
|
|
}
|
|
public bool Is2D()
|
|
{
|
|
double epsilon = GetDiagonal() * 1E-6;
|
|
//
|
|
if (Math.Abs(MinZ) < epsilon && Math.Abs(MaxZ) < epsilon) return true;
|
|
else return false;
|
|
}
|
|
public bool IsThinnInAnyDirection()
|
|
{
|
|
double epsilon = GetDiagonal() * 1E-2;
|
|
//
|
|
if (Math.Abs(MaxX - MinX) < epsilon) return true;
|
|
else if (Math.Abs(MaxY - MinY) < epsilon) return true;
|
|
else if (Math.Abs(MaxZ - MinZ) < epsilon) return true;
|
|
else return false;
|
|
}
|
|
//
|
|
public bool IsEqualInSize(BoundingBox boundingBox)
|
|
{
|
|
double size = Math.Max(Math.Max(MaxX - MinX, MaxY - MinY), MaxZ - MinZ) * 1E-6;
|
|
//
|
|
if (Math.Abs(MinX - boundingBox.MinX) > size) return false;
|
|
else if (Math.Abs(MaxX - boundingBox.MaxX) > size) return false;
|
|
//
|
|
else if (Math.Abs(MinY - boundingBox.MinY) > size) return false;
|
|
else if (Math.Abs(MaxY - boundingBox.MaxY) > size) return false;
|
|
//
|
|
else if (Math.Abs(MinZ - boundingBox.MinZ) > size) return false;
|
|
else if (Math.Abs(MaxZ - boundingBox.MaxZ) > size) return false;
|
|
//
|
|
else return true;
|
|
}
|
|
//
|
|
public BoundingBox DeepCopy()
|
|
{
|
|
return new BoundingBox(this);
|
|
}
|
|
}
|
|
}
|