Fix deleting last node in spline

The last node needs to have distance "0" to next node. Do the inverse of
when adding nodes.

Also add some basic tests covering this bug.

Fixes: #4659
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
This commit is contained in:
Ralph Sennhauser 2026-07-29 19:07:22 +02:00
parent df72c1aad1
commit 53d1ae00f1
No known key found for this signature in database
2 changed files with 122 additions and 0 deletions

View file

@ -247,6 +247,8 @@ void TNSpline::RemoveNode(const int index)
if (m_NodeCount == 0 || index > m_NodeCount - 1)
return;
if (index > 0)
std::swap(m_Nodes[index].Distance, m_Nodes[index - 1].Distance);
m_MaxDistance -= m_Nodes[index].Distance;
m_Nodes.erase(m_Nodes.begin() + index);
--m_NodeCount;

View file

@ -0,0 +1,120 @@
/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lib/self_test.h"
#include "maths/Fixed.h"
#include "maths/NUSpline.h"
#define TS_ASSERT_VEC_EQUALS(v, x, y, z) \
TS_ASSERT_EQUALS(v.X, x); \
TS_ASSERT_EQUALS(v.Y, y); \
TS_ASSERT_EQUALS(v.Z, z);
class TestNUSpline : public CxxTest::TestSuite
{
public:
void test_TNSAddNode()
{
TNSpline spline{};
CFixedVector3D p0{fixed::FromInt(0), fixed::FromInt(0), fixed::FromInt(0)};
CFixedVector3D r0{fixed::FromInt(1), fixed::FromInt(1), fixed::FromInt(1)};
fixed d0 = fixed::FromInt(33);
CFixedVector3D p1{fixed::FromInt(1), fixed::FromInt(1), fixed::FromInt(1)};
CFixedVector3D r1{fixed::FromInt(2), fixed::FromInt(2), fixed::FromInt(2)};
fixed d1 = fixed::FromInt(4);
CFixedVector3D p2{fixed::FromInt(2), fixed::FromInt(2), fixed::FromInt(2)};
CFixedVector3D r2{fixed::FromInt(3), fixed::FromInt(3), fixed::FromInt(3)};
fixed d2 = fixed::FromInt(12);
spline.AddNode(p0, r0, d0);
spline.AddNode(p1, r1, d1);
spline.AddNode(p2, r2, d2);
TS_ASSERT_VEC_EQUALS(spline.GetPosition(0.f), 0, 0, 0);
TS_ASSERT_VEC_EQUALS(spline.GetPosition(0.25f), 1, 1, 1);
TS_ASSERT_VEC_EQUALS(spline.GetPosition(1.f), 2, 2, 2);
TS_ASSERT_EQUALS(spline.GetMaxDistance(), fixed::FromInt(16));
}
void test_TNSInsertNode()
{
TNSpline spline{};
CFixedVector3D p0{fixed::FromInt(0), fixed::FromInt(0), fixed::FromInt(0)};
CFixedVector3D r0{fixed::FromInt(1), fixed::FromInt(1), fixed::FromInt(1)};
fixed d0 = fixed::FromInt(4);
CFixedVector3D p1{fixed::FromInt(1), fixed::FromInt(1), fixed::FromInt(1)};
CFixedVector3D r1{fixed::FromInt(2), fixed::FromInt(2), fixed::FromInt(2)};
fixed d1 = fixed::FromInt(33);
CFixedVector3D p2{fixed::FromInt(2), fixed::FromInt(2), fixed::FromInt(2)};
CFixedVector3D r2{fixed::FromInt(3), fixed::FromInt(3), fixed::FromInt(3)};
fixed d2 = fixed::FromInt(12);
spline.InsertNode(0, p1, r1, d1);
spline.InsertNode(0, p0, r0, d0);
spline.InsertNode(2, p2, r2, d2);
TS_ASSERT_VEC_EQUALS(spline.GetPosition(0.f), 0, 0, 0);
TS_ASSERT_VEC_EQUALS(spline.GetPosition(0.25f), 1, 1, 1);
TS_ASSERT_VEC_EQUALS(spline.GetPosition(1.f), 2, 2, 2);
TS_ASSERT_EQUALS(spline.GetMaxDistance(), fixed::FromInt(16));
}
void test_TNSRemoveNode()
{
TNSpline spline{};
CFixedVector3D p0{fixed::FromInt(0), fixed::FromInt(0), fixed::FromInt(0)};
CFixedVector3D r0{fixed::FromInt(1), fixed::FromInt(1), fixed::FromInt(1)};
fixed d0 = fixed::FromInt(33);
CFixedVector3D p1{fixed::FromInt(1), fixed::FromInt(1), fixed::FromInt(1)};
CFixedVector3D r1{fixed::FromInt(2), fixed::FromInt(2), fixed::FromInt(2)};
fixed d1 = fixed::FromInt(4);
CFixedVector3D p2{fixed::FromInt(2), fixed::FromInt(2), fixed::FromInt(2)};
CFixedVector3D r2{fixed::FromInt(3), fixed::FromInt(3), fixed::FromInt(3)};
fixed d2 = fixed::FromInt(12);
spline.AddNode(p0, r0, d0);
spline.AddNode(p1, r1, d1);
spline.AddNode(p2, r2, d2);
spline.RemoveNode(2);
TS_ASSERT_VEC_EQUALS(spline.GetPosition(0.f), 0, 0, 0);
TS_ASSERT_VEC_EQUALS(spline.GetPosition(1.f), 1, 1, 1);
TS_ASSERT_EQUALS(spline.GetMaxDistance(), fixed::FromInt(4));
spline.RemoveNode(0);
TS_ASSERT_EQUALS(spline.GetMaxDistance(), fixed::FromInt(0));
spline.AddNode(p2, r2, d2);
TS_ASSERT_VEC_EQUALS(spline.GetPosition(0.f), 1, 1, 1);
TS_ASSERT_VEC_EQUALS(spline.GetPosition(1.f), 2, 2, 2);
TS_ASSERT_EQUALS(spline.GetMaxDistance(), fixed::FromInt(12));
}
};