diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml new file mode 100644 index 0000000..11aa958 --- /dev/null +++ b/.github/workflows/pytest.yml @@ -0,0 +1,23 @@ +name: pytest + +on: + push: + branches: + - main + +permissions: + contents: write + +jobs: + RunTest: + runs-on: ubuntu-latest + steps: + - name: Run pytest + uses: pavelzw/pytest-action@v2 + with: + verbose: true + emoji: true + job-summary: true + custom-arguments: '-q' + click-to-expand: true + report-title: 'Test Report' \ No newline at end of file diff --git a/mbcp/mp_math/angle.py b/mbcp/mp_math/angle.py index 6240b96..2cd02ec 100644 --- a/mbcp/mp_math/angle.py +++ b/mbcp/mp_math/angle.py @@ -12,6 +12,7 @@ import math from typing import overload from .const import PI # type: ignore +from .utils import approx class AnyAngle: @@ -129,12 +130,21 @@ class AnyAngle: def __add__(self, other: 'AnyAngle') -> 'AnyAngle': return AnyAngle(self.radian + other.radian, is_radian=True) + def __eq__(self, other): + return approx(self.radian, other.radian) + def __sub__(self, other: 'AnyAngle') -> 'AnyAngle': return AnyAngle(self.radian - other.radian, is_radian=True) def __mul__(self, other: float) -> 'AnyAngle': return AnyAngle(self.radian * other, is_radian=True) + def __repr__(self): + return f"AnyAngle({self.radian}, is_radian=True)" + + def __str__(self): + return f"AnyAngle({self.degree}° or {self.radian} rad)" + @overload def __truediv__(self, other: float) -> 'AnyAngle': ... diff --git a/mbcp/mp_math/line.py b/mbcp/mp_math/line.py index 9fdde2d..c108d9a 100644 --- a/mbcp/mp_math/line.py +++ b/mbcp/mp_math/line.py @@ -64,14 +64,23 @@ class Line3: Returns: 距离 Raises: - ValueError: 直线不平行 TypeError: 不支持的类型 """ if isinstance(other, Line3): - if self.is_parallel(other): - return (self.point - other.point).cross(self.direction).length / self.direction.length + # 相交/重合 = 0;平行和异面需要计算距离 + if self == other: + # 重合 + return 0 + elif self.is_parallel(other): + # 平行 + return (other.point - self.point).cross(self.direction).length / self.direction.length + elif not self.is_coplanar(other): + # 异面 + return abs(self.direction.cross(other.direction) @ (self.point - other.point) / self.direction.cross(other.direction).length) else: - raise ValueError("Lines are not parallel.") + # 相交 + return 0 + elif isinstance(other, Point3): return (other - self.point).cross(self.direction).length / self.direction.length else: diff --git a/mbcp/mp_math/vector.py b/mbcp/mp_math/vector.py index 7e1c168..b82b732 100644 --- a/mbcp/mp_math/vector.py +++ b/mbcp/mp_math/vector.py @@ -6,10 +6,11 @@ import numpy as np from .const import APPROX from .mp_math_typing import RealNumber from .point import Point3 +from .angle import AnyAngle from .utils import approx -if TYPE_CHECKING: - from .angle import AnyAngle +# if TYPE_CHECKING: +# class Vector3: @@ -50,6 +51,10 @@ class Vector3: def cross(self, other: 'Vector3') -> 'Vector3': """ 向量积 叉乘:v1 cross v2 -> v3 + + 叉乘为0,则两向量平行。 + 其余结果的模为平行四边形的面积。 + 返回如下行列式的结果: ``i j k`` diff --git a/tests/test_angle.py b/tests/test_angle.py new file mode 100644 index 0000000..f0943e8 --- /dev/null +++ b/tests/test_angle.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/28 上午3:16 +@Author : snowykami +@Email : snowykami@outlook.com +@File : test_angle.py +@Software: PyCharm +""" +from mbcp.mp_math.angle import AnyAngle +from mbcp.mp_math.const import PI +from tests.answer import output_ans + + +class TestAngle: + def test_radian_to_degree(self): + angle = AnyAngle(1, is_radian=True) + output_ans(180 / PI, angle.degree, question="弧度转角度1") + + angle = AnyAngle(2, is_radian=True) + output_ans(360 / PI, angle.degree, question="弧度转角度2") + + angle = AnyAngle(PI, is_radian=True) + output_ans(180, angle.degree, question="弧度转角度3") + + def test_degree_to_radian(self): + angle = AnyAngle(180) + output_ans(PI, angle.radian, question="角度转弧度1") + + angle = AnyAngle(360) + output_ans(2 * PI, angle.radian, question="角度转弧度2") + + angle = AnyAngle(90) + output_ans(PI / 2, angle.radian, question="角度转弧度3") diff --git a/tests/test_line3.py b/tests/test_line3.py index 26516c0..c2ab133 100644 --- a/tests/test_line3.py +++ b/tests/test_line3.py @@ -10,7 +10,12 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved """ import logging +from liteyuki import logger # type: ignore + +from mbcp.mp_math.angle import AnyAngle +from mbcp.mp_math.const import PI from mbcp.mp_math.point import Point3 +from mbcp.mp_math.utils import approx from mbcp.mp_math.vector import Vector3 from mbcp.mp_math.line import Line3 from tests.answer import output_ans @@ -18,6 +23,68 @@ from tests.answer import output_ans class TestLine3: + def test_cal_angle(self) -> None: + """ + 计算两条直线的夹角 + """ + """测试样例们""" + samples: tuple[tuple[Line3, Line3, AnyAngle], ...] = ( + ( + Line3(Point3(0, 0, 0), Vector3(1, 1, 0)), + Line3(Point3(0, 0, 0), Vector3(1, 0, 0)), + AnyAngle(45) + ), + ( + Line3(Point3(0, 0, 0), Vector3(0, 1, 0)), + Line3(Point3(0, 0, 0), Vector3(1, 0, 0)), + AnyAngle(90) + ) + ) + + for sample in samples: + correct_ans = sample[2] + actual_ans = sample[0].cal_angle(sample[1]) + output_ans(correct_ans, actual_ans, question="计算两条直线的夹角") + assert correct_ans == actual_ans + + def test_cal_distance(self) -> None: + """ + 计算两条直线的距离 + """ + """测试样例们""" + samples: tuple[tuple[Line3, Line3, float], ...] = ( + ( # 重合 + Line3(Point3(0, 0, 0), Vector3(1, 1, 1)), + Line3(Point3(0, 0, 0), Vector3(2, 2, 2)), + 0 + ), + ( # 两条直线相交 + Line3(Point3(0, 0, 0), Vector3(1, 1, 0)), + Line3(Point3(0, 0, 0), Vector3(1, 0, 0)), + 0 + ), + ( # 平行线 + Line3.from_two_points(Point3(0, 0, 0), Point3(1, 1, 0)), + Line3.from_two_points(Point3(0, 0, 1), Point3(1, 1, 1)), + 1 + ), + ( # 异面 + Line3.from_two_points(Point3(0, 0, 0), Point3(1, 1, 0)), + Line3.from_two_points(Point3(1, 0, 2), Point3(0, 1, 2)), + 2 + ), + ( # 异面2 + Line3.from_two_points(Point3(1, 0, 1), Point3(2, 1 ,0)), + Line3.from_two_points(Point3(1, 2, 0), Point3(0, 1, 1)), + 2 ** 0.5 + ) + ) + + for sample in samples: + correct_ans = sample[2] + actual_ans = sample[0].cal_distance(sample[1]) + output_ans(correct_ans, actual_ans,approx(correct_ans, actual_ans), question="计算两条直线的距离") + def test_equal(self): line1 = Line3(Point3(1, 1, 1), Vector3(1, 1, 1)) line2 = Line3(Point3(1, 1, 1), Vector3(2, 2, 2)) @@ -25,7 +92,7 @@ class TestLine3: # 反例 line1 = Line3(Point3(1, 1, 1), Vector3(1, 1, 1)) - line2 = Line3(Point3(1, 1, 1), Vector3(2, 2, 2.000000001)) + line2 = Line3(Point3(1, 1, 1), Vector3(2, 2, 2.1)) output_ans(False, line1 == line2, question="判断两条直线是否不相等") def test_approx(self): diff --git a/tests/test_vector3.py b/tests/test_vector3.py index a4abd6e..d4fcec8 100644 --- a/tests/test_vector3.py +++ b/tests/test_vector3.py @@ -10,6 +10,8 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved """ import logging +from mbcp.mp_math.angle import AnyAngle +from mbcp.mp_math.const import PI from mbcp.mp_math.vector import Vector3 from tests.answer import output_ans @@ -18,6 +20,26 @@ from tests.answer import output_ans class TestVector3: """测试问题集""" + def test_cal_angle(self): + """ + 测试计算两个向量的夹角 + Returns: + """ + """小题1""" + v1 = Vector3(0 ,1, 0) + v2 = Vector3(0, 0, 1) + correct_ans = AnyAngle(90) + actual_ans = v1.cal_angle(v2) + output_ans(correct_ans, actual_ans) + + """小题2""" + v1 = Vector3(0, 1, 0) + v2 = Vector3(1, 1, 0) + correct_ans = AnyAngle(45) + actual_ans = v1.cal_angle(v2) + output_ans(correct_ans, actual_ans) + + def test_vector_cross_product(self): """ 测试向量叉乘