Rétro-conception d’un logo

Suite à la refonte du logo du BreizhCamp, il y a un peu plus de deux ans, il m’est venu l’envie d’en réaliser une version générée de manière procédurale. Ne me demandez pas pourquoi. Moi-même, je n’en sais rien.

Peut-être son aspect très épuré m’a-t-il laisser penser que la tâche serait simple.

Ça ne doit pas être bien compliqué à coder…
— Moi
un peu optimiste en ce début 2015

À l’époque, j’avais ainsi passé quelques soirées, d’abord à reproduire le logo sous la forme d’un dessin technique, puis à en extraire quelques formules de trigo avant de finalement implémenter tout ça à la va-vite dans un plunk.

Aujourd’hui, je saisis l’opportunité de devoir alimenter ce tout nouveau [1] blog pour reprendre ce travail au propre.

Le dessin technique

Appelé aussi dessin pour ceux qui aiment faire de jolies choses, mais ne savent pas dessiner.

Le logo du BreizhCamp est un triskell stylisé, composé de trois gouttes colorées.

Logo du BreizhCamp
Figure 1. Le logo du BreizhCamp — vue d’artiste

Ça, c’est la vision de l’artiste. On devine quelques irrégularités dans les arcs, les gouttes ne sont pas identiques, etc. Et c’est très bien comme ça !

Je ne suis pas un artiste. Je vais adopter un point de vue plus pragmatique pour la modélisation du logo. Ma version sera ainsi constituée de trois gouttes identiques, chacune composée de trois portions de cercles : la tête, le dos et le ventre.

Logo du BreizhCamp en DAO
Figure 2. Le logo du BreizhCamp — vue d’ingénieur

La figure de base pour la réalisation du triskell est un triangle équilatéral dont chaque sommet est le centre de l’arc de cercle constituant la tête d’une goutte.

Logo du BreizhCamp en DAO
Figure 3. Modélisation du logo

Un peu de maths…

L’objectif ici est de défricher le terrain avant de travailler sereinement sur une implémentation. Nous allons passer en revue tous les éléments nécessaires au tracé du triskell.

Première dimension à mettre en évidence : l’écart entre les gouttes du triskell. Il est représenté par le paramètre \(\Delta\). Il représente concrètement la distance entre les sommets de deux triangles :

  • Le triangle extérieur \(T_\Delta\), figure de base du triskell, connecte les centres des 3 têtes de gouttes. Il a des côtés de longueur \(2\left(r_1+\Delta\cos\left(\frac{\pi}{6}\right)\right)\).

  • Le triangle intérieur \(T_0\) est le cas particulier où \(\Delta=0\). Dans cette configuration, il n’y a pas d’écart entre les gouttes. Elles sont tangentes. La longueur d’un de ses côtés est égale à \(2r_1\).

Dans l’implémentation, \(\Delta\) sera utilisé en tant que paramètre d’entrée pour générer le triskell.

L’angle \(\theta\) sert à orienter la première goutte du triskell, avant de dessiner les suivantes, et définit ainsi l’orientation globale du dessin.

\(\theta\) pourra aussi être utilisé en tant que paramètre à la génération du triskell.

Les trois gouttes constituant le triskell étant construites de la même manière, seule l’une d’entre elles est annotée sur la Figure 3.

Regardons à présent comment est constituée cette goutte.

La tête

La tête de la goutte est la portion du cercle \(C_1\), de centre \(B\), entre les points \(A\) et \(C\). Le rayon de \(C_1\) est \(r_1\).

\(r_1\) sera également utilisé en tant que paramètre d’entrée.

Le dos

Le dos de la goutte est une portion du cercle \(C_2\), de centre \(O\) et de rayon \(r_2\).

\(C_2\) est le cercle circonscrit au triskell, tangent aux trois gouttes.

Le dos est la portion entre les points \(A\), jonction avec la tête, et le point \(E\), jonction avec le ventre.

Le rayon \(r_2\) peut être exprimé en fonction de paramètres déjà établis. Il est égal au rayon \(r_1\) ajouté à la distance \(\|\vec{OB}\|\). Ce qui donne :

\[r_2=r_1+\Delta+\frac{r_1}{\cos\left(\frac{\pi}{6}\right)}\]

À ceux qui demanderaient d’où ça sort, je répondrais : SOHCAHTOA.

Le ventre

Le dos de la goutte est une portion du cercle \(C_3\), de centre \(D\) et de rayon \(r_3\).

C’est la portion entre le point \(C\), jonction avec la tête, et le point \(E\), jonction avec le dos.

