1 /**
2     1D Parameter
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         Mireille Arseneault
13         Hoshino Lina
14 */
15 module inochi2d.param.parameters.param1d;
16 import inochi2d.param.parameters;
17 import inochi2d.param.utils;
18 import inochi2d.core.registry;
19 import inochi2d.core.serde;
20 import inochi2d.core.math;
21 import inochi2d.core.guid;
22 import inochi2d.common;
23 import inochi2d.puppet;
24 import nulib;
25 import numem;
26 
27 /**
28     1D variant of a parameter.
29 */
30 @TypeId("1d", IN_MAKE_TAG!(1, 0))
31 class Parameter1D : Parameter {
32 protected:
33 @nogc:
34 
35     /**
36         Serialize this parameter.
37     */
38     override
39     void onSerialize(ref DataNode object) {
40         super.onSerialize(object);
41         object["min"] = min.serialize();
42         object["max"] = max.serialize();
43         object["defaults"] = defaults.serialize();
44         object["points"] = points.serialize();
45     }
46 
47     /**
48         Deserialize this parameter.
49     */
50     override
51     void onDeserialize(ref DataNode object, ref ModelState state) {
52         super.onDeserialize(object, state);
53         object.tryGetRef(state, min, "min");
54         object.tryGetRef(state, max, "max");
55         object.tryGetRef(state, defaults, "defaults");
56 
57         // 0.8->0.9 upgrades
58         if (state.doUpgrade08) {
59             state.info("0.8->0.9: Upgrading 1D axis mapping...");
60             object.tryGetRef(state, points, "axis_points");
61             return;
62         }
63 
64         object.tryGetRef(state, points, "points");
65     }
66 
67     /**
68         Finalizes the parameter.
69 
70         Params:
71             puppet =    The parent puppet
72             state =     The state of the deserializer.
73     */
74     override
75     void onFinalize(Puppet puppet, ref ModelState state) {
76         super.onFinalize(puppet, state);
77         value = defaults;
78     }
79 
80 public:
81 
82     /**
83         The current value of this parameter.
84     */
85     float value = 0;
86 
87     /**
88         The previous value of this parameter.
89     */
90     float prev = 0;
91 
92     /**
93         The default value of this parameter.
94     */
95     float defaults = 0;
96 
97     /**
98         The lower bound of this parameter.
99     */
100     float min = 0;
101 
102     /**
103         The upper bound of this parameter.
104     */
105     float max = 1;
106 
107     /**
108         Our keypoints' positions.
109     */
110     vector!float points;
111 
112     /**
113         The lower bound of this parameter.
114     */
115     override @property float[] lowerBound() @trusted => (&min)[0 .. 1];
116 
117     /**
118         The upper bound of this parameter.
119     */
120     override @property float[] upperBound() @trusted => (&max)[0 .. 1];
121 
122     /**
123         The current value of the parameter.
124     */
125     override @property float[] currentValue() @trusted => (&value)[0 .. 1];
126 
127     /**
128         The dimensionality of the parameter.
129     */
130     override @property int dimensions() => 1;
131 
132     /**
133         Counts of elements in each axis.
134     */
135     override @property uint[] elementCounts() {
136         static uint[1] counts;
137         counts = [cast(uint)points.length];
138         return counts[0 .. 1];
139     }
140 
141     /**
142         Construct a new named parameter.
143     */
144     this(string name = null) {
145         float[2] points_init = [0, 1];
146         points = points_init;
147         guid = inNewGUID();
148         this.name = name;
149     }
150 
151     /**
152         Force this parameter to take on the given value.
153 
154         Params:
155             value = The new value this parameter will take on.
156     */
157     void pushValue(float value) {
158         this.value = value;
159     }
160 
161     /**
162         Find the keypoint index of the given position, as well as its normal.
163 
164         Params:
165             pos = A position along our keypoints. Must be within min & max.
166             norm = The given position, normalized within its keypoint.
167 
168         Returns:
169             The index of the keypoint the given position falls on.
170     */
171     ptrdiff_t findKeypointAndNormal(float pos, out float norm) {
172         return searchPoints(points, pos, norm);
173     }
174 
175     /**
176         Normalize the given position between min & max.
177     */
178     float normalize(float pos) const {
179         return (pos - min) / (max - min);
180     }
181 
182     /**
183         Linearly interpolate from min to max by the given value.
184     */
185     float lerp(float norm) const {
186         return .lerp(min, max, norm);
187     }
188 
189     /**
190         Update our bindings with the value of this parameter.
191     */
192     override
193     void update() {
194         if (!active)
195             return;
196 
197         float norm;
198         ptrdiff_t index = findKeypointAndNormal(value, norm);
199         if (index >= 0) {
200             foreach (binding; bindings) {
201                 binding.apply(vec2u(index, 0), vec2(norm, 0));
202             }
203         }
204     }
205 }
206 
207 mixin Register!(Parameter1D, in_param_registry);