#! /usr/bin/python2
# -*- coding: utf-8; -*-
#
# (c) 2013 booya (http://booya.at)
#
# This file is part of the OpenGlider project.
#
# OpenGlider is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# OpenGlider is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OpenGlider. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
from scipy.misc import comb
import scipy.interpolate
from scipy.optimize import bisect as findroot
__all__ = ['BezierCurve']
[docs]class BezierCurve(object):
def __init__(self, points=None):
"""Bezier Curve represantative
http://en.wikipedia.org/wiki/Bezier_curve#Generalization"""
self._BezierBase = self._BezierFunction = self._controlpoints = None
if points is None:
points = [[0, 0], [1, 10], [2, 0]]
self.controlpoints = points
def __call__(self, value):
if 0 <= value <= 1:
return self._BezierFunction(value)
else:
ValueError("value must be in the range (0,1) for xvalues use xpoint-function")
@property
def numpoints(self):
try:
leng = len(self._BezierBase)
#except AttributeError:
# leng = 0
except TypeError:
leng = 0
return leng
@numpoints.setter
[docs] def numpoints(self, num):
if not num == self.numpoints:
self._BezierBase = bernsteinbase(num)
@property
def controlpoints(self):
return self._controlpoints
@controlpoints.setter
[docs] def controlpoints(self, points):
self.numpoints = len(points)
self._controlpoints = points
self._BezierFunction = bezierfunction(points, self._BezierBase)
[docs] def xpoint(self, x):
root = findroot(lambda x2: self._BezierFunction(x2)[0] - x, 0, 1)
return self._BezierFunction(root)
[docs] def ypoint(self, y):
root = findroot(lambda y2: self._BezierFunction(y2)[1] - y, 0, 1)
return self._BezierFunction(root)
[docs] def fit(self, data, numpoints=None):
if numpoints:
self.numpoints = numpoints
self.controlpoints = fitbezier(data, self._BezierBase)
[docs] def interpolation(self, num=100):
x = []
y = []
for i in range(num):
point = self(i * 1. / (num - 1))
x.append(point[0])
y.append(point[1])
return scipy.interpolate.interp1d(x, y)
##############################FUNCTIONS
def bernsteinbase(d):
def bsf(n):
return lambda x: comb(d - 1, n) * (x ** n) * ((1 - x) ** (d - 1 - n))
return [bsf(i) for i in range(d)]
def bezierfunction(points, base=None):
""""""
if not base:
base = bernsteinbase(len(points))
def func(x):
val = np.zeros(len(points[0]))
for i in range(len(points)):
fakt = base[i](x)
v = np.array(points[i]) * fakt
val += v
return val
return func
def fitbezier(points, base=bernsteinbase(3), start=True, end=True):
"""Fit to a given set of points with a certain number of spline-points (default=3)
if start (/ end) is True, the first (/ last) point of the Curve is included"""
matrix = np.matrix(
[[base[column](row * 1. / (len(points) - 1)) for column in range(len(base))] for row in range(len(points))])
matrix = np.linalg.pinv(matrix)
out = np.array(matrix * points)
if start:
out[0] = points[0]
if end:
out[-1] = points[-1]
return out