Skip to content

Instantly share code, notes, and snippets.

@clayote
Created February 15, 2014 15:55
Show Gist options
  • Save clayote/9021211 to your computer and use it in GitHub Desktop.
Save clayote/9021211 to your computer and use it in GitHub Desktop.
A new vertex instruction for drawing arrows that point from things to other things. It's only displaying the "trunk" at the moment.
diff --git a/kivy/graphics/vertex_instructions.pyx b/kivy/graphics/vertex_instructions.pyx
index b867281..214ea57 100644
--- a/kivy/graphics/vertex_instructions.pyx
+++ b/kivy/graphics/vertex_instructions.pyx
@@ -50,7 +50,7 @@ This module includes all the classes for drawing simple vertex objects.
'''
__all__ = ('Triangle', 'Quad', 'Rectangle', 'BorderImage', 'Ellipse', 'Line',
- 'Point', 'Mesh', 'GraphicException', 'Bezier')
+ 'Point', 'Mesh', 'GraphicException', 'Bezier', 'Arrow')
include "config.pxi"
@@ -987,3 +987,158 @@ cdef class Ellipse(Rectangle):
def __set__(self, value):
self._angle_end = value
self.flag_update()
+
+cdef extern from "math.h":
+ double hypot(double x, double y) nogil
+
+cdef void get_arrow_verts(vertex_t *dest, float *pts, float r, float taillen):
+ cdef float xco = -1.0 if pts[0] < pts[2] else 1.0
+ cdef float yco = -1.0 if pts[1] < pts[3] else 1.0
+ cdef float off
+ dest[0].x = (pts[0] + r) * xco
+ dest[0].y = (pts[1] + r) * yco
+ dest[1].x = (pts[2] + r) * xco
+ dest[1].y = (pts[3] + r) * yco
+ # there are five points in the destination, but most are based on dest[1]
+ cdef float rise = dest[1].y - dest[0].y
+ cdef float run = dest[1].x - dest[0].x
+ cdef float length = hypot(rise, run) - r
+ cdef float theta, opp_theta
+ if run == 0:
+ if rise >= 0:
+ theta = PI2
+ else:
+ theta = -PI2
+ else:
+ theta = atan2(rise, run)
+ if rise == 0:
+ if run >= 0:
+ opp_theta = PI2
+ else:
+ opp_theta = -PI2
+ else:
+ opp_theta = atan2(run, rise)
+ if r != 0:
+ # truncate the line so it doesn't slip under the node
+ dest[1].x = dest[0].x + cos(theta) * length
+ dest[1].y = dest[0].y + sin(theta) * length
+ dest[2].x = dest[3].x = dest[4].x = dest[1].x
+ dest[2].y = dest[3].y = dest[4].y = dest[1].y
+ # drawing an arrowhead
+ if rise == 0:
+ off = cos(PI / 4) * taillen
+ dest[2].x += off
+ dest[2].y += off
+ dest[4].x += off
+ dest[4].y -= off
+ elif run == 0:
+ off = sin(PI / 4) * taillen
+ dest[2].x += off
+ dest[2].y += off
+ dest[4].x -= off
+ dest[4].y += off
+ else:
+ theta -= PI / 4
+ opp_theta = PI - PI / 4 - opp_theta
+ dest[2].x += cos(theta) * taillen
+ dest[2].y += sin(theta) * taillen
+ dest[4].x += cos(opp_theta) * taillen
+ dest[4].y += sin(opp_theta) * taillen
+ for i in xrange(0, 5):
+ dest[i].x *= xco
+ dest[i].y *= yco
+ print("Drawing an arrow. "
+ "trunk ({},{})->({},{}); left wedge ({},{})->({},{}); "
+ "right wedge ({},{})->({},{}).".format(
+ dest[0].x, dest[0].y,
+ dest[1].x, dest[1].y,
+ dest[1].x, dest[1].y,
+ dest[2].x, dest[2].y,
+ dest[3].x, dest[3].y,
+ dest[4].x, dest[4].y))
+cdef class Arrow(VertexInstruction):
+ cdef float me[4]
+ cdef float r, tl
+ cdef vertex_t _vertices[5]
+ cdef VertexFormat vertex_format
+
+ def __init__(self, **kwargs):
+ VertexInstruction.__init__(self, **kwargs)
+ fmt = kwargs.get('fmt')
+ if fmt is not None:
+ self.vertex_format = VertexFormat(*fmt)
+ self.batch = VertexBatch(vbo=VBO(self.vertex_format))
+ self.mode = kwargs.get('mode') or 'line_strip'
+ self.r = kwargs.get('radius') or 20.0
+ self.tl = kwargs.get('taillen') or 10.0
+
+ cdef void build(self):
+ cdef vertex_t *vertices = NULL
+ cdef unsigned short indices[5]
+ for i in xrange(0, 5):
+ indices[i] = i
+ vertices = <vertex_t *>malloc(5 * sizeof(vertex_t))
+ if vertices == NULL:
+ free(vertices)
+ raise MemoryError('vertices')
+ get_arrow_verts(vertices, self.me, self.r, self.tl)
+ self.batch.set_data(vertices, 5, indices, 5)
+ free(vertices)
+
+ property mode:
+ def __get__(self):
+ self.batch.get_mode()
+ def __set__(self, mode):
+ self.batch.set_mode(mode)
+
+ property ox:
+ def __get__(self):
+ return self.me[0]
+ def __set__(self, v):
+ self.me[0] = float(v)
+ self.flag_update()
+
+ property oy:
+ def __get__(self):
+ return self.me[1]
+ def __set__(self, v):
+ self.me[1] = float(v)
+ self.flag_update()
+
+ property dx:
+ def __get__(self):
+ return self.me[2]
+ def __set__(self, v):
+ self.me[2] = float(v)
+ self.flag_update()
+
+ property dy:
+ def __get__(self):
+ return self.me[3]
+ def __set__(self, v):
+ self.me[3] = float(v)
+ self.flag_update()
+
+ property trunk:
+ def __get__(self):
+ return [self.me[0], self.me[1], self.me[2], self.me[3]]
+ def __set__(self, v):
+ if len(v) != 4:
+ raise ValueError("Trunk has exactly 2 points, so 4 values")
+ for i in xrange(0, 4):
+ self.me[i] = float(v[i])
+ self.flag_update()
+
+ property radius:
+ def __get__(self):
+ return self.r
+ def __set__(self, v):
+ self.r = float(v)
+ self.flag_update()
+
+ property taillen:
+ def __get__(self):
+ return self.tl
+ def __set__(self, v):
+ self.tl = float(v)
+ self.flag_update()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment