diff --git a/Integration.code-workspace.tpl.json b/Integration.code-workspace.tpl.json index 049ced9f..618ecbd4 100644 --- a/Integration.code-workspace.tpl.json +++ b/Integration.code-workspace.tpl.json @@ -41,7 +41,7 @@ }, "editor.acceptSuggestionOnEnter": "off", "C_Cpp.intelliSenseEngine": "disabled", - "clangd.path": "/usr/bin/clangd-15", + "clangd.path": "/usr/bin/clangd-16", "clangd.arguments": [ "--log=verbose", "--query-driver=/usr/bin/g++", diff --git a/Source/Integration/RadialMenu.cpp b/Source/Integration/RadialMenu.cpp new file mode 100644 index 00000000..e1b55cbd --- /dev/null +++ b/Source/Integration/RadialMenu.cpp @@ -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); + } +} diff --git a/Source/Integration/RadialMenu.h b/Source/Integration/RadialMenu.h new file mode 100644 index 00000000..05b86234 --- /dev/null +++ b/Source/Integration/RadialMenu.h @@ -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 GetItems() const { return Items; } + + TArray Items; + +private: + using View = TArrayView; + + 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; +}; +