Files
FLESH/Source/FLESHEditor/Private/MatrixInputWidget.cpp

595 lines
17 KiB
C++

#include "MatrixInputWidget.h"
#include "SlateOptMacros.h"
#include "Widgets/Layout/SUniformGridPanel.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Layout/SScrollBox.h"
#include "Styling/AppStyle.h"
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SMatrixInputWidget::Construct(const FArguments& InArgs)
{
Matrix = InArgs._Matrix;
OnMatrixChanged = InArgs._OnMatrixChanged;
Rows = InArgs._Rows;
Columns = InArgs._Columns;
// Create a grid panel for the matrix elements
GridPanel = SNew(SGridPanel);
// Create numeric entry boxes for each matrix element
NumericEntryBoxes.SetNum(Rows * Columns); // Allocate numeric entry boxes based on rows and columns
// Add labels for rows and columns
for (int32 Col = 0; Col < Columns; ++Col)
{
FString ColLabel;
switch (Col)
{
case 0: ColLabel = "X"; break;
case 1: ColLabel = "Y"; break;
case 2: ColLabel = "Z"; break;
default: ColLabel = FString::Printf(TEXT("Column %d"), Col);
}
GridPanel->AddSlot(Col + 1, 0)
[
SNew(STextBlock)
.Text(FText::FromString(ColLabel))
.Margin(FMargin(5.0f))
];
}
for (int32 Row = 0; Row < Rows; ++Row)
{
FString RowLabel;
switch (Row)
{
case 0: RowLabel = "Right"; break;
case 1: RowLabel = "Forward"; break;
case 2: RowLabel = "Up"; break;
case 3: RowLabel = "Position"; break;
default: RowLabel = FString::Printf(TEXT("Row %d"), Row);
}
GridPanel->AddSlot(0, Row + 1)
[
SNew(STextBlock)
.Text(FText::FromString(RowLabel))
.Margin(FMargin(5.0f))
];
}
// Add numeric entry boxes for each matrix element
for (int32 Row = 0; Row < Rows; ++Row)
{
for (int32 Col = 0; Col < Columns; ++Col)
{
TSharedPtr<SNumericEntryBox<float>> NumericEntryBox = CreateMatrixElementWidget(Row, Col);
NumericEntryBoxes[Row * Columns + Col] = NumericEntryBox;
GridPanel->AddSlot(Col + 1, Row + 1)
[
SNew(SBox)
.WidthOverride(80.0f)
.Padding(FMargin(2.0f))
[
NumericEntryBox.ToSharedRef()
]
];
}
}
// Create buttons for common operations
TSharedPtr<SUniformGridPanel> ButtonPanel = SNew(SUniformGridPanel)
.SlotPadding(FMargin(2.0f));
ButtonPanel->AddSlot(0, 0)
[
SNew(SButton)
.Text(FText::FromString("Reset to Identity"))
.ToolTipText(FText::FromString("Reset the matrix to identity"))
.OnClicked(FOnClicked::CreateLambda([this]()
{
ResetToIdentity();
return FReply::Handled();
}))
];
ButtonPanel->AddSlot(1, 0)
[
SNew(SButton)
.Text(FText::FromString("Set from Rotation"))
.ToolTipText(FText::FromString("Set the matrix from rotation values (degrees)"))
.OnClicked(FOnClicked::CreateLambda([this]()
{
SetFromEulerAngles(0.0f, 0.0f, 0.0f);
return FReply::Handled();
}))
];
ButtonPanel->AddSlot(2, 0)
[
SNew(SButton)
.Text(FText::FromString("Set from Translation"))
.ToolTipText(FText::FromString("Set the matrix from translation values"))
.OnClicked(FOnClicked::CreateLambda([this]()
{
SetFromTranslation(FVector::ZeroVector);
return FReply::Handled();
}))
];
// Add preset cutting buttons
ButtonPanel->AddSlot(0, 1)
[
SNew(SButton)
.Text(FText::FromString("Horizontal Cut"))
.ToolTipText(FText::FromString("Set up a horizontal cutting plane"))
.OnClicked(FOnClicked::CreateLambda([this]()
{
// Set horizontal cutting plane
FMatrix HorizontalCutMatrix = FMatrix::Identity;
HorizontalCutMatrix.SetOrigin(FVector(0.0f, 0.0f, 0.0f));
SetMatrix(HorizontalCutMatrix);
return FReply::Handled();
}))
];
ButtonPanel->AddSlot(1, 1)
[
SNew(SButton)
.Text(FText::FromString("Vertical Cut"))
.ToolTipText(FText::FromString("Set up a vertical cutting plane"))
.OnClicked(FOnClicked::CreateLambda([this]()
{
// Set vertical cutting plane
FMatrix VerticalCutMatrix = FMatrix::Identity;
VerticalCutMatrix.SetOrigin(FVector(0.0f, 0.0f, 0.0f));
// Rotate 90 degrees to make it vertical
FRotator Rotator(0.0f, 0.0f, 90.0f);
FMatrix RotationMatrix = FRotationMatrix::Make(Rotator);
SetMatrix(VerticalCutMatrix * RotationMatrix);
return FReply::Handled();
}))
];
ButtonPanel->AddSlot(2, 1)
[
SNew(SButton)
.Text(FText::FromString("Diagonal Cut"))
.ToolTipText(FText::FromString("Set up a diagonal cutting plane"))
.OnClicked(FOnClicked::CreateLambda([this]()
{
// Set diagonal cutting plane
FMatrix DiagonalCutMatrix = FMatrix::Identity;
DiagonalCutMatrix.SetOrigin(FVector(0.0f, 0.0f, 0.0f));
// Rotate 45 degrees
FRotator Rotator(0.0f, 0.0f, 45.0f);
FMatrix RotationMatrix = FRotationMatrix::Make(Rotator);
SetMatrix(DiagonalCutMatrix * RotationMatrix);
return FReply::Handled();
}))
];
// Add apply cut button
TSharedPtr<SButton> ApplyCutButton = SNew(SButton)
.Text(FText::FromString("Apply Cut"))
.ButtonStyle(FAppStyle::Get(), "FlatButton.Success")
.ContentPadding(FMargin(8.0f, 4.0f))
.HAlign(HAlign_Center)
.ToolTipText(FText::FromString("Apply the current cutting plane to the model"))
.OnClicked(FOnClicked::CreateLambda([this]()
{
// Apply cutting
if (OnMatrixChanged.IsBound())
{
OnMatrixChanged.Execute(Matrix);
}
return FReply::Handled();
}));
// Create rotation controls
TSharedPtr<SVerticalBox> RotationControls = SNew(SVerticalBox);
// Add X-axis rotation
RotationControls->AddSlot()
.AutoHeight()
.Padding(0.0f, 4.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.0f, 0.0f, 4.0f, 0.0f)
[
SNew(STextBlock)
.Text(FText::FromString("X Rotation:"))
.MinDesiredWidth(80.0f)
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(SSpinBox<float>)
.MinValue(-180.0f)
.MaxValue(180.0f)
.Delta(1.0f)
.Value(0.0f)
.OnValueChanged_Lambda([this](float NewValue)
{
// Apply X-axis rotation
FRotator Rotator(NewValue, 0.0f, 0.0f);
FMatrix RotationMatrix = FRotationMatrix::Make(Rotator);
FMatrix NewMatrix = Matrix * RotationMatrix;
SetMatrix(NewMatrix);
})
]
];
// Add Y-axis rotation
RotationControls->AddSlot()
.AutoHeight()
.Padding(0.0f, 4.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.0f, 0.0f, 4.0f, 0.0f)
[
SNew(STextBlock)
.Text(FText::FromString("Y Rotation:"))
.MinDesiredWidth(80.0f)
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(SSpinBox<float>)
.MinValue(-180.0f)
.MaxValue(180.0f)
.Delta(1.0f)
.Value(0.0f)
.OnValueChanged_Lambda([this](float NewValue)
{
// Apply Y-axis rotation
FRotator Rotator(0.0f, NewValue, 0.0f);
FMatrix RotationMatrix = FRotationMatrix::Make(Rotator);
FMatrix NewMatrix = Matrix * RotationMatrix;
SetMatrix(NewMatrix);
})
]
];
// Add Z-axis rotation
RotationControls->AddSlot()
.AutoHeight()
.Padding(0.0f, 4.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.0f, 0.0f, 4.0f, 0.0f)
[
SNew(STextBlock)
.Text(FText::FromString("Z Rotation:"))
.MinDesiredWidth(80.0f)
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(SSpinBox<float>)
.MinValue(-180.0f)
.MaxValue(180.0f)
.Delta(1.0f)
.Value(0.0f)
.OnValueChanged_Lambda([this](float NewValue)
{
// Apply Z-axis rotation
FRotator Rotator(0.0f, 0.0f, NewValue);
FMatrix RotationMatrix = FRotationMatrix::Make(Rotator);
FMatrix NewMatrix = Matrix * RotationMatrix;
SetMatrix(NewMatrix);
})
]
];
// Create translation controls
TSharedPtr<SVerticalBox> TranslationControls = SNew(SVerticalBox);
// Add X-axis translation
TranslationControls->AddSlot()
.AutoHeight()
.Padding(0.0f, 4.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.0f, 0.0f, 4.0f, 0.0f)
[
SNew(STextBlock)
.Text(FText::FromString("X Position:"))
.MinDesiredWidth(80.0f)
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(SSpinBox<float>)
.MinValue(-1000.0f)
.MaxValue(1000.0f)
.Delta(1.0f)
.Value(0.0f)
.OnValueChanged_Lambda([this](float NewValue)
{
// Apply X-axis translation
FVector Translation = Matrix.GetOrigin();
Translation.X = NewValue;
FMatrix NewMatrix = Matrix;
NewMatrix.SetOrigin(Translation);
SetMatrix(NewMatrix);
})
]
];
// Add Y-axis translation
TranslationControls->AddSlot()
.AutoHeight()
.Padding(0.0f, 4.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.0f, 0.0f, 4.0f, 0.0f)
[
SNew(STextBlock)
.Text(FText::FromString("Y Position:"))
.MinDesiredWidth(80.0f)
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(SSpinBox<float>)
.MinValue(-1000.0f)
.MaxValue(1000.0f)
.Delta(1.0f)
.Value(0.0f)
.OnValueChanged_Lambda([this](float NewValue)
{
// Apply Y-axis translation
FVector Translation = Matrix.GetOrigin();
Translation.Y = NewValue;
FMatrix NewMatrix = Matrix;
NewMatrix.SetOrigin(Translation);
SetMatrix(NewMatrix);
})
]
];
// Add Z-axis translation
TranslationControls->AddSlot()
.AutoHeight()
.Padding(0.0f, 4.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.0f, 0.0f, 4.0f, 0.0f)
[
SNew(STextBlock)
.Text(FText::FromString("Z Position:"))
.MinDesiredWidth(80.0f)
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(SSpinBox<float>)
.MinValue(-1000.0f)
.MaxValue(1000.0f)
.Delta(1.0f)
.Value(0.0f)
.OnValueChanged_Lambda([this](float NewValue)
{
// Apply Z-axis translation
FVector Translation = Matrix.GetOrigin();
Translation.Z = NewValue;
FMatrix NewMatrix = Matrix;
NewMatrix.SetOrigin(Translation);
SetMatrix(NewMatrix);
})
]
];
// Create main layout
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 0.0f, 0.0f, 8.0f)
[
SNew(STextBlock)
.Text(FText::FromString("Cutting Plane Transformation Matrix"))
.Font(FAppStyle::GetFontStyle("HeadingFont"))
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(4.0f)
[
GridPanel.ToSharedRef()
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 8.0f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(4.0f)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(STextBlock)
.Text(FText::FromString("Rotation Controls"))
.Font(FAppStyle::GetFontStyle("NormalFontBold"))
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 4.0f)
[
RotationControls.ToSharedRef()
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 8.0f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(4.0f)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(STextBlock)
.Text(FText::FromString("Translation Controls"))
.Font(FAppStyle::GetFontStyle("NormalFontBold"))
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 4.0f)
[
TranslationControls.ToSharedRef()
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 8.0f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(4.0f)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(STextBlock)
.Text(FText::FromString("Presets"))
.Font(FAppStyle::GetFontStyle("NormalFontBold"))
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 4.0f)
[
ButtonPanel.ToSharedRef()
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 8.0f)
.HAlign(HAlign_Center)
[
ApplyCutButton.ToSharedRef()
]
];
// Initialize the UI
UpdateUI();
}
void SMatrixInputWidget::SetMatrix(const FMatrix& InMatrix)
{
Matrix = InMatrix;
UpdateUI();
// Notify listeners of the change
if (OnMatrixChanged.IsBound())
{
OnMatrixChanged.Execute(Matrix);
}
}
FMatrix SMatrixInputWidget::GetMatrix() const
{
return Matrix;
}
void SMatrixInputWidget::ResetToIdentity()
{
SetMatrix(FMatrix::Identity);
}
void SMatrixInputWidget::SetFromEulerAngles(float Roll, float Pitch, float Yaw)
{
FRotator Rotator(Pitch, Yaw, Roll);
FMatrix RotationMatrix = FRotationMatrix::Make(Rotator);
SetMatrix(RotationMatrix);
}
void SMatrixInputWidget::SetFromTranslation(const FVector& Translation)
{
FMatrix TranslationMatrix = FMatrix::Identity;
TranslationMatrix.SetOrigin(Translation);
SetMatrix(TranslationMatrix);
}
void SMatrixInputWidget::SetFromTransform(const FTransform& Transform)
{
SetMatrix(Transform.ToMatrixWithScale());
}
TSharedPtr<SNumericEntryBox<float>> SMatrixInputWidget::CreateMatrixElementWidget(int32 Row, int32 Col)
{
return SNew(SNumericEntryBox<float>)
.Value_Lambda([this, Row, Col]() -> float
{
return Matrix.M[Row][Col];
})
.OnValueChanged(SNumericEntryBox<float>::FOnValueChanged::CreateSP(this, &SMatrixInputWidget::OnMatrixElementChanged, Row, Col))
.AllowSpin(true)
.Delta(0.1f)
.MinValue(-10000.0f)
.MaxValue(10000.0f)
.MinSliderValue(-10.0f)
.MaxSliderValue(10.0f);
}
void SMatrixInputWidget::OnMatrixElementChanged(float NewValue, int32 Row, int32 Col)
{
// Update the matrix element
Matrix.M[Row][Col] = NewValue;
// Notify listeners of the change
if (OnMatrixChanged.IsBound())
{
OnMatrixChanged.Execute(Matrix);
}
}
void SMatrixInputWidget::UpdateUI()
{
// Update each numeric entry box with the current matrix values
for (int32 Row = 0; Row < Rows; ++Row)
{
for (int32 Col = 0; Col < Columns; ++Col)
{
int32 Index = Row * Columns + Col;
if (NumericEntryBoxes.IsValidIndex(Index) && NumericEntryBoxes[Index].IsValid())
{
// In UE5.5.4, we need to rebuild the grid panel to update the value
NumericEntryBoxes[Index] = CreateMatrixElementWidget(Row, Col);
}
}
}
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION