1 /*
2     Copyright © 2020,2022 Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4 */
5 module creator.actions.mesheditor;
6 
7 import creator.core.actionstack;
8 import creator.viewport.common.mesheditor;
9 import creator.viewport.common.mesh;
10 import creator.viewport.common.spline;
11 import creator.viewport.model.deform;
12 import creator.viewport.vertex;
13 import creator.viewport;
14 import creator.actions;
15 import creator;
16 import inochi2d;
17 import std.format;
18 import std.range;
19 import i18n;
20 
21 /**
22     Action for change of binding values at once
23 */
24 class MeshEditorDeformationAction  : LazyBoundAction {
25     alias  TSelf    = typeof(this);
26     string name;
27     bool dirty;
28     Parameter      param;
29     Drawable       target;
30     DeformationParameterBinding    deform;
31     bool isSet;
32     vec2[] vertices;
33     vec2u  keypoint;
34     bool bindingAdded;
35     bool undoable = true;
36 
37     this(string name, void delegate() update = null) {
38         this.name   = name;
39         this.bindingAdded = false;
40         this.clear();
41 
42         if (update !is null) {
43             update();
44             this.updateNewState();
45         }
46     }
47 
48     auto self() {
49         return incViewportModelDeformGetEditor();
50     }
51 
52     void addVertex(MeshVertex* vertex) {
53     }
54 
55     void markAsDirty() { dirty = true; }
56 
57     void updateNewState() {
58         if (param) {
59             auto newDeform      = cast(DeformationParameterBinding)param.getBinding(this.target, "deform");
60             if (deform is null && newDeform !is null)
61                 bindingAdded = true;
62             deform = newDeform;
63         }
64     }
65 
66     void clear() {
67         if (self is null) {
68             target       = null;
69             param        = null;
70             deform       = null;
71             bindingAdded = false;
72             dirty        = false;
73             vertices     = null;
74             isSet        = false;
75         } else {
76             target       = self.getTarget();
77             param        = incArmedParameter();
78             keypoint     = param.findClosestKeypoint();
79             vertices     = self.getOffsets();
80             deform       = cast(DeformationParameterBinding)param.getBinding(this.target, "deform");
81             bindingAdded = false;
82         }
83         if (deform !is null) {
84             isSet    = deform.isSet_[keypoint.x][keypoint.y];
85         }
86         this.dirty       = false;
87     }
88 
89     bool isApplyable() {
90         return self !is null && self.getTarget() == this.target && incArmedParameter() == this.param &&
91                incArmedParameter().findClosestKeypoint() == this.keypoint;
92     }
93 
94     /**
95         Rollback
96     */
97     void rollback() {
98         if (undoable) {
99             if (vertices) {
100                 if (deform !is null) {
101                     vec2[] tmpVertices = vertices;
102                     bool   tmpIsSet    = isSet;
103                     vertices = deform.values[keypoint.x][keypoint.y].vertexOffsets.dup;
104                     isSet    = deform.isSet_[keypoint.x][keypoint.y];
105                     deform.update(this.keypoint, tmpVertices);
106                     deform.isSet_[keypoint.x][keypoint.y] = tmpIsSet;
107                     deform.reInterpolate();
108                     if (bindingAdded) {
109                         param.removeBinding(deform);
110                     }
111                 }
112                 if (self !is null && self.getTarget() == this.target && incArmedParameter() == this.param) {
113                     self.resetMesh();
114                     if (deform !is null) {
115                         self.applyOffsets(deform.getValue(param.findClosestKeypoint()).vertexOffsets);            
116                     }
117                 }
118                 if (self !is null)
119                     self.getCleanDeformAction();
120             }
121             undoable = false;
122         }
123     }
124 
125     /**
126         Redo
127     */
128     void redo() {
129         if (!undoable) {
130             if (vertices) {
131                 if (deform !is null) {
132                     vec2[] tmpVertices = vertices;
133                     bool   tmpIsSet    = isSet;
134                     vertices = deform.values[keypoint.x][keypoint.y].vertexOffsets.dup;
135                     isSet    = deform.isSet_[keypoint.x][keypoint.y];
136                     deform.update(this.keypoint, tmpVertices);
137                     deform.isSet_[keypoint.x][keypoint.y] = tmpIsSet;
138                     deform.reInterpolate();
139                     if (bindingAdded) {
140                         param.addBinding(deform);
141                     }
142                 }
143                 if (self !is null && self.getTarget() == this.target && incArmedParameter() == this.param) {
144                     self.resetMesh();
145                     if (deform !is null) {
146                         self.applyOffsets(deform.getValue(param.findClosestKeypoint()).vertexOffsets);
147                     }
148                 }
149                 if (self !is null)
150                     self.getCleanDeformAction();
151             }
152             undoable = true;
153         }
154     }
155 
156     /**
157         Describe the action
158     */
159     string describe() {
160         return _("%s->Edited deformation of %s.").format("deform", name);
161     }
162 
163     /**
164         Describe the action
165     */
166     string describeUndo() {
167         return _("%s->deformation of %s was edited.").format("deform", name);
168     }
169 
170     /**
171         Gets name of this action
172     */
173     string getName() {
174         return this.stringof;
175     }
176     
177     /**
178         Merge
179     */
180     bool merge(Action other) {
181         if (this.canMerge(other)) {
182             return true;
183         }
184         return false;
185     }
186 
187     /**
188         Gets whether this node can merge with an other
189     */
190     bool canMerge(Action other) {
191         return false;
192     }
193 };
194 
195 class MeshEditorPathDeformAction : MeshEditorDeformationAction {
196 public:
197 //    CatmullSpline path;
198     SplinePoint[] oldPathPoints;
199     SplinePoint[] oldTargetPathPoints;
200     SplinePoint[] newPathPoints;
201     SplinePoint[] newTargetPathPoints;
202 
203     auto path() {
204         if (self !is null)
205             return self.getPath();
206         else
207             return null;
208     }
209 
210     this(string name, void delegate() update = null) {
211         super(name, update);
212         if (path !is null)
213             oldPathPoints = path.points.dup;
214         else
215             oldPathPoints = null;
216         if (this.path && this.path.target !is null)
217             oldTargetPathPoints = this.path.target.points.dup;
218         else
219             oldTargetPathPoints = null;
220     }
221 
222     override
223     void updateNewState() {
224         super.updateNewState();
225         if (path !is null)
226         newPathPoints = path.points.dup;
227         if (path !is null && path.target !is null) 
228             newTargetPathPoints = path.target.points.dup;
229     }
230 
231     override
232     void clear() {
233         super.clear();
234         if (path !is null)
235             oldPathPoints = path.points.dup;
236         else
237             oldPathPoints = null;
238         if (path !is null && path.target !is null)
239             oldTargetPathPoints = path.target.points.dup;
240         else
241             oldTargetPathPoints = null;
242         newPathPoints = null;
243         newTargetPathPoints = null;
244     }
245 
246     /**
247         Rollback
248     */
249     override
250     void rollback() {
251         if (isApplyable()) {
252             if (oldPathPoints !is null && oldPathPoints.length > 0 && path !is null) {
253                 path.points = oldPathPoints.dup;
254                 path.update();
255             }
256             if (oldTargetPathPoints !is null && oldTargetPathPoints.length > 0 && path !is null && path.target !is null) {
257                 path.target.points = oldTargetPathPoints.dup;
258                 path.target.update();
259             }
260         }
261         super.rollback();
262     }
263 
264     /**
265         Redo
266     */
267     override
268     void redo() {
269          if (isApplyable()) {
270             if (newPathPoints !is null && newPathPoints.length > 0 && path !is null) {
271                 this.path.points = newPathPoints.dup;
272                 this.path.update();
273             }
274             if (newTargetPathPoints !is null && newTargetPathPoints.length > 0 && path !is null && path.target !is null) {
275                 this.path.target.points = newTargetPathPoints.dup;
276                 this.path.target.update();
277             }
278         }
279         super.redo();
280    }
281 }