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 }