Files
integration/Source/Integration/RadialMenu.cpp

104 lines
3.1 KiB
C++

#include "RadialMenu.h"
void URadialMenu::Configure(int32 NItems, float ItemHeight, float InnerRadius, float MinSpoke, float Spread)
{
NumRight = (NItems / 2);
NumLeft = NItems - NumRight;
Items.SetNum(NItems);
View LeftItems(Items.GetData(), NumLeft);
View RightItems(Items.GetData() + NumLeft, NumRight);
CalculateSpokes(LeftItems, ItemHeight, InnerRadius, MinSpoke);
CalculateSpokes(RightItems, ItemHeight, InnerRadius, MinSpoke);
double LeftWidth = WidestSpoke(LeftItems);
double RightWidth = WidestSpoke(RightItems);
double HalfWidth = FMath::Max(LeftWidth, RightWidth) + Spread;
CalculateSpread(LeftItems, HalfWidth - LeftWidth);
CalculateSpread(RightItems, HalfWidth - RightWidth);
FlipHorizontal(LeftItems);
}
FVector2D URadialMenu::SpokeVector(int32 I, int32 NSide, int32 NTotal)
{
double SpokeAngle = 1.0 / NTotal;
double OffsetAngle = 0.5 * (0.5 - ((NSide - 1) * SpokeAngle));
double Revolutions = (I * SpokeAngle) + OffsetAngle;
double Radians = (Revolutions * 2.0 * UE_PI);
return FVector2D(FMath::Sin(Radians), -FMath::Cos(Radians));
}
void URadialMenu::FlipHorizontal(View V)
{
for (FRadialMenuItem& Item : V)
{
Item.Point1.X = -Item.Point1.X;
Item.Point2.X = -Item.Point2.X;
Item.Point3.X = -Item.Point3.X;
Item.RightSide = !Item.RightSide;
}
}
double URadialMenu::WidestSpoke(View V)
{
double Result = 0.0;
for (const FRadialMenuItem &Item : V)
{
Result = FMath::Max(Item.Point2.X, Result);
}
return Result;
}
void URadialMenu::CalculateSpread(View V, double Offset)
{
for (FRadialMenuItem &Item : V)
{
Item.Point3 = Item.Point2 + FVector2D(Offset, 0.0);
}
}
void URadialMenu::CalculateSpokes(View V, float ItemHeight, float InnerRadius, float MinSpoke)
{
if (V.Num() == 0) return;
// RightSide is always initialized to true, it may get
// reversed by FlipHorizontal.
for (int32 I = 0; I < V.Num(); I++)
{
V[I].RightSide = true;
V[I].Point1 = SpokeVector(I, V.Num(), Items.Num()) * InnerRadius;
}
// Calculate point2 for all spokes.
double NextLineMin = ItemHeight * 0.5;
int32 Mid = (V.Num() / 2);
if (V.Num() & 1)
{
V[Mid].Point2 = FVector2D(InnerRadius + MinSpoke, 0.0);
NextLineMin = ItemHeight;
Mid += 1;
}
for (int32 I = Mid; I < V.Num(); I++)
{
FVector2D UnitVec = SpokeVector(I, V.Num(), Items.Num());
double Y = (UnitVec.Y * (InnerRadius + MinSpoke));
if (Y < NextLineMin) Y = NextLineMin;
NextLineMin = Y + ItemHeight;
FVector2D Point2 = UnitVec * (Y / UnitVec.Y);
V[I].Point2 = Point2;
V[V.Num() - 1 - I].Point2 = Point2 * FVector2D(1.0,-1.0);
}
// The middle spoke is calculated using a different formula,
// which may result in a short spoke. If so, fix it to make
// it at least as long as the adjacent spoke.
if ((V.Num() & 1) && (V.Num() >= 3))
{
Mid = V.Num() / 2;
if (V[Mid].Point2.X < V[Mid + 1].Point2.X)
V[Mid].Point2.X = V[Mid + 1].Point2.X;
}
}