# -*- coding: utf-8 -*-
# vispy: gallery 10:50:5
# -----------------------------------------------------------------------------
# Copyright (c) Vispy Development Team. All Rights Reserved.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
# -----------------------------------------------------------------------------
"""
Update Image and Isocurve Visuals
=================================

Show use of SceneCanvas to display and update Image and Isocurve visuals using
ViewBox visual.
"""
import sys
import numpy as np
from itertools import cycle

from vispy import app, scene
from vispy.scene import STTransform
from vispy.util.filter import gaussian_filter
from vispy.color import get_colormaps, get_color_names

canvas = scene.SceneCanvas(keys='interactive',
                           title='Show update capabilities of Isocurve Visual',
                           show=True)
canvas.show()

# Setting up four viewboxes
vb1 = scene.widgets.ViewBox(border_color='yellow', parent=canvas.scene)
vb2 = scene.widgets.ViewBox(border_color='blue', parent=canvas.scene)
vb3 = scene.widgets.ViewBox(border_color='red', parent=canvas.scene)
vb4 = scene.widgets.ViewBox(border_color='purple', parent=canvas.scene)

vb = (vb1, vb2, vb3, vb4)

# add grid as central widget, add viewboxes into grid
grid = canvas.central_widget.add_grid()
grid.padding = 0
grid.add_widget(vb1, 0, 0)
grid.add_widget(vb2, 0, 1)
grid.add_widget(vb3, 1, 0)
grid.add_widget(vb4, 1, 1)

# panzoom cameras for every viewbox
for box in vb:
    box.camera = 'panzoom'
    box.camera.aspect = 1.0

# Create random image
img_data1 = np.empty((200, 100, 3), dtype=np.ubyte)
noise = np.random.normal(size=(200, 100), loc=50, scale=150)
noise = gaussian_filter(noise, (4, 4, 0)).astype(np.float32)
img_data1[:] = noise[..., np.newaxis]

# create 2d array with some function
x, y = np.mgrid[0:2*np.pi:201j, 0:2*np.pi:101j]
myfunc = np.cos(2*x[:-1, :-1]) + np.sin(2*y[:-1, :-1])
myfunc = myfunc.astype(np.float32)

# add image to viewbox1
image1 = scene.visuals.Image(noise, parent=vb1.scene, cmap='cubehelix')
# move image behind curves
image1.transform = STTransform(translate=(0, 0, 0.5))
vb1.camera.set_range()

# add image to viewbox2
image2 = scene.visuals.Image(myfunc, parent=vb2.scene, cmap='cubehelix')
# move image behind curves
image2.transform = STTransform(translate=(0, 0, 0.5))
vb2.camera.set_range()

# create some level for the isocurves
levels1 = np.linspace(noise.min(), noise.max(), num=52, endpoint=True)[1:-1]
levels2 = np.linspace(myfunc.min(), myfunc.max(), num=52, endpoint=True)[1:-1]

# create curve 1a (image overlay, black) and 1b (plain, cubehelix colored)
# to viewboxes 1 and 3
curve1a = scene.visuals.Isocurve(
    noise, levels=levels1[::4], color_lev='k', parent=vb1.scene)
curve1b = scene.visuals.Isocurve(
    noise, levels=levels1, color_lev='cubehelix', parent=vb3.scene)

# create curve 2a (2darray overlay, black) and 2b (plain, cubehelix colored)
# to viewboxes 2 and 4
curve2a = scene.visuals.Isocurve(
    myfunc, levels=levels2[::4], color_lev='k', parent=vb2.scene)
curve2b = scene.visuals.Isocurve(
    myfunc, levels=levels2, color_lev='cubehelix', parent=vb4.scene)

# set viewport
vb3.camera.set_range((-100, 200), (0, 200))
vb4.camera.set_range((0, 100), (0, 200))


# setup update parameters
up = 1
index = 1
clip = np.linspace(myfunc.min(), myfunc.max(), num=51)
cmap = cycle(get_colormaps())
color = cycle(get_color_names())


def update(ev):
    global myfunc, index, up, levels2, noise, cmap, color

    if index > 0 and index < 25:
        # update left panes rolling upwards
        noise = np.roll(noise, 1, axis=0)
        image1.set_data(noise)
        curve1a.set_data(noise)
        curve1b.set_data(noise)

        # update colors/colormap
        if (index % 5) == 0:
            curve1b.color = next(color)
            cm = next(cmap)
            image2.cmap = cm
            curve2b.color = cm

        # change isocurves by clipping data/or changing limits
        # update curve1b levels (clip)
        curve1b.levels = levels1[index:-index]

        # update curve2b data with clipped data
        im2 = np.clip(myfunc, clip[index], clip[-index])
        curve2b.set_data(im2)

        index += up
    else:
        # change index direction
        up = -up
        index += up

    canvas.update()

# setup timer
timer = app.Timer()
timer.connect(update)
# slow this down a bit to better see what happens
timer.start(0)

if __name__ == '__main__' and sys.flags.interactive == 0:
    app.run()