\(r_3\) est la dernière valeur utilisée en paramètre d’entrée dans l’implémentation.

Pour former la goutte, le ventre et la tête doivent être tangents en \(C\). Pour cela, le centre de \(C_3\), \(D\), doit être sur la droite \((BC)\).

Le point \(E\) est un point d’intersection entre les cercles \(C_2\) et \(C_3\). Si \(C_2\) et \(C_3\) ne se croisent pas, c’est-à-dire si le rayon \(r_3\) est trop petit, impossible de terminer la figure.

Le cas \(r_{3_{min}}\)

Il existe un rayon minimum, \(r_{3_{min}}\) en deçà duquel le cercle \(C_3\) est inclu dans le cercle \(C_2\) et ne croise donc pas celui-ci.

Dans le cas particuler \(r_3=r_{3_{min}}\), le ventre de la goutte est tangent à la tête, mais aussi au dos.

Logo du BreizhCamp r3min
Figure 4. Aperçu du logo avec \(r_3 = r_{3_{min}}\)

Nous allons essayer de déterminer \(r_{3_{min}}\).

À première vue, \(r_{3_{min}}\) ne dépend que de \(r_1\) et \(r_2\) (qui lui-même dépend de \(r_1\) et \(\Delta\)). Prenons cette hypothèse et essayons de simplifier la résolution en nous plaçant dans le cas particulier \(\theta = 0\).

Modélisation pour trouver r3min
Figure 5. Modélisation pour déterminer \(r_{3_{min}}\)

Le point \(E\) étant le point de tangence entre deux cercles, il se situe sur la droite \((OD)\) passant par les centres des deux cercles.

Intéressons-nous au vecteur \(\vec{OE}\). Pour cela, introduisons l’angle \(\alpha\) entre \(\vec{OA}\) et \(\vec{OE}\).

On peut définir \(\vec{OE}\) de la manière suivante :

\[\vec{OE} \begin{pmatrix} r_2\cos(\alpha) \\ r_2\sin(\alpha) \end{pmatrix}\]

On a aussi :

\[\vec{OE}=\vec{OB}+\vec{BD}+\vec{DE}\]

Avec :

\[\vec{OB} \begin{pmatrix} r_2-r_1 \\ O \end{pmatrix} \\ \vec{BD} \begin{pmatrix} (r_1+r_{3_{min}})\cos\left(\frac{5\pi}{6}\right) \\ (r_1+r_{3_{min}})\sin\left(\frac{5\pi}{6}\right) \end{pmatrix} \\ \vec{DE} \begin{pmatrix} r_{3_{min}}\cos(\alpha) \\ r_{3_{min}}\sin(\alpha) \end{pmatrix}\]

Ce qui donne :

\[\vec{OE} \begin{pmatrix} r_2-r_1+(r_1+r_{3_{min}})\cos\left(\frac{5\pi}{6}\right) +r_{3_{min}}\cos(\alpha) \\ (r_1+r_{3_{min}})\sin\left(\frac{5\pi}{6}\right)+r_{3_{min}}\sin(\alpha) \end{pmatrix}\]

On peut alors poser le système d’équations suivant, avec deux inconnues, \(\alpha\) et \(r_{3_{min}}\) :

\[\begin{cases} r_2\cos(\alpha)=r_2-r_1+(r_1+r_{3_{min}})\cos\left(\frac{5\pi}{6}\right) +r_{3_{min}}\cos(\alpha) \\ r_2\sin(\alpha)=(r_1+r_{3_{min}})\sin\left(\frac{5\pi}{6}\right) +r_{3_{min}}\sin(\alpha) \end{cases} \\ \Leftrightarrow \begin{cases} (r_2-r_{3_{min}})\cos(\alpha)=r_2-r_1-\frac{(r_1+r_{3_{min}})\sqrt{3}}{2} \\ (r_2-r_{3_{min}})\sin(\alpha)=\frac{r_1+r_{3_{min}}}{2} \end{cases} \\ \Leftrightarrow \begin{cases} \left((r_2-r_{3_{min}})\cos(\alpha)\right)^2=\left(r_2-r_1 -\frac{(r_1+r_{3_{min}})\sqrt{3}}{2}\right)^2 \\ \left((r_2-r_{3_{min}})\sin(\alpha)\right)^2 =\left(\frac{r_1+r_{3_{min}}}{2}\right)^2 \end{cases}\]

En additionnant les deux équations, on obtient :

