Created
February 15, 2014 15:55
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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