mirror of
https://github.com/LmeSzinc/StarRailCopilot.git
synced 2024-11-23 17:11:42 +00:00
184 lines
5.4 KiB
Python
184 lines
5.4 KiB
Python
import numpy as np
|
|
|
|
|
|
class Points:
|
|
def __init__(self, points, config):
|
|
if points is None:
|
|
self._bool = False
|
|
self.points = None
|
|
else:
|
|
self._bool = True
|
|
self.points = np.array(points)
|
|
if len(self.points.shape) == 1:
|
|
self.points = np.array([self.points])
|
|
self.config = config
|
|
self.x, self.y = self.points.T
|
|
|
|
def __str__(self):
|
|
return str(self.points)
|
|
|
|
def __iter__(self):
|
|
return iter(self.points)
|
|
|
|
def __getitem__(self, item):
|
|
return self.points[item]
|
|
|
|
def __len__(self):
|
|
return len(self.points)
|
|
|
|
def __bool__(self):
|
|
return self._bool
|
|
|
|
def link(self, point, is_horizontal=False):
|
|
if is_horizontal:
|
|
lines = [[y, np.pi/2] for y in self.y]
|
|
return Lines(lines, is_horizontal=True, config=self.config)
|
|
else:
|
|
x, y = point
|
|
theta = -np.arctan((self.x - x) / (self.y - y))
|
|
rho = self.x * np.cos(theta) + self.y * np.sin(theta)
|
|
lines = np.array([rho, theta]).T
|
|
return Lines(lines, is_horizontal=False, config=self.config)
|
|
|
|
|
|
class Lines:
|
|
def __init__(self, lines, is_horizontal, config):
|
|
if lines is None or len(lines) == 0:
|
|
self._bool = False
|
|
self.lines = None
|
|
else:
|
|
self._bool = True
|
|
self.lines = np.array(lines)
|
|
if len(self.lines.shape) == 1:
|
|
self.lines = np.array([self.lines])
|
|
self.rho, self.theta = self.lines.T
|
|
self.is_horizontal = is_horizontal
|
|
self.config = config
|
|
|
|
def __str__(self):
|
|
return str(self.lines)
|
|
|
|
def __iter__(self):
|
|
return iter(self.lines)
|
|
|
|
def __getitem__(self, item):
|
|
return Lines(self.lines[item], is_horizontal=self.is_horizontal, config=self.config)
|
|
|
|
def __len__(self):
|
|
if self:
|
|
return len(self.lines)
|
|
else:
|
|
return 0
|
|
|
|
def __bool__(self):
|
|
return self._bool
|
|
|
|
@property
|
|
def sin(self):
|
|
return np.sin(self.theta)
|
|
|
|
@property
|
|
def cos(self):
|
|
return np.cos(self.theta)
|
|
|
|
@property
|
|
def mean(self):
|
|
if not self:
|
|
return None
|
|
if self.is_horizontal:
|
|
return np.mean(self.lines, axis=0)
|
|
else:
|
|
x = np.mean(self.mid)
|
|
theta = np.mean(self.theta)
|
|
rho = x * np.cos(theta) + self.config.MID_Y * np.sin(theta)
|
|
return np.array((rho, theta))
|
|
|
|
@property
|
|
def mid(self):
|
|
if not self:
|
|
return np.array([])
|
|
if self.is_horizontal:
|
|
return self.rho
|
|
else:
|
|
return (self.rho - self.config.MID_Y * self.sin) / self.cos
|
|
|
|
def get_x(self, y):
|
|
return (self.rho - y * self.sin) / self.cos
|
|
|
|
def get_y(self, x):
|
|
return (self.rho - x * self.cos) / self.sin
|
|
|
|
def add(self, other):
|
|
if not other:
|
|
return self
|
|
lines = np.append(self.lines, other.lines, axis=0)
|
|
return Lines(lines, is_horizontal=self.is_horizontal, config=self.config)
|
|
|
|
def move(self, x, y):
|
|
if not self:
|
|
return self
|
|
if self.is_horizontal:
|
|
self.lines[:, 0] += y
|
|
else:
|
|
self.lines[:, 0] += x * self.cos + y * self.sin
|
|
return Lines(self.lines, is_horizontal=self.is_horizontal, config=self.config)
|
|
|
|
def sort(self):
|
|
if not self:
|
|
return self
|
|
lines = self.lines[np.argsort(self.mid)]
|
|
return Lines(lines, is_horizontal=self.is_horizontal, config=self.config)
|
|
|
|
def group(self, threshold=3):
|
|
if not self:
|
|
return self
|
|
lines = self.sort()
|
|
prev = 0
|
|
regrouped = []
|
|
group = []
|
|
for mid, line in zip(lines.mid, lines.lines):
|
|
line = line.tolist()
|
|
if mid - prev > threshold:
|
|
if len(regrouped) == 0:
|
|
if len(group) != 0:
|
|
regrouped = [group]
|
|
else:
|
|
regrouped += [group]
|
|
group = [line]
|
|
else:
|
|
group.append(line)
|
|
prev = mid
|
|
regrouped += [group]
|
|
regrouped = np.vstack([Lines(r, is_horizontal=self.is_horizontal, config=self.config).mean for r in regrouped])
|
|
return Lines(regrouped, is_horizontal=self.is_horizontal, config=self.config)
|
|
|
|
def distance_to_point(self, point):
|
|
x, y = point
|
|
return self.rho - x * self.cos - y * self.sin
|
|
|
|
@staticmethod
|
|
def cross_two_lines(lines1, lines2):
|
|
for rho1, sin1, cos1 in zip(lines1.rho, lines1.sin, lines1.cos):
|
|
for rho2, sin2, cos2 in zip(lines2.rho, lines2.sin, lines2.cos):
|
|
a = np.array([[cos1, sin1], [cos2, sin2]])
|
|
b = np.array([rho1, rho2])
|
|
yield np.linalg.solve(a, b)
|
|
|
|
def cross(self, other):
|
|
points = np.vstack(self.cross_two_lines(self, other))
|
|
points = Points(points, config=self.config)
|
|
return points
|
|
|
|
def delete(self, other, threshold=3):
|
|
if not self:
|
|
return self
|
|
|
|
other_mid = other.mid
|
|
lines = []
|
|
for mid, line in zip(self.mid, self.lines):
|
|
if np.any(np.abs(other_mid - mid) < threshold):
|
|
continue
|
|
lines.append(line)
|
|
|
|
return Lines(lines, is_horizontal=self.is_horizontal, config=self.config)
|