166 lines
5.4 KiB
Cython
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)
|