Petits calculs électoraux

En cette période pré-post-électorale, intéressons-nous à la question suivante : “Mais au fait, ça marche comment les élections ?

Tout d’abord, commençons par nous demander comment le nombre de sièges obtenu par chacun des partis est déterminé.
Pour cela, supposons que 10 sièges soient attribuables et que 3 partis se présentent aux élections avec les candidats suivants

Parti A Parti B Parti C
Nicolas de Condorcet Kenneth Arrow C. W. Seaton
Robert Dupond John Doe John Nash
Augustin-Louis Cauchy Marge Simpson Godfrey Hardy
Bernhard Riemann Eljjdx Henri Poincaré
Cédric Villani Grigori Perelman Pierre Deligne

Supposons également avoir le résultat suivant :

Parti A Parti B Parti C
46.600 18.300 35.100

Pour déterminer quel parti aura combien de sièges, nous allons diviser le nombre de voix de chaque liste par 1 puis 2, puis 3 et ainsi de suite jusqu’au nombre de sièges à pourvoir. Une fois ceci fait, nous prenons les m plus grand chiffres où m est également le nombre de sièges à pourvoir.
Dans notre example, m=10 et nous avons donc

Parti A Parti B Parti C
1 46.600 18.300 35.100
2 23.300 9.150 17.550
3 15.533 6.100 11.700
4 11.650 4.575 8.775
5 9.320 3.660 7.020
6 7.767 3.050 5.850

où 9.150 est le dixième plus grand chiffre de notre tableau (nous n’avons pas prolongé le calcul jusqu’à la dixième ligne car chaque valeur aurait été inférieure à 9.150).

Une fois ceci fait, nous considérons la valeur 9.150 comme étant le diviseur électoral. Ce diviseur va diviser le score de chaque liste et le quotient obtenu sera le nombre de siège.

Dans notre cas, nous obtenons

Parti Calcul Nombre de sièges
Parti A 46.600 = 9.150*5 + 850 5
Parti B 18.300 = 9.150*2 + 0 2
Parti C 35.100 = 9.150*3 + 7560 3

Cette façon de procéder s’appelle la méthode d’Hondt.

Et si on avait deux partis avec 50% chacun et un nombre impair de sièges ?
Dans ce cas, on regarde tout d’abord le nombre de voix de la liste. La liste ayant ne fusse qu’une voix en plus gagne le siège.
Si le nombre de voix est égale, on regarde qui a reçu le plus de voix de préférence parmi les 2 listes.

Mais ! C’est tout ? Les votes de préférences ne servent qu’à ça ?
Et bien non ! Les votes de préférences vont permettre de déterminer qui aura quel siège.
Dans notre exemple, le parti B a deux sièges.
Déterminons donc qui seront les élus.

Tout d’abord, reprenons l’ensemble des voix pour le parti B : 18.300.
Nous déterminons un seuil électoral pour la liste B en divisant ce nombre par le nombre de sièges + 1. Dans notre exemple, il est égal à 18.300 / (2+1) = 6.100.

De manière concrète, si un candidat possède strictement plus de voix de préférence que ce seuil, il décroche ce siège.

S’il reste des sièges a distribuer, les candidats, dans l’ordre de la liste, vont siphonner un pot commun (qui est constitué, en partie, de la moitié des votes pour la case de tête (ce qui est différent de l’ensemble des voix de la liste)) (1).

Prenons un exemple pour que cela soit plus clair.

Candidats Voix
Case de tête 2.500
Kenneth Arrow 3.800
John Doe 0
Marge Simpson 6.350
Eljjdx 4.000
Grigori Perelman 5.000

Le score de Marge Simpson étant supérieur au seuil, elle est élue malgré qu’elle ne soit que troisième sur la liste. Les préférences des électeurs ont donc été prioritaires sur l’ordre de présentation (qui est similaire à la préférence du parti).
Il reste donc un siège à pourvoir entre les quatre candidats restants :

Candidats Voix
Case de tête 2.500
Kenneth Arrow 3.800
John Doe 0
Eljjdx 4.000
Grigori Perelman 5.000

Pour ce faire, nous allons prendre les voix du pot commun et les donner au premier candidat de la liste jusqu’à ce qu’il ai atteint le seuil. S’il reste des voix à distribuer, elles vont au deuxième jusqu’au seuil et ainsi de suite jusqu’à ne plus avoir de voix à redistribuer.

C’est pour cela que le vote en case de tête est souvent décrit comme étant l’expression que l’électeur est d’accord avec l’ordre de présentation des candidats tel que donné par le parti car son vote va, si besoin est, avantager les candidats selon leur ordre de présentation.

Dans notre cas, nous avons cependant un problème. La tête de liste est si impopulaire que l’entièreté du pot ne lui suffit pas pour passer le seuil.
Nous nous retrouvons donc dans le cas où il reste un siège à pourvoir mais où plus aucun candidat ne dépasse le seuil.

Candidats Voix
Case de tête 2.500/2 = 1.250
Kenneth Arrow 3.800 +1.250 = 5.050
John Doe 0
Eljjdx 4.000
Grigori Perelman 5.000

Dans ce cas, nous prenons le candidat ayant obtenu le plus de voix (après répartition des voix du pot commun) et, ici, contre toute attente, c’est bien Mr Arrow qui est élu malgré que Mrs Eljjdx et Perelman soient plus populaires. (2)

Okay compris mais dis moi, dis moi comment on fait pour l’apparentement ?
C’est pas compliqué, j’vais tout expliquer … un jour 😉

(1) De manière exacte, tout les bulletins valables sont séparés en quatre catégories :

  • Bulletins marqués exclusivement en tête de liste.
  • Bulletins marqués exclusivement en faveur d’un ou de plusieurs candidats titulaires.
  • Bulletins marqués, à la fois, en faveur d’un ou de plusieurs candidats titulaires et d’un ou de plusieurs candidats suppléants.
  • Bulletins marqués exclusivement en faveur d’un ou de plusieurs candidats suppléants.

Et le pot commun est constitué de la moitié des voix de la première ET de la quatrième catégorie.
Donc, en ne votant que pour des suppléants, vous marquez implicitement votre accord pour l’ordre de présentation de la liste des candidats titulaires !

(2) C’est pour limiter ce genre de situation, et donc donner un plus grand poids aux bulletins nominatifs que le pot commun n’est constitué que de la moitié des voix.

source : http://www.verkiezingen.fgov.be/index.php?id=2111
Merci à mon ami Bertrand pour les éclaircissements.

Posted in Mathématiques, Société | Tagged | Leave a comment

Retour sur l’OCJP 6

Bonjour,
Commençons par le point positif :

>>>>****J’ai décroché la certif Java 6 !!! ****<<<<


OCJP6
😀

Vous l’aurez compris, je suis assez content de cela (mon score étant de 90%) surtout que, comme dit dans les billets précédents, les simulations d’examen faites les jours avant ne m’étaient pas très favorables.

Sur l’examen en lui-même, je l’ai passé au Luxembourg.
Trois centres d’examens sont à ce jour disponible :

  • Devoteam au Windhof
  • La chambre des salariés à Luxembourg-Ville
  • Telindus à Esch-sur-Alzette

Étant donné que chaque centre a ses propres jours de disponibilités, mon choix fut porté sur celui dont la date m’arrangeait; celui du Windhof.

Avant l’examen

Un point important est qu’il est parfois nécessaire de présenter deux pièces d’identité. Soyez donc prévoyant, histoire de ne pas commander un passeport en dernière minute juste pour passer une certification.

Une fois sur place, bien que j’avais réservé un “slot” particulier, la réceptionniste m’annonça que je pouvais tout aussi bien, si l’envie m’en prenait, attendre encore une heure avant de me lancer pour 2h30 d’examen. Sympa. 🙂

