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 vec2 endPoint; 25 26 void findEndPoint() { 27 foreach(i, x; points[0]) { 28 if (!x.fixed) endPoint.x = i; 29 } 30 31 foreach(i, y; points[1]) { 32 if (!y.fixed) endPoint.y = i; 33 } 34 } 35 36 protected: 37 override 38 void onBeginUpdate() { 39 igSetNextWindowSize(ImVec2(384*2, 192*2), ImGuiCond.Appearing); 40 igSetNextWindowSizeConstraints(ImVec2(384*2, 192*2), ImVec2(float.max, float.max)); 41 super.onBeginUpdate(); 42 } 43 44 void axisPointList(ulong axis, ImVec2 avail) { 45 int deleteIndex = -1; 46 47 igIndent(); 48 igPushID(cast(int)axis); 49 if (igBeginChild("###AXIS_ADJ", ImVec2(0, avail.y))) { 50 if (points[axis].length > 2) { 51 int ix; 52 foreach(i, ref pt; points[axis]) { 53 ix++; 54 if (pt.fixed) continue; 55 56 // Do not allow existing points to cross over 57 vec2 range; 58 if (pt.origIndex != -1) { 59 range = vec2(points[axis][i - 1].value, points[axis][i + 1].value); 60 } else if (axis == 0) { 61 range = vec2(param.min.x, param.max.x); 62 } else { 63 range = vec2(param.min.y, param.max.y); 64 } 65 66 // Offset range so points cannot overlap 67 range = range + vec2(0.01, -0.01); 68 69 igSetNextItemWidth(80); 70 igPushID(cast(int)i); 71 if (incDragFloat( 72 "adj_offset", &pt.value, 0.01, 73 range.x, range.y, "%.2f", ImGuiSliderFlags.NoRoundToFormat) 74 ) { 75 pt.normValue = param.mapAxis(cast(uint)axis, pt.value); 76 } 77 igSameLine(0, 0); 78 79 if (i == endPoint.vector[axis]) { 80 incDummy(ImVec2(-52, 32)); 81 igSameLine(0, 0); 82 if (igButton("", ImVec2(24, 24))) { 83 deleteIndex = cast(int)i; 84 } 85 igSameLine(0, 0); 86 if (igButton("", ImVec2(24, 24))) { 87 createPoint(axis); 88 } 89 90 } else { 91 incDummy(ImVec2(-28, 32)); 92 igSameLine(0, 0); 93 if (igButton("", ImVec2(24, 24))) { 94 deleteIndex = cast(int)i; 95 } 96 } 97 igPopID(); 98 } 99 } else { 100 incDummy(ImVec2(-28, 24)); 101 igSameLine(0, 0); 102 if (igButton("", ImVec2(24, 24))) { 103 createPoint(axis); 104 } 105 } 106 } 107 igEndChild(); 108 igPopID(); 109 igUnindent(); 110 111 if (deleteIndex != -1) { 112 points[axis] = points[axis].remove(cast(uint)deleteIndex); 113 this.findEndPoint(); 114 } 115 } 116 117 void createPoint(ulong axis) { 118 float normValue = (points[axis][0].normValue + points[axis][1].normValue) / 2; 119 float value = param.unmapAxis(cast(uint)axis, normValue); 120 points[axis] ~= EditableAxisPoint(-1, false, value, normValue); 121 this.findEndPoint(); 122 } 123 124 override 125 void onUpdate() { 126 igPushID(cast(void*)param); 127 ImVec2 avail = incAvailableSpace(); 128 float reqSpace = param.isVec2 ? 128 : 32; 129 130 if (igBeginChild("###ControllerView", ImVec2(192, avail.y))) { 131 incDummy(ImVec2(0, (avail.y/2)-(reqSpace/2))); 132 incControllerAxisDemo("###CONTROLLER", param, points, ImVec2(192, reqSpace)); 133 } 134 igEndChild(); 135 136 igSameLine(0, 0); 137 138 igBeginGroup(); 139 if (igBeginChild("###ControllerSettings", ImVec2(0, -(28)))) { 140 avail = incAvailableSpace(); 141 if (param.isVec2) { 142 143 // Skip start and end point 144 if (incBeginCategory("X", IncCategoryFlags.NoCollapse)) { 145 axisPointList(0, ImVec2(avail.x, (avail.y/2)-42)); 146 } 147 incEndCategory(); 148 149 if (incBeginCategory("Y", IncCategoryFlags.NoCollapse)) { 150 axisPointList(1, ImVec2(avail.x, (avail.y/2)-42)); 151 } 152 incEndCategory(); 153 } else { 154 155 // Points where the user can set parameter values 156 if (incBeginCategory(__("Breakpoints"), IncCategoryFlags.NoCollapse)) { 157 axisPointList(0, ImVec2(avail.x, avail.y-38)); 158 } 159 incEndCategory(); 160 } 161 } 162 igEndChild(); 163 164 if (igBeginChild("###SettingsBtns", ImVec2(0, 0))) { 165 incDummy(ImVec2(-132, 0)); 166 igSameLine(0, 0); 167 168 // Cancels the edited state for the axies points 169 if (igButton(__("Cancel"), ImVec2(64, 24))) { 170 this.close(); 171 } 172 173 igSameLine(0, 4); 174 175 // Actually saves the edited state for the axies points 176 if (igButton(__("Save"), ImVec2(64, 24))) { 177 bool success = true; 178 179 // Make sure there isn't any invalid state 180 iloop: foreach(axis; 0..points.length) { 181 182 foreach(x; 0..points[0].length) { 183 foreach(xi; 0..points[0].length) { 184 if (x == xi) continue; 185 186 if (points[0][x].normValue == points[0][xi].normValue) { 187 incDialog(__("Error"), _("One or more axes points are overlapping, this is not allowed.")); 188 success = false; 189 break iloop; 190 } 191 } 192 } 193 } 194 195 if (success) { 196 foreach (axis, axisPoints; points) { 197 int skew = 0; 198 foreach (i, ref point; axisPoints) { 199 if (point.origIndex != -1) { 200 // Update point 201 202 // If we skipped over some original points, they were deleted, 203 // so delete them here 204 while (point.origIndex != -1 && (i + skew) < point.origIndex) { 205 param.deleteAxisPoint(cast(uint)axis, cast(uint)i); 206 skew++; 207 } 208 209 // Do not touch fixed points 210 if (!point.fixed) 211 param.axisPoints[axis][i] = point.normValue; 212 } else { 213 // Add point 214 param.insertAxisPoint(cast(uint)axis, point.normValue); 215 } 216 } 217 } 218 this.close(); 219 } 220 } 221 } 222 igEndChild(); 223 igEndGroup(); 224 igPopID(); 225 } 226 227 public: 228 this(ref Parameter param) { 229 this.param = param; 230 foreach(i, ref axisPoints; points) { 231 axisPoints.length = param.axisPoints[i].length; 232 foreach(j, ref point; axisPoints) { 233 point.origIndex = cast(int)j; 234 point.normValue = param.axisPoints[i][j]; 235 point.value = param.unmapAxis(cast(uint)i, point.normValue); 236 } 237 axisPoints[0].fixed = true; 238 axisPoints[$ - 1].fixed = true; 239 } 240 this.findEndPoint(); 241 242 // Title for the parameter axis points window 243 // This window allows adjusting axies in the 244 // Parameter it's attached to. 245 // Keypoints show up on every intersecting axis line. 246 super(_("Parameter Axes Points")); 247 } 248 }