Skip to content

Commit 358e00e

Browse files
authored
Merge pull request #2368 from jMonkeyEngine/sgold/issue/2366
solve issue #2366 (bugs related to saving/loading a Spline)
2 parents e6f0c9d + dcdd451 commit 358e00e

File tree

2 files changed

+200
-4
lines changed

2 files changed

+200
-4
lines changed

jme3-core/src/main/java/com/jme3/math/Spline.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2021 jMonkeyEngine
2+
* Copyright (c) 2009-2025 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -480,7 +480,18 @@ public void write(JmeExporter ex) throws IOException {
480480
oc.writeSavableArrayList((ArrayList) CRcontrolPoints, "CRControlPoints", null);
481481
oc.write(curveTension, "curveTension", 0.5f);
482482
oc.write(cycle, "cycle", false);
483-
oc.writeSavableArrayList((ArrayList<Float>) knots, "knots", null);
483+
484+
float[] knotArray;
485+
if (knots == null) {
486+
knotArray = null;
487+
} else {
488+
knotArray = new float[knots.size()];
489+
for (int i = 0; i < knotArray.length; ++i) {
490+
knotArray[i] = knots.get(i);
491+
}
492+
}
493+
oc.write(knotArray, "knots", null);
494+
484495
oc.write(weights, "weights", null);
485496
oc.write(basisFunctionDegree, "basisFunctionDegree", 0);
486497
}
@@ -506,12 +517,22 @@ public void read(JmeImporter im) throws IOException {
506517
segmentsLength.add(list[i]);
507518
}
508519
}
509-
type = in.readEnum("pathSplineType", SplineType.class, SplineType.CatmullRom);
520+
type = in.readEnum("type", SplineType.class, SplineType.CatmullRom);
510521
totalLength = in.readFloat("totalLength", 0);
511522
CRcontrolPoints = in.readSavableArrayList("CRControlPoints", null);
512523
curveTension = in.readFloat("curveTension", 0.5f);
513524
cycle = in.readBoolean("cycle", false);
514-
knots = in.readSavableArrayList("knots", null);
525+
526+
float[] knotArray = in.readFloatArray("knots", null);
527+
if (knotArray == null) {
528+
this.knots = null;
529+
} else {
530+
this.knots = new ArrayList<>(knotArray.length);
531+
for (float knot : knotArray) {
532+
knots.add(knot);
533+
}
534+
}
535+
515536
weights = in.readFloatArray("weights", null);
516537
basisFunctionDegree = in.readInt("basisFunctionDegree", 0);
517538
}
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Copyright (c) 2025 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package com.jme3.math;
33+
34+
import com.jme3.asset.AssetManager;
35+
import com.jme3.asset.DesktopAssetManager;
36+
import com.jme3.export.binary.BinaryExporter;
37+
import java.util.ArrayList;
38+
import java.util.List;
39+
import org.junit.Assert;
40+
import org.junit.Test;
41+
42+
/**
43+
* Verifies that the {@link Spline} class works correctly.
44+
*
45+
* @author Stephen Gold
46+
*/
47+
public class SplineTest {
48+
// *************************************************************************
49+
// fields
50+
51+
private static final AssetManager assetManager = new DesktopAssetManager();
52+
// *************************************************************************
53+
// tests
54+
55+
/**
56+
* Verifies that spline serialization/deserialization works correctly.
57+
*/
58+
@Test
59+
public void saveAndLoadSplines() {
60+
// Serialize and deserialize a Bezier spline:
61+
{
62+
Vector3f[] controlPoints1 = {
63+
new Vector3f(0f, 1f, 0f), new Vector3f(1f, 2f, 1f),
64+
new Vector3f(1.5f, 1.5f, 1.5f), new Vector3f(2f, 0f, 1f)
65+
};
66+
67+
Spline test1 = new Spline(
68+
Spline.SplineType.Bezier, controlPoints1, 0.1f, true);
69+
Spline copy1 = BinaryExporter.saveAndLoad(assetManager, test1);
70+
assertSplineEquals(test1, copy1);
71+
}
72+
73+
// Serialize and deserialize a NURB spline:
74+
{
75+
List<Vector4f> controlPoints2 = new ArrayList<>(5);
76+
controlPoints2.add(new Vector4f(0f, 1f, 2f, 3f));
77+
controlPoints2.add(new Vector4f(3f, 1f, 4f, 0f));
78+
controlPoints2.add(new Vector4f(2f, 5f, 3f, 0f));
79+
controlPoints2.add(new Vector4f(3f, 2f, 3f, 1f));
80+
controlPoints2.add(new Vector4f(0.5f, 1f, 0.6f, 5f));
81+
List<Float> nurbKnots = new ArrayList<>(6);
82+
nurbKnots.add(0.2f);
83+
nurbKnots.add(0.3f);
84+
nurbKnots.add(0.4f);
85+
nurbKnots.add(0.43f);
86+
nurbKnots.add(0.51f);
87+
nurbKnots.add(0.52f);
88+
89+
Spline test2 = new Spline(controlPoints2, nurbKnots);
90+
Spline copy2 = BinaryExporter.saveAndLoad(assetManager, test2);
91+
assertSplineEquals(test2, copy2);
92+
}
93+
94+
// Serialize and deserialize a Catmull-Rom spline:
95+
{
96+
List<Vector3f> controlPoints3 = new ArrayList<>(6);
97+
controlPoints3.add(new Vector3f(0f, 1f, 2f));
98+
controlPoints3.add(new Vector3f(3f, -1f, 4f));
99+
controlPoints3.add(new Vector3f(2f, 5f, 3f));
100+
controlPoints3.add(new Vector3f(3f, -2f, 3f));
101+
controlPoints3.add(new Vector3f(0.5f, 1f, 0.6f));
102+
controlPoints3.add(new Vector3f(-0.5f, 4f, 0.2f));
103+
104+
Spline test3 = new Spline(
105+
Spline.SplineType.CatmullRom, controlPoints3, 0.01f, false);
106+
Spline copy3 = BinaryExporter.saveAndLoad(assetManager, test3);
107+
assertSplineEquals(test3, copy3);
108+
}
109+
110+
// Serialize and deserialize a linear spline:
111+
{
112+
List<Vector3f> controlPoints4 = new ArrayList<>(3);
113+
controlPoints4.add(new Vector3f(3f, -1f, 4f));
114+
controlPoints4.add(new Vector3f(2f, 0f, 3f));
115+
controlPoints4.add(new Vector3f(3f, -2f, 3f));
116+
117+
Spline test4 = new Spline(
118+
Spline.SplineType.Linear, controlPoints4, 0f, true);
119+
Spline copy4 = BinaryExporter.saveAndLoad(assetManager, test4);
120+
assertSplineEquals(test4, copy4);
121+
}
122+
123+
// Serialize and deserialize a default spline:
124+
{
125+
Spline test5 = new Spline();
126+
Spline copy5 = BinaryExporter.saveAndLoad(assetManager, test5);
127+
assertSplineEquals(test5, copy5);
128+
}
129+
}
130+
// *************************************************************************
131+
// private helper methods
132+
133+
/**
134+
* Verify that the specified lists are equivalent.
135+
*
136+
* @param s1 the first list to compare (may be null, unaffected)
137+
* @param s2 the 2nd list to compare (may be null, unaffected)
138+
*/
139+
private static void assertListEquals(List<?> a1, List<?> a2) {
140+
if (a1 != a2) {
141+
Assert.assertEquals(a1.size(), a2.size());
142+
for (int i = 0; i < a1.size(); ++i) {
143+
Assert.assertEquals(a1.get(i), a2.get(i));
144+
}
145+
}
146+
}
147+
148+
/**
149+
* Verify that the specified splines are equivalent.
150+
*
151+
* @param s1 the first spline to compare (not null, unaffected)
152+
* @param s2 the 2nd split to compare (not null, unaffected)
153+
*/
154+
private static void assertSplineEquals(Spline s1, Spline s2) {
155+
Assert.assertEquals(s1.getType(), s2.getType());
156+
Assert.assertEquals(s1.isCycle(), s2.isCycle());
157+
158+
Assert.assertEquals(
159+
s1.getBasisFunctionDegree(), s2.getBasisFunctionDegree());
160+
assertListEquals(s1.getControlPoints(), s2.getControlPoints());
161+
Assert.assertEquals(s1.getCurveTension(), s2.getCurveTension(), 0f);
162+
assertListEquals(s1.getKnots(), s2.getKnots());
163+
164+
if (s1.getType() == Spline.SplineType.Nurb) {
165+
// These methods throw NPEs on non-NURB splines.
166+
Assert.assertEquals(s1.getMaxNurbKnot(), s2.getMaxNurbKnot(), 0f);
167+
Assert.assertEquals(s1.getMinNurbKnot(), s2.getMinNurbKnot(), 0f);
168+
}
169+
170+
assertListEquals(s1.getSegmentsLength(), s2.getSegmentsLength());
171+
Assert.assertEquals(
172+
s1.getTotalLength(), s2.getTotalLength(), 0f);
173+
Assert.assertArrayEquals(s1.getWeights(), s2.getWeights(), 0f);
174+
}
175+
}

0 commit comments

Comments
 (0)