Règles

Cependant, on ne peut strictement rien emporter avec soi dans la salle d’examen (enfin … sauf vos vêtements). Il faut donc remettre son porte-feuille à la réceptionniste.

Heureusement, le centre est sensé prêter de quoi écrire afin de pouvoir coucher sur papier nos pensées et suivre le comportement de certains programmes de manière plus aisé. Pour le centre du Windhof, j’eus droit à une ardoise plastifiée avec son marqueur et j’ai eu plus de place que nécessaire.

Temps

Actuellement, le temps de l’examen est de 2h30 pour 60 questions.
Cependant, cela est largement suffisant. J’ai fini mes 60 questions en 1h20 et j’ai mis à profit l’heure restante pour bien relire les énoncés, détecter les chausse-trapes et reréfléchir sur les points me faisant hésiter.

Les questions

N’ayant pas la mémoire eidétique, je suis bien incapable de vous donner mes questions. :p
Je me souviens juste des éléments suivants :

  • Trois questions sur la compilation et l’exécution en ligne de commande. Les questions que je comptais rater et que j’ai certainement raté. ^^
  • Comme prévu dans les objectifs, aucune question sur la serialization. Même en le sachant à l’avance, c’est un peu triste. Sans doute est-ce trop facile.
  • Je n’ai eu aucune question avec du multi-threading. Là, c’est carrément dommage que les seules questions évaluant ma connaissance des objectifs sur les Thread se limite à chaque fois à un code avec un seul Thread ! Je ne vois pas l’utilité…
  • Dans le même registre, je n’ai eu aucune question non plus avec des wait/notify/notifyAll. Redommage.
  • J’ai également trouvé que les questions portant sur la généricité ne faisaient que survoler la matière et que cela n’aurait pas fait de mal d’avoir au moins une question avec la wildcard.

Bref, sans dire que tout est ultra-facile (si c’était le cas, j’aurais eu un meilleur score ^^), je pensais que cela serait beaucoup plus difficile.

Conclusion

Cependant, je pense que ce sont justement les examens remplis de questions pièges du livre de Sierra et Bates ainsi que ceux d’ExamLab qui me donnent cette impression.
Ainsi, je recommande néanmoins de les faire juste pour se prendre de bonnes raclées et comprendre qu’il reste toujours beaucoup à apprendre.

Au final, l’important n’était pas la destination mais tout ce que j’ai appris sur le chemin… même si je suis quand même super content de l’avoir réussie. 😀

Posted in IT | Tagged , | Leave a comment

Passage de l’OCJP 6 (suite)

10 avril 2014, 13h45 :

Ca y est, après avoir présenté deux pièces d’identité (heureusement que j’ai retrouvé un passeport valable dans le fin fond d’une caisse de déménagement), je me vois confisquer la nourriture que j’espérais consommer sur place ainsi que mon portefeuille et mes clés.
Pas le droit d’avoir quoique ce soit dans la salle d’examen et pas le droit de manger ou de boire.
Heureusement que je ne suis pas dépendant à la caféine. 🙂

En m’inscrivant, je me suis même fait tiré le portrait. Si vous êtes de ceux pour qui les photos d’identité ressemblent à une foire aux monstres, je vous laisse imaginer le résultat lorsque l’on est, en plus, pas préparé à cela.
Bon … bah, vu ma tronche, Oracle ne me contactera pas pour être Mister Java Developer 2014.

Me voilà à présent dans la salle d’attente à attendre … quoi en fait ?
La réceptionniste m’a dit que tout était en règle et qu’elle ouvrirait la session dès que je le demande. J’en profite donc pour me promener et m’asseoir dans les fauteuils confortables avant d’imposer à mon dos la compagnie d’une chaise inconfortable pendant un peu moins de deux heures et demie.

J’essaye de ne penser à rien tout en priant pour que le maximum d’information reste dans ma tête.
Dieu que les examens ne m’ont pas manqués depuis ma sortie des études !

Après m’être suffisamment reposé, je décide d’entrer dans l’arène.
Une rapide lecture des conditions d’utilisation et me voilà enfermé dans une petite pièce ayant place pour 3 personnes avec, pour fidèles compagnons, une feuille de brouillon et de quoi écrire dessus.

C’est ainsi et avec pour fond sonore l’alternance de mes soupirs et ceux de mon voisin que l’examen commença.

Ce qu’il s’est passé ensuite … bien, c’est entre Oracle et moi. 😉

Posted in IT | Tagged , | Leave a comment

Passage de l’OCJP 6

10 avril 2014, 11h :

Il reste moins d’une heure pour réviser avant de se préparer et de se mettre en route pour le centre d’examen.

Merde !

Je n’ai pas utilisé toutes mes cartouches, j’ai à peine passé un seul examen sur ExamLab et, en plus, je l’ai raté.
Décidément … que cela soit dans le livre (de Sierra et Bates, la référence) ou sur internet, je n’arrive pas à faire plus de 60% de bonnes réponses (quand j’y arrive).

Merde !

J’aurais du m’y prendre un peu plus à l’avance ou reporter l’examen. Là, à moins de 24 heures de l’heure fatidique, ce n’est plus possible.
Et pourtant, lorsque je lis et relis le bouquin, j’ai l’impression de tourner en rond, de ne rien apprendre de nouveau.

Je peste.

C’est quoi ces examens ? Est-ce vraiment fondamental de savoir que tailSet(Object) renvoie un SortedSet quand tailSet(Object, boolean) renvoie un NavigableSet ?
Sérieux ?
Il y a pourtant d’autres choses à tester comme … je sais pas, le “narrowing primitive conversion”, la serialization, le covariant return dans les méthodes overridées ou bien des petites questions piégeuses sur l’autoboxing et l’unboxing.
Mais non, là, je me tape l’évaluation brute de ma connaissance de la JavaDoc.

Pfff.

Le temps avance. Irrémédiablement.
Cet être implacable et incorruptible qui m’impose sa présence sur tous les écrans des appareils modernes est, en ce moment, mon pire ennemi.

Rapidement, je relis les corrigés des “mock exams” à ma disposition en essayant non pas de mémoriser les questions, cela serait inutile, mais bien d’identifier les différentes chausse-trapes pour redoubler d’attention et éviter de perdre des points pour des stupides fautes d’attention. J’en aurais bien besoin…

Mais il est l’heure déjà, il faut y aller.
Le livre se ferme. Je l’espère pour la dernière fois. En cas de doute futur, Internet prendra le relais.
Ciao compagnon, je continue seul à présent.

Alea jacta est.

Posted in IT | Tagged , | Leave a comment

Petit piège en passant.

Imaginons que vous passiez un concours, un examen ou une certification et que vous tombiez sur la question suivante :
“Quel sera le résultat du code ci-dessous ?”

int[] arr = new int[4];
arr[0] = 8;
arr[1] = 5;
arr[2] = 1;
arr[3] = 7;
System.out.println("find 5 :"+Arrays.binarySearch(arr, 5));

Continue reading

Posted in IT | Tagged , | 1 Comment

Faire une carte de visite en LaTeX

Tout d’abord, ceci a pu être fait grâce à la lecture du livre “Tout ce que vous avez toujours voulu savoir sur LaTeX sans jamais oser le demander (ou comment utiliser LaTeX quand on n’y connaît goutte)” disponible gratuitement ici et plus précisément le chapitre 4 relatif à la manipulation de boites.

