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 methods to generate random points within a circle and a ring #28

Merged
merged 1 commit into from
Oct 26, 2020
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
19 changes: 19 additions & 0 deletions core/math/2d/geometry/goost_geometry_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,22 @@ Vector<Point2> GoostGeometry2D::circle(real_t p_radius, real_t p_max_error) {

return regular_polygon(vertex_count, p_radius); // vertex count == edge count
}

Vector2 GoostGeometry2D::rand_point_in_circle(real_t p_radius) {
real_t r = Math::sqrt(Math::random(0.0, 1.0)) * p_radius;
real_t t = Math::random(0.0, 1.0) * Math_TAU;
return Vector2(r * Math::cos(t), r * Math::sin(t));
}

Vector2 GoostGeometry2D::rand_point_on_circle(real_t p_radius) {
real_t t = Math::random(0.0, 1.0) * Math_TAU;
return Vector2(p_radius * Math::cos(t), p_radius * Math::sin(t));
}

Vector2 GoostGeometry2D::rand_point_in_ring(real_t p_min_radius, real_t p_max_radius) {
const double r2_max = p_max_radius * p_max_radius;
const double r2_min = p_min_radius * p_min_radius;
real_t r = Math::sqrt(Math::random(0.0, 1.0) * (r2_max - r2_min) + r2_min);
real_t t = Math::random(0.0, 1.0) * Math_TAU;
return Vector2(r * Math::cos(t), r * Math::sin(t));
}
5 changes: 5 additions & 0 deletions core/math/2d/geometry/goost_geometry_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ class GoostGeometry2D {
static Vector<Point2> regular_polygon(int p_edge_count, real_t p_size);
static Vector<Point2> circle(real_t p_radius, real_t p_max_error = 0.25);

/* Random methods */
static Vector2 rand_point_in_circle(real_t p_radius);
static Vector2 rand_point_on_circle(real_t p_radius);
static Vector2 rand_point_in_ring(real_t p_min_radius, real_t p_max_radius);

public:
static void initialize();
static void finalize();
Expand Down
16 changes: 16 additions & 0 deletions core/math/2d/geometry/goost_geometry_2d_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,18 @@ Vector<Point2> _GoostGeometry2D::circle(real_t p_radius, real_t p_max_error) con
return GoostGeometry2D::circle(p_radius, p_max_error);
}

Vector2 _GoostGeometry2D::rand_point_in_circle(real_t p_radius) const {
return GoostGeometry2D::rand_point_in_circle(p_radius);
}

Vector2 _GoostGeometry2D::rand_point_on_circle(real_t p_radius) const {
return GoostGeometry2D::rand_point_on_circle(p_radius);
}

Vector2 _GoostGeometry2D::rand_point_in_ring(real_t p_min_radius, real_t p_max_radius) const {
return GoostGeometry2D::rand_point_in_ring(p_min_radius, p_max_radius);
}

void _GoostGeometry2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("merge_polygons", "polygon_a", "polygon_b", "params"), &_GoostGeometry2D::merge_polygons, DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("clip_polygons", "polygon_a", "polygon_b", "params"), &_GoostGeometry2D::clip_polygons, DEFVAL(Variant()));
Expand Down Expand Up @@ -405,6 +417,10 @@ void _GoostGeometry2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("regular_polygon", "sides", "size"), &_GoostGeometry2D::regular_polygon, DEFVAL(64.0));
ClassDB::bind_method(D_METHOD("circle", "radius", "max_error"), &_GoostGeometry2D::circle, DEFVAL(0.25));

ClassDB::bind_method(D_METHOD("rand_point_in_circle", "radius"), &_GoostGeometry2D::rand_point_in_circle);
ClassDB::bind_method(D_METHOD("rand_point_on_circle", "radius"), &_GoostGeometry2D::rand_point_on_circle);
ClassDB::bind_method(D_METHOD("rand_point_in_ring", "min_radius", "max_radius"), &_GoostGeometry2D::rand_point_in_ring);

