Repository: vispy/vispy

Issue:
Z-indexing visuals in 2d plots using scene.SceneCanvas
The translation of z-coordinates to move visuals back and forth (like z-indexing) doens't seems to work with different types of visual classes? I tried with a custom visual and a scene.visuals.Image(). The only thing that mattered to me was which visuals that was first created. 


Discussion:
For custom visuals you'll need to make sure that your vertex shader is properly using the Z-coordinate of the transform. I've had issues using ImageVisuals too, but never had time to look in to what the issue was. Additionally you can control the draw order by specifying `the_vis_obj.order = integer`. You'll want to set this order before you add it to the scene though otherwise you have to call a private method `canvas._update_scenegraph(None)` which is not recommended because it is private, but I don't know of another way to do it.

@larsoner @kmuehlbauer Any other tips for specifying z-order?
@mkkb @davidh-ssec A minimal example showing the behaviour is always best for getting fast into the problem. 

I can make a simplified example showing the behavior early next week
Here is an example that does not let me edit the "z-index". I first make a red line visual, then an image and at last I make a blue line. On_key_press I added that one can change the z-transform of the red line, preferably moving it to the front/back of the canvas.
```
import sys
from vispy import scene
from vispy import app
from vispy.io import load_data_file, read_png

import numpy as np
from vispy.visuals.transforms import STTransform

canvas = scene.SceneCanvas(keys='interactive')
canvas.size = 800, 600
canvas.show()

# Set up a viewbox to display the image with interactive pan/zoom
view = canvas.central_widget.add_view()

# Create the image
img_data = read_png(load_data_file('mona_lisa/mona_lisa_sm.png'))
interpolation = 'nearest'

# Add other visuals to main_view
pos = np.zeros((2, 2)).astype(np.float32)
pos[:, 0] = np.array([-10, 600]).astype(np.float32)
pos[:, 1] = np.array([-10, 600]).astype(np.float32)
line_pre = scene.visuals.Line(pos=pos, parent=view.scene, color='red', width=40, method='agg')
line_pre_z_ind = 0
line_pre.transform = STTransform(translate=[0, 0, line_pre_z_ind])

image = scene.visuals.Image(img_data, interpolation=interpolation, parent=view.scene, method='subdivide')
image.transform = STTransform(translate=[0, 0, 0])

canvas.title = 'Spatial Filtering using %s Filter' % interpolation

# Set 2D camera (the camera will scale to the contents in the scene)
view.camera = scene.PanZoomCamera(aspect=1)
# flip y-axis to have correct aligment
view.camera.flip = (0, 1, 0)
view.camera.set_range()
view.camera.zoom(0.1, (250, 200))

# get interpolation functions from Image
names = image.interpolation_functions
names = list(names)
names.sort()
act = 17

# Add other visuals to main_view
pos = np.zeros((2, 2)).astype(np.float32)
pos[:, 0] = np.array([-10, 600]).astype(np.float32)
pos[:, 1] = np.array([-9, 601]).astype(np.float32)
line_post = scene.visuals.Line(pos=pos, parent=view.scene, color='blue', width=40, method='agg')
line_post.transform = STTransform(translate=[0, 0, 0])

view.camera.set_range(x=(-10,10), y=(-10,10))

# Implement key presses
@canvas.events.key_press.connect
def on_key_press(event):
    global act
    if event.key in ['Left', 'Right']:
        if event.key == 'Right':
            step = 1
            line_pre.transform = STTransform(translate=[0, -5, line_pre_z_ind])
        else:
            step = -1
            line_pre.transform = STTransform(translate=[0, 5, line_pre_z_ind])
        act = (act + step) % len(names)
        interpolation = names[act]
        image.interpolation = interpolation
        canvas.title = 'Spatial Filtering using %s Filter' % interpolation
        canvas.update()
    if event.key in ['Down', 'Up']:
        if event.key == 'Down':
            line_pre_z_ind = -10
            print( event.key, 'New z-indx for red line', line_pre_z_ind)
            line_pre.transform = STTransform(translate=[0, 0, line_pre_z_ind])
        if event.key == 'Up':
            line_pre_z_ind = 10
            print( event.key, 'New z-indx for red line', line_pre_z_ind)
            line_pre.transform = STTransform(translate=[0, 0, line_pre_z_ind])
        act = (act) % len(names)
        interpolation = names[act]
        canvas.title = 'Spatial Filtering using {:} Filter, z_ind={:d}'.format(interpolation, line_pre_z_ind)
        canvas.update()


if __name__ == '__main__' and sys.flags.interactive == 0:
    app.run()
```
@mkkb Might not be the issue here, but canvas.update() should be called last in the callback. 
@mkkb You would need to add canvas.update() after you changed the transform in the keypressed event. 
I did the following updates but it doesn't help. Did it work for you? (I edited the code above)
For fun I also added a translation moving the lines in y-direction with left/right key, that works but not the z-indexing.
@mkkb Sorry, can't test atm. I'll have a look as soon as possible. 
@mkkb AFAICT this seems to be a bug in the `LineVisual` `agg`-method. When I change the method to `gl` then everything works as expected (red line will be switched in z-index above and below the image).
For the time being you can use 'gl'-method. If you can dig something up concerning the `agg`-method, please let us know.
This will be much more important in the future since I'd like to consider changing the default text method to `agg` (version 0.7?). Good to know.
Thx Kai. I'll see if I can dig up something when I get the time.
Just wanted to post a small clarification and explanation of what is wrong with the agg method: It seems the entire agg.vert shader is written with vec2's. It uses the transform you give it, but immediately drops any Z coordinate by doing `.xy` everywhere it is used (multiple spots). Handling a Z dimension would likely require a completely different algorithm to be implemented. The results if someone did a transform that resulted in points not all being on the same Z plane would likely be undesirable in one way or another (ex. x/y differences show a small distance, but visually the line spans a long distance in the Z dimension).
Most of the algorithm is indeed 2d, but the z component can/should be stored and used when setting `gl_Position` at the bottom. I am looking into this now.
It also does not help that the agg line has the depth test disabled ...

Resolving patch:
diff --git a/vispy/visuals/line/line.py b/vispy/visuals/line/line.py
--- a/vispy/visuals/line/line.py
+++ b/vispy/visuals/line/line.py
@@ -418,6 +418,8 @@ def __init__(self, parent):
         Visual.__init__(self, vcode=self.VERTEX_SHADER,
                         fcode=self.FRAGMENT_SHADER)
         self._index_buffer = gloo.IndexBuffer()
+        # The depth_test being disabled prevents z-ordering, but if
+        # we turn it on the blending of the aa edges produces artifacts.
         self.set_gl_state('translucent', depth_test=False)
         self._draw_mode = 'triangles'
 
@@ -425,7 +427,6 @@ def _prepare_transforms(self, view):
         data_doc = view.get_transform('visual', 'document')
         doc_px = view.get_transform('document', 'framebuffer')
         px_ndc = view.get_transform('framebuffer', 'render')
-
         vert = view.view_program.vert
         vert['transform'] = data_doc
         vert['doc_px_transform'] = doc_px


Test patch: