|
18 | 18 | */
|
19 | 19 | package net.thenextlvl.gopaint.utils.curve;
|
20 | 20 |
|
21 |
| -import org.bukkit.Location; |
| 21 | +import lombok.Getter; |
| 22 | +import org.bukkit.util.Vector; |
| 23 | +import org.jetbrains.annotations.Contract; |
22 | 24 |
|
| 25 | +import java.util.Arrays; |
23 | 26 | import java.util.LinkedList;
|
| 27 | +import java.util.OptionalDouble; |
24 | 28 |
|
| 29 | +@Getter |
25 | 30 | public class BezierSpline {
|
26 | 31 |
|
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; |
31 | 35 |
|
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]); |
42 | 41 | }
|
43 | 42 | calculateControlPoints();
|
44 |
| - calculateLength(); |
45 |
| - } |
46 |
| - |
47 |
| - public double getCurveLength() { |
48 |
| - return length; |
| 43 | + this.curveLength = calculateLength(); |
49 | 44 | }
|
50 | 45 |
|
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) { |
55 | 50 | length += segment.getCurveLength();
|
56 | 51 | }
|
| 52 | + return length; |
57 | 53 | }
|
58 | 54 |
|
59 |
| - public Location getPoint(double point) { |
| 55 | + @Contract(pure = true) |
| 56 | + public Vector getPoint(double point) { |
60 | 57 | if (point >= segments.length) {
|
61 | 58 | return getPoint(segments.length - 1, 1);
|
62 | 59 | } else {
|
63 | 60 | return getPoint((int) Math.floor(point), point - Math.floor(point));
|
64 | 61 | }
|
65 | 62 | }
|
66 | 63 |
|
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); |
72 | 69 | }
|
73 | 70 |
|
74 | 71 | public void calculateControlPoints() {
|
75 |
| - if (segments == null) return; |
76 | 72 | if (segments.length == 0) return;
|
77 | 73 |
|
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(); |
100 | 80 |
|
101 | 81 | 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()); |
108 | 88 | } 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()); |
115 | 95 |
|
116 | 96 | int n = knots.length - 1;
|
117 | 97 | float m;
|
118 | 98 |
|
119 | 99 | 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()); |
126 | 106 | }
|
127 | 107 |
|
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()); |
134 | 114 |
|
135 | 115 | 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()); |
141 | 121 | }
|
142 | 122 |
|
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()); |
146 | 126 |
|
147 | 127 | 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()); |
151 | 131 | }
|
152 | 132 |
|
153 | 133 | 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()); |
157 | 137 | }
|
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())); |
161 | 141 | }
|
162 | 142 |
|
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))); |
166 | 146 | }
|
167 | 147 |
|
168 | 148 | @Override
|
169 | 149 | public String toString() {
|
170 |
| - return (knots != null ? knots.length : 0) + " points."; |
| 150 | + return knots.length + " points."; |
171 | 151 | }
|
172 | 152 | }
|
0 commit comments