Permalink
Fetching contributors…
Cannot retrieve contributors at this time
182 lines (143 sloc) 5.42 KB
# -*- coding: utf-8 -*-
# vispy: gallery 30
# -----------------------------------------------------------------------------
# Copyright (c) Vispy Development Team. All Rights Reserved.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
# -----------------------------------------------------------------------------
# Author: John David Reaver
# Date: 04/29/2014
# -----------------------------------------------------------------------------
from vispy import app, gloo
# Shader source code
# -----------------------------------------------------------------------------
vertex = """
attribute vec2 position;
void main()
{
gl_Position = vec4(position, 0, 1.0);
}
"""
fragment = """
uniform vec2 resolution;
uniform vec2 center;
uniform float scale;
vec3 hot(float t)
{
return vec3(smoothstep(0.00,0.33,t),
smoothstep(0.33,0.66,t),
smoothstep(0.66,1.00,t));
}
void main()
{
const int n = 300;
const float log_2 = 0.6931471805599453;
vec2 c;
// Recover coordinates from pixel coordinates
c.x = (gl_FragCoord.x / resolution.x - 0.5) * scale + center.x;
c.y = (gl_FragCoord.y / resolution.y - 0.5) * scale + center.y;
float x, y, d;
int i;
vec2 z = c;
for(i = 0; i < n; ++i)
{
x = (z.x*z.x - z.y*z.y) + c.x;
y = (z.y*z.x + z.x*z.y) + c.y;
d = x*x + y*y;
if (d > 4.0) break;
z = vec2(x,y);
}
if ( i < n ) {
float nu = log(log(sqrt(d))/log_2)/log_2;
float index = float(i) + 1.0 - nu;
float v = pow(index/float(n),0.5);
gl_FragColor = vec4(hot(v),1.0);
} else {
gl_FragColor = vec4(hot(0.0),1.0);
}
}
"""
# vispy Canvas
# -----------------------------------------------------------------------------
class Canvas(app.Canvas):
def __init__(self, *args, **kwargs):
app.Canvas.__init__(self, *args, **kwargs)
self.program = gloo.Program(vertex, fragment)
# Draw a rectangle that takes up the whole screen. All of the work is
# done in the shader.
self.program["position"] = [(-1, -1), (-1, 1), (1, 1),
(-1, -1), (1, 1), (1, -1)]
self.scale = self.program["scale"] = 3
self.center = self.program["center"] = [-0.5, 0]
self.apply_zoom()
self.bounds = [-2, 2]
self.min_scale = 0.00005
self.max_scale = 4
gloo.set_clear_color(color='black')
self._timer = app.Timer('auto', connect=self.update, start=True)
self.show()
def on_draw(self, event):
self.program.draw()
def on_resize(self, event):
self.apply_zoom()
def apply_zoom(self):
width, height = self.physical_size
gloo.set_viewport(0, 0, width, height)
self.program['resolution'] = [width, height]
def on_mouse_move(self, event):
"""Pan the view based on the change in mouse position."""
if event.is_dragging and event.buttons[0] == 1:
x0, y0 = event.last_event.pos[0], event.last_event.pos[1]
x1, y1 = event.pos[0], event.pos[1]
X0, Y0 = self.pixel_to_coords(float(x0), float(y0))
X1, Y1 = self.pixel_to_coords(float(x1), float(y1))
self.translate_center(X1 - X0, Y1 - Y0)
def translate_center(self, dx, dy):
"""Translates the center point, and keeps it in bounds."""
center = self.center
center[0] -= dx
center[1] -= dy
center[0] = min(max(center[0], self.bounds[0]), self.bounds[1])
center[1] = min(max(center[1], self.bounds[0]), self.bounds[1])
self.program["center"] = self.center = center
def pixel_to_coords(self, x, y):
"""Convert pixel coordinates to Mandelbrot set coordinates."""
rx, ry = self.size
nx = (x / rx - 0.5) * self.scale + self.center[0]
ny = ((ry - y) / ry - 0.5) * self.scale + self.center[1]
return [nx, ny]
def on_mouse_wheel(self, event):
"""Use the mouse wheel to zoom."""
delta = event.delta[1]
if delta > 0: # Zoom in
factor = 0.9
elif delta < 0: # Zoom out
factor = 1 / 0.9
for _ in range(int(abs(delta))):
self.zoom(factor, event.pos)
def on_key_press(self, event):
"""Use + or - to zoom in and out.
The mouse wheel can be used to zoom, but some people don't have mouse
wheels :)
"""
if event.text == '+' or event.text == '=':
self.zoom(0.9)
elif event.text == '-':
self.zoom(1/0.9)
def zoom(self, factor, mouse_coords=None):
"""Factors less than zero zoom in, and greater than zero zoom out.
If mouse_coords is given, the point under the mouse stays stationary
while zooming. mouse_coords should come from MouseEvent.pos.
"""
if mouse_coords is not None: # Record the position of the mouse
x, y = float(mouse_coords[0]), float(mouse_coords[1])
x0, y0 = self.pixel_to_coords(x, y)
self.scale *= factor
self.scale = max(min(self.scale, self.max_scale), self.min_scale)
self.program["scale"] = self.scale
# Translate so the mouse point is stationary
if mouse_coords is not None:
x1, y1 = self.pixel_to_coords(x, y)
self.translate_center(x1 - x0, y1 - y0)
if __name__ == '__main__':
canvas = Canvas(size=(800, 800), keys='interactive')
app.run()