\[(r_2-r_{3_{min}})^2\left(\cos^2(\alpha)+\sin^2(\alpha)\right) = \left(r_2-r_1-\frac{(r_1+r_{3_{min}})\sqrt{3}}{2}\right)^2 + \left(\frac{r_1+r_{3_{min}}}{2}\right)^2 \\ \Leftrightarrow (r_2-r_{3_{min}})^2 = \left(r_2-r_1-\frac{(r_1+r_{3_{min}})\sqrt{3}}{2}\right)^2 + \left(\frac{r_1+r_{3_{min}}}{2}\right)^2\]

Nous sommes parvenu à éliminer \(\alpha\) de l’équation.

Avec un peu d’aide pour la résolution, on obtient :

\[r_{3_{min}} =\frac{(2+\sqrt{3})r_1(r_2-r_1)}{(2+\sqrt{3})r_1+(2-\sqrt{3})r_2}\]

Nous sommes à présent en mesure d’exprimer la contrainte \(r_{3_{min}}\) en fonction des rayons \(r_1\) et \(r_2\).

Les points

À présent que nous avons définis les rayons des trois portions de cercles constituant une goutte, \(r_1\), \(r_2\)et \(r_3\), nous allons déterminer les coordonnées des points nécessaires au tracé de celle-ci.

Nous nous concentrerons ici sur la goutte violette, telle qu’annotée sur la Figure 3.

A

Le point \(A\), départ de la tête se définit simplement à l’aide du rayon \(r2\) et de l’angle \(\theta\) :

\[A \begin{pmatrix} r_2\cos(\theta) \\ r_2\sin(\theta) \end{pmatrix}\]

B

De la même manière, \(B\), centre de l’arc de cercle constituant la tête, se définit comme suit :

\[B \begin{pmatrix} (r_2-r_1)\cos(\theta) \\ (r_2-r_1)\sin(\theta) \end{pmatrix} \Leftrightarrow B \begin{pmatrix} x_B \\ y_B \end{pmatrix}\]

C

Les coordonnées du point \(C\), fin de la tête et départ du ventre, sont également les coordonnées du vecteur \(\vec{OC}\). En posant \(\vec{OC}=\vec{OB}+\vec{BC}\), on obtient :

\[C \begin{pmatrix} x_B+r_1\cos(\theta + \frac{5\pi}{6}) \\ y_B+r_1\sin(\theta + \frac{5\pi}{6}) \end{pmatrix}\]

D

On peut procéder de façon similaire pour le point \(D\), centre de l’arc de cercle constituant le ventre. En posant \(\vec{OD}=\vec{OB}+\vec{BD}\) on obtient :

\[D \begin{pmatrix} x_B+(r_1+r_3)\cos(\theta + \frac{5\pi}{6}) \\ y_B+(r_1+r_3)\sin(\theta + \frac{5\pi}{6}) \end{pmatrix}\]

E

Déterminer les coordonnées du point \(E\) s’avère légèrement plus complexe. Il s’agit de trouver les coordonnées de l’un des points d’intersections entre les cercles \(C_2\) et \(C_3\).

Calcul des coordonnées du point E
Figure 6. Calcul des coordonnées du point E

