From 2ea2f5230863210a5e74cdd574889f48db98aca4 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Wed, 19 Sep 2012 19:22:01 +0200 Subject: [PATCH 01/14] line: start to add width/cap/joint --- kivy/graphics/vertex_instructions.pyx | 133 +------------------------- 1 file changed, 2 insertions(+), 131 deletions(-) diff --git a/kivy/graphics/vertex_instructions.pyx b/kivy/graphics/vertex_instructions.pyx index 56eb818f1..0338f7afc 100644 --- a/kivy/graphics/vertex_instructions.pyx +++ b/kivy/graphics/vertex_instructions.pyx @@ -26,136 +26,7 @@ class GraphicException(Exception): '''Exception fired when a graphic error is fired. ''' -cdef class Line(VertexInstruction): - '''A 2d line. - - .. versionadded:: 1.0.8 - `dash_offset` and `dash_length` have been added - - :Parameters: - `points`: list - List of points in the format (x1, y1, x2, y2...) - `dash_length`: int - length of a segment (if dashed), default 1 - `dash_offset`: int - offset between the end of a segments and the begining of the - next one, default 0, changing this makes it dashed. - ''' - cdef list _points - cdef int _dash_offset, _dash_length - - def __init__(self, **kwargs): - VertexInstruction.__init__(self, **kwargs) - v = kwargs.get('points') - self.points = v if v is not None else [] - self.batch.set_mode('line_strip') - self._dash_length = kwargs.get('dash_length') or 1 - self._dash_offset = kwargs.get('dash_offset') or 0 - - cdef void build(self): - cdef int i, count = len(self.points) / 2 - cdef list p = self.points - cdef vertex_t *vertices = NULL - cdef unsigned short *indices = NULL - cdef float tex_x - cdef char *buf = NULL - cdef Texture texture = self.texture - - if count < 2: - self.batch.clear_data() - return - - if self._dash_offset != 0: - if texture is None or texture._width != \ - (self._dash_length + self._dash_offset) or \ - texture._height != 1: - - self.texture = texture = Texture.create( - size=(self._dash_length + self._dash_offset, 1)) - texture.wrap = 'repeat' - - # create a buffer to fill our texture - buf = malloc(4 * (self._dash_length + self._dash_offset)) - memset(buf, 255, self._dash_length * 4) - memset(buf + self._dash_length * 4, 0, self._dash_offset * 4) - p_str = PyString_FromStringAndSize(buf, (self._dash_length + self._dash_offset) * 4) - - self.texture.blit_buffer(p_str, colorfmt='rgba', bufferfmt='ubyte') - free(buf) - - elif texture is not None: - self.texture = None - - vertices = malloc(count * sizeof(vertex_t)) - if vertices == NULL: - raise MemoryError('vertices') - - indices = malloc(count * sizeof(unsigned short)) - if indices == NULL: - free(vertices) - raise MemoryError('indices') - - tex_x = 0 - for i in xrange(count): - if self._dash_offset != 0 and i > 0: - tex_x += sqrt( - pow(p[i * 2] - p[(i - 1) * 2], 2) + - pow(p[i * 2 + 1] - p[(i - 1) * 2 + 1], 2)) / ( - self._dash_length + self._dash_offset) - - vertices[i].s0 = tex_x - vertices[i].t0 = 0 - - vertices[i].x = p[i * 2] - vertices[i].y = p[i * 2 + 1] - indices[i] = i - - self.batch.set_data(vertices, count, indices, count) - - free(vertices) - free(indices) - - property points: - '''Property for getting/settings points of the line - - .. warning:: - - This will always reconstruct the whole graphics from the new points - list. It can be very CPU expensive. - ''' - def __get__(self): - return self._points - def __set__(self, points): - self._points = list(points) - self.flag_update() - - property dash_length: - '''Property for getting/setting the length of the dashes in the curve - - .. versionadded:: 1.0.8 - ''' - def __get__(self): - return self._dash_length - - def __set__(self, value): - if value < 0: - raise GraphicException('Invalid dash_length value, must be >= 0') - self._dash_length = value - self.flag_update() - - property dash_offset: - '''Property for getting/setting the offset between the dashes in the curve - - .. versionadded:: 1.0.8 - ''' - def __get__(self): - return self._dash_offset - - def __set__(self, value): - if value < 0: - raise GraphicException('Invalid dash_offset value, must be >= 0') - self._dash_offset = value - self.flag_update() +include "vertex_instructions_line.pxi" cdef class Bezier(VertexInstruction): @@ -903,7 +774,7 @@ cdef class BorderImage(Rectangle): hs[0], vs[1], ths[0], tvs[1], #v11 hs[1], vs[1], ths[1], tvs[1], #v12 hs[2], vs[1], ths[2], tvs[1], #v13 - hs[2], vs[2], ths[2], tvs[2], #v14 + hs[2], vs[2], ths[2], tvs[2], #v14 hs[1], vs[2], ths[1], tvs[2]] #v15 cdef unsigned short *indices = [ From 0b256b418b1ad706f3c378682ff3da131d839547 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Wed, 19 Sep 2012 19:22:22 +0200 Subject: [PATCH 02/14] line: add the line pxi --- kivy/graphics/vertex_instructions_line.pxi | 232 +++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 kivy/graphics/vertex_instructions_line.pxi diff --git a/kivy/graphics/vertex_instructions_line.pxi b/kivy/graphics/vertex_instructions_line.pxi new file mode 100644 index 000000000..bbe0372c5 --- /dev/null +++ b/kivy/graphics/vertex_instructions_line.pxi @@ -0,0 +1,232 @@ +DEF LINE_CAP_NONE = 0 +DEF LINE_CAP_SQUARE = 1 +DEF LINE_CAP_ROUND = 2 + +DEF LINE_JOINT_NONE = 0 +DEF LINE_JOINT_MITER = 1 +DEF LINE_JOINT_BEVEL = 2 +DEF LINE_JOINT_ROUND = 3 + +cdef class Line(VertexInstruction): + '''A 2d line. + + .. versionadded:: 1.0.8 + `dash_offset` and `dash_length` have been added + + :Parameters: + `points`: list + List of points in the format (x1, y1, x2, y2...) + `dash_length`: int + Length of a segment (if dashed), default 1 + `dash_offset`: int + Offset between the end of a segments and the begining of the + next one, default 0, changing this makes it dashed. + `width`: float + Width of the line, default 1.0 + ''' + cdef int _cap + cdef int _joint + cdef list _points + cdef float _width + cdef int _dash_offset, _dash_length + + def __init__(self, **kwargs): + VertexInstruction.__init__(self, **kwargs) + v = kwargs.get('points') + self.points = v if v is not None else [] + self.batch.set_mode('line_strip') + self._dash_length = kwargs.get('dash_length') or 1 + self._dash_offset = kwargs.get('dash_offset') or 0 + self._width = kwargs.get('width') or 1.0 + self.joint = kwargs.get('joint') or 'round' + self.cap = kwargs.get('cap') or 'round' + + cdef void build(self): + if self._width == 1.0: + self.build_legacy() + else: + self.build_extended() + + cdef void build_legacy(self): + cdef int i, count = len(self.points) / 2 + cdef list p = self.points + cdef vertex_t *vertices = NULL + cdef unsigned short *indices = NULL + cdef float tex_x + cdef char *buf = NULL + cdef Texture texture = self.texture + + if count < 2: + self.batch.clear_data() + return + + if self._dash_offset != 0: + if texture is None or texture._width != \ + (self._dash_length + self._dash_offset) or \ + texture._height != 1: + + self.texture = texture = Texture.create( + size=(self._dash_length + self._dash_offset, 1)) + texture.wrap = 'repeat' + + # create a buffer to fill our texture + buf = malloc(4 * (self._dash_length + self._dash_offset)) + memset(buf, 255, self._dash_length * 4) + memset(buf + self._dash_length * 4, 0, self._dash_offset * 4) + p_str = PyString_FromStringAndSize(buf, (self._dash_length + self._dash_offset) * 4) + + self.texture.blit_buffer(p_str, colorfmt='rgba', bufferfmt='ubyte') + free(buf) + + elif texture is not None: + self.texture = None + + vertices = malloc(count * sizeof(vertex_t)) + if vertices == NULL: + raise MemoryError('vertices') + + indices = malloc(count * sizeof(unsigned short)) + if indices == NULL: + free(vertices) + raise MemoryError('indices') + + tex_x = 0 + for i in xrange(count): + if self._dash_offset != 0 and i > 0: + tex_x += sqrt( + pow(p[i * 2] - p[(i - 1) * 2], 2) + + pow(p[i * 2 + 1] - p[(i - 1) * 2 + 1], 2)) / ( + self._dash_length + self._dash_offset) + + vertices[i].s0 = tex_x + vertices[i].t0 = 0 + + vertices[i].x = p[i * 2] + vertices[i].y = p[i * 2 + 1] + indices[i] = i + + self.batch.set_data(vertices, count, indices, count) + + free(vertices) + free(indices) + + cdef void build_extended(self): + # TODO: current implementation doesn't support alpha well. + cdef int i, count = len(self.points) / 2 + cdef list p = self.points + cdef vertex_t *vertices = NULL + cdef unsigned short *indices = NULL + cdef float tex_x + cdef char *buf = NULL + cdef Texture texture = self.texture + + + + property points: + '''Property for getting/settings points of the line + + .. warning:: + + This will always reconstruct the whole graphics from the new points + list. It can be very CPU expensive. + ''' + def __get__(self): + return self._points + def __set__(self, points): + self._points = list(points) + self.flag_update() + + property dash_length: + '''Property for getting/setting the length of the dashes in the curve + + .. versionadded:: 1.0.8 + ''' + def __get__(self): + return self._dash_length + + def __set__(self, value): + if value < 0: + raise GraphicException('Invalid dash_length value, must be >= 0') + self._dash_length = value + self.flag_update() + + property dash_offset: + '''Property for getting/setting the offset between the dashes in the curve + + .. versionadded:: 1.0.8 + ''' + def __get__(self): + return self._dash_offset + + def __set__(self, value): + if value < 0: + raise GraphicException('Invalid dash_offset value, must be >= 0') + self._dash_offset = value + self.flag_update() + + property width: + '''Determine the width of the line, default to 1.0. + + .. versionadded:: 1.4.1 + ''' + def __get__(self): + return self._width + + def __set__(self, value): + if value <= 0: + raise GraphicException('Invalid width value, must be > 0') + self._width = value + self.flag_update() + + property cap: + '''Determine the cap of the line, default to "round" + + .. versionadded:: 1.4.1 + ''' + def __get__(self): + if self._cap == LINE_CAP_SQUARE: + return 'square' + elif self._cap == LINE_CAP_ROUND: + return 'round' + return None + + def __set__(self, value): + if value not in (None, 'square', 'round'): + raise GraphicException('Invalid cap, must be one of ' + 'None, "square", "round"') + if value == 'square': + self._cap = LINE_CAP_SQUARE + elif value == 'round': + self._cap = LINE_CAP_ROUND + else: + self._cap = LINE_CAP_NONE + self.flag_update() + + property joint: + '''Determine the join of the line, default to "round" + + .. versionadded:: 1.4.1 + ''' + + def __get__(self): + if self._joint == LINE_JOINT_ROUND: + return 'round' + elif self._joint == LINE_JOINT_BEVEL: + return 'bevel' + elif self._joint == LINE_JOINT_MITER: + return 'miter' + return None + + def __set__(self, value): + if value not in (None, 'miter', 'bevel', 'round'): + raise GraphicException('Invalid joint, must be one of ' + 'None, "miter", "bevel", "round"') + if value == 'round': + self._joint = LINE_JOINT_ROUND + elif value == 'bevel': + self._joint = LINE_JOINT_BEVEL + elif value == 'miter': + self._joint = LINE_JOINT_MITER + else: + self._joint = LINE_JOINT_NONE + self.flag_update() From 39c45abaf2a7986540094eddb22dc11b7793060f Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Fri, 21 Sep 2012 01:24:48 +0200 Subject: [PATCH 03/14] first implementation of extended line, with bevel joint and square cap --- kivy/graphics/common.pxi | 6 +- kivy/graphics/vertex_instructions.pyx | 2 +- kivy/graphics/vertex_instructions_line.pxi | 196 ++++++++++++++++++++- 3 files changed, 196 insertions(+), 8 deletions(-) diff --git a/kivy/graphics/common.pxi b/kivy/graphics/common.pxi index 8f45fb089..c644af435 100644 --- a/kivy/graphics/common.pxi +++ b/kivy/graphics/common.pxi @@ -2,15 +2,19 @@ # Common definition # +DEF PI2 = 1.5707963267948966 +DEF PI = 3.1415926535897931 + cdef extern from *: ctypedef char* const_char_ptr "const char*" -cdef double pi = 3.1415926535897931 +cdef double pi = PI cdef extern from "math.h": double cos(double) nogil double sin(double) nogil double sqrt(double) nogil double pow(double x, double y) nogil + double atan2(double y, double x) nogil cdef extern from "stdlib.h": ctypedef unsigned long size_t diff --git a/kivy/graphics/vertex_instructions.pyx b/kivy/graphics/vertex_instructions.pyx index 0338f7afc..2c92b3490 100644 --- a/kivy/graphics/vertex_instructions.pyx +++ b/kivy/graphics/vertex_instructions.pyx @@ -19,7 +19,7 @@ from kivy.graphics.c_opengl cimport * IF USE_OPENGL_DEBUG == 1: from kivy.graphics.c_opengl_debug cimport * from kivy.logger import Logger -from kivy.graphics.texture import Texture +from kivy.graphics.texture cimport Texture class GraphicException(Exception): diff --git a/kivy/graphics/vertex_instructions_line.pxi b/kivy/graphics/vertex_instructions_line.pxi index bbe0372c5..ac2e20042 100644 --- a/kivy/graphics/vertex_instructions_line.pxi +++ b/kivy/graphics/vertex_instructions_line.pxi @@ -60,6 +60,7 @@ cdef class Line(VertexInstruction): self.batch.clear_data() return + self.batch.set_mode('line_strip') if self._dash_offset != 0: if texture is None or texture._width != \ (self._dash_length + self._dash_offset) or \ @@ -120,6 +121,189 @@ cdef class Line(VertexInstruction): cdef char *buf = NULL cdef Texture texture = self.texture + if count < 2: + self.batch.clear_data() + return + + self.batch.set_mode('triangles') + cdef unsigned int vertices_count = (count - 1) * 4 + cdef unsigned int indices_count = (count - 1) * 6 + cdef unsigned int iv = 0, ii = 0 + + if self._joint == LINE_JOINT_BEVEL: + indices_count += (count - 2) * 3 + vertices_count += (count - 2) + + if self._cap == LINE_CAP_SQUARE: + indices_count += 12 + vertices_count += 4 + + vertices = malloc(vertices_count * sizeof(vertex_t)) + if vertices == NULL: + raise MemoryError('vertices') + + indices = malloc(indices_count * sizeof(unsigned short)) + if indices == NULL: + free(vertices) + raise MemoryError('indices') + + cdef double ax, ay, bx, by, cx, cy, angle, a1, a2 + cdef double x1, y1, x2, y2, x3, y3, x4, y4 + cdef double sx1, sy1, sx4, sy4, sangle + cdef double pcx, pcy, px1, py1, px2, py2, px3, py3, px4, py4, pangle + cdef double w = self._width + cdef unsigned int pii, piv, pii2, piv2 + cdef double jangle + pii = piv = pcx = pcy = cx = cy = ii = iv = 0 + for i in range(0, count - 1): + ax = p[i * 2] + ay = p[i * 2 + 1] + bx = p[i * 2 + 2] + by = p[i * 2 + 3] + + if i > 0 and self._joint != LINE_JOINT_NONE: + pcx = cx + pcy = cy + px1 = x1 + px2 = x2 + px3 = x3 + px4 = x4 + py1 = y1 + py2 = y2 + py3 = y3 + py4 = y4 + + pii2 = pii + piv2 = piv + pii = ii + piv = iv + pangle = angle + + # calculate the orientation of the segment, between pi and -pi + cx = bx - ax + cy = by - ay + angle = atan2(cy, cx) + a1 = angle - PI2 + a2 = angle + PI2 + + # calculate the position of the segment + x1 = ax + cos(a1) * w + y1 = ay + sin(a1) * w + x4 = ax + cos(a2) * w + y4 = ay + sin(a2) * w + x2 = bx + cos(a1) * w + y2 = by + sin(a1) * w + x3 = bx + cos(a2) * w + y3 = by + sin(a2) * w + + if i == 0: + sx1 = x1 + sy1 = y1 + sx4 = x4 + sy4 = y4 + sangle = angle + + indices[ii ] = iv + indices[ii + 1] = iv + 1 + indices[ii + 2] = iv + 2 + indices[ii + 3] = iv + indices[ii + 4] = iv + 2 + indices[ii + 5] = iv + 3 + ii += 6 + + vertices[iv].x = x1 + vertices[iv].y = y1 + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + iv += 1 + vertices[iv].x = x2 + vertices[iv].y = y2 + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + iv += 1 + vertices[iv].x = x3 + vertices[iv].y = y3 + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + iv += 1 + vertices[iv].x = x4 + vertices[iv].y = y4 + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + iv += 1 + + # joint generation + if i == 0 or self._joint == LINE_JOINT_NONE: + continue + + # calculate the angle of the previous and current segment + jangle = atan2( + cx * pcy - cy * pcx, + cx * pcx + cy * pcy) + + # in case of the angle is NULL, avoid the generation + if jangle == 0 or jangle == PI or jangle == -PI: + if self._joint == LINE_JOINT_BEVEL: + vertices_count -= 1 + indices_count -= 3 + continue + + if self._joint == LINE_JOINT_BEVEL: + vertices[iv].x = ax + vertices[iv].y = ay + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + if jangle < 0: + indices[ii] = piv2 + 1 + indices[ii + 1] = piv + indices[ii + 2] = iv + else: + indices[ii] = piv2 + 2 + indices[ii + 1] = piv + 3 + indices[ii + 2] = iv + ii += 3 + iv += 1 + + # caps + if self._cap == LINE_CAP_SQUARE: + vertices[iv].x = x2 + cos(angle) * w + vertices[iv].y = y2 + sin(angle) * w + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + vertices[iv + 1].x = x3 + cos(angle) * w + vertices[iv + 1].y = y3 + sin(angle) * w + vertices[iv + 1].s0 = 0 + vertices[iv + 1].t0 = 0 + indices[ii] = piv + 1 + indices[ii + 1] = piv + 2 + indices[ii + 2] = iv + 1 + indices[ii + 3] = piv + 1 + indices[ii + 4] = iv + indices[ii + 5] = iv + 1 + ii += 6 + iv += 2 + vertices[iv].x = sx1 - cos(sangle) * w + vertices[iv].y = sy1 - sin(sangle) * w + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + vertices[iv + 1].x = sx4 - cos(sangle) * w + vertices[iv + 1].y = sy4 - sin(sangle) * w + vertices[iv + 1].s0 = 0 + vertices[iv + 1].t0 = 0 + indices[ii] = 0 + indices[ii + 1] = 3 + indices[ii + 2] = iv + 1 + indices[ii + 3] = 0 + indices[ii + 4] = iv + indices[ii + 5] = iv + 1 + ii += 6 + iv += 2 + + self.batch.set_data(vertices, vertices_count, indices, indices_count) + + free(vertices) + free(indices) + property points: @@ -188,12 +372,12 @@ cdef class Line(VertexInstruction): return 'square' elif self._cap == LINE_CAP_ROUND: return 'round' - return None + return 'none' def __set__(self, value): - if value not in (None, 'square', 'round'): + if value not in ('none', 'square', 'round'): raise GraphicException('Invalid cap, must be one of ' - 'None, "square", "round"') + '"none", "square", "round"') if value == 'square': self._cap = LINE_CAP_SQUARE elif value == 'round': @@ -215,12 +399,12 @@ cdef class Line(VertexInstruction): return 'bevel' elif self._joint == LINE_JOINT_MITER: return 'miter' - return None + return 'none' def __set__(self, value): - if value not in (None, 'miter', 'bevel', 'round'): + if value not in ('none', 'miter', 'bevel', 'round'): raise GraphicException('Invalid joint, must be one of ' - 'None, "miter", "bevel", "round"') + '"none", "miter", "bevel", "round"') if value == 'round': self._joint = LINE_JOINT_ROUND elif value == 'bevel': From 21501651af6605e8d87dffe37e8275f278539921 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Fri, 21 Sep 2012 02:11:26 +0200 Subject: [PATCH 04/14] line: add round cap --- kivy/graphics/vertex_instructions_line.pxi | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/kivy/graphics/vertex_instructions_line.pxi b/kivy/graphics/vertex_instructions_line.pxi index ac2e20042..a03bcdd9a 100644 --- a/kivy/graphics/vertex_instructions_line.pxi +++ b/kivy/graphics/vertex_instructions_line.pxi @@ -25,6 +25,7 @@ cdef class Line(VertexInstruction): Width of the line, default 1.0 ''' cdef int _cap + cdef int _cap_precision cdef int _joint cdef list _points cdef float _width @@ -40,6 +41,7 @@ cdef class Line(VertexInstruction): self._width = kwargs.get('width') or 1.0 self.joint = kwargs.get('joint') or 'round' self.cap = kwargs.get('cap') or 'round' + self._cap_precision = kwargs.get('cap_precision') or 20 cdef void build(self): if self._width == 1.0: @@ -137,6 +139,9 @@ cdef class Line(VertexInstruction): if self._cap == LINE_CAP_SQUARE: indices_count += 12 vertices_count += 4 + elif self._cap == LINE_CAP_ROUND: + indices_count += (self._cap_precision * 3) * 2 + vertices_count += (self._cap_precision + 1) * 2 vertices = malloc(vertices_count * sizeof(vertex_t)) if vertices == NULL: @@ -299,6 +304,71 @@ cdef class Line(VertexInstruction): ii += 6 iv += 2 + elif self._cap == LINE_CAP_ROUND: + + # cap start + a1 = sangle - PI2 + a2 = sangle + PI2 + step = (a1 - a2) / float(self._cap_precision) + siv = iv + cx = p[0] + cy = p[1] + vertices[iv].x = cx + vertices[iv].y = cy + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + iv += 1 + for i in xrange(0, self._cap_precision - 1): + vertices[iv].x = cx + cos(a1 + step * i) * w + vertices[iv].y = cy + sin(a1 + step * i) * w + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + if i == 0: + indices[ii] = siv + indices[ii + 1] = 0 + indices[ii + 2] = iv + else: + indices[ii] = siv + indices[ii + 1] = iv - 1 + indices[ii + 2] = iv + iv += 1 + ii += 3 + indices[ii] = siv + indices[ii + 1] = iv - 1 + indices[ii + 2] = 3 + ii += 3 + + # cap end + a1 = angle - PI2 + a2 = angle + PI2 + step = (a2 - a1) / float(self._cap_precision) + siv = iv + cx = p[-2] + cy = p[-1] + vertices[iv].x = cx + vertices[iv].y = cy + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + iv += 1 + for i in xrange(0, self._cap_precision - 1): + vertices[iv].x = cx + cos(a1 + step * i) * w + vertices[iv].y = cy + sin(a1 + step * i) * w + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + if i == 0: + indices[ii] = siv + indices[ii + 1] = piv + 1 + indices[ii + 2] = iv + else: + indices[ii] = siv + indices[ii + 1] = iv - 1 + indices[ii + 2] = iv + iv += 1 + ii += 3 + indices[ii] = siv + indices[ii + 1] = iv - 1 + indices[ii + 2] = piv + 2 + self.batch.set_data(vertices, vertices_count, indices, indices_count) free(vertices) From 3c32e887425952a1184ffad26a55365ee5eb8ee5 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Fri, 21 Sep 2012 02:59:42 +0200 Subject: [PATCH 05/14] line: implement round cap (found invalid indices/vertices counting, bugs excepted right now.) --- kivy/graphics/vertex_instructions_line.pxi | 57 ++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/kivy/graphics/vertex_instructions_line.pxi b/kivy/graphics/vertex_instructions_line.pxi index a03bcdd9a..8ad7c5e60 100644 --- a/kivy/graphics/vertex_instructions_line.pxi +++ b/kivy/graphics/vertex_instructions_line.pxi @@ -41,7 +41,7 @@ cdef class Line(VertexInstruction): self._width = kwargs.get('width') or 1.0 self.joint = kwargs.get('joint') or 'round' self.cap = kwargs.get('cap') or 'round' - self._cap_precision = kwargs.get('cap_precision') or 20 + self._cap_precision = kwargs.get('cap_precision') or 10 cdef void build(self): if self._width == 1.0: @@ -115,7 +115,7 @@ cdef class Line(VertexInstruction): cdef void build_extended(self): # TODO: current implementation doesn't support alpha well. - cdef int i, count = len(self.points) / 2 + cdef int i, j, count = len(self.points) / 2 cdef list p = self.points cdef vertex_t *vertices = NULL cdef unsigned short *indices = NULL @@ -135,6 +135,9 @@ cdef class Line(VertexInstruction): if self._joint == LINE_JOINT_BEVEL: indices_count += (count - 2) * 3 vertices_count += (count - 2) + elif self._joint == LINE_JOINT_ROUND: + indices_count += (self._cap_precision * 3) * (count - 2) + vertices_count += (self._cap_precision + 1) * (count - 2) if self._cap == LINE_CAP_SQUARE: indices_count += 12 @@ -155,7 +158,7 @@ cdef class Line(VertexInstruction): cdef double ax, ay, bx, by, cx, cy, angle, a1, a2 cdef double x1, y1, x2, y2, x3, y3, x4, y4 cdef double sx1, sy1, sx4, sy4, sangle - cdef double pcx, pcy, px1, py1, px2, py2, px3, py3, px4, py4, pangle + cdef double pcx, pcy, px1, py1, px2, py2, px3, py3, px4, py4, pangle, pangle2 cdef double w = self._width cdef unsigned int pii, piv, pii2, piv2 cdef double jangle @@ -182,6 +185,7 @@ cdef class Line(VertexInstruction): piv2 = piv pii = ii piv = iv + pangle2 = pangle pangle = angle # calculate the orientation of the segment, between pi and -pi @@ -269,6 +273,49 @@ cdef class Line(VertexInstruction): ii += 3 iv += 1 + elif self._joint == LINE_JOINT_ROUND: + + # cap end + if jangle < 0: + a1 = pangle2 - PI2 + a2 = angle + PI2 + a0 = a2 + step = (abs(jangle)) / float(self._cap_precision) + pivstart = piv + 3 + pivend = piv2 + 1 + else: + a1 = angle - PI2 + a2 = pangle2 + PI2 + a0 = a1 + step = -(abs(jangle)) / float(self._cap_precision) + pivstart = piv + pivend = piv2 + 2 + siv = iv + vertices[iv].x = ax + vertices[iv].y = ay + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + iv += 1 + for j in xrange(0, self._cap_precision - 1): + vertices[iv].x = ax - cos(a0 - step * j) * w + vertices[iv].y = ay - sin(a0 - step * j) * w + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + if j == 0: + indices[ii] = siv + indices[ii + 1] = pivstart + indices[ii + 2] = iv + else: + indices[ii] = siv + indices[ii + 1] = iv - 1 + indices[ii + 2] = iv + iv += 1 + ii += 3 + indices[ii] = siv + indices[ii + 1] = iv - 1 + indices[ii + 2] = pivend + ii += 3 + # caps if self._cap == LINE_CAP_SQUARE: vertices[iv].x = x2 + cos(angle) * w @@ -368,6 +415,10 @@ cdef class Line(VertexInstruction): indices[ii] = siv indices[ii + 1] = iv - 1 indices[ii + 2] = piv + 2 + ii += 3 + + print 'ii=', ii, 'indices_count=', indices_count + print 'iv=', iv, 'vertices_count', vertices_count self.batch.set_data(vertices, vertices_count, indices, indices_count) From 670449afc242a1896eee868f77bf2e32a9c1e33e Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Fri, 21 Sep 2012 19:12:31 +0200 Subject: [PATCH 06/14] line: add miter joint --- kivy/graphics/vertex_instructions_line.pxi | 66 +++++++++++++++++++++- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/kivy/graphics/vertex_instructions_line.pxi b/kivy/graphics/vertex_instructions_line.pxi index 8ad7c5e60..3f5b2b1b0 100644 --- a/kivy/graphics/vertex_instructions_line.pxi +++ b/kivy/graphics/vertex_instructions_line.pxi @@ -7,6 +7,17 @@ DEF LINE_JOINT_MITER = 1 DEF LINE_JOINT_BEVEL = 2 DEF LINE_JOINT_ROUND = 3 +cdef inline int line_intersection(double x1, double y1, double x2, double y2, + double x3, double y3, double x4, double y4, double *px, double *py): + cdef double u = (x1 * y2 - y1 * x2) + cdef double v = (x3 * y4 - y3 * x4) + cdef double denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4) + if denom == 0: + return 0 + px[0] = (u * (x3 - x4) - (x1 - x2) * v) / denom + py[0] = (u * (y3 - y4) - (y1 - y2) * v) / denom + return 1 + cdef class Line(VertexInstruction): '''A 2d line. @@ -138,6 +149,9 @@ cdef class Line(VertexInstruction): elif self._joint == LINE_JOINT_ROUND: indices_count += (self._cap_precision * 3) * (count - 2) vertices_count += (self._cap_precision + 1) * (count - 2) + elif self._joint == LINE_JOINT_MITER: + indices_count += (count - 2) * 6 + vertices_count += (count - 2) * 2 if self._cap == LINE_CAP_SQUARE: indices_count += 12 @@ -160,9 +174,10 @@ cdef class Line(VertexInstruction): cdef double sx1, sy1, sx4, sy4, sangle cdef double pcx, pcy, px1, py1, px2, py2, px3, py3, px4, py4, pangle, pangle2 cdef double w = self._width + cdef double ix, iy cdef unsigned int pii, piv, pii2, piv2 cdef double jangle - pii = piv = pcx = pcy = cx = cy = ii = iv = 0 + pii = piv = pcx = pcy = cx = cy = ii = iv = ix = iy = 0 for i in range(0, count - 1): ax = p[i * 2] ay = p[i * 2 + 1] @@ -255,6 +270,9 @@ cdef class Line(VertexInstruction): if self._joint == LINE_JOINT_BEVEL: vertices_count -= 1 indices_count -= 3 + elif self._joint == LINE_JOINT_MITER: + vertices_count -= 2 + indices_count -= 6 continue if self._joint == LINE_JOINT_BEVEL: @@ -273,6 +291,48 @@ cdef class Line(VertexInstruction): ii += 3 iv += 1 + elif self._joint == LINE_JOINT_MITER: + vertices[iv].x = ax + vertices[iv].y = ay + vertices[iv].s0 = 0 + vertices[iv].t0 = 0 + if jangle < 0: + if line_intersection(px1, py1, px2, py2, x1, y1, x2, y2, &ix, &iy) == 0: + vertices_count -= 2 + indices_count -= 6 + continue + vertices[iv + 1].x = ix + vertices[iv + 1].y = iy + vertices[iv + 1].s0 = 0 + vertices[iv + 1].t0 = 0 + indices[ii] = iv + indices[ii + 1] = iv + 1 + indices[ii + 2] = piv2 + 1 + indices[ii + 3] = iv + indices[ii + 4] = piv + indices[ii + 5] = iv + 1 + ii += 6 + iv += 2 + else: + if line_intersection(px3, py3, px4, py4, x3, y3, x4, y4, &ix, &iy) == 0: + vertices_count -= 2 + indices_count -= 6 + continue + vertices[iv + 1].x = ix + vertices[iv + 1].y = iy + vertices[iv + 1].s0 = 0 + vertices[iv + 1].t0 = 0 + indices[ii] = iv + indices[ii + 1] = iv + 1 + indices[ii + 2] = piv2 + 2 + indices[ii + 3] = iv + indices[ii + 4] = piv + 3 + indices[ii + 5] = iv + 1 + ii += 6 + iv += 2 + + + elif self._joint == LINE_JOINT_ROUND: # cap end @@ -417,8 +477,8 @@ cdef class Line(VertexInstruction): indices[ii + 2] = piv + 2 ii += 3 - print 'ii=', ii, 'indices_count=', indices_count - print 'iv=', iv, 'vertices_count', vertices_count + #print 'ii=', ii, 'indices_count=', indices_count + #print 'iv=', iv, 'vertices_count', vertices_count self.batch.set_data(vertices, vertices_count, indices, indices_count) From 2f031f652a79fc3f1bb15fb5fb279c4b8b80ff93 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Fri, 21 Sep 2012 19:31:11 +0200 Subject: [PATCH 07/14] line: seperate joint precision, and fix round vertices calculation --- kivy/graphics/vertex_instructions_line.pxi | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/kivy/graphics/vertex_instructions_line.pxi b/kivy/graphics/vertex_instructions_line.pxi index 3f5b2b1b0..15e9d31ff 100644 --- a/kivy/graphics/vertex_instructions_line.pxi +++ b/kivy/graphics/vertex_instructions_line.pxi @@ -37,6 +37,7 @@ cdef class Line(VertexInstruction): ''' cdef int _cap cdef int _cap_precision + cdef int _joint_precision cdef int _joint cdef list _points cdef float _width @@ -53,6 +54,7 @@ cdef class Line(VertexInstruction): self.joint = kwargs.get('joint') or 'round' self.cap = kwargs.get('cap') or 'round' self._cap_precision = kwargs.get('cap_precision') or 10 + self._joint_precision = kwargs.get('joint_precision') or 10 cdef void build(self): if self._width == 1.0: @@ -147,8 +149,8 @@ cdef class Line(VertexInstruction): indices_count += (count - 2) * 3 vertices_count += (count - 2) elif self._joint == LINE_JOINT_ROUND: - indices_count += (self._cap_precision * 3) * (count - 2) - vertices_count += (self._cap_precision + 1) * (count - 2) + indices_count += (self._joint_precision * 3) * (count - 2) + vertices_count += (self._joint_precision) * (count - 2) elif self._joint == LINE_JOINT_MITER: indices_count += (count - 2) * 6 vertices_count += (count - 2) * 2 @@ -158,7 +160,7 @@ cdef class Line(VertexInstruction): vertices_count += 4 elif self._cap == LINE_CAP_ROUND: indices_count += (self._cap_precision * 3) * 2 - vertices_count += (self._cap_precision + 1) * 2 + vertices_count += (self._cap_precision) * 2 vertices = malloc(vertices_count * sizeof(vertex_t)) if vertices == NULL: @@ -340,14 +342,14 @@ cdef class Line(VertexInstruction): a1 = pangle2 - PI2 a2 = angle + PI2 a0 = a2 - step = (abs(jangle)) / float(self._cap_precision) + step = (abs(jangle)) / float(self._joint_precision) pivstart = piv + 3 pivend = piv2 + 1 else: a1 = angle - PI2 a2 = pangle2 + PI2 a0 = a1 - step = -(abs(jangle)) / float(self._cap_precision) + step = -(abs(jangle)) / float(self._joint_precision) pivstart = piv pivend = piv2 + 2 siv = iv @@ -356,7 +358,7 @@ cdef class Line(VertexInstruction): vertices[iv].s0 = 0 vertices[iv].t0 = 0 iv += 1 - for j in xrange(0, self._cap_precision - 1): + for j in xrange(0, self._joint_precision - 1): vertices[iv].x = ax - cos(a0 - step * j) * w vertices[iv].y = ay - sin(a0 - step * j) * w vertices[iv].s0 = 0 @@ -477,8 +479,8 @@ cdef class Line(VertexInstruction): indices[ii + 2] = piv + 2 ii += 3 - #print 'ii=', ii, 'indices_count=', indices_count - #print 'iv=', iv, 'vertices_count', vertices_count + print 'ii=', ii, 'indices_count=', indices_count + print 'iv=', iv, 'vertices_count', vertices_count self.batch.set_data(vertices, vertices_count, indices, indices_count) From a217c363bcef30ef9e21f42e563b593a54c095a3 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Sat, 22 Sep 2012 00:08:49 +0200 Subject: [PATCH 08/14] line: add an example --- examples/canvas/lines.py | 131 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 examples/canvas/lines.py diff --git a/examples/canvas/lines.py b/examples/canvas/lines.py new file mode 100644 index 000000000..21880815d --- /dev/null +++ b/examples/canvas/lines.py @@ -0,0 +1,131 @@ +from kivy.app import App +from kivy.properties import OptionProperty, NumericProperty, ListProperty +from kivy.uix.floatlayout import FloatLayout +from kivy.lang import Builder + +Builder.load_string(''' +: + canvas: + Color: + rgba: .4, .4, 1, root.alpha + Line: + points: self.points + joint: self.joint + cap: self.cap + width: self.linewidth + Color: + rgba: .8, .8, .8, root.alpha_controlline + Line: + points: self.points + + GridLayout: + cols: 2 + size_hint: 1, None + height: 44 * 5 + + GridLayout: + cols: 2 + + Label: + text: 'Alpha' + Slider: + value: root.alpha + on_value: root.alpha = float(args[1]) + min: 0. + max: 1. + Label: + text: 'Alpha Control Line' + Slider: + value: root.alpha_controlline + on_value: root.alpha_controlline = float(args[1]) + min: 0. + max: 1. + Label: + text: 'Width' + Slider: + value: root.linewidth + on_value: root.linewidth = args[1] + min: 1 + max: 40 + Label: + text: 'Cap' + GridLayout: + rows: 1 + ToggleButton: + group: 'cap' + text: 'none' + on_press: root.cap = self.text + ToggleButton: + group: 'cap' + text: 'round' + on_press: root.cap = self.text + ToggleButton: + group: 'cap' + text: 'square' + on_press: root.cap = self.text + Label: + text: 'Joint' + GridLayout: + rows: 1 + ToggleButton: + group: 'joint' + text: 'none' + on_press: root.joint = self.text + ToggleButton: + group: 'joint' + text: 'round' + on_press: root.joint = self.text + ToggleButton: + group: 'joint' + text: 'miter' + on_press: root.joint = self.text + ToggleButton: + group: 'joint' + text: 'bevel' + on_press: root.joint = self.text + + AnchorLayout: + Button: + size_hint: None, None + size: 100, 44 + text: 'Clear' + on_press: root.points = [] + +''') + + +class LinePlayground(FloatLayout): + alpha_controlline = NumericProperty(1.0) + alpha = NumericProperty(0.5) + points = ListProperty([500, 500, 300, 300, 500, 300, 500, 400, 600, 400]) + joint = OptionProperty('none', options=('round', 'miter', 'bevel', 'none')) + cap = OptionProperty('none', options=('round', 'square', 'none')) + linewidth = NumericProperty(10.0) + + def on_touch_down(self, touch): + if super(LinePlayground, self).on_touch_down(touch): + return True + touch.grab(self) + self.points = self.points + list(touch.pos) + return True + + def on_touch_move(self, touch): + if touch.grab_current is self: + self.points[-2:] = list(touch.pos) + return True + return super(LinePlayground, self).on_touch_move(touch) + + def on_touch_up(self, touch): + if touch.grab_current is self: + touch.ungrab(self) + return True + return super(LinePlayground, self).on_touch_up(touch) + + +class TestLineApp(App): + def build(self): + return LinePlayground() + + +if __name__ == '__main__': + TestLineApp().run() From 71ac8e263d6e90461e6dc787d6cf1b12268e8290 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Sat, 22 Sep 2012 00:47:46 +0200 Subject: [PATCH 09/14] stencilinstruction: add the possibility to configure the stencil glop code --- kivy/graphics/stencil_instructions.pxd | 11 +++++++++++ kivy/graphics/stencil_instructions.pyx | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 kivy/graphics/stencil_instructions.pxd diff --git a/kivy/graphics/stencil_instructions.pxd b/kivy/graphics/stencil_instructions.pxd new file mode 100644 index 000000000..1cf556e4c --- /dev/null +++ b/kivy/graphics/stencil_instructions.pxd @@ -0,0 +1,11 @@ +from kivy.graphics.instructions cimport Instruction + +cdef class StencilPush(Instruction): + cdef void apply(self) +cdef class StencilPop(Instruction): + cdef void apply(self) +cdef class StencilUse(Instruction): + cdef unsigned int _op + cdef void apply(self) +cdef class StencilUnUse(Instruction): + cdef void apply(self) diff --git a/kivy/graphics/stencil_instructions.pyx b/kivy/graphics/stencil_instructions.pyx index 092fe3146..6ea16d7e2 100644 --- a/kivy/graphics/stencil_instructions.pyx +++ b/kivy/graphics/stencil_instructions.pyx @@ -147,11 +147,14 @@ cdef class StencilUse(Instruction): '''Use current stencil buffer as a mask. Check module documentation for more information. ''' + def __init__(self, **kwargs): + super(StencilUse, self).__init__(**kwargs) + self._op = kwargs.get('op') or GL_EQUAL cdef void apply(self): global _stencil_in_push _stencil_in_push = 0 glColorMask(1, 1, 1, 1) - glStencilFunc(GL_EQUAL, _stencil_level, 0xff) + glStencilFunc(self._op, _stencil_level, 0xff) glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP) cdef class StencilUnUse(Instruction): From 80b747bf13f5fbd39d8a747e1e305e268e3008bb Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Sat, 22 Sep 2012 00:48:15 +0200 Subject: [PATCH 10/14] line: implement bbox calculation + stencil usage if the current color alpha is not 1.0 --- kivy/graphics/instructions.pxd | 1 + kivy/graphics/vertex_instructions_line.pxi | 67 +++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/kivy/graphics/instructions.pxd b/kivy/graphics/instructions.pxd index fe90ae929..196d931ef 100644 --- a/kivy/graphics/instructions.pxd +++ b/kivy/graphics/instructions.pxd @@ -114,3 +114,4 @@ cdef class RenderContext(Canvas): cpdef draw(self) cdef void reload(self) +cdef RenderContext getActiveContext() diff --git a/kivy/graphics/vertex_instructions_line.pxi b/kivy/graphics/vertex_instructions_line.pxi index 15e9d31ff..bac8e9ce0 100644 --- a/kivy/graphics/vertex_instructions_line.pxi +++ b/kivy/graphics/vertex_instructions_line.pxi @@ -7,6 +7,8 @@ DEF LINE_JOINT_MITER = 1 DEF LINE_JOINT_BEVEL = 2 DEF LINE_JOINT_ROUND = 3 +from kivy.graphics.stencil_instructions cimport StencilUse, StencilUnUse, StencilPush, StencilPop + cdef inline int line_intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, double *px, double *py): cdef double u = (x1 * y2 - y1 * x2) @@ -42,6 +44,13 @@ cdef class Line(VertexInstruction): cdef list _points cdef float _width cdef int _dash_offset, _dash_length + cdef int _use_stencil + cdef Instruction _stencil_rect + cdef Instruction _stencil_push + cdef Instruction _stencil_use + cdef Instruction _stencil_unuse + cdef Instruction _stencil_pop + cdef double _bxmin, _bxmax, _bymin, _bymax def __init__(self, **kwargs): VertexInstruction.__init__(self, **kwargs) @@ -55,6 +64,12 @@ cdef class Line(VertexInstruction): self.cap = kwargs.get('cap') or 'round' self._cap_precision = kwargs.get('cap_precision') or 10 self._joint_precision = kwargs.get('joint_precision') or 10 + self._stencil_rect = None + self._stencil_push = None + self._stencil_use = None + self._stencil_unuse = None + self._stencil_pop = None + self._use_stencil = 0 cdef void build(self): if self._width == 1.0: @@ -62,6 +77,38 @@ cdef class Line(VertexInstruction): else: self.build_extended() + cdef void ensure_stencil(self): + if self._stencil_rect == None: + self._stencil_rect = Rectangle() + self._stencil_push = StencilPush() + self._stencil_pop = StencilPop() + self._stencil_use = StencilUse(op=GL_LEQUAL) + self._stencil_unuse = StencilUnUse() + + cdef void apply(self): + if self._width == 1.: + VertexInstruction.apply(self) + return + + cdef double alpha = getActiveContext()['color'][-1] + self._use_stencil = alpha < 1 + if self._use_stencil: + self.ensure_stencil() + + self._stencil_push.apply() + VertexInstruction.apply(self) + self._stencil_use.apply() + self._stencil_rect.pos = self._bxmin, self._bymin + self._stencil_rect.size = self._bxmax - self._bxmin, self._bymax - self._bymin + self._stencil_rect.apply() + self._stencil_unuse.apply() + VertexInstruction.apply(self) + self._stencil_pop.apply() + else: + VertexInstruction.apply(self) + + + cdef void build_legacy(self): cdef int i, count = len(self.points) / 2 cdef list p = self.points @@ -136,6 +183,11 @@ cdef class Line(VertexInstruction): cdef char *buf = NULL cdef Texture texture = self.texture + self._bxmin = 999999999 + self._bymin = 999999999 + self._bxmax = -999999999 + self._bymax = -999999999 + if count < 2: self.batch.clear_data() return @@ -479,8 +531,19 @@ cdef class Line(VertexInstruction): indices[ii + 2] = piv + 2 ii += 3 - print 'ii=', ii, 'indices_count=', indices_count - print 'iv=', iv, 'vertices_count', vertices_count + #print 'ii=', ii, 'indices_count=', indices_count + #print 'iv=', iv, 'vertices_count', vertices_count + + # compute bbox + for i in xrange(vertices_count): + if vertices[i].x < self._bxmin: + self._bxmin = vertices[i].x + if vertices[i].x > self._bxmax: + self._bxmax = vertices[i].x + if vertices[i].y < self._bymin: + self._bymin = vertices[i].y + if vertices[i].y > self._bymax: + self._bymax = vertices[i].y self.batch.set_data(vertices, vertices_count, indices, indices_count) From 9f4a649a3e3ba62b9e17dfcff811e13730c0360f Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Sat, 22 Sep 2012 01:02:19 +0200 Subject: [PATCH 11/14] enhance line example with 2 lines and sin/cos animation --- examples/canvas/lines.py | 51 ++++++++++++++++++++-- kivy/graphics/vertex_instructions_line.pxi | 1 - 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/examples/canvas/lines.py b/examples/canvas/lines.py index 21880815d..528e03e58 100644 --- a/examples/canvas/lines.py +++ b/examples/canvas/lines.py @@ -2,6 +2,8 @@ from kivy.app import App from kivy.properties import OptionProperty, NumericProperty, ListProperty from kivy.uix.floatlayout import FloatLayout from kivy.lang import Builder +from kivy.clock import Clock +from math import cos, sin Builder.load_string(''' : @@ -17,6 +19,13 @@ Builder.load_string(''' rgba: .8, .8, .8, root.alpha_controlline Line: points: self.points + Color: + rgba: 1, .4, .4, root.alpha + Line: + points: self.points2 + joint: self.joint + cap: self.cap + width: self.linewidth GridLayout: cols: 2 @@ -85,11 +94,20 @@ Builder.load_string(''' on_press: root.joint = self.text AnchorLayout: - Button: + GridLayout: + cols: 1 size_hint: None, None - size: 100, 44 - text: 'Clear' - on_press: root.points = [] + size: self.minimum_size + ToggleButton: + size_hint: None, None + size: 100, 44 + text: 'Animate' + on_state: root.animate(self.state == 'down') + Button: + size_hint: None, None + size: 100, 44 + text: 'Clear' + on_press: root.points = root.points2 = [] ''') @@ -98,9 +116,11 @@ class LinePlayground(FloatLayout): alpha_controlline = NumericProperty(1.0) alpha = NumericProperty(0.5) points = ListProperty([500, 500, 300, 300, 500, 300, 500, 400, 600, 400]) + points2 = ListProperty([]) joint = OptionProperty('none', options=('round', 'miter', 'bevel', 'none')) cap = OptionProperty('none', options=('round', 'square', 'none')) linewidth = NumericProperty(10.0) + dt = NumericProperty(0) def on_touch_down(self, touch): if super(LinePlayground, self).on_touch_down(touch): @@ -121,6 +141,29 @@ class LinePlayground(FloatLayout): return True return super(LinePlayground, self).on_touch_up(touch) + def animate(self, do_animation): + if do_animation: + Clock.schedule_interval(self.update_points_animation, 0) + else: + Clock.unschedule(self.update_points_animation) + + def update_points_animation(self, dt): + cy = self.height * 0.6 + cx = self.width * 0.1 + w = self.width * 0.8 + step = 20 + points = [] + points2 = [] + self.dt += dt + for i in xrange(int(w / step)): + x = i * step + points.append(cx + x) + points.append(cy + cos(x / w * 8. + self.dt) * self.height * 0.2) + points2.append(cx + x) + points2.append(cy + sin(x / w * 8. + self.dt) * self.height * 0.2) + self.points = points + self.points2 = points2 + class TestLineApp(App): def build(self): diff --git a/kivy/graphics/vertex_instructions_line.pxi b/kivy/graphics/vertex_instructions_line.pxi index bac8e9ce0..987462f61 100644 --- a/kivy/graphics/vertex_instructions_line.pxi +++ b/kivy/graphics/vertex_instructions_line.pxi @@ -174,7 +174,6 @@ cdef class Line(VertexInstruction): free(indices) cdef void build_extended(self): - # TODO: current implementation doesn't support alpha well. cdef int i, j, count = len(self.points) / 2 cdef list p = self.points cdef vertex_t *vertices = NULL From 2e736eb192c3e3f254d3362b93c7ee541ff60c78 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Sat, 22 Sep 2012 16:37:14 +0200 Subject: [PATCH 12/14] line: add documentation + python access on cap_precision and joint_precision access. --- kivy/graphics/vertex_instructions_line.pxi | 75 ++++++++++++++++++++-- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/kivy/graphics/vertex_instructions_line.pxi b/kivy/graphics/vertex_instructions_line.pxi index 987462f61..59ab459d5 100644 --- a/kivy/graphics/vertex_instructions_line.pxi +++ b/kivy/graphics/vertex_instructions_line.pxi @@ -23,8 +23,22 @@ cdef inline int line_intersection(double x1, double y1, double x2, double y2, cdef class Line(VertexInstruction): '''A 2d line. - .. versionadded:: 1.0.8 - `dash_offset` and `dash_length` have been added + Drawing a line can be done easily:: + + with self.canvas: + Line(points=[100, 100, 200, 100, 100, 200], width=10) + + Actually, the line have 3 internal drawing mode that you should know about + if you want to get the best performance of it: + + #. If the :data:`width` is 1.0, then we will use standard GL_LINE drawing + from OpenGL. :data:`dash_length` and :data:`dash_offset` works, while + properties for cap and joint have no sense for this. + #. If the :data:`width` is > 1.0, then we will use a custom drawing method, + based on triangles. :data:`dash_length` and :data:`dash_offset` is not + working on that mode. + Additionally, if the current color have an alpha < 1.0, stencil will be + used internally to draw the line. :Parameters: `points`: list @@ -36,6 +50,21 @@ cdef class Line(VertexInstruction): next one, default 0, changing this makes it dashed. `width`: float Width of the line, default 1.0 + `cap`: str, default to 'round' + See :data:`cap` for more information. + `joint`: str, default to 'round' + See :data:`joint` for more information. + `cap_precision`: int, default to 10 + See :data:`cap_precision` for more information + `joint_precision`: int, default to 10 + See :data:`joint_precision` for more information + + .. versionadded:: 1.0.8 + `dash_offset` and `dash_length` have been added + + .. versionadded:: 1.4.1 + `width`, `cap`, `joint`, `cap_precision`, `joint_precision` have been + added. ''' cdef int _cap cdef int _cap_precision @@ -230,7 +259,11 @@ cdef class Line(VertexInstruction): cdef double ix, iy cdef unsigned int pii, piv, pii2, piv2 cdef double jangle + sangle = 0 pii = piv = pcx = pcy = cx = cy = ii = iv = ix = iy = 0 + px1 = px2 = px3 = px4 = py1 = py2 = py3 = py4 = 0 + sx1 = sy1 = sx4 = sy4 = 0 + x1 = x2 = x3 = x4 = y1 = y2 = y3 = y4 = 0 for i in range(0, count - 1): ax = p[i * 2] ay = p[i * 2 + 1] @@ -608,7 +641,8 @@ cdef class Line(VertexInstruction): self.flag_update() property cap: - '''Determine the cap of the line, default to "round" + '''Determine the cap of the line, default to 'round'. Can be one of + 'none', 'square' or 'round' .. versionadded:: 1.4.1 ''' @@ -632,7 +666,8 @@ cdef class Line(VertexInstruction): self.flag_update() property joint: - '''Determine the join of the line, default to "round" + '''Determine the join of the line, default to 'round'. Can be one of + 'none', 'round', 'bevel', 'miter'. .. versionadded:: 1.4.1 ''' @@ -659,3 +694,35 @@ cdef class Line(VertexInstruction): else: self._joint = LINE_JOINT_NONE self.flag_update() + + property cap_precision: + '''Number of iteration for drawing the "round" cap, default to 10. + The cap_precision must be at least 1. + + .. versionadded:: 1.4.1 + ''' + + def __get__(self): + return self._cap_precision + + def __set__(self, value): + if value < 1: + raise GraphicException('Invalid cap_precision value, must be >= 1') + self._cap_precision = int(value) + self.flag_update() + + property joint_precision: + '''Number of iteration for drawing the "round" joint, default to 10. + The joint_precision must be at least 1. + + .. versionadded:: 1.4.1 + ''' + + def __get__(self): + return self._joint_precision + + def __set__(self, value): + if value < 1: + raise GraphicException('Invalid cap_precision value, must be >= 1') + self._joint_precision = int(value) + self.flag_update() From 8c642388afb28d363ccbaafd92077f7f3346c899 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Mon, 24 Sep 2012 15:41:13 +0200 Subject: [PATCH 13/14] line: add close property, use it for both legacy or extended mode. + don't draw cap if line is closed + optimize cos/sin calculation + fix crash due to invalid calculation if some round joint are avoided --- examples/canvas/lines.py | 13 +++- kivy/graphics/vertex_instructions_line.pxi | 72 ++++++++++++++++------ 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/examples/canvas/lines.py b/examples/canvas/lines.py index 528e03e58..09c8879e8 100644 --- a/examples/canvas/lines.py +++ b/examples/canvas/lines.py @@ -1,5 +1,6 @@ from kivy.app import App -from kivy.properties import OptionProperty, NumericProperty, ListProperty +from kivy.properties import OptionProperty, NumericProperty, ListProperty, \ + BooleanProperty from kivy.uix.floatlayout import FloatLayout from kivy.lang import Builder from kivy.clock import Clock @@ -15,10 +16,12 @@ Builder.load_string(''' joint: self.joint cap: self.cap width: self.linewidth + close: self.close Color: rgba: .8, .8, .8, root.alpha_controlline Line: points: self.points + close: self.close Color: rgba: 1, .4, .4, root.alpha Line: @@ -26,6 +29,7 @@ Builder.load_string(''' joint: self.joint cap: self.cap width: self.linewidth + close: self.close GridLayout: cols: 2 @@ -93,6 +97,12 @@ Builder.load_string(''' text: 'bevel' on_press: root.joint = self.text + Label: + text: 'Close' + ToggleButton: + text: 'Close line' + on_press: root.close = self.state == 'down' + AnchorLayout: GridLayout: cols: 1 @@ -115,6 +125,7 @@ Builder.load_string(''' class LinePlayground(FloatLayout): alpha_controlline = NumericProperty(1.0) alpha = NumericProperty(0.5) + close = BooleanProperty(False) points = ListProperty([500, 500, 300, 300, 500, 300, 500, 400, 600, 400]) points2 = ListProperty([]) joint = OptionProperty('none', options=('round', 'miter', 'bevel', 'none')) diff --git a/kivy/graphics/vertex_instructions_line.pxi b/kivy/graphics/vertex_instructions_line.pxi index 59ab459d5..692ca35ec 100644 --- a/kivy/graphics/vertex_instructions_line.pxi +++ b/kivy/graphics/vertex_instructions_line.pxi @@ -58,13 +58,15 @@ cdef class Line(VertexInstruction): See :data:`cap_precision` for more information `joint_precision`: int, default to 10 See :data:`joint_precision` for more information + `close`: bool, default to False + If True, the line will be closed. .. versionadded:: 1.0.8 `dash_offset` and `dash_length` have been added .. versionadded:: 1.4.1 - `width`, `cap`, `joint`, `cap_precision`, `joint_precision` have been - added. + `width`, `cap`, `joint`, `cap_precision`, `joint_precision`, `close` + have been added. ''' cdef int _cap cdef int _cap_precision @@ -74,6 +76,7 @@ cdef class Line(VertexInstruction): cdef float _width cdef int _dash_offset, _dash_length cdef int _use_stencil + cdef int _close cdef Instruction _stencil_rect cdef Instruction _stencil_push cdef Instruction _stencil_use @@ -93,6 +96,7 @@ cdef class Line(VertexInstruction): self.cap = kwargs.get('cap') or 'round' self._cap_precision = kwargs.get('cap_precision') or 10 self._joint_precision = kwargs.get('joint_precision') or 10 + self._close = int(bool(kwargs.get('close', 0))) self._stencil_rect = None self._stencil_push = None self._stencil_use = None @@ -151,6 +155,10 @@ cdef class Line(VertexInstruction): self.batch.clear_data() return + if self._close: + p = p + [p[0], p[1]] + count += 1 + self.batch.set_mode('line_strip') if self._dash_offset != 0: if texture is None or texture._width != \ @@ -208,6 +216,7 @@ cdef class Line(VertexInstruction): cdef vertex_t *vertices = NULL cdef unsigned short *indices = NULL cdef float tex_x + cdef int cap cdef char *buf = NULL cdef Texture texture = self.texture @@ -220,6 +229,12 @@ cdef class Line(VertexInstruction): self.batch.clear_data() return + cap = self._cap + if self._close and count > 2: + p = p + p[0:4] + count += 2 + cap = LINE_CAP_NONE + self.batch.set_mode('triangles') cdef unsigned int vertices_count = (count - 1) * 4 cdef unsigned int indices_count = (count - 1) * 6 @@ -235,10 +250,10 @@ cdef class Line(VertexInstruction): indices_count += (count - 2) * 6 vertices_count += (count - 2) * 2 - if self._cap == LINE_CAP_SQUARE: + if cap == LINE_CAP_SQUARE: indices_count += 12 vertices_count += 4 - elif self._cap == LINE_CAP_ROUND: + elif cap == LINE_CAP_ROUND: indices_count += (self._cap_precision * 3) * 2 vertices_count += (self._cap_precision) * 2 @@ -257,13 +272,14 @@ cdef class Line(VertexInstruction): cdef double pcx, pcy, px1, py1, px2, py2, px3, py3, px4, py4, pangle, pangle2 cdef double w = self._width cdef double ix, iy - cdef unsigned int pii, piv, pii2, piv2 + cdef unsigned int piv, pii2, piv2 cdef double jangle sangle = 0 - pii = piv = pcx = pcy = cx = cy = ii = iv = ix = iy = 0 + piv = pcx = pcy = cx = cy = ii = iv = ix = iy = 0 px1 = px2 = px3 = px4 = py1 = py2 = py3 = py4 = 0 sx1 = sy1 = sx4 = sy4 = 0 x1 = x2 = x3 = x4 = y1 = y2 = y3 = y4 = 0 + cdef double cos1 = 0, cos2 = 0, sin1 = 0, sin2 = 0 for i in range(0, count - 1): ax = p[i * 2] ay = p[i * 2 + 1] @@ -282,9 +298,7 @@ cdef class Line(VertexInstruction): py3 = y3 py4 = y4 - pii2 = pii piv2 = piv - pii = ii piv = iv pangle2 = pangle pangle = angle @@ -297,14 +311,18 @@ cdef class Line(VertexInstruction): a2 = angle + PI2 # calculate the position of the segment - x1 = ax + cos(a1) * w - y1 = ay + sin(a1) * w - x4 = ax + cos(a2) * w - y4 = ay + sin(a2) * w - x2 = bx + cos(a1) * w - y2 = by + sin(a1) * w - x3 = bx + cos(a2) * w - y3 = by + sin(a2) * w + cos1 = cos(a1) * w + sin1 = sin(a1) * w + cos2 = cos(a2) * w + sin2 = sin(a2) * w + x1 = ax + cos1 + y1 = ay + sin1 + x4 = ax + cos2 + y4 = ay + sin2 + x2 = bx + cos1 + y2 = by + sin1 + x3 = bx + cos2 + y3 = by + sin2 if i == 0: sx1 = x1 @@ -353,7 +371,10 @@ cdef class Line(VertexInstruction): # in case of the angle is NULL, avoid the generation if jangle == 0 or jangle == PI or jangle == -PI: - if self._joint == LINE_JOINT_BEVEL: + if self._joint == LINE_JOINT_ROUND: + vertices_count -= self._joint_precision + indices_count -= self._joint_precision * 3 + elif self._joint == LINE_JOINT_BEVEL: vertices_count -= 1 indices_count -= 3 elif self._joint == LINE_JOINT_MITER: @@ -463,7 +484,7 @@ cdef class Line(VertexInstruction): ii += 3 # caps - if self._cap == LINE_CAP_SQUARE: + if cap == LINE_CAP_SQUARE: vertices[iv].x = x2 + cos(angle) * w vertices[iv].y = y2 + sin(angle) * w vertices[iv].s0 = 0 @@ -497,7 +518,7 @@ cdef class Line(VertexInstruction): ii += 6 iv += 2 - elif self._cap == LINE_CAP_ROUND: + elif cap == LINE_CAP_ROUND: # cap start a1 = sangle - PI2 @@ -726,3 +747,16 @@ cdef class Line(VertexInstruction): raise GraphicException('Invalid cap_precision value, must be >= 1') self._joint_precision = int(value) self.flag_update() + + property close: + '''If True, the line will be closed. + + .. versionadded:: 1.4.1 + ''' + + def __get__(self): + return self._close + + def __set__(self, value): + self._close = int(bool(value)) + self.flag_update() From d58ee1fdcb10bfb91e5767dfd41b535503064214 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Mon, 24 Sep 2012 18:22:49 +0200 Subject: [PATCH 14/14] line: add image for line instruction --- doc/sources/images/line-instruction.png | Bin 0 -> 17714 bytes kivy/graphics/vertex_instructions_line.pxi | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 doc/sources/images/line-instruction.png diff --git a/doc/sources/images/line-instruction.png b/doc/sources/images/line-instruction.png new file mode 100644 index 0000000000000000000000000000000000000000..d6973d0ee8b55587cdee185ecbbb633a7099372a GIT binary patch literal 17714 zcmeHvc~p~Ew>Oq5wy3Gs0Tm_os#Plz6cLpnTB{aOX{CzD5G|965M?kxNFJ3cuUd(s z5^(@gktzy`Kx9fpM3i|DBV(Au6apk<9=`nqR8;Ky-u10-t?!S!9G;NmoU_k9`|Pv# z@3#-9e%-cd;iAo4-JnzV&5ZA+eJiWXr)g=;)w2G1-S2@lJ@v@J zs3m`=_?*#rF(z7la_IZHjDd^ zUmR^ody>0W9!b?&R28cpTdD{Z;i}FdTM>JEx@+wGTu;fMNG0c!E%ua!Q3tTNF*6p^ z^lLWB?e8eRX9*b$hWYf(%N6N&@Mmht_xQT6Dc{ldKs(yG9)N z^w$|Ov={8_IM)69h8RoXui;pHw25bln_R@JFF^#wW|igT=RP0f|1j%l~=-RnA@{}tUt z(ezM4C>AliYa?%MqP6!_Kh8T=YtPpUuIEYP2L}U<54`?cq2S_6^RdV^kw}y^P_QK1 z06U?*4BNvNc@g9nH_EkLWZ1yQV|7$H&*9;ob@vhy9v`T>=fhPu>fnC~2nd)pYgWbE zx4SHqrD{c~U&~puc4)VosaEp2VR*$}s_+(eS}9(A?55cnJzR-GDzMwJ<5Fj3Joovj zDf{>BTNJHs48?e;6wq2 zX4Pd*U-u32H%?{$SZUt-WS*eC*k;F$9g#-PHAxM)!T#2GymJ5arMoZI`eZI;JCmHF z$-S#NMNWgUF%wi?^3){C=3B}!>Vgt&ok^`F=KLaaewN@8J>mZSBF`8~XvN^pZ%&i?~GqZS?ql4ty{Mq>8-2g=>#@z4#WSBEENBGK>bHBKWI0Bro+z`yF@N^ zj08bK+76>8m(af|mcp}}#lr-4@;f?S-nPC_QC%&K6HGVUbuQJ5?eXTm!+s*s7P?J3 ze|C=fN^fuPyZ7&Bbw~;e3VeI((wp~N(jFuyFOLZsuzqn?pPkfAI&$RB&6_uC^A!p~ zsfkC?3{KvJ%@Q#Ozr>HbtvD-4aI%hTaZ5f=xVH16PwmCaH7ZennMhq1bj!4L&*)t<>gEKet#PQ-cZ{ECq^TtKk zVr^q%QU&Q#_|MevpP-RtB zK#u3vOmQ|p?%us0!4&r&JXn;3D7%}3vXxWSYiSQ+W4}*LO}+cz!PfQb*RS-hdCl)O zGc|Q@d2u$%l6>UHAAejh*U@!< z{r7L_q8cw9f4Tn3gD;b)g9&y1m0K6%a5z;Z-lwa?=H-%- z5}0XQwP#HBeUhvu)yw2?^Yfls2F^N}s+3s>icFMDRYpL!Z+lVH!TJnSSc<~CcJaw% zaj&66@^jJ(`vs_rTju7{f`47ZPY~}&*co^C?qVH`!Iq$)pxus+k6rV_sT``StLwKX zexeGla$05GNu}{I)HOG{Zz-276JBWTYtn-)l7io(P2H-_8wg(YC(_@9slxVFSxx9I z!v_uY-M}ep!YcN$8g0g{$A|FibWf@d?Ut;QuTP@b;3r(@TdFel4YRtT+zZFVLaRTK z&L)h={O|$n$&K>deg`V=mNK@A=_$)8(@DWx3WmV$Y{7KbdDII@YRe-he1_*9dif!1pl zK?A3qtgL^qjSk7FB$G3Hv&rH~QN-|Af5Y)=-wLK?D6&PFs?P^`n$*dc?oKVgwTXLb zVe~K1MNLi3z*|?EJ6KG%i>k_=uocXD&6+j7U9azEDFzA&-!c;t5+w0BQhk6uiSb~yL#`i^=xJKSDg!vEFtGV&rY|j+XjXUbr`U6wu$15DJi|$2QLPi?>mPmMii3a4 zH!EvVcG@CWQ16{q>r)uOcBXTq$=i%;u+w?;1vATNQ6$*2X9ns?oZ^AD!pDeOHUHIp zhsVj@wP{7yRtt{P`BL!*r^{@V(tQtF63I96YXo2NvY@d^8i{JKT|7cj|DeT#q!tP| z+T?@P$+K4-YFt9o=3!DrxS$dKHEKTi(4vJgArXgLUJNcl&E}dpDUtowsO@cVgJ>-+ zl{;u|Zf^J6Z{<8!s!Gb8Y2xwM_CE;&r33p3usW(=i7ZyW{i3NE*|)g~kt(Kvt)a38 zYXmD$NJdv+%hi<+B;p}|=)X26nomO%E$;fa-%X~txELI0x2W{oNoL=sXJxS7sY%MF zGAa_Dl$vS^E=-S-wRGvy^!^w6V{a+jXI(<$p0y0t-rSRlE_+tNROIes9L?lgNxdT`CJ~1$R#y$rw?PLl~ByUM(-ccW5O$;a{ z6?cnHQS%N5$qzz92v;@1Xq#vgy+&`>^YMsVg%vswiBycjDp} zT)%!jNIA$>`~Q04Cx|94L`FuYq@{TjMlQX3?_Nf$t*F?HvjsxWNd^{|Pw482g41)X zjQ+&W-iKQHkX_cQj?z^+27z-K94Z-&OuD+d?(a^$$1WV*-55o4-oCw@$!D5ZRaK!! zV88Iv5_^wV5epnr_WkDbr0FDhX$x7$tvmti$GXJCM4VFk3`6Y;{oUR^9**#{XU}Fg zyvQ@qdxmTCl)_@5E(f+8ow%MqBOd?5Ofu&Jpu$_{81aJr7Tx^;Q>`bq-wH@+m)TS!C5x zb~VQWR@3R@zUivst7h>KDS$slUEyCdO-^$u!tzb&;INnPefDRwyZ)?M+riC%RjpXD z!VMyiyh4O)&as9l=LoOn(v>S$nmZvXt$uRY)uS}}`svfB|H9&}z$_O>Bg^o4ghf+* zWx`JXfB;Ho_37w*$|F>zQ6H&PA{OEYU)$aN`|p?f#H{nDrY!VeCpxDOzHz)Tf631K zFI#B%=(GA}9(7L+J7ga5{$bm-%iydQSieak#CS#r-3fGLDcW#;kZqxosa5V9*Nx8Nr;Kxi7za(OTjgf4M^C;1npakFhnTooTne%Ytv1Jg{#Kt!| z>b&3OnGo?jGZU_p*XZ!*Xeje1u_F}qHMoA!QssC~nGVE+DTHR8qiixW7NgnB&ubaN zc_Hhus8gYyVh}qwNlse9dfZU{7m-szd*@CTlzey{N&U|11_e!jKYyYvDVqNKG-7z6 zeVot?MR+IB82Egh60N0{rB?*DaP-N zHmqOSs87_Y3G!XC6YsR2|5+Cw*8Wplz}kEm`^#O7T>Fu>Fl2Qg)qAUZB-BN%4vyed zS{wmg3}_2ujAjEPfG*JO^X~a*csrrbq3!d}U96Sw z>D1F~{!gEyYwMGVpgxQ8P^de~^NVZtp$jV*L$mCkKL6?ih3x47-L~Z++?vjgtNS?l&EjubYmVmDB1dYjK7BJ!U#vgd0 zCkS%mpZ`U(Xo#zyW~d8_(hYqi2pt`p&$2@2HLM zV!YFMAN_Zj0{~CV9rktih6>c+feq5H&VRX zp|DhHt?e{YTQ3J4aIbjX$y|RU#zv)=_?&gkIhE832==%zx_Bg94bZdJQyAhCO#2Jq z_WN%SznE~mvr@grkE?n%XAGQY#Llqo6V)*8!c$F{;h!g|bx-2K$HE!9c~wX*77k1} zDCf=nr`u%)tw!_nk*SH$8UXFo13Lg4N5PKS=0^-!kUgH}5;{_k zSlV{;o{R*v|JEHXk!f6(d+A%`VHkjyaDXOJP7>wr*5ibg3lnxeC;+rUkc(9r=ZDYH zz!HGWM~khum7<9hhaz^*J3S4+Xxxh?6xj6uR0-GpO(8DL865A~;J7ab1g*Xt9$21Sp@m2`oi$H6B3pHMl59@a>f|MXuSHTUKX+wpnG-Z-!#%dYts^$6izJo zTjME7!#8SdHwqx#v8Cju6M6%!7i>MA{rOE401NgFDB29hqa%ED73dZJ&I%MlV6c?l z{X(!pN}Jl7k(PEREiH{L(DAJ!%--}4oqqAx%$CgSHj|Lvy1Uhnxi2@IK)~DB3;h7~ zLqU>KH088Ww-;Zn1ZV>Qo!@uv^ngZ4#-pFUkGGB4W^Zr5#@w9cU(cuT$&gTQhMocN z6?7BS8G$ZDd>+E&hj7i&94FB^6VgLF3ksS{x!${XuOQjIFvFP$oKcXhGv36#;3CjF zKf)&T7MsV?T6*t7f~IfgQv{m`ps4d;tf2VS%$9V+CcIiXQN0u^b1w34N;y!%_pQ!f$c&-(-ox zf=36Il{XVfaWvi^G~FglUMvByRBJA4{onaoJTG+tLk-s5a|zFw>&h@^{6NnpIpO!H}*e) z&MvPPQe$1ad9H-N&R9a${`w^KZYqA41v-&=36x?lUW_^+i+t+GdmQO^vvFj1PLc8YhW{Mmdfht zDAWd|Y(&Yf0~oBDNg>zN)}mmTTFFwA0OT&E7=3>OmlZGd*T1hrjqp+VxK zE=W=5^$-EFLutquWsm3LsoyHEaERH1I{~?==A|)}JR%nulrDA;*cCvJUg`+s9Lh5l zp@bqJ+cKG~&bM%>Lw0aL5}lA0c(}bZCI$rC5@Th#aND(C04Ze@0?Rs09kLflSc*X* zU;w@-Rsi<5`_7iA2(7@SlMNS@kbTPIm0;q3KT)1|qV`7!R}+ zFRj+MK7ic@(8`p*z!qNe7QOFlM@tqgjS+jM3kGd7ze8(05;34^{yxLv#Ya$ z{8%3N8mtcwBPmV??~}1X`Z?O=yuZu#T`hpOW4}6@?84-7;$en>mc(Ub$c}Rf1&@@~ zF-iy2@%qd00=)b&zVMi~jxU^dqM(C>RlcpiOvkxgTOT%Qfi-|q$$s3DW(?1{VA(uH z({f>$om|f^1Pr9-mvq1hPX`zLd|l@|5smFH^14S zfG6fkfXD${o?M<^TTu}Kc-C*mfLMNaK>b3W*ducEmtEl?YB3T6-e?s-bwM4mVHw#9 zb`Kl2IPz-fh$$;O2Y4HhqjvxVy}HsnqodZ`zvEYwGJfQmkDfbum1Oy1RpDI*tsc%J z6dZ$i2|ZjO?tuD(*_Xy6&nKZo!ha0x+f0M3REMFozVHKppB85I{&vxTR{}_x#u3P4 z)Y2FY)LfIY7>zbmZ3I;uhpmJ?;9F`bc7qf6s|wY_eg9KpU{cNX^4fftFCG_vZ;;Fr z)vpA1O_{^Fw<7^179eQ50}`S>=#lFt7LoPn(K6uCSkhvmF96(Jpl;`s4Z^97%T~gP z)cW+8cGRlFf1m{yz@{p4@rw2R3RMxGCC_9P;+2(nci5iwKAB0~iMt;CHw~`}T0g&`Uh|5|& z{+P((*r?@vR!n$!cpZsDVLHOeB=e-8(R!#T43TTAE?u9iI4@n}qJae<(mGBY^Bzv* zeGDfO-o&YMG5Fb$oC&#uPbO=41=HJ<6b_a?XsWcAFTKm^sz1W&dXjZ14FE-S5IAY+ zzVcf(JYB;-fWz1i2QM~-let%VMWteznvG|r?9;r3|HTH~ZwA2~u_)b-f~O*0Ejhbq z^b@98DsCVX(b5yxk8A>>wAIzYfp*EOJ+N=zOF=0<2E637 zBvR|k^M)>2tHLJD*&Olu^=mh7$@Qo;0bTY6jtF8LKO#&I92MuMtH46GTv@Rgh#JZZ zwjdd*9;6W6F88T>GXE8WIr!k<+lSwqnwqAAK*=P2!+dK`PfsXi$fy@_V~}W|x19VH zy3-9`qQo4q$|(#+{GB^@0;{7(B{C#!miaVnDdaG+fNG4Dj%0&ZYClxacvN73bF zw*ni0JHANnO<#yI5-w?>7VKm=%ty!ukuv5T}ABwx# z^ymj2L3Mg$piVAoXjzEYfkXwe3b9q&*;-zL(;^E9RMZKel!YTP+tfPwkG#_sDoG2< zTR^=G9j&o}%&VF~Bt*C=WrIQOQJ$w6gM|duax+ed-?nS3&V$E(5-9s(8Aj_(d^won zZSY1=i%<-8Dgse0kz~$Sv>1G*BBO5ef#Vu%5*z$2GvK>-96`!|$PqMrL^=M4WQ3ve z9WgNsqXQhFfzv;Fei6_|q19p1LIYN4fMJm1)8~6gc9;lw*AB(UQM_!J6!<^}ydwqR z`TiRkn0-`!gOndC8VRO;%u#5lhfzix(VqrX-G!p;z^uG`4lyj?fxj&Da2KFO4XFC* z(*{2to!!6dViYW0+qlMgZb%a*8fthBJ3urXy{Yalg!(Xj@hcPM5aj?r&{vJX%mJ8x z2fL$8%@A_`DcLdd=OK~^`$-pygm$0?+TdUMJk9wt%vr#2uAO@CD56hW4I$X?bocpl zG)CY9y8K?rAJqSG7a+*zaK6TN3_oZ?!;_3y!I51IDenst7{bp-z~i5;Si!&R0t;WzFk|nYBbl#4skT0u2+E4QV@F0lbISgK z)A_K1U*-pT{+AQ@|A!OE8sZ0XU*4q5*lWXz#hhe{_O=25`B1d?wxM!5bw>IfTeTN2 zcl;X;C;Ap;M!OB`1a_n78UCCKD1p04p7K;oZb&Di53GgRRZZpP8WUto6>AZ!b_z!2 z2~>l`NLrk3JwaD=l61 zG)Iv4Xx!JEiQn{H)|wYTXoWU1{?4Ucdpec&jj?byZkOa(p*Q&4tk*h*ih3)Z#;!D% z$lHa_FuA03gGmkBe%?@AU`C~Fw8gdgt3xCkNdHB@WVZk5 zRlhM$cesdfZZL};ilo`KQ|D+?TA$dn8qaxkAq>ZTtP)w-;3HMv2On~FUc6+9pW(&U zMpjX7mcPM8f7%JPjZx)e6*Fp+G5%%Yxi<|By}K4w=ODx6(`j`bt@lx1Xk5(X&Y@y? ztb$;uMmde^x5Sk#;1xiQRLsVG12UG)Ed-$Y9U(q1n91X=&kEa0G(J#P=1mOA$v+n%5A8pb4)vE zy<f_I5_b z8bH#a#@H}g#YI$Hv>HzU)WTAZS|!%-33ypK?KC%rwMgjE-<*fmT|(*u6qE4Us>A;c zZSw@!kiMLv7(5=57+@Gmmon-isuJwQa@i95#3DeXlJ}M0tUHuD5dg3tsK23y+ywkvxRlUb26W6k`xscAL3rfu4FF&NMZA85i-s%?`=jbv3qq2k*Hc?jD zYRqX%qHDp$#b{Iul*5JjUrJ?^8LIR1HYEr4H_u+=hLEeAg^j@4V9eFEw`j^AF0v41q!JYxPR7`Grp zx!Py~wVn{wJCf&ghr(yJAcYxDg3@xSv=oU3jA|9=31D=4VG>yvA4%O@J33I}f@8s^ zMja2_l!>yU1fz*8ltcQ^HRdmU_UweNJir3I+klp7qc>-eem&$oOIgcW8qFBB{(FTr z@Kup@6mfmMPd9+%#lVWmvy8^6f6-U0zbpUvYR*5@gB!w1nzROSr;ZbP{eXhn6WahE zGb^OyN>xhvh51k$Spc_dPM2O0XN8oqY@?Ph0E)~eAYgE8DeIzZ!br3^0>GDf4uJko znzQEOd;kw#0d9=mwg>_Cw)85I83p|-R5euy+w}r|E~XxZd+S z*@NPqAh$yX^H2UJ9tG!?6tt`ruYXA9+@AM#A88D|`6@@@!657)!G)g?xKhz09>hZ} zD?m7%r)Ss3t`EylZafQgF&BeKfP)ADxY{~GEmo@t4Z+nd}eBl)7nh8f#rgHk(_V#x0RRQFgIZ|vlU#;Q`fXj;?zN{8a5mi$OdZEbt ziK^H-$|yDGc{erIwJ{tm6a5sh@h@kfQ%6|Aq6n<*i7utBk{ zy*j}m|Jl_XW{*yN?yv1_mQE3);W|b#5q01~%x&<_5D~oMBWigF+|1*N14un@&%>oG zH$Z~Was}C7w*aH=!{iCr4;<;}y*_ZFz&-CqujfH^geO!o=M^gIIZ9_$XN+nw+>_`! zQcH0rK^;MmqCfvJfXIL8n;8KTC@9|B*G>H{gLS#Z5v_2zas`ceF4F1o5SN%A>FE&j z%vP?v^XsBkGnRB~B1zAqV4M#>ciU{eVEbZvLZCS7e!!UH*U@ayXU3G3MQv=O^E zlpyYCcvjliXsUEt$&tJ;ORUN7m4-)gp^93+0NgMQb@JGg*#eyqKmw1^;H>peBCn#+ z#l}5ZaIdQOYmfr%yz~;_i;-al!HZB(5-!vw!%^mfGavoxW1uQq+;9~t*rBw#eUMF1 zSDLwxLQWcWjw~3d&Lj~dbXhAJ6_tt45AW&gb`hh;)r6o?BEKX$r7uUv1W_qov)L&4 z%&7CV*_q_xY=N>3;HF6Ebx6Pvn>$Aj;LdPyPEQU!&8OtgA-OeV1wlzbK}DR6Jm5Mr z99&n~HW3e@ezvFEXw@ z#l~{d1XVPYn{Hr*&$DKQ?}a>JT$}23c5eV-d3~*Xqj9OwWG;aCl*2PbZKzvo03NuR zg8^rX-n7VLse$itS9K++G*#44h;Swzl91JiBo|vzRdoqUkrcIXW=xY${GKovii8Hu zzNl(sXZMy{LTJ4by%h@q(O`Engu&|=g>QyfwLF%YnVBcBruz>ZC?F#8TAz5}+v4uu zzc29w#F(!1+wbj-22@F0%$pGlwivwu0~Hhy(QSv~d-SqPseA+zl~=fO#fsZ$X)EFW z3|usQd1vd@({yzSiFpB#V6<)pd{JKIsP|&*EY!Zr=`^Tj;&#Tzlw4eFhqCETWL|20 z3BP-lv1C&LI;{X=lcz*Lmd!jDA07N7T&oF!>#xhvOMB2eqaoNU!tE0mfCk4r- zE`<7(B9qdo#BMrE;qJqfwxf9h2Cq2OFAV5@(}udki~ui?I})H|ces4)knHx#UFo#* z!R|`{>Cd(ZG)C{bczYK^2slm&azSY9kFmdk?YL3sd^88YDinF5nv3gTx@9oLXZJwa z#0*8K@kimCRwx#>BHYLx$I8KEuwkLbgmM~n>bnilNRHSya?5`l*?r!^P&w2B-R#5B zA#`7c_BnyNRt%TGhsQRSOr(}&qt0Z8VjRB11^s9O^}QsTz!5|}I>Oz+*dIyvek=$< zpEjCF)`{V2`Datpj6FP`&s(5wOykYak05A8weW828)P)b{i2{4R8KH9jo@oXjQzzH zLrRA3p^6Zl8YIgj$NrLZ`iggwjH%Z~rwT#$$gx3<(9@`DWaYe^H#R^^>+5**_X1!t zbUvQu2RVjYg91k^!UuEsY??!IfQdX5hH6YC07S~sEW(gVQK3l|BgK5LB4|6Tq7Pz! z7#L*I*x`RJ>bn+fbSXYxk$)7`P`L+EksD+HUNkF${tRc^do_JNxpytlr9h2M8=W_- z$Y)~@EAnIMhV~VzG%z;wBd~^dW24p(+B;H_ACCna#!{oFwPLML$Ic&C{bwzPZ3B#L zcxofZhHVEueNoU)ruJd%;T9nHu**Ov|4&nUKenqz+o;l(4LJZ1YozUsGBsFIa8Dn` z0x^cidRjr#*xqPU)2MY=y3bn#V9FSPrfv4_TlmV9y`{7+opp#C+B@c7p0dR`=+dCW z4K0ltA(M#Rb0>`5bFt{z?$!K_<3I|LeFBq zW~J+Vr~KM19A3{B7F!7xlP9 zW-YNLY~V6ktDJTg8+fBGQ_*hFxW=j^uW5VNbZk-7N|ltecBT%+4>MP`_-76)AuV9e z9`B85JwfJ?>0vYcFgIgr_4D`kk3X6>k$N@0j^TQ0me#!j6XmtmQ^&qKj#rzF@t9bS zSzFeqvsRe<)8^}Ew8n&T)_q+x>n8T-(;WV<814h@xw1$5jy0Nb_RP^LFSXp2GPidl zk(smRDJ9$Kcr&F$6&6tMhU82+C@fu}o{K$nHDImy6!mA;T&*1bIgEX&s`ZAc<{iA& z4f(%dY^Xp&fA>c@6iojMaN4yfR(cbD^~ZPU?*Qoltn;%^R@LaWRoq=?-M}LQpsHoP NVcXA%KkYgGe*g<%^c4UA literal 0 HcmV?d00001 diff --git a/kivy/graphics/vertex_instructions_line.pxi b/kivy/graphics/vertex_instructions_line.pxi index 692ca35ec..965c71680 100644 --- a/kivy/graphics/vertex_instructions_line.pxi +++ b/kivy/graphics/vertex_instructions_line.pxi @@ -40,6 +40,9 @@ cdef class Line(VertexInstruction): Additionally, if the current color have an alpha < 1.0, stencil will be used internally to draw the line. + .. image:: images/line-instruction.png + :align: center + :Parameters: `points`: list List of points in the format (x1, y1, x2, y2...)