line: add bezier/bezier_precision properties

This commit is contained in:
Mathieu Virbel 2012-10-01 17:00:19 +02:00
parent 038f8ffad7
commit 04c5535631
2 changed files with 100 additions and 1 deletions

View File

@ -83,6 +83,20 @@ Builder.load_string('''
Label:
center: root.center
text: 'Rectangle'
<LineBezier>:
canvas:
Color:
rgba: .1, .1, 1, .9
Line:
width: 2.
bezier: (self.x, self.y, self.center_x - 40, self.y + 100, self.center_x + 40, self.y - 100, self.right, self.y)
Label:
center: root.center
text: 'Bezier'
''')
@ -107,6 +121,9 @@ class LineCircle3(Widget):
class LineRectangle(Widget):
pass
class LineBezier(Widget):
pass
class LineExtendedApp(App):
def build(self):
root = GridLayout(cols=2, padding=50, spacing=50)
@ -117,6 +134,7 @@ class LineExtendedApp(App):
root.add_widget(LineCircle2())
root.add_widget(LineCircle3())
root.add_widget(LineRectangle())
root.add_widget(LineBezier())
return root
if __name__ == '__main__':

View File

@ -11,6 +11,7 @@ DEF LINE_MODE_POINTS = 0
DEF LINE_MODE_ELLIPSE = 1
DEF LINE_MODE_CIRCLE = 2
DEF LINE_MODE_RECTANGLE = 3
DEF LINE_MODE_BEZIER = 4
from kivy.graphics.stencil_instructions cimport StencilUse, StencilUnUse, StencilPush, StencilPop
@ -77,6 +78,11 @@ cdef class Line(VertexInstruction):
`rectangle`: list
If set, the :data:`points` will be set to build a rectangle. Check
:data:`rectangle` for more information.
`bezier`: list
If set, the :data:`points` will be set to build a bezier line. Check
:data:`bezier` for more information.
`bezier_precision`: int, default to 180
Precision of the Bezier drawing.
.. versionadded:: 1.0.8
`dash_offset` and `dash_length` have been added
@ -84,10 +90,14 @@ cdef class Line(VertexInstruction):
.. versionadded:: 1.4.1
`width`, `cap`, `joint`, `cap_precision`, `joint_precision`, `close`,
`ellipse`, `rectangle` have been added.
.. versionadded:: 1.4.1
`bezier`, `bezier_precision` have been added.
'''
cdef int _cap
cdef int _cap_precision
cdef int _joint_precision
cdef int _bezier_precision
cdef int _joint
cdef list _points
cdef float _width
@ -115,6 +125,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._bezier_precision = kwargs.get('bezier_precision') or 180
self._close = int(bool(kwargs.get('close', 0)))
self._stencil_rect = None
self._stencil_push = None
@ -130,6 +141,8 @@ cdef class Line(VertexInstruction):
self.prebuild_circle()
elif self._mode == LINE_MODE_RECTANGLE:
self.prebuild_rectangle()
elif self._mode == LINE_MODE_BEZIER:
self.prebuild_bezier()
if self._width == 1.0:
self.build_legacy()
else:
@ -767,7 +780,7 @@ cdef class Line(VertexInstruction):
def __set__(self, value):
if value < 1:
raise GraphicException('Invalid cap_precision value, must be >= 1')
raise GraphicException('Invalid joint_precision value, must be >= 1')
self._joint_precision = int(value)
self.flag_update()
@ -987,3 +1000,71 @@ cdef class Line(VertexInstruction):
self._points = [x, y, x + width, y, x + width, y + height, x, y + height]
self._close = 1
property bezier:
'''Use this property to build a rectangle, without calculate the
:data:`points`. You can only set this property, not get it.
The argument must be a tuple of (x, y, width, height)
angle_end, segments):
* x and y represent the bottom-left position of the rectangle
* width and height represent the size
The line is automatically closed.
Usage::
Line(rectangle=(0, 0, 200, 200))
.. versionadded:: 1.4.2
'''
def __set__(self, args):
if args == None:
raise GraphicException(
'Invalid rectangle value: {0!r}'.format(args))
self._mode_args = tuple(args)
self._mode = LINE_MODE_BEZIER
self.flag_update()
cdef void prebuild_bezier(self):
cdef double x, y, l
cdef int segments = self._bezier_precision
cdef list T = list(self._mode_args)[:]
self._points = []
for x in xrange(segments):
l = x / (1.0 * segments)
# http://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm
# as the list is in the form of (x1, y1, x2, y2...) iteration is
# done on each item and the current item (xn or yn) in the list is
# replaced with a calculation of "xn + x(n+1) - xn" x(n+1) is
# placed at n+2. each iteration makes the list one item shorter
for i in range(1, len(T)):
for j in xrange(len(T) - 2*i):
T[j] = T[j] + (T[j+2] - T[j]) * l
# we got the coordinates of the point in T[0] and T[1]
self._points.append(T[0])
self._points.append(T[1])
# add one last point to join the curve to the end
self._points.append(T[-2])
self._points.append(T[-1])
property bezier_precision:
'''Number of iteration for drawing the bezier between 2 segments,
default to 180. The bezier_precision must be at least 1.
.. versionadded:: 1.4.2
'''
def __get__(self):
return self._bezier_precision
def __set__(self, value):
if value < 1:
raise GraphicException('Invalid bezier_precision value, must be >= 1')
self._bezier_precision = int(value)
self.flag_update()