Skip to content

Commit 5ca1dbf

Browse files
author
david
committed
use vectors
clarified field, parameter and method names extracted side effects were possible cleanup
1 parent 8a39845 commit 5ca1dbf

File tree

2 files changed

+138
-145
lines changed

2 files changed

+138
-145
lines changed

src/main/java/net/thenextlvl/gopaint/utils/curve/BezierSpline.java

Lines changed: 78 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -18,155 +18,135 @@
1818
*/
1919
package net.thenextlvl.gopaint.utils.curve;
2020

21-
import org.bukkit.Location;
21+
import lombok.Getter;
22+
import org.bukkit.util.Vector;
23+
import org.jetbrains.annotations.Contract;
2224

25+
import java.util.Arrays;
2326
import java.util.LinkedList;
27+
import java.util.OptionalDouble;
2428

29+
@Getter
2530
public class BezierSpline {
2631

27-
private final LinkedList<Location> knotsList;
28-
private Location[] knots;
29-
private BezierSplineSegment[] segments;
30-
private double length = 0;
32+
private final Vector[] knots;
33+
private final BezierSplineSegment[] segments;
34+
private final double curveLength;
3135

32-
public BezierSpline(LinkedList<Location> knotsList) {
33-
this.knotsList = knotsList;
34-
recalculate();
35-
}
36-
37-
private void recalculate() {
38-
knots = knotsList.toArray(new Location[0]);
39-
segments = new BezierSplineSegment[knots.length - 1];
40-
for (int i = 0; i < knots.length - 1; i++) {
41-
segments[i] = new BezierSplineSegment(knots[i], knots[i + 1]);
36+
public BezierSpline(LinkedList<Vector> curve) {
37+
this.knots = curve.toArray(new Vector[0]);
38+
this.segments = new BezierSplineSegment[knots.length - 1];
39+
for (var segment = 0; segment < knots.length - 1; segment++) {
40+
segments[segment] = new BezierSplineSegment(knots[segment], knots[segment + 1]);
4241
}
4342
calculateControlPoints();
44-
calculateLength();
45-
}
46-
47-
public double getCurveLength() {
48-
return length;
43+
this.curveLength = calculateLength();
4944
}
5045

51-
public void calculateLength() {
52-
length = 0;
53-
for (BezierSplineSegment segment : segments) {
54-
segment.calculateCurveLength();
46+
@Contract(pure = true)
47+
public double calculateLength() {
48+
var length = this.curveLength;
49+
for (var segment : segments) {
5550
length += segment.getCurveLength();
5651
}
52+
return length;
5753
}
5854

59-
public Location getPoint(double point) {
55+
@Contract(pure = true)
56+
public Vector getPoint(double point) {
6057
if (point >= segments.length) {
6158
return getPoint(segments.length - 1, 1);
6259
} else {
6360
return getPoint((int) Math.floor(point), point - Math.floor(point));
6461
}
6562
}
6663

67-
public Location getPoint(int n, double f) {
68-
assert (n < segments.length);
69-
assert (0 <= f && f <= 1);
70-
BezierSplineSegment segment = segments[n];
71-
return segment.getPoint(f);
64+
@Contract(pure = true)
65+
public Vector getPoint(int segmentIndex, double factor) {
66+
assert (segmentIndex < segments.length);
67+
assert (factor > 0 && factor <= 1);
68+
return segments[segmentIndex].getPoint(factor);
7269
}
7370

7471
public void calculateControlPoints() {
75-
if (segments == null) return;
7672
if (segments.length == 0) return;
7773

78-
Double xflat, yflat, zflat;
79-
xflat = knots[0].getX();
80-
yflat = knots[0].getY();
81-
zflat = knots[0].getZ();
82-
for (Location l : knots) {
83-
if (l.getBlockX() != xflat) {
84-
xflat = null;
85-
break;
86-
}
87-
}
88-
for (Location l : knots) {
89-
if (l.getBlockY() != yflat) {
90-
yflat = null;
91-
break;
92-
}
93-
}
94-
for (Location l : knots) {
95-
if (l.getBlockZ() != zflat) {
96-
zflat = null;
97-
break;
98-
}
99-
}
74+
var xFlat = Arrays.stream(knots).allMatch(l -> l.getBlockX() == knots[0].getX())
75+
? OptionalDouble.of(knots[0].getX()) : OptionalDouble.empty();
76+
var yFlat = Arrays.stream(knots).allMatch(l -> l.getBlockY() == knots[0].getY())
77+
? OptionalDouble.of(knots[0].getY()) : OptionalDouble.empty();
78+
var zFlat = Arrays.stream(knots).allMatch(l -> l.getBlockZ() == knots[0].getZ())
79+
? OptionalDouble.of(knots[0].getZ()) : OptionalDouble.empty();
10080

10181
if (segments.length == 1) {
102-
Location midpoint = new Location(segments[0].getP0().getWorld(), 0, 0, 0);
103-
midpoint.setX((segments[0].getP0().getX() + segments[0].getP3().getX()) / 2);
104-
midpoint.setY((segments[0].getP0().getY() + segments[0].getP3().getY()) / 2);
105-
midpoint.setZ((segments[0].getP0().getZ() + segments[0].getP3().getZ()) / 2);
106-
segments[0].setP1(midpoint);
107-
segments[0].setP2(midpoint.clone());
82+
var midpoint = new Vector(0, 0, 0);
83+
midpoint.setX((segments[0].getStartPoint().getX() + segments[0].getEndPoint().getX()) / 2);
84+
midpoint.setY((segments[0].getStartPoint().getY() + segments[0].getEndPoint().getY()) / 2);
85+
midpoint.setZ((segments[0].getStartPoint().getZ() + segments[0].getEndPoint().getZ()) / 2);
86+
segments[0].setIntermediatePoint1(midpoint);
87+
segments[0].setIntermediatePoint2(midpoint.clone());
10888
} else {
109-
segments[0].setA(0);
110-
segments[0].setB(2);
111-
segments[0].setC(1);
112-
segments[0].getR().setX(knots[0].getX() + 2 * knots[1].getX());
113-
segments[0].getR().setY(knots[0].getY() + 2 * knots[1].getY());
114-
segments[0].getR().setZ(knots[0].getZ() + 2 * knots[1].getZ());
89+
segments[0].setCoefficient1(0);
90+
segments[0].setCoefficient2(2);
91+
segments[0].setCoefficient3(1);
92+
segments[0].getResult().setX(knots[0].getX() + 2 * knots[1].getX());
93+
segments[0].getResult().setY(knots[0].getY() + 2 * knots[1].getY());
94+
segments[0].getResult().setZ(knots[0].getZ() + 2 * knots[1].getZ());
11595

11696
int n = knots.length - 1;
11797
float m;
11898

11999
for (int i = 1; i < n - 1; i++) {
120-
segments[i].setA(1);
121-
segments[i].setB(4);
122-
segments[i].setC(1);
123-
segments[i].getR().setX(4 * knots[i].getX() + 2 * knots[i + 1].getX());
124-
segments[i].getR().setY(4 * knots[i].getY() + 2 * knots[i + 1].getY());
125-
segments[i].getR().setZ(4 * knots[i].getZ() + 2 * knots[i + 1].getZ());
100+
segments[i].setCoefficient1(1);
101+
segments[i].setCoefficient2(4);
102+
segments[i].setCoefficient3(1);
103+
segments[i].getResult().setX(4 * knots[i].getX() + 2 * knots[i + 1].getX());
104+
segments[i].getResult().setY(4 * knots[i].getY() + 2 * knots[i + 1].getY());
105+
segments[i].getResult().setZ(4 * knots[i].getZ() + 2 * knots[i + 1].getZ());
126106
}
127107

128-
segments[n - 1].setA(2);
129-
segments[n - 1].setB(7);
130-
segments[n - 1].setC(0);
131-
segments[n - 1].getR().setX(8 * knots[n - 1].getX() + knots[n].getX());
132-
segments[n - 1].getR().setY(8 * knots[n - 1].getY() + knots[n].getY());
133-
segments[n - 1].getR().setZ(8 * knots[n - 1].getZ() + knots[n].getZ());
108+
segments[n - 1].setCoefficient1(2);
109+
segments[n - 1].setCoefficient2(7);
110+
segments[n - 1].setCoefficient3(0);
111+
segments[n - 1].getResult().setX(8 * knots[n - 1].getX() + knots[n].getX());
112+
segments[n - 1].getResult().setY(8 * knots[n - 1].getY() + knots[n].getY());
113+
segments[n - 1].getResult().setZ(8 * knots[n - 1].getZ() + knots[n].getZ());
134114

135115
for (int i = 1; i < n; i++) {
136-
m = segments[i].getA() / segments[i - 1].getB();
137-
segments[i].setB(segments[i].getB() - m * segments[i - 1].getC());
138-
segments[i].getR().setX(segments[i].getR().getX() - m * segments[i - 1].getR().getX());
139-
segments[i].getR().setY(segments[i].getR().getY() - m * segments[i - 1].getR().getY());
140-
segments[i].getR().setZ(segments[i].getR().getZ() - m * segments[i - 1].getR().getZ());
116+
m = segments[i].getCoefficient1() / segments[i - 1].getCoefficient2();
117+
segments[i].setCoefficient2(segments[i].getCoefficient2() - m * segments[i - 1].getCoefficient3());
118+
segments[i].getResult().setX(segments[i].getResult().getX() - m * segments[i - 1].getResult().getX());
119+
segments[i].getResult().setY(segments[i].getResult().getY() - m * segments[i - 1].getResult().getY());
120+
segments[i].getResult().setZ(segments[i].getResult().getZ() - m * segments[i - 1].getResult().getZ());
141121
}
142122

143-
segments[n - 1].getP1().setX(segments[n - 1].getR().getX() / segments[n - 1].getB());
144-
segments[n - 1].getP1().setY(segments[n - 1].getR().getY() / segments[n - 1].getB());
145-
segments[n - 1].getP1().setZ(segments[n - 1].getR().getZ() / segments[n - 1].getB());
123+
segments[n - 1].getIntermediatePoint1().setX(segments[n - 1].getResult().getX() / segments[n - 1].getCoefficient2());
124+
segments[n - 1].getIntermediatePoint1().setY(segments[n - 1].getResult().getY() / segments[n - 1].getCoefficient2());
125+
segments[n - 1].getIntermediatePoint1().setZ(segments[n - 1].getResult().getZ() / segments[n - 1].getCoefficient2());
146126

147127
for (int i = n - 2; i >= 0; i--) {
148-
segments[i].getP1().setX((segments[i].getR().getX() - segments[i].getC() * segments[i + 1].getP1().getX()) / segments[i].getB());
149-
segments[i].getP1().setY((segments[i].getR().getY() - segments[i].getC() * segments[i + 1].getP1().getY()) / segments[i].getB());
150-
segments[i].getP1().setZ((segments[i].getR().getZ() - segments[i].getC() * segments[i + 1].getP1().getZ()) / segments[i].getB());
128+
segments[i].getIntermediatePoint1().setX((segments[i].getResult().getX() - segments[i].getCoefficient3() * segments[i + 1].getIntermediatePoint1().getX()) / segments[i].getCoefficient2());
129+
segments[i].getIntermediatePoint1().setY((segments[i].getResult().getY() - segments[i].getCoefficient3() * segments[i + 1].getIntermediatePoint1().getY()) / segments[i].getCoefficient2());
130+
segments[i].getIntermediatePoint1().setZ((segments[i].getResult().getZ() - segments[i].getCoefficient3() * segments[i + 1].getIntermediatePoint1().getZ()) / segments[i].getCoefficient2());
151131
}
152132

153133
for (int i = 0; i < n - 1; i++) {
154-
segments[i].getP2().setX(2 * knots[i + 1].getX() - segments[i + 1].getP1().getX());
155-
segments[i].getP2().setY(2 * knots[i + 1].getY() - segments[i + 1].getP1().getY());
156-
segments[i].getP2().setZ(2 * knots[i + 1].getZ() - segments[i + 1].getP1().getZ());
134+
segments[i].getIntermediatePoint2().setX(2 * knots[i + 1].getX() - segments[i + 1].getIntermediatePoint1().getX());
135+
segments[i].getIntermediatePoint2().setY(2 * knots[i + 1].getY() - segments[i + 1].getIntermediatePoint1().getY());
136+
segments[i].getIntermediatePoint2().setZ(2 * knots[i + 1].getZ() - segments[i + 1].getIntermediatePoint1().getZ());
157137
}
158-
segments[n - 1].getP2().setX(0.5 * (knots[n].getX() + segments[n - 1].getP1().getX()));
159-
segments[n - 1].getP2().setY(0.5 * (knots[n].getY() + segments[n - 1].getP1().getY()));
160-
segments[n - 1].getP2().setZ(0.5 * (knots[n].getZ() + segments[n - 1].getP1().getZ()));
138+
segments[n - 1].getIntermediatePoint2().setX(0.5 * (knots[n].getX() + segments[n - 1].getIntermediatePoint1().getX()));
139+
segments[n - 1].getIntermediatePoint2().setY(0.5 * (knots[n].getY() + segments[n - 1].getIntermediatePoint1().getY()));
140+
segments[n - 1].getIntermediatePoint2().setZ(0.5 * (knots[n].getZ() + segments[n - 1].getIntermediatePoint1().getZ()));
161141
}
162142

163-
if (xflat != null) for (BezierSplineSegment cs : segments) cs.setX(xflat);
164-
if (yflat != null) for (BezierSplineSegment cs : segments) cs.setY(yflat);
165-
if (zflat != null) for (BezierSplineSegment cs : segments) cs.setZ(zflat);
143+
xFlat.ifPresent(value -> Arrays.stream(segments).forEach(segment -> segment.setX(value)));
144+
yFlat.ifPresent(value -> Arrays.stream(segments).forEach(segment -> segment.setY(value)));
145+
zFlat.ifPresent(value -> Arrays.stream(segments).forEach(segment -> segment.setZ(value)));
166146
}
167147

168148
@Override
169149
public String toString() {
170-
return (knots != null ? knots.length : 0) + " points.";
150+
return knots.length + " points.";
171151
}
172152
}

src/main/java/net/thenextlvl/gopaint/utils/curve/BezierSplineSegment.java

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,73 +19,86 @@
1919
package net.thenextlvl.gopaint.utils.curve;
2020

2121
import lombok.Getter;
22+
import lombok.RequiredArgsConstructor;
2223
import lombok.Setter;
23-
import org.bukkit.Location;
24+
import org.bukkit.util.Vector;
25+
import org.jetbrains.annotations.Contract;
2426
import org.jetbrains.annotations.Nullable;
2527

2628
import java.util.Objects;
2729

2830
@Getter
2931
@Setter
32+
@RequiredArgsConstructor
3033
public class BezierSplineSegment {
3134

32-
private final double[] lengths = new double[20];
33-
private Location p0, p1, p2, p3;
34-
private float a, b, c;
35+
private final Vector startPoint;
36+
private final Vector endPoint;
37+
38+
private Vector intermediatePoint1 = new Vector(0, 0, 0);
39+
private Vector intermediatePoint2 = new Vector(0, 0, 0);
40+
41+
private float coefficient1;
42+
private float coefficient2;
43+
private float coefficient3;
44+
3545
private @Nullable Double xFlat, yFlat, zFlat;
36-
private Location r;
37-
private double curveLength;
3846

39-
public BezierSplineSegment(Location p0, Location p3) {
40-
this.p0 = p0;
41-
this.p3 = p3;
42-
p1 = new Location(p0.getWorld(), 0, 0, 0);
43-
p2 = new Location(p0.getWorld(), 0, 0, 0);
44-
r = new Location(p0.getWorld(), 0, 0, 0);
45-
}
47+
private Vector result = new Vector(0, 0, 0);
4648

47-
public void setX(double xflat2) {
48-
p0.setX(xflat2);
49-
p1.setX(xflat2);
50-
p2.setX(xflat2);
51-
p3.setX(xflat2);
52-
xFlat = xflat2;
49+
public void setX(double xFlat) {
50+
startPoint.setX(xFlat);
51+
intermediatePoint1.setX(xFlat);
52+
intermediatePoint2.setX(xFlat);
53+
endPoint.setX(xFlat);
54+
this.xFlat = xFlat;
5355
}
5456

55-
public void setY(double yflat2) {
56-
p0.setY(yflat2);
57-
p1.setY(yflat2);
58-
p2.setY(yflat2);
59-
p3.setY(yflat2);
60-
yFlat = yflat2;
57+
public void setY(double yFlat) {
58+
startPoint.setY(yFlat);
59+
intermediatePoint1.setY(yFlat);
60+
intermediatePoint2.setY(yFlat);
61+
endPoint.setY(yFlat);
62+
this.yFlat = yFlat;
6163
}
6264

63-
public void setZ(double zflat2) {
64-
p0.setZ(zflat2);
65-
p1.setZ(zflat2);
66-
p2.setZ(zflat2);
67-
p3.setZ(zflat2);
68-
zFlat = zflat2;
65+
public void setZ(double zFlat) {
66+
startPoint.setZ(zFlat);
67+
intermediatePoint1.setZ(zFlat);
68+
intermediatePoint2.setZ(zFlat);
69+
endPoint.setZ(zFlat);
70+
this.zFlat = zFlat;
6971
}
7072

71-
public void calculateCurveLength() {
72-
Location current = p0.clone();
73-
double step = 0.05;
74-
lengths[0] = 0;
75-
Location temp;
76-
for (int i = 1; i < 20; i++) {
77-
temp = getPoint(i * step);
78-
lengths[i] = lengths[i - 1] + temp.distance(current);
79-
current = temp;
73+
@Contract(pure = true)
74+
public double getCurveLength() {
75+
var current = startPoint.clone();
76+
var lengths = new double[20];
77+
for (int i = 1; i < lengths.length; i++) {
78+
var point = getPoint(i * 0.05);
79+
lengths[i] = lengths[i - 1] + point.distance(current);
80+
current = point;
8081
}
81-
curveLength = lengths[19];
82+
return lengths[lengths.length - 1];
83+
}
84+
85+
@Contract(pure = true)
86+
public Vector getPoint(double factor) {
87+
var x = Objects.requireNonNullElseGet(xFlat, () -> calculatePoint(
88+
factor, startPoint.getX(), intermediatePoint1.getX(), intermediatePoint2.getX(), endPoint.getX()
89+
));
90+
var y = Objects.requireNonNullElseGet(yFlat, () -> calculatePoint(
91+
factor, startPoint.getY(), intermediatePoint1.getY(), intermediatePoint2.getY(), endPoint.getY()
92+
));
93+
var z = Objects.requireNonNullElseGet(zFlat, () -> calculatePoint(
94+
factor, startPoint.getZ(), intermediatePoint1.getZ(), intermediatePoint2.getZ(), endPoint.getZ()
95+
));
96+
return new Vector(x, y, z);
8297
}
8398

84-
public Location getPoint(double f) {
85-
Location result = new Location(p0.getWorld(), 0, 0, 0);
86-
result.setX(Objects.requireNonNullElseGet(xFlat, () -> (Math.pow(1 - f, 3) * p0.getX()) + (3 * Math.pow(1 - f, 2) * f * p1.getX()) + (3 * (1 - f) * f * f * p2.getX()) + (Math.pow(f, 3) * p3.getX())));
87-
result.setY(Objects.requireNonNullElseGet(yFlat, () -> (Math.pow(1 - f, 3) * p0.getY()) + (3 * Math.pow(1 - f, 2) * f * p1.getY()) + (3 * (1 - f) * f * f * p2.getY()) + (Math.pow(f, 3) * p3.getY())));
88-
result.setZ(Objects.requireNonNullElseGet(zFlat, () -> (Math.pow(1 - f, 3) * p0.getZ()) + (3 * Math.pow(1 - f, 2) * f * p1.getZ()) + (3 * (1 - f) * f * f * p2.getZ()) + (Math.pow(f, 3) * p3.getZ())));
89-
return result;
99+
@Contract(pure = true)
100+
private double calculatePoint(double factor, double startPoint, double intermediatePoint1, double intermediatePoint2, double endPoint) {
101+
return (Math.pow(1 - factor, 3) * startPoint) + (3 * Math.pow(1 - factor, 2) * factor * intermediatePoint1)
102+
+ (3 * (1 - factor) * factor * factor * intermediatePoint2) + (Math.pow(factor, 3) * endPoint);
90103
}
91104
}

0 commit comments

Comments
 (0)