1 /*
2     Copyright © 2020,2022 Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4 */
5 module creator.actions.parameter;
6 
7 import creator.core.actionstack;
8 import creator.actions;
9 import creator.actions.binding;
10 import creator;
11 import inochi2d;
12 import std.format;
13 import i18n;
14 
15 /**
16     Action to add parameter to active puppet.
17 */
18 class ParameterAddRemoveAction(bool added = true) : Action {
19 public:
20     Parameter self;
21     Driver[] drivers;
22     Parameter[]* parentList;
23 
24     this(Parameter self, Parameter[]* parentList) {
25         this.self = self;
26         this.parentList = parentList;
27 
28         // Find drivers
29         foreach(ref driver; incActivePuppet().getDrivers()) {
30             if (SimplePhysics sf = cast(SimplePhysics)driver) {
31                 if (sf.param !is null && sf.param.uuid == self.uuid) {
32                     drivers ~= driver;
33                 }
34             }
35         }
36 
37         // Empty drivers
38         foreach(ref driver; drivers) {
39             if (SimplePhysics sf = cast(SimplePhysics)driver) {
40                 sf.param = null;
41             }
42         }
43     }
44 
45     /**
46         Rollback
47     */
48     void rollback() {
49         if (!added) 
50         incActivePuppet().parameters ~= self;
51         else
52             incActivePuppet().removeParameter(self);
53             
54         // Re-apply drivers
55         foreach(ref driver; drivers) {
56             if (SimplePhysics sf = cast(SimplePhysics)driver) {
57                 sf.param = self;
58             }
59         }
60     }
61 
62     /**
63         Redo
64     */
65     void redo() {
66         if (added)
67             incActivePuppet().parameters ~= self;
68         else
69             incActivePuppet().removeParameter(self);
70             
71         // Empty drivers
72         foreach(ref driver; drivers) {
73             if (SimplePhysics sf = cast(SimplePhysics)driver) {
74                 sf.param = null;
75             }
76         }
77     }
78 
79     /**
80         Describe the action
81     */
82     string describe() {
83         if (added)
84             return _("Added parameter %s").format(self.name);
85         else
86             return _("Removed parameter %s").format(self.name);
87     }
88 
89     /**
90         Describe the action
91     */
92     string describeUndo() {
93         if (added)
94             return _("Parameter %s was removed").format(self.name);
95         else
96             return _("Parameter %s was added").format(self.name);
97     }
98 
99     /**
100         Gets name of this action
101     */
102     string getName() {
103         return this.stringof;
104     }
105     
106     bool merge(Action other) { return false; }
107     bool canMerge(Action other) { return false; }
108 }
109 
110 alias ParameterAddAction = ParameterAddRemoveAction!true;
111 alias ParameterRemoveAction = ParameterAddRemoveAction!false;
112 
113 
114 /**
115     Action to remove parameter from active puppet.
116 */
117 class ParameterValueChangeAction(T) : LazyBoundAction {
118 public:
119     alias TSelf = typeof(this);
120     string name;
121     Parameter self;
122     T oldValue;
123     T newValue;
124     T* valuePtr;
125 
126     this(string name, Parameter self, T oldValue, T newValue, T* valuePtr) {
127         this.name     = name;
128         this.self     = self;
129         this.oldValue = oldValue;
130         this.newValue = newValue;
131         this.valuePtr = valuePtr;
132     }
133 
134     this(string name, Parameter self, T* valuePtr, void delegate() update = null) {
135         this.name     = name;
136         this.self     = self;
137         this.valuePtr = valuePtr;
138         this.oldValue = *valuePtr;
139         if (update !is null) {
140             update();
141             updateNewState();
142         }
143     }
144 
145     void updateNewState() {
146         this.newValue = *valuePtr;
147     }
148 
149     /**
150         Rollback
151     */
152     void rollback() {
153         *valuePtr = oldValue;
154     }
155 
156     /**
157         Redo
158     */
159     void redo() {
160         *valuePtr = newValue;
161     }
162 
163     /**
164         Describe the action
165     */
166     string describe() {
167         if (name == "axis points")
168             return _("%s->%s changed").format(self.name, name);
169         else
170             return _("%s->%s changed to %s").format(self.name, name, newValue);
171     }
172 
173     /**
174         Describe the action
175     */
176     string describeUndo() {
177         if (name == "axis points")
178             return _("%s->%s change cancelled").format(self.name, name);
179         else
180             return _("%s->%s changed from %s").format(self.name, name, oldValue);
181     }
182 
183     /**
184         Gets name of this action
185     */
186     string getName() {
187         return name;
188     }
189     
190     /**
191         Merge
192     */
193     bool merge(Action other) {
194         if (this.canMerge(other)) {
195             this.newValue = (cast(TSelf)other).newValue;
196             return true;
197         }
198         return false;
199     }
200 
201     /**
202         Gets whether this node can merge with an other
203     */
204     bool canMerge(Action other) {
205         TSelf otherChange = cast(TSelf) other;
206         return (otherChange !is null && this.name == otherChange.name);
207     }
208 }
209 
210 /**
211     Base class for actions to change multiple bindings of the same parameter at once.
212 */
213 class AbstractParameterChangeBindingsAction(VarArg...) : GroupAction, LazyBoundAction {
214 public:
215     alias TSelf = typeof(this);
216     string name;
217     Parameter self;
218 
219     this(string name, Parameter self, ParameterBinding[] bindings, Action function(ParameterBinding, VarArg) bindingActionMapper, VarArg args) {
220         super([]);
221         this.name     = name;
222         this.self     = self;
223         foreach (binding; (bindings !is null)? bindings: self.bindings) {
224             Action action = bindingActionMapper(binding, args);
225             if (action !is null) 
226                 addAction(action);
227         }
228     }
229 
230     override
231     void updateNewState() {
232         foreach (action; actions) {
233             LazyBoundAction lazyAction = cast(LazyBoundAction)action;
234             if (lazyAction !is null) 
235                 lazyAction.updateNewState();
236         }
237     }
238 
239     /**
240         Describe the action
241     */
242     override
243     string describe() {
244         return _("%s->%s changed").format(self.name, name);
245     }
246 
247     /**
248         Describe the action
249     */
250     override
251     string describeUndo() {
252         return _("%s->%s change cancelled").format(self.name, name);
253     }
254 
255     /**
256         Gets name of this action
257     */
258     override
259     string getName() {
260         return name;
261     }
262 }
263 
264 
265 /**
266     Actions to add bindings to parameter at once.
267 */
268 
269 Action BindingAddMapper(ParameterBinding binding, Parameter parent) {
270     return new ParameterBindingAddAction(parent, binding);
271 }
272 class ParameterAddBindingsAction : AbstractParameterChangeBindingsAction!(Parameter) {
273     this(string name, Parameter self, ParameterBinding[] bindings) {
274         super(name, self, bindings, &BindingAddMapper, self);
275     }
276 }
277 
278 
279 /**
280     Actions to remove bindings from parameter at once.
281 */
282 
283 Action BindingRemoveMapper(ParameterBinding binding, Parameter parent) {
284     return new ParameterBindingRemoveAction(parent, binding);
285 }
286 class ParameterRemoveBindingsAction : AbstractParameterChangeBindingsAction!(Parameter) {
287     this(string name, Parameter self, ParameterBinding[] bindings) {
288         super(name, self, bindings, &BindingRemoveMapper, self);
289     }
290 }
291 
292 
293 /**
294     Actions to change all binding values at once.
295 */
296 
297 Action BindingChangeMapper(ParameterBinding binding) {
298     if (auto typedBinding = cast(ParameterBindingImpl!float)binding) {
299         return new ParameterBindingAllValueChangeAction!(float)(typedBinding.getName(), typedBinding);
300     } else if (auto typedBinding = cast(ParameterBindingImpl!Deformation)binding) {
301         return new ParameterBindingAllValueChangeAction!(Deformation)(typedBinding.getName(), typedBinding);
302     } else {
303         return null;
304     }
305 }
306 class ParameterChangeBindingsAction : AbstractParameterChangeBindingsAction!() {
307     this(string name, Parameter self, ParameterBinding[] bindings) {
308         super(name, self, bindings, &BindingChangeMapper);
309     }
310 }
311 
312 
313 /**
314     Actions to change binding value of specified keypoints at once.
315 */
316 
317 Action BindingValueChangeMapper(ParameterBinding binding, int pointx, int pointy) {
318     if (auto typedBinding = cast(ParameterBindingImpl!float)binding) {
319         return new ParameterBindingValueChangeAction!(float)(typedBinding.getName(), typedBinding, pointx, pointy);
320     } else if (auto typedBinding = cast(ParameterBindingImpl!Deformation)binding) {
321         return new ParameterBindingValueChangeAction!(Deformation)(typedBinding.getName(), typedBinding, pointx, pointy);
322     } else {
323         return null;
324     }
325 }
326 class ParameterChangeBindingsValueAction : AbstractParameterChangeBindingsAction!(int, int) {
327     this(string name, Parameter self, ParameterBinding[] bindings, int pointx, int pointy) {
328         super(name, self, bindings, &BindingValueChangeMapper, pointx, pointy);
329     }
330 }