From 23077982b80cb23c6b1b62a55f09f7a67b3afa93 Mon Sep 17 00:00:00 2001 From: RydertHuGlIfE Date: Sat, 3 Jan 2026 18:18:26 +0530 Subject: [PATCH 1/2] #issue2770 we could add math functions like this and still use the previous one for backwards compatiblity here's my implementation with some test cases as well they pass --- arcade/math.py | 34 +++++++++++++++++++++++----------- tests/unit/test_math.py | 13 +++++++++++++ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/arcade/math.py b/arcade/math.py index a35237fcad..5ed32fd4c1 100644 --- a/arcade/math.py +++ b/arcade/math.py @@ -1,6 +1,6 @@ import math import random -from typing import TypeVar +from typing import TypeVar, overload from pyglet.math import Vec2, Vec3 @@ -311,18 +311,30 @@ def rand_vec_magnitude( return vel.x, vel.y -def get_distance(x1: float, y1: float, x2: float, y2: float) -> float: - """ - Get the distance between two points. +@overload +def get_distance(p1: Point2, p2: Point2) -> float: ... + +@overload +def get_distance(x1: float, y1: float, x2: float, y2: float) -> float: ... + - Args: - x1 (float): x coordinate of the first point - y1 (float): y coordinate of the first point - x2 (float): x coordinate of the second point - y2 (float): y coordinate of the second point - """ - return math.hypot(x1 - x2, y1 - y2) +def get_distance(*args) -> float: + """ + get distance between two points. + args: + can be called as: + get_distance(p1, p2) with point2 args + get_distance(x1, y1, x2, y2) with float args + + get distance with x1 y1 can be removed later down the line for now it is for backwards compatiblity + """ + if len(args) == 2: + return math.hypot(args[0][0] - args[1][0], args[0][1] - args[1][1]) + elif len(args) == 4: + return math.hypot(args[0] - args[2], args[1] - args[3]) + else: + raise ValueError("get_distance() takes 2 or 4 arguments") def rotate_point( x: float, diff --git a/tests/unit/test_math.py b/tests/unit/test_math.py index cd25515506..a6053348ff 100644 --- a/tests/unit/test_math.py +++ b/tests/unit/test_math.py @@ -87,3 +87,16 @@ def test_rand_vec_spread_deg(): def test_rand_vec_magnitude(): """Smoke test""" rand_vec_magnitude(30.5, 3.3, 4.4) + + +#adding more test for getdistance with floats of point2 and x1 type + +def test_get_distance_with_floats(): + """Test get_distance with x1, y1, x2, y2 parameters""" + assert get_distance(0, 0, 3, 4) == approx(5.0) + assert get_distance(1, 1, 4, 5) == approx(5.0) + +def test_get_distance_with_point2(): + """Test get_distance with Point2 parameters""" + assert get_distance((0, 0), (3, 4)) == approx(5.0) + assert get_distance((1, 1), (4, 5)) == approx(5.0) \ No newline at end of file From 6377d98bb89ed38470624bb984c54f36607aa3fb Mon Sep 17 00:00:00 2001 From: RydertHuGlIfE Date: Sat, 3 Jan 2026 21:07:53 +0530 Subject: [PATCH 2/2] add point2 overloads for other non-standardized functions including rotate_point, get_angle_degrees and get_angle_radians --- arcade/math.py | 132 +++++++++++++++++++++++++--------------- tests/unit/test_math.py | 43 ++++++++++++- 2 files changed, 125 insertions(+), 50 deletions(-) diff --git a/arcade/math.py b/arcade/math.py index 5ed32fd4c1..d21eecbb90 100644 --- a/arcade/math.py +++ b/arcade/math.py @@ -336,38 +336,58 @@ def get_distance(*args) -> float: else: raise ValueError("get_distance() takes 2 or 4 arguments") + +@overload def rotate_point( x: float, y: float, cx: float, cy: float, angle_degrees: float, -) -> Point2: - """ - Rotate a point around a center. - - Args: - x (float): x value of the point you want to rotate - y (float): y value of the point you want to rotate - cx (float): x value of the center point you want to rotate around - cy (float): y value of the center point you want to rotate around - angle_degrees (float): Angle, in degrees, to rotate - """ - temp_x = x - cx - temp_y = y - cy - - # now apply rotation - angle_radians = math.radians(angle_degrees) - cos_angle = math.cos(angle_radians) - sin_angle = math.sin(angle_radians) - rotated_x = temp_x * cos_angle + temp_y * sin_angle - rotated_y = -temp_x * sin_angle + temp_y * cos_angle - - # translate back - x = round(rotated_x + cx, _PRECISION) - y = round(rotated_y + cy, _PRECISION) +) -> Point2: ... - return x, y +@overload +def rotate_point( + p1: Point2 , p2: Point2, + angle_degrees: float, +) -> Point2: ... + + +def rotate_point(*args) -> Point2: + + if len(args) == 3: + p1, p2, angle_degrees = args + temp_x = p1[0] - p2[0] + temp_y = p1[1] - p2[1] + angle_radians = math.radians(angle_degrees) + cos_angle = math.cos(angle_radians) + sin_angle = math.sin(angle_radians) + rotated_x = temp_x * cos_angle + temp_y * sin_angle + rotated_y = -temp_x * sin_angle + temp_y * cos_angle + + # translate back + x = round(rotated_x + p2[0], _PRECISION) + y = round(rotated_y + p2[1], _PRECISION) + + return x, y + elif len(args) == 5: + x, y, cx, cy, angle_degrees = args + temp_x = x - cx + temp_y = y - cy + angle_radians = math.radians(angle_degrees) + cos_angle = math.cos(angle_radians) + sin_angle = math.sin(angle_radians) + rotated_x = temp_x * cos_angle + temp_y * sin_angle + rotated_y = -temp_x * sin_angle + temp_y * cos_angle + + # translate back + x = round(rotated_x + cx, _PRECISION) + y = round(rotated_y + cy, _PRECISION) + + return x, y + else: + raise ValueError("rotate_point() takes 2 or 4 arguments") + # scale around point @@ -436,35 +456,49 @@ def rotate_around_point(source: Point2, target: Point2, angle: float): return target[0] + dx, target[1] + dy -def get_angle_degrees(x1: float, y1: float, x2: float, y2: float) -> float: - """ - Get the angle in degrees between two points. +@overload +def get_angle_degrees(x1: float, y1: float, x2: float, y2: float) -> float: ... - Args: - x1 (float): x coordinate of the first point - y1 (float): y coordinate of the first point - x2 (float): x coordinate of the second point - y2 (float): y coordinate of the second point - """ - x_diff = x2 - x1 - y_diff = y2 - y1 - return -math.degrees(math.atan2(y_diff, x_diff)) +@overload +def get_angle_degrees(p1: Point2, p2: Point2) -> float: ... -def get_angle_radians(x1: float, y1: float, x2: float, y2: float) -> float: - """ - Get the angle in radians between two points. - Args: - x1 (float): x coordinate of the first point - y1 (float): y coordinate of the first point - x2 (float): x coordinate of the second point - y2 (float): y coordinate of the second point - """ - x_diff = x2 - x1 - y_diff = y2 - y1 - return math.atan2(x_diff, y_diff) +def get_angle_degrees(*args): + """same as other function can take arguments as p1,p2 [point range] or float args x1 y2..""" + + if len(args) == 2: + p1, p2 = args + x_diff = p2[0] - p1[0] + y_diff = p2[1] - p1[1] + elif len(args) == 4: + x1, y1, x2, y2 = args + x_diff = x2 - x1 + y_diff = y2 - y1 + else: + raise ValueError("get_angle_degrees() takes 2 or 4 arguments") + return -math.degrees(math.atan2(y_diff, x_diff)) + +@overload +def get_angle_radians(x1: float, y1: float, x2: float, y2: float) -> float: ... + +@overload +def get_angle_radians(p1: Point2, p2: Point2) -> float: ... + + +def get_angle_radians(*args): + if len(args) == 4: + x1, y1, x2, y2 = args + x_diff = x2 - x1 + y_diff = y2 - y1 + elif len(args) == 2: + p1, p2 = args + x_diff = p2[0] - p1[0] + y_diff = p2[1] - p1[1] + else: + raise ValueError("get_angle_radians() takes 2 or 4 arguments") + return math.atan2(x_diff, y_diff) def quaternion_rotation(axis: Point3, vector: Point3, angle: float) -> tuple[float, float, float]: """ diff --git a/tests/unit/test_math.py b/tests/unit/test_math.py index a6053348ff..41f1c2a9c2 100644 --- a/tests/unit/test_math.py +++ b/tests/unit/test_math.py @@ -99,4 +99,45 @@ def test_get_distance_with_floats(): def test_get_distance_with_point2(): """Test get_distance with Point2 parameters""" assert get_distance((0, 0), (3, 4)) == approx(5.0) - assert get_distance((1, 1), (4, 5)) == approx(5.0) \ No newline at end of file + assert get_distance((1, 1), (4, 5)) == approx(5.0) + +def test_rotate_point_with_floats(): + """Test rotate_point with x, y, cx, cy, angle parameters""" + # Rotate (1, 0) around origin by 90 degrees + result = rotate_point(1, 0, 0, 0, 90) + assert result[0] == approx(0.0, abs=0.01) + assert result[1] == approx(-1.0, abs=0.01) + + +def test_rotate_point_with_point2(): + """Test rotate_point with Point2 parameters""" + # Rotate (1, 0) around origin (0, 0) by 90 degrees + result = rotate_point((1, 0), (0, 0), 90) + assert result[0] == approx(0.0, abs=0.01) + assert result[1] == approx(-1.0, abs=0.01) + + +def test_get_angle_degrees_with_floats(): + """Test get_angle_degrees with x1, y1, x2, y2 parameters""" + # Angle from (0,0) to (1,0) should be 0 degrees + assert get_angle_degrees(0, 0, 1, 0) == approx(0.0) + # Angle from (0,0) to (0,1) should be -90 degrees + assert get_angle_degrees(0, 0, 0, 1) == approx(-90.0) + + +def test_get_angle_degrees_with_point2(): + """Test get_angle_degrees with Point2 parameters""" + assert get_angle_degrees((0, 0), (1, 0)) == approx(0.0) + assert get_angle_degrees((0, 0), (0, 1)) == approx(-90.0) + + +def test_get_angle_radians_with_floats(): + """Test get_angle_radians with x1, y1, x2, y2 parameters""" + import math + assert get_angle_radians(0, 0, 1, 0) == approx(math.pi / 2) + + +def test_get_angle_radians_with_point2(): + """Test get_angle_radians with Point2 parameters""" + import math + assert get_angle_radians((0, 0), (1, 0)) == approx(math.pi / 2) \ No newline at end of file