Add $1 uni-stroke recognizer system
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
#include "UniStrokeDataTable.h"
|
||||
|
||||
FUniStrokeDataTable::FUniStrokeDataTable()
|
||||
{
|
||||
// DO NOTHING
|
||||
}
|
||||
|
||||
FUniStrokeDataTable::~FUniStrokeDataTable()
|
||||
{
|
||||
// DO NOTHING
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright Team Lumi. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataTable.h"
|
||||
#include "UniStrokeDataTable.generated.h"
|
||||
|
||||
USTRUCT()
|
||||
struct WIZARDINGCENTRAL_API FUniStrokeDataTable : public FTableRowBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
FUniStrokeDataTable();
|
||||
virtual ~FUniStrokeDataTable() override;
|
||||
|
||||
/**
|
||||
* The name of the shape
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FString Name;
|
||||
|
||||
/**
|
||||
* The points of the shape
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<FVector2D> Points;
|
||||
};
|
||||
@@ -0,0 +1,404 @@
|
||||
#include "UniStrokePoint.h"
|
||||
|
||||
#include "ScreenPass.h"
|
||||
|
||||
FUniStrokePoint::FUniStrokePoint()
|
||||
{
|
||||
this->X = 0.0f;
|
||||
this->Y = 0.0f;
|
||||
}
|
||||
|
||||
FUniStrokePoint::FUniStrokePoint(const float& X, const float& Y)
|
||||
{
|
||||
this->X = X;
|
||||
this->Y = Y;
|
||||
}
|
||||
|
||||
float
|
||||
FUniStrokePoint::Distance(const FUniStrokePoint& PointA,
|
||||
const FUniStrokePoint& PointB)
|
||||
{
|
||||
return FMath::Sqrt(
|
||||
// delta X
|
||||
FMath::Square(PointB.X - PointA.X) +
|
||||
/// delta Y
|
||||
FMath::Square(PointB.Y - PointA.Y)
|
||||
);
|
||||
}
|
||||
|
||||
FUniStrokePoint
|
||||
FUniStrokePoint::Centroid(const TArray<FUniStrokePoint>& Points)
|
||||
{
|
||||
float SumX = 0.0f;
|
||||
float SumY = 0.0f;
|
||||
|
||||
for (const FUniStrokePoint& Point : Points)
|
||||
{
|
||||
SumX += Point.X;
|
||||
SumY += Point.Y;
|
||||
}
|
||||
|
||||
return FUniStrokePoint(
|
||||
SumX / Points.Num(),
|
||||
SumY / Points.Num()
|
||||
);
|
||||
}
|
||||
|
||||
FUniStrokeRectangle
|
||||
FUniStrokePoint::BoundingBox(const TArray<FUniStrokePoint>& Points)
|
||||
{
|
||||
// Edge case
|
||||
if (Points.Num() == 0)
|
||||
{
|
||||
return FUniStrokeRectangle();
|
||||
}
|
||||
|
||||
float MinX = Points[0].X;
|
||||
float MinY = Points[0].Y;
|
||||
float MaxX = Points[0].X;
|
||||
float MaxY = Points[0].Y;
|
||||
|
||||
for (const FUniStrokePoint& Point : Points)
|
||||
{
|
||||
MinX = FMath::Min(MinX, Point.X);
|
||||
MinY = FMath::Min(MinY, Point.Y);
|
||||
MaxX = FMath::Max(MaxX, Point.X);
|
||||
MaxY = FMath::Max(MaxY, Point.Y);
|
||||
}
|
||||
|
||||
return FUniStrokeRectangle(MinX, MinY, MaxX - MinX, MaxY - MinY);
|
||||
}
|
||||
|
||||
// TODO: Proofread this function
|
||||
TArray<float>
|
||||
FUniStrokePoint::Vectorize(const TArray<FUniStrokePoint>& Points)
|
||||
{
|
||||
float Sum = 0.0f;
|
||||
TArray<float> Vector;
|
||||
|
||||
for (const FUniStrokePoint& Point : Points)
|
||||
{
|
||||
Vector.Add(Point.X);
|
||||
Vector.Add(Point.Y);
|
||||
Sum += FMath::Square(Point.X) + FMath::Square(Point.Y);
|
||||
}
|
||||
|
||||
const float Magnitude = FMath::Sqrt(Sum);
|
||||
|
||||
for (float& V : Vector)
|
||||
{
|
||||
V /= Magnitude;
|
||||
}
|
||||
|
||||
return Vector;
|
||||
}
|
||||
|
||||
// TODO: Proofread this function
|
||||
float
|
||||
FUniStrokePoint::OptimalCosineDistance(const TArray<float>& VectorA,
|
||||
const TArray<float>& VectorB)
|
||||
{
|
||||
float A = 0.0f;
|
||||
float B = 0.0f;
|
||||
|
||||
for (int i = 0; i < VectorA.Num(); i += 2)
|
||||
{
|
||||
A += VectorA[i] * VectorB[i] + VectorA[i + 1] * VectorB[i + 1];
|
||||
B += VectorA[i] * VectorB[i + 1] - VectorA[i + 1] * VectorB[i];
|
||||
}
|
||||
|
||||
const float Angle = FMath::Atan(B / A);
|
||||
|
||||
return FMath::Acos(A * FMath::Cos(Angle) + B * FMath::Sin(Angle));
|
||||
}
|
||||
|
||||
TArray<FUniStrokePoint>
|
||||
FUniStrokePoint::From(const TArray<FVector2D>& Points)
|
||||
{
|
||||
TArray<FUniStrokePoint> NewPoints;
|
||||
|
||||
NewPoints.Reserve(Points.Num());
|
||||
Algo::Transform(
|
||||
Points,
|
||||
NewPoints,
|
||||
[](const FVector2D& Point)
|
||||
{
|
||||
return FUniStrokePoint(Point.X, Point.Y);
|
||||
}
|
||||
);
|
||||
|
||||
return NewPoints;
|
||||
}
|
||||
|
||||
void
|
||||
FUniStrokePoint::Resample(TArray<FUniStrokePoint>& Points,
|
||||
const int& Num)
|
||||
{
|
||||
const float I = PathLength(Points) / (Num - 1);
|
||||
// D <- 0
|
||||
float D = 0.0f;
|
||||
// newPoints <- points[0]
|
||||
TArray<FUniStrokePoint> OldPoints = Points;
|
||||
Points.SetNum(1);
|
||||
|
||||
// foreach point p[i] for i >= 1 in points do
|
||||
for (int i = 1; i < OldPoints.Num(); ++i)
|
||||
{
|
||||
// d <- Distance(p[i - 1], p[i])
|
||||
const float d = Distance(OldPoints[i - 1], OldPoints[i]);
|
||||
|
||||
// if (D + d) >= I then
|
||||
if (D + d >= I)
|
||||
{
|
||||
// q.X <- p[i - 1].X + ((I - D) / d) * (p[i].X - p[i - 1].X)
|
||||
const float qx = OldPoints[i - 1].X + (I - D) / d * (OldPoints[i].X - OldPoints[i - 1].X);
|
||||
// q.Y <- p[i - 1].Y + ((I - D) / d) * (p[i].Y - p[i - 1].Y)
|
||||
const float qy = OldPoints[i - 1].Y + (I - D) / d * (OldPoints[i].Y - OldPoints[i - 1].Y);
|
||||
|
||||
// Append(newPoints, q)
|
||||
FUniStrokePoint NewPoint = FUniStrokePoint(qx, qy);
|
||||
Points.Add(NewPoint);
|
||||
// Insert(points, i, q)
|
||||
OldPoints.Insert(NewPoint, i);
|
||||
|
||||
// D <- 0
|
||||
D = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// D <- D + d
|
||||
D += d;
|
||||
}
|
||||
}
|
||||
|
||||
// Edge case
|
||||
if (Points.Num() == Num - 1)
|
||||
{
|
||||
Points.Add(OldPoints.Last());
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
FUniStrokePoint::PathLength(const TArray<FUniStrokePoint>& Points)
|
||||
{
|
||||
// d <- 0
|
||||
float d = 0;
|
||||
|
||||
// for i from 1 to |A| step 1 do
|
||||
for (auto i = 1; i < Points.Num(); ++i)
|
||||
{
|
||||
// d <- d + Distance(A[i - 1], A[i])
|
||||
d += Distance(Points[i - 1], Points[i]);
|
||||
}
|
||||
|
||||
// return d
|
||||
return d;
|
||||
}
|
||||
|
||||
void
|
||||
FUniStrokePoint::RotateToZero(TArray<FUniStrokePoint>& Points)
|
||||
{
|
||||
// c <- Centroid(points)
|
||||
FUniStrokePoint c = Centroid(Points);
|
||||
// theta <- Atan(c.Y - points[0].Y, c.X - points[0].X)
|
||||
const float Theta = FMath::Atan2(c.Y - Points[0].Y, c.X - Points[0].X);
|
||||
|
||||
// newPoints <- RotateBy(points, -theta)
|
||||
RotateBy(Points, -Theta);
|
||||
// return newPoints
|
||||
}
|
||||
|
||||
void
|
||||
FUniStrokePoint::RotateBy(TArray<FUniStrokePoint>& Points,
|
||||
const float& Theta)
|
||||
{
|
||||
TArray<FUniStrokePoint> OldPoints = Points;
|
||||
Points.Empty();
|
||||
|
||||
// c <- Centroid(points)
|
||||
const FUniStrokePoint c = Centroid(OldPoints);
|
||||
|
||||
const float SinTheta = FMath::Sin(Theta);
|
||||
const float CosTheta = FMath::Cos(Theta);
|
||||
|
||||
// foreach point p in points do
|
||||
for (const FUniStrokePoint& p : OldPoints)
|
||||
{
|
||||
FUniStrokePoint q = FUniStrokePoint(
|
||||
// q.x <- (p.x - c.x) * Cos(theta) - (p.y - c.y) * Sin(theta) + c.x
|
||||
(p.X - c.X) * CosTheta - (p.Y - c.Y) * SinTheta + c.X,
|
||||
// q.y <- (p.x - c.x) * Sin(theta) + (p.y - c.y) * Cos(theta) + c.y
|
||||
(p.X - c.X) * SinTheta + (p.Y - c.Y) * CosTheta + c.Y
|
||||
);
|
||||
// Append(newPoints, q)
|
||||
Points.Add(q);
|
||||
}
|
||||
// return newPoints
|
||||
}
|
||||
|
||||
void
|
||||
FUniStrokePoint::ScaleToSquare(TArray<FUniStrokePoint>& Points,
|
||||
const float& Size)
|
||||
{
|
||||
TArray<FUniStrokePoint> OldPoints = Points;
|
||||
Points.Empty();
|
||||
|
||||
// B <- BoundingRect(points)
|
||||
const FUniStrokeRectangle B = BoundingBox(OldPoints);
|
||||
|
||||
// foreach point p in points do
|
||||
for (const FUniStrokePoint& p : OldPoints)
|
||||
{
|
||||
const FUniStrokePoint q = FUniStrokePoint(
|
||||
// q.x <- p.x * (size / B.width)
|
||||
p.X * (Size / B.Width),
|
||||
// q.y <- p.y * (size / B.height)
|
||||
p.Y * (Size / B.Height)
|
||||
);
|
||||
// Append(newPoints, q)
|
||||
Points.Add(q);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FUniStrokePoint::TranslateTo(TArray<FUniStrokePoint>& Points,
|
||||
const FUniStrokePoint& Point)
|
||||
{
|
||||
TArray<FUniStrokePoint> OldPoints = Points;
|
||||
Points.Empty();
|
||||
|
||||
// c <- Centroid(points)
|
||||
const FUniStrokePoint c = Centroid(OldPoints);
|
||||
|
||||
// foreach point p in points do
|
||||
for (; const FUniStrokePoint& p : OldPoints)
|
||||
{
|
||||
const FUniStrokePoint q = FUniStrokePoint(
|
||||
// q.x <- p.x + point.x - c.x
|
||||
p.X + Point.X - c.X,
|
||||
// q.y <- p.y + point.y - c.y.
|
||||
p.Y + Point.Y - c.Y
|
||||
);
|
||||
// Append(newPoints, q)
|
||||
Points.Add(q);
|
||||
}
|
||||
// return newPoints
|
||||
}
|
||||
|
||||
void
|
||||
FUniStrokePoint::TranslateToOrigin(TArray<FUniStrokePoint>& Points)
|
||||
{
|
||||
TArray<FUniStrokePoint> OldPoints = Points;
|
||||
Points.Empty();
|
||||
|
||||
// c <- Centroid(points)
|
||||
const FUniStrokePoint c = Centroid(OldPoints);
|
||||
|
||||
// foreach point p in points do
|
||||
for (const FUniStrokePoint& p : OldPoints)
|
||||
{
|
||||
const FUniStrokePoint q = FUniStrokePoint(
|
||||
// q.x <- p.x - c.x
|
||||
p.X - c.X,
|
||||
// q.y <- p.y - c.y.
|
||||
p.Y - c.Y
|
||||
);
|
||||
// Append(newPoints, q)
|
||||
Points.Add(q);
|
||||
}
|
||||
// return newPoints
|
||||
}
|
||||
|
||||
float
|
||||
FUniStrokePoint::DistanceAtBestAngle(const TArray<FUniStrokePoint>& Points,
|
||||
const TArray<FUniStrokePoint>& T,
|
||||
const float& ThetaFrom,
|
||||
const float& ThetaTo,
|
||||
const float& ThetaThreshold)
|
||||
{
|
||||
float ThetaA = ThetaFrom;
|
||||
float ThetaB = ThetaTo;
|
||||
|
||||
// x1 <- phi theta_a + (1 - phi) theta_b
|
||||
float x1 = Phi * ThetaA + (1 - Phi) * ThetaB;
|
||||
// f1 <- DistanceAtAngle(points, T, x1)
|
||||
float f1 = DistanceAtAngle(Points, T, x1);
|
||||
// x2 <- (1 - phi) theta_a + phi theta_b
|
||||
float x2 = (1 - Phi) * ThetaA + Phi * ThetaB;
|
||||
// f2 <- DistanceAtAngle(points, T, x2)
|
||||
float f2 = DistanceAtAngle(Points, T, x2);
|
||||
|
||||
// while |theta_b - theta_a| > threshold do
|
||||
while (FMath::Abs(ThetaB - ThetaA) > ThetaThreshold)
|
||||
{
|
||||
// if f1 < f2 then
|
||||
if (f1 < f2)
|
||||
{
|
||||
// theta_b <- x2
|
||||
ThetaB = x2;
|
||||
// x2 <- x1
|
||||
x2 = x1;
|
||||
// f2 <- f1
|
||||
f2 = f1;
|
||||
// x1 <- phi theta_a + (1 - phi) theta_b
|
||||
x1 = Phi * ThetaA + (1 - Phi) * ThetaB;
|
||||
// f1 <- DistanceAtAngle(points, T, x1)
|
||||
f1 = DistanceAtAngle(Points, T, x1);
|
||||
}
|
||||
// else
|
||||
else
|
||||
{
|
||||
// theta_a <- x1
|
||||
ThetaA = x1;
|
||||
// x1 <- x2
|
||||
x1 = x2;
|
||||
// f1 <- f2
|
||||
f1 = f2;
|
||||
// x2 <- (1 - phi) theta_a + phi theta_b
|
||||
x2 = (1 - Phi) * ThetaA + Phi * ThetaB;
|
||||
// f2 <- DistanceAtAngle(points, T, x2)
|
||||
f2 = DistanceAtAngle(Points, T, x2);
|
||||
}
|
||||
}
|
||||
|
||||
// return min(f1, f2)
|
||||
return FMath::Min(f1, f2);
|
||||
}
|
||||
|
||||
float
|
||||
FUniStrokePoint::DistanceAtAngle(const TArray<FUniStrokePoint>& Points,
|
||||
const TArray<FUniStrokePoint>& T,
|
||||
const float& Theta)
|
||||
{
|
||||
// newPoints <- RotateBy(points, theta)
|
||||
TArray<FUniStrokePoint> NewPoints = Points;
|
||||
RotateBy(NewPoints, Theta);
|
||||
|
||||
// d <- PathDistance(newPoints, T.points)
|
||||
// return d
|
||||
return PathDistance(NewPoints, T);
|
||||
}
|
||||
|
||||
float
|
||||
FUniStrokePoint::PathDistance(const TArray<FUniStrokePoint>& PathA,
|
||||
const TArray<FUniStrokePoint>& PathB)
|
||||
{
|
||||
// Edge case: Different number of points
|
||||
if (PathA.Num() != PathB.Num())
|
||||
{
|
||||
return TNumericLimits<float>::Max();
|
||||
}
|
||||
|
||||
// d <- 0
|
||||
float d = 0;
|
||||
|
||||
// for i from 0 to |A| step 1 do
|
||||
for (int i = 0; i < PathA.Num(); ++i)
|
||||
{
|
||||
// d <- d + Distance(A[i], B[i])
|
||||
d += Distance(PathA[i], PathB[i]);
|
||||
}
|
||||
|
||||
// return d / |A|
|
||||
return d / PathA.Num();
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright Team Lumi. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UniStrokeRectangle.h"
|
||||
#include "UniStrokePoint.generated.h"
|
||||
|
||||
static const float Phi = 0.5f * (-1.0f + FMath::Sqrt(5.0f));
|
||||
|
||||
USTRUCT()
|
||||
struct WIZARDINGCENTRAL_API FUniStrokePoint
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
FUniStrokePoint();
|
||||
FUniStrokePoint(const float& X, const float& Y);
|
||||
~FUniStrokePoint() = default;
|
||||
|
||||
static void
|
||||
Resample(TArray<FUniStrokePoint>& Points,
|
||||
const int& Num);
|
||||
|
||||
static float
|
||||
PathLength(const TArray<FUniStrokePoint>& Points);
|
||||
|
||||
static void
|
||||
RotateToZero(TArray<FUniStrokePoint>& Points);
|
||||
|
||||
static void
|
||||
RotateBy(TArray<FUniStrokePoint>& Points,
|
||||
const float& Theta);
|
||||
|
||||
static void
|
||||
ScaleToSquare(TArray<FUniStrokePoint>& Points,
|
||||
const float& Size);
|
||||
|
||||
static void
|
||||
TranslateTo(TArray<FUniStrokePoint>& Points,
|
||||
const FUniStrokePoint& Point);
|
||||
|
||||
static void
|
||||
TranslateToOrigin(TArray<FUniStrokePoint>& Points);
|
||||
|
||||
static float
|
||||
DistanceAtBestAngle(const TArray<FUniStrokePoint>& Points,
|
||||
const TArray<FUniStrokePoint>& T,
|
||||
const float& ThetaFrom,
|
||||
const float& ThetaTo,
|
||||
const float& ThetaThreshold);
|
||||
|
||||
static float
|
||||
DistanceAtAngle(const TArray<FUniStrokePoint>& Points,
|
||||
const TArray<FUniStrokePoint>& T,
|
||||
const float& Theta);
|
||||
|
||||
static float
|
||||
PathDistance(const TArray<FUniStrokePoint>& PathA,
|
||||
const TArray<FUniStrokePoint>& PathB);
|
||||
|
||||
/**
|
||||
* Converts a TArray of FVector2D to a TArray of FUniStrokePoint
|
||||
*
|
||||
* @param Points The points to convert
|
||||
* @return The converted points as a TArray of FUniStrokePoint
|
||||
*/
|
||||
static TArray<FUniStrokePoint>
|
||||
From(const TArray<FVector2D>& Points);
|
||||
|
||||
static float
|
||||
Distance(const FUniStrokePoint& PointA,
|
||||
const FUniStrokePoint& PointB);
|
||||
|
||||
static FUniStrokePoint
|
||||
Centroid(const TArray<FUniStrokePoint>& Points);
|
||||
|
||||
static FUniStrokeRectangle
|
||||
BoundingBox(const TArray<FUniStrokePoint>& Points);
|
||||
|
||||
static float
|
||||
OptimalCosineDistance(const TArray<float>& VectorA,
|
||||
const TArray<float>& VectorB);
|
||||
|
||||
static TArray<float>
|
||||
Vectorize(const TArray<FUniStrokePoint>& Points);
|
||||
|
||||
private:
|
||||
float X;
|
||||
float Y;
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "UniStrokeRecognizer.h"
|
||||
|
||||
FUniStrokeRecognizer::FUniStrokeRecognizer()
|
||||
{
|
||||
this->Templates = TArray<FUniStrokeTemplate>();
|
||||
}
|
||||
|
||||
FUniStrokeRecognizer::~FUniStrokeRecognizer()
|
||||
{
|
||||
// DO NOTHING
|
||||
}
|
||||
|
||||
// TODO: Review this function
|
||||
FUniStrokeResult
|
||||
FUniStrokeRecognizer::Recognize(const TArray<FVector2D>& VectorPoints,
|
||||
const bool& UseProtractor)
|
||||
{
|
||||
TArray<FUniStrokePoint> Points = FUniStrokePoint::From(VectorPoints);
|
||||
|
||||
// Edge case: Not enough points
|
||||
if (Points.Num() < 2 || FUniStrokePoint::PathLength(Points) < 100.0f)
|
||||
{
|
||||
return FUniStrokeResult("Too few points made", 0.0);
|
||||
}
|
||||
|
||||
const FUniStrokeTemplate Candidate = FUniStrokeTemplate("", Points);
|
||||
int TemplateIndex = -1;
|
||||
|
||||
// b <- +infty
|
||||
float b = TNumericLimits<float>::Max();
|
||||
|
||||
// foreach template T in templates do
|
||||
for (int i = 0; i < Templates.Num(); i++)
|
||||
{
|
||||
// d <- DistanceAtBestAngle(points, T, -theta, theta, threshold)
|
||||
const float d = UseProtractor
|
||||
? FUniStrokePoint::OptimalCosineDistance(
|
||||
Templates[i].Vector,
|
||||
Candidate.Vector
|
||||
)
|
||||
: FUniStrokePoint::DistanceAtBestAngle(
|
||||
Candidate.Points,
|
||||
Templates[i].Points,
|
||||
-AngleRange,
|
||||
AngleRange,
|
||||
AnglePrecision
|
||||
);
|
||||
|
||||
// if d < b then
|
||||
if (d < b)
|
||||
{
|
||||
// b <- d
|
||||
b = d;
|
||||
// T' <- T
|
||||
TemplateIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// score <- 1 – b / 0.5 sqrt(size^2 + size^2)
|
||||
const float Score = UseProtractor ? 1.0 - b : 1.0 - b / HalfDiagonal;
|
||||
|
||||
// return <T', score>
|
||||
return TemplateIndex == -1
|
||||
? FUniStrokeResult("No match", 0.0)
|
||||
: FUniStrokeResult(Templates[TemplateIndex].Name, Score);
|
||||
}
|
||||
|
||||
void
|
||||
FUniStrokeRecognizer::AddTemplate(const FString& Name,
|
||||
const TArray<FVector2D>& VectorPoints)
|
||||
{
|
||||
const TArray<FUniStrokePoint> Points = FUniStrokePoint::From(VectorPoints);
|
||||
this->Templates.Add(FUniStrokeTemplate(Name, Points));
|
||||
}
|
||||
|
||||
void FUniStrokeRecognizer::Reset()
|
||||
{
|
||||
Templates.SetNum(NumTemplates);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright Team Lumi. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UniStrokeResult.h"
|
||||
#include "UniStrokeTemplate.h"
|
||||
#include "UniStrokeRecognizer.generated.h"
|
||||
|
||||
static constexpr int NumTemplates = 16;
|
||||
static const float Diagonal = FMath::Sqrt(2 * FMath::Square(SquareSize));
|
||||
static const float HalfDiagonal = 0.5 * Diagonal;
|
||||
static constexpr float AngleRange = FMath::DegreesToRadians(45.0);
|
||||
static constexpr float AnglePrecision = FMath::DegreesToRadians(2.0);
|
||||
|
||||
USTRUCT()
|
||||
struct WIZARDINGCENTRAL_API FUniStrokeRecognizer
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
FUniStrokeRecognizer();
|
||||
~FUniStrokeRecognizer();
|
||||
|
||||
FUniStrokeResult
|
||||
Recognize(const TArray<FVector2D>& VectorPoints, const bool& UseProtractor);
|
||||
|
||||
void
|
||||
AddTemplate(const FString& Name, const TArray<FVector2D>& VectorPoints);
|
||||
|
||||
void
|
||||
Reset();
|
||||
|
||||
TArray<FUniStrokeTemplate> Templates;
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "UniStrokeRectangle.h"
|
||||
|
||||
FUniStrokeRectangle::FUniStrokeRectangle()
|
||||
{
|
||||
X = 0.0f;
|
||||
Y = 0.0f;
|
||||
Width = 0.0f;
|
||||
Height = 0.0f;
|
||||
}
|
||||
|
||||
FUniStrokeRectangle::FUniStrokeRectangle(const float& X, const float& Y, const float& Width, const float& Height)
|
||||
{
|
||||
this->X = X;
|
||||
this->Y = Y;
|
||||
this->Width = Width;
|
||||
this->Height = Height;
|
||||
}
|
||||
|
||||
FUniStrokeRectangle::~FUniStrokeRectangle()
|
||||
{
|
||||
// DO NOTHING
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright Team Lumi. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UniStrokeRectangle.generated.h"
|
||||
|
||||
USTRUCT()
|
||||
struct FUniStrokeRectangle
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
FUniStrokeRectangle();
|
||||
FUniStrokeRectangle(const float& X,
|
||||
const float& Y,
|
||||
const float& Width,
|
||||
const float& Height);
|
||||
~FUniStrokeRectangle();
|
||||
|
||||
float X;
|
||||
float Y;
|
||||
float Width;
|
||||
float Height;
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "UniStrokeResult.h"
|
||||
|
||||
FUniStrokeResult::FUniStrokeResult()
|
||||
{
|
||||
this->Name = "No match";
|
||||
this->Score = 0.0f;
|
||||
}
|
||||
|
||||
FUniStrokeResult::FUniStrokeResult(const FString& Name,
|
||||
const float& Score)
|
||||
{
|
||||
this->Name = Name;
|
||||
this->Score = Score;
|
||||
}
|
||||
|
||||
FUniStrokeResult::~FUniStrokeResult()
|
||||
{
|
||||
// DO NOTHING
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright Team Lumi. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UniStrokeResult.generated.h"
|
||||
|
||||
USTRUCT()
|
||||
struct WIZARDINGCENTRAL_API FUniStrokeResult
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
FUniStrokeResult();
|
||||
FUniStrokeResult(const FString& Name,
|
||||
const float& Score);
|
||||
~FUniStrokeResult();
|
||||
|
||||
FString Name;
|
||||
float Score;
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "UniStrokeTemplate.h"
|
||||
|
||||
FUniStrokeTemplate::FUniStrokeTemplate()
|
||||
{
|
||||
this->Name = "";
|
||||
this->Vector = TArray<float>();
|
||||
this->Points = TArray<FUniStrokePoint>();
|
||||
}
|
||||
|
||||
FUniStrokeTemplate::FUniStrokeTemplate(const FString& Name,
|
||||
const TArray<FUniStrokePoint>)
|
||||
{
|
||||
this->Name = Name;
|
||||
this->Points = Points;
|
||||
|
||||
FUniStrokePoint::Resample(this->Points, NumPoints);
|
||||
FUniStrokePoint::RotateToZero(this->Points);
|
||||
FUniStrokePoint::ScaleToSquare(this->Points, SquareSize);
|
||||
FUniStrokePoint::TranslateToOrigin(this->Points);
|
||||
|
||||
this->Vector = FUniStrokePoint::Vectorize(this->Points);
|
||||
}
|
||||
|
||||
FUniStrokeTemplate::~FUniStrokeTemplate()
|
||||
{
|
||||
// DO NOTHING
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright Team Lumi. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UniStrokePoint.h"
|
||||
#include "UniStrokeTemplate.generated.h"
|
||||
|
||||
static constexpr int NumPoints = 64;
|
||||
static constexpr float SquareSize = 250.0f;
|
||||
|
||||
USTRUCT()
|
||||
struct WIZARDINGCENTRAL_API FUniStrokeTemplate
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
FUniStrokeTemplate();
|
||||
FUniStrokeTemplate(const FString& Name,
|
||||
const TArray<FUniStrokePoint>);
|
||||
~FUniStrokeTemplate();
|
||||
|
||||
FString Name;
|
||||
TArray<float> Vector;
|
||||
TArray<FUniStrokePoint> Points;
|
||||
};
|
||||
Reference in New Issue
Block a user