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
|
# -*- 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()
|