Skip to content

Commit 5927021

Browse files
authored
feat: Add RangedVector3 class (#2308)
1 parent ce5f084 commit 5927021

File tree

5 files changed

+193
-0
lines changed

5 files changed

+193
-0
lines changed

src/math/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ add_library(
6565
praxis.h
6666
range.cpp
6767
range.h
68+
rangedVector3.cpp
69+
rangedVector3.h
6870
regression.cpp
6971
regression.h
7072
sampledData1D.cpp

src/math/rangedVector3.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
// Copyright (c) 2026 Team Dissolve and contributors
3+
4+
#include "math/rangedVector3.h"
5+
6+
RangedVector3::RangedVector3(const Vector3 &value) : value_(value) {}
7+
8+
RangedVector3::RangedVector3(const Vector3 &value, std::optional<Vector3> min, std::optional<Vector3> max)
9+
: min_(min), max_(max)
10+
{
11+
set(value);
12+
}
13+
14+
RangedVector3::operator Vector3() const { return value_; }
15+
16+
/*
17+
* Data
18+
*/
19+
20+
// Impose limit on vector
21+
Vector3 RangedVector3::limit(const Vector3 &v) const
22+
{
23+
Vector3 result = v;
24+
if (hasLowerBound())
25+
{
26+
result.x = result.x < min_->x ? min_->x : result.x;
27+
result.y = result.y < min_->y ? min_->y : result.y;
28+
result.z = result.z < min_->z ? min_->z : result.z;
29+
}
30+
if (hasUpperBound())
31+
{
32+
result.x = result.x > max_->x ? max_->x : result.x;
33+
result.y = result.y > max_->y ? max_->y : result.y;
34+
result.z = result.z > max_->z ? max_->z : result.z;
35+
}
36+
return result;
37+
}
38+
39+
// Set from vector
40+
void RangedVector3::set(const Vector3 &v) { value_ = limit(v); }
41+
42+
// Return whether the number has lower bound
43+
bool RangedVector3::hasLowerBound() const { return min_.has_value(); }
44+
45+
// Return whether the number has upper bound
46+
bool RangedVector3::hasUpperBound() const { return max_.has_value(); }
47+
48+
// Return whether the number has bounds
49+
bool RangedVector3::isBounded() const { return hasLowerBound() && hasUpperBound(); }
50+
51+
// Return optional min
52+
std::optional<Vector3> RangedVector3::min() const { return min_; }
53+
54+
// Return optional max
55+
std::optional<Vector3> RangedVector3::max() const { return max_; }
56+
57+
// Return x component of vector
58+
double RangedVector3::x() const { return value_.x; }
59+
60+
// Return y component of vector
61+
double RangedVector3::y() const { return value_.y; }
62+
63+
// Return z component of vector
64+
double RangedVector3::z() const { return value_.z; }
65+
66+
// Return current value
67+
Vector3 RangedVector3::value() const { return value_; }
68+
69+
/*
70+
* Serialisable
71+
*/
72+
73+
// Express as a serialisable value
74+
void RangedVector3::serialise(std::string tag, SerialisedValue &target) const { value_.serialise(tag, target); }
75+
76+
// Read values from a serialisable value
77+
void RangedVector3::deserialise(const SerialisedValue &node) { value_.deserialise(node); }

src/math/rangedVector3.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
// Copyright (c) 2026 Team Dissolve and contributors
3+
4+
#pragma once
5+
6+
#include "base/serialiser.h"
7+
#include "math/vector3.h"
8+
#include <optional>
9+
10+
// Ranged Vector3
11+
class RangedVector3 : public Serialisable<>
12+
{
13+
public:
14+
RangedVector3() = default;
15+
RangedVector3(const Vector3 &value);
16+
RangedVector3(const Vector3 &value, std::optional<Vector3> min, std::optional<Vector3> max = {});
17+
~RangedVector3() = default;
18+
operator Vector3() const;
19+
20+
/*
21+
* Data
22+
*/
23+
protected:
24+
// Value
25+
Vector3 value_;
26+
// Lower bound
27+
std::optional<Vector3> min_;
28+
// Upper bound
29+
std::optional<Vector3> max_;
30+
31+
private:
32+
// Impose limit on vector
33+
Vector3 limit(const Vector3 &v) const;
34+
35+
public:
36+
// Set from vector
37+
void set(const Vector3 &v);
38+
// Return whether the number has lower bound
39+
bool hasLowerBound() const;
40+
// Return whether the number has upper bound
41+
bool hasUpperBound() const;
42+
// Return whether the number has bounds
43+
bool isBounded() const;
44+
// Return optional min
45+
std::optional<Vector3> min() const;
46+
// Return optional max
47+
std::optional<Vector3> max() const;
48+
// Return x component of vector
49+
double x() const;
50+
// Return y component of vector
51+
double y() const;
52+
// Return z component of vector
53+
double z() const;
54+
// Return current value
55+
Vector3 value() const;
56+
57+
/*
58+
* Serialisable
59+
*/
60+
public:
61+
// Express as a serialisable value
62+
void serialise(std::string tag, SerialisedValue &target) const override;
63+
// Read values from a serialisable value
64+
void deserialise(const SerialisedValue &node) override;
65+
};

tests/math/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dissolve_add_test(SRC geometryMin.cpp)
1010
dissolve_add_test(SRC interpolator.cpp)
1111
dissolve_add_test(SRC peaks.cpp)
1212
dissolve_add_test(SRC polynomial.cpp)
13+
dissolve_add_test(SRC rangedVector3.cpp)
1314
dissolve_add_test(SRC sampledValues.cpp)
1415
dissolve_add_test(SRC svd.cpp)
1516
dissolve_add_test(SRC vector3.cpp)

tests/math/rangedVector3.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
// Copyright (c) 2026 Team Dissolve and contributors
3+
4+
#include "math/rangedVector3.h"
5+
#include "tests/testData.h"
6+
#include <gtest/gtest.h>
7+
8+
namespace UnitTest
9+
{
10+
TEST(RangedVector3Test, Construction)
11+
{
12+
// No limiting
13+
RangedVector3 v1({1.0, 2.0, 3.0});
14+
EXPECT_DOUBLE_EQ(v1.x(), 1.0);
15+
EXPECT_DOUBLE_EQ(v1.y(), 2.0);
16+
EXPECT_DOUBLE_EQ(v1.z(), 3.0);
17+
18+
// Limiting on construction - minimum only
19+
RangedVector3 v2({1.0, 2.0, 3.0}, Vector3{2.0, 0.0, 0.0});
20+
EXPECT_DOUBLE_EQ(v2.x(), 2.0);
21+
EXPECT_DOUBLE_EQ(v2.y(), 2.0);
22+
EXPECT_DOUBLE_EQ(v2.z(), 3.0);
23+
24+
// Limiting on construction - minimum and maximum
25+
RangedVector3 v3({1.0, 2.0, 3.0}, Vector3{2.0, 0.0, 0.0}, Vector3{10.0, 1.0, 0.0});
26+
EXPECT_DOUBLE_EQ(v3.x(), 2.0);
27+
EXPECT_DOUBLE_EQ(v3.y(), 1.0);
28+
EXPECT_DOUBLE_EQ(v3.z(), 0.0);
29+
}
30+
31+
TEST(RangedVector3Test, Set)
32+
{
33+
RangedVector3 v({}, Vector3(-1.0, -2.0, -3.0), Vector3(1.0, 2.0, 3.0));
34+
EXPECT_DOUBLE_EQ(v.x(), 0.0);
35+
EXPECT_DOUBLE_EQ(v.y(), 0.0);
36+
EXPECT_DOUBLE_EQ(v.z(), 0.0);
37+
38+
v.set({-3.0, -3.0, -3.0});
39+
EXPECT_DOUBLE_EQ(v.x(), -1.0);
40+
EXPECT_DOUBLE_EQ(v.y(), -2.0);
41+
EXPECT_DOUBLE_EQ(v.z(), -3.0);
42+
43+
v.set({2.0, 1.0, 6.0});
44+
EXPECT_DOUBLE_EQ(v.x(), 1.0);
45+
EXPECT_DOUBLE_EQ(v.y(), 1.0);
46+
EXPECT_DOUBLE_EQ(v.z(), 3.0);
47+
}
48+
} // namespace UnitTest

0 commit comments

Comments
 (0)