Wed, 23 Dec
En cour d'écriture ...
Convertir un path Svg en java2D
Introduction
Dans le but de faire des animations à partir de fichiers SVG (en utilisant l'API Batik et donc Java2D ), j'ai chercher un moyen de pouvoir facilement convertir un path dessiner au préalable avec un outils tel Inkscape en code utilisable par mon application "Animation de SVG" (java).
Pas évident de créer directement des formes (shape) complexes en les calculant (j'ai pas de doctorat en math ...) !
Il me semblait donc bien plus facile de les dessiner avec un outil approprié, laissant plus de liberté à mon imagination.
Après avoir commencer à coder un "parseur" de chemin, je me suis rendu compte qu'il existait déjà ...
Et comme c'est bien connu : Rien ne sert de réinventer ... blablabla !".
On y avait pensé pour moi sous le bien nommé : Class AWTPathProducer
AWTPathProducer : Utilisation
Cette classe est vraiment très utile.
Elle permet de convertir un chaine de caractère définissant un "path" en un forme géométrique (
shape )utilisable par l'API batik.
C'est juste ce que je cherchais :)
Et comme Graphics2D sait très bien dessiner une forme (ex : g2.fill(Shape shape)), je n'ai "plus qu'a" (!!!) créer
une animation à partir d'une liste de chaine de caratère (String) ...
faut juste faire tous les dessins avant !
1 an ou 2 pour une animation de type Cartoon de quelques minutes ... mais non je ferai de l'anim type manga (gloup).
Ouvrer votre Inkscape ...
... à l'onglet "nouveau dossier" et ... défoulez-vous !
"je l'aime bien ce chapitre ..."
C'est fait ... Vous avez votre graphique SVG prêt ?
Nous pouvons passer aux choses sérieuses.
Comment utiliser notre SVG
Notre application va traduire un seul path à la fois.
Et il y a beaucoup de chances que votre dessin en comporte plusieurs.
Plusieurs solutions viennent à l'esprit.
Nous pouvons tenter de réunir tous les chemins en un seul , c'est à dire
avoir un seul attribut d="...".
Mais peu fiable et plutôt long à mettre en place (pas vraiment optimisé).
Nous pouvons également mettre tous les différents paths de notre graphique SVG
dans un même fichier.
Et parcourir celui-ci ligne après ligne :
tant que différent de EOF
lire path
dessiner path
pourquoi pas ... !!!
La meilleure solution serait bien entendu de lire le fichier SVG en entier et
de ne parser que les éléments path (path data).
Il faudra pour cela charger (load) notre fichier et les extraire l'un après l'autre.
Tant que différent de EOF
si element est de type path
alors dessiner element
Oui mais si nous avons mis par exemple un rectangle dans notre SVG ?
Pour l'instant nous allons l'ignorer.Mais il est à noter que Inkscape propose de convertir tout objet en chemin.
Autre petit désagrément lorsque nous créons un dessin "à main levée" avec Inkscape : il est plutôt verbose !!!
Nous allons utiliser en temps utile le génial outil de (écrit en python) Scour (je vous l'ai présenté dans une récente 'news').
Bon il est temps d'itérer comme dirant les développeurs agiles et codant notre première application qui affichera un seul path.
Notre appication (iteration1)
Elle est très simple : elle doit dessiner une forme géométrique.
Je ne vais donc pas entrer dans le détail.
public class Application extends JFrame{
private static Shape shape = null;
private boolean isSelected;
public Application(Shape s){
super("render svg path");
this.shape = s;
this.setSize(500,400);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
//this.dispose();
this.setVisible(true);
}
public void setShape(Shape shape) {
this.shape = shape;
}
public static Shape getShape() {
return shape;
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.white);
g2.draw(this.shape);
}
}
Pour tester votre code (et vous amusez avec ... mais trop longtemps hein ! ... ce serait suspect!), ajouter une porte d'entrée pour la JVM grâce la fameuse static void main.
public static void main(String[] mesArguments){
Application app = new Application(new Rectangle(100, 100, 400, 400));
}
Remarque
Prenez l'habitude de tester votre code au fur et à mesure et non pas à la fin.
C'est un des bénéfices de l'utilisation de "méthodes itératives". (notez les guillemets qui
me dédouane des commentaires de puristes 'Agiles' et autres 'Unified Process').
Que cette remarque ne nous éloigne pas de notre objectif : lire des svg path
depuis un fichier source pour l'afficher dans une application Java2D (à loisir de le
transformer à nouveau en Svg Graphics 2D ...).
L'intérêt ou non de ces applications est ici hors sujet !
Création de notre forme géométrique
Pour rappel nous avons besoin à partir d'une simple chaîne de caractères de créer une forme
géométrique utilisable par l'API 2D.
Et ainsi de passer cette 'shape' (forme) en tant qu'argument à notre constructeur d'Apllication.
Pour cela, je prends un des 'chemins' dessinés à l'aide d'inkscape que je sauvegarde en dur dans un objet String :
String monPath = ""M 242,272.10595 C 242,263.67478 243.74404,247.27669 ...etc";
Il ne reste plus qu'à transformer cette chaine en forme géométrique.
Bref on a rien à faire puique AWTPathProducer va tout faire pour nous à l'aide de
la méthode createShape.
Notez les différents arguments qu'il nous faut passer à cette méthode :
- r - The reader used to read the path specification
- wr - The winding rule to use for creating the path.
En cherchant un peu quels sont les 'readers' en java, on a justement un lecteur de chaîne à notre disposition (class StringReader ).
Le code est très simple également ...
shape = AWTPathProducer.createShape(new StringReader(pathString), GeneralPath.WIND_EVEN_ODD);
Le premier argument est notre reader; Le second est le rôle utilisé
(fill-rules properties).
Il utilise un algorithme the winding number algorithm.
