You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

229 lines
6.6 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import pygame
import math
class Vector2D:
def __init__(self, x: float = 0, y: float = 0):
self._vector = pygame.Vector2(x, y)
@property
def x(self) -> float:
return self._vector.x
@x.setter
def x(self, value: float):
self._vector.x = value
@property
def y(self) -> float:
return self._vector.y
@y.setter
def y(self, value: float):
self._vector.y = value
def __add__(self, other):
if isinstance(other, Vector2D):
return Vector2D(self.x + other.x, self.y + other.y)
else:
result = self._vector + other._vector
return Vector2D(result.x, result.y)
def __sub__(self, other):
if isinstance(other, Vector2D):
return Vector2D(self.x - other.x, self.y - other.y)
else:
result = self._vector - other._vector
return Vector2D(result.x, result.y)
def __mul__(self, scalar):
if isinstance(scalar, Vector2D):
return Vector2D(self.x * scalar.x, self.y * scalar.y)
else:
result = self._vector * scalar
return Vector2D(result.x, result.y)
def __truediv__(self, other):
if isinstance(other, Vector2D):
return Vector2D(self.x / other.x, self.y / other.y)
else:
result = self._vector / other
return Vector2D(result.x, result.y)
def length(self):
return self._vector.length()
def normalize(self):
result = self._vector.normalize()
return Vector2D(result.x, result.y)
def dot(self, other):
if isinstance(other, Vector2D):
return self._vector.dot(other._vector)
else:
return self._vector.dot(other)
class Transform2D:
IDENTITY = None
FLIP_X = None
FLIP_Y = None
def __init__(self, position: Vector2D = None, rotation: float = 0.0,
scale: Vector2D = None):
"""
åˆ<C3A5>å§åŒå<E28093>˜æ<CB9C>¢çŸ©é˜µ
Args:
position: ä½<C3A4>ç½®å<C2AE>é‡<C3A9>
rotation: 旋转角度(弧度)
scale: 缩放å<C2BE>é‡<C3A9>
"""
if position is None:
position = Vector2D(0, 0)
if scale is None:
scale = Vector2D(1.0, 1.0)
self._set_transform(position, rotation, scale)
def _set_transform(self, position: Vector2D, rotation: float, scale: Vector2D):
"""
æ ¹æ<C2B9>®ä½<C3A4>ç½®ã€<C3A3>æ—转åŒç¼©æ”¾è®¾ç½®å<C2AE>˜æ<CB9C>¢çŸ©é˜µ
"""
cos = math.cos(rotation)
sin = math.sin(rotation)
# 设置基å<C2BA>é‡<C3A9>(æ—转åŒç¼©æ”¾çš„组å<E2809E>ˆï¼‰
self.x = Vector2D(cos * scale.x, sin * scale.x)
self.y = Vector2D(-sin * scale.y, cos * scale.y)
# 设置原点(平移)
self.origin = position
@property
def position(self) -> Vector2D:
"""获å<EFBFBD>ä½<EFBFBD>ç½®"""
return self.origin
@position.setter
def position(self, value: Vector2D):
"""设置ä½<EFBFBD>ç½®"""
self.origin = value
@property
def scale(self) -> Vector2D:
"""获å<EFBFBD>缩放"""
# 计算基å<C2BA>é‡<C3A9>的长度æ<C2A6>¥å¾—到缩放å å­<C3A5>
scale_x = math.sqrt(self.x.x * self.x.x + self.x.y * self.x.y)
scale_y = math.sqrt(self.y.x * self.y.x + self.y.y * self.y.y)
return Vector2D(scale_x, scale_y)
@scale.setter
def scale(self, value: Vector2D):
"""设置缩放"""
current_scale = self.scale
if current_scale.x != 0 and current_scale.y != 0:
# 调整基å<C2BA>é‡<C3A9>以é€åº”æ°çš„缩放
scale_ratio_x = value.x / current_scale.x
scale_ratio_y = value.y / current_scale.y
self.x = Vector2D(self.x.x * scale_ratio_x, self.x.y * scale_ratio_x)
self.y = Vector2D(self.y.x * scale_ratio_y, self.y.y * scale_ratio_y)
@property
def rotation(self) -> float:
"""获å<EFBFBD>æ—转è§åº¦"""
return math.atan2(self.x.y, self.x.x)
@rotation.setter
def rotation(self, value: float):
"""设置旋转角度"""
current_scale = self.scale
cos = math.cos(value)
sin = math.sin(value)
self.x = Vector2D(cos * current_scale.x, sin * current_scale.x)
self.y = Vector2D(-sin * current_scale.y, cos * current_scale.y)
def transform_point(self, point: Vector2D) -> Vector2D:
"""
应用å<C2A8>˜æ<CB9C>¢åˆ°ç¹
Args:
point: è¦<C3A8>å<EFBFBD>˜æ<CB9C>¢çš„ç¹
Returns:
å<>˜æ<CB9C>¢å<C2A2>Žçš„ç¹
"""
# 应用线性å<C2A7>˜æ<CB9C>¢ç„¶å<C2B6>ŽåŠ ä¸Šå¹³ç§»
x = self.x * point.x
y = self.y * point.y
return self.origin + x + y
def multiply(self, other: 'Transform2D') -> 'Transform2D':
"""
å°†å<E280A0>¦ä¸€ä¸ªå<C2AA>˜æ<CB9C>¢åº”用到当å‰<C3A5>å<EFBFBD>˜æ<CB9C>¢
Args:
other: è¦<C3A8>应用的å<E2809E>˜æ<CB9C>¢
Returns:
æ°çš„å<E2809E>˜æ<CB9C>¢å¯¹è±¡
"""
# 组å<E2809E>ˆä¸¤ä¸ªå<C2AA>˜æ<CB9C>¢çŸ©é˜µ
new_x = Vector2D(
self.x.x * other.x.x + self.y.x * other.x.y,
self.x.y * other.x.x + self.y.y * other.x.y
)
new_y = Vector2D(
self.x.x * other.y.x + self.y.x * other.y.y,
self.x.y * other.y.x + self.y.y * other.y.y
)
new_origin = self.transform_point(other.origin)
result = Transform2D()
result.x = new_x
result.y = new_y
result.origin = new_origin
return result
def inverse(self) -> 'Transform2D':
"""
计算逆å<E280A0>˜æ<CB9C>¢
Returns:
逆å<E280A0>˜æ<CB9C>¢
"""
# 计算2x2矩阵的行列å¼<C3A5>
det = self.x.x * self.y.y - self.x.y * self.y.x
if abs(det) < 1e-10:
raise ValueError("Transform is not invertible")
inv_det = 1.0 / det
# 计算逆矩阵的基å<C2BA>é‡<C3A9>
inv_x = Vector2D(
self.y.y * inv_det,
-self.x.y * inv_det
)
inv_y = Vector2D(
-self.y.x * inv_det,
self.x.x * inv_det
)
# 计算逆矩阵的原点
origin_dot = self.origin.x * inv_x.x + self.origin.y * inv_x.y
origin_dot2 = self.origin.x * inv_y.x + self.origin.y * inv_y.y
inv_origin = Vector2D(-origin_dot, -origin_dot2)
result = Transform2D()
result.x = inv_x
result.y = inv_y
result.origin = inv_origin
return result
# åˆ<C3A5>å§åŒé¢„定义常é‡<C3A9>
Transform2D.IDENTITY = Transform2D()
Transform2D.FLIP_X = Transform2D(scale=Vector2D(-1, 1))
Transform2D.FLIP_Y = Transform2D(scale=Vector2D(1, -1))