Skip to content

Instantly share code, notes, and snippets.

@Sxtanna
Last active July 4, 2020 08:05
Show Gist options
  • Save Sxtanna/5c0ed9de175a7b417198e493dd6db5c1 to your computer and use it in GitHub Desktop.
Save Sxtanna/5c0ed9de175a7b417198e493dd6db5c1 to your computer and use it in GitHub Desktop.
Scaled Bezier Curve
/**
* Automatically scaled Bezier Curve calculator.
* <p>
* adapted from: https://www.algosome.com/articles/continuous-bezier-curve-line.html
*/
public final class Curve
{
public static final class Vec
{
public final double x;
public final double y;
public final double z;
public Vec(final double x, final double y, final double z)
{
this.x = x;
this.y = y;
this.z = z;
}
public Vec(@NotNull final Number x, @NotNull final Number y, @NotNull final Number z)
{
this(x.doubleValue(), y.doubleValue(), z.doubleValue());
}
@Override
public String toString()
{
return String.format("Vec[x:%s, y:%s, z:%s]", x, y, z);
}
@Override
public boolean equals(final Object o)
{
if (this == o)
{
return true;
}
if (!(o instanceof Vec))
{
return false;
}
final Vec vec = (Vec) o;
return Double.compare(vec.x, x) == 0 && Double.compare(vec.y, y) == 0 && Double.compare(vec.z, z) == 0;
}
@Override
public int hashCode()
{
return Objects.hash(x, y, z);
}
}
private Curve() {}
@NotNull
public static Collection<Vec> getCurvePoints(final double detail, @NotNull final Collection<Vec> points)
{
return getCurvePoints(detail, points.toArray(new Vec[0]));
}
@NotNull
public static Collection<Vec> getCurvePoints(final double detail, @NotNull final Vec... points)
{
if (points.length == 0)
{
return Collections.emptyList();
}
if (detail < 0.0 || detail > 1.0)
{
throw new IllegalArgumentException(String.format("Detail value (%s) out of range [0.0:1.0]", detail));
}
final List<Vec> outputs = new ArrayList<Vec>();
int index = 0;
while (true)
{
final Vec point0 = points[index];
if (points.length <= index + 1)
{
break;
}
final Vec point1 = points[++index];
if (points.length <= index + 1)
{
// line
for (double step = 0; step <= 1; step += detail)
{
outputs.add(lineBezier(point0, point1, step));
}
break;
}
final Vec point2 = points[++index];
if (points.length <= index + 1)
{
// quad
for (double step = 0; step <= 1; step += detail)
{
outputs.add(quadBezier(point0, point1, point2, step));
}
break;
}
final Vec point3 = points[++index];
// cube
for (double step = 0; step <= 1; step += detail)
{
outputs.add(cubeBezier(point0, point1, point2, point3, step));
}
}
return outputs;
}
@NotNull
@Contract("_, _, _ -> new")
private static Vec lineBezier(@NotNull Vec p1, @NotNull Vec p2, double step)
{
return new Vec(lineBezierPoint(p1.x, p2.x, step),
lineBezierPoint(p1.y, p2.y, step),
lineBezierPoint(p1.z, p2.z, step));
}
@NotNull
@Contract("_, _, _, _ -> new")
private static Vec quadBezier(@NotNull Vec p1, @NotNull Vec p2, @NotNull Vec p3, double step)
{
return new Vec(quadBezierPoint(p1.x, p2.x, p3.x, step),
quadBezierPoint(p1.y, p2.y, p3.y, step),
quadBezierPoint(p1.z, p2.z, p3.z, step));
}
@NotNull
@Contract("_, _, _, _, _ -> new")
private static Vec cubeBezier(@NotNull Vec p1, @NotNull Vec p2, @NotNull Vec p3, @NotNull Vec p4, double step)
{
return new Vec(cubeBezierPoint(p1.x, p2.x, p3.x, p4.x, step),
cubeBezierPoint(p1.y, p2.y, p3.y, p4.y, step),
cubeBezierPoint(p1.z, p2.z, p3.z, p4.z, step));
}
private static double lineBezierPoint(double point0, double point1, double step)
{
final double for0 = point0 * (1.0 - step);
final double for1 = point1 * (step);
return for0 + for1;
}
private static double quadBezierPoint(double point0, double point1, double point2, double step)
{
final double for0 = point0 * (Math.pow(1.0 - step, 2.0));
final double for1 = point1 * (2.0 * (1.0 - step) * step);
final double for2 = point2 * (Math.pow(step, 2.0));
return for0 + for1 + for2;
}
private static double cubeBezierPoint(double point0, double point1, double point2, double point3, double step)
{
final double for0 = point0 * (Math.pow(1.0 - step, 3.0));
final double for1 = point1 * (3.0 * Math.pow(1.0 - step, 2.0) * step);
final double for2 = point2 * (3.0 * (1.0 - step) * Math.pow(step, 2.0));
final double for3 = point3 * (Math.pow(step, 3.0));
return for0 + for1 + for2 + for3;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment