diff --git a/cr_paths/line_agg_collec.py b/cr_paths/line_agg_collec.py index 02e3b2d..54ef2e8 100644 --- a/cr_paths/line_agg_collec.py +++ b/cr_paths/line_agg_collec.py @@ -184,7 +184,9 @@ def __init__(self, **kwargs): self._program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) self._collec = LineCollection(**kwargs) self._V, self._I, self._U = self._collec.build_buffers() + self._V_buf = gloo.VertexBuffer(self._V) self.index = gloo.IndexBuffer(self._I) + self._dash_atlas = gloo.Texture2D(self._collec.da._data) def set_options(self): gloo.set_state(clear_color=(1, 1, 1, 1), blend=True, @@ -199,10 +201,11 @@ def draw(self): # WARNING: THIS IS TERRIBLY INEFFICIENT BECAUSE ALL DATA # IS SENT ON GPU AT EVERY REFRESH!!! # We need to put this stuff at initialization time. - self._program._create() - self._program._build() # attributes / uniforms are not available until program is built - self._program.bind(gloo.VertexBuffer(self._V)) + # attributes / uniforms are not available until program is built + self._program.prepare() + + self._program.bind(self._V_buf) for n, v in uniforms.iteritems(): self._program[n] = v @@ -210,13 +213,7 @@ def draw(self): for n, v in self._U[0].iteritems(): self._program[n] = v - self._program['tr_scale'] = self._parent.panzoom.scale[:2] - - self._program['u_dash_atlas'] = gloo.Texture2D(self._collec.da._data) - width, height = self.width, self.height - self._program['u_scale'] = width//2, height//2 - self._program['u_proj'] = orthographic( -width//2, width//2, - -height//2, height//2, -1, +1 ) + self._program['u_dash_atlas'] = self._dash_atlas self._program.draw('triangles', indices=self.index) @@ -227,7 +224,8 @@ def draw(self): x = np.linspace(-1., 1., 1000) - y = .25*np.sin(15*x) + y = .25*np.sin(15*x) + 1. + print y vertices1 = np.c_[x,y] vertices2 = np.c_[np.cos(3*x)*.5, np.sin(3*x)*.5] diff --git a/cr_paths/path2.vert b/cr_paths/path2.vert index c45b186..2959283 100644 --- a/cr_paths/path2.vert +++ b/cr_paths/path2.vert @@ -20,15 +20,23 @@ void rotate( in vec2 v, in float alpha, out vec2 result ) { } vec4 transform(vec4); +vec4 doc_px_transform(vec4); +vec4 px_ndc_transform(vec4); + +vec2 transform_vector(vec2 x, vec2 base) { + vec4 o = transform(vec4(base, 0, 1)); + return (transform(vec4(base+x, 0, 1)) - o).xy; +} + + // Uniforms //uniform mat4 u_matrix; //uniform mat4 u_view; -uniform mat4 u_proj; attribute vec4 color; -uniform vec2 u_scale; -uniform vec2 tr_scale; +// uniform vec2 u_scale; +// uniform vec2 tr_scale; uniform float linewidth; uniform float antialias; uniform vec2 linecaps; @@ -43,15 +51,15 @@ uniform float closed; // Attributes -attribute vec2 a_position; -attribute vec4 a_tangents; -attribute vec2 a_segment; +attribute vec2 a_position; // position of each vertex +attribute vec4 a_tangents; // vector pointing from one vertex to the next +attribute vec2 a_segment; // distance along path attribute vec2 a_angles; attribute vec2 a_texcoord; // Varying varying vec4 v_color; -varying vec2 v_segment; +varying vec2 v_segment; varying vec2 v_angles; varying vec2 v_linecaps; varying vec2 v_texcoord; @@ -88,8 +96,9 @@ void main() // Attributes to varyings v_angles = a_angles; - v_segment = a_segment * u_scale.x * tr_scale.x; // TODO: proper scaling - v_length = v_length * u_scale * tr_scale; // TODO: proper scaling + //v_segment = a_segment * u_scale.x * tr_scale.x; // TODO: proper scaling + //v_length = v_length * u_scale * tr_scale; // TODO: proper scaling + v_segment = a_segment; // Thickness below 1 pixel are represented using a 1 pixel thickness // and a modified alpha @@ -104,11 +113,19 @@ void main() } // This is the actual half width of the line + // TODO: take care of logical - physical pixel difference here. float w = ceil(1.25*v_antialias+v_linewidth)/2.0; - vec2 position = transform(vec4(a_position,0.,1.)).xy*u_scale; - vec2 t1 = normalize(tr_scale*a_tangents.xy); - vec2 t2 = normalize(tr_scale*a_tangents.zw); + //vec2 position = transform(vec4(a_position,0.,1.)).xy*u_scale; + vec2 position = transform(vec4(a_position,0.,1.)).xy; + // At this point, position must be in _doc_ coordinates because the line + // width will be added to it. + + + //vec2 t1 = normalize(tr_scale*a_tangents.xy); + //vec2 t2 = normalize(tr_scale*a_tangents.zw); + vec2 t1 = normalize(transform_vector(a_tangents.xy, a_position)); + vec2 t2 = normalize(transform_vector(a_tangents.zw, a_position)); float u = a_texcoord.x; float v = a_texcoord.y; vec2 o1 = vec2( +t1.y, -t1.x); @@ -203,7 +220,8 @@ void main() // Miter distance // ------------------------------------------------------------------------ vec2 t; - vec2 curr = transform(vec4(a_position,0.,1.)).xy*u_scale; + //vec2 curr = transform(vec4(a_position,0.,1.)).xy*u_scale; + vec2 curr = transform(vec4(a_position,0.,1.)).xy; if( a_texcoord.x < 0.0 ) { vec2 next = curr + t2*(v_segment.y-v_segment.x); @@ -232,5 +250,5 @@ void main() v_texcoord = vec2( u, v*w ); - gl_Position = u_proj*vec4(position, 0.0, 1.0); + gl_Position = px_ndc_transform(doc_px_transform(vec4(position, 0.0, 1.0))); } diff --git a/cr_paths/plot.py b/cr_paths/plot.py index 751b474..10237cd 100644 --- a/cr_paths/plot.py +++ b/cr_paths/plot.py @@ -7,7 +7,7 @@ from vispy import gloo from vispy.scene.shaders import Function, ModularProgram from vispy.scene.visuals import Visual -from vispy.scene.transforms import STTransform +from vispy.scene.transforms import STTransform, LogTransform, PolarTransform class PanZoomTransform(STTransform): @@ -15,21 +15,14 @@ class PanZoomTransform(STTransform): def move(self, (dx, dy)): """I call this when I want to translate.""" - self.pan = (self.pan[0] + dx/self.scale[0], - self.pan[1] + dy/self.scale[1]) - self.translate = (self.pan[0]*self.scale[0], - self.pan[1]*self.scale[1]) + self.translate = self.translate + (dx, -dy, 0, 0) def zoom(self, (dx, dy), center=(0., 0.)): """I call this when I want to zoom.""" - scale = (self.scale[0] * exp(2.5*dx), - self.scale[1] * exp(2.5*dy)) - tr = self.pan - self.pan = (tr[0] - center[0] * (1./self.scale[0] - 1./scale[0]), - tr[1] + center[1] * (1./self.scale[1] - 1./scale[1])) - self.scale = scale - self.translate = (self.pan[0]*self.scale[0], - self.pan[1]*self.scale[1]) + scale = (exp(0.01*dx), exp(0.01*dy), 1, 1) + center = center + (0., 1.) + self.translate = center + ((self.translate - center) * scale) + self.scale = self.scale * scale class MarkerVisual(Visual): @@ -154,19 +147,37 @@ def draw(self): class PlotCanvas(app.Canvas): - def _normalize(self, (x, y)): - w, h = float(self.size[0]), float(self.size[1]) - return x/(w/2.)-1., y/(h/2.)-1. + #def _normalize(self, (x, y)): + #w, h = float(self.size[0]), float(self.size[1]) + #return x/(w/2.)-1., y/(h/2.)-1. def __init__(self, **kwargs): app.Canvas.__init__(self, close_keys='escape', **kwargs) self._visuals = [] self.panzoom = PanZoomTransform() + self.panzoom.scale = (200, -200) + self.panzoom.translate = (300, 300) + self.doc_px_transform = STTransform(scale=(1, 1), translate=(0, 0)) + self.px_ndc_transform = STTransform(scale=(1, 1), translate=(0, 0)) + self._update_transforms() + + def _update_transforms(self): + # Update doc and pixel transforms to account for new canvas shape. + + # Eventually this should be provided by the base Canvas class + # and should account for logical vs physical pixels, framebuffers, + # and glViewport. + + s = self.size + self.px_ndc_transform.scale = (2.0 / s[0], -2.0 / s[1]) + self.px_ndc_transform.translate = (-1, 1) def add_visual(self, name, value): self._visuals.append(value) value._parent = self - value._program['transform'] = self.panzoom.shader_map() + value._program['transform'] = (self.panzoom * PolarTransform()).shader_map() + value._program['doc_px_transform'] = self.doc_px_transform.shader_map() + value._program['px_ndc_transform'] = self.px_ndc_transform.shader_map() def __setattr__(self, name, value): super(PlotCanvas, self).__setattr__(name, value) @@ -175,9 +186,12 @@ def __setattr__(self, name, value): def on_mouse_move(self, event): if event.is_dragging: - x0, y0 = self._normalize(event.press_event.pos) - x1, y1 = self._normalize(event.last_event.pos) - x, y = self._normalize(event.pos) + #x0, y0 = self._normalize(event.press_event.pos) + #x1, y1 = self._normalize(event.last_event.pos) + #x, y = self._normalize(event.pos) + x0, y0 = event.press_event.pos + x1, y1 = event.last_event.pos + x, y = event.pos dxy = ((x - x1), -(y - y1)) center = (x0, y0) button = event.press_event.button @@ -188,20 +202,20 @@ def on_mouse_move(self, event): self.panzoom.zoom(dxy, center=center) self.update() - self.panzoom.shader_map() def on_mouse_wheel(self, event): c = event.delta[1] * .1 x, y = self._normalize(event.pos) self.panzoom.zoom((c, c), center=(x, y)) self.update() - self.panzoom.shader_map() def on_resize(self, event): self.width, self.height = event.size gloo.set_viewport(0, 0, self.width, self.height) + self._update_transforms() for v in self._visuals: v.resize(event.size) + def on_draw(self, event): gloo.clear() @@ -211,6 +225,10 @@ def on_draw(self, event): def show(self): super(PlotCanvas, self).show() app.run() + + + +