Skip to content

Commit a5be6b2

Browse files
committed
Added SecondOrderDynamics to aid in animations
1 parent 266ffbd commit a5be6b2

File tree

2 files changed

+398
-0
lines changed

2 files changed

+398
-0
lines changed
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
package doggytalents.client.debug;
2+
3+
import java.util.Map;
4+
import java.util.function.Consumer;
5+
import java.util.function.Function;
6+
7+
import com.google.common.collect.Maps;
8+
9+
import doggytalents.client.screen.framework.widget.FlatButton;
10+
import doggytalents.client.screen.framework.widget.ScrollBar;
11+
import doggytalents.client.screen.framework.widget.TextOnlyButton;
12+
import doggytalents.client.screen.framework.widget.ScrollBar.Direction;
13+
import doggytalents.common.entity.anim.SecondOrderDynamics;
14+
import net.minecraft.client.Minecraft;
15+
import net.minecraft.client.gui.GuiGraphics;
16+
import net.minecraft.client.gui.components.EditBox;
17+
import net.minecraft.client.gui.screens.Screen;
18+
import net.minecraft.network.chat.Component;
19+
import net.minecraft.stats.StatFormatter;
20+
import net.minecraft.util.Mth;
21+
import net.minecraft.util.RandomSource;
22+
23+
public class SODTunningScreen extends Screen {
24+
25+
private static final float MIN_VAL = 0f;
26+
private static final float MAX_VAL = 1f;
27+
28+
private final RandomSource random = RandomSource.create();
29+
private DebugGraph sampleGraph = new DebugGraph(0, 0, 480, 120);
30+
private boolean simpleMode = false;
31+
32+
//Persistent state
33+
private static float[] xArr = null;
34+
private static final Map<String, TunningEntry> persistentData = Maps.newHashMap();
35+
36+
public SODTunningScreen() {
37+
super(Component.empty());
38+
resetGraph(xArr == null);
39+
}
40+
41+
private void resetGraph(boolean new_graph) {
42+
final int simulate_ticks = 200;
43+
sampleGraph.reset(simulate_ticks,
44+
DebugGraph.entriesBuilder()
45+
.entry("x", -0.2f, 1.2f, 0xffff0000)
46+
.entry("y", -0.2f, 1.2f, 0xff0000ff)
47+
);
48+
if (new_graph || xArr == null) {
49+
xArr = simpleMode ? generateSimpleData(simulate_ticks)
50+
: generateRandomData(simulate_ticks);
51+
}
52+
float f = persistentData.computeIfAbsent("f", k -> new TunningEntry())
53+
.value;
54+
float z = persistentData.computeIfAbsent("z", k -> new TunningEntry())
55+
.value;
56+
float r = persistentData.computeIfAbsent("r", k -> new TunningEntry())
57+
.value;
58+
float[] y = f > 0 ?
59+
calculateRespond(f, z, r, xArr)
60+
: xArr;
61+
for (int i = 0; i < simulate_ticks; ++i) {
62+
sampleGraph.recordValue("x", xArr[i]);
63+
sampleGraph.recordValue("y", y[i]);
64+
}
65+
}
66+
67+
private float[] generateRandomData(int ticks) {
68+
float value = MIN_VAL + random.nextFloat() * (MAX_VAL - MIN_VAL);
69+
int duration = random.nextInt(40);
70+
var buffer = new float[ticks];
71+
for (int i = 0; i < ticks; ++i) {
72+
if (duration <= 0) {
73+
value = MIN_VAL + random.nextFloat() * (MAX_VAL - MIN_VAL);
74+
duration = random.nextInt(20);
75+
}
76+
--duration;
77+
buffer[i] = value;
78+
}
79+
return buffer;
80+
}
81+
82+
private float[] generateSimpleData(int ticks) {
83+
var buffer = new float[ticks];
84+
int change_tick = Mth.floor(0.3f * ticks);
85+
for (int i = 0; i < ticks; ++i) {
86+
buffer[i] = i < change_tick ? 0.1f : 0.8f;
87+
}
88+
return buffer;
89+
}
90+
91+
private float[] calculateRespond(float f, float z, float r, float[] raw_data) {
92+
var respond = new float[raw_data.length];
93+
respond[0] = raw_data[0];
94+
var dynamics = SecondOrderDynamics.single(f, z, r, respond[0]);
95+
for (int i = 1; i < raw_data.length; ++i) {
96+
respond[i] = dynamics.update(1/20f, raw_data[i]);
97+
}
98+
return respond;
99+
}
100+
101+
@Override
102+
protected void init() {
103+
int width1 = Mth.clamp(this.width, 0, 500);
104+
int mX = this.width/2;
105+
int mY = this.height/2;
106+
this.sampleGraph.width = width1;
107+
this.sampleGraph.height = Mth.floor(this.height * 0.4f);
108+
int pY = Mth.floor(this.height * 0.4f) + 30;
109+
var reset_button = new FlatButton(
110+
width1 - 70, pY, 60, 30, Component.literal("New sample"), b -> {
111+
this.resetGraph(true);
112+
});
113+
this.addRenderableWidget(reset_button);
114+
115+
int pX = 30;
116+
createAndAddTunningEntry("f", pX, pY, new_val -> {
117+
resetGraph(false);
118+
});
119+
pY += 25;
120+
createAndAddTunningEntry("z", pX, pY, new_val -> {
121+
resetGraph(false);
122+
});
123+
pY += 25;
124+
createAndAddTunningEntry("r", pX, pY, new_val -> {
125+
resetGraph(false);
126+
});
127+
128+
}
129+
130+
private TunningEntry createAndAddTunningEntry(String id, int x, int y, Consumer<Float> responder) {
131+
int pX = x;
132+
var entry = persistentData.computeIfAbsent(id, k -> new TunningEntry());
133+
final Function<Float, Component> value_show_to_str =
134+
val -> Component.literal(id + ": " + StatFormatter.DECIMAL_FORMAT.format(val));
135+
final var value_show = new TextOnlyButton(pX, y, 60, 20,
136+
value_show_to_str.apply(entry.value), b -> {
137+
try {
138+
var value = StatFormatter.DECIMAL_FORMAT.format(entry.value);
139+
Minecraft.getInstance().keyboardHandler.setClipboard(value);
140+
} catch (NumberFormatException e) {
141+
142+
}
143+
}, font);
144+
145+
final var clamp_field = new EditBox(font, pX, y, 60, 20, Component.empty());
146+
clamp_field.setValue(""
147+
+ StatFormatter.DECIMAL_FORMAT.format(entry.min) + ":"
148+
+ StatFormatter.DECIMAL_FORMAT.format(entry.max));
149+
clamp_field.setResponder(s -> {
150+
var splitted = s.split(":");
151+
if (splitted.length == 1) {
152+
float value = 0;
153+
try {
154+
value = Float.parseFloat(splitted[0].strip());
155+
} catch (NumberFormatException e) {
156+
157+
}
158+
entry.min = value;
159+
entry.max = value;
160+
entry.value = 0;
161+
return;
162+
}
163+
if (splitted.length != 2)
164+
return;
165+
float min = 0;
166+
float max = 0;
167+
try {
168+
min = Float.parseFloat(splitted[0].strip());
169+
max = Float.parseFloat(splitted[1].strip());
170+
} catch (NumberFormatException e) {
171+
172+
}
173+
if (max < min)
174+
return;
175+
entry.min = min;
176+
entry.max = max;
177+
entry.value = 0;
178+
});
179+
this.addRenderableWidget(clamp_field);
180+
pX += clamp_field.getWidth() + 20;
181+
final var slider = new ScrollBar(pX, y + 5, 100, 10, Direction.HORIZONTAL, 10, this) {
182+
@Override
183+
public void onValueUpdated() {
184+
entry.value = (float) (entry.min + this.getProgressValue() * (entry.max - entry.min));
185+
entry.value = Mth.clamp(entry.value, entry.min, entry.max);
186+
entry.responder.accept(entry.value);
187+
value_show.setMessage(value_show_to_str.apply(entry.value));
188+
}
189+
}.alignMode(ScrollBar.AlignMode.CENTER);
190+
double progress = entry.max - entry.min <= 0 ? 0 :
191+
(entry.value - entry.min) / (entry.max - entry.min);
192+
double slider_off = progress * slider.getMaxOffsetValue();
193+
slider.setBarOffset(slider_off);
194+
this.addRenderableWidget(slider);
195+
pX += slider.getWidth() + 20;
196+
value_show.setX(pX);
197+
value_show.setY(y);
198+
this.addRenderableWidget(value_show);
199+
200+
entry.responder = responder;
201+
return entry;
202+
}
203+
204+
@Override
205+
public void render(GuiGraphics graphics, int mouseX, int mouseY, float pticks) {
206+
graphics.fill(0, 0, this.width, this.height, 0x40000000);
207+
super.render(graphics, mouseX, mouseY, pticks);
208+
this.sampleGraph.render(graphics, pticks);
209+
}
210+
211+
@Override
212+
public boolean keyPressed(int keyCode, int scanCode, int modifier) {
213+
var sneakKey = this.minecraft.options.keyShift;
214+
if (keyCode == sneakKey.getKey().getValue()) {
215+
this.simpleMode = true;
216+
}
217+
return super.keyPressed(keyCode, scanCode, modifier);
218+
}
219+
220+
@Override
221+
public boolean keyReleased(int keyCode, int scanCode, int modifier) {
222+
var sneakKey = this.minecraft.options.keyShift;
223+
if (keyCode == sneakKey.getKey().getValue()) {
224+
this.simpleMode = false;
225+
}
226+
return super.keyReleased(keyCode, scanCode, modifier);
227+
}
228+
229+
private static class TunningEntry {
230+
public float min, max;
231+
public float value;
232+
public Consumer<Float> responder;
233+
public TunningEntry() {}
234+
}
235+
236+
}

0 commit comments

Comments
 (0)