Last active
July 4, 2020 08:05
-
-
Save Sxtanna/5c0ed9de175a7b417198e493dd6db5c1 to your computer and use it in GitHub Desktop.
Scaled Bezier Curve
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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