From 5497370493a769691b4b76758a07f947178a2c95 Mon Sep 17 00:00:00 2001 From: Gabriel Pettier Date: Sat, 27 Aug 2011 12:39:45 +0200 Subject: [PATCH] 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 --- examples/canvas/bezier.py | 45 +++++++++++++-------------- kivy/graphics/vertex_instructions.pyx | 41 ++++++++++++++++-------- 2 files changed, 50 insertions(+), 36 deletions(-) diff --git a/examples/canvas/bezier.py b/examples/canvas/bezier.py index c45eab42c..7138850c2 100644 --- a/examples/canvas/bezier.py +++ b/examples/canvas/bezier.py @@ -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__': diff --git a/kivy/graphics/vertex_instructions.pyx b/kivy/graphics/vertex_instructions.pyx index 4cfe20113..cc2f9038c 100644 --- a/kivy/graphics/vertex_instructions.pyx +++ b/kivy/graphics/vertex_instructions.pyx @@ -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 = malloc(self._segments * sizeof(vertex_t)) + vertices = malloc((self._segments + 1) * sizeof(vertex_t)) if vertices == NULL: raise MemoryError('vertices') - indices = malloc(self._segments * sizeof(unsigned short)) + indices = 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)