1 /** 2 Inochi2D Lattice Deformer Node 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.nodes.deformer.latticedeformer; 14 import inochi2d.nodes.deformer; 15 import inochi2d.nodes; 16 import inochi2d.common; 17 import inochi2d.core; 18 import numath; 19 import numem; 20 21 /** 22 A deformer which uses a 2D lattice as the basis for 23 its deformation. 24 */ 25 @TypeId("LatticeDeformer", IN_MAKE_TAG!(2, 2)) 26 class LatticeDeformer : Deformer { 27 private: 28 @nogc: 29 int subdivs; 30 vec2 size_; 31 32 float[][] weights_; 33 vec2[] latticeInitial; 34 vec2[] lattice; 35 36 // Regenerates the lattice points. 37 void regenLattice() { 38 if (subdivs == 0) 39 return; 40 41 this.latticeInitial = latticeInitial.nu_resize(subdivs * subdivs); 42 this.lattice = lattice.nu_resize(latticeInitial.length); 43 latticeInitial[0 .. $] = vec2.zero; 44 45 vec2 iter = vec2(size_.x / subdivs, size_.y / subdivs); 46 foreach (i; 0 .. lattice.length) { 47 float x = mod(cast(float)i, cast(float)subdivs); 48 float y = cast(float)i / cast(float)subdivs; 49 latticeInitial[i] = iter * vec2(x, y); 50 } 51 } 52 53 // Clears lattice weights 54 void clearWeights() { 55 foreach (i; 0 .. weights_.length) { 56 nu_freea(weights_[i]); 57 } 58 nu_freea(weights_); 59 } 60 61 protected: 62 63 /** 64 Serializes this node to a DataNode. 65 66 Params: 67 object = The DataNode to serialize to. 68 */ 69 override 70 void onSerialize(ref DataNode object) { 71 super.onSerialize(object); 72 object["subdivisions"] = subdivs.serialize(); 73 } 74 75 /** 76 Deserializes this node from a DataNode. 77 78 Params: 79 object = The DataNode to deserialize from. 80 state = The state of the deserializer. 81 */ 82 override 83 void onDeserialize(ref DataNode object, ref ModelState state) { 84 super.onDeserialize(object, state); 85 object.tryGetRef(state, subdivs, "subdivisions"); 86 object.tryGetRef(state, size_, "size"); 87 } 88 89 /** 90 Called when the node is to finalize its deserialization from disk. 91 92 Params: 93 state = The state of the deserializer. 94 */ 95 override 96 void onFinalize(ref ModelState state) { 97 this.regenLattice(); 98 } 99 100 /** 101 Called during the update phase of a new frame. 102 103 Params: 104 delta = Time since the last frame. 105 drawList = The drawlist for the active scene. 106 */ 107 override 108 void onUpdate(float delta, DrawList drawList) { 109 super.onUpdate(delta, drawList); 110 } 111 112 public: 113 114 /** 115 The size of the lattice (in pixels) 116 */ 117 @property vec2 size() => size_; 118 @property void size(vec2 value) { 119 this.size_ = value; 120 this.regenLattice(); 121 } 122 123 /** 124 The amount of subdivisions in the lattice. 125 */ 126 @property int subdivisions() => subdivs; 127 @property void subdivisions(int value) { 128 this.subdivs = value; 129 this.regenLattice(); 130 } 131 132 /** 133 The base position of the deformable's points. 134 */ 135 override @property const(vec2)[] basePoints() => latticeInitial; 136 137 /** 138 The control points of the deformer. 139 */ 140 override @property vec2[] controlPoints() => lattice; 141 override @property void controlPoints(vec2[] value) { 142 import nulib.math : min; 143 144 size_t m = min(value.length, lattice.length); 145 lattice[0 .. m] = value[0 .. m]; 146 } 147 148 /** 149 Constructs a new MeshGroup node 150 */ 151 this(Node parent = null) { 152 super(parent); 153 } 154 155 /** 156 Deforms the IDeformable. 157 158 Params: 159 deformed = The deformation delta. 160 absolute = Whether the deformation is absolute, 161 replacing the original deformation. 162 */ 163 override 164 void deform(vec2[] deformed, bool absolute) { 165 super.deform(deformed, absolute); 166 } 167 168 /** 169 Resets the deformation. 170 */ 171 override 172 void resetDeform() { 173 lattice[0 .. $] = latticeInitial[0 .. $]; 174 } 175 } 176 177 mixin Register!(LatticeDeformer, in_node_registry);