À la conquête du “Undo”

Une application de dessin sans le “undo” (annuler ou retour arrière ? en fait je sais pas trop, je n’utilise jamais mon ordi en français) c’est très handicapant, d’autant que je conçois Paintual comme une plateforme d’essai, ce qui implique forcément faire et défaire.

J’ai révisé tout le code des grands cycles de l’application:
– workflow (qui contenait VIOME [le gestionnaire direct des actions de l’utilisateur] qui a disparu depuis) qui détermine les actions de l’application selon les séquences d’actions de l’utilisateur (clics de souris, déplacements, sélection d’outils, etc.);
– le DrawingBoard (le plan de travail) en deux parties : la surface de dessin (PaintualCanvas) comme telle et son enveloppe (le DrawingBoard) avec les barres de défilement et le contrôle de zoom;
– et la VisualPropertyPage, le panneau d’édition des propriétés des outils de dessin, généré dynamiquement selon l’exposition des paramètres/propriétés des effets et outils au moyen d’attributs et de la réflexion.

Trois cycles qui s’imbriquent et qui me donnent souvent des maux de tête. C’est un peu bête que ça soit si compliqué puisque ce sont trois concepts que j’ai créés et développés progressivement au fur et à mesure que je voulais ajouter des fonctionnalités à l’application. Petit ménage, donc, qui a fait du bien et qui m’a réconforté quant à leur relative complexité/simplicité selon le point de vue.

Alors ça devenait facile d’ajouter le si nécessaire Undo : suffit d’une pile d’historique de travail qui enregistre les différentes actions de l’utilisateur. Pour simplifier, une action consiste soit en un tracé complet avec la souris ou le stylet (cliquer, glisser, relâcher l’outil) ou l’application d’un effet. Cliquer sur Undo reviendrait simplement à afficher l’image d’avant modification et d’enlever une action de la pile d’historique de travail.

Au travail : construction de la pile de type LIFO (Last In, First Out, c’est-à-dire que la série des entrées est séquentielle et chronologique [du plus ancien au plus récent] mais que le retrait ne peut se faire que du plus récent vers le plus ancien)… ça existe déjà tout fait dans le .NET Framework, tant mieux. Créer une classe d’item d’historique qui contient l’image entière avant modification et le type d’outil utilisé pour la modification.

Simple. Ça ne marche pas.

À la première action correspond une première entrée dans la pile, c’est assez évident. Si on clique sur Undo, mais j’avais fait le code de sorte qu’en enlevant un élément de la pile je réaffichais l’image contenue dans l’élément précédent de la pile. Hors, il n’y a pas d’élément précédent. Donc, ajout d’un élément d’historique de départ, un canevas vide.

Erreur de code où je créais deux éléments d’historique : un pour le tracé et un autre pour la sélection d’un outil ou d’un effet. Perdu une heure là-dessus.

Autre erreur : l’accumulation indéfinie de copies d’images “clichés en cours de travail” gonfle dans la mémoire de l’ordi à un rythme stupéfiant (en mémoire les images sont décompressées; le moindre petit jpg est en fait un gros monstre). Bon, il faut pour l’instant limiter la pile à, disons, 10 degrés de retour. Mais ça signifie que je dois retirer le plus vieil élément mais la pile n’est pas conçue comme ça. Sans compter que le premier élément doit rester puisque c’est l’état d’origine du dessin avant toute modification.

Moins simple qu’il n’y paraît mais c’est à peu près fonctionnel.

J’ai à repenser les détails. La beauté de la chose, cependant, c’est l’intégration incroyablement facile de l’historique dans le Workflow comme si, dès le départ, j’avais prévu le coup, ce qui n’est pas du tout le cas !

À mettre en place aussi c’est la compression des images de chaque étape. Deux choses à appliquer:
– déterminer les zones qui ont changé et n’enregistrer que des portions d’images. Déjà là il y a une réduction considérable de la taille mais la détection prend du temps, donc il faut transférer le travail dans une queue de thread pour ne pas bloquer l’interface, ce qui veut dire aussi savoir détecter la fin du travail dans le thread pour savoir quand on peut retirer un élément de l’historique après qu’il ait été effectivement enregistré;
– sauvegarder les images d’étapes dans des fichiers temporaires sur disque afin de libérer la précieuse mémoire de l’ordi (avec 32 GB de RAM j’ai la paix pour un bout avec de petites images, mais avec les formats de 6000 x 6000 pixels qui me permettent une finesse de détails intéressante, la mémoire se sature rapidement).

Donc je focus sur cette fonctionnalité jusqu’à satisfaction.

Entre temps, je vous laisse avec GLIC , qui roule sous Processing, et dont je veux incorporer certaines fonctionnalités dans Paintual avec, éventuellement si possible, de pouvoir “GLICquer” certaines portions d’une image avec des outils de dessin, soit du glitch-painting. Pour avoir parcouru le code de GLIC, ça sera un assez long travail.

Paintual 0.0.13.0

