work on bezier curve

optimised bezier algorithm (no more nested lists, use less ram)
fixed last point problem (curves wasn't goint up to it)
added loop option to bezier curve
updated example widget
This commit is contained in:
Gabriel Pettier 2011-08-27 12:39:45 +02:00
parent 1ace31dfdf
commit 5497370493
2 changed files with 50 additions and 36 deletions

View File

@ -5,23 +5,11 @@ from kivy.uix.widget import Widget
from kivy.graphics import Color, Line, Bezier, Ellipse, Line
class BezierTest(Widget):
def __init__(self, *args, **kwargs):
def __init__(self, points=[], loop=False, *args, **kwargs):
super(BezierTest, self).__init__(*args, **kwargs)
self.d = 10
self.points = [
0, 0,
0.1 * self.width, 0.2 * self.height,
0.2 * self.width, 0.3 * self.height,
0.3 * self.width, 0.3 * self.height,
0.4 * self.width, 0.4 * self.height,
0.5 * self.width, 0.5 * self.height,
0.6 * self.width, 0.6 * self.height,
0.7 * self.width, 0.6 * self.height,
0.8 * self.width, 0.7 * self.height,
0.9 * self.width, 0.8 * self.height,
self.width, self.height,
0, self.height]
self.points = points
self.loop = loop
self.current_point = None
self.update()
@ -30,7 +18,7 @@ class BezierTest(Widget):
with self.canvas:
Color(1.0, 0.0, 0.0)
Bezier(points=self.points, segments=100)
Bezier(points=self.points, segments=150, loop=self.loop)
Color(1.0, 1.0, 1.0)
for p in zip(self.points[::2], self.points[1::2]):
@ -39,27 +27,25 @@ class BezierTest(Widget):
size=(self.d, self.d))
Color(1.0, 0.0, 1.0)
Line(points=self.points)
Line(points=self.points+self.points[:2])
def on_touch_down(self, touch):
if self.collide_point(touch.pos[0], touch.pos[1]):
for i, p in enumerate(zip(self.points[::2], self.points[1::2])):
if (
abs(touch.pos[0] - self.pos[0] - p[0]) < self.d and
abs(touch.pos[1] - self.pos[1] - p[1]) < self.d):
self.current_point = i + 1
return True
super(BezierTest, self).on_touch_down(touch)
return super(BezierTest, self).on_touch_down(touch)
def on_touch_up(self, touch):
if self.collide_point(touch.pos[0], touch.pos[1]):
if self.current_point:
self.current_point = None
return True
super(BezierTest, self).on_touch_up(touch)
return super(BezierTest, self).on_touch_up(touch)
def on_touch_move(self, touch):
if self.collide_point(touch.pos[0], touch.pos[1]):
@ -68,13 +54,26 @@ class BezierTest(Widget):
self.points[(self.current_point - 1) * 2 + 1] = touch.pos[1] - self.pos[1]
self.update()
return True
super(BezierTest, self).on_touch_move(touch)
return super(BezierTest, self).on_touch_move(touch)
class Main(App):
def build(self):
layout = FloatLayout()
layout.add_widget(BezierTest())
layout.add_widget(BezierTest(points=[
0, 0,
0.1 * 100, 0.2 * 100,
0.2 * 100, 0.3 * 100,
0.3 * 100, 0.3 * 100,
0.4 * 100, 0.4 * 100,
0.5 * 100, 0.5 * 100,
0.6 * 100, 0.6 * 100,
0.7 * 100, 0.6 * 100,
0.8 * 100, 0.7 * 100,
0.9 * 100, 0.8 * 100,
100, 100,
0, 100], loop=True))
return layout
if __name__ == '__main__':

View File

@ -89,28 +89,42 @@ cdef class Bezier(VertexInstruction):
`segments`: int, default to 180
Define how much segment is needed for drawing the ellipse.
The drawing will be smoother if you have lot of segment.
`loop`: bool, default to False
Set the bezier curve to join last point to first.
#TODO: refactoring:
a) find interface common to all splines (given control points and
perhaps tangents, what's the position on the spline for parameter t),
b) make that a superclass Spline, c) create BezierSpline subclass that
does the computation
'''
cdef list _points
cdef int _segments
cdef bint _loop
def __init__(self, **kwargs):
VertexInstruction.__init__(self, **kwargs)
self.points = kwargs.get('points', [])
self._segments = kwargs.get('segments', 10)
self._loop = kwargs.get('loop', False)
if self._loop:
self.points.extend(self.points[:2])
self.batch.set_mode('line_strip')
cdef void build(self):
cdef int x, i, j
cdef float l
cdef list T
cdef list T = self.points
cdef vertex_t *vertices = NULL
cdef unsigned short *indices = NULL
vertices = <vertex_t *>malloc(self._segments * sizeof(vertex_t))
vertices = <vertex_t *>malloc((self._segments + 1) * sizeof(vertex_t))
if vertices == NULL:
raise MemoryError('vertices')
indices = <unsigned short *>malloc(self._segments * sizeof(unsigned short))
indices = <unsigned short *>malloc(
(self._segments + 1) * sizeof(unsigned short))
if indices == NULL:
free(vertices)
raise MemoryError('indices')
@ -118,19 +132,20 @@ cdef class Bezier(VertexInstruction):
for x in xrange(self._segments):
l = x / (1.0 * self._segments)
T = [zip(self.points[::2], self.points[1::2]),]
for i in range(1, len(self.points)/2):
T.append([])
for j in xrange(len(self.points)/2 - i):
T[i].append([
T[i-1][j][0] + (T[i-1][j+1][0] - T[i-1][j][0]) * l,
T[i-1][j][1] + (T[i-1][j+1][1] - T[i-1][j][1]) * l])
for i in range(1, len(self.points)):
for j in xrange(len(self.points) - 2*i):
T[j] = T[j] + (T[j+2] - T[j]) * l
vertices[x].x = T[-1][0][0]
vertices[x].y = T[-1][0][1]
vertices[x].x = T[0]
vertices[x].y = T[1]
indices[x] = x
self.batch.set_data(vertices, self._segments, indices, self._segments)
vertices[x+1].x = self.points[-2]
vertices[x+1].y = self.points[-1]
indices[x+1] = x + 1
self.batch.set_data(vertices, self._segments + 1, indices,
self._segments + 1)
free(vertices)
free(indices)