{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Texture drawing\n\nDrawing ROI on a mesh in a texture. Of course the SurfPaint module allows to do  it with much richer features, but this example demonstrates how to code the basics of it in a relatively small program.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from __future__ import print_function\n\nfrom __future__ import absolute_import\nimport anatomist.direct.api as anatomist\nfrom soma import aims\nfrom soma.aims import colormaphints\nimport sys\nimport os\nimport math\nimport six\n\nsys.path.insert(0, '.')\n\nuserLevel = 4\n# determine wheter we are using Qt4 or Qt5, and hack a little bit accordingly\n# the boolean qt4 gloabl variable will tell it for later usage\nfrom soma.qt_gui.qt_backend import QtCore, QtGui\nfrom soma.qt_gui.qt_backend import loadUi\n\n# do we have to run QApplication ?\nif QtGui.QApplication.instance() is None:\n    runqt = True\nelse:\n    runqt = False\n\nimport sphere\nimport numpy\n\nselmesh = None\nselanamesh = None\n\n\nclass TexDrawAction(anatomist.cpp.Action):\n\n    def name(self):\n        return 'TexDrawAction'\n\n    def takePolygon(self, x, y, globx, globy):\n        # print('takePolygon', x, y)\n        w = self.view().aWindow()\n        obj = w.objectAtCursorPosition(x, y)\n\n        if obj is not None:\n            print('object:', obj, obj.name())\n            surf = None\n            if obj.objectTypeName(obj.type()) == 'SURFACE':\n                surf = obj\n            elif isinstance(obj, anatomist.cpp.MObject):\n                surf = [o for o in obj if o.type() == 3]\n                if len(surf) != 1:\n                    return\n                surf = surf[0]\n            if surf is not None:\n                poly = w.polygonAtCursorPosition(x, y, obj)\n                if poly == 0xffffff:  # white\n                    return  # background\n                print('polygon:', poly)\n                mesh = anatomist.cpp.AObjectConverter.aims(surf)\n                # print('mesh:', mesh)\n                ppoly = mesh.polygon()[poly]\n                vert = mesh.vertex()\n                global selmesh, selanamesh\n                if selmesh is None:\n                    selmesh = aims.AimsSurfaceTriangle()\n                selmesh.vertex().assign([vert[ppoly[0]], vert[ppoly[1]],\n                                         vert[ppoly[2]]])\n                selmesh.polygon().assign([aims.AimsVector_U32_3(0, 1, 2)])\n                if selanamesh is None:\n                    selanamesh = anatomist.cpp.AObjectConverter.anatomist(\n                        selmesh)\n                    a = anatomist.Anatomist()\n                    a.execute('SetMaterial', objects=[\n                              selanamesh], diffuse=[0, 0, 1., 1.])\n                    a.execute('AddObject', objects=[selanamesh], windows=[w])\n                selanamesh.setChanged()\n                selanamesh.notifyObservers()\n\n    def delPolygon(self):\n        global selmesh, selanamesh\n        selmesh = None\n        # keep object ID and release python reference to it\n        id = a.convertSingleObjectParamsToIDs(selanamesh)\n        selanamesh = None\n        a.execute('DeleteElement', elements=[id])\n\n    def newtexture(self, x, y, globx, globy):\n        print('new texture')\n        w = self.view().aWindow()\n        aw = a.AWindow(a, w)\n        obj = w.objectAtCursorPosition(x, y)\n        # print('object:', obj)\n        if obj is not None:\n            if obj.objectTypeName(obj.type()) == 'SURFACE':\n                surf = obj\n                texs = []\n            # elif obj.objectTypeName( obj.type() ) == 'TEXTURED SURF.':\n                # print('TEXTURED SURF.')\n                # surf = [ o for o in obj if o.type() == 3 ]\n                # if len( surf ) != 1:\n                # print('not one mesh, but', len( surf ))\n                # return\n                # surf = surf[0]\n                # texs = [ o for o in obj if o.type() == 18 ]\n                # self._texsurf = obj\n                # print('draw initiated')\n                # return\n            else:\n                return\n            gl = surf.glAPI()\n            if gl:\n                vs = anatomist.cpp.ViewState()\n                nv = gl.glNumVertex(vs)\n                if nv > 0:\n                    tex = aims.TimeTexture_FLOAT()\n                    t = tex[0]\n                    t.reserve(nv)\n                    for i in six.moves.xrange(nv):\n                        t.push_back(0.)\n                    atex = a.toAObject(tex)\n                    atex.releaseAppRef()\n                    # atex = a.AObject( a, surf ).generateTexture()\n                    texs.append(atex)\n                    # ...\n                    tsurf = a.fusionObjects(\n                        [atex, obj], method='FusionTexSurfMethod')\n                    tsurf.setPalette(palette='Blue-Red-fusion')\n                    self._texsurf = tsurf\n                    # tsurf.takeRef()\n                    aw.removeObjects(obj)\n                    aw.addObjects(tsurf)\n\n    def startDraw(self, x, y, globx, globy):\n        self._startDraw(x, y, 1.)\n\n    def startErase(self, x, y, globx, globy):\n        self._startDraw(x, y, 0.)\n\n    def _startDraw(self, x, y, value):\n        w = self.view().aWindow()\n        obj = w.objectAtCursorPosition(x, y)\n        # print('object:', obj)\n        if obj is not None:\n            texs = []\n            if obj.objectTypeName(obj.type()) == 'SURFACE':\n                surf = obj\n                p = obj.parents()\n                found = False\n                for o in p:\n                    if o.objectTypeName(o.type()) == 'TEXTURED SURF.' \\\n                            and w.hasObject(o):\n                        texs = [ob for ob in o if ob.type() == 18]\n                        self._texsurf = o\n                        found = True\n                        break\n                if not found:\n                    # create a new texture\n                    self.newtexture(x, y, 0, 0)\n                    self._startDraw(x, y, value)\n                    return\n            elif obj.objectTypeName(obj.type()) == 'TEXTURED SURF.':\n                surf = [o for o in obj if o.type() == 3]\n                if len(surf) != 1:\n                    return\n                surf = surf[0]\n                texs = [o for o in obj if o.type() == 18]\n                self._texsurf = obj\n            else:\n                return\n            if len(texs) == 0:\n                return\n            self._surf = surf\n            self._tex = texs[-1]\n            self._mesh = anatomist.cpp.AObjectConverter.aims(surf)\n            self._aimstex = anatomist.cpp.AObjectConverter.aims(\n                self._tex, {'scale': 0})\n            self.draw(x, y, value)\n\n    def endDraw(self, x, y, globx, globy):\n        if hasattr(self, '_aimstex'):\n            del self._aimstex\n            del self._mesh\n            del self._tex\n            del self._surf\n\n    def moveDraw(self, x, y, globx, globy):\n        self.draw(x, y, 1.)\n\n    def erase(self, x, y, globx, globy):\n        self.draw(x, y, 0.)\n\n    def draw(self, x, y, value):\n        if not hasattr(self, '_aimstex'):\n            return\n        w = self.view().aWindow()\n        obj = self._texsurf\n        poly = w.polygonAtCursorPosition(x, y, obj)\n        if poly == 0xffffff or poly < 0 or poly >= len(self._mesh.polygon()):\n            return\n        ppoly = self._mesh.polygon()[poly]\n        print('poly:', poly, ppoly)\n        vert = self._mesh.vertex()\n        pos = aims.Point3df()\n        pos = w.positionFromCursor(x, y)\n        print('pos:', pos)\n        v = ppoly[numpy.argmin([(vert[p] - pos).norm() for p in ppoly])]\n        print('vertex:', v, vert[v])\n        self._aimstex[0][v] = value\n        self._tex.setChanged()\n        self._tex.notifyObservers()\n\n\nclass TexDrawControl(anatomist.cpp.Control):\n\n    def __init__(self, prio=25):\n        anatomist.cpp.Control.__init__(self, prio, 'TexDrawControl')\n\n    def eventAutoSubscription(self, pool):\n        key = QtCore.Qt\n        NoModifier = key.NoModifier\n        ShiftModifier = key.ShiftModifier\n        ControlModifier = key.ControlModifier\n        AltModifier = key.AltModifier\n        self.mouseLongEventSubscribe(\n            key.LeftButton, NoModifier,\n            pool.action('TexDrawAction').startDraw,\n            pool.action('TexDrawAction').moveDraw,\n            pool.action('TexDrawAction').endDraw,\n            False)\n        self.mouseLongEventSubscribe(\n            key.LeftButton, NoModifier,\n            pool.action('TexDrawAction').startDraw,\n            pool.action('TexDrawAction').moveDraw,\n            pool.action('TexDrawAction').endDraw,\n            False)\n        self.mouseLongEventSubscribe(\n            key.LeftButton, ControlModifier,\n            pool.action('TexDrawAction').startErase,\n            pool.action('TexDrawAction').erase,\n            pool.action('TexDrawAction').endDraw,\n            False)\n        self.mousePressButtonEventSubscribe(\n            key.RightButton, ControlModifier,\n            pool.action('TexDrawAction').newtexture)\n        # polygon picking\n        self.mousePressButtonEventSubscribe(key.RightButton, NoModifier,\n                                            pool.action('TexDrawAction').takePolygon)\n        self.keyPressEventSubscribe(key.Key_Escape, NoModifier,\n                                    pool.action(\"TexDrawAction\").delPolygon)\n        # now plug the standard actions\n        self.mouseLongEventSubscribe(key.MidButton, ShiftModifier,\n                                     pool.action(\"Zoom3DAction\").beginZoom,\n                                     pool.action(\"Zoom3DAction\").moveZoom,\n                                     pool.action(\"Zoom3DAction\").endZoom, True)\n        self.wheelEventSubscribe(pool.action(\"Zoom3DAction\").zoomWheel)\n        self.keyPressEventSubscribe(key.Key_C, ControlModifier,\n                                    pool.action(\"Trackball\").setCenter)\n        self.keyPressEventSubscribe(key.Key_C, AltModifier,\n                                    pool.action(\"Trackball\").showRotationCenter)\n        self.mouseLongEventSubscribe(key.MidButton, ControlModifier,\n                                     pool.action(\n                                         \"Translate3DAction\").beginTranslate,\n                                     pool.action(\n                                         \"Translate3DAction\").moveTranslate,\n                                     pool.action(\"Translate3DAction\").endTranslate, True)\n        self.mouseLongEventSubscribe(\n            key.MidButton, NoModifier,\n            pool.action('ContinuousTrackball').beginTrackball,\n            pool.action('ContinuousTrackball').moveTrackball,\n            pool.action('ContinuousTrackball').endTrackball, True)\n        self.keyPressEventSubscribe(key.Key_Space, ControlModifier,\n                                    pool.action(\"ContinuousTrackball\").startOrStop)\n\n\n# a = anatomist.Anatomist()\n# pix = QtGui.QPixmap( 'control.xpm' )\n# anatomist.cpp.IconDictionary.instance().addIcon( 'MyControl',\n    # pix )\n# ad = anatomist.cpp.ActionDictionary.instance()\n# ad.addAction( 'MyAction', lambda: MyAction() )\n# cd = anatomist.cpp.ControlDictionary.instance()\n# cd.addControl( 'MyControl', lambda: MyControl(), 25 )\n# cm = anatomist.cpp.ControlManager.instance()\n# cm.addControl( 'QAGLWidget3D', '', 'MyControl' )\n\n# s = sphere.ASphere()\n# a.registerObject( s )\n# aw = a.createWindow( '3D' )\n# a.addObjects( [ s ], [ aw ] )\n\na = anatomist.Anatomist()\nqapp = QtGui.QApplication.instance()\n\npix = QtGui.QPixmap('control.xpm')\nanatomist.cpp.IconDictionary.instance().addIcon('TexDrawControl',\n                                                pix)\nad = anatomist.cpp.ActionDictionary.instance()\nad.addAction('TexDrawAction', TexDrawAction)\ncd = anatomist.cpp.ControlDictionary.instance()\ncd.addControl('TexDrawControl', TexDrawControl, 26)\ncm = anatomist.cpp.ControlManager.instance()\ncm.addControl('QAGLWidget3D', '', 'TexDrawControl')\n\ns = a.loadObject('test.mesh')\naw = a.createWindow('3D')\na.addObjects([s], [aw])\na.execute('SetControl', windows=[aw], control='TexDrawControl')\n\nQtGui.QMessageBox.information(\n    None, 'texture drawing', '1. put a mesh in a 3D view\\n2.select the \"Mickey\" control\\n3. ctrl+right click on the mesh to create an empty texture or initiate the drawing session\\n4. draw on the mesh using the mouse left button\\n   ctrl+left button erases', QtGui.QMessageBox.Ok)\n\n# run Qt\nif runqt:\n    qapp.exec_()\n    del ad, cm, aw, s, pix"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.11.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}