All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.badlogic.gdx.math.Bezier Maven / Gradle / Ivy

There is a newer version: 1.13.0
Show newest version
/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.badlogic.gdx.math;

import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;

/** Implementation of the Bezier curve.
 * @author Xoppa */
public class Bezier> implements Path {
	// TODO implement Serializable

	/** Simple linear interpolation
	 * @param out The {@link Vector} to set to the result.
	 * @param t The location (ranging 0..1) on the line.
	 * @param p0 The start point.
	 * @param p1 The end point.
	 * @param tmp A temporary vector to be used by the calculation.
	 * @return The value specified by out for chaining */
	public static > T linear (final T out, final float t, final T p0, final T p1, final T tmp) {
		// B1(t) = p0 + (p1-p0)*t
		return out.set(p0).scl(1f - t).add(tmp.set(p1).scl(t)); // Could just use lerp...
	}

	/** Simple linear interpolation derivative
	 * @param out The {@link Vector} to set to the result.
	 * @param t The location (ranging 0..1) on the line.
	 * @param p0 The start point.
	 * @param p1 The end point.
	 * @param tmp A temporary vector to be used by the calculation.
	 * @return The value specified by out for chaining */
	public static > T linear_derivative (final T out, final float t, final T p0, final T p1, final T tmp) {
		// B1'(t) = p1-p0
		return out.set(p1).sub(p0);
	}

	/** Quadratic Bezier curve
	 * @param out The {@link Vector} to set to the result.
	 * @param t The location (ranging 0..1) on the curve.
	 * @param p0 The first bezier point.
	 * @param p1 The second bezier point.
	 * @param p2 The third bezier point.
	 * @param tmp A temporary vector to be used by the calculation.
	 * @return The value specified by out for chaining */
	public static > T quadratic (final T out, final float t, final T p0, final T p1, final T p2, final T tmp) {
		// B2(t) = (1 - t) * (1 - t) * p0 + 2 * (1-t) * t * p1 + t*t*p2
		final float dt = 1f - t;
		return out.set(p0).scl(dt * dt).add(tmp.set(p1).scl(2 * dt * t)).add(tmp.set(p2).scl(t * t));
	}

	/** Quadratic Bezier curve derivative
	 * @param out The {@link Vector} to set to the result.
	 * @param t The location (ranging 0..1) on the curve.
	 * @param p0 The first bezier point.
	 * @param p1 The second bezier point.
	 * @param p2 The third bezier point.
	 * @param tmp A temporary vector to be used by the calculation.
	 * @return The value specified by out for chaining */
	public static > T quadratic_derivative (final T out, final float t, final T p0, final T p1, final T p2,
		final T tmp) {
		// B2'(t) = 2 * (1 - t) * (p1 - p0) + 2 * t * (p2 - p1)
		final float dt = 1f - t;
		return out.set(p1).sub(p0).scl(2).scl(1 - t).add(tmp.set(p2).sub(p1).scl(t).scl(2));
	}

	/** Cubic Bezier curve
	 * @param out The {@link Vector} to set to the result.
	 * @param t The location (ranging 0..1) on the curve.
	 * @param p0 The first bezier point.
	 * @param p1 The second bezier point.
	 * @param p2 The third bezier point.
	 * @param p3 The fourth bezier point.
	 * @param tmp A temporary vector to be used by the calculation.
	 * @return The value specified by out for chaining */
	public static > T cubic (final T out, final float t, final T p0, final T p1, final T p2, final T p3,
		final T tmp) {
		// B3(t) = (1-t) * (1-t) * (1-t) * p0 + 3 * (1-t) * (1-t) * t * p1 + 3 * (1-t) * t * t * p2 + t * t * t * p3
		final float dt = 1f - t;
		final float dt2 = dt * dt;
		final float t2 = t * t;
		return out.set(p0).scl(dt2 * dt).add(tmp.set(p1).scl(3 * dt2 * t)).add(tmp.set(p2).scl(3 * dt * t2))
			.add(tmp.set(p3).scl(t2 * t));
	}

