1 /** 2 Deformation information. 3 4 Copyright: 5 Copyright © 2020-2026, Inochi2D Project 6 7 License: 8 $(LINK2 https://github.com/Inochi2D/inochi2d/blob/main/LICENSE, BSD 2-clause License) 9 10 Authors: 11 Luna Nielsen 12 */ 13 module inochi2d.core.math.deform; 14 import inochi2d.core; 15 import inochi2d.core.math; 16 import inochi2d.common; 17 import inochi2d; 18 import nulib.collections.vector; 19 import numem; 20 21 /** 22 Interface implemented by types which can be deformed. 23 */ 24 interface IDeformable { 25 @nogc: 26 public: 27 28 /** 29 The base position of the deformable's points. 30 */ 31 @property const(vec2)[] basePoints(); 32 33 /** 34 The points which may be deformed by the deformer. 35 */ 36 @property vec2[] deformPoints(); 37 38 /** 39 The base matrix of the object before any parameters have been applied. 40 */ 41 @property Basis deformBaseMatrix(); 42 43 /** 44 World matrix of the deformable object. 45 */ 46 @property Basis deformMatrix(); 47 48 /** 49 Deforms the IDeformable. 50 51 Params: 52 deformed = The deformation delta. 53 absolute = Whether the deformation is absolute, 54 replacing the original deformation. 55 */ 56 void deform(vec2[] deformed, bool absolute = false); 57 58 /** 59 Deforms a single vertex in the IDeformable 60 61 Params: 62 offset = The offset into the point list to deform. 63 deform = The deformation delta. 64 absolute = Whether the deformation is absolute, 65 replacing the original deformation. 66 */ 67 void deform(size_t offset, vec2 deform, bool absolute = false); 68 69 /** 70 Resets the deformation for the IDeformable. 71 */ 72 void resetDeform(); 73 } 74 75 /** 76 A deformation 77 */ 78 struct Deformation { 79 vec2[] vertexOffsets; 80 81 this(ref return scope Deformation other) @trusted @nogc nothrow pure { 82 this.vertexOffsets = vertexOffsets.nu_dup(); 83 } 84 85 this(vec2[] data) @trusted @nogc nothrow pure { 86 this.update(data); 87 } 88 89 void update(vec2[] points) @trusted @nogc nothrow pure { 90 vertexOffsets = vertexOffsets.nu_resize(points.length); 91 vertexOffsets[0 .. points.length] = points[0 .. $]; 92 } 93 94 void clear(size_t length) @trusted @nogc nothrow pure { 95 vertexOffsets = vertexOffsets.nu_resize(length); 96 vertexOffsets[0 .. $] = vec2.zero; 97 } 98 99 Deformation opUnary(string op : "-")() @trusted nothrow pure { 100 Deformation new_; 101 102 new_.vertexOffsets.length = vertexOffsets.length; 103 foreach (i; 0 .. vertexOffsets.length) { 104 new_.vertexOffsets[i] = -vertexOffsets[i]; 105 } 106 107 return new_; 108 } 109 110 Deformation opBinary(string op : "*", T)(T other) @trusted @nogc nothrow pure { 111 static if (is(T == Deformation)) { 112 Deformation new_; 113 114 new_.clear(this.vertexOffsets.length); 115 foreach (i; 0 .. this.vertexOffsets.length) { 116 new_.vertexOffsets[i] = this.vertexOffsets[i] * other.vertexOffsets[i]; 117 } 118 119 return new_; 120 } else static if (is(T == vec2)) { 121 Deformation new_; 122 123 new_.clear(this.vertexOffsets.length); 124 foreach (i; 0 .. this.vertexOffsets.length) { 125 new_.vertexOffsets[i] = this.vertexOffsets[i] * other; 126 } 127 128 return new_; 129 } else { 130 Deformation new_; 131 132 new_.clear(this.vertexOffsets.length); 133 foreach (i; 0 .. this.vertexOffsets.length) { 134 new_.vertexOffsets[i] = this.vertexOffsets[i] * other; 135 } 136 137 return new_; 138 } 139 } 140 141 Deformation opBinaryRight(string op : "*", T)(T other) @trusted nothrow pure { 142 static if (is(T == mat4)) { 143 144 // Optimized matrix multiplication 145 Deformation new_; 146 new_.vertexOffsets = self.vertexOffsets.nu_dup(); 147 simd_mul(new_.vertexOffsets, other); 148 149 return new_; 150 } 151 static if (is(T == Deformation)) { 152 Deformation new_; 153 154 new_.clear(this.vertexOffsets.length); 155 foreach (i; 0 .. this.vertexOffsets.length) { 156 new_.vertexOffsets[i] = other.vertexOffsets[i] * this.vertexOffsets[i]; 157 } 158 159 return new_; 160 } else static if (is(T == vec2)) { 161 Deformation new_; 162 163 new_.clear(this.vertexOffsets.length); 164 foreach (i; 0 .. this.vertexOffsets.length) { 165 new_.vertexOffsets[i] = other * this.vertexOffsets[i]; 166 } 167 168 return new_; 169 } else { 170 Deformation new_; 171 172 new_.clear(this.vertexOffsets.length); 173 foreach (i; 0 .. this.vertexOffsets.length) { 174 new_.vertexOffsets[i] = other * this.vertexOffsets[i]; 175 } 176 177 return new_; 178 } 179 } 180 181 Deformation opBinary(string op : "+", T)(T other) @trusted nothrow pure { 182 static if (is(T == Deformation)) { 183 Deformation new_; 184 185 new_.clear(this.vertexOffsets.length); 186 foreach (i; 0 .. this.vertexOffsets.length) { 187 new_.vertexOffsets[i] = this.vertexOffsets[i] + other.vertexOffsets[i]; 188 } 189 190 return new_; 191 } else static if (is(T == vec2)) { 192 Deformation new_; 193 194 new_.clear(this.vertexOffsets.length); 195 foreach (i; 0 .. this.vertexOffsets.length) { 196 new_.vertexOffsets[i] = this.vertexOffsets[i] + other; 197 } 198 199 return new_; 200 } else { 201 Deformation new_; 202 203 new_.clear(this.vertexOffsets.length); 204 foreach (i; 0 .. this.vertexOffsets.length) { 205 new_.vertexOffsets[i] = this.vertexOffsets[i] + other; 206 } 207 208 return new_; 209 } 210 } 211 212 Deformation opBinary(string op : "-", T)(T other) @trusted nothrow pure { 213 static if (is(T == Deformation)) { 214 Deformation new_; 215 216 new_.clear(this.vertexOffsets.length); 217 foreach (i; 0 .. this.vertexOffsets.length) { 218 new_.vertexOffsets[i] = this.vertexOffsets[i] - other.vertexOffsets[i]; 219 } 220 221 return new_; 222 } else static if (is(T == vec2)) { 223 Deformation new_; 224 225 new_.clear(this.vertexOffsets.length); 226 foreach (i; 0 .. this.vertexOffsets.length) { 227 new_.vertexOffsets[i] = this.vertexOffsets[i] - other; 228 } 229 230 return new_; 231 } else { 232 Deformation new_; 233 234 new_.clear(this.vertexOffsets.length); 235 foreach (i; 0 .. this.vertexOffsets.length) { 236 new_.vertexOffsets[i] = this.vertexOffsets[i] - other; 237 } 238 239 return new_; 240 } 241 } 242 243 void onSerialize(ref DataNode data) @nogc { 244 foreach (offset; vertexOffsets) { 245 data ~= offset.serialize(); 246 } 247 } 248 249 void onDeserialize(ref DataNode data, ref ModelState state) @nogc { 250 if (state.doUpgrade08) { 251 252 this.vertexOffsets = nu_malloca!vec2(data.length); 253 foreach (i, ref element; data.array) { 254 this.vertexOffsets[i / 2] = element.deserialize!vec2(state); 255 } 256 return; 257 } 258 } 259 260 void free() @nogc { 261 if (vertexOffsets) 262 nu_freea(vertexOffsets); 263 } 264 }