Pour faire un court résumé, tout fonctionne via des boites. Les lettres sont incluses dans des petites boites qui sont elles-mêmes contenues dans une boite représentant la ligne et le tout est dans une boite représentant le paragraphe.
Lorsque l’on arrive à identifier ces boites et leurs arrangements, il est possible de faire beaucoup de choses : Du texte en diagonal, imposer que la première lettre de chaque chapitre soit calligraphiée, ou même faire des super template LaTeX notamment pour les CV (modernCV est d’ailleurs un template qui est à la fois visuellement beau et bien écrit. Ceux voulant approfondir la mise en page en LaTeX peuvent regarder du coté du fichier “style classic” pour commencer).

Entrons dans le vif du sujet. Si vous avez lu le titre de l’article, vous aurez compris que le but est de faire des cartes de visite en LaTeX. Nous n’utiliserons pas ici de package comme bizcard ou ticket mais nous allons tout faire nous-même (ou presque).

Voici le rendu final :
Eighth step

Tout d’abord, définissons le contour de notre carte de visite.
Afin d’éviter les lourdeurs, seule la commande principale createonecard sera répétée. Pas de panique, le code entier est donné en fin d’article.

Notez ici, que j’utilise des frameboxes (boites avec une frontière) à la place de simples boxes afin de pouvoir visualiser les boites que nous manipulerons.
Afin de minimiser les changements non désirés lorsque nous supprimerons ces frameboxes (et mettrons des boxes à la place), j’impose le margin de ces boites à 0 via la commande \setlength{\fboxsep}{0pt}.

\documentclass[11pt]{article}
\usepackage[T1]{fontenc}
\parindent0em
\usepackage{graphicx}
\usepackage{xcolor}
\usepackage{fontawesome}
\usepackage{geometry}
\geometry{hmargin=20mm,vmargin=20mm}
\usepackage{ifthen}

\thispagestyle{empty}

\newlength{\cardWidth}
\newlength{\cardHeight}
\setlength{\cardWidth}{8.5cm}
\setlength{\cardHeight}{5.5cm}

\setlength{\fboxsep}{0pt} %to have no space between boxes

\newcommand{\createonecard}{
 \framebox{
  \begin{minipage}[][\cardHeight][t]{\cardWidth}
   %nothing
  \end{minipage}
 }
}

\begin{document}
 \createonecard
\end{document}

Cependant, il serait peut-être intéressant de pouvoir définir une image de fond à notre carte de visite.
Pour cela, nous allons créer un nouvel environnement imgminipage qui va générer notre minipage ainsi qu’un boite contenant l’image qui nous intéresse. On superpose enfin les boites via une translation horizontale de notre boite contenant l’image.


\newsavebox\mysavebox
\newenvironment{imgminipage}[3]{ %here the begin{imgminipage}
 \def\imgcmd{
  \ifx&#3& %if the third argument is empty
   %nothing
  \else
   \includegraphics[width=#2,height=#1]{#3}
  \fi
 }%
 \begin{lrbox}{\mysavebox}%
 \begin{minipage}[][#1][t]{#2}%
}{ %here the end{imgminipage}
 \end{minipage}
 \end{lrbox}%
 \sbox\mysavebox{\framebox{\usebox\mysavebox}}%
 \mbox{\rlap{\raisebox{-\dp\mysavebox}{\imgcmd}}\usebox\mysavebox}%
}

\newcommand{\createonecard}{
 \begin{imgminipage}{\cardHeight}{\cardWidth}{}%bgIT.jpg}
  %nothing
 \end{imgminipage}
}

Pour comprendre plus en détail la définition du nouvel environnement vous pouvez consulter la page 85 du livre cité en début d’article. (NB : pour une meilleure compréhension sachez que \begin{lrbox}{\coucou}\end{lrbox} est équivalent à \savebox\coucou).

Nous obtenons le résultat impressionnant suivant :
First step
Bon, ici pas de secret, LaTeX peut faire beaucoup de choses … mais pas arranger magiquement les boites pour vous créer un design sans que vous n’y ayez pensé. :p
Je vous donne un répit. Un design standard est :
– une partie supérieure contenant le nom ainsi que le titre.
– une partie inférieure contenant les informations supplémentaires.

En gros, le découpage suivant :
Second step
Vous aurez déjà compris le principe. On va créer des boites dans notre boite. 🙂
C’est partiiii !

\newlength{\sepLine}
\setlength{\sepLine}{1pt}
\newlength{\mainBoxWidth}
\setlength{\mainBoxWidth}{\cardWidth}
\addtolength{\mainBoxWidth}{-2\fboxsep}
\newlength{\headerHeight}
\setlength{\headerHeight}{0.4\cardHeight}
\addtolength{\headerHeight}{-2\fboxsep}
\addtolength{\headerHeight}{-\sepLine}
\newlength{\bottomHeight}
\setlength{\bottomHeight}{0.6\cardHeight}
\addtolength{\bottomHeight}{-2\fboxsep}
\addtolength{\bottomHeight}{-\sepLine}

\newcommand{\createonecard}{
 \begin{imgminipage}{\cardHeight}{\cardWidth}{}%bgIT.jpg}
  \framebox{%border
   \parbox[][\headerHeight][c]{\mainBoxWidth}{%sup
    \hfill
   }
  }

  \framebox{%border
   \parbox[][\bottomHeight][c]{\mainBoxWidth}{%inf
    \hfill
   }
  }
 \end{imgminipage}
}

Les \hfill ne sont normalement pas obligatoires mais vu qu’il n’y a encore rien dans nos boites, elles prendraient directement une taille nulle et cela ne nous aiderait pas pour les voir.

Bon … il est temps de faire un découpage plus précis que cela et c’est donc à partir d’ici que vos goûts ne vont pas forcément rejoindre les miens. :p
En effet, tout d’abord et afin de séparer les deux composants, j’avais envie de tracer une ligne partant du bord droit et s’arretant peu avant le bord gauche.
Pour cela, on va créer une nouvelle boite qui sera entre la boite “sup” et la boite “inf”.

\definecolor{uni}{rgb}{.3019,.7216,.7019} %couleur de notre ligne

\newcommand{\createonecard}{
 \begin{imgminipage}{\cardHeight}{\cardWidth}{}%bgIT.jpg}
  \framebox{%border
   \parbox[][\headerHeight][c]{\mainBoxWidth}{%sup
   }
  }

  \makebox{%
   \hspace*{0.2cm}%bricolage immonde pour que la ligne commence à droite de la carte sans padding
   \parbox[c]{\mainBoxWidth}{
    \raggedleft\raisebox{1pt}{
     \color{uni}\rule{0.8\mainBoxWidth}{0.25ex}
    }
   }%
  }

  \framebox{%border
   \parbox[][\bottomHeight][c]{\mainBoxWidth}{%inf
   }
  }
 \end{imgminipage}
}

Et voici le résultat.
Third step
Nous sommes arrivés au moment où nous devons remplir notre carte de visite et donc de savoir comment arranger les différentes informations. :p
Pour ma part, voici le design que je désirais :
– une partie supérieure contenant un logo à gauche ainsi que mon nom et titre à droite.
– une partie inférieure contenant mes informations à gauche et un QRcode redirigeant vers un CV détaillé à droite.

Et c’est reparti pour des boites !

\newcommand{\createonecard}{
 \begin{imgminipage}{\cardHeight}{\cardWidth}{}%bgIT.jpg}
  \framebox{%border
   \parbox[][\headerHeight][c]{\mainBoxWidth}{%sup
    \framebox{
     \parbox{0.3\mainBoxWidth}{%logo
      \hfill
     }
    }
    \framebox{%border
     \hspace{3pt}
     \parbox[c][\headerHeight][c]{0.65\mainBoxWidth}{%main info
      \hfill
     }
    }
   }
  }

  \makebox{%
   \hspace*{0.2cm}%bricolage immonde pour que la ligne commence à droite de la carte sans padding
   \parbox[c]{\mainBoxWidth}{
    \raggedleft\raisebox{1pt}{
     \color{uni}\rule{0.8\mainBoxWidth}{0.25ex}
    }
   }%
  }

  \framebox{%border
   \parbox[][\bottomHeight][c]{\mainBoxWidth}{%inf
    \framebox{%border
     \hspace{3pt}
     \parbox[c][0.9\bottomHeight][c]{0.64\mainBoxWidth}{%details
      \hfill
     }
    }
    \hspace{3pt}
    \framebox{
     \parbox{0.3\mainBoxWidth}{%qrcode
      \hfill
     }
    }
   }
  }
 \end{imgminipage}
}

Voici le résultat de notre premier découpage :
Fourst step
Et … en fait, la partie la plus compliquée est déjà terminée !
Il ne nous reste plus qu’à choisir les images et les polices de caractère.
Le dernier petit détail était pour les informations : je désirais afficher diverses icones correspondant aux informations à afficher. A part cela, le reste est très standard.
Nous allons remplir nos boites avec des \adress, \phone, \logo dont nous attribuerons la valeur ultérieurement.

C’est parti pour la dernière ligne droite !

\newlength{\iconWidth}

\newcommand{\createonecard}{
 \begin{imgminipage}{\cardHeight}{\cardWidth}{}%bgIT.jpg}
  \framebox{%border
   \parbox[][\headerHeight][c]{\mainBoxWidth}{%sup
    \framebox{
     \parbox{0.3\mainBoxWidth}{%logo
      \ifthenelse{\isundefined{\logo}}
      {\hfill}
      {\includegraphics[width=0.3\mainBoxWidth]{\logo}}
     }
    }
    \framebox{%border
     \hspace{3pt}
     \parbox[c][\headerHeight][c]{0.65\mainBoxWidth}{%main info
      {\bfseries{\LARGE \name}} \par
      \small\title
     }
    }
   }
  }

  \makebox{%
   \hspace*{0.2cm}%bricolage immonde pour que la ligne commence à droite de la carte sans padding
   \parbox[c]{\mainBoxWidth}{
    \raggedleft\raisebox{1pt}{
     \color{uni}\rule{0.8\mainBoxWidth}{0.25ex}
    }
   }%
  }

  \framebox{%border
   \parbox[][\bottomHeight][c]{\mainBoxWidth}{%inf
    \framebox{%border
     \hspace{3pt}
     \parbox[c][0.9\bottomHeight][c]{0.64\mainBoxWidth}{%details
      \hfill
      \settowidth{\iconWidth}{\mailsymbol}
      \tiny
       \parbox[][][c]{0.63\mainBoxWidth}{
        \makebox[\iconWidth]{
         \mailsymbol
        }
        \raisebox{3pt}{
          \email
        }
       }
       \parbox[][][c]{0.63\mainBoxWidth}{
        \makebox[\iconWidth]{
         \phonesymbol
        }
        \raisebox{3pt}{
          \phone
        }
       }
       \parbox[][][c]{0.63\mainBoxWidth}{
        \makebox[\iconWidth]{
         \addresssymbol
        }
        \raisebox{3pt}{
          \address
        }
       }
       \parbox[][][c]{0.63\mainBoxWidth}{
        \vspace*{5pt}
        \makebox[\iconWidth]{
        }
        \extrainfo
       }
     }
    }
    \hspace{3pt}
    \framebox{
     \parbox{0.3\mainBoxWidth}{%qrcode
      \ifthenelse{\isundefined{\qr}}
      {\hfill}
      {\includegraphics[width=0.3\mainBoxWidth]{\qr}}
     }
    }
   }
  }
 \end{imgminipage}
}

\begin{document}

 % personal data
 \def\name{Renaud Hoyoux}
 \def\title{Software Engineer -- IThusiast}
 \def\address{Somewhere}
 \def\email{Something}
 \def\phone{Something}
 \def\extrainfo{Great interest in Java technologies and banking environment.}
 \def\logo{turtle}
 \def\qr{radiuscode}

 \def\mailsymbol{{\Large\faEnvelope}}
 \def\addresssymbol{{\Large\faHome}}
 \def\phonesymbol{{\Large\faMobilePhone}}
 \def\homepagesymbol{{\Large\faGlobe}}

 \createonecard
\end{document}

nous donne
Fifth step
où le QRcode a été généré avec Zxing grâce au tutorial de Thierry Leriche situé ici.

En retirant toutes les boites et en jouant un peu avec les espaces, nous obtenons enfin notre rendu final.
Sixth step
J’en profite pour faire une petite pub pour le site Unitag qui peut vous générer de jolis QRcodes gratuitement (comme le suivant) pour tout ceux qui n’ont pas envie de faire joujou avec Java2D. :p
Seventh step
Maintenant que nous savons faire une carte de visite, il ne nous reste plus qu’à en faire toute une page !


\newcommand{\createcards}{
 \newcounter{counter}
 \setcounter{counter}{0}
 \whiledo{\value{counter}<8}{%
  \ifthenelse{\isodd{\value{counter}}}%
  {%
   \createonecard%
   \par%
  }{%
   \createonecard%
  }%
  \addtocounter{counter}{1}%
 }
}

\begin{document}

% personal data
%...

 \createcards
\end{document}

Nous sommes arrivés à générer le PDF montré au début de cet article et la boucle est bouclée. 🙂
Eighth step

Et voici comme promis le code complet 🙂
Update : Pour plus de facilité, vous trouverez ici le code actuel “empackagé” pour l’occasion.

\documentclass[11pt]{article}
\usepackage[T1]{fontenc}
\parindent0em
\usepackage{graphicx}
\usepackage{xcolor}
\usepackage{fontawesome}
\usepackage{geometry}
\geometry{hmargin=20mm,vmargin=20mm}
\usepackage{ifthen}

\thispagestyle{empty}

\newsavebox\mysavebox
\newenvironment{imgminipage}[3]{%
 \def\imgcmd{
  \ifx&#3&%
   %nothing
  \else
   \includegraphics[width=#2,height=#1]{#3}
  \fi
 }%
 \begin{lrbox}{\mysavebox}%
 \begin{minipage}[][#1][t]{#2}%
}{%
 \end{minipage}
 \end{lrbox}%
 \sbox\mysavebox{\framebox{\usebox\mysavebox}}%
 \mbox{\rlap{\raisebox{-\dp\mysavebox}{\imgcmd}}\usebox\mysavebox}%
}

\newlength{\cardWidth}
\newlength{\cardHeight}
\setlength{\cardWidth}{8.5cm}
\setlength{\cardHeight}{5.5cm}
\newlength{\sepLine}
\setlength{\sepLine}{1pt}
\newlength{\mainBoxWidth}
\setlength{\mainBoxWidth}{\cardWidth}
\addtolength{\mainBoxWidth}{-2\fboxsep}
\newlength{\headerHeight}
\setlength{\headerHeight}{0.4\cardHeight}
\addtolength{\headerHeight}{-2\fboxsep}
\addtolength{\headerHeight}{-\sepLine}
\newlength{\bottomHeight}
\setlength{\bottomHeight}{0.6\cardHeight}
\addtolength{\bottomHeight}{-2\fboxsep}
\addtolength{\bottomHeight}{-\sepLine}
\newlength{\iconWidth}

\setlength{\fboxsep}{0pt} %to have no space between boxes

\newcommand{\createonecard}{
 \begin{imgminipage}{\cardHeight}{\cardWidth}{}%bgIT.jpg}
  \parbox[][\headerHeight][c]{\mainBoxWidth}{%sup
   \parbox{0.3\mainBoxWidth}{%logo
    \ifthenelse{\isundefined{\logo}}
    {\hfill}
    {\includegraphics[width=0.3\mainBoxWidth]{\logo}}
   }
   \hspace{3pt}
   \parbox[c][\headerHeight][c]{0.65\mainBoxWidth}{%main info
    {\bfseries{\LARGE \name}} \par
    \small\title
    \hfill
   }
  }

  \makebox{%
   \hspace*{0.2cm}%bricolage immonde pour que la ligne commence à droite de la carte sans padding
   \parbox[c]{\mainBoxWidth}{
    \raggedleft\raisebox{1pt}{
     \color{uni}\rule{0.8\mainBoxWidth}{0.25ex}
    }
   }%
  }

  \parbox[][\bottomHeight][c]{\mainBoxWidth}{%inf
   \hspace{3pt}
   \parbox[c][0.9\bottomHeight][c]{0.64\mainBoxWidth}{%details
    \hfill
    \settowidth{\iconWidth}{\mailsymbol}
    \tiny
    \parbox[][][c]{0.63\mainBoxWidth}{
     \makebox[\iconWidth]{
      \mailsymbol
     }
     \raisebox{3pt}{
      \email
     }
    }
    \parbox[][][c]{0.63\mainBoxWidth}{
     \makebox[\iconWidth]{
      \phonesymbol
     }
     \raisebox{3pt}{
      \phone
     }
    }
    \parbox[][][c]{0.63\mainBoxWidth}{
     \makebox[\iconWidth]{
      \addresssymbol
     }
     \raisebox{3pt}{
      \address
     }
    }
    \parbox[][][c]{0.63\mainBoxWidth}{
     \vspace*{5pt}
     \makebox[\iconWidth]{
     }
     \extrainfo
    }
   }
   \hspace{3pt}
   \parbox{0.3\mainBoxWidth}{%qrcode
    \ifthenelse{\isundefined{\qr}}
     {\hfill}
     {\includegraphics[width=0.3\mainBoxWidth]{\qr}}
    }
   }
  }
 \end{imgminipage}
}

\newcommand{\createcards}{
 \newcounter{counter}
 \setcounter{counter}{0}
 \whiledo{\value{counter}<8}{%
  \ifthenelse{\isodd{\value{counter}}}%
  {%
   \createonecard%
   \par%
  }{%
   \createonecard%
  }%
  \addtocounter{counter}{1}%
 }
}

\begin{document}
 \definecolor{uni}{rgb}{.3019,.7216,.7019}

% personal data
 \def\name{Renaud Hoyoux}
 \def\title{Software Engineer -- IThusiast}
 \def\address{Somewhere}
 \def\email{Something}
 \def\phone{Something}
 \def\extrainfo{Great interest in Java technologies and banking environment.}
 \def\logo{turtle}
 \def\qr{radiuscode}

 \def\mailsymbol{{\Large\faEnvelope}}
 \def\addresssymbol{{\Large\faHome}}
 \def\phonesymbol{{\Large\faMobilePhone}}

 \createcards
\end{document}

Posted in Uncategorized | Tagged | Leave a comment

How to programmatically resize the stage in a JavaFX app.

Recently, I had to do an UI in JavaFX with custom minimize, resize & close buttons.
So, to hide the default three minimize, resize & close buttons, I put the StageStyle to UNDECORATED.

But, a side effect of this is the disabling of the capacity to resize the window…

I didn’t find a simple way to reactivate this functionality so … I had to code it myself.
Here’s the result.
This is the default code for a new JavaFX project in NetBeans where I added the stageStyle and a background color for the window.


public class Test extends Application {
  Scene scene;
  Stage stage;
  @Override
  public void start(Stage primaryStage) {
    Button btn = new Button();
    btn.setText("Say 'Hello World'");
    btn.setOnAction(new EventHandler<ActionEvent>() {
      @Override
      public void handle(ActionEvent event) {
        System.out.println("Hello World!");
      }
    });
    StackPane root = new StackPane();
    root.getChildren().add(btn);

    scene = new Scene(root, 300, 250);
    root.setStyle("-fx-background-color: red;"); // just to see the limit of the window ;-)
    stage = primaryStage;
    stage.initStyle(StageStyle.UNDECORATED); // remove the "three top buttons on the window"
    primaryStage.setTitle("Hello World!");
    primaryStage.setScene(scene);
    primaryStage.show();
  }
}

For the resize functionality, I added the following to the start method:

  ResizeListener listener = new ResizeListener();
  scene.setOnMouseMoved(listener);
  scene.setOnMousePressed(listener);
  scene.setOnMouseDragged(listener);

We will do it in three steps :
– First, we will define a border; if the cursor is between this border and the edge of the window, we will change the cursor and inform the app than the user can now resize following one or two directions.
– Secondly, when the user will press a mouse button, we will memorize this position. It will be usefull to compute a “delta”.
– At the end, while the user will drag, we will finally resize.

Here’s the class.
class ResizeListener implements EventHandler<MouseEvent>{ 
  double dx;
  double dy;
  double deltaX;
  double deltaY;
  double border = 10;
  boolean moveH;
  boolean moveV;
  boolean resizeH = false;
  boolean resizeV = false;
  @Override
  public void handle(MouseEvent t) {
    if(MouseEvent.MOUSE_MOVED.equals(t.getEventType())){
      Will change the cursor and enable the resize
    }
    else if(MouseEvent.MOUSE_PRESSED.equals(t.getEventType())){
      Will save some initial data
    }
    else if(MouseEvent.MOUSE_DRAGGED.equals(t.getEventType())){ 
      will do the resize
    }
  }
}

In this article, we will distinguish two ways of resizing following one direction : for a horizontal resize, we can resize on the left or on the right side.
If we resize from the left side for the horizontal resize (and top side for the vertical resize), we must also move the window (it will be expressed by the move boolean), so the move boolean will be equal to true when the cursor is on the left edge of the window.


if(MouseEvent.MOUSE_MOVED.equals(t.getEventType())){
  if(t.getX() < border && t.getY() < border){
    scene.setCursor(Cursor.NW_RESIZE);
    resizeH = true;
    resizeV = true;
    moveH = true;
    moveV = true;
  }
  else if(t.getX() < border && t.getY() > scene.getHeight() -border){
    scene.setCursor(Cursor.SW_RESIZE);
    resizeH = true;
    resizeV = true;
    moveH = true;
    moveV = false;
  }
  else if(t.getX() > scene.getWidth() -border && t.getY() < border){
    scene.setCursor(Cursor.NE_RESIZE);
    resizeH = true;
    resizeV = true;
    moveH = false;
    moveV = true;
  }
  else if(t.getX() > scene.getWidth() -border && t.getY() > scene.getHeight() -border){
    scene.setCursor(Cursor.SE_RESIZE);
    resizeH = true;
    resizeV = true;
    moveH = false;
    moveV = false;
  }
  else if(t.getX() < border || t.getX() > scene.getWidth() -border){
    scene.setCursor(Cursor.E_RESIZE);
    resizeH = true;
    resizeV = false;
    moveH = (t.getX() < border);
    moveV = false;
  }
  else if(t.getY() < border || t.getY() > scene.getHeight() -border){
    scene.setCursor(Cursor.N_RESIZE);
    resizeH = false;
    resizeV = true;
    moveH = false;
    moveV = (t.getY() < border);
  }
  else{
    scene.setCursor(Cursor.DEFAULT);
    resizeH = false;
    resizeV = false;
    moveH = false;
    moveV = false;
  }
}