	/** Cubic Bezier curve derivative
	 * @param out The {@link Vector} to set to the result.
	 * @param t The location (ranging 0..1) on the curve.
	 * @param p0 The first bezier point.
	 * @param p1 The second bezier point.
	 * @param p2 The third bezier point.
	 * @param p3 The fourth bezier point.
	 * @param tmp A temporary vector to be used by the calculation.
	 * @return The value specified by out for chaining */
	public static > T cubic_derivative (final T out, final float t, final T p0, final T p1, final T p2,
		final T p3, final T tmp) {
		// B3'(t) = 3 * (1-t) * (1-t) * (p1 - p0) + 6 * (1 - t) * t * (p2 - p1) + 3 * t * t * (p3 - p2)
		final float dt = 1f - t;
		final float dt2 = dt * dt;
		final float t2 = t * t;
		return out.set(p1).sub(p0).scl(dt2 * 3).add(tmp.set(p2).sub(p1).scl(dt * t * 6)).add(tmp.set(p3).sub(p2).scl(t2 * 3));
	}

	public Array points = new Array();
	private T tmp;
	private T tmp2;
	private T tmp3;

	public Bezier () {
	}

	public Bezier (final T... points) {
		set(points);
	}

	public Bezier (final T[] points, final int offset, final int length) {
		set(points, offset, length);
	}

	public Bezier (final Array points, final int offset, final int length) {
		set(points, offset, length);
	}

	public Bezier set (final T... points) {
		return set(points, 0, points.length);
	}

	public Bezier set (final T[] points, final int offset, final int length) {
		if (length < 2 || length > 4)
			throw new GdxRuntimeException("Only first, second and third degree Bezier curves are supported.");
		if (tmp == null) tmp = points[0].cpy();
		if (tmp2 == null) tmp2 = points[0].cpy();
		if (tmp3 == null) tmp3 = points[0].cpy();
		this.points.clear();
		this.points.addAll(points, offset, length);
		return this;
	}

	public Bezier set (final Array points, final int offset, final int length) {
		if (length < 2 || length > 4)
			throw new GdxRuntimeException("Only first, second and third degree Bezier curves are supported.");
		if (tmp == null) tmp = points.get(0).cpy();
		this.points.clear();
		this.points.addAll(points, offset, length);
		return this;
	}

	@Override
	public T valueAt (final T out, final float t) {
		final int n = points.size;
		if (n == 2)
			linear(out, t, points.get(0), points.get(1), tmp);
		else if (n == 3)
			quadratic(out, t, points.get(0), points.get(1), points.get(2), tmp);
		else if (n == 4) cubic(out, t, points.get(0), points.get(1), points.get(2), points.get(3), tmp);
		return out;
	}

	@Override
	public T derivativeAt (final T out, final float t) {
		final int n = points.size;
		if (n == 2)
			linear_derivative(out, t, points.get(0), points.get(1), tmp);
		else if (n == 3)
			quadratic_derivative(out, t, points.get(0), points.get(1), points.get(2), tmp);
		else if (n == 4) cubic_derivative(out, t, points.get(0), points.get(1), points.get(2), points.get(3), tmp);
		return out;
	}

	@Override
	public float approximate (final T v) {
		// TODO: make a real approximate method
		T p1 = points.get(0);
		T p2 = points.get(points.size - 1);
		T p3 = v;
		float l1Sqr = p1.dst2(p2);
		float l2Sqr = p3.dst2(p2);
		float l3Sqr = p3.dst2(p1);
		float l1 = (float)Math.sqrt(l1Sqr);
		float s = (l2Sqr + l1Sqr - l3Sqr) / (2 * l1);
		return MathUtils.clamp((l1 - s) / l1, 0f, 1f);
	}

	@Override
	public float locate (T v) {
		// TODO implement a precise method
		return approximate(v);
	}

	@Override
	public float approxLength (int samples) {
		float tempLength = 0;
		for (int i = 0; i < samples; ++i) {
			tmp2.set(tmp3);
			valueAt(tmp3, (i) / ((float)samples - 1));
			if (i > 0) tempLength += tmp2.dst(tmp3);
		}
		return tempLength;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy