Cleaning up and documenting scmp code.
This commit is contained in:
parent
4eb4008130
commit
b49caa3ec9
|
@ -1,35 +1,27 @@
|
||||||
# Use the latest 2.1 version of CircleCI pipeline process engine.
|
|
||||||
# See: https://circleci.com/docs/configuration-reference
|
|
||||||
version: 2.1
|
version: 2.1
|
||||||
|
|
||||||
# Define a job to be invoked later in a workflow.
|
|
||||||
# See: https://circleci.com/docs/configuration-reference/#jobs
|
|
||||||
jobs:
|
jobs:
|
||||||
ctest:
|
ctest:
|
||||||
# Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub.
|
|
||||||
# See: https://circleci.com/docs/configuration-reference/#executor-job
|
|
||||||
docker:
|
docker:
|
||||||
- image: git.wntrmute.dev/sc/dev:alpine
|
- image: git.wntrmute.dev/sc/dev:alpine
|
||||||
# Add steps to the job
|
|
||||||
# See: https://circleci.com/docs/configuration-reference/#steps
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run:
|
- run:
|
||||||
name: Setup cmake build
|
name: Setup cmake build
|
||||||
command: setup-cmake.sh
|
command: setup-cmake.sh
|
||||||
|
|
||||||
static_analysis:
|
static_analysis:
|
||||||
docker:
|
docker:
|
||||||
- image: git.wntrmute.dev/sc/dev:alpine
|
- image: git.wntrmute.dev/sc/dev:alpine
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run:
|
- run:
|
||||||
- name: trunk check
|
name: Trunk check
|
||||||
command: ./trunk check
|
command: ./trunk check
|
||||||
|
|
||||||
# Orchestrate jobs using workflows
|
|
||||||
# See: https://circleci.com/docs/configuration-reference/#workflows
|
|
||||||
workflows:
|
workflows:
|
||||||
ctest:
|
ctest:
|
||||||
jobs:
|
jobs:
|
||||||
- ctest
|
- ctest
|
||||||
|
static_analysis:
|
||||||
|
jobs:
|
||||||
|
- static_analysis
|
||||||
|
|
|
@ -46,6 +46,7 @@ set(HEADER_FILES
|
||||||
include/scsl/StringUtil.h
|
include/scsl/StringUtil.h
|
||||||
include/scsl/TLV.h
|
include/scsl/TLV.h
|
||||||
|
|
||||||
|
include/scmp/geom.h
|
||||||
include/scmp/scmp.h
|
include/scmp/scmp.h
|
||||||
include/scmp/Math.h
|
include/scmp/Math.h
|
||||||
include/scmp/Motion2D.h
|
include/scmp/Motion2D.h
|
||||||
|
@ -116,6 +117,7 @@ generate_test(stringutil)
|
||||||
# math and physics
|
# math and physics
|
||||||
generate_test(coord2d)
|
generate_test(coord2d)
|
||||||
generate_test(madgwick)
|
generate_test(madgwick)
|
||||||
|
generate_test(math)
|
||||||
generate_test(orientation)
|
generate_test(orientation)
|
||||||
generate_test(quaternion)
|
generate_test(quaternion)
|
||||||
generate_test(vector)
|
generate_test(vector)
|
||||||
|
|
|
@ -31,11 +31,10 @@
|
||||||
namespace scmp {
|
namespace scmp {
|
||||||
|
|
||||||
|
|
||||||
/// MAX_RADIAN is a precomputed 2 * M_PI. and MIN_RADIAN is -2 * M_PI.
|
/// MAX_RADIAN is a precomputed 2 * M_PI.
|
||||||
constexpr double MAX_RADIAN = 2 * M_PI;
|
constexpr double MAX_RADIAN = 2 * M_PI;
|
||||||
constexpr double MIN_RADIAN = -2 * M_PI;
|
constexpr double MIN_RADIAN = -2 * M_PI;
|
||||||
constexpr double POS_HALF_RADIAN = M_PI / 2;
|
constexpr double PI_D = 3.141592653589793;
|
||||||
constexpr double NEG_HALF_RADIAN = -(M_PI / 2);
|
|
||||||
|
|
||||||
|
|
||||||
/// Roll m die of n sides, returning a vector of the dice.
|
/// Roll m die of n sides, returning a vector of the dice.
|
||||||
|
@ -48,50 +47,65 @@ int DieTotal(int m, int n);
|
||||||
int BestDie(int k, int m, int n);
|
int BestDie(int k, int m, int n);
|
||||||
|
|
||||||
|
|
||||||
/// Convert radians to degrees.
|
/// \brief Convert radians to degrees.
|
||||||
/// @param rads the angle in radians
|
///
|
||||||
/// @return the angle in degrees.
|
/// \param rads the angle in radians
|
||||||
|
/// \return the angle in degrees.
|
||||||
float RadiansToDegreesF(float rads);
|
float RadiansToDegreesF(float rads);
|
||||||
|
|
||||||
/// Convert radians to degrees.
|
/// \brief Convert radians to degrees.
|
||||||
/// @param rads the angle in radians
|
///
|
||||||
/// @return the angle in degrees.
|
/// \param rads the angle in radians
|
||||||
|
/// \return the angle in degrees.
|
||||||
double RadiansToDegreesD(double rads);
|
double RadiansToDegreesD(double rads);
|
||||||
|
|
||||||
/// Convert degrees to radians.
|
/// \brief Convert degrees to radians.
|
||||||
/// @param degrees the angle in degrees
|
///
|
||||||
/// @return the angle in radians.
|
/// \param degrees the angle in degrees
|
||||||
|
/// \return the angle in radians.
|
||||||
float DegreesToRadiansF(float degrees);
|
float DegreesToRadiansF(float degrees);
|
||||||
|
|
||||||
/// Convert degrees to radians.
|
/// \brief Convert degrees to radians.
|
||||||
/// @param degrees the angle in degrees
|
///
|
||||||
/// @return the angle in radians.
|
/// \param degrees the angle in degrees
|
||||||
|
/// \return the angle in radians.
|
||||||
double DegreesToRadiansD(double degrees);
|
double DegreesToRadiansD(double degrees);
|
||||||
|
|
||||||
/// RotateRadians rotates theta0 by theta1 radians, wrapping the result to
|
/// \brief RotateRadians rotates theta0 by theta1 radians, wrapping
|
||||||
/// MIN_RADIAN <= result <= MAX_RADIAN.
|
/// the result to MIN_RADIAN <= result <= MAX_RADIAN.
|
||||||
|
///
|
||||||
|
/// \param theta0
|
||||||
|
/// \param theta1
|
||||||
|
/// \return
|
||||||
double RotateRadians(double theta0, double theta1);
|
double RotateRadians(double theta0, double theta1);
|
||||||
|
|
||||||
/// Get the default epsilon value.
|
/// \brief Get the default epsilon value.
|
||||||
/// @param epsilon The variable to store the epsilon value in.
|
///
|
||||||
|
/// \param epsilon The variable to store the epsilon value in.
|
||||||
void DefaultEpsilon(double &epsilon);
|
void DefaultEpsilon(double &epsilon);
|
||||||
|
|
||||||
/// Get the default epsilon value.
|
/// Get the default epsilon value.
|
||||||
/// @param epsilon The variable to store the epsilon value in.
|
///
|
||||||
|
/// \param epsilon The variable to store the epsilon value in.
|
||||||
void DefaultEpsilon(float &epsilon);
|
void DefaultEpsilon(float &epsilon);
|
||||||
|
|
||||||
|
|
||||||
/// Return whether the two values of type T are equal to within some tolerance.
|
/// \brief Return whether the two values of type T are equal to within
|
||||||
/// @tparam T The type of value
|
/// some tolerance.
|
||||||
/// @param a A value of type T used as the left-hand side of an equality check.
|
///
|
||||||
/// @param b A value of type T used as the right-hand side of an equality check.
|
/// \tparam T The type of value
|
||||||
/// @param epsilon The tolerance value.
|
/// \param a A value of type T used as the left-hand side of an
|
||||||
/// @return Whether the two values are "close enough" to be considered equal.
|
/// equality check.
|
||||||
|
/// \param b A value of type T used as the right-hand side of an
|
||||||
|
/// equality check.
|
||||||
|
/// \param epsilon The tolerance value.
|
||||||
|
/// \return Whether the two values are "close enough" to be considered
|
||||||
|
/// equal.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static T
|
static T
|
||||||
WithinTolerance(T a, T b, T epsilon)
|
WithinTolerance(T a, T b, T epsilon)
|
||||||
{
|
{
|
||||||
return std::abs(a - b) < epsilon;
|
return std::abs(a - b) <= epsilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,6 @@ public:
|
||||||
Madgwick() : deltaT(0.0), previousSensorFrame(), sensorFrame()
|
Madgwick() : deltaT(0.0), previousSensorFrame(), sensorFrame()
|
||||||
{};
|
{};
|
||||||
|
|
||||||
|
|
||||||
/// \brief The Madgwick filter is initialised with a sensor frame.
|
/// \brief The Madgwick filter is initialised with a sensor frame.
|
||||||
///
|
///
|
||||||
/// \param sf A sensor frame; if zero, the sensor frame will be
|
/// \param sf A sensor frame; if zero, the sensor frame will be
|
||||||
|
@ -67,7 +66,6 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// \brief Initialise the filter with a sensor frame quaternion.
|
/// \brief Initialise the filter with a sensor frame quaternion.
|
||||||
///
|
///
|
||||||
/// \param sf A quaternion representing the current Orientation.
|
/// \param sf A quaternion representing the current Orientation.
|
||||||
|
@ -75,7 +73,6 @@ public:
|
||||||
deltaT(0.0), previousSensorFrame(), sensorFrame(sf)
|
deltaT(0.0), previousSensorFrame(), sensorFrame(sf)
|
||||||
{};
|
{};
|
||||||
|
|
||||||
|
|
||||||
/// \brief Return the current orientation as measured by the
|
/// \brief Return the current orientation as measured by the
|
||||||
/// filter.
|
/// filter.
|
||||||
///
|
///
|
||||||
|
@ -86,7 +83,6 @@ public:
|
||||||
return this->sensorFrame;
|
return this->sensorFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// \brief Return the filter's rate of angular change from a
|
/// \brief Return the filter's rate of angular change from a
|
||||||
/// sensor frame.
|
/// sensor frame.
|
||||||
///
|
///
|
||||||
|
@ -152,7 +148,6 @@ public:
|
||||||
this->UpdateFrame(this->sensorFrame + q, delta);
|
this->UpdateFrame(this->sensorFrame + q, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// \brief Update the sensor frame with a gyroscope reading.
|
/// \brief Update the sensor frame with a gyroscope reading.
|
||||||
///
|
///
|
||||||
/// If no Δt is provided, the filter's default is used.
|
/// If no Δt is provided, the filter's default is used.
|
||||||
|
@ -168,7 +163,6 @@ public:
|
||||||
this->UpdateAngularOrientation(gyro, this->deltaT);
|
this->UpdateAngularOrientation(gyro, this->deltaT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// \brief Retrieve a vector of the Euler angles in ZYX Orientation.
|
/// \brief Retrieve a vector of the Euler angles in ZYX Orientation.
|
||||||
///
|
///
|
||||||
/// \return A vector of Euler angles as <ψ, θ, ϕ>.
|
/// \return A vector of Euler angles as <ψ, θ, ϕ>.
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
///
|
||||||
|
/// \file include/scmp/geom.h
|
||||||
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
|
/// \date 2023-10-20
|
||||||
|
/// \brief 2D point and polar coordinate systems.
|
||||||
|
///
|
||||||
|
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||||
|
///
|
||||||
|
/// Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
/// any purpose with or without fee is hereby granted, provided that
|
||||||
|
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||||
|
///
|
||||||
|
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifndef SCSL_GEOM_H
|
||||||
|
#define SCSL_GEOM_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <scmp/geom/Coord2D.h>
|
||||||
|
#include <scmp/geom/Orientation.h>
|
||||||
|
#include <scmp/geom/Quaternion.h>
|
||||||
|
#include <scmp/geom/Vector.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace scmp {
|
||||||
|
|
||||||
|
|
||||||
|
/// \brief Geometry-related code.
|
||||||
|
namespace geom {}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace scmp
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SCSL_GEOM_H
|
|
@ -1,29 +1,29 @@
|
||||||
/// coord2d.h defines 2D point and polar coordinate systems.
|
///
|
||||||
//
|
/// \file include/scmp/geom/Coord2D.h
|
||||||
// Project: scccl
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
// File: include/math/coord2d.h
|
/// \date 2017-06-05
|
||||||
// Author: Kyle Isom
|
/// \brief 2D point and polar coordinate systems.
|
||||||
// Date: 2017-06-05
|
///
|
||||||
// Namespace: math::geom
|
/// Copyright 2017 K. Isom <kyle@imap.cc>
|
||||||
//
|
///
|
||||||
// coord2d.h defines 2D coordinate classes and functions.
|
/// Permission to use, copy, modify, and/or distribute this software for
|
||||||
//
|
/// any purpose with or without fee is hereby granted, provided that
|
||||||
// Copyright 2017 Kyle Isom <kyle@imap.cc>
|
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||||
//
|
///
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
// you may not use this file except in compliance with the License.
|
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
// You may obtain a copy of the License at
|
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
//
|
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
//
|
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
///
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
#ifndef SCMATH_GEOM_COORD2D_H
|
#ifndef SCMATH_GEOM_COORD2D_H
|
||||||
#define SCMATH_GEOM_COORD2D_H
|
#define SCMATH_GEOM_COORD2D_H
|
||||||
|
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -34,72 +34,81 @@ namespace geom {
|
||||||
|
|
||||||
|
|
||||||
class Point2D;
|
class Point2D;
|
||||||
|
|
||||||
class Polar2D;
|
class Polar2D;
|
||||||
|
|
||||||
// Point2D is a logical grouping of a set of 2D cartesian coordinates.
|
/// \brief Point2D is a logical grouping of a set of 2D cartesian
|
||||||
|
/// coordinates.
|
||||||
class Point2D {
|
class Point2D {
|
||||||
public:
|
public:
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
// A Point2D can be initialised by setting its members to 0, by providing the
|
// A Point2D can be initialised by setting its members to 0, by providing the
|
||||||
// x and y coordiantes, or through translation from a polar coordinate.
|
// x and y coordiantes, or through translation from a polar coordinate.
|
||||||
Point2D() : x(0), y(0) {}
|
Point2D();
|
||||||
Point2D(int _x, int _y) : x(_x), y(_y) {}
|
Point2D(int _x, int _y);
|
||||||
Point2D(const Polar2D&);
|
Point2D(const Polar2D &);
|
||||||
|
|
||||||
std::string ToString(void);
|
std::string ToString();
|
||||||
void ToPolar(Polar2D&);
|
void ToPolar(Polar2D &);
|
||||||
|
|
||||||
// Rotate rotates the point by theta radians. Alternatively, a rotation
|
// Rotate rotates the point by theta radians. Alternatively, a rotation
|
||||||
// can use this point as the centre, with a polar coordinate and a rotation
|
// can use this point as the centre, with a polar coordinate and a rotation
|
||||||
// amount (in radians). The latter is used to specify a central point
|
// amount (in radians). The latter is used to specify a central point
|
||||||
// of rotation with vertices specified as polar coordinates from the centre.
|
// of rotation with vertices specified as polar coordinates from the centre.
|
||||||
// Both forms take a reference to a Point2D to store the rotated point.
|
// Both forms take a reference to a Point2D to store the rotated point.
|
||||||
void Rotate(Point2D& rotated, double theta);
|
void Rotate(Point2D &rotated, double theta);
|
||||||
std::vector<Point2D> Rotate(std::vector<Polar2D>, double);
|
std::vector<Point2D> Rotate(std::vector<Polar2D>, double);
|
||||||
|
|
||||||
// Translate adds this point to the first argument, storing the result in the
|
// Translate adds this point to the first argument, storing the result in the
|
||||||
// second argument.
|
// second argument.
|
||||||
void Translate(const Point2D& other, Point2D& translated);
|
void Translate(const Point2D &other, Point2D &translated);
|
||||||
|
|
||||||
// Distance returns the distance from this point to another.
|
// Distance returns the distance from this point to another.
|
||||||
int Distance(const Point2D& other);
|
int Distance(const Point2D &other);
|
||||||
|
|
||||||
Point2D operator+(const Point2D &rhs) const { return Point2D(x + rhs.x, y + rhs.y); }
|
Point2D operator+(const Point2D &rhs) const
|
||||||
Point2D operator-(const Point2D &rhs) const { return Point2D(x - rhs.x, y - rhs.y); }
|
{ return Point2D(x + rhs.x, y + rhs.y); }
|
||||||
Point2D operator*(const int k) const { return Point2D(x * k, y * k); }
|
Point2D operator-(const Point2D &rhs) const
|
||||||
bool operator==(const Point2D& rhs) const;
|
{ return Point2D(x - rhs.x, y - rhs.y); }
|
||||||
bool operator!=(const Point2D& rhs) const { return !(*this == rhs); }
|
Point2D operator*(const int k) const
|
||||||
friend std::ostream& operator<<(std::ostream& outs, const Point2D& pt);
|
{ return Point2D(x * k, y * k); }
|
||||||
|
bool operator==(const Point2D &rhs) const;
|
||||||
|
bool operator!=(const Point2D &rhs) const
|
||||||
|
{ return !(*this == rhs); }
|
||||||
|
friend std::ostream &operator<<(std::ostream &outs, const Point2D &pt);
|
||||||
};
|
};
|
||||||
|
|
||||||
// A Polar2D is a 2D polar coordinate, specified in terms of the radius from
|
// A Polar2D is a 2D polar coordinate, specified in terms of the radius from
|
||||||
// some origin and the angle from the positive X axis of a cartesian coordinate
|
// some origin and the angle from the positive X axis of a cartesian coordinate
|
||||||
// system.
|
// system.
|
||||||
class Polar2D {
|
class Polar2D {
|
||||||
public:
|
public:
|
||||||
double r, theta;
|
double r, theta;
|
||||||
|
|
||||||
// A Polar2D can be initialised as a zeroised polar coordinate, by specifying
|
// A Polar2D can be initialised as a zeroised polar coordinate, by specifying
|
||||||
// the radius and angle directly, or via conversion from a Point2D.
|
// the radius and angle directly, or via conversion from a Point2D.
|
||||||
Polar2D() : r(0.0), theta(0.0) {}
|
Polar2D() : r(0.0), theta(0.0)
|
||||||
Polar2D(double _r, double _theta) : r(_r), theta(_theta) {}
|
{}
|
||||||
Polar2D(const Point2D&);
|
Polar2D(double _r, double _theta) : r(_r), theta(_theta)
|
||||||
|
{}
|
||||||
|
Polar2D(const Point2D &);
|
||||||
|
|
||||||
std::string ToString();
|
std::string ToString();
|
||||||
void ToPoint(Point2D&);
|
void ToPoint(Point2D &);
|
||||||
|
|
||||||
// Rotate rotates the polar coordinate by the number of radians, storing the result
|
// Rotate rotates the polar coordinate by the number of radians, storing the result
|
||||||
// in the Polar2D argument.
|
// in the Polar2D argument.
|
||||||
void Rotate(Polar2D&, double);
|
void Rotate(Polar2D &, double);
|
||||||
|
|
||||||
// RotateAround rotates this point about by theta radians, storing the rotated point
|
// RotateAround rotates this point about by theta radians, storing the rotated point
|
||||||
// in result.
|
// in result.
|
||||||
void RotateAround(const Point2D& other, Point2D& result, double tjeta);
|
void RotateAround(const Point2D &other, Point2D &result, double tjeta);
|
||||||
|
|
||||||
bool operator==(const Polar2D&) const;
|
bool operator==(const Polar2D &) const;
|
||||||
bool operator!=(const Polar2D& rhs) const { return !(*this == rhs); }
|
bool operator!=(const Polar2D &rhs) const
|
||||||
friend std::ostream& operator<<(std::ostream&, const Polar2D&);
|
{ return !(*this == rhs); }
|
||||||
|
friend std::ostream &operator<<(std::ostream &, const Polar2D &);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,10 @@
|
||||||
|
|
||||||
|
|
||||||
/// \brief Shimmering Clarity Math & Physics toolkit.
|
/// \brief Shimmering Clarity Math & Physics toolkit.
|
||||||
namespace scmp {
|
///
|
||||||
|
/// The Shimmering Clarity contains code related to math and physics,
|
||||||
} // namespace scmp
|
/// particularly as relevant to game programming and robotics.
|
||||||
|
namespace scmp {}
|
||||||
|
|
||||||
|
|
||||||
#endif //SCSL_SCMP_H
|
#endif //SCSL_SCMP_H
|
||||||
|
|
|
@ -35,11 +35,17 @@ namespace sctest {
|
||||||
#define SCTEST_CHECK_FALSE(x) if ((x)) { return false; }
|
#define SCTEST_CHECK_FALSE(x) if ((x)) { return false; }
|
||||||
#define SCTEST_CHECK_EQ(x, y) if ((x) != (y)) { return false; }
|
#define SCTEST_CHECK_EQ(x, y) if ((x) != (y)) { return false; }
|
||||||
#define SCTEST_CHECK_NE(x, y) if ((x) == (y)) { return false; }
|
#define SCTEST_CHECK_NE(x, y) if ((x) == (y)) { return false; }
|
||||||
|
#define SCTEST_CHECK_GEQ(x, y) if ((x) < (y)) { return false; }
|
||||||
|
#define SCTEST_CHECK_LEQ(x, y) if ((x) > (y)) { return false; }
|
||||||
#define SCTEST_CHECK_FEQ(x, y) { float eps; scmp::DefaultEpsilon(eps); if (!scmp::WithinTolerance((x), (y), eps)) { return false; }}
|
#define SCTEST_CHECK_FEQ(x, y) { float eps; scmp::DefaultEpsilon(eps); if (!scmp::WithinTolerance((x), (y), eps)) { return false; }}
|
||||||
#define SCTEST_CHECK_DEQ(x, y) { double eps; scmp::DefaultEpsilon(eps); if (!scmp::WithinTolerance((x), (y), eps)) { return false; }}
|
#define SCTEST_CHECK_DEQ(x, y) { double eps; scmp::DefaultEpsilon(eps); if (!scmp::WithinTolerance((x), (y), eps)) { return false; }}
|
||||||
|
|
||||||
#define SCTEST_CHECK_FEQ_EPS(x, y, eps) { if (!scmp::WithinTolerance<float>((x), (y), eps)) { return false; }}
|
#define SCTEST_CHECK_FEQ_EPS(x, y, eps) { if (!scmp::WithinTolerance<float>((x), (y), (eps))) { return false; }}
|
||||||
#define SCTEST_CHECK_DEQ_EPS(x, y, eps) { if (!scmp::WithinTolerance<double>((x), (y), eps)) { return false; }}
|
#define SCTEST_CHECK_FNE_EPS(x, y, eps) { if (scmp::WithinTolerance<float>((x), (y), (eps))) { return false; }}
|
||||||
|
|
||||||
|
#define SCTEST_CHECK_DEQ_EPS(x, y, eps) { if (!scmp::WithinTolerance<double>((x), (y), (eps))) { return false; }}
|
||||||
|
#define SCTEST_CHECK_DNE_EPS(x, y, eps) { if (scmp::WithinTolerance<double>((x), (y), (eps))) { return false; }}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace sctest
|
} // namespace sctest
|
||||||
|
|
|
@ -42,6 +42,11 @@ namespace geom {
|
||||||
//
|
//
|
||||||
// Point2D
|
// Point2D
|
||||||
|
|
||||||
|
|
||||||
|
Point2D::Point2D() : x(0), y(0) {}
|
||||||
|
|
||||||
|
Point2D::Point2D(int _x, int _y) : x(_x), y(_y) {}
|
||||||
|
|
||||||
Point2D::Point2D(const Polar2D &pol)
|
Point2D::Point2D(const Polar2D &pol)
|
||||||
: x(std::rint(std::cos(pol.theta) * pol.r)),
|
: x(std::rint(std::cos(pol.theta) * pol.r)),
|
||||||
y(std::rint(std::sin(pol.theta) * pol.r)) {}
|
y(std::rint(std::sin(pol.theta) * pol.r)) {}
|
||||||
|
|
|
@ -61,7 +61,7 @@ SimpleSuite::AddTest(std::string name, std::function<bool()> test)
|
||||||
void
|
void
|
||||||
SimpleSuite::AddFailingTest(std::string name, std::function<bool()> test)
|
SimpleSuite::AddFailingTest(std::string name, std::function<bool()> test)
|
||||||
{
|
{
|
||||||
const UnitTest test_case = {std::move(name), test, false};
|
const UnitTest test_case = {std::move(name), std::move(test), false};
|
||||||
tests.push_back(test_case);
|
tests.push_back(test_case);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <scsl/Flags.h>
|
||||||
#include <scmp/Math.h>
|
#include <scmp/Math.h>
|
||||||
#include <scmp/geom/Coord2D.h>
|
#include <scmp/geom/Coord2D.h>
|
||||||
#include <sctest/Checks.h>
|
#include <sctest/Checks.h>
|
||||||
|
@ -222,9 +223,25 @@ geomRotatePointsAboutOrigin()
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main()
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
auto quiet = false;
|
||||||
|
auto flags = new scsl::Flags("test_orientation",
|
||||||
|
"This test validates various orientation-related components in scmp.");
|
||||||
|
flags->Register("-q", false, "suppress test output");
|
||||||
|
|
||||||
|
auto parsed = flags->Parse(argc, argv);
|
||||||
|
if (parsed != scsl::Flags::ParseStatus::OK) {
|
||||||
|
std::cerr << "Failed to parse flags: "
|
||||||
|
<< scsl::Flags::ParseStatusToString(parsed) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
SimpleSuite suite;
|
SimpleSuite suite;
|
||||||
|
flags->GetBool("-q", quiet);
|
||||||
|
if (quiet) {
|
||||||
|
suite.Silence();
|
||||||
|
}
|
||||||
|
|
||||||
suite.AddTest("geomValidateAngularRotation", geomValidateAngularRotation);
|
suite.AddTest("geomValidateAngularRotation", geomValidateAngularRotation);
|
||||||
suite.AddTest("geomConversionIdentities", geomConversionIdentities);
|
suite.AddTest("geomConversionIdentities", geomConversionIdentities);
|
||||||
suite.AddTest("geomVerifyBasicProperties", geomVerifyBasicProperties);
|
suite.AddTest("geomVerifyBasicProperties", geomVerifyBasicProperties);
|
||||||
|
@ -232,8 +249,8 @@ main()
|
||||||
suite.AddTest("geomRotatePoint2D", geomRotatePoint2D);
|
suite.AddTest("geomRotatePoint2D", geomRotatePoint2D);
|
||||||
suite.AddTest("geomRotatePointsAboutOrigin", geomRotatePointsAboutOrigin);
|
suite.AddTest("geomRotatePointsAboutOrigin", geomRotatePointsAboutOrigin);
|
||||||
|
|
||||||
|
delete flags;
|
||||||
auto result = suite.Run();
|
auto result = suite.Run();
|
||||||
std::cout << suite << "\n";
|
std::cout << suite.GetReport() << "\n";
|
||||||
|
|
||||||
return result ? 0 : 1;
|
return result ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
///
|
||||||
|
/// \file test/math.cc
|
||||||
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
|
/// \date 2023-10-20
|
||||||
|
/// \brief Unit tests for math functions.
|
||||||
|
///
|
||||||
|
/// Arena defines a memory management backend for pre-allocating memory.
|
||||||
|
///
|
||||||
|
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||||
|
///
|
||||||
|
/// Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
/// any purpose with or without fee is hereby granted, provided that
|
||||||
|
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||||
|
///
|
||||||
|
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
///
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <scsl/Flags.h>
|
||||||
|
#include <scmp/Math.h>
|
||||||
|
#include <sctest/Checks.h>
|
||||||
|
#include <sctest/SimpleSuite.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
BestDie()
|
||||||
|
{
|
||||||
|
// Theoretically, this could fail. The odds of that happening
|
||||||
|
// with 100 die and a proper RNG is 0.99999998792533,
|
||||||
|
// practically 100%. At 1000 die, it's virtually guaranteed.
|
||||||
|
auto n = scmp::BestDie(1000, 6, 1);
|
||||||
|
|
||||||
|
SCTEST_CHECK_EQ(n, 6);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
DieTotal()
|
||||||
|
{
|
||||||
|
auto n = scmp::DieTotal(100, 6);
|
||||||
|
|
||||||
|
SCTEST_CHECK_GEQ(n, 100);
|
||||||
|
SCTEST_CHECK_LEQ(n, 600);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
WithinToleranceFloat()
|
||||||
|
{
|
||||||
|
float x = 0.1235;
|
||||||
|
float y = 0.1236;
|
||||||
|
float eps = 0.0;
|
||||||
|
float expected = 0.1234;
|
||||||
|
|
||||||
|
scmp::DefaultEpsilon(eps);
|
||||||
|
|
||||||
|
SCTEST_CHECK_FEQ_EPS(x, expected, eps);
|
||||||
|
SCTEST_CHECK_FNE_EPS(y, expected, eps);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
WithinToleranceDouble()
|
||||||
|
{
|
||||||
|
double x = 0.12348;
|
||||||
|
double y = 0.1236;
|
||||||
|
double eps = 0.0;
|
||||||
|
double expected = 0.12345;
|
||||||
|
|
||||||
|
scmp::DefaultEpsilon(eps);
|
||||||
|
|
||||||
|
SCTEST_CHECK_DEQ_EPS(x, expected, eps);
|
||||||
|
SCTEST_CHECK_DNE_EPS(y, expected, eps);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RotateRadians()
|
||||||
|
{
|
||||||
|
double theta0 = 0.0;
|
||||||
|
double theta1 = scmp::PI_D;
|
||||||
|
|
||||||
|
auto rotated = scmp::RotateRadians(theta0, theta1);
|
||||||
|
SCTEST_CHECK_DEQ(rotated, theta1);
|
||||||
|
|
||||||
|
rotated = scmp::RotateRadians(rotated, theta1);
|
||||||
|
SCTEST_CHECK_DEQ(rotated, theta0);
|
||||||
|
|
||||||
|
theta1 = scmp::PI_D * 3 / 2;
|
||||||
|
rotated = scmp::RotateRadians(theta0, theta1);
|
||||||
|
SCTEST_CHECK_DEQ(rotated, -scmp::PI_D / 2);
|
||||||
|
|
||||||
|
rotated = scmp::RotateRadians(rotated, theta1);
|
||||||
|
SCTEST_CHECK_DEQ(rotated, scmp::PI_D);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
auto quiet = false;
|
||||||
|
auto flags = new scsl::Flags("test_orientation",
|
||||||
|
"This test validates various orientation-related components in scmp.");
|
||||||
|
flags->Register("-q", false, "suppress test output");
|
||||||
|
|
||||||
|
auto parsed = flags->Parse(argc, argv);
|
||||||
|
if (parsed != scsl::Flags::ParseStatus::OK) {
|
||||||
|
std::cerr << "Failed to parse flags: "
|
||||||
|
<< scsl::Flags::ParseStatusToString(parsed) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sctest::SimpleSuite suite;
|
||||||
|
flags->GetBool("-q", quiet);
|
||||||
|
if (quiet) {
|
||||||
|
suite.Silence();
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.AddTest("BestDie", BestDie);
|
||||||
|
suite.AddTest("DieTotal", DieTotal);
|
||||||
|
suite.AddTest("WithinToleranceFloat", WithinToleranceFloat);
|
||||||
|
suite.AddTest("WithinToleranceDouble", WithinToleranceDouble);
|
||||||
|
suite.AddTest("RotateRadians", RotateRadians);
|
||||||
|
|
||||||
|
delete flags;
|
||||||
|
auto result = suite.Run();
|
||||||
|
std::cout << suite.GetReport() << "\n";
|
||||||
|
return result ? 0 : 1;
|
||||||
|
}
|
Loading…
Reference in New Issue