Layout code for radial menu complete.
This commit is contained in:
@@ -3,28 +3,30 @@
|
|||||||
|
|
||||||
void URadialMenu::Configure(int32 NItems, float ItemHeight, float InnerRadius, float MinSpoke, float Spread)
|
void URadialMenu::Configure(int32 NItems, float ItemHeight, float InnerRadius, float MinSpoke, float Spread)
|
||||||
{
|
{
|
||||||
CNItems = NItems;
|
NumRight = (NItems / 2);
|
||||||
CItemHeight = ItemHeight;
|
NumLeft = NItems - NumRight;
|
||||||
CInnerRadius = InnerRadius;
|
|
||||||
CMinSpoke = MinSpoke;
|
|
||||||
CSpread = Spread;
|
|
||||||
|
|
||||||
CNumRight = (NItems / 2);
|
|
||||||
CNumLeft = NItems - CNumRight;
|
|
||||||
Items.SetNum(NItems);
|
Items.SetNum(NItems);
|
||||||
|
|
||||||
LeftItems = View(Items.GetData(), CNumLeft);
|
View LeftItems(Items.GetData(), NumLeft);
|
||||||
RightItems = View(Items.GetData() + CNumLeft, CNumRight);
|
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);
|
||||||
|
|
||||||
CalculateSide(LeftItems);
|
|
||||||
CalculateSide(RightItems);
|
|
||||||
FlipHorizontal(LeftItems);
|
FlipHorizontal(LeftItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
FVector2D URadialMenu::PieSliceToVector(double Slice, double Slices)
|
FVector2D URadialMenu::SpokeVector(int32 I, int32 NSide, int32 NTotal)
|
||||||
{
|
{
|
||||||
double HalfRevolutions = (Slice + 0.5) / Slices;
|
double SpokeAngle = 1.0 / NTotal;
|
||||||
double Radians = (HalfRevolutions * UE_PI);
|
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));
|
return FVector2D(FMath::Sin(Radians), -FMath::Cos(Radians));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,48 +41,63 @@ void URadialMenu::FlipHorizontal(View V)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenu::CalculateSide(View V)
|
double URadialMenu::WidestSpoke(View V)
|
||||||
{
|
{
|
||||||
// Point1 is simple. RightSide is always initialized to
|
double Result = 0.0;
|
||||||
// true, it may get reversed in FlipHorizontal.
|
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++)
|
for (int32 I = 0; I < V.Num(); I++)
|
||||||
{
|
{
|
||||||
V[I].RightSide = true;
|
V[I].RightSide = true;
|
||||||
V[I].Point1 = PieSliceToVector(I, V.Num()) * CInnerRadius;
|
V[I].Point1 = SpokeVector(I, V.Num(), Items.Num()) * InnerRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate point2 for all spokes.
|
// Calculate point2 for all spokes.
|
||||||
double NextLineMin = CItemHeight * 0.5;
|
double NextLineMin = ItemHeight * 0.5;
|
||||||
int32 Mid = (V.Num() / 2);
|
int32 Mid = (V.Num() / 2);
|
||||||
if (V.Num() & 1)
|
if (V.Num() & 1)
|
||||||
{
|
{
|
||||||
V[Mid].Point2 = FVector2D(CInnerRadius + CMinSpoke, 0.0);
|
V[Mid].Point2 = FVector2D(InnerRadius + MinSpoke, 0.0);
|
||||||
NextLineMin = CItemHeight;
|
NextLineMin = ItemHeight;
|
||||||
Mid += 1;
|
Mid += 1;
|
||||||
}
|
}
|
||||||
for (int32 I = Mid; I < V.Num(); I++)
|
for (int32 I = Mid; I < V.Num(); I++)
|
||||||
{
|
{
|
||||||
FVector2D UnitVec = PieSliceToVector(I, V.Num());
|
FVector2D UnitVec = SpokeVector(I, V.Num(), Items.Num());
|
||||||
double Y = (UnitVec.Y * (CInnerRadius + CMinSpoke));
|
double Y = (UnitVec.Y * (InnerRadius + MinSpoke));
|
||||||
if (Y < NextLineMin) Y = NextLineMin;
|
if (Y < NextLineMin) Y = NextLineMin;
|
||||||
NextLineMin = Y + CItemHeight;
|
NextLineMin = Y + ItemHeight;
|
||||||
FVector2D Point2 = UnitVec * (Y / UnitVec.Y);
|
FVector2D Point2 = UnitVec * (Y / UnitVec.Y);
|
||||||
V[I].Point2 = Point2;
|
V[I].Point2 = Point2;
|
||||||
V[V.Num() - I].Point2 = Point2 * FVector2D(1.0,-1.0);
|
V[V.Num() - 1 - I].Point2 = Point2 * FVector2D(1.0,-1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The rule we use for calculating point2 may result in
|
// The middle spoke is calculated using a different formula,
|
||||||
// a very short horizontal spoke. If so, fix it.
|
// 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))
|
if ((V.Num() & 1) && (V.Num() >= 3))
|
||||||
{
|
{
|
||||||
Mid = V.Num() / 2;
|
Mid = V.Num() / 2;
|
||||||
if (V[Mid].Point2.X < V[Mid + 1].Point2.X)
|
if (V[Mid].Point2.X < V[Mid + 1].Point2.X)
|
||||||
V[Mid].Point2.X = V[Mid + 1].Point2.X;
|
V[Mid].Point2.X = V[Mid + 1].Point2.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate Point3.
|
|
||||||
for (int32 I = 0; I < V.Num(); I++)
|
|
||||||
{
|
|
||||||
V[I].Point3 = V[I].Point2 + FVector2D(CSpread, 0.0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,31 +30,48 @@ public:
|
|||||||
void Configure(int32 NItems, float ItemHeight, float InnerRadius, float MinSpoke, float Spread);
|
void Configure(int32 NItems, float ItemHeight, float InnerRadius, float MinSpoke, float Spread);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
int32 NumItems() const { return Items.Num(); }
|
int32 LeftNum() const { return NumLeft; }
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
|
int32 RightNum() const { return NumRight; }
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
const TArray<FRadialMenuItem> GetItems() const { return Items; }
|
const TArray<FRadialMenuItem> GetItems() const { return Items; }
|
||||||
|
|
||||||
TArray<FRadialMenuItem> Items;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using View = TArrayView<FRadialMenuItem>;
|
using View = TArrayView<FRadialMenuItem>;
|
||||||
|
|
||||||
void CalculateSide(View V);
|
// Give the unit vector for the selected spoke. NSide is
|
||||||
|
// the number of spokes on the side of the wheel that
|
||||||
|
// we're calculating, and NTotal is the total number of
|
||||||
|
// spokes on both sides. The spokes on a given side are
|
||||||
|
// organized top-to-bottom, and the angle between the
|
||||||
|
// spokes is always equal to (1/NTotal) of the circle.
|
||||||
|
FVector2D SpokeVector(int32 I, int32 NSide, int32 NTotal);
|
||||||
|
|
||||||
|
// Populate Point1 and Point2, these are the
|
||||||
|
// endpoints of the spoke segment. Spokes are
|
||||||
|
// designed to always be long enough to make room
|
||||||
|
// for MinSpoke, but also to make enough room to
|
||||||
|
// keep the menu items from overlapping.
|
||||||
|
void CalculateSpokes(View V, float ItemHeight, float InnerRadius, float MinSpoke);
|
||||||
|
|
||||||
|
// Search for the widest spoke, and return its X coordinate.
|
||||||
|
double WidestSpoke(View V);
|
||||||
|
|
||||||
|
// Populate Point3, this is the endpoint of the spread
|
||||||
|
// line that goes horizontal.
|
||||||
|
void CalculateSpread(View V, double Offset);
|
||||||
|
|
||||||
|
// Flip everything in the specified view horizontally.
|
||||||
void FlipHorizontal(View V);
|
void FlipHorizontal(View V);
|
||||||
|
|
||||||
// The half-circle is divided into pie slices. Returns a direction vector
|
// The array of items.
|
||||||
// which aims directly down the center of the pie slice.
|
TArray<FRadialMenuItem> Items;
|
||||||
FVector2D PieSliceToVector(double Slice, double Slices);
|
|
||||||
|
// Number of items on the left, and on the right.
|
||||||
int32 CNItems = 0;
|
int32 NumLeft = 0;
|
||||||
float CItemHeight = 0;
|
int32 NumRight = 0;
|
||||||
float CInnerRadius = 0;
|
|
||||||
float CMinSpoke = 0;
|
|
||||||
float CSpread = 0;
|
|
||||||
int32 CNumLeft = 0;
|
|
||||||
int32 CNumRight = 0;
|
|
||||||
View LeftItems;
|
|
||||||
View RightItems;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user