1 /*
2     Copyright © 2020, Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4     
5     Authors:
6     - Luna Nielsen
7     - Asahi Lina
8 */
9 module creator.windows.paramaxes;
10 import std.algorithm.mutation : remove;
11 import creator.windows;
12 import creator.widgets;
13 import creator.core;
14 import std.string;
15 import creator.utils.link;
16 import i18n;
17 import inochi2d;
18 import std.math;
19 
20 class ParamAxesWindow : Window {
21 private:
22     Parameter param;
23     EditableAxisPoint[][2] points;
24 
25 
26 protected:
27     override
28     void onBeginUpdate() {
29         flags |= ImGuiWindowFlags.NoResize;
30         igSetNextWindowSize(ImVec2(384, 192), ImGuiCond.Appearing);
31         igSetNextWindowSizeConstraints(ImVec2(384, 192), ImVec2(float.max, float.max));
32         super.onBeginUpdate();
33     }
34 
35     void axisPointList(ulong axis, ImVec2 avail) {
36         int deleteIndex = -1;
37 
38         igIndent();
39             igPushID(cast(int)axis);
40                 if (igBeginChild("###AXIS_ADJ", ImVec2(0, avail.y-26))) {
41                     foreach(x, ref pt; points[axis]) {
42                         if (pt.fixed) continue;
43 
44                         // Do not allow existing points to cross over
45                         vec2 range;
46                         if (pt.origIndex != -1) {
47                             range = vec2(points[axis][x - 1].value, points[axis][x + 1].value);
48                         } else if (axis == 0) {
49                             range = vec2(param.min.x, param.max.x);
50                         } else {
51                             range = vec2(param.min.y, param.max.y);
52                         }
53 
54                         // Offset range so points cannot overlap
55                         range = range + vec2(0.01, -0.01);
56 
57                         igSetNextItemWidth(80);
58                         igPushID(cast(int)x);
59                             if (incDragFloat(
60                                 "adj_offset", &pt.value, 0.01,
61                                 range.x, range.y, "%.2f", ImGuiSliderFlags.NoRoundToFormat)
62                             ) {
63                                 pt.normValue = param.mapAxis(cast(uint)axis, pt.value);
64                             }
65                             igSameLine(0, 0);
66                             incDummy(ImVec2(-24, 32));
67                             igSameLine(0, 0);
68                             if (igButton("", ImVec2(24, 24))) {
69                                 deleteIndex = cast(int)x;
70                             }
71                         igPopID();
72                     }
73                 }
74                 igEndChild();
75 
76                 incDummy(ImVec2(-24, 24));
77                 igSameLine(0, 0);
78                 if (igButton("", ImVec2(24, 24))) {
79                     createPoint(axis);
80                 }
81             igPopID();
82         igUnindent();
83 
84         if (deleteIndex != -1) {
85             points[axis] = points[axis].remove(cast(uint)deleteIndex);
86         }
87     }
88 
89     void createPoint(ulong axis) {
90         float normValue = (points[axis][0].normValue + points[axis][1].normValue) / 2;
91         float value = param.unmapAxis(cast(uint)axis, normValue);
92         points[axis] ~= EditableAxisPoint(-1, false, value, normValue);
93     }
94 
95     override
96     void onUpdate() {
97         igPushID(cast(void*)param);
98             ImVec2 avail = incAvailableSpace();
99             float reqSpace = param.isVec2 ? 128 : 32;
100 
101             if (igBeginChild("###ControllerView", ImVec2(192, reqSpace))) {
102                 incControllerAxisDemo("###CONTROLLER", param, points, ImVec2(192, reqSpace));
103             }
104             igEndChild();
105 
106             igSameLine(0, 0);
107 
108             igBeginGroup();
109                 if (igBeginChild("###ControllerSettings", ImVec2(0, -(28)))) {
110                     avail = incAvailableSpace();
111                     if (param.isVec2) {
112 
113                         // Skip start and end point
114                         igText("X");
115                         axisPointList(0, ImVec2(avail.x, (avail.y/2)-24));
116 
117                         igText("Y");
118                         axisPointList(1, ImVec2(avail.x, (avail.y/2)-24));
119                     } else {
120 
121                         // Points where the user can set parameter values
122                         igText("Breakpoints");
123                         axisPointList(0, ImVec2(avail.x, avail.y-24));
124                     }
125                 }
126                 igEndChild();
127 
128                 if (igBeginChild("###SettingsBtns", ImVec2(0, 0))) {
129                     incDummy(ImVec2(-132, 0));
130                     igSameLine(0, 0);
131 
132                     // Cancels the edited state for the axies points
133                     if (igButton(__("Cancel"), ImVec2(64, 24))) {
134                         this.close();
135                     }
136 
137                     igSameLine(0, 4);
138 
139                     // Actually saves the edited state for the axies points
140                     if (igButton(__("Save"), ImVec2(64, 24))) {
141                         foreach (axis, axisPoints; points) {
142                             int skew = 0;
143                             foreach (i, ref point; axisPoints) {
144                                 if (point.origIndex != -1) {
145                                     // Update point
146 
147                                     // If we skipped over some original points, they were deleted,
148                                     // so delete them here
149                                     while (point.origIndex != -1 && (i + skew) < point.origIndex) {
150                                         param.deleteAxisPoint(cast(uint)axis, cast(uint)i);
151                                         skew++;
152                                     }
153 
154                                     // Do not touch fixed points
155                                     if (!point.fixed)
156                                         param.axisPoints[axis][i] = point.normValue;
157                                 } else {
158                                     // Add point
159                                     param.insertAxisPoint(cast(uint)axis, point.normValue);
160                                 }
161                             }
162                         }
163                         this.close();
164                     }
165                 }
166                 igEndChild();
167             igEndGroup();
168         igPopID();
169     }
170 
171 public:
172     this(ref Parameter param) {
173         this.param = param;
174         foreach(i, ref axisPoints; points) {
175             axisPoints.length = param.axisPoints[i].length;
176             foreach(j, ref point; axisPoints) {
177                 point.origIndex = cast(int)j;
178                 point.normValue = param.axisPoints[i][j];
179                 point.value = param.unmapAxis(cast(uint)i, point.normValue);
180             }
181             axisPoints[0].fixed = true;
182             axisPoints[$ - 1].fixed = true;
183         }
184 
185         // Title for the parameter axis points window
186         // This window allows adjusting axies in the
187         // Parameter it's attached to.
188         // Keypoints show up on every intersecting axis line.
189         super(_("Parameter Axes Points"));
190     }
191 }