BIND_ENUM_CONSTANT(OPERATION_NONE);
BIND_ENUM_CONSTANT(OPERATION_UNION);
BIND_ENUM_CONSTANT(OPERATION_DIFFERENCE);
Expand Down
4 changes: 4 additions & 0 deletions core/math/2d/geometry/goost_geometry_2d_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ class _GoostGeometry2D : public Object {
Vector<Point2> regular_polygon(int p_edge_count, real_t p_size) const;
Vector<Point2> circle(real_t p_radius, real_t p_max_error) const;

Vector2 rand_point_in_circle(real_t p_radius) const;
Vector2 rand_point_on_circle(real_t p_radius) const;
Vector2 rand_point_in_ring(real_t p_min_radius, real_t p_max_radius) const;

_GoostGeometry2D();
};

Expand Down
39 changes: 39 additions & 0 deletions doc/GoostGeometry2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,45 @@
Returns the total length of the segments representing the polyline. See also [method polygon_perimeter].
</description>
</method>
<method name="rand_point_in_circle" qualifiers="const">
<return type="Vector2">
</return>
<argument index="0" name="radius" type="float">
</argument>
<description>
Returns a random point uniformly distributed within a circle, such that [method Geometry.is_point_in_circle] shall return [code]true[/code] given the same [code]radius[/code].
This method is faster than [method rand_point_in_ring] and equivalent to:
[codeblock]
GoostGeometry2D.rand_point_in_ring(0.0, radius)
[/codeblock]
</description>
</method>
<method name="rand_point_in_ring" qualifiers="const">
<return type="Vector2">
</return>
<argument index="0" name="min_radius" type="float">
</argument>
<argument index="1" name="max_radius" type="float">
</argument>
<description>
Returns a random point uniformly distributed within the ring's area confined by inner (hole) and outer (boundary) circles as specified with [code]min_radius[/code] and [code]max_radius[/code] respectively.
The method can be used to generate points distributed strictly on the circle's boundary if [code]min_radius == max_radius[/code] which is equivalent to [method rand_point_on_circle].
</description>
</method>
<method name="rand_point_on_circle" qualifiers="const">
<return type="Vector2">
</return>
<argument index="0" name="radius" type="float">
</argument>
<description>
Returns a random point uniformly distributed strictly [b]on[/b] the circle's boundary.
This method is faster than [method rand_point_in_ring] with the following equivalent code:
[codeblock]
GoostGeometry2D.rand_point_in_ring(radius, radius)
[/codeblock]
[b]Note[/b]: the point may slightly deviate from the actual circle's boundary due to floating point error accumulation.
</description>
</method>
<method name="regular_polygon" qualifiers="const">
<return type="PoolVector2Array">
</return>
Expand Down
24 changes: 24 additions & 0 deletions tests/project/goost/core/math/2d/geometry/test_geometry_2d.gd
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,27 @@ func test_regular_polygon():
func test_circle():
solution = GoostGeometry2D.circle(SIZE, 0.25)
assert_eq(solution.size(), 32)


func test_rand_point_in_circle():
var max_radius = 10.0
for i in 100:
var point = GoostGeometry2D.rand_point_in_circle(max_radius)
assert_true(Geometry.is_point_in_circle(point, Vector2(), max_radius))
assert_lt(point.length(), max_radius)


func test_rand_point_on_circle():
var max_radius = 50.0
for i in 100:
var point = GoostGeometry2D.rand_point_on_circle(max_radius)
assert_true(is_equal_approx(point.length(), max_radius))


func test_rand_point_in_ring():
var min_radius = 10.0
var max_radius = 100.0
for i in 100:
var point = GoostGeometry2D.rand_point_in_ring(min_radius, max_radius)
assert_lt(point.length(), max_radius)
assert_gt(point.length(), min_radius)