First, consider the case of resizing on the right side.
When the user presses the button, the event.getX() will be width-epsilon where epsilon is less than the border previously choosen.
Keep in memory this epsilon in the dx variable and if the user moves the mouse, set the width to event.getX()+dx.
This dx will avoid brutal resizing from width-epsilon to width with the first move.

So we have

else if(MouseEvent.MOUSE_PRESSED.equals(t.getEventType())){
  dx = stage.getWidth() - t.getX();
  dy = stage.getHeight() - t.getY();
}
else if(MouseEvent.MOUSE_DRAGGED.equals(t.getEventType())){
  if(stage.getWidth() <= minSize.width){
    if(t.getX()+dx - stage.getWidth() > 0){
      stage.setWidth(t.getX()+dx);
    }
  }
  else if(stage.getWidth() > minSize.width){ 
    stage.setWidth(t.getX()+dx);
  }
}

Now, consider the case of resizing on the left side.
When we drag the mouse, the x coordinate of the window must follow the mouse and the end of the window must stay unchanged.
For this, we will also use a delta : deltaX.
So, for the left resizing, we have in the else if(MouseEvent.MOUSE_DRAGGED.equals(t.getEventType())) part.

  if(stage.getWidth() <= minSize.width){
    deltaX = stage.getX()-t.getScreenX();
    if(t.getX() < 0){// if new > old, it's permitted
      stage.setWidth(deltaX+stage.getWidth());
      stage.setX(t.getScreenX());
    }
  }
  else if(stage.getWidth() > minSize.width){ 
    deltaX = stage.getX()-t.getScreenX();
    stage.setWidth(deltaX+stage.getWidth());
    stage.setX(t.getScreenX());
  }

Now, we just have to add a condition to differentiate the two cases with the aid of the move variable.

NB : If you set a minimal size, don’t forget to consider than if the current width is less than or equal to the min width, the only permitted move is to enlarge the window.

All in all, we have this (almost) complete code:

class ResizeListener implements EventHandler<MouseEvent>{ 
  double dx;
  double dy;
  double deltaX;
  double deltaY;
  double border = 10;
  boolean moveH;
  boolean moveV;
  boolean resizeH = false;
  boolean resizeV = false;
  @Override
  public void handle(MouseEvent t) {
    if(MouseEvent.MOUSE_MOVED.equals(t.getEventType())){
      if(t.getX() < border && t.getY() < border){
        scene.setCursor(Cursor.NW_RESIZE);
        resizeH = true;
        resizeV = true;
        moveH = true;
        moveV = true;
      }
      else if(t.getX() < border && t.getY() > scene.getHeight() -border){
        scene.setCursor(Cursor.SW_RESIZE);
        resizeH = true;
        resizeV = true;
        moveH = true;
        moveV = false;
      }
      else if(t.getX() > scene.getWidth() -border && t.getY() < border){
        scene.setCursor(Cursor.NE_RESIZE);
        resizeH = true;
        resizeV = true;
        moveH = false;
        moveV = true;
      }
      else if(t.getX() > scene.getWidth() -border && t.getY() > scene.getHeight() -border){
        scene.setCursor(Cursor.SE_RESIZE);
        resizeH = true;
        resizeV = true;
        moveH = false;
        moveV = false;
      }
      else if(t.getX() < border || t.getX() > scene.getWidth() -border){
        scene.setCursor(Cursor.E_RESIZE);
        resizeH = true;
        resizeV = false;
        moveH = (t.getX() < border);
        moveV = false;
      }
      else if(t.getY() < border || t.getY() > scene.getHeight() -border){
        scene.setCursor(Cursor.N_RESIZE);
        resizeH = false;
        resizeV = true;
        moveH = false;
        moveV = (t.getY() < border);
      }
      else{
        scene.setCursor(Cursor.DEFAULT);
        resizeH = false;
        resizeV = false;
        moveH = false;
        moveV = false;
      }
    }
    else if(MouseEvent.MOUSE_PRESSED.equals(t.getEventType())){
      dx = stage.getWidth() - t.getX();
      dy = stage.getHeight() - t.getY();
    }
    else if(MouseEvent.MOUSE_DRAGGED.equals(t.getEventType())){ 
      if(resizeH){
        if(stage.getWidth() <= minSize.width){
          if(moveH){ 
            deltaX = stage.getX()-t.getScreenX();
            if(t.getX() < 0){// if new > old, it's permitted
              stage.setWidth(deltaX+stage.getWidth());
              stage.setX(t.getScreenX());
            }
          }
          else{
            if(t.getX()+dx - stage.getWidth() > 0){
              stage.setWidth(t.getX()+dx);
            }
          }
        }
        else if(stage.getWidth() > minSize.width){ 
          if(moveH){
            deltaX = stage.getX()-t.getScreenX();
            stage.setWidth(deltaX+stage.getWidth());
            stage.setX(t.getScreenX());
          }
          else{
            stage.setWidth(t.getX()+dx);
          }
        }
      }
    }
  }
}

The height can be resized in the same manner.

Posted in IT | Tagged | 3 Comments

Un automate en KineticJS

Bonjour,
Ayant eu à faire récemment un site utilisant Canvas de HTML5 et ayant pour thème “Automates mathématiques et Doctor Who“, il me fallait donc apprendre le Javascript (officiellement “me mettre à niveau “).

Mon but principal était de pouvoir afficher un automate mathématique du même style que l’exemple ci-dessous :

automata

Pour cela, rien d’a priori très compliqué; savoir afficher des lignes et des cercles et savoir combiner le tout ensemble.
N’ayant pas besoin d’un truc ultra personnalisable, je me suis mis à la recherche d’une librairie me convenant et je suis tombé sur KineticJS qui a donc fait l’affaire notamment grâce à la bonne qualité de sa documentation (un tuto est disponible ici).

Supposons vouloir dessiner l’automate basique suivant :
Christmas Automata

Pour cela, nous considérerons deux objets principaux : l’automate et un état de l’automate.
Commençons par l’état.
Je considère cela comme un Kinetic.Group constitué d’un cercle ou deux et possédant ou non une image de fond, et pouvant contenir un effet au survol de la souris.
L’avantage d’avoir l’état en tant qu’objet à part entière sera de pouvoir par après ajouter soi-même à l’état en question les différents effets que l’on veut (déplacement, grossissement, etc.).
Un état pouvant être final et étant caractérisé par un double cercle, on en arrive au code suivant.


function AutomataState(x, y, radius, image, bFinal){
  this.x=x;
  this.y=y;
  Kinetic.Group.call(this,{
    x: this.x,
    y: this.y
  });
  this.radius=radius;
  this.bFinal = bFinal;
  this.circle = new Kinetic.Circle({
    x: 0,
    y: 0,
    radius: this.radius,
    stroke: 'black',
    strokeWidth: 3
  });
  this.add(this.circle);
  if(this.bFinal){
    this.circle.setFill('white');
    var circle2 = new Kinetic.Circle({
      x: 0,
      y: 0,
      radius: this.radius-5,
      stroke: 'black',
      strokeWidth: 3
    });
    this.add(circle2);
  }
}

Pour bien faire, il faut également prévoir un label en son centre mais cela ne sera pas fait ici. Il est cependant aisé de le rajouter.

Dans mon cas, j’aimerais qu’un état de l’automate ait une image de fond, soit un lien cliquable et ait un effet de rotation et d’agrandissement au survol de ma souris.
Je lui ajoute donc les méthodes setImage, setLink et setTween
On a donc le code


