mirror of
https://github.com/snowykami/mbcp.git
synced 2024-11-22 14:17:38 +08:00
🐛 fix line.cal_intersection
This commit is contained in:
parent
5f4adbb75f
commit
ab17af3fda
23
.github/workflows/pytest.yml
vendored
Normal file
23
.github/workflows/pytest.yml
vendored
Normal 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'
|
@ -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':
|
||||||
...
|
...
|
||||||
|
@ -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:
|
||||||
|
@ -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
35
tests/test_angle.py
Normal 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")
|
@ -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):
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
测试向量叉乘
|
测试向量叉乘
|
||||||
|
Loading…
Reference in New Issue
Block a user