CubicSpline.java

net.minecraft.util.CubicSpline

信息

  • 全限定名:net.minecraft.util.CubicSpline
  • 类型:public interface
  • 包:net.minecraft.util
  • 源码路径:src/main/java/net/minecraft/util/CubicSpline.java
  • 起始行号:L17
  • 继承:BoundedFloatFunction
  • 职责:

    TODO

字段/常量

内部类/嵌套类型

  • net.minecraft.util.CubicSpline.Point

    • 类型: record
    • 修饰符: package-private
    • 源码定位: L26
    • 说明:

      TODO

  • net.minecraft.util.CubicSpline.Builder

    • 类型: class
    • 修饰符: public static final
    • 源码定位: L85
    • 说明:

      TODO

  • net.minecraft.util.CubicSpline.Constant

    • 类型: record
    • 修饰符: public
    • 源码定位: L136
    • 说明:

      TODO

  • net.minecraft.util.CubicSpline.CoordinateVisitor

    • 类型: interface
    • 修饰符: public
    • 源码定位: L163
    • 说明:

      TODO

  • net.minecraft.util.CubicSpline.Multipoint

    • 类型: record
    • 修饰符: public
    • 源码定位: L168
    • 说明:

      TODO

构造器

方法

下面的方法块按源码顺序生成。

String parityString() @ L18

  • 方法名:parityString
  • 源码定位:L18
  • 返回类型:String
  • 修饰符:package-private

参数:

说明:

TODO

CubicSpline<C,I> mapAll(CubicSpline.CoordinateVisitor<I> visitor) @ L21

  • 方法名:mapAll
  • 源码定位:L21
  • 返回类型:CubicSpline<C,I>
  • 修饰符:package-private

参数:

  • visitor: CubicSpline.CoordinateVisitor

说明:

TODO

static <C,I extends BoundedFloatFunction<C>> Codec<CubicSpline<C,I>> codec(Codec<I> coordinateCodec) @ L23

  • 方法名:codec
  • 源码定位:L23
  • 返回类型:<C,I extends BoundedFloatFunction> Codec<CubicSpline<C,I>>
  • 修饰符:static

参数:

  • coordinateCodec: Codec

说明:

TODO

static <C,I extends BoundedFloatFunction<C>> CubicSpline<C,I> constant(float value) @ L73

  • 方法名:constant
  • 源码定位:L73
  • 返回类型:<C,I extends BoundedFloatFunction> CubicSpline<C,I>
  • 修饰符:static

参数:

  • value: float

说明:

TODO

static <C,I extends BoundedFloatFunction<C>> CubicSpline.Builder<C,I> builder(I coordinate) @ L77

  • 方法名:builder
  • 源码定位:L77
  • 返回类型:<C,I extends BoundedFloatFunction> CubicSpline.Builder<C,I>
  • 修饰符:static

参数:

  • coordinate: I

说明:

TODO

static <C,I extends BoundedFloatFunction<C>> CubicSpline.Builder<C,I> builder(I coordinate, BoundedFloatFunction<Float> valueTransformer) @ L81

  • 方法名:builder
  • 源码定位:L81
  • 返回类型:<C,I extends BoundedFloatFunction> CubicSpline.Builder<C,I>
  • 修饰符:static

参数:

  • coordinate: I
  • valueTransformer: BoundedFloatFunction

说明:

TODO

代码

public interface CubicSpline<C, I extends BoundedFloatFunction<C>> extends BoundedFloatFunction<C> {
    @VisibleForDebug
    String parityString();
 
    CubicSpline<C, I> mapAll(final CubicSpline.CoordinateVisitor<I> visitor);
 
