From 84d04d391bc4bf9481106d4f5ac4d3dd8f27ed87 Mon Sep 17 00:00:00 2001
From: Fox Caminiti <fox@foxcam.net>
Date: Fri, 6 Jan 2023 17:44:22 -0500
Subject: round edges

---
 src/bezier.cpp            | 42 ++++++++++++++++++++++
 src/createcalls.cpp       | 13 +++++++
 src/imgui_ui.cpp          |  1 +
 src/imgui_ui_viewport.cpp | 92 ++++++++++-------------------------------------
 src/include/main.h        |  7 ++--
 src/include/my_math.h     |  1 +
 src/layer.cpp             |  2 --
 7 files changed, 80 insertions(+), 78 deletions(-)

diff --git a/src/bezier.cpp b/src/bezier.cpp
index 6bb1541..1585495 100644
--- a/src/bezier.cpp
+++ b/src/bezier.cpp
@@ -34,6 +34,48 @@ Bezier_SolveYForX(v2 Point_P0, v2 Point_P1, v2 Point_P2, v2 Point_P3, real32 Tar
     return Y;
 }
 
+// TODO(fox): Incorporate sorting for non-continuous shapes.
+static uint32
+Bezier_Shape_Sort(memory *Memory, shape_layer *Shape, bezier_point *PointData)
+{
+    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);
+        if (Radius <= 0 && Point->Type == interpolation_type_linear) {
+            v2 Pos = Point->Pos[0];
+            int Index_Prev = (i != 0) ? i-1 : Shape->Point_Count-1;
+            int Index_Next = (i != Shape->Point_Count-1) ? i+1 : 0;
+            v2 Pos_Prev = Bezier_LookupAddress(Memory, Shape->Block_Bezier_Index, Index_Prev, 1)->Pos[0];
+            v2 Pos_Next = Bezier_LookupAddress(Memory, Shape->Block_Bezier_Index, Index_Next, 1)->Pos[0];
+            v2 Vector_Prev = Pos - Pos_Prev;
+            v2 Vector_Next = Pos - Pos_Next;
+            real32 Length_Prev = sqrtf(LengthSq(Vector_Prev));
+            real32 Length_Next = sqrtf(LengthSq(Vector_Next));
+
+            // real32 RadAngle = acos(Inner(Vector_Prev, VectorR) / (LengthL * LengthR)) * PI / 180;
+            // real32 AngleKappa = (4.f/3) * tan(RadAngle * 1/4);
+
+            real32 Ratio_Prev = Radius / Length_Prev;
+            real32 Ratio_Prev_Inv = 1.0f - Ratio_Prev;
+
+            real32 Ratio_Next = Radius / Length_Next;
+            real32 Ratio_Next_Inv = 1.0f - Ratio_Next;
+
+            v2 Point_1 = Pos_Prev + V2(Vector_Prev.x * Ratio_Prev_Inv, (Vector_Prev.y * Ratio_Prev_Inv));
+            v2 Point_2 = Vector_Prev * Ratio_Prev * (1-KAPPA);
+            v2 Point_3 = Pos_Next + V2(Vector_Next.x * Ratio_Next_Inv, Vector_Next.y * Ratio_Next_Inv);
+            v2 Point_4 = Vector_Next * Ratio_Next * (1-KAPPA);
+
+            *PointData++ = { 1, { Point_1, Point_2, V2(0, 0) }, interpolation_type_bezier, 0 };
+            *PointData++ = { 1, { Point_3, V2(0, 0), Point_4 }, interpolation_type_bezier, 0 };
+        } else {
+            *PointData++ = *Point;
+        }
+    }
+    return PointData - PointStart;
+}
+
 static bezier_point *
 Bezier_LookupAddress(memory *Memory, uint16 *Block_Bezier_Index, uint16 Index, bool32 AssertExists)
 {
diff --git a/src/createcalls.cpp b/src/createcalls.cpp
index cd8f3d5..4334dc8 100644
--- a/src/createcalls.cpp
+++ b/src/createcalls.cpp
@@ -627,6 +627,18 @@ Project_ShapeLayer_New(project_data *File, project_state *State, memory *Memory)
     Layer->Frame_End = MainComp->Frame_End;
     Layer_Select(Memory, State, Memory_Block_LazyIndexAtAddress(Memory, F_Layers, Layer));
 
+    shape_layer *Shape = &File->UI.Shape;
+    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);
+    Memory_PopScratch(Memory, sizeof(nvg_point) * 128);
+    Shape->Width =  Max.x - Min.x;
+    Shape->Height = Max.y - Min.y;
+    Layer->x.CurrentValue = Min.x + (Shape->Width / 2);
+    Layer->y.CurrentValue  = Min.y + (Shape->Height / 2);
+
+    /*
     v2 Min = V2(10000, 10000);
     v2 Max = V2(-10000, -10000);
     shape_layer *Shape = &File->UI.Shape;
@@ -644,6 +656,7 @@ Project_ShapeLayer_New(project_data *File, project_state *State, memory *Memory)
     v2 ShapeSize = (Max - Min);
     Layer->x.CurrentValue = Min.x + (ShapeSize.x / 2);
     Layer->y.CurrentValue  = Min.y + (ShapeSize.y / 2);
+    */
     for (int i = 0; i < Shape->Point_Count; i++) {
         bezier_point *Point = Bezier_LookupAddress(Memory, Shape->Block_Bezier_Index, i, 1);
         Point->Pos[0] = Point->Pos[0] - Min;
diff --git a/src/imgui_ui.cpp b/src/imgui_ui.cpp
index 50d25a4..0a7f310 100644
--- a/src/imgui_ui.cpp
+++ b/src/imgui_ui.cpp
@@ -23,6 +23,7 @@ ImGui_File(project_data *File, project_state *State, memory *Memory, ImGuiIO io,
     ImGui::Begin("Files");
     ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
 
+
     if (ImGui::IsKeyPressed(ImGuiKey_Backspace)) {
         // TODO(fox): Delete sources code!
 
diff --git a/src/imgui_ui_viewport.cpp b/src/imgui_ui_viewport.cpp
index fae7bd4..774b305 100644
--- a/src/imgui_ui_viewport.cpp
+++ b/src/imgui_ui_viewport.cpp
@@ -82,7 +82,7 @@ ImGui_Viewport_ShapeUI(project_state *State, memory *Memory, ui *UI, ImGuiIO &io
             if (Point_1->Type == interpolation_type_bezier)
                 draw_list->AddLine(ScreenPoint_0[1], ScreenPoint_0[2], wcol, 2.0f);
 
-            ImU32 PointCol = (Point_1->IsSelected) ? ImColor(0.8f, 0.5f, 0.0f, 1.0f) : ImColor(0.1f, 0.1f, 0.1f, 1.0f);
+            ImU32 PointCol = (Point_1->IsSelected) ? ImColor(0.8f, 0.5f, 0.0f, 1.0f) : ImColor(0.1f, 0.1f, 0.1f, 0.2f);
             ImGui::PushID(i);
             for (int a = 0; a < 3; a++) {
                 ImGui::PushID(a);
@@ -100,6 +100,12 @@ ImGui_Viewport_ShapeUI(project_state *State, memory *Memory, ui *UI, ImGuiIO &io
                     ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                 }
                 if (IsItemActivated) {
+                    if (!io.KeyShift) {
+                        for (int p = 0; p < Shape->Point_Count; p++) {
+                             bezier_point *Point = Bezier_LookupAddress(Memory, Shape->Block_Bezier_Index, p, 1);
+                             Point->IsSelected = 0;
+                        }
+                    }
                     Point_1->IsSelected = 1;
                 }
                 if (IsItemActive) {
@@ -111,12 +117,12 @@ ImGui_Viewport_ShapeUI(project_state *State, memory *Memory, ui *UI, ImGuiIO &io
                         Assert(State->Interact_Active == interact_type_keyframe_move);
                         ImVec2 DragDelta = io.MousePos - ImVec2(State->Interact_Offset[2], State->Interact_Offset[3]);
                         if (io.MouseDelta.x || io.MouseDelta.y) {
+                            if (State->Interact_Active == interact_type_keyframe_move) {
+                                State->Interact_Offset[0] = DragDelta.x * (MainComp->Width  / UI->CompZoom.x);
+                                State->Interact_Offset[1] = DragDelta.y * (MainComp->Height / UI->CompZoom.y);
+                            }
                             State->UpdateFrame = true;
                         }
-                        if (State->Interact_Active == interact_type_keyframe_move) {
-                            State->Interact_Offset[0] = DragDelta.x * (MainComp->Width  / UI->CompZoom.x);
-                            State->Interact_Offset[1] = DragDelta.y * (MainComp->Height / UI->CompZoom.y);
-                        }
                     }
                 }
                 if (IsItemDeactivated) {
@@ -126,85 +132,23 @@ ImGui_Viewport_ShapeUI(project_state *State, memory *Memory, ui *UI, ImGuiIO &io
                         Shape->IsClosed = true;
                         History_Entry_End(Memory);
                     } else if (Layer != NULL) {
-                        layer_transforms T = Layer_GetTransforms(Layer);
                         History_Entry_Commit(Memory, "Move point");
-                        void *Data = Memory_PushScratch(Memory, sizeof(nvg_point) * 128);
-                        v2 Min_Old = {}, Max_Old = {};
-                        NVG_FlattenPath(Memory, Shape, (nvg_point *)Data, State, T, 0, 0, Width, Height, 0, &Min_Old, &Max_Old);
-                        v2 TestMin = Min_Old, TestMax = Max_Old;
+                        layer_transforms T = Layer_GetTransforms(Layer);
                         for (int p = 0; p < Shape->Point_Count; p++) {
                              bezier_point *Point = Bezier_LookupAddress(Memory, Shape->Block_Bezier_Index, p, 1);
                              if (Point->IsSelected) {
                                 v2 *Pos = &Point->Pos[Point->IsSelected-1];
                                 History_Action_Swap(Memory, F_Bezier, sizeof(*Pos), Pos);
+                                *Pos = TransformPoint(T, Shape->Width, Shape->Height, *Pos);
                                 Pos->x += State->Interact_Offset[0];
                                 Pos->y += State->Interact_Offset[1];
-                                if (Pos->x < TestMin.x) {
-                                    TestMin.x = Pos->x;
-                                }
-                                if (Pos->y < TestMin.y) {
-                                    TestMin.y = Pos->y;
-                                }
-                                if (Pos->x > TestMax.x) {
-                                    TestMax.x = Pos->x;
-                                }
-                                if (Pos->y > TestMax.y) {
-                                    TestMax.y = Pos->y;
-                                }
+                                *Pos = T_CompPosToLayerPos(T, MainComp->Width, MainComp->Height, Shape->Width, Shape->Height, Pos->x, Pos->y);
                              }
                         }
-                        v2 MinDiff = TestMin - Min_Old;
-                        v2 MaxDiff = TestMax - Max_Old;
-                        for (int p = 0; p < Shape->Point_Count; p++) {
-                             bezier_point *Point = Bezier_LookupAddress(Memory, Shape->Block_Bezier_Index, p, 1);
-                             v2 *Pos = &Point->Pos[0];
-                             History_Action_Swap(Memory, F_Bezier, sizeof(*Pos), Pos);
-                             if (MinDiff.x)
-                                Pos->x -= State->Interact_Offset[0];
-                             if (MinDiff.y)
-                                Pos->y -= State->Interact_Offset[1];
-                             if (MaxDiff.x)
-                                // Pos->x -= State->Interact_Offset[0];
-                             if (MaxDiff.y)
-                                // Pos->y += State->Interact_Offset[1];
-                             int FU = 0;
-                        }
-                        State->Interact_Active = interact_type_none;
-                        v2 Min_New = {}, Max_New = {};
-                        NVG_FlattenPath(Memory, Shape, (nvg_point *)Data, State, T, Shape->Width, Shape->Height, Width, Height, 1, &Min_New, &Max_New);
-
-                        Memory_PopScratch(Memory, sizeof(nvg_point) * 128);
-
-                        int Width_Old = Max_Old.x - Min_Old.x;
-                        int Width_New = Max_New.x - Min_New.x;
-                        int Height_New = Max_New.y - Min_New.y;
-                        if (MinDiff.x) {
-                           History_Action_Swap(Memory, F_Layers, sizeof(Layer->ax.CurrentValue), &Layer->ax.CurrentValue);
-                           Layer->ax.CurrentValue -= (real32)(State->Interact_Offset[0]) * (1.0f - Layer->ax.CurrentValue) / Width_New;
-                        } if (MinDiff.y) {
-                           History_Action_Swap(Memory, F_Layers, sizeof(Layer->ay.CurrentValue), &Layer->ay.CurrentValue);
-                           Layer->ay.CurrentValue -= (real32)(State->Interact_Offset[1]) * (1.0f - Layer->ay.CurrentValue) / Height_New;
-                        } if (MaxDiff.x) {
-                           History_Action_Swap(Memory, F_Layers, sizeof(Layer->ax.CurrentValue), &Layer->ax.CurrentValue);
-                           Layer->ax.CurrentValue -= (real32)(State->Interact_Offset[0]) * (Layer->ax.CurrentValue) / Width_New;
-                        } if (MaxDiff.y) {
-                           History_Action_Swap(Memory, F_Layers, sizeof(Layer->ay.CurrentValue), &Layer->ay.CurrentValue);
-                           Layer->ay.CurrentValue -= (real32)(State->Interact_Offset[1]) * (Layer->ay.CurrentValue) / Height_New;
-                        }
-                        /*
-                        if (Min_New.y < Min_Old.y) {
-                            Layer->y.CurrentValue += Min_New.y - Min_Old.y;
-                        }
-                        if (Max_New.x > Max_Old.x) {
-                            Layer->x.CurrentValue += Max_New.x - Max_Old.x;
-                        }
-                        if (Max_New.y > Max_Old.y) {
-                            Layer->y.CurrentValue += Max_New.y - Max_Old.y;
-                        }
-                        */
-                        Shape->Width = Width_New;
-                        Shape->Height = Height_New;
                         History_Entry_End(Memory);
+                        State->Interact_Active = interact_type_none;
+                        State->Interact_Offset[0] = 0;
+                        State->Interact_Offset[1] = 0;
                     }
                 }
                 draw_list->AddNgon(ScreenPoint_1[a], 4, PointCol, 8, 5.0f);
@@ -735,7 +679,7 @@ ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory,
     }
 
     shape_layer *Shape = &UI->Shape;
-    if (State->Tool == tool_pen && State->MostRecentlySelectedLayer == -1)
+    if ((State->Tool == tool_pen || State->Tool == tool_square) && State->MostRecentlySelectedLayer == -1)
         ImGui_Viewport_ShapeUI(State, Memory, UI, io, NULL, 0, 0, Shape, MainComp, draw_list);
 
     // Interactions for dragging and zooming
diff --git a/src/include/main.h b/src/include/main.h
index db384ce..7d3299e 100644
--- a/src/include/main.h
+++ b/src/include/main.h
@@ -188,9 +188,10 @@ struct shape_options {
     int Visibility;
     v4 FillCol =  {1, 1, 1, 1};
     v4 StrokeCol =  {0, 1, 0, 1};
-    float StrokeWidth = 10;
+    float StrokeWidth = 25;
     nvg_line_cap LineJoinType = NVG_ROUND;
     nvg_line_cap LineCapType = NVG_ROUND;
+    real32 Roundness;
 };
 
 struct shape_layer {
@@ -248,7 +249,8 @@ char *ToolName[] {
     "Move",
     "Crop",
     "Brush",
-    "Pen"
+    "Pen",
+    "Square"
 };
 
 enum tool {
@@ -256,6 +258,7 @@ enum tool {
     tool_crop,
     tool_brush,
     tool_pen,
+    tool_square,
     tool_count
 };
 
diff --git a/src/include/my_math.h b/src/include/my_math.h
index 865f11c..72c6b99 100644
--- a/src/include/my_math.h
+++ b/src/include/my_math.h
@@ -1,4 +1,5 @@
 #define PI 3.141592653589793238
+#define KAPPA 0.5522847493f
 
 union v2
 {
diff --git a/src/layer.cpp b/src/layer.cpp
index 7d40310..6159467 100644
--- a/src/layer.cpp
+++ b/src/layer.cpp
@@ -114,8 +114,6 @@ Layer_UpdateMasksEffects(project_state *State, block_layer *Layer, memory *Memor
         v2 Min = {}, Max = {};
         if (State->Interact_Active == interact_type_keyframe_move) {
             NVG_FlattenPath(Memory, Shape, (nvg_point *)Data, State, T, 0, 0, Width, Height, 0, &Min, &Max);
-            Shape->Width =  Max.x - Min.x;
-            Shape->Height = Max.y - Min.y;
         }
         uint32 NumberOfVerts = NVG_FlattenPath(Memory, Shape, (nvg_point *)Data,
                                                State, T, Shape->Width, Shape->Height, Width, Height, 1, &Min, &Max);
-- 
cgit v1.2.3