🐛 fix line.cal_intersection

This commit is contained in:
远野千束 2024-08-28 04:22:22 +08:00
parent 5f4adbb75f
commit ab17af3fda
7 changed files with 178 additions and 7 deletions

23
.github/workflows/pytest.yml vendored Normal file
View File

@ -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'

View File

@ -12,6 +12,7 @@ import math
from typing import overload from typing import overload
from .const import PI # type: ignore from .const import PI # type: ignore
from .utils import approx
class AnyAngle: class AnyAngle:
@ -129,12 +130,21 @@ class AnyAngle:
def __add__(self, other: 'AnyAngle') -> 'AnyAngle': def __add__(self, other: 'AnyAngle') -> 'AnyAngle':
return AnyAngle(self.radian + other.radian, is_radian=True) 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': def __sub__(self, other: 'AnyAngle') -> 'AnyAngle':
return AnyAngle(self.radian - other.radian, is_radian=True) return AnyAngle(self.radian - other.radian, is_radian=True)
def __mul__(self, other: float) -> 'AnyAngle': def __mul__(self, other: float) -> 'AnyAngle':
return AnyAngle(self.radian * other, is_radian=True) 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 @overload
def __truediv__(self, other: float) -> 'AnyAngle': def __truediv__(self, other: float) -> 'AnyAngle':
... ...

View File

@ -64,14 +64,23 @@ class Line3:
Returns: Returns:
距离 距离
Raises: Raises:
ValueError: 直线不平行
TypeError: 不支持的类型 TypeError: 不支持的类型
""" """
if isinstance(other, Line3): if isinstance(other, Line3):
if self.is_parallel(other): # 相交/重合 = 0平行和异面需要计算距离
return (self.point - other.point).cross(self.direction).length / self.direction.length 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: else:
raise ValueError("Lines are not parallel.") # 相交
return 0
elif isinstance(other, Point3): elif isinstance(other, Point3):
return (other - self.point).cross(self.direction).length / self.direction.length return (other - self.point).cross(self.direction).length / self.direction.length
else: else:

View File

@ -6,10 +6,11 @@ import numpy as np
from .const import APPROX from .const import APPROX
from .mp_math_typing import RealNumber from .mp_math_typing import RealNumber
from .point import Point3 from .point import Point3
from .angle import AnyAngle
from .utils import approx from .utils import approx
if TYPE_CHECKING: # if TYPE_CHECKING:
from .angle import AnyAngle #
class Vector3: class Vector3:
@ -50,6 +51,10 @@ class Vector3:
def cross(self, other: 'Vector3') -> 'Vector3': def cross(self, other: 'Vector3') -> 'Vector3':
""" """
向量积 叉乘v1 cross v2 -> v3 向量积 叉乘v1 cross v2 -> v3
叉乘为0则两向量平行
其余结果的模为平行四边形的面积
返回如下行列式的结果 返回如下行列式的结果
``i j k`` ``i j k``

35
tests/test_angle.py Normal file
View File

@ -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")

View File

@ -10,7 +10,12 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
""" """
import logging 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.point import Point3
from mbcp.mp_math.utils import approx
from mbcp.mp_math.vector import Vector3 from mbcp.mp_math.vector import Vector3
from mbcp.mp_math.line import Line3 from mbcp.mp_math.line import Line3
from tests.answer import output_ans from tests.answer import output_ans
@ -18,6 +23,68 @@ from tests.answer import output_ans
class TestLine3: 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): def test_equal(self):
line1 = Line3(Point3(1, 1, 1), Vector3(1, 1, 1)) line1 = Line3(Point3(1, 1, 1), Vector3(1, 1, 1))
line2 = Line3(Point3(1, 1, 1), Vector3(2, 2, 2)) 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)) 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="判断两条直线是否不相等") output_ans(False, line1 == line2, question="判断两条直线是否不相等")
def test_approx(self): def test_approx(self):

View File

@ -10,6 +10,8 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
""" """
import logging import logging
from mbcp.mp_math.angle import AnyAngle
from mbcp.mp_math.const import PI
from mbcp.mp_math.vector import Vector3 from mbcp.mp_math.vector import Vector3
from tests.answer import output_ans from tests.answer import output_ans
@ -18,6 +20,26 @@ from tests.answer import output_ans
class TestVector3: 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): def test_vector_cross_product(self):
""" """
测试向量叉乘 测试向量叉乘