Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GoostMath singleton #153

Merged
merged 1 commit into from
Nov 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions core/math/math.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include "math.h"

GoostMath *GoostMath::singleton = nullptr;

bool GoostMath::is_equal_approx(real_t a, real_t b, real_t tolerance) {
// Check for exact equality first, required to handle "infinity" values.
if (a == b) {
return true;
}
// Then check for approximate equality.
return abs(a - b) < tolerance;
}

bool GoostMath::is_zero_approx(real_t s, real_t tolerance) {
return abs(s) < tolerance;
}

Variant GoostMath::catmull_rom(const Variant &p0, const Variant &p1, const Variant &p2, const Variant &p3, float t) {
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_V(t < 0.0f, Variant());
ERR_FAIL_COND_V(t > 1.0f, Variant());
#endif
switch (p0.get_type()) {
case Variant::INT:
case Variant::REAL: {
return goost::catmull_rom(p0.operator real_t(), p1.operator real_t(), p2.operator real_t(), p3.operator real_t(), t);
} break;
case Variant::VECTOR2: {
return goost::catmull_rom(p0.operator Vector2(), p1.operator Vector2(), p2.operator Vector2(), p3.operator Vector2(), t);
} break;
case Variant::VECTOR3: {
return goost::catmull_rom(p0.operator Vector3(), p1.operator Vector3(), p2.operator Vector3(), p3.operator Vector3(), t);
} break;
default: {
ERR_FAIL_V_MSG(Variant(), "Unsupported types.");
}
}
return Variant();
}

Variant GoostMath::bezier(const Variant &p0, const Variant &p1, const Variant &p2, const Variant &p3, float t) {
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_V(t < 0.0f, Variant());
ERR_FAIL_COND_V(t > 1.0f, Variant());
#endif
switch (p0.get_type()) {
case Variant::INT:
case Variant::REAL: {
return goost::bezier(p0.operator real_t(), p1.operator real_t(), p2.operator real_t(), p3.operator real_t(), t);
} break;
case Variant::VECTOR2: {
return goost::bezier(p0.operator Vector2(), p1.operator Vector2(), p2.operator Vector2(), p3.operator Vector2(), t);
} break;
case Variant::VECTOR3: {
return goost::bezier(p0.operator Vector3(), p1.operator Vector3(), p2.operator Vector3(), p3.operator Vector3(), t);
} break;
default: {
ERR_FAIL_V_MSG(Variant(), "Unsupported types.");
}
}
return Variant();
}

void GoostMath::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_equal_approx", "a", "b", "tolerance"), &GoostMath::is_equal_approx, DEFVAL(GOOST_CMP_EPSILON));
ClassDB::bind_method(D_METHOD("is_zero_approx", "s", "tolerance"), &GoostMath::is_zero_approx, DEFVAL(GOOST_CMP_EPSILON));

ClassDB::bind_method(D_METHOD("catmull_rom", "ac", "a", "b", "bc", "weight"), &GoostMath::catmull_rom);
ClassDB::bind_method(D_METHOD("bezier", "a", "ac", "bc", "b", "weight"), &GoostMath::bezier);
}
52 changes: 52 additions & 0 deletions core/math/math.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#pragma once

#include "core/object.h"

#define GOOST_CMP_EPSILON 0.000001

class GoostMath : public Object {
GDCLASS(GoostMath, Object);

private:
static GoostMath *singleton;

protected:
static void _bind_methods();

public:
static GoostMath *get_singleton() { return singleton; }

bool is_equal_approx(real_t a, real_t b, real_t tolerance = GOOST_CMP_EPSILON);
bool is_zero_approx(real_t s, real_t tolerance = GOOST_CMP_EPSILON);

Variant catmull_rom(const Variant &p0, const Variant &p1, const Variant &p2, const Variant &p3, float t);
Variant bezier(const Variant &p0, const Variant &p1, const Variant &p2, const Variant &p3, float t);

GoostMath() {
ERR_FAIL_COND_MSG(singleton != nullptr, "Singleton already exists");
singleton = this;
}
};

namespace goost {

template <class T>
T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) {
float t2 = t * t;
float t3 = t2 * t;

return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
}

template <class T>
T bezier(T start, T control_1, T control_2, T end, float t) {
real_t it = (1.0 - t);
real_t it2 = it * it;
real_t it3 = it2 * it;
real_t t2 = t * t;
real_t t3 = t2 * t;

return start * it3 + control_1 * it2 * t * 3.0 + control_2 * it * t2 * 3.0 + end * t3;
}

} // namespace goost
19 changes: 16 additions & 3 deletions core/math/register_math_types.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
#include "register_math_types.h"

#include "geometry/register_geometry_types.h"
#include "goost/classes_enabled.gen.h"
#include "math.h"
#include "random.h"