AutomataState.prototype ={
  setLink: function(link){
    this.on('mouseover', function() {
      document.body.style.cursor = 'pointer';
    });
    this.on('mouseout', function() {
      document.body.style.cursor = 'default';
    });
    this.on('click', function() {
      window.location.href = link;
    });
  },
  setImage: function(img){
    if(this.bFinal){
      this.circle2.setFillPatternImage(img);
      this.circle2.setFillPatternOffset([this.radius-2.5, this.radius-2.5])
    }else{
      this.circle.setFillPatternImage(img);
      this.circle.setFillPatternOffset([this.radius, this.radius])
    }
  },
  fill: function(color){
    if(this.bFinal){
      this.circle2.setFill(color);
    }else{
      this.circle.setFill(color);
    }
  },
  setTweenEffect: function(){
    var tween = new Kinetic.Tween({
      node: this,
      duration: 0.5,
      rotation: Math.PI * 2,
      opacity: 1,
      strokeWidth: 6,
      scaleX: 1.3,
      scaleY: 1.3,
      easing: Kinetic.Easings.Linear
    });
    this.on('mouseover', function() {
      tween.play();
    });
    this.on('mouseout', function() {
      tween.reverse();
    });
  }
}

En n’oubliant pas de déclarer AutomataState comme fils de Kinetic.Group de la façon suivante :

Kinetic.Util.extend(AutomataState, Kinetic.Group);

Il est déjà temps de passer à l’autre objet qui nous intéresse à savoir l’automate.
Un automate est un Kinetic.Group qui contiendra les AutomataState et qui aura pour responsabilité de lier entre eux les différents états de l’automate.
Son constructeur est par conséquent assez bref

function Automata(){
    Kinetic.Group.call(this,{});
}

Pour les méthodes, nous aurons

  • addState : qui ajoute visuellement l’automataState à l’automate.
  • setLoop : permettant de créer une boucle sur un état (dont la conception est expliquée ici).
  • setEdge : liant deux états de l’automate (idem).
  • setInputArrow : indiquant où commence notre automate (idem).


Automata.prototype ={
  addState: function(state){
    this.add(state);
  },
  setLoop: function(state, angle, label, textFontSize) {
    var pos = state.circle.getAbsolutePosition();
    var group = new Kinetic.Group({});
    var gap = 22/180 * Math.PI;
    var middleX = pos.x+(state.radius*Math.cos(angle))*2;
    var middleY = pos.y-(state.radius*Math.sin(angle))*2;
    var spline = new Kinetic.Spline({
      points: [{
        x: pos.x+(state.radius*Math.cos(angle-gap)),
        y: pos.y-(state.radius*Math.sin(angle-gap))
      }, {
        x: middleX,
        y: middleY
      }, {
        x: pos.x+(state.radius*Math.cos(angle+gap)),
        y: pos.y-(state.radius*Math.sin(angle+gap))
      }],
      strokeWidth: 2,
      lineCap: 'round',
      tension: 2.5
    });
    var arrow = new Kinetic.Line({
      points: [
        middleX + (10*Math.cos(angle+(Math.PI*(90+135)/180))), middleY - (10*Math.sin(angle+(Math.PI*(90+135)/180))),
        middleX, middleY,
        middleX + (10*Math.cos(angle+(Math.PI*(90-135)/180))), middleY - (10*Math.sin(angle+(Math.PI*(90-135)/180)))
      ],
      stroke: 'black',
      strokeWidth: 2,
      lineJoin: 'round'
    });

    var labelText = new Kinetic.Text({
      x: middleX + 10,
      y: middleY - 1.5*textFontSize,
      text: label,
      fontSize: textFontSize,
      fontFamily: 'Calibri',
      fill: 'black'
    });

    labelText.setOffset({
      x: (labelText.getWidth()/2)*Math.abs(Math.sin(angle)),
      y: (labelText.getHeight()/2)*Math.abs(Math.cos(angle))
    });

    group.add(spline);
    group.add(arrow);
    group.add(labelText);
    this.add(group);
    state.moveToTop();
  },
  setEdge: function(autState1,autState2,label, textFontSize, isDashed) {
    this.add(new Arrow(autState1.circle.getAbsolutePosition().x,       autState1.circle.getAbsolutePosition().y,
      autState2.circle.getAbsolutePosition().x,       autState2.circle.getAbsolutePosition().y, label, textFontSize, isDashed));
    autState1.moveToTop();
    autState2.moveToTop();
  },
  setInputArrow: function(x, y, autState, label, textFontSize, isDashed) {
    this.add(new Arrow(x, y, autState.circle.getAbsolutePosition().x,       autState.circle.getAbsolutePosition().y, label, textFontSize, isDashed));
    autState.moveToTop();
  }
}
Kinetic.Util.extend(Automata, Kinetic.Group);

Tout ce dont nous avons besoin pour dessiner notre automate “MERRY CHRISTMAS” est à présent en place.
Cependant, pour éviter l’overdose de code, je ne montrerais ici qu’un automate à deux états.

  var stage = new Kinetic.Stage({
    container: 'container',
    width: 600,
    height: 400
  });
  var layer = new Kinetic.Layer();
  stage.add(layer);

  var aut = new Automata();
  layer.add(aut);

  var radius = 50;
  var state1 = new AutomataState(100, 150, radius);
  aut.addState(state1);
  state1.fill("white");

  aut.setInputArrow(25,150,state1,"");
  aut.setLoop(state1,Math.PI/3,"Test1", 15);

  var state2 = new AutomataState(250, 150, radius);
  aut.addState(state2);
  state2.fill("white");
  aut.setEdge(state1,state2,"Enjoy!",15);

Ce qui donne :
Result automata

NB : Il est important d’associer en priorité le layer au stage, l’automate au layer et les états à l’automate car les méthodes associant des animations (comme setTween) nécessitent, au sein de leur implémentation, de pouvoir récupérer l’instance actuelle du Layer.

Pour finir, et afin de ne pas trop vous laisser sur votre faim, voici la version finale de l’automate implémenté :
rendu final automate

Bonne année !

Posted in IT | Tagged , , | Leave a comment

Être une flèche en trigonométrie

Bonjour,

Ayant eu récemment besoin de dessiner des flèches, droites, courbes ou même des boucles, j’ai malheureusement constaté que la librairie JS que j’utilisais pour dessiner en Canvas n’offrait pas de solution toute faite pour cela.
Qu’importe, créons nous même ce dont nous avons besoin ! Cela sera un bon rappel de trigonométrie dont certains disait à l’école que “ça ne sert à rien ce truc”.
Zoouuu, retour à l’école.

Nous allons considérer 2 objets : la droite et la boucle. Commençons donc par la droite.

La flèche droite

Notre but est d’avoir l’objet ci-dessous : une droite avec en son milieu deux autres lignes formant une flèche.
Simple arrow
Les flèches usuelles ayant une forme géométrique fermée située au bout de la droite ne sont pas fondamentalement plus compliquées à dessiner, il suffira de rajouter un point de notre droite pour fermer le path et remplir ce dernier.

Tout d’abord, soyons conscient que notre droite est orientée (c’est une flèche). Elle a donc une origine et une extrémité.
Vu que l’orientation de notre flèche sera différente si on va de A à B ou de B à A, nous devons déterminer de manière précise (et unique) quel est l’angle de notre droite.
Dans ce qui suit, je considère que l’angle de notre droite orientée sera celui entre l’horizontale placée à l’origine de notre droite et notre droite elle-même.
Pour les puristes, je considère l’origine de mon segment orienté comme centre d’un cercle trigonométrique et je calcule mon angle dans ce repère.
From A to B or B to A
De cette manière, l’angle de notre droite sera différent selon son sens et nous n’aurons pas à faire de distinctions pour la suite du traitement.

Pour calculer cet angle, on part de la formule géométrique du produit scalaire : \vec{a}.\vec{b} = ||\vec{a}||\times||\vec{b}|| .\cos (\theta) que l’on réécrit de la façon suivante :
\theta = \arccos (\frac{\vec{a}.\vec{b}}{||\vec{a}||\times||\vec{b}||})
où l’un des deux vecteurs est un vecteur normé (et horizontal donc n’ayant pas de coordonnées selon y).
Nous avons donc
\theta = \arccos ( \frac{a_x . b_x + a_y . b_y}{1\times||\vec{b}||} ) = \arccos ( \frac{1 . b_x + 0 . b_y}{||\vec{b}||} ) = \arccos ( \frac{b_x}{||\vec{b}||} )
Vu que la fonction \arccos a son image égale à [0,\pi], il nous reste à faire un petit test pour déterminer dans quel demi-cercle nous sommes.

Voici donc le code correspondant (en KineticJS):
function Arrow(x1, y1, x2, y2){
  var begPos = {
    x: x1,
    y: y1
  };
  var endPos = {
    x: x2,
    y: y2
  };

  var line = new Kinetic.Line({
    points: [begPos.x, begPos.y, endPos.x, endPos.y],
    stroke: 'black',
    strokeWidth: 2,
    lineJoin: 'round'
  });

  var scalarProd = endPos.x-begPos.x;
  var norme = Math.sqrt((endPos.y-begPos.y)*(endPos.y-begPos.y) + (endPos.x-begPos.x)*(endPos.x-begPos.x));
  if(endPos.y >= begPos.y){
    var angle = -Math.acos((scalarProd)/norme);
  }
  else{
    var angle = Math.acos((scalarProd)/norme);
  }
}

où le b_x est endPos.x-begPos.x car je dois effectuer le changement de repère entre le repère global et celui qu’on a déterminé à l’origine de notre droite.

Maintenant que cela est fait, l’idée de base est simple : chaque aile de notre flèche à un angle de 45° (ou un autre angle constant, c’est selon vos préférences) avec notre droite.
Cependant, vu que les ailes d’une flèches sont toujours à l’arrière de la direction indiquée, je préfère dire que l’angle est en réalité de 135°.
Angles of the wings

A présent, pour déterminer le x et y de chacune des ailes de notre flèche en fonction de l’angle original de notre droite, nous utiliserons bien évidemment les fonctions sinus pour le y et cosinus pour le x.

La suite du code est donc :

...
  var delta = Math.PI * (135)/180;
  var arrow = new Kinetic.Line({
    points: [
      X + wingLength*Math.cos(angle + delta), Y - wingLength*Math.sin(angle + delta),
      X, Y,
      X + wingLength*Math.cos(angle - delta), Y - wingLength*Math.sin(angle - delta)
],
    stroke: 'black',
    strokeWidth: 2,
    lineJoin: 'round'
  });

où nous avons du faire une soustraction pour l’ordonnée car, pour Canvas, l’axe des ordonnées est orienté “vers le bas”.

Nous arrivons donc au code suivant où j’ai imposé la wingLength comme étant un dixième de la norme de la droite et où la flèche est située au milieu de la droite:

function Arrow(x1, y1, x2, y2){
  Kinetic.Group.call(this,{});
  var begPos = {
    x: x1,
    y: y1
  };
  var endPos = {
    x: x2,
    y: y2
  };

  var line = new Kinetic.Line({
    points: [begPos.x, begPos.y, endPos.x, endPos.y],
    stroke: 'black',
    strokeWidth: 2,
    lineJoin: 'round'
  });

  var middleX = (endPos.x-begPos.x)/2 + begPos.x;
  var middleY = (endPos.y-begPos.y)/2 + begPos.y;

  var scalarProd = endPos.x-begPos.x;
  var norme = Math.sqrt((endPos.y-begPos.y)*(endPos.y-begPos.y) + (endPos.x-begPos.x)*(endPos.x-begPos.x));
  if(endPos.y >= begPos.y){
    var angle = -Math.acos((scalarProd)/norme);
  }
  else{
    var angle = Math.acos((scalarProd)/norme);
  }

  norme = norme/10; //divided by 10 for the length of the arrow

  var delta = Math.PI * (135)/180;
  var arrow = new Kinetic.Line({
    points: [
      middleX + norme*Math.cos(angle + delta), middleY - norme*Math.sin(angle + delta),
      middleX, middleY,
      middleX + norme*Math.cos(angle - delta), middleY - norme*Math.sin(angle - delta)],
    stroke: 'black',
    strokeWidth: 2,
    lineJoin: 'round'
  });

  this.add(line);
  this.add(arrow);
}
Kinetic.Util.extend(Arrow, Kinetic.Group);

La boucle fléchée

Voici ce que je voulais obtenir (le cercle pouvant être réduit à un point).
Loop
Vu que nous n’avons pas de droite, nous déterminons l’angle de la flèche en fonction de l’angle de la boucle qui lui est donné en input par l’utilisateur.
Pour cela, rien de compliqué, la flèche est à 90° par rapport à l’angle de la boucle.
Loop angle
Le reste du code demeure inchangé.

function Loop(x1, y1, height, radius, angle) {
  var pos = {
    x: x1,
    y: y1
  };
  Kinetic.Group.call(this,{});
  var gap = 22/180 * Math.PI;
  var middleX = pos.x+(height*Math.cos(angle));
  var middleY = pos.y-(height*Math.sin(angle));
  var spline = new Kinetic.Spline({
    points: [{
      x: pos.x+(radius*Math.cos(angle-gap)),
      y: pos.y-(radius*Math.sin(angle-gap))
    }, {
      x: middleX,
      y: middleY
      }, {
      x: pos.x+(radius*Math.cos(angle+gap)),
      y: pos.y-(radius*Math.sin(angle+gap))
    }],
    strokeWidth: 2,
    lineCap: 'round',
    tension: 2.5
  });
  var arrow = new Kinetic.Line({
    points: [
      middleX + (10*Math.cos(angle+(Math.PI*(90+135)/180))), middleY - (10*Math.sin(angle+(Math.PI*(90+135)/180))),
      middleX, middleY,
      middleX + (10*Math.cos(angle+(Math.PI*(90-135)/180))), middleY - (10*Math.sin(angle+(Math.PI*(90-135)/180)))
    ],
    stroke: 'black',
    strokeWidth: 2,
    lineJoin: 'round'
  });

  this.add(spline);
  this.add(arrow);
}

Comme quoi, un petit peu de trigonométrie, ça ne fait jamais de mal.

Posted in IT | Tagged , | 1 Comment

Ils sont là, tout chaud et tout beau !

Quoi donc ? Les sucreries de Saint-Nicolas ? 😮
Non, les attestations Coursera pour le cours de Scala !
Bon, en fait, je tarde car ils sont là depuis 1 semaine …

Mais bon, ce n’est pas une raison pour passer cela sous silence car même si suivre le cours d’un langage comme Scala est déjà plaisant en soi, recevoir dans sa boite mail le pdf attestant que vous n’étiez pas trop mauvais est un petit bonus non négligeable (surtout pour l’égo). 🙂

Malgré quelques soucis de cotations qui ne m’ont pas affectés (certains Assignements avaient un pro-rata plus important que d’autres sans que cela soit spécifié), les certifications sont arrivées environ 1 semaine et demi après la fin du cours (c’est à dire 3 semaines et demi après la mise en lignes des dernières vidéos).

Enfin bref, le voilà. 😉

Par contre, il est clair que niveau esthétique, c’est pas top et, apparemment, les personnes ayant des noms avec des caractères spéciaux ont vu les caractères concernés remplacés par un espace…
Pour un MOOC international, c’est pas la gloire et franchement dommage pour les personnes concernées.

Pour info, les grades ont été recalculés en 1 semaine donc personne n’a été lésé sur le résultat attendu.

Posted in Uncategorized | Leave a comment