2025-11-24 22:52:51 +03:00

294 lines
14 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.annotations.VisibleForTesting
* com.google.common.collect.ImmutableList
* com.google.common.collect.ImmutableList$Builder
* com.google.common.collect.Lists
* com.mojang.datafixers.kinds.App
* com.mojang.datafixers.kinds.Applicative
* com.mojang.datafixers.util.Either
* com.mojang.serialization.Codec
* com.mojang.serialization.codecs.RecordCodecBuilder
* it.unimi.dsi.fastutil.floats.FloatArrayList
* it.unimi.dsi.fastutil.floats.FloatList
* org.apache.commons.lang3.mutable.MutableObject
*/
package net.minecraft.util;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.mojang.datafixers.kinds.App;
import com.mojang.datafixers.kinds.Applicative;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.floats.FloatArrayList;
import it.unimi.dsi.fastutil.floats.FloatList;
import java.util.List;
import java.util.Locale;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import net.minecraft.util.BoundedFloatFunction;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.Mth;
import net.minecraft.util.VisibleForDebug;
import org.apache.commons.lang3.mutable.MutableObject;
public interface CubicSpline<C, I extends BoundedFloatFunction<C>>
extends BoundedFloatFunction<C> {
@VisibleForDebug
public String parityString();
public CubicSpline<C, I> mapAll(CoordinateVisitor<I> var1);
public static <C, I extends BoundedFloatFunction<C>> Codec<CubicSpline<C, I>> codec(Codec<I> coordinateCodec) {
record Point<C, I extends BoundedFloatFunction<C>>(float location, CubicSpline<C, I> value, float derivative) {
}
MutableObject result = new MutableObject();
Codec pointCodec = RecordCodecBuilder.create(i -> i.group((App)Codec.FLOAT.fieldOf("location").forGetter(Point::location), (App)Codec.lazyInitialized((Supplier)result).fieldOf("value").forGetter(Point::value), (App)Codec.FLOAT.fieldOf("derivative").forGetter(Point::derivative)).apply((Applicative)i, (x$0, x$1, x$2) -> new Point((float)x$0, x$1, (float)x$2)));
Codec multipointCodec = RecordCodecBuilder.create(i -> i.group((App)coordinateCodec.fieldOf("coordinate").forGetter(Multipoint::coordinate), (App)ExtraCodecs.nonEmptyList(pointCodec.listOf()).fieldOf("points").forGetter(m -> IntStream.range(0, m.locations.length).mapToObj(p -> new Point(m.locations()[p], m.values().get(p), m.derivatives()[p])).toList())).apply((Applicative)i, (coordinate, points) -> {
float[] locations = new float[points.size()];
ImmutableList.Builder values = ImmutableList.builder();
float[] derivatives = new float[points.size()];
for (int p = 0; p < points.size(); ++p) {
Point point = (Point)points.get(p);
locations[p] = point.location();
values.add(point.value());
derivatives[p] = point.derivative();
}
return Multipoint.create(coordinate, locations, values.build(), derivatives);
}));
result.setValue((Object)Codec.either((Codec)Codec.FLOAT, (Codec)multipointCodec).xmap(e -> (CubicSpline)e.map(Constant::new, m -> m), s -> {
Either either;
if (s instanceof Constant) {
Constant c = (Constant)s;
either = Either.left((Object)Float.valueOf(c.value()));
} else {
either = Either.right((Object)((Multipoint)s));
}
return either;
}));
return (Codec)result.get();
}
public static <C, I extends BoundedFloatFunction<C>> CubicSpline<C, I> constant(float value) {
return new Constant(value);
}
public static <C, I extends BoundedFloatFunction<C>> Builder<C, I> builder(I coordinate) {
return new Builder(coordinate);
}
public static <C, I extends BoundedFloatFunction<C>> Builder<C, I> builder(I coordinate, BoundedFloatFunction<Float> valueTransformer) {
return new Builder(coordinate, valueTransformer);
}
@VisibleForDebug
public record Constant<C, I extends BoundedFloatFunction<C>>(float value) implements CubicSpline<C, I>
{
@Override
public float apply(C c) {
return this.value;
}
@Override
public String parityString() {
return String.format(Locale.ROOT, "k=%.3f", Float.valueOf(this.value));
}
@Override
public float minValue() {
return this.value;
}
@Override
public float maxValue() {
return this.value;
}
@Override
public CubicSpline<C, I> mapAll(CoordinateVisitor<I> visitor) {
return this;
}
}
public static final class Builder<C, I extends BoundedFloatFunction<C>> {
private final I coordinate;
private final BoundedFloatFunction<Float> valueTransformer;
private final FloatList locations = new FloatArrayList();
private final List<CubicSpline<C, I>> values = Lists.newArrayList();
private final FloatList derivatives = new FloatArrayList();
protected Builder(I coordinate) {
this(coordinate, BoundedFloatFunction.IDENTITY);
}
protected Builder(I coordinate, BoundedFloatFunction<Float> valueTransformer) {
this.coordinate = coordinate;
this.valueTransformer = valueTransformer;
}
public Builder<C, I> addPoint(float location, float value) {
return this.addPoint(location, new Constant(this.valueTransformer.apply(Float.valueOf(value))), 0.0f);
}
public Builder<C, I> addPoint(float location, float value, float derivative) {
return this.addPoint(location, new Constant(this.valueTransformer.apply(Float.valueOf(value))), derivative);
}
public Builder<C, I> addPoint(float location, CubicSpline<C, I> sampler) {
return this.addPoint(location, sampler, 0.0f);
}
private Builder<C, I> addPoint(float location, CubicSpline<C, I> sampler, float derivative) {
if (!this.locations.isEmpty() && location <= this.locations.getFloat(this.locations.size() - 1)) {
throw new IllegalArgumentException("Please register points in ascending order");
}
this.locations.add(location);
this.values.add(sampler);
this.derivatives.add(derivative);
return this;
}
public CubicSpline<C, I> build() {
if (this.locations.isEmpty()) {
throw new IllegalStateException("No elements added");
}
return Multipoint.create(this.coordinate, this.locations.toFloatArray(), ImmutableList.copyOf(this.values), this.derivatives.toFloatArray());
}
}
@VisibleForDebug
public record Multipoint<C, I extends BoundedFloatFunction<C>>(I coordinate, float[] locations, List<CubicSpline<C, I>> values, float[] derivatives, float minValue, float maxValue) implements CubicSpline<C, I>
{
public Multipoint {
Multipoint.validateSizes(locations, values, derivatives);
}
private static <C, I extends BoundedFloatFunction<C>> Multipoint<C, I> create(I coordinate, float[] locations, List<CubicSpline<C, I>> values, float[] derivatives) {
float edge2;
float edge1;
Multipoint.validateSizes(locations, values, derivatives);
int lastIndex = locations.length - 1;
float minValue = Float.POSITIVE_INFINITY;
float maxValue = Float.NEGATIVE_INFINITY;
float minInput = coordinate.minValue();
float maxInput = coordinate.maxValue();
if (minInput < locations[0]) {
edge1 = Multipoint.linearExtend(minInput, locations, values.get(0).minValue(), derivatives, 0);
edge2 = Multipoint.linearExtend(minInput, locations, values.get(0).maxValue(), derivatives, 0);
minValue = Math.min(minValue, Math.min(edge1, edge2));
maxValue = Math.max(maxValue, Math.max(edge1, edge2));
}
if (maxInput > locations[lastIndex]) {
edge1 = Multipoint.linearExtend(maxInput, locations, values.get(lastIndex).minValue(), derivatives, lastIndex);
edge2 = Multipoint.linearExtend(maxInput, locations, values.get(lastIndex).maxValue(), derivatives, lastIndex);
minValue = Math.min(minValue, Math.min(edge1, edge2));
maxValue = Math.max(maxValue, Math.max(edge1, edge2));
}
for (CubicSpline<C, I> value : values) {
minValue = Math.min(minValue, value.minValue());
maxValue = Math.max(maxValue, value.maxValue());
}
for (int i = 0; i < lastIndex; ++i) {
float x1 = locations[i];
float x2 = locations[i + 1];
float xDiff = x2 - x1;
CubicSpline<C, I> v1 = values.get(i);
CubicSpline<C, I> v2 = values.get(i + 1);
float min1 = v1.minValue();
float max1 = v1.maxValue();
float min2 = v2.minValue();
float max2 = v2.maxValue();
float d1 = derivatives[i];
float d2 = derivatives[i + 1];
if (d1 == 0.0f && d2 == 0.0f) continue;
float p1 = d1 * xDiff;
float p2 = d2 * xDiff;
float minLerp1 = Math.min(min1, min2);
float maxLerp1 = Math.max(max1, max2);
float minA = p1 - max2 + min1;
float maxA = p1 - min2 + max1;
float minB = -p2 + min2 - max1;
float maxB = -p2 + max2 - min1;
float minLerp2 = Math.min(minA, minB);
float maxLerp2 = Math.max(maxA, maxB);
minValue = Math.min(minValue, minLerp1 + 0.25f * minLerp2);
maxValue = Math.max(maxValue, maxLerp1 + 0.25f * maxLerp2);
}
return new Multipoint<C, I>(coordinate, locations, values, derivatives, minValue, maxValue);
}
private static float linearExtend(float input, float[] locations, float value, float[] derivatives, int index) {
float derivative = derivatives[index];
if (derivative == 0.0f) {
return value;
}
return value + derivative * (input - locations[index]);
}
private static <C, I extends BoundedFloatFunction<C>> void validateSizes(float[] locations, List<CubicSpline<C, I>> values, float[] derivatives) {
if (locations.length != values.size() || locations.length != derivatives.length) {
throw new IllegalArgumentException("All lengths must be equal, got: " + locations.length + " " + values.size() + " " + derivatives.length);
}
if (locations.length == 0) {
throw new IllegalArgumentException("Cannot create a multipoint spline with no points");
}
}
@Override
public float apply(C c) {
float input = this.coordinate.apply(c);
int start = Multipoint.findIntervalStart(this.locations, input);
int lastIndex = this.locations.length - 1;
if (start < 0) {
return Multipoint.linearExtend(input, this.locations, this.values.get(0).apply(c), this.derivatives, 0);
}
if (start == lastIndex) {
return Multipoint.linearExtend(input, this.locations, this.values.get(lastIndex).apply(c), this.derivatives, lastIndex);
}
float x1 = this.locations[start];
float x2 = this.locations[start + 1];
float t = (input - x1) / (x2 - x1);
BoundedFloatFunction f1 = this.values.get(start);
BoundedFloatFunction f2 = this.values.get(start + 1);
float d1 = this.derivatives[start];
float d2 = this.derivatives[start + 1];
float y1 = f1.apply(c);
float y2 = f2.apply(c);
float a = d1 * (x2 - x1) - (y2 - y1);
float b = -d2 * (x2 - x1) + (y2 - y1);
float offset = Mth.lerp(t, y1, y2) + t * (1.0f - t) * Mth.lerp(t, a, b);
return offset;
}
private static int findIntervalStart(float[] locations, float input) {
return Mth.binarySearch(0, locations.length, i -> input < locations[i]) - 1;
}
@Override
@VisibleForTesting
public String parityString() {
return "Spline{coordinate=" + String.valueOf(this.coordinate) + ", locations=" + this.toString(this.locations) + ", derivatives=" + this.toString(this.derivatives) + ", values=" + this.values.stream().map(CubicSpline::parityString).collect(Collectors.joining(", ", "[", "]")) + "}";
}
private String toString(float[] arr) {
return "[" + IntStream.range(0, arr.length).mapToDouble(i -> arr[i]).mapToObj(f -> String.format(Locale.ROOT, "%.3f", f)).collect(Collectors.joining(", ")) + "]";
}
@Override
public CubicSpline<C, I> mapAll(CoordinateVisitor<I> visitor) {
return Multipoint.create((BoundedFloatFunction)visitor.visit(this.coordinate), this.locations, this.values().stream().map(v -> v.mapAll(visitor)).toList(), this.derivatives);
}
}
public static interface CoordinateVisitor<I> {
public I visit(I var1);
}
}