static Ref<Random> _random;
#include "goost/classes_enabled.gen.h"

namespace goost {

static Ref<Random> _random;
static GoostMath *_math = nullptr;

void register_math_types() {
#ifdef GOOST_GoostMath
_math = memnew(GoostMath);
ClassDB::register_class<GoostMath>();
Engine::get_singleton()->add_singleton(Engine::Singleton("GoostMath", GoostMath::get_singleton()));
#endif
#ifdef GOOST_Random
_random.instance();
ClassDB::register_class<Random>();
Expand All @@ -21,10 +30,14 @@ void register_math_types() {
}

void unregister_math_types() {
#ifdef GOOST_GoostMath
if (_math) {
memdelete(_math);
}
#endif
#ifdef GOOST_Random
_random.unref();
#endif

#ifdef GOOST_GEOMETRY_ENABLED
goost::unregister_geometry_types();
#endif
Expand Down
59 changes: 59 additions & 0 deletions doc/GoostMath.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GoostMath" inherits="Object" version="3.4">
<brief_description>
Math functions.
</brief_description>
<description>
The singleton which provides various math functions such as interpolation.
</description>
<tutorials>
</tutorials>
<methods>
<method name="bezier">
<return type="Variant" />
<argument index="0" name="a" type="Variant" />
<argument index="1" name="ac" type="Variant" />
<argument index="2" name="bc" type="Variant" />
<argument index="3" name="b" type="Variant" />
<argument index="4" name="weight" type="float" />
<description>
Interpolates a cubic Bézier curve between the four control points [code]a[/code], [code]ac[/code], [code]bc[/code], [code]b[/code]. Points [code]ac[/code] and [code]bc[/code] provide directional information, and the distance between these points determines how fast the curve moves towards [code]ac[/code] before turning towards [code]bc[/code].
The following types are supported: [float], [Vector2], [Vector3].
</description>
</method>
<method name="catmull_rom">
<return type="Variant" />
<argument index="0" name="ac" type="Variant" />
<argument index="1" name="a" type="Variant" />
<argument index="2" name="b" type="Variant" />
<argument index="3" name="bc" type="Variant" />
<argument index="4" name="weight" type="float" />
<description>
Interpolates a centripetal Catmull–Rom spline between the four control points [code]ac[/code], [code]a[/code], [code]b[/code], [code]bc[/code]. Points [code]ac[/code] and [code]bc[/code] provide directional information, and the interpolated values lie between [code]a[/code] and [code]b[/code] points.
The following types are supported: [float], [Vector2], [Vector3].
</description>
</method>
<method name="is_equal_approx">
<return type="bool" />
<argument index="0" name="a" type="float" />
<argument index="1" name="b" type="float" />
<argument index="2" name="tolerance" type="float" default="1e-06" />
<description>
Returns [code]true[/code] if [code]a[/code] and [code]b[/code] are approximately equal to each other.
Here, approximately equal means that [code]a[/code] and [code]b[/code] are within [code]tolerance[/code] of each other.
Infinity values of the same sign are considered equal.
</description>
</method>
<method name="is_zero_approx">
<return type="bool" />
<argument index="0" name="s" type="float" />
<argument index="1" name="tolerance" type="float" default="1e-06" />
<description>
Returns [code]true[/code] if [code]s[/code] is zero or almost zero.
This method is faster than using [method is_equal_approx] with one value as zero.
</description>
</method>
</methods>
<constants>
</constants>
</class>
3 changes: 3 additions & 0 deletions goost.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
components = [
"core/script",
"core/image",
"core/math",
"core/math/geometry",
"scene/physics",
"scene/gui",
Expand All @@ -31,6 +32,7 @@ def get_component_readable_name(component):
name = {
"script": "Scripting",
"image": "Image Processing",
"math": "Mathematics",
"geometry": "Geometry",
"physics": "Physics",
"gui": "User Interface",
Expand Down Expand Up @@ -151,6 +153,7 @@ def add_depencency(self, goost_class):
"CommandLineOption": "core",
"CommandLineParser": "core",
"GoostEngine": "core",
"GoostMath": "math",
"GoostGeometry2D": "geometry",
"GoostImage": "image",
"GradientTexture2D": "scene",
Expand Down
19 changes: 19 additions & 0 deletions tests/project/goost/core/math/test_math.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
extends "res://addons/gut/test.gd"


func test_is_equal_approx():
assert_true(GoostMath.is_equal_approx(0.05, 0.050001))


func test_is_zero_approx():
assert_true(GoostMath.is_zero_approx(0.0000001))


func test_catmull_rom():
var r = GoostMath.catmull_rom(0, 1, 2, 3, 0.5)
assert_eq(r, 1.5)


func test_bezier():
var r = GoostMath.bezier(10, 0.1, -0.1, 20, 0.5)
assert_eq(r, 3.75)