J’ai choisi d’introduire le cercle \(C_3'\), symétrique du cercle \(C_3\) par rapport à la droite \((EE')\), avec \(E\) et \(E'\), points d’intersection entre les cercles \(C_2\) et \(C_3\).

Le cercle \(C_3'\) n’est pas indispensable, mais il fournira un support visuel plus accessible pour dérouler la résolution des coordonnées du point \(E\).

Soient [2] :

\[\begin{cases} a = \overline{ID'} = -\overline{ID} \\ b = \overline{OI} \\ h = \overline{IE} = -\overline{IE'} \\ d = \overline{OD'} = a + b \end{cases}\]

\(d\) est la distance entre les points \(O\) et \(D'\), respectivement centres des cercles \(C_2\) et \(C_3'\).

On a donc :

\[d = \sqrt{(x_D'-x_O)^2+(y_D'-y_O)^2}\]
\(O\) étant l’origine de notre référentiel, nous sommes en réalité dans le cas particulier où \(x_D'-x_O = x_D'\) et \(y_D'-y_O = y_D'\).

\(OEI\)et \(D'EI\) étant des triangles rectangles, grâce à ce bon vieux Pythagore on peut poser :

\[\begin{cases} a^2 + h^2 = r_3^2 \\ b^2 + h^2 = r_2^2 \end{cases}\]

En soustrayant les deux équations, on obtient :

\[a^2 - b^2 = r_3^2 - r_2^2 \\ \Leftrightarrow a^2 - (d-a)^2 = r_3^2 - r_2^2 \\ \Leftrightarrow a^2 - d^2 +2ad -a^2 = r_3^2 - r_2^2 \\ \Leftrightarrow a = \frac{r_3^2 - r_2^2 + d^2}{2d}\]

À présent que \(a\) est connu, reprenons la première équation du système pour déterminer \(h\) :

\[h = \sqrt{r_3^2 - a^2}\]

Cherchons à présent à calculer les coordonnées du point \(I\).

Application du théorème de Thalès
Figure 7. Application du théorème de Thalès

En invoquant cette fois notre vieux pote Thalès, on peut poser :

\[\frac{\overline{ID'}}{\overline{OD'}} = \frac{\overline{QD'}}{\overline{PD'}} = \frac{\overline{IQ}}{\overline{OP}} \\ \Leftrightarrow \frac{a}{d} = \frac{x_D' - x_I}{x_D' - x_O} = \frac{y_D' - y_I}{y_D' - y_O} \\ \Leftrightarrow \frac{a}{d} = \frac{x_D' - x_I}{x_D'-x_O} = \frac{y_D' - y_I}{y_D'-y_O}\]

Ce qui donne :

\[I \begin{pmatrix} x_D' - (x_D'-x_O) \frac{a}{d} \\ y_D' - (y_D'-y_O) \frac{a}{d} \end{pmatrix}\]

On remarque sur la Figure 7 que le triangle \(IER\) est une version modèle réduit du triangle \(ID'Q\), orientée de -90° par rapport au point \(I\) (un mathématicien parlerait plutôt d’une combinaison de rotation plane et d’homothétie).

Faisons à nouveau appel à Thalès et posons [3] :

\[\frac{\overline{IE}}{\overline{ID'}} = \frac{\overline{RE}}{\overline{QD'}} = \frac{\overline{IR}}{\overline{IQ}} \\ \Leftrightarrow \frac{h}{a} = \frac{y_E - y_I}{x_D' - x_I} = \frac{x_E - x_I}{y_D' - y_I}\]

On peut alors calculer les coordonnées du point \(E\) :

\[E \begin{pmatrix} (y_D' - y_I) \frac{h}{a} + x_I \\ (x_D' - x_I) \frac{h}{a} + y_I \end{pmatrix}\]
il existe une méthode sans doute plus simple pour calculer les points d’intersection entre deux cercles, à partir des équations de ceux-ci. Mais la méthode employée ici a le mérite de permettre de calculer explicitement, et sans ambigüité les coordonnées du seul point d’intersection qui nous intéresse, le point \(E\).

Comme évoqué plus tôt, le cercle \(C_3'\) a été introduit uniquement pour fournir un support visuel plus clair et ainsi simplifier la résolution du problème. Toutes les équations établies pour calculer les coordonnées du point \(E\) restent valables si on y substitue \(D\) à \(D'\) en travaillant directement avec le cercle \(C_3\).

En attendant de prouver ces dires au travers de l’implémentation à venir, avançons le résultat suivant :

\[E \begin{pmatrix} (y_D - y_I) \frac{h}{a} + x_I \\ (x_D - x_I) \frac{h}{a} + y_I \end{pmatrix}\]

avec :

\[\begin{cases} x_I = x_D - (x_D - x_O) \frac{a}{d} \\ y_I = y_D - (y_D - y_O) \frac{a}{d} \\ h = \sqrt{r_3^2 - a^2} \\ a = \frac{r_3^2 - r_2^2 + d^2}{2d} \\ d = \sqrt{(x_D-x_O)^2+(y_D-y_O)^2} \end{cases}\]

Conclusion

Nous avons à présent tous les éléments nécessaires au tracé d’une première goutte du triskell. Pour tracer les autres, il suffit d’appliquer les même calculs, mais avec un angle \(\theta\) différent.

On a un angle de 120° entre deux gouttes du triskell. Si le tracé de la première est calculé avec un angle \(\theta\), le tracé de la deuxième sera calculé avec \(\theta + \frac{2\pi}{3}\) et enfin la troisième avec \(\theta + \frac{4\pi}{3}\).

Nous verrons tout ceci dans un second article qui s’attardera sur les détails d’implémentation avec Vue.js.


1. nouveau dans le sens de premier, et non remplaçant d’une précédente version.
2. Pour ceux qui, comme moi, l’auraient oublié, la barre horizontale placée au dessus des couples de lettres désigne une mesure algébrique
3. On est d’accord, ça manque d’une démonstration sérieuse…