Merge branch 'core-line'

This commit is contained in:
Mathieu Virbel 2012-09-24 18:23:07 +02:00
commit 72c7eb5deb
8 changed files with 974 additions and 134 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

185
examples/canvas/lines.py Normal file
View File

@ -0,0 +1,185 @@
from kivy.app import App
from kivy.properties import OptionProperty, NumericProperty, ListProperty, \
BooleanProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.clock import Clock
from math import cos, sin
Builder.load_string('''
<LinePlayground>:
canvas:
Color:
rgba: .4, .4, 1, root.alpha
Line:
points: self.points
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:
points: self.points2
joint: self.joint
cap: self.cap
width: self.linewidth
close: self.close
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
Label:
text: 'Close'
ToggleButton:
text: 'Close line'
on_press: root.close = self.state == 'down'
AnchorLayout:
GridLayout:
cols: 1
size_hint: None, None
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 = []
''')
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'))
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):
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)
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):
return LinePlayground()
if __name__ == '__main__':
TestLineApp().run()

View File

@ -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

View File

@ -114,3 +114,4 @@ cdef class RenderContext(Canvas):
cpdef draw(self)
cdef void reload(self)
cdef RenderContext getActiveContext()

View File

@ -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)

View File

@ -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):

View File

@ -19,143 +19,14 @@ 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):
'''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 = <char *>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 = <vertex_t *>malloc(count * sizeof(vertex_t))
if vertices == NULL:
raise MemoryError('vertices')
indices = <unsigned short *>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 = [

View File

@ -0,0 +1,765 @@
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
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)
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.
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.
.. image:: images/line-instruction.png
:align: center
: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
`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
`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`, `close`
have been added.
'''
cdef int _cap
cdef int _cap_precision
cdef int _joint_precision
cdef int _joint
cdef list _points
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
cdef Instruction _stencil_unuse
cdef Instruction _stencil_pop
cdef double _bxmin, _bxmax, _bymin, _bymax
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'
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
self._stencil_unuse = None
self._stencil_pop = None
self._use_stencil = 0
cdef void build(self):
if self._width == 1.0:
self.build_legacy()
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
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._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 != \
(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 = <char *>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 = <vertex_t *>malloc(count * sizeof(vertex_t))
if vertices == NULL:
raise MemoryError('vertices')
indices = <unsigned short *>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):
cdef int i, j, 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 int cap
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
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
cdef unsigned int iv = 0, ii = 0
if self._joint == LINE_JOINT_BEVEL:
indices_count += (count - 2) * 3
vertices_count += (count - 2)
elif self._joint == LINE_JOINT_ROUND:
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
if cap == LINE_CAP_SQUARE:
indices_count += 12
vertices_count += 4
elif cap == LINE_CAP_ROUND:
indices_count += (self._cap_precision * 3) * 2
vertices_count += (self._cap_precision) * 2
vertices = <vertex_t *>malloc(vertices_count * sizeof(vertex_t))
if vertices == NULL:
raise MemoryError('vertices')
indices = <unsigned short *>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, pangle2
cdef double w = self._width
cdef double ix, iy
cdef unsigned int piv, pii2, piv2
cdef double jangle
sangle = 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]
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
piv2 = piv
piv = iv
pangle2 = pangle
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
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
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_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:
vertices_count -= 2
indices_count -= 6
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
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
if jangle < 0:
a1 = pangle2 - PI2
a2 = angle + PI2
a0 = a2
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._joint_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._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
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 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
elif 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
ii += 3
#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)
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()
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'. Can be one of
'none', 'square' or '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'. Can be one of
'none', 'round', 'bevel', 'miter'.
.. 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()
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()
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()