L’effet “Flow” est un peu plus rapide, juste un peu. J’ai commencé à pousser l’exécution du code vers le GPU (accélération matérielle). Pour l’instant et encore pour un bon bout de temps, tout le code de “Flow” ne peut être facilement accéléré parce toutes les opérations ne sont pas parallélisables, c’est-à-dire exécutées en même temps, puisqu’il y a de nombreuses dépendances sur les résultats des opérations : telle couleur doit être calculée avant de procéder au calcul du vecteur de déplacement, etc. J’ai à repenser mon algorithme. Cependant, dans l’état actuel de la version 0.0.13.0 c’est quand même un peu plus rapide que la version précédente.

Plusieurs, peut-être trop ? d’options s’offrent à nous lorsque vient le temps de travailler avec le GPU ; chacune a ses qualités, ses limitations. J’ai opté pour une option “morte” de Microsoft : AMP (Accelerated Massive Parallelism) ; morte parce qu’elle ne semble plus être en développement actif. Mais pour ce dont j’ai besoin en ce moment c’est amplement suffisant (effectuer des centaines de calculs simples en parallèle). Il fallait cependant communiquer avec C++ (mon projet est en C#). Pas simple, mais pas si mal.

Ensuite, il a fallu corriger un fichier de Microsoft (amp.h) selon les instructions ici pour que ça fonctionne avec Visual Studio 2017.

Il existe quantité de pages web dédiées à C++ AMP, sans compter les vidéos : une simple recherche Google vous mettra sur la voie.

Pourquoi ai-je choisi cette option plutôt qu’une autre ? Simplement parce qu’elle était déjà disponible sur mon ordi, que j’avais en main un bout de code qui indiquait la marche à suivre avec AMP et que ça correspondait exactement à mon besoin immédiat. Finalement, AMP est censé rouler sur toutes les cartes graphiques (à tout le moins ça fonctionne avec NVIDIA Quadro K2200 et AMD Radeon Vega 8). Pour l’instant c’est aussi la simplicité (relative) d’utilisation qui prime bien davantage que la pleine prise en charge de la puissance de la carte graphique.

Plus tard, évidemment, je me tournerai vers une option plus standard mais je n’ai pas encore fixé mon choix. CUDA est exclu parce que cela restreint mon application à ne rouler que sur un ordi qui a une carte NVIDIA, alors que mon laptop et mon Microsoft Surface sont équipés différemment. Je prévois créer un contrôleur “touch base” sur Surface pour changer les paramètres de l’application en cours d’utilisation (couleurs, traits, effets, etc.) et un ou + ordis pour permettre à plus d’une personne de pouvoir travailler sur la même image en même temps. C’est encore très loin mais c’est dans les plans.

En attendant, on a la version 0.0.13.0 code source et la version installable sous Windows.

geographie nouvelle

Paintual 0.0.12.0

Flous et fluides, deux nouveau effets. “Flow” est loin d’être aussi fluide que ce que je voulais obtenir mais ça reste une belle réussite. Le code inclus dans Paintual n’a pour l’instant que peu à voir avec l’exemple que j’avais trouvé, non que l’exemple n’était pas bon mais je n’arrivais pas à cerner les échelles de valeurs entre force, vélocité et différence de luminance. J’ai donc reconstruit partiellement le code à partir de zéro et ai pris une tangente différente. Pour l’instant le code est encore très lent malgré le multithreading en place. Il est temps de passer à l’accélération via GPU, une toute autre aventure.

Le code et l’installable Windows.

Ci-dessous, petite galerie.

fuild dynamics 04fuild dynamics 05a test 2a testbug

* * *

Blur and Flow, two new effects. Flow is far from being as fluid as what I wanted but it’s still convincing. The code included in Paintual has now very little to do with the example I had found, not that the example was bad but I could not identify the scales of values between force velocity and luminance difference. So I built the code from scratch and took a different tangent. For now the code is still very slow despite the multithreading in place. It’s time to move to GPU acceleration, another adventure.

Above, a sample gallery.

Source code and Windows Installer.

Dynamique des fluides

Presque intéressant. Vidéo accélérée parce que, pour l’instant, le code est lent. À remarquer qu’on aperçoit un effet de grille ; plus la grille est fine, plus l’effet semble naturel même si pour l’instant la fluidité est linéaire.

* * *

Almost interesting. This video shows the evolution at 3x speed because the code is not optimized. There is a grid effect. The finer the grid, the more natural the overall effect looks even if for now fluid direction is just linear.

Paintual 0.0.11.0

Color Variance

Quoique mon contrôle de sélection de couleur soit incomplet (et il ne répond pas bien au stylet de la tablette graphique), il fonctionne suffisamment bien pour permettre d’ajouter de la couleur dans la vie (de l’application 🙂  ).

2018-08-17_22-59-03

Ainsi, qui veut bien essayer l’application pourra faire des petits dessins en couleurs comme je le montrais dans mon post précédent.

Le code version 0.0.11.0 sur GitHub et l’installable Windows.

* * *

Color Variance

Even though my color picker control is incomplete (and it does not behave that well with the stylus of the graphic tablet), it works well enough to add color to life (within the app 🙂 ). This feature is now available to anyone who wants to experiment with colors like I posted before.

Version 0.0.11.0 on GitHub and Windows installable.