This repository has been archived on 2022-02-23. You can view files and clone it, but cannot push or open issues or pull requests.
lets/pp/catch_the_pp/osu_parser/curves.pyx

166 lines
5.4 KiB
Cython

import math
from .. import constants
from . import mathhelper
class Linear(object): #Because it made sense at the time...
def __init__(self, points):
self.pos = points
cdef class Bezier(object):
cdef public list points, pos
cdef public int order
def __init__(self, points):
self.points = points
self.order = len(self.points)
self.pos = []
self.calc_points()
cpdef calc_points(self):
if len(self.pos) != 0: #This should never happen but since im working on this I want to warn myself if I fuck up
raise Exception("Bezier was calculated twice!")
cdef list sub_points = []
for i in range(len(self.points)):
if i == len(self.points) - 1:
sub_points.append(self.points[i])
self.bezier(sub_points)
sub_points.clear()
elif len(sub_points) > 1 and self.points[i] == sub_points[-1]:
self.bezier(sub_points)
sub_points.clear()
sub_points.append(self.points[i])
cpdef bezier(self, list points):
cdef int order = len(points)
cdef float step = 0.25 / constants.SLIDER_QUALITY / order #Normaly 0.0025
cdef float i = 0
cdef int n = order - 1
cdef float x, y
cdef int p
while i < 1 + step:
x = 0
y = 0
for p in range(n + 1):
a = mathhelper.cpn(p, n) * ((1 - i) ** (n - p)) * (i ** p)
x += a * points[p].x
y += a * points[p].y
point = mathhelper.Vec2(x, y)
self.pos.append(point)
i += step
def point_at_distance(self, length):
return {
0: False,
1: self.points[0],
}.get(self.order, self.rec(length))
def rec(self, length):
return mathhelper.point_at_distance(self.pos, length)
cdef class Catmull(object): #Yes... I cry deep down on the inside aswell
cdef public list points, pos
cdef public int order
cdef public float step
def __init__(self, points):
self.points = points
self.order = len(points)
self.step = 2.5 / constants.SLIDER_QUALITY #Normaly 0.025
self.pos = []
self.calc_points()
cpdef calc_points(self):
if len(self.pos) != 0: #This should never happen but since im working on this I want to warn myself if I fuck up
raise Exception("Catmull was calculated twice!")
cdef int x
cdef float t
cdef object v1, v2, v3
for x in range(self.order - 1):
t = 0
while t < self.step + 1:
if x >= 1:
v1 = self.points[x - 1]
else:
v1 = self.points[x]
v2 = self.points[x]
if x + 1 < self.order:
v3 = self.points[x + 1]
else:
v3 = v2.calc(1, v2.calc(-1, v1))
if x + 2 < self.order:
v4 = self.points[x + 2]
else:
v4 = v3.calc(1, v3.calc(-1, v2))
point = get_point([v1, v2, v3, v4], t)
self.pos.append(point)
t += self.step
def point_at_distance(self, length):
return {
0: False,
1: self.points[0],
}.get(self.order, self.rec(length))
def rec(self, length):
return mathhelper.point_at_distance(self.pos, length)
cdef class Perfect(object):
cdef public list points
cdef float cx, cy
cdef float radius
def __init__(self, points):
self.points = points
self.cx = 0
self.cy = 0
self.radius = 0
self.setup_path()
def setup_path(self):
self.cx, self.cy, self.radius = get_circum_circle(self.points)
if is_left(self.points):
self.radius *= -1
cpdef point_at_distance(self, float length):
cdef float radians = length / self.radius
return rotate(self.cx, self.cy, self.points[0], radians)
cpdef object get_point(object p, float length):
cdef float x = mathhelper.catmull([o.x for o in p], length)
cdef float y = mathhelper.catmull([o.y for o in p], length)
return mathhelper.Vec2(x, y)
cpdef tuple get_circum_circle(list p):
cdef float d = 2 * (p[0].x * (p[1].y - p[2].y) + p[1].x * (p[2].y - p[0].y) + p[2].x * (p[0].y - p[1].y))
if d == 0:
raise Exception("Invalid circle! Unable to chose angle.")
cdef float ux = ((pow(p[0].x, 2) + pow(p[0].y, 2)) * (p[1].y - p[2].y) + (pow(p[1].x, 2) + pow(p[1].y, 2)) * (p[2].y - p[0].y) + (pow(p[2].x, 2) + pow(p[2].y, 2)) * (p[0].y - p[1].y)) / d
cdef float uy = ((pow(p[0].x, 2) + pow(p[0].y, 2)) * (p[2].x - p[1].x) + (pow(p[1].x, 2) + pow(p[1].y, 2)) * (p[0].x - p[2].x) + (pow(p[2].x, 2) + pow(p[2].y, 2)) * (p[1].x - p[0].x)) / d
cdef float px = ux - p[0].x
cdef float py = uy - p[0].y
cdef float r = pow(pow(px, 2) + pow(py, 2), 0.5)
return ux, uy, r
cpdef float is_left(object p):
return ((p[1].x - p[0].x) * (p[2].y - p[0].y) - (p[1].y - p[0].y) * (p[2].x - p[0].x)) < 0
cpdef object rotate(float cx, float cy, object p, float radians):
cdef float cos = math.cos(radians)
cdef float sin = math.sin(radians)
return mathhelper.Vec2((cos * (p.x - cx)) - (sin * (p.y - cy)) + cx, (sin * (p.x - cx)) + (cos * (p.y - cy)) + cy)