465 lines
10 KiB
C++
465 lines
10 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
#pragma once
|
|
|
|
#include "Core/Types.h"
|
|
#include "Geo/GeoEnum.h"
|
|
#include "Math/MathConst.h"
|
|
#include "Math/Point.h"
|
|
|
|
namespace UE::CADKernel
|
|
{
|
|
/**
|
|
* MINIMAL_UNIT_LINEAR_TOLERANCE allows to define the minimal tolerance value of a parametric space
|
|
* @see FLinearBoundary::ComputeMinimalTolerance
|
|
*/
|
|
#define MINIMAL_UNIT_LINEAR_TOLERANCE 1e-5
|
|
|
|
struct CADKERNEL_API FLinearBoundary
|
|
{
|
|
|
|
/** A default boundary (0., 1.)*/
|
|
static const FLinearBoundary DefaultBoundary;
|
|
|
|
double Min;
|
|
double Max;
|
|
|
|
FLinearBoundary()
|
|
{
|
|
Min = 0.;
|
|
Max = 1.;
|
|
}
|
|
|
|
FLinearBoundary(const FLinearBoundary& Boundary)
|
|
: Min(Boundary.Min)
|
|
, Max(Boundary.Max)
|
|
{
|
|
}
|
|
|
|
FLinearBoundary(const double UMin, const double UMax)
|
|
{
|
|
Set(UMin, UMax);
|
|
}
|
|
|
|
FLinearBoundary(const FLinearBoundary& Boundary, const double OffsetTolerance)
|
|
: Min(Boundary.Min - OffsetTolerance)
|
|
, Max(Boundary.Max + OffsetTolerance)
|
|
{
|
|
}
|
|
|
|
FLinearBoundary(const double UMin, const double UMax, const double OffsetTolerance)
|
|
{
|
|
Set(UMin, UMax);
|
|
Offset(OffsetTolerance);
|
|
}
|
|
|
|
friend FArchive& operator<<(FArchive& Ar, FLinearBoundary& Boundary)
|
|
{
|
|
Ar.Serialize(&Boundary, sizeof(FLinearBoundary));
|
|
return Ar;
|
|
}
|
|
|
|
constexpr double GetMin() const
|
|
{
|
|
return Min;
|
|
}
|
|
|
|
constexpr double GetMax() const
|
|
{
|
|
return Max;
|
|
}
|
|
|
|
constexpr double GetAt(const double Coordinate) const
|
|
{
|
|
return Min + (Max - Min) * Coordinate;
|
|
}
|
|
|
|
constexpr double GetMiddle() const
|
|
{
|
|
return (Min + Max) * 0.5;
|
|
}
|
|
|
|
double Size() const { return Max - Min; }
|
|
|
|
void SetMin(const double Coordinates)
|
|
{
|
|
FMath::GetMinMax(Coordinates, Max, Min, Max);
|
|
}
|
|
|
|
void SetMax(const double Coordinates)
|
|
{
|
|
FMath::GetMinMax(Min, Coordinates, Min, Max);
|
|
}
|
|
|
|
void Set(const double InUMin = 0., const double InUMax = 1.)
|
|
{
|
|
FMath::GetMinMax(InUMin, InUMax, Min, Max);
|
|
}
|
|
|
|
/**
|
|
* Set the boundary with the min and max of the array
|
|
*/
|
|
void Set(const TArray<double>& Coordinates)
|
|
{
|
|
Init();
|
|
for (const double& Coordinate : Coordinates)
|
|
{
|
|
ExtendTo(Coordinate);
|
|
}
|
|
}
|
|
|
|
|
|
bool IsValid() const
|
|
{
|
|
return Min <= Max;
|
|
}
|
|
|
|
bool Contains(const double Coordinate) const
|
|
{
|
|
return RealCompare(Coordinate, Min) >= 0 && RealCompare(Coordinate, Max) <= 0;
|
|
}
|
|
|
|
double Length() const
|
|
{
|
|
return GetMax() - GetMin();
|
|
}
|
|
|
|
/**
|
|
* Return true if the parametric domain is to small
|
|
*/
|
|
bool IsDegenerated() const
|
|
{
|
|
double DeltaU = (Max - Min);
|
|
return (DeltaU < DOUBLE_SMALL_NUMBER);
|
|
}
|
|
|
|
/**
|
|
* Compute the minimal tolerance of the parametric domain i.e.
|
|
* ToleranceMin = Boundary.Length() * MINIMAL_UNIT_LINEAR_TOLERANCE
|
|
* e.g. for a curve of 1m with a parametric space define between [0, 1], the parametric tolerance is 0.01
|
|
* This is a minimal value that has to be replace with a more accurate value when its possible
|
|
*/
|
|
double ComputeMinimalTolerance() const
|
|
{
|
|
return Length() * MINIMAL_UNIT_LINEAR_TOLERANCE;
|
|
}
|
|
|
|
/**
|
|
* If a coordinate is outside the bounds, set the coordinate at the closed limit
|
|
*/
|
|
void MoveInsideIfNot(double& Coordinate, const double Tolerance = DOUBLE_SMALL_NUMBER) const
|
|
{
|
|
if (Coordinate <= Min)
|
|
{
|
|
Coordinate = Min + Tolerance;
|
|
}
|
|
else if (Coordinate >= Max)
|
|
{
|
|
Coordinate = Max - Tolerance;
|
|
}
|
|
}
|
|
|
|
void Offset(const double Tolerance = DOUBLE_SMALL_NUMBER)
|
|
{
|
|
Min -= Tolerance;
|
|
Max += Tolerance;
|
|
}
|
|
|
|
/**
|
|
* Uses to initiate a boundary computation with ExtendTo
|
|
*/
|
|
void Init()
|
|
{
|
|
Min = HUGE_VALUE;
|
|
Max = -HUGE_VALUE;
|
|
}
|
|
|
|
void ExtendTo(double MinCoordinate, double MaxCoordinate)
|
|
{
|
|
FMath::GetMinMax(MinCoordinate, MaxCoordinate);
|
|
Min = FMath::Min(Min, MinCoordinate);
|
|
Max = FMath::Max(Max, MaxCoordinate);
|
|
}
|
|
|
|
void TrimAt(const FLinearBoundary& MaxBound)
|
|
{
|
|
if (Max < MaxBound.Min || Min > MaxBound.Max)
|
|
{
|
|
*this = MaxBound;
|
|
return;
|
|
}
|
|
|
|
Min = FMath::Max(Min, MaxBound.Min);
|
|
Max = FMath::Min(Max, MaxBound.Max);
|
|
}
|
|
|
|
void ExtendTo(const FLinearBoundary& MaxBound)
|
|
{
|
|
Min = FMath::Min(Min, MaxBound.Min);
|
|
Max = FMath::Max(Max, MaxBound.Max);
|
|
}
|
|
|
|
void ExtendTo(const double Coordinate)
|
|
{
|
|
if (Coordinate < Min)
|
|
{
|
|
Min = Coordinate;
|
|
}
|
|
|
|
if (Coordinate > Max)
|
|
{
|
|
Max = Coordinate;
|
|
}
|
|
}
|
|
|
|
void RestrictTo(const FLinearBoundary& MaxBound)
|
|
{
|
|
if (MaxBound.Min > Min)
|
|
{
|
|
Min = MaxBound.Min;
|
|
}
|
|
if (MaxBound.Max < Max)
|
|
{
|
|
Max = MaxBound.Max;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If the boundary width is near or equal to zero, it's widened by +/- DOUBLE_SMALL_NUMBER
|
|
*/
|
|
void WidenIfDegenerated()
|
|
{
|
|
if (FMath::IsNearlyEqual(Min, Max))
|
|
{
|
|
Min -= DOUBLE_SMALL_NUMBER;
|
|
Max += DOUBLE_SMALL_NUMBER;
|
|
}
|
|
}
|
|
|
|
FLinearBoundary& operator=(const FLinearBoundary& InBounds)
|
|
{
|
|
Min = InBounds.Min;
|
|
Max = InBounds.Max;
|
|
return *this;
|
|
}
|
|
|
|
};
|
|
|
|
class CADKERNEL_API FSurfacicBoundary
|
|
{
|
|
private:
|
|
FLinearBoundary UVBoundaries[2];
|
|
|
|
public:
|
|
/** A default boundary (0., 1., 0., 1.)*/
|
|
static const FSurfacicBoundary DefaultBoundary;
|
|
|
|
FSurfacicBoundary() = default;
|
|
|
|
FSurfacicBoundary(const double InUMin, const double InUMax, const double InVMin, const double InVMax)
|
|
{
|
|
UVBoundaries[EIso::IsoU].Set(InUMin, InUMax);
|
|
UVBoundaries[EIso::IsoV].Set(InVMin, InVMax);
|
|
}
|
|
|
|
FSurfacicBoundary(const double InUMin, const double InUMax, const double InVMin, const double InVMax, const double OffsetTolerance)
|
|
{
|
|
UVBoundaries[EIso::IsoU].Set(InUMin, InUMax);
|
|
UVBoundaries[EIso::IsoV].Set(InVMin, InVMax);
|
|
Offset(OffsetTolerance);
|
|
}
|
|
|
|
FSurfacicBoundary(const FVector2d& Point1, const FVector2d& Point2)
|
|
{
|
|
Set(Point1, Point2);
|
|
}
|
|
|
|
FSurfacicBoundary(const FVector2d& Point1, const FVector2d& Point2, const double OffsetTolerance)
|
|
{
|
|
Set(Point1, Point2);
|
|
Offset(OffsetTolerance);
|
|
}
|
|
|
|
void Set(const FVector2d& Point1, const FVector2d& Point2)
|
|
{
|
|
UVBoundaries[EIso::IsoU].Set(Point1.X, Point2.X);
|
|
UVBoundaries[EIso::IsoV].Set(Point1.Y, Point2.Y);
|
|
}
|
|
|
|
friend FArchive& operator<<(FArchive& Ar, FSurfacicBoundary& Boundary)
|
|
{
|
|
Ar << Boundary[EIso::IsoU];
|
|
Ar << Boundary[EIso::IsoV];
|
|
return Ar;
|
|
}
|
|
|
|
void Set(const FLinearBoundary& BoundU, const FLinearBoundary& BoundV)
|
|
{
|
|
UVBoundaries[EIso::IsoU] = BoundU;
|
|
UVBoundaries[EIso::IsoV] = BoundV;
|
|
}
|
|
|
|
void Set(const double InUMin, const double InUMax, const double InVMin, const double InVMax)
|
|
{
|
|
UVBoundaries[EIso::IsoU].Set(InUMin, InUMax);
|
|
UVBoundaries[EIso::IsoV].Set(InVMin, InVMax);
|
|
}
|
|
|
|
void Set()
|
|
{
|
|
UVBoundaries[EIso::IsoU].Set();
|
|
UVBoundaries[EIso::IsoV].Set();
|
|
}
|
|
|
|
/**
|
|
* Set the boundary with the min and max of this array
|
|
*/
|
|
void Set(const TArray<FVector2d>& Points)
|
|
{
|
|
Init();
|
|
for (const FVector2d& Point : Points)
|
|
{
|
|
ExtendTo(Point);
|
|
}
|
|
}
|
|
|
|
const FLinearBoundary& Get(EIso Type) const
|
|
{
|
|
return UVBoundaries[Type];
|
|
}
|
|
|
|
bool IsValid() const
|
|
{
|
|
return UVBoundaries[EIso::IsoU].IsValid() && UVBoundaries[EIso::IsoV].IsValid();
|
|
}
|
|
|
|
/**
|
|
* Return true if the parametric domain is to small
|
|
*/
|
|
bool IsDegenerated() const
|
|
{
|
|
return UVBoundaries[EIso::IsoU].IsDegenerated() || UVBoundaries[EIso::IsoV].IsDegenerated();
|
|
}
|
|
|
|
ESituation IsInside(const FSurfacicBoundary& OtherBoundary, const FSurfacicTolerance& Tolerance2D)
|
|
{
|
|
int32 Inside = 0;
|
|
int32 Outside = 0;
|
|
TFunction<void(double, double, double)> CheckInside = [&](double LeftSide, double RigthSide, double Tolerance)
|
|
{
|
|
if (LeftSide + Tolerance < RigthSide)
|
|
{
|
|
Inside++;
|
|
}
|
|
else if (RigthSide + Tolerance < LeftSide)
|
|
{
|
|
Outside++;
|
|
}
|
|
};
|
|
|
|
CheckInside(OtherBoundary[EIso::IsoU].GetMin(), UVBoundaries[EIso::IsoU].GetMin(), Tolerance2D[EIso::IsoU]);
|
|
CheckInside(OtherBoundary[EIso::IsoV].GetMin(), UVBoundaries[EIso::IsoV].GetMin(), Tolerance2D[EIso::IsoV]);
|
|
CheckInside(UVBoundaries[EIso::IsoU].GetMax(), OtherBoundary[EIso::IsoU].GetMax(), Tolerance2D[EIso::IsoU]);
|
|
CheckInside(UVBoundaries[EIso::IsoV].GetMax(), OtherBoundary[EIso::IsoV].GetMax(), Tolerance2D[EIso::IsoV]);
|
|
|
|
if (Inside > 2)
|
|
{
|
|
return ESituation::Inside;
|
|
}
|
|
if (Outside > 2)
|
|
{
|
|
return ESituation::Outside;
|
|
}
|
|
return ESituation::Undefined;
|
|
}
|
|
|
|
/**
|
|
* Uses to initiate a boundary computation with ExtendTo
|
|
*/
|
|
void Init()
|
|
{
|
|
UVBoundaries[EIso::IsoU].Init();
|
|
UVBoundaries[EIso::IsoV].Init();
|
|
}
|
|
|
|
void TrimAt(const FSurfacicBoundary& MaxLimit)
|
|
{
|
|
UVBoundaries[EIso::IsoU].TrimAt(MaxLimit[EIso::IsoU]);
|
|
UVBoundaries[EIso::IsoV].TrimAt(MaxLimit[EIso::IsoV]);
|
|
}
|
|
|
|
void ExtendTo(const FSurfacicBoundary& MaxLimit)
|
|
{
|
|
UVBoundaries[EIso::IsoU].ExtendTo(MaxLimit[EIso::IsoU]);
|
|
UVBoundaries[EIso::IsoV].ExtendTo(MaxLimit[EIso::IsoV]);
|
|
}
|
|
|
|
void ExtendTo(const FVector2d& Point)
|
|
{
|
|
UVBoundaries[EIso::IsoU].ExtendTo(Point.X);
|
|
UVBoundaries[EIso::IsoV].ExtendTo(Point.Y);
|
|
}
|
|
|
|
void ExtendTo(const FVector& Point)
|
|
{
|
|
UVBoundaries[EIso::IsoU].ExtendTo(Point.X);
|
|
UVBoundaries[EIso::IsoV].ExtendTo(Point.Y);
|
|
}
|
|
|
|
void RestrictTo(const FSurfacicBoundary& MaxBound)
|
|
{
|
|
UVBoundaries[EIso::IsoU].RestrictTo(MaxBound.UVBoundaries[EIso::IsoU]);
|
|
UVBoundaries[EIso::IsoV].RestrictTo(MaxBound.UVBoundaries[EIso::IsoV]);
|
|
}
|
|
|
|
/**
|
|
* If Along each axis, the bound width is near equal to zero, it's widened by +/- DOUBLE_SMALL_NUMBER
|
|
*/
|
|
void WidenIfDegenerated()
|
|
{
|
|
UVBoundaries[EIso::IsoU].WidenIfDegenerated();
|
|
UVBoundaries[EIso::IsoV].WidenIfDegenerated();
|
|
}
|
|
|
|
/**
|
|
* If a point is outside the bounds, set the coordinate to insert the point inside the bounds
|
|
*/
|
|
void MoveInsideIfNot(FVector& Point, const double Tolerance = DOUBLE_SMALL_NUMBER) const
|
|
{
|
|
UVBoundaries[EIso::IsoU].MoveInsideIfNot(Point.X, Tolerance);
|
|
UVBoundaries[EIso::IsoV].MoveInsideIfNot(Point.Y, Tolerance);
|
|
}
|
|
|
|
/**
|
|
* If a point is outside the bounds, set the coordinate to insert the point inside the bounds
|
|
*/
|
|
void MoveInsideIfNot(FVector2d& Point, const double Tolerance = DOUBLE_SMALL_NUMBER) const
|
|
{
|
|
UVBoundaries[EIso::IsoU].MoveInsideIfNot(Point.X, Tolerance);
|
|
UVBoundaries[EIso::IsoV].MoveInsideIfNot(Point.Y, Tolerance);
|
|
}
|
|
|
|
double Length(const EIso& Iso) const
|
|
{
|
|
return UVBoundaries[Iso].Length();
|
|
}
|
|
|
|
constexpr const FLinearBoundary& operator[](const EIso& Iso) const
|
|
{
|
|
return UVBoundaries[Iso];
|
|
}
|
|
|
|
constexpr FLinearBoundary& operator[](const EIso& Iso)
|
|
{
|
|
return UVBoundaries[Iso];
|
|
}
|
|
|
|
void Offset(const double Tolerance = DOUBLE_SMALL_NUMBER)
|
|
{
|
|
UVBoundaries[EIso::IsoU].Offset(Tolerance);
|
|
UVBoundaries[EIso::IsoV].Offset(Tolerance);
|
|
}
|
|
|
|
};
|
|
}
|
|
|