summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bezier.cpp3
-rw-r--r--src/createcalls.cpp59
-rw-r--r--src/imgui_helper_internal.cpp16
-rw-r--r--src/imgui_ui.cpp20
-rw-r--r--src/imgui_ui_properties.cpp2
-rw-r--r--src/imgui_ui_viewport.cpp556
-rw-r--r--src/include/all.h6
-rw-r--r--src/include/imgui_internal_widgets.h3
-rw-r--r--src/include/keybinds.h3
-rw-r--r--src/include/main.h21
-rw-r--r--src/include/memory.h2
-rw-r--r--src/layer.cpp12
-rw-r--r--src/main.cpp26
-rw-r--r--src/nanovg.cpp4
-rw-r--r--src/prenderer.cpp33
15 files changed, 683 insertions, 83 deletions
diff --git a/src/bezier.cpp b/src/bezier.cpp
index 5916c42..dd3974d 100644
--- a/src/bezier.cpp
+++ b/src/bezier.cpp
@@ -44,9 +44,8 @@ Bezier_SolveYForX(v2 Point_P0, v2 Point_P1, v2 Point_P2, v2 Point_P3, real32 Tar
static uint32
Bezier_Shape_Sort(memory *Memory, shape_layer *Shape, bezier_point *PointData,
project_state *State, layer_transforms T, int Width, int Height,
- int CompWidth, int CompHeight, bool32 Interact)
+ int CompWidth, int CompHeight, real32 Radius, bool32 Interact)
{
- real32 Radius = Shape->Opt.Roundness;
bezier_point *PointStart = PointData;
for (int i = 0; i < Shape->Point_Count; i++) {
bezier_point Point = *Bezier_LookupAddress(Memory, Shape->Block_Bezier_Index, i, 1);
diff --git a/src/createcalls.cpp b/src/createcalls.cpp
index 800dd49..adb6ff5 100644
--- a/src/createcalls.cpp
+++ b/src/createcalls.cpp
@@ -450,6 +450,46 @@ void Clipboard_Store(project_data *File, project_state *State, memory *Memory, s
}
}
+void Slide_Init(project_data *File, project_state *State, memory *Memory)
+{
+ // It's impossible to use the brush while this is active, so for now we can
+ // just use its buffer to store our data:
+ uint8 *InteractBuffer = (uint8 *)State->Brush.TransientBitmap;
+ State->Interact_Count = 0;
+ int h = 0, c = 0, i = 0;
+ while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (Layer->IsSelected) {
+ interact_slide_layer *Interact_Layer = (interact_slide_layer *)InteractBuffer;
+ *Interact_Layer = {0};
+ Interact_Layer->Index = i;
+ InteractBuffer += sizeof(interact_slide_layer);
+ State->Interact_Count++;
+ }
+ }
+}
+
+void Slide_Test(project_data *File, project_state *State, memory *Memory, sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray)
+{
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ v2 CompDimensions = V2(MainComp->Width, MainComp->Height);
+ real32 Threshold = 5;
+ uint8 *InteractBuffer = (uint8 *)State->Brush.TransientBitmap;
+ for (int i = 0; i < State->Interact_Count; i++) {
+ interact_slide_layer *Interact_Layer = (interact_slide_layer *)InteractBuffer;
+ InteractBuffer += sizeof(interact_slide_layer);
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Interact_Layer->Index);
+ v2 CompUV = V2(State->Interact_Offset[0], State->Interact_Offset[1]);
+ v2 CompPos = CompUV * CompDimensions;
+ v2 LayerPos = Layer_TraverseForPoint(File, State, Memory, CompUV, SortedCompArray, SortedLayerArray);
+ int Width, Height;
+ Layer_GetDimensions(Memory, Layer, &Width, &Height);
+ if (abs(CompPos.x - LayerPos.x) > Threshold &&
+ abs(CompPos.y - LayerPos.y) > Threshold)
+ Assert(0);
+ }
+}
+
void Property_MinMax_X(memory *Memory, project_state *State, property_channel *Property,
uint16 *ArrayLocation, real32 *Min, real32 *Max)
{
@@ -687,10 +727,25 @@ Project_ShapeLayer_New(project_data *File, project_state *State, memory *Memory)
Layer_Select(Memory, State, Memory_Block_LazyIndexAtAddress(Memory, F_Layers, Layer));
shape_layer *Shape = &File->UI.Shape;
+
+ if (State->Interact_Modifier == 1) {
+ v2 *CompPoint = (v2 *)&State->Interact_Offset[0];
+ for (int i = 0; i < 4; i++) {
+ bezier_point PointData = { 1, { CompPoint[i], V2(0, 0), V2(0, 0) }, interpolation_type_linear, 0 };
+ Bezier_Add(Memory, F_File, Shape->Block_Bezier_Index, &Shape->Block_Bezier_Count, &Shape->Point_Count, PointData);
+ CompPoint[i] = {0};
+ }
+ Shape->IsClosed = true;
+ State->Interact_Modifier = 0;
+ }
+
+ shape_options ShapeOpt = File->UI.ShapeOpt;
+ ShapeOpt.FillCol = File->UI.Color;
+ ShapeOpt.StrokeCol = File->UI.AltColor;
v2 Min = {}, Max = {};
void *Data = Memory_PushScratch(Memory, sizeof(nvg_point) * 128);
layer_transforms T = {};
- NVG_FlattenPath(Memory, Shape, (nvg_point *)Data, State, T, 0, 0, 0, 0, 0, &Min, &Max);
+ NVG_FlattenPath(Memory, Shape, ShapeOpt, (nvg_point *)Data, State, T, 0, 0, 0, 0, 0, &Min, &Max);
Memory_PopScratch(Memory, sizeof(nvg_point) * 128);
Shape->Width = Max.x - Min.x;
Shape->Height = Max.y - Min.y;
@@ -726,6 +781,8 @@ Project_ShapeLayer_New(project_data *File, project_state *State, memory *Memory)
History_Action_Swap(Memory, F_Layers, sizeof(Layer->Shape), &Layer->Shape);
Layer->Shape = File->UI.Shape;
+ History_Action_Swap(Memory, F_Layers, sizeof(Layer->ShapeOpt), &Layer->ShapeOpt);
+ Layer->ShapeOpt = ShapeOpt;
History_Action_Swap(Memory, F_File, sizeof(File->UI.Shape), &File->UI.Shape);
File->UI.Shape = {};
diff --git a/src/imgui_helper_internal.cpp b/src/imgui_helper_internal.cpp
index 7d6944b..da94586 100644
--- a/src/imgui_helper_internal.cpp
+++ b/src/imgui_helper_internal.cpp
@@ -11,7 +11,7 @@
#include "imgui_internal.h"
// NOTE(fox): This API will change in the future!
-void ImGui::MyWindowSetup(ImGuiID id)
+void ImGui::MyWindowSetup(ImGuiID *Placement, ImGuiID id)
{
ImGuiViewport *Viewport = GetMainViewport();
ImVec2 WindowMin = Viewport->WorkPos;
@@ -22,24 +22,30 @@ void ImGui::MyWindowSetup(ImGuiID id)
DockBuilderSetNodeSize(id, WindowSize);
DockBuilderSetNodePos(id, WindowMin);
+ ImGuiID *DockRight = Placement;
ImGuiID DockTop = ImGui::DockBuilderSplitNode(id, ImGuiDir_Down, 1.f, nullptr, &id);
ImGuiID DockBottom = ImGui::DockBuilderSplitNode(DockTop, ImGuiDir_Down, 0.4f, nullptr, &DockTop);
ImGuiID DockLeft = ImGui::DockBuilderSplitNode(DockTop, ImGuiDir_Left, 0.15f, nullptr, &DockTop);
- ImGuiID DockRight = ImGui::DockBuilderSplitNode(DockTop, ImGuiDir_Right, 0.2f, nullptr, &DockTop);
- ImGuiID DockRightBottom = ImGui::DockBuilderSplitNode(DockRight, ImGuiDir_Down, 0.2f, nullptr, &DockRight);
- ImGuiID DockRightTop = ImGui::DockBuilderSplitNode(DockRight, ImGuiDir_Up, 0.6f, nullptr, &DockRight);
+ *DockRight = ImGui::DockBuilderSplitNode(DockTop, ImGuiDir_Right, 0.2f, nullptr, &DockTop);
+ ImGuiID DockRightBottom = ImGui::DockBuilderSplitNode(*DockRight, ImGuiDir_Down, 0.2f, nullptr, DockRight);
+ ImGuiID DockRightTop = ImGui::DockBuilderSplitNode(*DockRight, ImGuiDir_Up, 0.6f, nullptr, DockRight);
ImGui::DockBuilderDockWindow("Menu", id);
ImGui::DockBuilderDockWindow("Viewport", DockTop);
ImGui::DockBuilderDockWindow("Timeline", DockBottom);
ImGui::DockBuilderDockWindow("Properties###Properties", DockLeft);
ImGui::DockBuilderDockWindow("Colors", DockRightTop);
- ImGui::DockBuilderDockWindow("Files", DockRight);
+ ImGui::DockBuilderDockWindow("Files", *DockRight);
ImGui::DockBuilderDockWindow("Effects list", DockRightBottom);
ImGui::DockBuilderFinish(id);
}
+void ImGui::MyDockWindow(char *Window, ImGuiID Dock)
+{
+ ImGui::DockBuilderDockWindow(Window, Dock);
+}
+
// A modded version of ScalarSlider allowing for the minimum and maximum parts
// of the slider to be draggable by two other buttons. p_mid is from range -1
// to 1, and s_min and max are from 0-1.
diff --git a/src/imgui_ui.cpp b/src/imgui_ui.cpp
index 380aaf2..bfb79f3 100644
--- a/src/imgui_ui.cpp
+++ b/src/imgui_ui.cpp
@@ -387,6 +387,26 @@ ImGui_ProcessInputs(project_data *File, project_state *State, ui *UI, memory *Me
State->Interact_Modifier = 1;
else
State->Interact_Modifier = 0;
+ } else if (io.KeyShift) {
+ bool32 CommitAction = 0;
+ int h = 0, c = 0, i = 0;
+ while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (Layer->IsSelected && Layer->IsShapeLayer) {
+ if (!CommitAction) {
+ History_Entry_Commit(Memory, "Swap shape colors");
+ CommitAction = 1;
+ }
+ v4 Temp = Layer->ShapeOpt.FillCol;
+ History_Action_Swap(Memory, F_Layers, sizeof(Layer->ShapeOpt.FillCol), &Layer->ShapeOpt.FillCol);
+ Layer->ShapeOpt.FillCol = Layer->ShapeOpt.StrokeCol;
+ History_Action_Swap(Memory, F_Layers, sizeof(Layer->ShapeOpt.StrokeCol), &Layer->ShapeOpt.StrokeCol);
+ Layer->ShapeOpt.StrokeCol = Temp;
+ }
+ }
+ if (CommitAction) {
+ History_Entry_End(Memory);
+ }
} else {
v4 Temp = UI->Color;
UI->Color = UI->AltColor;
diff --git a/src/imgui_ui_properties.cpp b/src/imgui_ui_properties.cpp
index 341045e..c427971 100644
--- a/src/imgui_ui_properties.cpp
+++ b/src/imgui_ui_properties.cpp
@@ -442,7 +442,7 @@ ImGui_PropertiesPanel(project_data *File, project_state *State, ui *UI, memory *
}
if (Layer->IsShapeLayer) {
shape_layer *Shape = &Layer->Shape;
- shape_options *Opt = &Layer->Shape.Opt;
+ shape_options *Opt = &Layer->ShapeOpt;
// TODO(fox): Combine with RGBA function?
ImGui::DragScalar("Shape width", ImGuiDataType_Float, &Shape->Width);
if (ImGui::IsItemActive()) {
diff --git a/src/imgui_ui_viewport.cpp b/src/imgui_ui_viewport.cpp
index f3e124e..2012354 100644
--- a/src/imgui_ui_viewport.cpp
+++ b/src/imgui_ui_viewport.cpp
@@ -1,3 +1,4 @@
+#include "imgui.h"
#if SPECIAL
#include "main.h"
#endif
@@ -221,6 +222,456 @@ ImGui_Viewport_BrushUI(project_state *State, memory *Memory, ImVec2 ViewportMin,
}
}
+#if 0
+static void
+ImGui_Viewport_TransformUI2(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list,
+ v2 Min, v2 Max, ImVec2 ViewportMin, uint32 CompWidth, uint32 CompHeight, uint16 *SortedKeyframeArray)
+{
+ interact_transform *Interact = (interact_transform *)&State->Interact_Offset[0];
+
+ v2 InteractMin = Min;
+ v2 InteractMax = Max;
+ real32 Rad = 0;
+ real32 Scale = 1;
+
+
+ if (State->Interact_Active == interact_type_viewport_transform_gizmo) {
+ InteractMin = Interact->Min + Interact->Position;
+ InteractMax = Interact->Max + Interact->Position;
+ Rad = Interact->Radians;
+ Scale = Interact->Scale;
+ }
+
+ v2 BoxLength = InteractMax - InteractMin;
+ v2 Center = InteractMax - (BoxLength/2);
+
+ real32 Point0X = Center.x - InteractMin.x;
+ real32 Point0Y = Center.y - InteractMin.y;
+
+ v2 XAxis = (Point0X * Scale)*V2(cos(Rad), sin(Rad));
+ v2 YAxis = (Point0Y * -Scale)*V2(sin(Rad), -cos(Rad));
+
+ // Points are clockwise starting from the top left.
+ real32 X0 = -XAxis.x - YAxis.x + Center.x;
+ real32 Y0 = -XAxis.y - YAxis.y + Center.y;
+ real32 X1 = X0 + XAxis.x*2;
+ real32 Y1 = Y0 + XAxis.y*2;
+ real32 X2 = X1 + YAxis.x*2;
+ real32 Y2 = Y1 + YAxis.y*2;
+ real32 X3 = X2 - XAxis.x*2;
+ real32 Y3 = Y2 - XAxis.y*2;
+
+ // Midway points.
+ real32 Mid_X0 = X0 + XAxis.x;
+ real32 Mid_Y0 = Y0 + XAxis.y;
+ real32 Mid_X1 = X1 + YAxis.x;
+ real32 Mid_Y1 = Y1 + YAxis.y;
+ real32 Mid_X2 = X2 - XAxis.x;
+ real32 Mid_Y2 = Y2 - XAxis.y;
+ real32 Mid_X3 = X3 - YAxis.x;
+ real32 Mid_Y3 = Y3 - YAxis.y;
+
+ ImVec2 CompScale = UI->CompZoom / ImVec2(CompWidth, CompHeight);
+
+ ImVec2 P[4];
+ P[0] = ImVec2(X0, Y0)*CompScale + UI->CompPos;
+ P[1] = ImVec2(X1, Y1)*CompScale + UI->CompPos;
+ P[2] = ImVec2(X2, Y2)*CompScale + UI->CompPos;
+ P[3] = ImVec2(X3, Y3)*CompScale + UI->CompPos;
+
+ ImVec2 Mid_P[4];
+ Mid_P[0] = ImVec2(Mid_X0, Mid_Y0)*CompScale + UI->CompPos;
+ Mid_P[1] = ImVec2(Mid_X1, Mid_Y1)*CompScale + UI->CompPos;
+ Mid_P[2] = ImVec2(Mid_X2, Mid_Y2)*CompScale + UI->CompPos;
+ Mid_P[3] = ImVec2(Mid_X3, Mid_Y3)*CompScale + UI->CompPos;
+
+ if (State->Interact_Active == interact_type_viewport_transform_gizmo) {
+ ImU32 wcol = ImGui::GetColorU32(ImGuiCol_Text);
+ draw_list->AddLine(P[0], P[1], wcol, 2.0f);
+ draw_list->AddLine(P[1], P[2], wcol, 2.0f);
+ draw_list->AddLine(P[2], P[3], wcol, 2.0f);
+ draw_list->AddLine(P[3], P[0], wcol, 2.0f);
+ }
+
+ v2 XAxis2 = (BoxLength*CompScale.x)*V2(cos(Rad), sin(Rad));
+ v2 YAxis2 = (BoxLength*CompScale.y)*V2(sin(Rad), -cos(Rad));
+
+ v2 XAxisPerp = (1.0f / LengthSq(XAxis))*XAxis;
+ v2 YAxisPerp = (1.0f / LengthSq(YAxis))*YAxis;
+
+ // real32 LocalX = ((io.MousePos.x - UI->CompPos.x) - Center.x) ;
+ // real32 LocalY = ((io.MousePos.y - UI->CompPos.y) - Center.y) ;
+ layer_transforms BoxTransforms = { Center.x, Center.y, 0.5, 0.5, (real32)(Rad / (PI / 180)), Scale };
+ v2 LayerPoint = Transform_ScreenSpaceToLocal(BoxTransforms, CompWidth, CompHeight, BoxLength.x, BoxLength.y, UI->CompPos, UI->CompZoom, ViewportMin, io.MousePos);
+
+ real32 U = LayerPoint.x / BoxLength.x;
+ real32 V = LayerPoint.y / BoxLength.y;
+
+ bool32 OtherActions = ImGui::IsKeyDown(ImGuiKey_Z);
+ real32 FontSize = ImGui::GetFontSize() * 3;
+ real32 Delta = io.MouseDelta.x + io.MouseDelta.y;
+
+ ImVec2 Screen_Center = IV2(Center)*CompScale + UI->CompPos;
+
+ char *Buttons[] = { "##position", "##rotation", "##scale" };
+ for (int i = 0; i < 3; i++) {
+ ImGui::SetCursorScreenPos(Screen_Center + ImVec2(i*FontSize, i*FontSize));
+ ImGui::Button(Buttons[i], ImVec2(FontSize, FontSize));
+ bool32 IsActivated = ImGui::IsItemActivated();
+ bool32 IsActive = ImGui::IsItemActive();
+ bool32 IsDeactivated = ImGui::IsItemDeactivated();
+ if (IsActivated) {
+ State->Interact_Active = interact_type_viewport_transform_gizmo;
+ *Interact = {};
+ Interact->Min = Min;
+ Interact->Max = Max;
+ }
+ if (IsActive && Delta != 0.0f) {
+ v2 Increase = V2(io.MousePos - io.MouseClickedPos[0]);
+ if (i == 0) {
+ Interact->Position = Increase;
+ } else if (i == 1) {
+ real32 LocalX = (io.MousePos.x - UI->CompPos.x)/CompScale.x - InteractMin.x - (BoxLength.x/2);
+ real32 LocalY = (io.MousePos.y - UI->CompPos.y)/CompScale.y - InteractMin.y - (BoxLength.y/2);
+ v2 SlopeDot = V2(BoxLength.x, BoxLength.y);
+
+ real32 Slope_Mouse = LocalY/LocalX;
+
+ real32 Slope_Corner = BoxLength.y / BoxLength.x;
+ real32 Slope_Flipped = -BoxLength.x / BoxLength.y;
+ real32 Dot = LocalX * SlopeDot.x + LocalY * SlopeDot.y;
+
+ Interact->Radians = atan((Slope_Mouse - Slope_Corner) / (1 + Slope_Mouse * Slope_Corner));
+ real32 ExtraRadians2 = atan((Slope_Mouse - Slope_Flipped) / (1 + Slope_Mouse * Slope_Flipped));
+
+ if (Dot < 0) {
+ if (Interact->Radians < 0) {
+ Interact->Radians = (90 * (PI / 180)) + ExtraRadians2;
+ } else {
+ Interact->Radians = (-90 * (PI / 180)) + ExtraRadians2;
+ }
+ }
+ } else if (i == 2) {
+ real32 StartX = (io.MouseClickedPos[0].x - UI->CompPos.x)/CompScale.x - InteractMin.x - (BoxLength.x/2);
+ real32 StartY = (io.MouseClickedPos[0].y - UI->CompPos.y)/CompScale.y - InteractMin.y - (BoxLength.y/2);
+ real32 LocalX = (io.MousePos.x - UI->CompPos.x)/CompScale.x - InteractMin.x - (BoxLength.x/2);
+ real32 LocalY = (io.MousePos.y - UI->CompPos.y)/CompScale.y - InteractMin.y - (BoxLength.y/2);
+ real32 Normalized = LocalX / StartX;
+ if (Normalized < 0.0f)
+ Normalized = 0.0f;
+ Interact->Scale = Normalized;
+ // printf("Local X: %.2f\n", Normalized);
+ } else {
+ Assert(0);
+ }
+ }
+ if (IsDeactivated) {
+ Assert(State->Interact_Active == interact_type_viewport_transform_gizmo);
+ int h = 0, c = 0, i = 0;
+ History_Entry_Commit(Memory, "Transform layers");
+ while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (Layer->IsSelected == 1) {
+ History_Action_Swap(Memory, F_Layers, sizeof(Layer->x.CurrentValue), &Layer->x.CurrentValue);
+ History_Action_Swap(Memory, F_Layers, sizeof(Layer->y.CurrentValue), &Layer->y.CurrentValue);
+ History_Action_Swap(Memory, F_Layers, sizeof(Layer->scale.CurrentValue), &Layer->scale.CurrentValue);
+ History_Action_Swap(Memory, F_Layers, sizeof(Layer->rotation.CurrentValue), &Layer->rotation.CurrentValue);
+ Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &Layer->x.CurrentValue, &Layer->y.CurrentValue, &Layer->rotation.CurrentValue, &Layer->scale.CurrentValue);
+ }
+ }
+ History_Entry_End(Memory);
+ State->Interact_Active = interact_type_none;
+ *Interact = {};
+ State->UpdateFrame = true;
+ }
+ }
+}
+#endif
+
+static void
+ImGui_Viewport_TransformUI2(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list,
+ v2 Min, v2 Max, ImVec2 ViewportMin, uint32 CompWidth, uint32 CompHeight, uint16 *SortedKeyframeArray)
+{
+ interact_transform *Interact = (interact_transform *)&State->Interact_Offset[0];
+
+ v2 InteractMin = Min;
+ v2 InteractMax = Max;
+ real32 Rad = 0;
+ real32 Scale = 1;
+
+ if (Interact->Min.x != 0.0f) {
+ InteractMin = Interact->Min + Interact->Position;
+ InteractMax = Interact->Max + Interact->Position;
+ Rad = Interact->Radians;
+ Scale = Interact->Scale;
+ }
+
+ DebugWatchVar("Rad", &Interact->Radians, d_float);
+
+ v2 BoxLength = InteractMax - InteractMin;
+ v2 Center = InteractMax - (BoxLength/2);
+
+ real32 Point0X = Center.x - InteractMin.x;
+ real32 Point0Y = Center.y - InteractMin.y;
+
+ v2 XAxis = (Point0X * Scale)*V2(cos(Rad), sin(Rad));
+ v2 YAxis = (Point0Y * -Scale)*V2(sin(Rad), -cos(Rad));
+
+ // Points are clockwise starting from the top left.
+ real32 X0 = -XAxis.x - YAxis.x + Center.x;
+ real32 Y0 = -XAxis.y - YAxis.y + Center.y;
+ real32 X1 = X0 + XAxis.x*2;
+ real32 Y1 = Y0 + XAxis.y*2;
+ real32 X2 = X1 + YAxis.x*2;
+ real32 Y2 = Y1 + YAxis.y*2;
+ real32 X3 = X2 - XAxis.x*2;
+ real32 Y3 = Y2 - XAxis.y*2;
+
+ // Midway points.
+ real32 Mid_X0 = X0 + XAxis.x;
+ real32 Mid_Y0 = Y0 + XAxis.y;
+ real32 Mid_X1 = X1 + YAxis.x;
+ real32 Mid_Y1 = Y1 + YAxis.y;
+ real32 Mid_X2 = X2 - XAxis.x;
+ real32 Mid_Y2 = Y2 - XAxis.y;
+ real32 Mid_X3 = X3 - YAxis.x;
+ real32 Mid_Y3 = Y3 - YAxis.y;
+
+ ImVec2 CompScale = UI->CompZoom / ImVec2(CompWidth, CompHeight);
+
+ ImVec2 P[4];
+ P[0] = ImVec2(X0, Y0)*CompScale + UI->CompPos;
+ P[1] = ImVec2(X1, Y1)*CompScale + UI->CompPos;
+ P[2] = ImVec2(X2, Y2)*CompScale + UI->CompPos;
+ P[3] = ImVec2(X3, Y3)*CompScale + UI->CompPos;
+
+ ImVec2 Mid_P[4];
+ Mid_P[0] = ImVec2(Mid_X0, Mid_Y0)*CompScale + UI->CompPos;
+ Mid_P[1] = ImVec2(Mid_X1, Mid_Y1)*CompScale + UI->CompPos;
+ Mid_P[2] = ImVec2(Mid_X2, Mid_Y2)*CompScale + UI->CompPos;
+ Mid_P[3] = ImVec2(Mid_X3, Mid_Y3)*CompScale + UI->CompPos;
+
+ ImU32 wcol = ImGui::GetColorU32(ImGuiCol_Text);
+ draw_list->AddLine(P[0], P[1], wcol, 2.0f);
+ draw_list->AddLine(P[1], P[2], wcol, 2.0f);
+ draw_list->AddLine(P[2], P[3], wcol, 2.0f);
+ draw_list->AddLine(P[3], P[0], wcol, 2.0f);
+
+ v2 XAxis2 = (BoxLength*CompScale.x)*V2(cos(Rad), sin(Rad));
+ v2 YAxis2 = (BoxLength*CompScale.y)*V2(sin(Rad), -cos(Rad));
+
+ v2 XAxisPerp = (1.0f / LengthSq(XAxis))*XAxis;
+ v2 YAxisPerp = (1.0f / LengthSq(YAxis))*YAxis;
+
+ // real32 LocalX = ((io.MousePos.x - UI->CompPos.x) - Center.x) ;
+ // real32 LocalY = ((io.MousePos.y - UI->CompPos.y) - Center.y) ;
+ layer_transforms BoxTransforms = { Center.x, Center.y, 0.5, 0.5, (real32)(Rad / (PI / 180)), Scale };
+ v2 LayerPoint = Transform_ScreenSpaceToLocal(BoxTransforms, CompWidth, CompHeight, BoxLength.x, BoxLength.y, UI->CompPos, UI->CompZoom, ViewportMin, io.MousePos);
+
+ real32 U = LayerPoint.x / BoxLength.x;
+ real32 V = LayerPoint.y / BoxLength.y;
+
+ ImVec2 ScaleHandleSize(50, 50);
+
+ bool32 OtherActions = ImGui::IsKeyDown(ImGuiKey_Z);
+
+ /*
+ // First do the halfway scale points, since they don't need UVs considered:
+ for (int i = 0; i < 4; i++) {
+ ImGui::SetCursorScreenPos(Mid_P[i] - ScaleHandleSize/2);
+ ImGui::PushID(i);
+
+ ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertFloat4ToU32(ImVec4(0.6f, 0.0f, 0.3f, 1.0f)));
+ ImGui::Button("##ScaleMids", ScaleHandleSize);
+ ImGui::PopStyleColor();
+
+ if (ImGui::IsItemActivated() && !OtherActions) {
+ State->InteractTransformMode = 1;
+ }
+
+ if (State->InteractTransformMode == 1 && ImGui::IsItemActive())
+ {
+ uint32 side = i;
+ if (side == 0) {
+ Interact->Scale -= io.MouseDelta.y / BoxLength.y;
+ Interact->Position.y += io.MouseDelta.y / 2;
+ } else if (side == 1) {
+ Interact->Scale += io.MouseDelta.x / BoxLength.x;
+ Interact->Position.x += io.MouseDelta.x / 2;
+ } else if (side == 2) {
+ Interact->Scale += io.MouseDelta.y / BoxLength.y;
+ Interact->Position.y += io.MouseDelta.y / 2;
+ } else if (side == 3) {
+ Interact->Scale -= io.MouseDelta.x / BoxLength.x;
+ Interact->Position.x += io.MouseDelta.x / 2;
+ }
+ }
+ ImGui::PopID();
+ }
+ */
+
+ bool32 InBounds = false;
+ // Scale if cursor is on button within the UV, rotate if outside UV, and position if a non-button is dragged.
+ if (U >= 0.0f && U <= 1.0f && V >= 0.0f && V <= 1.0f)
+ {
+ ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertFloat4ToU32(ImVec4(0.6f, 0.0f, 0.3f, 1.0f)));
+ InBounds = true;
+ }
+
+ for (int i = 0; i < 4; i++) {
+ ImGui::SetCursorScreenPos(P[i] - ScaleHandleSize/2);
+ ImGui::PushID(i);
+ ImGui::Button("##ScaleRotateCorners", ScaleHandleSize);
+
+ if (ImGui::IsItemActivated() && !OtherActions) {
+ if (InBounds)
+ State->InteractTransformMode = 1;
+ else
+ State->InteractTransformMode = 2;
+ State->Interact_Active = interact_type_viewport_transform_gizmo;
+ if (Interact->Max.x == 0) {
+ *Interact = {};
+ Interact->Min = Min;
+ Interact->Max = Max;
+ }
+ }
+
+ // Scale part
+ if (State->InteractTransformMode == 1 && ImGui::IsItemActive())
+ {
+ // TODO(fox): Corner dragging scale only works in the X
+ // axis. Mostly feels right when dragged how you expect,
+ // but I'll fix it if someone complains.
+ uint32 side = i;
+ if (side == 0) {
+ Interact->Scale -= io.MouseDelta.x / BoxLength.x;
+ Interact->Position.x += io.MouseDelta.x / 2;
+ Interact->Position.y += io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2;
+ } else if (side == 1) {
+ Interact->Scale += io.MouseDelta.x / BoxLength.x;
+ Interact->Position.x += io.MouseDelta.x / 2;
+ Interact->Position.y -= io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2;
+ } else if (side == 2) {
+ Interact->Scale += io.MouseDelta.x / BoxLength.x;
+ Interact->Position.x += io.MouseDelta.x / 2;
+ Interact->Position.y += io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2;
+ } else if (side == 3) {
+ Interact->Scale -= io.MouseDelta.x / BoxLength.x;
+ Interact->Position.x += io.MouseDelta.x / 2;
+ Interact->Position.y -= io.MouseDelta.x*(BoxLength.y/BoxLength.x) / 2;
+ }
+ }
+
+ // Rotation part
+ if (State->InteractTransformMode == 2 && ImGui::IsItemActive())
+ {
+ real32 LocalX = (io.MousePos.x - UI->CompPos.x)/CompScale.x - InteractMin.x - (BoxLength.x/2);
+ real32 LocalY = (io.MousePos.y - UI->CompPos.y)/CompScale.y - InteractMin.y - (BoxLength.y/2);
+
+ real32 Slope_Mouse = LocalY/LocalX;
+ real32 Slope_Corner = 0;
+ real32 Slope_Flipped = 0;
+ real32 Dot = 0;
+
+ // TODO(fox) learn basic geometry to do this properly
+
+ // We find the angle between the direction of whichever corner the
+ // mouse is grabbing (Slope_Corner) and the mouse's current
+ // position (Slope_Mouse) to get ExtraRadians. The calculation only
+ // works between -90 and 90, so I take the dot product of the
+ // opposite edge of the corner and add the extra degrees when it's negative.
+
+ v2 SlopeDot = V2(BoxLength.x, BoxLength.y);
+ // top left clockwise
+ uint32 side = i;
+ if (side == 0) {
+ Slope_Corner = BoxLength.y / BoxLength.x;
+ Slope_Flipped = -BoxLength.x / BoxLength.y;
+ Dot = LocalX * -SlopeDot.x + LocalY * -SlopeDot.y;
+ } else if (side == 1) {
+ Slope_Corner = -BoxLength.y / BoxLength.x;
+ Slope_Flipped = BoxLength.x / BoxLength.y;
+ Dot = LocalX * SlopeDot.x + LocalY * -SlopeDot.y;
+ } else if (side == 2) {
+ Slope_Corner = BoxLength.y / BoxLength.x;
+ Slope_Flipped = -BoxLength.x / BoxLength.y;
+ Dot = LocalX * SlopeDot.x + LocalY * SlopeDot.y;
+ } else if (side == 3) {
+ Slope_Corner = -BoxLength.y / BoxLength.x;
+ Slope_Flipped = BoxLength.x / BoxLength.y;
+ Dot = LocalX * -SlopeDot.x + LocalY * SlopeDot.y;
+ }
+
+ Interact->Radians = atan((Slope_Mouse - Slope_Corner) / (1 + Slope_Mouse * Slope_Corner));
+ real32 ExtraRadians2 = atan((Slope_Mouse - Slope_Flipped) / (1 + Slope_Mouse * Slope_Flipped));
+
+ if (Dot < 0) {
+ if (Interact->Radians < 0) {
+ Interact->Radians = (90 * (PI / 180)) + ExtraRadians2;
+ } else {
+ Interact->Radians = (-90 * (PI / 180)) + ExtraRadians2;
+ }
+ }
+ }
+
+ ImGui::PopID();
+ }
+
+ if (InBounds && !io.KeyAlt) {
+ ImGui::SetCursorScreenPos(io.MousePos - ScaleHandleSize/2);
+ ImGui::Button("##mover", ScaleHandleSize);
+ if (!State->InteractTransformMode && ImGui::IsItemActivated() && !OtherActions) {
+ State->Interact_Active = interact_type_viewport_transform_gizmo;
+ if (Interact->Max.x == 0) {
+ *Interact = {};
+ Interact->Min = Min;
+ Interact->Max = Max;
+ }
+ State->InteractTransformMode = 3;
+ }
+ }
+
+ if (State->InteractTransformMode == 3) {
+ Interact->Position.x += (real32)io.MouseDelta.x/CompScale.x;
+ Interact->Position.y += (real32)io.MouseDelta.y/CompScale.y;
+ }
+
+ if (State->InteractTransformMode)
+ {
+ if (io.MouseDelta.x || io.MouseDelta.y)
+ State->UpdateFrame = true;
+ if (!ImGui::IsMouseDown(ImGuiMouseButton_Left))
+ {
+ Assert(State->Interact_Active == interact_type_viewport_transform_gizmo);
+ int h = 0, c = 0, i = 0;
+ History_Entry_Commit(Memory, "Transform layers");
+ while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) {
+ block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
+ if (Layer->IsSelected == 1) {
+ History_Action_Swap(Memory, F_Layers, sizeof(Layer->x.CurrentValue), &Layer->x.CurrentValue);
+ History_Action_Swap(Memory, F_Layers, sizeof(Layer->y.CurrentValue), &Layer->y.CurrentValue);
+ History_Action_Swap(Memory, F_Layers, sizeof(Layer->scale.CurrentValue), &Layer->scale.CurrentValue);
+ History_Action_Swap(Memory, F_Layers, sizeof(Layer->rotation.CurrentValue), &Layer->rotation.CurrentValue);
+ Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &Layer->x.CurrentValue, &Layer->y.CurrentValue, &Layer->rotation.CurrentValue, &Layer->scale.CurrentValue);
+ }
+ }
+ History_Entry_End(Memory);
+ State->Interact_Active = interact_type_none;
+ // *Interact = {};
+ State->UpdateFrame = true;
+ State->InteractTransformMode = 0;
+ State->Interact_Modifier = 0;
+ State->UncommitedKeyframe = 1;
+ State->UpdateFrame = true;
+ }
+ }
+
+ if (InBounds == true) {
+ ImGui::PopStyleColor();
+ }
+
+}
+
static void
ImGui_Viewport_TransformUI(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list,
interact_transform *Interact, ImVec2 ViewportMin, uint32 CompWidth, uint32 CompHeight, uint16 *SortedKeyframeArray)
@@ -457,26 +908,27 @@ ImGui_Viewport_TransformUI(project_data *File, project_state *State, memory *Mem
// Second condition so you don't have to reach for Enter.
if (ImGui::IsKeyPressed(ImGuiKey_Enter) || (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && io.KeyCtrl)) {
int h = 0, c = 0, i = 0;
- if (!io.KeyCtrl)
- History_Entry_Commit(Memory, "Transform layers");
+ // if (!io.KeyCtrl)
+ History_Entry_Commit(Memory, "Transform layers");
while (Block_Loop(Memory, F_Layers, File->Layer_Count, &h, &c, &i)) {
block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, i);
if (Layer->IsSelected == 1) {
- if (io.KeyCtrl) {
- layer_transforms T = Layer_GetTransforms(Layer);
- Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale);
- property_channel *Property[4] = { &Layer->x, &Layer->y, &Layer->rotation, &Layer->scale };
- real32 Val[4] = { T.x, T.y, T.rotation, T.scale };
- for (int a = 0; a < 4; a++) {
- if (Property[a]->CurrentValue != Val[a]) {
- History_Entry_Commit(Memory, "Add keyframe");
- bezier_point Point = { 1, {(real32)State->Frame_Current, Val[a], -1, 0, 1, 0}, interpolation_type_linear, 0, {0, 0, 0}, 0 };
- uint16 *ArrayLocation = Property_GetSortedArray(SortedKeyframeArray, State->MostRecentlySelectedLayer, h);
- Bezier_Add(Memory, F_Layers, Property[a], Point, ArrayLocation);
- History_Entry_End(Memory);
- }
- }
- } else {
+ // if (io.KeyCtrl) {
+ // layer_transforms T = Layer_GetTransforms(Layer);
+ // Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale);
+ // property_channel *Property[4] = { &Layer->x, &Layer->y, &Layer->rotation, &Layer->scale };
+ // real32 Val[4] = { T.x, T.y, T.rotation, T.scale };
+ // for (int a = 0; a < 4; a++) {
+ // if (Property[a]->CurrentValue != Val[a]) {
+ // History_Entry_Commit(Memory, "Add keyframe");
+ // bezier_point Point = { 1, {(real32)State->Frame_Current, Val[a], -1, 0, 1, 0}, interpolation_type_linear, 0, {0, 0, 0}, 0 };
+ // uint16 *ArrayLocation = Property_GetSortedArray(SortedKeyframeArray, State->MostRecentlySelectedLayer, h);
+ // Bezier_Add(Memory, F_Layers, Property[a], Point, ArrayLocation);
+ // History_Entry_End(Memory);
+ // }
+ // }
+ // } else {
+ {
History_Action_Swap(Memory, F_Layers, sizeof(Layer->x.CurrentValue), &Layer->x.CurrentValue);
History_Action_Swap(Memory, F_Layers, sizeof(Layer->y.CurrentValue), &Layer->y.CurrentValue);
History_Action_Swap(Memory, F_Layers, sizeof(Layer->scale.CurrentValue), &Layer->scale.CurrentValue);
@@ -485,8 +937,8 @@ ImGui_Viewport_TransformUI(project_data *File, project_state *State, memory *Mem
}
}
}
- if (!io.KeyCtrl)
- History_Entry_End(Memory);
+ // if (!io.KeyCtrl)
+ History_Entry_End(Memory);
State->Interact_Active = interact_type_none;
State->Interact_Modifier = 0;
State->UncommitedKeyframe = 1;
@@ -500,12 +952,11 @@ ImGui_Viewport_TransformUI(project_data *File, project_state *State, memory *Mem
}
static void
-ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list, block_composition *MainComp, uint32 CompIndex, block_layer *ParentLayer[4], uint32 Recursions,
+ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list, block_composition *MainComp, uint32 CompIndex, block_layer *ParentLayer[4], uint32 Recursions, v2 *Min, v2 *Max,
sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray)
{
sorted_comp_array *SortedCompStart = &SortedCompArray[CompIndex];
sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex);
- ImU32 wcol = ImGui::GetColorU32(ImGuiCol_Text);
int LayerCount = SortedCompStart->LayerCount + SortedCompStart->FakeLayerCount;
for (int i = 0; i < LayerCount; i++)
{
@@ -514,7 +965,7 @@ ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImG
block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical);
if (Layer->IsPrecomp) {
ParentLayer[Recursions] = Layer;
- ImGui_Viewport_SelectedLayerUI(State, Memory, UI, io, draw_list, MainComp, Layer->Block_Source_Index, ParentLayer, Recursions + 1, SortedCompArray, SortedLayerArray);
+ ImGui_Viewport_SelectedLayerUI(State, Memory, UI, io, draw_list, MainComp, Layer->Block_Source_Index, ParentLayer, Recursions + 1, Min, Max, SortedCompArray, SortedLayerArray);
}
if (Layer->IsSelected) {
uint32 Width = 0, Height = 0;
@@ -546,7 +997,8 @@ ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImG
layer_transforms T = Layer_GetTransforms(Layer);
- if (State->Interact_Active == interact_type_viewport_transform && Layer->IsSelected == 1) {
+ if ((State->Interact_Active == interact_type_viewport_transform ||
+ State->Interact_Active == interact_type_viewport_transform_gizmo) && Layer->IsSelected == 1) {
Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale);
}
if (State->Interact_Active == interact_type_viewport_duplicate && SortEntry.IsFake) {
@@ -604,6 +1056,14 @@ ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImG
i++;
}
+ for (int i = 0; i < 4; i++) {
+ v2 Pos = NewPos[i+1];
+ if (Pos.x < Min->x) { Min->x = Pos.x; }
+ if (Pos.y < Min->y) { Min->y = Pos.y; }
+ if (Pos.x > Max->x) { Max->x = Pos.x; }
+ if (Pos.y > Max->y) { Max->y = Pos.y; }
+ }
+
ImVec2 ScreenPoint[5];
for (int i = 0; i < 5; i++) {
v2 CompUV = NewPos[i] / V2(MainComp->Width, MainComp->Height);
@@ -612,15 +1072,18 @@ ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImG
UI->CompPos.y + CompUV.y * UI->CompZoom.y);
}
- if (State->Tool != tool_brush) {
- ImU32 wcol2 = IM_COL32(10, 10, 10, 128);
- draw_list->AddNgon(ScreenPoint[0], 10, wcol2, 8, 9.0f);
- draw_list->AddNgon(ScreenPoint[0], 10, wcol, 8, 5.0f);
+ ImU32 wcol = IM_COL32(200, 200, 255, 255);
+ if (State->Interact_Active != interact_type_viewport_transform_gizmo) {
+ if (State->Tool != tool_brush) {
+ ImU32 wcol2 = IM_COL32(10, 10, 10, 128);
+ draw_list->AddNgon(ScreenPoint[0], 10, wcol2, 8, 9.0f);
+ draw_list->AddNgon(ScreenPoint[0], 10, wcol, 8, 5.0f);
+ }
+ draw_list->AddLine(ScreenPoint[1], ScreenPoint[2], wcol, 1.0f);
+ draw_list->AddLine(ScreenPoint[2], ScreenPoint[4], wcol, 1.0f);
+ draw_list->AddLine(ScreenPoint[1], ScreenPoint[3], wcol, 1.0f);
+ draw_list->AddLine(ScreenPoint[3], ScreenPoint[4], wcol, 1.0f);
}
- draw_list->AddLine(ScreenPoint[1], ScreenPoint[2], wcol, 2.0f);
- draw_list->AddLine(ScreenPoint[2], ScreenPoint[4], wcol, 2.0f);
- draw_list->AddLine(ScreenPoint[1], ScreenPoint[3], wcol, 2.0f);
- draw_list->AddLine(ScreenPoint[3], ScreenPoint[4], wcol, 2.0f);
}
}
}
@@ -693,9 +1156,13 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory,
if (State->MostRecentlySelectedLayer > -1)
{
block_layer *ParentLayer[4];
- ImGui_Viewport_SelectedLayerUI(State, Memory, UI, io, draw_list, MainComp, File->PrincipalCompIndex, ParentLayer, 0, SortedCompArray, SortedLayerArray);
+ // NOTE(fox): This min and max val is affected by interactive transforms!
+ v2 Min = V2(10000, 10000), Max = V2(-10000, -10000);
+ ImGui_Viewport_SelectedLayerUI(State, Memory, UI, io, draw_list, MainComp, File->PrincipalCompIndex, ParentLayer, 0, &Min, &Max, SortedCompArray, SortedLayerArray);
if (State->Interact_Active == interact_type_viewport_transform) {
ImGui_Viewport_TransformUI(File, State, Memory, UI, io, draw_list, (interact_transform *)&State->Interact_Offset[0], ViewportMin, MainComp->Width, MainComp->Height, SortedKeyframeArray);
+ } else {
+ ImGui_Viewport_TransformUI2(File, State, Memory, UI, io, draw_list, Min, Max, ViewportMin, MainComp->Width, MainComp->Height, SortedKeyframeArray);
}
}
@@ -784,7 +1251,9 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory,
}
}
- if (IsHovered && IsDeactivated && !io.KeyCtrl && !io.KeyAlt && !ImGui::IsMouseDown(ImGuiMouseButton_Right && !ImGui::IsKeyDown(ImGuiKey_Z)))
+ if (((io.KeyAlt && IsActivated && State->MostRecentlySelectedLayer == -1) ||
+ (IsHovered && IsDeactivated && !io.KeyCtrl && !io.KeyAlt)) &&
+ !ImGui::IsMouseDown(ImGuiMouseButton_Right) && !ImGui::IsKeyDown(ImGuiKey_Z))
{
// Layer selection
if (State->Tool == tool_default && State->Interact_Active == interact_type_none) {
@@ -883,6 +1352,7 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory,
real32 Delta = io.MouseDelta.x + io.MouseDelta.y;
if (Delta && IsActive) {
State->Interact_Active = interact_type_viewport_slide;
+ Slide_Init(File, State, Memory);
}
}
@@ -891,10 +1361,13 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory,
Assert(0);
} else {
Assert(IsActive);
+ v2 PrevMouseCompPos = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePosPrev);
v2 MouseCompPos = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos);
- v2 DragDelta = MouseCompPos - State->LastClickedPoint;
- State->Interact_Offset[0] = DragDelta.x * MainComp->Width;
- State->Interact_Offset[1] = DragDelta.y * MainComp->Height;
+ State->Interact_Offset[0] = MouseCompPos.x;
+ State->Interact_Offset[1] = MouseCompPos.y;
+ State->Interact_Offset[3] = PrevMouseCompPos.x;
+ State->Interact_Offset[4] = PrevMouseCompPos.y;
+ Slide_Test(File, State, Memory, SortedCompArray, SortedLayerArray);
}
}
@@ -913,16 +1386,13 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory,
}
if (Shape->Point_Count == 0 && IsDeactivated && !OtherActions && !ImGui::IsMouseReleased(ImGuiMouseButton_Right)) {
ImVec2 ScreenPoint[4] = { ScreenPointStart, ImVec2(ScreenPointStart.x, MousePos.y), MousePos, ImVec2(MousePos.x, ScreenPointStart.y) };
- History_Entry_Commit(Memory, "Add rectangle");
+ v2 *CompPoint = (v2 *)&State->Interact_Offset[0];
for (int i = 0; i < 4; i++) {
- v2 CompPoint = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, ScreenPoint[i]);
- CompPoint = CompPoint * V2(MainComp->Width, MainComp->Height);
- bezier_point PointData = { 1, { CompPoint, V2(0, 0), V2(0, 0) }, interpolation_type_linear, 0 };
- Bezier_Add(Memory, F_File, Shape->Block_Bezier_Index, &Shape->Block_Bezier_Count, &Shape->Point_Count, PointData);
+ CompPoint[i] = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, ScreenPoint[i]);
+ CompPoint[i] = CompPoint[i] * V2(MainComp->Width, MainComp->Height);
}
- Shape->IsClosed = true;
- History_Entry_End(Memory);
State->HotkeyInput = hotkey_newlayer_shape;
+ State->Interact_Modifier = 1;
}
}
diff --git a/src/include/all.h b/src/include/all.h
index a0368d8..9d7fe77 100644
--- a/src/include/all.h
+++ b/src/include/all.h
@@ -2,7 +2,7 @@
static uint32
Bezier_Shape_Sort(memory *Memory, shape_layer *Shape, bezier_point *PointData,
project_state *State, layer_transforms T, int Width, int Height,
- int CompWidth, int CompHeight, bool32 Interact);
+ int CompWidth, int CompHeight, real32 Radius, bool32 Interact);
static bezier_point *
Bezier_LookupAddress(memory *Memory, uint16 *Block_Bezier_Index, uint16 Index, bool32 AssertExists = 1);
@@ -317,7 +317,7 @@ ImGui_Viewport_TransformUI(project_data *File, project_state *State, memory *Mem
interact_transform *Interact, ImVec2 ViewportMin, uint32 CompWidth, uint32 CompHeight, uint16 *SortedKeyframeArray);
static void
-ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list, block_composition *MainComp, uint32 CompIndex, block_layer *ParentLayer[4], uint32 Recursions,
+ImGui_Viewport_SelectedLayerUI(project_state *State, memory *Memory, ui *UI, ImGuiIO &io, ImDrawList *draw_list, block_composition *MainComp, uint32 CompIndex, block_layer *ParentLayer[4], uint32 Recursions, v2 *Min, v2 *Max,
sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray);
static void
@@ -460,7 +460,7 @@ static void
Data_Decompress(memory *Memory, void *CompressedLocation, uint64 CompressedSize, void *BitmapLocation, uint64 ExpectedSize);
static uint32
-NVG_FlattenPath(memory *Memory, shape_layer *Shape, nvg_point *PointData,
+NVG_FlattenPath(memory *Memory, shape_layer *Shape, shape_options ShapeOpt, nvg_point *PointData,
project_state *State, layer_transforms T, int Width, int Height,
int CompWidth, int CompHeight, bool32 Interact, v2 *Min, v2 *Max);
diff --git a/src/include/imgui_internal_widgets.h b/src/include/imgui_internal_widgets.h
index 863e934..875d21c 100644
--- a/src/include/imgui_internal_widgets.h
+++ b/src/include/imgui_internal_widgets.h
@@ -6,7 +6,8 @@
// NOTE(fox): Appending to the standard ImGui namespace so I don't have to convert all the functions to ImGui::Function()
namespace ImGui {
- IMGUI_API void MyWindowSetup(ImGuiID id);
+ IMGUI_API void MyWindowSetup(ImGuiID *Placement, ImGuiID id);
+ IMGUI_API void MyDockWindow(char *Window, ImGuiID Dock);
IMGUI_API bool SliderLevels(const char* label, const char* label2, const char* label3, void* p_data, void* p_min, void* p_max);
IMGUI_API bool TestLine(ImVec2 P1, ImVec2 P2);
IMGUI_API bool BezierInteractive(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3);
diff --git a/src/include/keybinds.h b/src/include/keybinds.h
index 7ea0499..ddacb49 100644
--- a/src/include/keybinds.h
+++ b/src/include/keybinds.h
@@ -45,7 +45,10 @@ static shortcut_entry ShortcutArray[] {
{ ImGuiKey_None, Mod_None, key_mode_viewport, "Hold right click to pan." },
{ ImGuiKey_None, Mod_None, key_mode_viewport, "Hold Z and drag left click to zoom." },
{ ImGuiKey_None, Mod_None, key_mode_viewport, "Press Enter or ctrl+click to commit a transform." },
+ { ImGuiKey_None, Mod_None, key_mode_viewport, "Hold alt while dragging a layer to duplicate it." },
{ ImGuiKey_T, Mod_None, key_mode_viewport, "Transform selected layers" },
+ { ImGuiKey_D, Mod_None, key_mode_viewport, "Stepped duplicate" },
+ { ImGuiKey_X, Mod_Shift, key_mode_viewport, "Swap stroke/fill colors of selected shape layers" },
{ ImGuiKey_Tab, Mod_None, key_mode_timeline, "Switch between timeline and graph" },
{ ImGuiKey_2, Mod_None, key_mode_timeline, "Toggle precomp view" },
diff --git a/src/include/main.h b/src/include/main.h
index 3129c5d..de33a25 100644
--- a/src/include/main.h
+++ b/src/include/main.h
@@ -248,10 +248,10 @@ struct sorted_file
struct shape_options {
int Visibility;
v4 FillCol = {1, 1, 1, 1};
- v4 StrokeCol = {1, 0, 0, 1};
- float StrokeWidth = 10;
- nvg_line_cap LineJoinType = NVG_ROUND;
- nvg_line_cap LineCapType = NVG_ROUND;
+ v4 StrokeCol = {0, 0, 0, 1};
+ float StrokeWidth = 0;
+ nvg_line_cap LineJoinType = NVG_SQUARE;
+ nvg_line_cap LineCapType = NVG_MITER;
real32 Roundness;
};
@@ -265,7 +265,6 @@ struct shape_layer {
// They get set once the shape becomes a shape layer!
real32 Width;
real32 Height;
- shape_options Opt;
};
enum timeline_mode
@@ -301,6 +300,7 @@ enum interact_type
interact_type_layer_timeadjust,
interact_type_viewport_selection,
interact_type_viewport_transform,
+ interact_type_viewport_transform_gizmo,
interact_type_viewport_duplicate,
interact_type_viewport_slide,
interact_type_keyframe_move,
@@ -341,6 +341,12 @@ struct interact_transform
uint32 TransformMode;
};
+struct interact_slide_layer
+{
+ uint16 Index;
+ real32 Offset[4];
+};
+
enum hotkey_input
{
hotkey_none,
@@ -483,6 +489,9 @@ struct project_state
real32 Interact_Offset[12];
real32 Interact_Dup_Previous[2];
void *Interact_Address;
+ int Interact_Count;
+
+ ImGuiID RightDock;
// NOTE(fox): We need to keep track of when the user changes the CurrentValue of a
// channel that has keyframes on it (i.e. CurrentValue will now evaluate to
@@ -547,6 +556,7 @@ struct ui
#endif
shape_layer Shape;
+ shape_options ShapeOpt;
ImU32 LayerColors[16] = {
0xff8b1f1f,
@@ -675,6 +685,7 @@ struct block_layer {
uint16 Block_Effect_Count;
shape_layer Shape;
+ shape_options ShapeOpt;
blend_mode BlendMode;
diff --git a/src/include/memory.h b/src/include/memory.h
index e6b7676..ae3fd61 100644
--- a/src/include/memory.h
+++ b/src/include/memory.h
@@ -2,7 +2,6 @@ enum memory_table_list {
P_AVInfo,
P_UndoBuffer,
- P_MiscCache,
F_File,
F_Precomps,
@@ -17,7 +16,6 @@ enum memory_table_list {
B_Thumbnails,
B_PointData,
B_ScratchSpace,
- B_CacheEntries,
B_CachedBitmaps,
M_Count
diff --git a/src/layer.cpp b/src/layer.cpp
index d21ab0e..c158466 100644
--- a/src/layer.cpp
+++ b/src/layer.cpp
@@ -112,14 +112,15 @@ Layer_UpdateMasksEffects(project_state *State, block_layer *Layer, memory *Memor
if (Layer->IsShapeLayer) {
shape_layer *Shape = &Layer->Shape;
+ shape_options ShapeOpt = Layer->ShapeOpt;
void *Data = Memory_PushScratch(Memory, sizeof(nvg_point) * 128);
Arbitrary_Zero((uint8 *)Data, sizeof(nvg_point) * 128);
layer_transforms T = Layer_GetTransforms(Layer);
v2 Min = {}, Max = {};
- uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, (nvg_point *)Data,
+ uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, ShapeOpt, (nvg_point *)Data,
State, T, Shape->Width, Shape->Height, Width, Height, 1, &Min, &Max);
void *Data_Stroke = Memory_PushScratch(Memory, sizeof(real32) * 4 * 256);
- uint32 StrokeCount = NVG_ExpandStroke(Memory, NumberOfVerts, Shape->Opt.StrokeWidth, Shape->Opt.LineCapType, Shape->Opt.LineJoinType, Shape->IsClosed, (nvg_point *)Data, (real32 *)Data_Stroke);
+ uint32 StrokeCount = NVG_ExpandStroke(Memory, NumberOfVerts, ShapeOpt.StrokeWidth, ShapeOpt.LineCapType, ShapeOpt.LineJoinType, Shape->IsClosed, (nvg_point *)Data, (real32 *)Data_Stroke);
void *Data_Fill = Memory_PushScratch(Memory, sizeof(real32) * 4 * NumberOfVerts);
NVG_ExpandFill(Memory, NumberOfVerts, (nvg_point *)Data, (real32 *)Data_Fill);
@@ -569,11 +570,12 @@ Layer_TestBoxSelect(memory *Memory, project_state *State, ui *UI, sorted_comp_ar
nvg_point *NVGPointData = (nvg_point *)Memory_PushScratch(Memory, sizeof(nvg_point) * 1024);
v2 ShapeMin = {}, ShapeMax = {};
- uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, NVGPointData,
+ uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, Layer->ShapeOpt, NVGPointData,
State, T, Shape->Width, Shape->Height, Comp->Width, Comp->Height, 1, &ShapeMin, &ShapeMax);
v2 Box[4] = { MinPos, V2(MinPos.x, MaxPos.y), MaxPos, V2(MaxPos.x, MinPos.y) };
- if (Shape_TestBoxSelect(T, Comp->Width, Comp->Height, Shape->Height, Shape->Width, Box, NVGPointData, NumberOfVerts))
- Layer->IsSelected = true;
+ if (Shape_TestBoxSelect(T, Comp->Width, Comp->Height, Shape->Height, Shape->Width, Box, NVGPointData, NumberOfVerts)) {
+ Layer_Select(Memory, State, i);
+ }
Memory_PopScratch(Memory, sizeof(nvg_point) * 1024);
} else {
diff --git a/src/main.cpp b/src/main.cpp
index 82f0153..3f8388e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,4 +1,5 @@
#include "main.h"
+#include "imgui_internal_widgets.h"
SDL_Thread *Thread[8];
SDL_sem *Semaphore;
@@ -98,8 +99,9 @@ Main_InputTest(project_data *File, project_state *State, memory *Memory, sorted_
ImGuiID DockID = ImGui::DockSpaceOverViewport(ImGui::GetMainViewport(), ImGuiDockNodeFlags_AutoHideTabBar);
if (State->FirstFrame) {
- ImGui::MyWindowSetup(DockID);
- }
+ ImGui::MyWindowSetup(&State->RightDock, DockID);
+ }
+ ImGui::MyDockWindow("Timeline", State->RightDock);
if (State->Warp_WantSetPos) {
ImGui_WarpMouseFinish(State, io.MousePos);
@@ -413,12 +415,16 @@ Render_UI(project_data *File, project_state *State, memory *Memory, ui *UI, ImDr
Frame_End_Abs > Frame_Current && Layer->IsVisible)
{
shape_layer *Shape = &Layer->Shape;
+ shape_options ShapeOpt = Layer->ShapeOpt;
void *Data = StartAddress;
layer_transforms T = Layer_GetTransforms(Layer);
if (State->Interact_Active == interact_type_viewport_transform && Layer->IsSelected == 1) {
Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale);
}
+ if (State->Interact_Active == interact_type_viewport_transform_gizmo && Layer->IsSelected == 1) {
+ Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale);
+ }
if (State->Interact_Active == interact_type_viewport_slide && Layer->IsSelected == 1) {
// Transform_ApplySlide((v2 *)&State->Interact_Offset[0], &T);
}
@@ -429,11 +435,11 @@ Render_UI(project_data *File, project_state *State, memory *Memory, ui *UI, ImDr
}
v2 Min = {}, Max = {};
- uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, (nvg_point *)Data,
+ uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, ShapeOpt, (nvg_point *)Data,
State, T, Shape->Width, Shape->Height, Comp->Width, Comp->Height, 1, &Min, &Max);
StartAddress += NumberOfVerts * sizeof(nvg_point);
void *Data_Stroke = StartAddress;
- uint32 StrokeCount = NVG_ExpandStroke(Memory, NumberOfVerts, Shape->Opt.StrokeWidth, Shape->Opt.LineCapType, Shape->Opt.LineJoinType, Shape->IsClosed, (nvg_point *)Data, (real32 *)Data_Stroke);
+ uint32 StrokeCount = NVG_ExpandStroke(Memory, NumberOfVerts, ShapeOpt.StrokeWidth, ShapeOpt.LineCapType, ShapeOpt.LineJoinType, Shape->IsClosed, (nvg_point *)Data, (real32 *)Data_Stroke);
StartAddress += StrokeCount * sizeof(real32) * 4;
void *Data_Fill = StartAddress;
NVG_ExpandFill(Memory, NumberOfVerts, (nvg_point *)Data, (real32 *)Data_Fill);
@@ -447,9 +453,9 @@ Render_UI(project_data *File, project_state *State, memory *Memory, ui *UI, ImDr
RenderData->LayerCount++;
StartAddress += sizeof(gl_data);
- int Visibility = (Shape->Opt.StrokeWidth > 0.0f) ? Shape->Opt.Visibility : 1;
- *GL_Data = { Data_Stroke, StrokeCount, Shape->Opt.StrokeCol,
- Data_Fill, NumberOfVerts, Shape->Opt.FillCol,
+ int Visibility = (ShapeOpt.StrokeWidth > 0.0f) ? ShapeOpt.Visibility : 1;
+ *GL_Data = { Data_Stroke, StrokeCount, ShapeOpt.StrokeCol,
+ Data_Fill, NumberOfVerts, ShapeOpt.FillCol,
T, Shape->Width, Shape->Height, Visibility };
}
}
@@ -724,7 +730,6 @@ int main(int argc, char *argv[]) {
Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, P_AVInfo, "FFmpeg state", sizeof(av_info));
Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, P_UndoBuffer, "Undo buffer");
- Memory_InitTable(&GlobalMemory, &Memory, 40 * 1024 * 1024, P_MiscCache, "Misc persistent");
Memory_InitTable(&GlobalMemory, &Memory, sizeof(project_data), F_File, "File", sizeof(project_data));
Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, F_Precomps, "Precomps", sizeof(block_composition));
@@ -739,7 +744,6 @@ int main(int argc, char *argv[]) {
Memory_InitTable(&GlobalMemory, &Memory, (uint64)5 * 1024 * 1024, B_Thumbnails, "Thumbnails");
Memory_InitTable(&GlobalMemory, &Memory, (uint64)5 * 1024 * 1024, B_PointData, "Point data");
Memory_InitTable(&GlobalMemory, &Memory, (uint64)128 * 1024 * 1024, B_ScratchSpace, "Scratch");
- // Memory_InitTable(&GlobalMemory, &Memory, (uint64)1 * 1024 * 1024, B_CacheEntries, "Cache entries", sizeof(cache_entry));
Memory_InitTable(&GlobalMemory, &Memory, (uint64)700 * 1024 * 1024, B_CachedBitmaps, "Cached bitmap buffer");
uint8 *Test = (uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.Slot[B_ScratchSpace].Size + 2;
@@ -791,7 +795,7 @@ int main(int argc, char *argv[]) {
block_composition *MainComp = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps);
MainComp->Width = 1280;
- MainComp->Height = 720;
+ MainComp->Height = 1280;
MainComp->FPS = 60;
MainComp->BytesPerPixel = 4;
MainComp->Frame_Count = 80;
@@ -1157,6 +1161,8 @@ int main(int argc, char *argv[]) {
File_Sort_Pop(&Memory, Sorted.Layer_SortSize, Sorted.Property_SortSize, Sorted.Source_SortSize);
+ // NOTE(fox): We could theoretically allow scratch to persist between
+ // frames, but we'd have to make sure the pop order stays the same in all scenarios.
Assert(Debug.ScratchState == 0);
if (State->IsPlaying && State->HotFramePerf > 1 && FullyCached) {
diff --git a/src/nanovg.cpp b/src/nanovg.cpp
index b7b426f..f1d1702 100644
--- a/src/nanovg.cpp
+++ b/src/nanovg.cpp
@@ -177,7 +177,7 @@ static real32 * NVG_ButtCap(nvg_point *Point, real32 *StrokeData,
// NOTE(fox): We only have to care about winding if we want to do HW accelerated
// shape subtraction with the stencil buffer (I think).
static uint32
-NVG_FlattenPath(memory *Memory, shape_layer *Shape, nvg_point *PointData,
+NVG_FlattenPath(memory *Memory, shape_layer *Shape, shape_options ShapeOpt, nvg_point *PointData,
project_state *State, layer_transforms T, int Width, int Height,
int CompWidth, int CompHeight, bool32 Interact, v2 *Min, v2 *Max)
{
@@ -185,7 +185,7 @@ NVG_FlattenPath(memory *Memory, shape_layer *Shape, nvg_point *PointData,
bezier_point *BezierPointData = (bezier_point *)Memory_PushScratch(Memory, sizeof(bezier_point) * 128);
uint32 BezierCount = Bezier_Shape_Sort(Memory, Shape, BezierPointData,
State, T, Width, Height,
- CompWidth, CompHeight, Interact);
+ CompWidth, CompHeight, ShapeOpt.Roundness, Interact);
for (int i = 0; i < BezierCount; i++) {
bezier_point *Point = &BezierPointData[i];
if (i == 0 || Point->Type == interpolation_type_linear) {
diff --git a/src/prenderer.cpp b/src/prenderer.cpp
index 08cdadf..1cc490c 100644
--- a/src/prenderer.cpp
+++ b/src/prenderer.cpp
@@ -83,7 +83,8 @@ Transform_ApplyInteractive(interact_transform Interact, real32 *OutputX, real32
*OutputX = X0 + Interact.Position.x;
*OutputY = Y0 + Interact.Position.y;
*OutputRotation += Rotation;
- *OutputScale += Interact.Scale - 1.0f;
+ // *OutputScale += Interact.Scale - 1.0f;
+ *OutputScale *= Interact.Scale;
}
static void
@@ -222,6 +223,30 @@ Interact_Transform_Begin(project_data *File, memory *Memory, project_state *Stat
*/
}
+static void
+Interact_Transform2_Begin(project_data *File, memory *Memory, project_state *State, ImVec2 OGPos,
+ sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray)
+{
+ real32 MinX = 100000;
+ real32 MinY = 100000;
+ real32 MaxX = -100000;
+ real32 MaxY = -100000;
+ block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex);
+ block_layer *ParentLayer[4];
+ Transform_Recurse(State, Memory, MainComp, File->PrincipalCompIndex, ParentLayer, 0,
+ SortedCompArray, SortedLayerArray,
+ &MinX, &MinY, &MaxX, &MaxY);
+ if (MinX != 100000) {
+ State->Interact_Active = interact_type_viewport_transform;
+ interact_transform *Interact = (interact_transform *)&State->Interact_Offset[0];
+ Interact->Min = V2(MinX, MinY);
+ Interact->Max = V2(MaxX, MaxY);
+ Interact->Position = V2(0);
+ Interact->Radians = 0;
+ Interact->Scale = 1.0f;
+ Interact->OGPos = OGPos;
+ }
+}
static v2
TransformPoint(layer_transforms T, real32 Width, real32 Height, v2 Point)
@@ -263,7 +288,8 @@ Layer_LocalToScreenSpace(project_state *State, memory *Memory, block_layer *Laye
layer_transforms T = Layer_GetTransforms(Layer);
- if (State->Interact_Active == interact_type_viewport_transform && Layer->IsSelected == 1) {
+ if ((State->Interact_Active == interact_type_viewport_transform ||
+ State->Interact_Active == interact_type_viewport_transform_gizmo) && Layer->IsSelected == 1) {
Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &T.x, &T.y, &T.rotation, &T.scale);
}
@@ -364,7 +390,8 @@ Transform_Calculate(project_state *State, memory *Memory, project_data *File, bl
real32 s = Layer->scale.CurrentValue;
blend_mode BlendMode = Layer->BlendMode;
- if (State->Interact_Active == interact_type_viewport_transform && Layer->IsSelected == 1) {
+ if ((State->Interact_Active == interact_type_viewport_transform ||
+ State->Interact_Active == interact_type_viewport_transform_gizmo) && Layer->IsSelected == 1) {
Transform_ApplyInteractive(*(interact_transform *)&State->Interact_Offset[0], &X, &Y, &Rotation, &s);
}