File: border.py

package info (click to toggle)
python-vispy 0.15.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,868 kB
  • sloc: python: 59,799; javascript: 6,800; makefile: 69; sh: 6
file content (208 lines) | stat: -rw-r--r-- 6,877 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright (c) Vispy Development Team. All Rights Reserved.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
# -----------------------------------------------------------------------------
# Author: Siddharth Bhat
# -----------------------------------------------------------------------------

import numpy as np

from . import Visual
from ..color import Color

_VERTEX_SHADER = """
attribute vec2 a_position;
attribute vec2 a_adjust_dir;

void main() {
    // First map the vertex to document coordinates
    vec4 doc_pos = $visual_to_doc(vec4(a_position, 0, 1));

    // Also need to map the adjustment direction vector, but this is tricky!
    // We need to adjust separately for each component of the vector:
    vec4 adjusted;
    if ( a_adjust_dir.x == 0. ) {
        // If this is an outer vertex, no adjustment for line weight is needed.
        // (In fact, trying to make the adjustment would result in no
        // triangles being drawn, hence the if/else block)
        adjusted = doc_pos;
    }
    else {
        // Inner vertexes must be adjusted for line width, but this is
        // surprisingly tricky given that the rectangle may have been scaled
        // and rotated!
        vec4 doc_x = $visual_to_doc(vec4(a_adjust_dir.x, 0., 0., 0.)) -
                    $visual_to_doc(vec4(0., 0., 0., 0.));
        vec4 doc_y = $visual_to_doc(vec4(0, a_adjust_dir.y, 0., 0.)) -
                    $visual_to_doc(vec4(0., 0., 0., 0.));
        doc_x = normalize(doc_x);
        doc_y = normalize(doc_y);

        // Now doc_x + doc_y points in the direction we need in order to
        // correct the line weight of _both_ segments, but the magnitude of
        // that correction is wrong. To correct it we first need to
        // measure the width that would result from using doc_x + doc_y:
        vec4 proj_y_x = dot(doc_x, doc_y) * doc_x;  // project y onto x
        float cur_width = length(doc_y - proj_y_x);  // measure current weight

        // And now we can adjust vertex position for line width:
        adjusted = doc_pos + ($border_width / cur_width) * (doc_x + doc_y);
    }

    // Finally map the remainder of the way to render coordinates
    gl_Position = $doc_to_render(adjusted);
}
"""

_FRAGMENT_SHADER = """
void main() {
    gl_FragColor = $border_color;
}
"""  # noqa


class _BorderVisual(Visual):
    """
    Visual subclass to display 2D pixel-width borders.

    Parameters
    ----------
    pos : tuple (x, y)
        Position where the colorbar is to be placed with
        respect to the center of the colorbar
    halfdim : tuple (half_width, half_height)
        Half the dimensions of the colorbar measured
        from the center. That way, the total dimensions
        of the colorbar is (x - half_width) to (x + half_width)
        and (y - half_height) to (y + half_height)
    border_width : float (in px)
        The width of the border the colormap should have. This measurement
        is given in pixels
    border_color : str | vispy.color.Color
        The color of the border of the colormap. This can either be a
        str as the color's name or an actual instace of a vipy.color.Color
    """

    _shaders = {
        'vertex': _VERTEX_SHADER,
        'fragment': _FRAGMENT_SHADER,
    }

    def __init__(self, pos, halfdim,
                 border_width=1.0,
                 border_color=None,
                 **kwargs):

        self._pos = pos
        self._halfdim = halfdim
        self._border_width = border_width
        self._border_color = Color(border_color)

        Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment'])

    @staticmethod
    def _prepare_transforms(view):

        program = view.shared_program
        program.vert['visual_to_doc'] = \
            view.transforms.get_transform('visual', 'document')
        program.vert['doc_to_render'] = \
            view.transforms.get_transform('document', 'render')

    @property
    def visual_border_width(self):
        """The border width in visual coordinates"""
        render_to_doc =  \
            self.transforms.get_transform('document', 'visual')

        vec = render_to_doc.map([self.border_width, self.border_width, 0])
        origin = render_to_doc.map([0, 0, 0])

        visual_border_width = [vec[0] - origin[0], vec[1] - origin[1]]

        # we need to flip the y axis because coordinate systems are inverted
        visual_border_width[1] *= -1

        return visual_border_width

    def _update(self):
        x, y = self._pos
        halfw, halfh = self._halfdim

        border_vertices = np.array([
            [x - halfw, y - halfh],
            [x - halfw, y - halfh],

            [x + halfw, y - halfh],
            [x + halfw, y - halfh],

            [x + halfw, y + halfh],
            [x + halfw, y + halfh],

            [x - halfw, y + halfh],
            [x - halfw, y + halfh],

            [x - halfw, y - halfh],
            [x - halfw, y - halfh],
        ], dtype=np.float32)

        # Direction each vertex should move to correct for line width
        adjust_dir = np.array([
            [0, 0], [-1, -1],
            [0, 0], [1, -1],
            [0, 0], [1, 1],
            [0, 0], [-1, 1],
            [0, 0], [-1, -1],
        ], dtype=np.float32)

        self.shared_program['a_position'] = border_vertices
        self.shared_program['a_adjust_dir'] = adjust_dir
        self.shared_program.vert['border_width'] = float(self._border_width)
        self.shared_program.frag['border_color'] = self._border_color.rgba

    def _prepare_draw(self, view=None):
        self._update()
        self._draw_mode = "triangle_strip"
        return True

    @property
    def border_width(self):
        """The width of the border"""
        return self._border_width

    @border_width.setter
    def border_width(self, border_width):
        self._border_width = border_width
        # positions of text need to be changed accordingly
        self._update()

    @property
    def border_color(self):
        """The color of the border in pixels"""
        return self._border_color

    @border_color.setter
    def border_color(self, border_color):
        self._border_color = Color(border_color)
        self.shared_program.frag['border_color'] = self._border_color.rgba

    @property
    def pos(self):
        """The center of the BorderVisual"""
        return self._pos

    @pos.setter
    def pos(self, pos):
        self._pos = pos
        self._update()

    @property
    def halfdim(self):
        """The half-dimensions measured from the center of the BorderVisual"""
        return self._halfdim

    @halfdim.setter
    def halfdim(self, halfdim):
        self._halfdim = halfdim
        self._update()