1 /* 2 Copyright © 2020,2022 Inochi2D Project 3 Distributed under the 2-Clause BSD License, see LICENSE file. 4 */ 5 module creator.actions.binding; 6 7 import creator.core.actionstack; 8 import creator.actions; 9 import creator; 10 import inochi2d; 11 import std.format; 12 import std.stdio; 13 import std.range; 14 import std.algorithm.mutation; 15 import i18n; 16 17 private { 18 T[][] duplicate(T)(ref T[][] source) { 19 T[][] target = source.dup; 20 foreach (i, s; source) { 21 target[i] = s.dup; 22 } 23 return target; 24 } 25 26 void copy(T)(ref T[][] source, ref T[][] target) { 27 foreach (sarray, tarray; zip(source, target)) { 28 foreach (i, s; sarray) { 29 tarray[i] = s; 30 } 31 } 32 } 33 34 } 35 36 /** 37 Action for add / remove of binding 38 */ 39 class ParameterBindingAddRemoveAction(bool added = true) : Action { 40 public: 41 Parameter parent; 42 ParameterBinding self; 43 44 this(Parameter parent, ParameterBinding self) { 45 this.parent = parent; 46 this.self = self; 47 } 48 49 /** 50 Rollback 51 */ 52 void rollback() { 53 if (!added) 54 parent.bindings ~= self; 55 else 56 parent.removeBinding(self); 57 } 58 59 /** 60 Redo 61 */ 62 void redo() { 63 if (added) 64 parent.bindings ~= self; 65 else 66 parent.removeBinding(self); 67 } 68 69 /** 70 Describe the action 71 */ 72 string describe() { 73 if (added) 74 return _("Added binding %s").format(self.getName()); 75 else 76 return _("Removed binding %s").format(self.getName()); 77 } 78 79 /** 80 Describe the action 81 */ 82 string describeUndo() { 83 if (added) 84 return _("Binding %s was removed").format(self.getName()); 85 else 86 return _("Binding %s was added").format(self.getName()); 87 } 88 89 /** 90 Gets name of this action 91 */ 92 string getName() { 93 return this.stringof; 94 } 95 96 bool merge(Action other) { return false; } 97 bool canMerge(Action other) { return false; } 98 } 99 100 alias ParameterBindingAddAction = ParameterBindingAddRemoveAction!true; 101 alias ParameterBindingRemoveAction = ParameterBindingAddRemoveAction!false; 102 103 /** 104 Action for change of all of binding values (and isSet value) at once 105 */ 106 class ParameterBindingAllValueChangeAction(T) : LazyBoundAction { 107 alias TSelf = typeof(this); 108 alias TBinding = ParameterBindingImpl!(T); 109 string name; 110 TBinding self; 111 T[][] values; 112 bool[][] isSet; 113 bool undoable = true; 114 115 this(string name, TBinding self, void delegate() update = null) { 116 this.name = name; 117 this.self = self; 118 values = duplicate!T(self.values); 119 isSet = duplicate!bool(self.isSet_); 120 if (update !is null) { 121 update(); 122 updateNewState(); 123 } 124 } 125 126 void updateNewState() { 127 } 128 129 /** 130 Rollback 131 */ 132 void rollback() { 133 if (undoable) { 134 swap(values, self.values); 135 swap(isSet, self.isSet_); 136 undoable = false; 137 } 138 } 139 140 /** 141 Redo 142 */ 143 void redo() { 144 if (!undoable) { 145 swap(values, self.values); 146 swap(isSet, self.isSet_); 147 undoable = true; 148 } 149 } 150 151 /** 152 Describe the action 153 */ 154 string describe() { 155 return _("%s->%s changed").format(self.getName(), name); 156 } 157 158 /** 159 Describe the action 160 */ 161 string describeUndo() { 162 return _("%s->%s change cancelled").format(self.getName(), name); 163 } 164 165 /** 166 Gets name of this action 167 */ 168 string getName() { 169 return this.stringof; 170 } 171 172 /** 173 Merge 174 */ 175 bool merge(Action other) { 176 if (this.canMerge(other)) { 177 return true; 178 } 179 return false; 180 } 181 182 /** 183 Gets whether this node can merge with an other 184 */ 185 bool canMerge(Action other) { 186 TSelf otherChange = cast(TSelf) other; 187 return (otherChange !is null && this.name == otherChange.name); 188 } 189 }; 190 191 192 /** 193 Action to change binding value (and isSet) of specified keypoint. 194 */ 195 class ParameterBindingValueChangeAction(T) : LazyBoundAction { 196 alias TSelf = typeof(this); 197 string name; 198 alias TBinding = ParameterBindingImpl!(T); 199 TBinding self; 200 int pointx; 201 int pointy; 202 T value; 203 bool isSet; 204 bool undoable; 205 206 this(string name, TBinding self, int pointx, int pointy, void delegate() update = null) { 207 this.name = name; 208 this.self = self; 209 this.pointx = pointx; 210 this.pointy = pointy; 211 this.value = self.values[pointx][pointy]; 212 this.isSet = self.isSet_[pointx][pointy]; 213 this.undoable = true; 214 if (update !is null) { 215 update(); 216 updateNewState(); 217 } 218 } 219 220 void updateNewState() { 221 } 222 223 /** 224 Rollback 225 */ 226 void rollback() { 227 if (undoable) { 228 swap(self.values[pointx][pointy], value); 229 swap(self.isSet_[pointx][pointy], isSet); 230 import std.stdio; 231 self.reInterpolate(); 232 undoable = false; 233 } 234 } 235 236 /** 237 Redo 238 */ 239 void redo() { 240 if (!undoable) { 241 swap(self.values[pointx][pointy], value); 242 swap(self.isSet_[pointx][pointy], isSet); 243 self.reInterpolate(); 244 undoable = true; 245 } 246 } 247 248 /** 249 Describe the action 250 */ 251 string describe() { 252 return _("%s->%s changed").format(self.getName(), name); 253 } 254 255 /** 256 Describe the action 257 */ 258 string describeUndo() { 259 return _("%s->%s change cancelled").format(self.getName(), name); 260 } 261 262 /** 263 Gets name of this action 264 */ 265 string getName() { 266 return this.stringof; 267 } 268 269 /** 270 Merge 271 */ 272 bool merge(Action other) { 273 if (this.canMerge(other)) { 274 return true; 275 } 276 return false; 277 } 278 279 /** 280 Gets whether this node can merge with an other 281 */ 282 bool canMerge(Action other) { 283 TSelf otherChange = cast(TSelf) other; 284 return (otherChange !is null && this.name == otherChange.name && this.pointx == otherChange.pointx && this.pointy == otherChange.pointy); 285 } 286 };