More work on radial menus
This commit is contained in:
@@ -41,7 +41,7 @@
|
|||||||
},
|
},
|
||||||
"editor.acceptSuggestionOnEnter": "off",
|
"editor.acceptSuggestionOnEnter": "off",
|
||||||
"C_Cpp.intelliSenseEngine": "disabled",
|
"C_Cpp.intelliSenseEngine": "disabled",
|
||||||
"clangd.path": "/usr/bin/clangd-15",
|
"clangd.path": "/usr/bin/clangd-16",
|
||||||
"clangd.arguments": [
|
"clangd.arguments": [
|
||||||
"--log=verbose",
|
"--log=verbose",
|
||||||
"--query-driver=/usr/bin/g++",
|
"--query-driver=/usr/bin/g++",
|
||||||
|
|||||||
86
Source/Integration/RadialMenu.cpp
Normal file
86
Source/Integration/RadialMenu.cpp
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#include "RadialMenu.h"
|
||||||
|
|
||||||
|
|
||||||
|
void URadialMenu::Configure(int32 NItems, float ItemHeight, float InnerRadius, float MinSpoke, float Spread)
|
||||||
|
{
|
||||||
|
CNItems = NItems;
|
||||||
|
CItemHeight = ItemHeight;
|
||||||
|
CInnerRadius = InnerRadius;
|
||||||
|
CMinSpoke = MinSpoke;
|
||||||
|
CSpread = Spread;
|
||||||
|
|
||||||
|
CNumRight = (NItems / 2);
|
||||||
|
CNumLeft = NItems - CNumRight;
|
||||||
|
Items.SetNum(NItems);
|
||||||
|
|
||||||
|
LeftItems = View(Items.GetData(), CNumLeft);
|
||||||
|
RightItems = View(Items.GetData() + CNumLeft, CNumRight);
|
||||||
|
|
||||||
|
CalculateSide(LeftItems);
|
||||||
|
CalculateSide(RightItems);
|
||||||
|
FlipHorizontal(LeftItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
FVector2D URadialMenu::PieSliceToVector(double Slice, double Slices)
|
||||||
|
{
|
||||||
|
double HalfRevolutions = (Slice + 0.5) / Slices;
|
||||||
|
double Radians = (HalfRevolutions * 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void URadialMenu::CalculateSide(View V)
|
||||||
|
{
|
||||||
|
// Point1 is simple. RightSide is always initialized to
|
||||||
|
// true, it may get reversed in FlipHorizontal.
|
||||||
|
for (int32 I = 0; I < V.Num(); I++)
|
||||||
|
{
|
||||||
|
V[I].RightSide = true;
|
||||||
|
V[I].Point1 = PieSliceToVector(I, V.Num()) * CInnerRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate point2 for all spokes.
|
||||||
|
double NextLineMin = CItemHeight * 0.5;
|
||||||
|
int32 Mid = (V.Num() / 2);
|
||||||
|
if (V.Num() & 1)
|
||||||
|
{
|
||||||
|
V[Mid].Point2 = FVector2D(CInnerRadius + CMinSpoke, 0.0);
|
||||||
|
NextLineMin = CItemHeight;
|
||||||
|
Mid += 1;
|
||||||
|
}
|
||||||
|
for (int32 I = Mid; I < V.Num(); I++)
|
||||||
|
{
|
||||||
|
FVector2D UnitVec = PieSliceToVector(I, V.Num());
|
||||||
|
double Y = (UnitVec.Y * (CInnerRadius + CMinSpoke));
|
||||||
|
if (Y < NextLineMin) Y = NextLineMin;
|
||||||
|
NextLineMin = Y + CItemHeight;
|
||||||
|
FVector2D Point2 = UnitVec * (Y / UnitVec.Y);
|
||||||
|
V[I].Point2 = Point2;
|
||||||
|
V[V.Num() - I].Point2 = Point2 * FVector2D(1.0,-1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The rule we use for calculating point2 may result in
|
||||||
|
// a very short horizontal spoke. If so, fix it.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate Point3.
|
||||||
|
for (int32 I = 0; I < V.Num(); I++)
|
||||||
|
{
|
||||||
|
V[I].Point3 = V[I].Point2 + FVector2D(CSpread, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
60
Source/Integration/RadialMenu.h
Normal file
60
Source/Integration/RadialMenu.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// This class implements the layout calculatations for a radial
|
||||||
|
// menu.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "RadialMenu.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FRadialMenuItem
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
FVector2D Point1 = {0,0};
|
||||||
|
FVector2D Point2 = {0,0};
|
||||||
|
FVector2D Point3 = {0,0};
|
||||||
|
bool RightSide = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
UCLASS(BlueprintType)
|
||||||
|
class URadialMenu : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
|
void Configure(int32 NItems, float ItemHeight, float InnerRadius, float MinSpoke, float Spread);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
|
int32 NumItems() const { return Items.Num(); }
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
|
const TArray<FRadialMenuItem> GetItems() const { return Items; }
|
||||||
|
|
||||||
|
TArray<FRadialMenuItem> Items;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using View = TArrayView<FRadialMenuItem>;
|
||||||
|
|
||||||
|
void CalculateSide(View V);
|
||||||
|
void FlipHorizontal(View V);
|
||||||
|
|
||||||
|
// The half-circle is divided into pie slices. Returns a direction vector
|
||||||
|
// which aims directly down the center of the pie slice.
|
||||||
|
FVector2D PieSliceToVector(double Slice, double Slices);
|
||||||
|
|
||||||
|
int32 CNItems = 0;
|
||||||
|
float CItemHeight = 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