    static <C, I extends BoundedFloatFunction<C>> Codec<CubicSpline<C, I>> codec(Codec<I> coordinateCodec) {
        MutableObject<Codec<CubicSpline<C, I>>> result = new MutableObject<>();
 
        record Point<C, I extends BoundedFloatFunction<C>>(float location, CubicSpline<C, I> value, float derivative) {
        }
 
        Codec<Point<C, I>> pointCodec = RecordCodecBuilder.create(
            i -> i.group(
                    Codec.FLOAT.fieldOf("location").forGetter(Point::location),
                    Codec.lazyInitialized(result).fieldOf("value").forGetter(Point::value),
                    Codec.FLOAT.fieldOf("derivative").forGetter(Point::derivative)
                )
                .apply(i, (x$0, x$1, x$2) -> new Point<>(x$0, x$1, x$2))
        );
        Codec<CubicSpline.Multipoint<C, I>> multipointCodec = RecordCodecBuilder.create(
            i -> i.group(
                    coordinateCodec.fieldOf("coordinate").forGetter(CubicSpline.Multipoint::coordinate),
                    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(i, (coordinate, points) -> {
                    float[] locations = new float[points.size()];
                    ImmutableList.Builder<CubicSpline<C, I>> values = ImmutableList.builder();
                    float[] derivatives = new float[points.size()];
 
                    for (int p = 0; p < points.size(); p++) {
                        Point<C, I> point = points.get(p);
                        locations[p] = point.location();
                        values.add(point.value());
                        derivatives[p] = point.derivative();
                    }
 
                    return CubicSpline.Multipoint.create(coordinate, locations, values.build(), derivatives);
                })
        );
        result.setValue(
            Codec.either(Codec.FLOAT, multipointCodec)
                .xmap(
                    e -> e.map(CubicSpline.Constant::new, m -> m),
                    s -> s instanceof CubicSpline.Constant<C, I> c ? Either.left(c.value()) : Either.right((CubicSpline.Multipoint<C, I>)s)
                )
        );
        return result.get();
    }
 
    static <C, I extends BoundedFloatFunction<C>> CubicSpline<C, I> constant(float value) {
        return new CubicSpline.Constant<>(value);
    }
 
    static <C, I extends BoundedFloatFunction<C>> CubicSpline.Builder<C, I> builder(I coordinate) {
        return new CubicSpline.Builder<>(coordinate);
    }
 
    static <C, I extends BoundedFloatFunction<C>> CubicSpline.Builder<C, I> builder(I coordinate, BoundedFloatFunction<Float> valueTransformer) {
        return new CubicSpline.Builder<>(coordinate, valueTransformer);
    }
 
    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 CubicSpline.Builder<C, I> addPoint(float location, float value) {
            return this.addPoint(location, new CubicSpline.Constant<>(this.valueTransformer.apply(value)), 0.0F);
        }
 
        public CubicSpline.Builder<C, I> addPoint(float location, float value, float derivative) {
            return this.addPoint(location, new CubicSpline.Constant<>(this.valueTransformer.apply(value)), derivative);
        }
 
        public CubicSpline.Builder<C, I> addPoint(float location, CubicSpline<C, I> sampler) {
            return this.addPoint(location, sampler, 0.0F);
        }
 
        private CubicSpline.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");
            } else {
                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");
            } else {
                return CubicSpline.Multipoint.create(
                    this.coordinate, this.locations.toFloatArray(), ImmutableList.copyOf(this.values), this.derivatives.toFloatArray()
                );
            }
        }
    }
 
    @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", this.value);
        }
 
        @Override
        public float minValue() {
            return this.value;
        }
 
        @Override
        public float maxValue() {
            return this.value;
        }
 
        @Override
        public CubicSpline<C, I> mapAll(CubicSpline.CoordinateVisitor<I> visitor) {
            return this;
        }
    }
 
    public interface CoordinateVisitor<I> {
        I visit(final I input);
    }
 
    @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(I coordinate, float[] locations, List<CubicSpline<C, I>> values, float[] derivatives, float minValue, float maxValue) {
            validateSizes(locations, values, derivatives);
            this.coordinate = coordinate;
            this.locations = locations;
            this.values = values;
            this.derivatives = derivatives;
            this.minValue = minValue;
            this.maxValue = maxValue;
        }
 
        private static <C, I extends BoundedFloatFunction<C>> CubicSpline.Multipoint<C, I> create(
            I coordinate, float[] locations, List<CubicSpline<C, I>> values, float[] derivatives
        ) {
            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]) {
                float edge1 = linearExtend(minInput, locations, values.get(0).minValue(), derivatives, 0);
                float edge2 = 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]) {
                float edge1 = linearExtend(maxInput, locations, values.get(lastIndex).minValue(), derivatives, lastIndex);
                float edge2 = 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) {
                    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 CubicSpline.Multipoint<>(coordinate, locations, values, derivatives, minValue, maxValue);
        }
 
        private static float linearExtend(float input, float[] locations, float value, float[] derivatives, int index) {
            float derivative = derivatives[index];
            return derivative == 0.0F ? value : 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);
            } else 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 = findIntervalStart(this.locations, input);
            int lastIndex = this.locations.length - 1;
            if (start < 0) {
                return linearExtend(input, this.locations, this.values.get(0).apply(c), this.derivatives, 0);
            } else if (start == lastIndex) {
                return linearExtend(input, this.locations, this.values.get(lastIndex).apply(c), this.derivatives, lastIndex);
            } else {
                float x1 = this.locations[start];
                float x2 = this.locations[start + 1];
                float t = (input - x1) / (x2 - x1);
                BoundedFloatFunction<C> f1 = (BoundedFloatFunction<C>)this.values.get(start);
                BoundedFloatFunction<C> f2 = (BoundedFloatFunction<C>)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);
                return Mth.lerp(t, y1, y2) + t * (1.0F - t) * Mth.lerp(t, a, b);
            }
        }
 
        private static int findIntervalStart(float[] locations, float input) {
            return Mth.binarySearch(0, locations.length, i -> input < locations[i]) - 1;
        }
 
        @VisibleForTesting
        @Override
        public String parityString() {
            return "Spline{coordinate="
                + 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(CubicSpline.CoordinateVisitor<I> visitor) {
            return create(visitor.visit(this.coordinate), this.locations, this.values().stream().map(v -> v.mapAll(visitor)).toList(), this.derivatives);
        }
    }
}

引用的其他类

  • BoundedFloatFunction

    • 引用位置: 参数/继承/返回值
  • ExtraCodecs

    • 引用位置: 方法调用
    • 关联成员: ExtraCodecs.nonEmptyList()
  • Mth

    • 引用位置: 方法调用
    • 关联成员: Mth.binarySearch(), Mth.lerp()