From edc6a5ddaf7a096cfc2d0fcf224b59eb08805aa1 Mon Sep 17 00:00:00 2001 From: snowy Date: Wed, 7 Aug 2024 00:54:13 +0800 Subject: [PATCH] :zap: add some mathematical method for object `Segment3` --- mbcp/mp_math/line.py | 6 +- mbcp/mp_math/point.py | 38 ++++++++++++- mbcp/mp_math/py.typed | 2 + mbcp/mp_math/segment.py | 90 ++++++++++++++++++++++++++++++ mbcp/mp_math/vector.py | 119 ++++++++++++++++++++++++++++++++++++---- mbcp/py.typed | 2 + tests/test_vector.py | 10 ++-- 7 files changed, 243 insertions(+), 24 deletions(-) create mode 100644 mbcp/mp_math/segment.py create mode 100644 mbcp/py.typed diff --git a/mbcp/mp_math/line.py b/mbcp/mp_math/line.py index f2a1da3..8bcc38a 100644 --- a/mbcp/mp_math/line.py +++ b/mbcp/mp_math/line.py @@ -8,10 +8,10 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved @File : line.py @Software: PyCharm """ -from typing import overload, TYPE_CHECKING +from typing import TYPE_CHECKING if TYPE_CHECKING: - from .point import Point3 + from .point import Point3 # type: ignore class Line3: @@ -49,8 +49,6 @@ class Line3: :param line: :return: """ - normal1 = (self.b, -self.a, 0) - normal2 = (-line.b, line.a, 0) if self.is_parallel(line): raise ValueError("Lines are parallel and do not intersect.") diff --git a/mbcp/mp_math/point.py b/mbcp/mp_math/point.py index 8a729f9..5fe7b84 100644 --- a/mbcp/mp_math/point.py +++ b/mbcp/mp_math/point.py @@ -1,7 +1,7 @@ -from typing import overload, TYPE_CHECKING +from typing import TYPE_CHECKING, overload -if TYPE_CHECKING: # type: ignore - from .vector import Vector3 +if TYPE_CHECKING: + from .vector import Vector3 # type: ignore class Point3: @@ -19,5 +19,37 @@ class Point3: def __str__(self): return f"Point3({self.x}, {self.y}, {self.z})" + @overload def __add__(self, other: "Vector3") -> "Point3": + ... + + @overload + def __add__(self, other: "Point3") -> "Point3": + ... + + def __add__(self, other): + """ + P + V -> P + P + P -> P + :param other: + :return: + """ return Point3(self.x + other.x, self.y + other.y, self.z + other.z) + + def __sub__(self, other: "Point3") -> "Vector3": + """ + P - P -> V + + P - V -> P 已在 :class:`Vector3` 中实现 + :param other: + :return: + """ + return Vector3(self.x - other.x, self.y - other.y, self.z - other.z) + + def __truediv__(self, other: float) -> "Point3": + """ + P / n -> P + :param other: + :return: + """ + return Point3(self.x / other, self.y / other, self.z / other) diff --git a/mbcp/mp_math/py.typed b/mbcp/mp_math/py.typed index e69de29..a8a7f97 100644 --- a/mbcp/mp_math/py.typed +++ b/mbcp/mp_math/py.typed @@ -0,0 +1,2 @@ +# py.typed +partial \ No newline at end of file diff --git a/mbcp/mp_math/segment.py b/mbcp/mp_math/segment.py new file mode 100644 index 0000000..db3f328 --- /dev/null +++ b/mbcp/mp_math/segment.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/7 上午12:42 +@Author : snowykami +@Email : snowykami@outlook.com +@File : segment.py +@Software: PyCharm +""" + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .point import Point3 # type: ignore + from .vector import Vector3 # type: ignore + + +class Segment3: + def __init__(self, start: "Point3", end: "Point3"): + """ + 三维空间中的线段。 + :param start: + :param end: + """ + self._start = start + self._end = end + + """方向向量""" + self._direction = self._end - self._start + """长度""" + self._length = self._direction.length + """中心点""" + self._midpoint = (self._start + self._end) / 2 + + def __str__(self): + return f"Segment3({self._start}, {self._end})" + + def _unset_properties(self): + self._length = None + self._direction = None + self._midpoint = None + + @property + def start(self) -> "Point3": + return self._start + + @start.setter + def start(self, value: "Point3"): + self._start = value + self._unset_properties() + + @property + def end(self) -> "Point3": + return self._end + + @end.setter + def end(self, value: "Point3"): + self._end = value + self._unset_properties() + + @property + def length(self) -> float: + """ + 线段的长度。 + :return: + """ + if self._length is None: + self._length = (self._end - self._start).length + return self._length + + @property + def direction(self) -> "Vector3": + """ + 线段的方向向量。 + :return: + """ + if self._direction is None: + self._direction = self._end - self._start + return self._direction + + @property + def midpoint(self) -> "Point3": + """ + 线段的中点。 + :return: + """ + if self._midpoint is None: + self._midpoint = (self._start + self._end) / 2 + return self._midpoint diff --git a/mbcp/mp_math/vector.py b/mbcp/mp_math/vector.py index b96dea2..a3c9ccb 100644 --- a/mbcp/mp_math/vector.py +++ b/mbcp/mp_math/vector.py @@ -1,7 +1,7 @@ from typing import overload, TYPE_CHECKING -if TYPE_CHECKING: # type: ignore - from .point import Point3 +if TYPE_CHECKING: + from .point import Point3 # type: ignore class Vector3: @@ -12,12 +12,65 @@ class Vector3: :param y: :param z: """ - self.x = x - self.y = y - self.z = z + self._x = x + self._y = y + self._z = z + self._length = (x ** 2 + y ** 2 + z ** 2) ** 0.5 + self._normalized = self / self._length def __str__(self): - return f"Vector3({self.x}, {self.y}, {self.z})" + return f"Vector3({self._x}, {self._y}, {self._z})" + + def _unset_properties(self): + self._length = None + self._normalized = None + + @property + def x(self): + return self._x + + @x.setter + def x(self, value): + self._x = value + self._unset_properties() + + @property + def y(self): + return self._y + + @y.setter + def y(self, value): + self._y = value + self._unset_properties() + + @property + def z(self): + return self._z + + @z.setter + def z(self, value): + self._z = value + self._unset_properties() + + @property + def length(self) -> float: + """ + 向量的模。 + :return: + """ + if self._length is None: + self._length = (self._x ** 2 + self._y ** 2 + self._z ** 2) ** 0.5 + return self._length + + @property + def normalized(self) -> 'Vector3': + """ + 返回该向量的单位向量。 + :return: + """ + if self._normalized is None: + self._normalized = self / self.length + return self._normalized @overload def __add__(self, other: 'Vector3') -> 'Vector3': @@ -29,14 +82,14 @@ class Vector3: def __add__(self, other): if isinstance(other, Vector3): - return Vector3(self.x + other.x, self.y + other.y, self.z + other.z) + return Vector3(self._x + other._x, self._y + other._y, self._z + other._z) elif isinstance(other, Point3): - return Point3(self.x + other.x, self.y + other.y, self.z + other.z) + return Point3(self._x + other.x, self._y + other.y, self._z + other.z) else: raise TypeError(f"unsupported operand type(s) for +: 'Vector3' and '{type(other)}'") def __radd__(self, other: 'Point3') -> 'Point3': - return Point3(self.x + other.x, self.y + other.y, self.z + other.z) + return Point3(self._x + other.x, self._y + other.y, self._z + other.z) @overload def __sub__(self, other: 'Vector3') -> 'Vector3': @@ -54,9 +107,9 @@ class Vector3: :return: """ if isinstance(other, Vector3): - return Vector3(self.x - other.x, self.y - other.y, self.z - other.z) + return Vector3(self._x - other._x, self._y - other._y, self._z - other._z) elif isinstance(other, Point3): - return Point3(self.x - other.x, self.y - other.y, self.z - other.z) + return Point3(self._x - other.x, self._y - other.y, self._z - other.z) else: raise TypeError(f"unsupported operand type(s) for -: 'Vector3' and '{type(other)}'") @@ -68,6 +121,48 @@ class Vector3: """ if isinstance(other, Point3): - return Point3(other.x - self.x, other.y - self.y, other.z - self.z) + return Point3(other.x - self._x, other.y - self._y, other.z - self._z) else: raise TypeError(f"unsupported operand type(s) for -: '{type(other)}' and 'Vector3'") + + @overload + def __mul__(self, other: float) -> 'Vector3': + ... + + @overload + def __mul__(self, other: 'Vector3') -> float: + ... + + def __mul__(self, other): + """ + 乘法。包括点乘和数乘。 + :param other: + :return: + """ + if isinstance(other, (int, float)): + return Vector3(self._x * other, self._y * other, self._z * other) + elif isinstance(other, Vector3): + return self._x * other._x + self._y * other._y + self._z * other._z + else: + raise TypeError(f"unsupported operand type(s) for *: 'Vector3' and '{type(other)}'") + + def __rmul__(self, other: float) -> 'Vector3': + """ + 右乘。 + :param other: + :return: + """ + return Vector3(self._x * other, self._y * other, self._z * other) + + def __matmul__(self, other: 'Vector3') -> 'Vector3': + """ + 叉乘。 + :param other: 另一个向量 + :return: 叉乘结果向量 + """ + return Vector3(self._y * other._z - self._z * other._y, + self._z * other._x - self._x * other._z, + self._x * other._y - self._y * other._x) + + def __truediv__(self, other: float) -> 'Vector3': + return Vector3(self._x / other, self._y / other, self._z / other) diff --git a/mbcp/py.typed b/mbcp/py.typed new file mode 100644 index 0000000..a8a7f97 --- /dev/null +++ b/mbcp/py.typed @@ -0,0 +1,2 @@ +# py.typed +partial \ No newline at end of file diff --git a/tests/test_vector.py b/tests/test_vector.py index 4a4b890..f2bcc97 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -8,15 +8,15 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved @File : test_vector.py @Software: PyCharm """ -from mcpe.mp_math.vector import Vector3 -from mcpe.mp_math.point import Point3 +from mbcp.mp_math.vector import Vector3 +from mbcp.mp_math.point import Point3 def test_v(): v1 = Vector3(1, 2, 3) v2 = Vector3(4, 5, 6) v3 = v1 + v2 - assert v3.x == 5 - assert v3.y == 7 - assert v3.z == 9 + assert v3._x == 5 + assert v3._y == 7 + assert v3._z == 9 print("test_v 1111passed")