
class ChromaDepth_Mgr:
    def __init__(self, session, model):
        self.session = session
        self.model = model
        self._colormap = None
        self._handler = None
        self._last_camera_origin = [0,0,0]
        self._last_view_direction = [0,0,0]

    def start_auto_coloring(self):
        if self._handler is None:
            self._handler = self.session.triggers.add_handler('new frame',
                self._update_colors)

    def stop_auto_coloring(self):
        if self._handler is not None:
            self.session.triggers.remove_handler(self._handler)
            self._handler = None

    @property
    def colormap(self):
        if self._colormap is None:
            from chimerax.core.colors import Colormap
            self._colormap = Colormap([0,0.5,1],[[1,0,0,1],[0,1,0,1],[0,0,1,1]])
        return self._colormap

    @property
    def far_clip(self):
        v = self.session.view
        cp = v.clip_planes
        fc = cp.find_plane('far')
        if fc is not None:
            return fc
        else:
            from chimerax.core.graphics.view import ClipPlane
            c = v.camera
            cofr = v.center_of_rotation
            cpos = c.position.origin()
            clip_point = cpos + (1+self._far_clip_multiplier)* (cofr-cpos)
            fc = ClipPlane('far', c.view_direction(),
                                clip_point, camera_normal = (0,0,1))
            # Put the near clip at the camera position for depth cueing
            v.clip_planes.add_plane(fc)
            return fc

    @property
    def near_clip(self):
        v = self.session.view
        cp = v.clip_planes
        nc = cp.find_plane('near')
        if nc is not None:
            return nc
        else:
            from chimerax.core.graphics.view import ClipPlane
            c = v.camera
            cofr = v.center_of_rotation
            cpos = c.position.origin()
            clip_point = cpos + (1-self._near_clip_multiplier) * (cofr-cpos)
            nc = ClipPlane('near', c.view_direction(),
                                clip_point, camera_normal = (0,0,-1))
            # Put the near clip at the camera position for depth cueing
            v.clip_planes.add_plane(nc)
            return nc

    def _update_colors(self, *_):
        import numpy
        v = self.session.view
        camera = v.camera
        o = camera.position.origin()
        d = camera.view_direction()
        if numpy.allclose(o,self._last_camera_origin) and numpy.allclose(d, self._last_view_direction):
            return
        self._last_camera_origin = o
        self._last_view_direction = d




        v = self.session.view
        camera = v.camera
        o = camera.position.origin()
        d = camera.view_direction()

        m = self.model
        b = m.bounds()
        distances = numpy.dot(b.box_corners()-o, d)
        nd = min(distances)
        fd = max(distances)

        atoms = m.atoms
        residues = m.residues

        coords = atoms.scene_coords
        distances = numpy.dot(coords-o, d)
        normalised_distances = (distances-nd)/(fd-nd)
        atoms.colors = self.colormap.interpolated_rgba8(normalised_distances)

        residues.ribbon_colors = [r.principal_atom.color if r.principal_atom is not None else [0,0,0,0] for r in residues]
