IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

Rendre mon texte sous forme sphérique


Sujet :

JavaScript

  1. #1
    Nouveau candidat au Club
    Homme Profil pro
    Enseignant
    Inscrit en
    Février 2013
    Messages
    1
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Tunisie

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Février 2013
    Messages : 1
    Par défaut Rendre mon texte sous forme sphérique
    J'ai une base de données dont le champs NOM doit être affiché sur ma page web mais sous forme sphérique à chaque fois qu'un nom est choisi, comme le montre l'image suivante, j'ai pas trouvé un moyen pour le programmer, est-il possible avec SVG par exemple ?
    Nom : CircleWord.png
Affichages : 465
Taille : 43,6 Ko

  2. #2
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 198
    Par défaut
    Bonjour,
    J'ai une base de données dont le champs NOM doit être affiché sur ma page web
    Ces noms sont-ils « fixes » ou proviennent-ils d'une source extérieure ?

    • Si les noms sont « fixes », tu peux toujours créer les images correspondantes, application d'un texte sur une sphère avec PhotoShop par exemple.

    • Si les noms sont d'origine externe alors la solution de la création via un script est tout à fait réalisable en passant par un élément <canvas> dans lequel tu affiches ton texte, puis tu appliques le filtre, à faire ou à trouver, en triturant les datas de ton <canvas>.

    Il existe peut une bibliothèque graphique côté serveur qui fait le job.


    est-il possible avec SVG par exemple ?
    cela ne va pas être simple compte-tenu de la complexité de la gestion des lettres et de leur position dans le mot.

  3. #3
    Membre émérite
    Femme Profil pro
    Autre
    Inscrit en
    Janvier 2017
    Messages
    340
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Janvier 2017
    Messages : 340
    Par défaut
    Bonjour,
    Citation Envoyé par NoSmoking Voir le message
    la solution de la création via un script est tout à fait réalisable en passant par un élément <canvas> dans lequel tu affiches ton texte, puis tu appliques le filtre, à faire ou à trouver, en triturant les datas de ton <canvas>.
    Je viens d'essayer de concevoir cela : pas facile !
    J'ai fait en sorte que le contour rectangulaire du mot se transforme en cercle, et qu'à l'intérieur, on utilise des ellipses.
    Je place chaque trait vertical du texte source sur une ellipse dont le rayon "y" augmente au fur et à mesure qu'on se rapproche du centre (le rayon "y" est supérieur au rayon du cercle).
    Le rayon "y" est calculé en fonction du "x" du trait et du point du contour rectangulaire projeté sur le cercle.
    Rien de simple, à moins qu'il n'y ait une autre approche ?

    Il y aurait au moins deux améliorations à faire à mon code :
    - Lisser davantage le rendu.
    - Trouver le point correspondant du mot à partir d'un point du cercle, et non l'inverse comme j'ai fait. Cela permettrait de partir sur une taille de texte beaucoup plus petite et de gagner beaucoup en temps d'exécution.

    Voilà mon code qui est très expérimental en l'état :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    <canvas id="texteCercle" width="260" height="260"></canvas>
    <script>
    "use strict";
    function rendreTexteCercle(cvsDest,texte,police,couleur,taille,coefZoom,attenuationZoomCentre)
    	{
    	var cvs;
    	var ctx;
    	var angle;
    	var i;
    	var j;
    	var largeurTexte;
    	var hauteurTexte;
    	var rayon;
    	var rx;
    	var ry;
    	var x;
    	var x2;
    	var xCentre;
    	var xTexte;
    	var xTexte_droite;
    	var y;
    	var y2;
    	var yCentre;
    	var yTexte;
    	var yTexte_bas;
    	var pixels;
    	var pixels2;
     
    	cvs=document.createElement("canvas");
    	cvs.width=texte.length*taille;
    	cvs.height=taille*2;
    	ctx=cvs.getContext("2d");
    	ctx.font=taille+"px "+police;
    	ctx.fillStyle=couleur;
    	ctx.fillText(texte,0,taille);
    	largeurTexte=Math.ceil(ctx.measureText(texte).width+taille);
    	hauteurTexte=cvs.height;
    	pixels=ctx.getImageData(0,0,largeurTexte,hauteurTexte).data;
    	xTexte=-1;
    	while(++xTexte<largeurTexte)
    		{
    		y=hauteurTexte;
    		while(--y>=0)
    			{
    			if(pixels[4*(xTexte+y*largeurTexte)+3]!==0)
    				{
    				break;
    				}
    			}
    		if(y>=0)
    			{
    			break;
    			}
    		}
    	xTexte_droite=largeurTexte;
    	while(--xTexte_droite>=0)
    		{
    		y=hauteurTexte;
    		while(--y>=0)
    			{
    			if(pixels[4*(xTexte_droite+y*largeurTexte)+3]!==0)
    				{
    				break;
    				}
    			}
    		if(y>=0)
    			{
    			break;
    			}
    		}
    	yTexte=-1;
    	while(++yTexte<hauteurTexte)
    		{
    		x=largeurTexte;
    		while(--x>=0)
    			{
    			if(pixels[4*(x+yTexte*largeurTexte)+3]!==0)
    				{
    				break;
    				}
    			}
    		if(x>=0)
    			{
    			break;
    			}
    		}
    	yTexte_bas=hauteurTexte;
    	while(--yTexte_bas>=0)
    		{
    		x=largeurTexte;
    		while(--x>=0)
    			{
    			if(pixels[4*(x+yTexte_bas*largeurTexte)+3]!==0)
    				{
    				break;
    				}
    			}
    		if(x>=0)
    			{
    			break;
    			}
    		}
    	largeurTexte=xTexte_droite-xTexte;
    	hauteurTexte=yTexte_bas-yTexte;
    	pixels=ctx.getImageData(xTexte,yTexte,largeurTexte,hauteurTexte).data;
    	yCentre=xCentre=rayon=cvsDest.width*0.5;
    	pixels2=new Uint8ClampedArray(rayon*rayon*16);
    	i=pixels.length;
    	while((i-=4)>=0)
    		{
    		x=((i/4)%largeurTexte)*rayon*2/largeurTexte-xCentre;
    		y=Math.floor((i/4)/largeurTexte)*rayon*2/hauteurTexte-yCentre;
    		angle=Math.atan2(rayon,x);
    		x2=rayon*Math.cos(angle);
    		y2=rayon*Math.sin(angle);
    		rx=Math.abs(x);
    		rx=rayon*Math.pow(rx/rayon,coefZoom+Number(attenuationZoomCentre)*(1-coefZoom)*(1-rx/rayon));
    		ry=y2/Math.sin(Math.acos(x2/rx));
    		angle=Math.atan2(y,x*ry/rx);
    		j=4*(xCentre+Math.round(rx*Math.cos(angle))+(yCentre+Math.round(ry*Math.sin(angle)))*rayon*2);
    		if(pixels2[j+3]===0)
    			{
    			if(pixels[i+3]!==0)
    				{
    				pixels2[j]=pixels[i];
    				pixels2[j+1]=pixels[i+1];
    				pixels2[j+2]=pixels[i+2];
    				pixels2[j+3]=pixels[i+3];
    				}
    			}
    		else
    			{
    			pixels2[j+3]=(pixels2[j+3]+pixels[i+3])*0.5; //j'ai simplifié, car, idéalement, pour un point donné, il faudrait faire une moyenne des points du texte source qui arrivent sur ce point
    			}
    		}
    	cvsDest.getContext("2d").putImageData(new ImageData(pixels2,rayon*2),0,0);
    	}
    rendreTexteCercle(
    	document.getElementById("texteCercle"),
    	"CIRCLE",
    	"impact",
    	"#0099FF",
    	400, //si taille trop petite, le remplissage ne sera pas plein (augmenter la taille si des points blancs ou craquelures apparaissent)
    	0.8, //[0,1], plus la valeur est petite plus la déformation est forte
    	true
    	);
    </script>
    4/8, 9h19 : Correction et optimisation du calcul des limites du texte initial.

  4. #4
    Membre très actif
    Homme Profil pro
    bricoleur par les mots
    Inscrit en
    Avril 2015
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 80
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : bricoleur par les mots
    Secteur : Distribution

    Informations forums :
    Inscription : Avril 2015
    Messages : 734
    Par défaut
    expérimentale mais fonctionnel

  5. #5
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 198
    Par défaut
    Expérimental, certes, mais déjà bien abouti.

    Récupération de la taille de la zone d'affichage, on peut également créer un élément temporaire, <span> par exemple afin de récupérer les dimensions au plus près.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      cvs = document.createElement("canvas");
      // NOTA : le paramètre taille n'est pas utilisée, on récupère la taille du canvas destination
      // Calcul dimensions au mieux
      const FONT_SIZE = cvsDest.height *1.54;  // 1.54 valeur empirique se rapproche de Loralina
      const oSpan = document.createElement("SPAN");
      oSpan.style.fontSize = FONT_SIZE + "px";
      oSpan.style.fontFamily = police;
      oSpan.style.position = "absolute";
      oSpan.style.top = "-2em";
      oSpan.textContent = texte;
      // ajout a document pour récup. dimensions
      document.body.appendChild(oSpan);
      const MAX_LARGEUR = oSpan.offsetWidth;
      const MAX_HEIGHT = oSpan.offsetHeight;
      // supprime plus besoin
      document.body.removeChild(oSpan);
    on initialise ensuite le « context2d » et on récupère les données
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
      // init context2d
      cvs.width = MAX_LARGEUR
      cvs.height = FONT_SIZE;
      ctx = cvs.getContext("2d");
      ctx.textBaseline = "top";         // (!) on place le texte en haut
      ctx.font = FONT_SIZE + "px " + police;
      ctx.fillStyle = couleur;
      ctx.fillText(texte, 0, 0);
      // récup. des données
      pixels = ctx.getImageData(0, 0, MAX_LARGEUR, MAX_HEIGHT).data;
    Remarques :
    • Je n'ai pas utilisé le paramètre taille mais les dimensions de l'élément destination ;
    • La taille de la font est empirique et se rapproche de ce que tu obtiens ;
    • On place le texte en position haute sinon cela ne fonctionne pas ;
    • Je ne suis pas sûr que l'on gagne grand chose mais on récupère au mieux.


    Récupération de la zone d'intérêt, là où il y a du texte, j'avais par le passé fait quelque chose du même type que j'ai adapté à ton code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
      // ajustage zone d'intérêt
      let px;
      let col;
      let offsetY;
      const rectTexte = {};
      const bufferLineSize = (MAX_LARGEUR << 2);
      // recherche 1st line à partir du début  
      px = 3;
      while (pixels[px] === 0) {
        px += 4;
      }
      rectTexte.top = parseInt(px / bufferLineSize, 10);
      // recherche last line à partir de la fin
      px = pixels.length - 1;
      while (pixels[px] === 0) {
        px -= 4;
      }
      rectTexte.bottom = parseInt(px / bufferLineSize, 10);
      const MAX_DATA = rectTexte.bottom * bufferLineSize;
      // recherche bord gauche
      offsetY = rectTexte.top * bufferLineSize;
      col = 3;
      px = offsetY + col;
      while (pixels[px] === 0) {
        px += bufferLineSize;
        if (px > MAX_DATA) {
          px = (col += 4) + offsetY;
          // il faut bien s'arrêter un jour
          if (col > bufferLineSize) break;
        }
      }
      rectTexte.left = col >> 2;
      // recherche bord droit
      col = bufferLineSize - 1;
      px = offsetY + col;
      while (pixels[px] === 0) {
        px += bufferLineSize;
        if (px > MAX_DATA) {
          px = (col -= 4) + offsetY;
          // il faut bien s'arrêter un jour
          if (col < 0) break;
        }
      }
      rectTexte.right = col >> 2;
    et pour que cela soit compatible avec ton code je rajoutes
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      xTexte = rectTexte.left;
      yTexte = rectTexte.top;
      largeurTexte = rectTexte.right - rectTexte.left;
      hauteurTexte = rectTexte.bottom - rectTexte.top;
    Traitement des données.

    Je te fais confiance pour tout ce qui concerne la trigo, je n'ai plus le cerveau en état pour ce genre de chose.

    Il est avantageux de pré-calculer les valeurs attendu que le même calcul se répète pour chaque ligne et chaque colonne, cela peut ressembler à cela
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      // précalcul pour mise en cache
      const _2r = rayon * 2;
      const coef = Number(attenuationZoomCentre) * (1 - coefZoom);
      const rapportX = _2r / largeurTexte;
      const rapportY = _2r / hauteurTexte;
      // précalcul
      const tabAtan2 = [];
      const tabX2 = [];
      const tabY2 = [];
      for (col = 0; col < largeurTexte; col += 1) {
        angle = Math.atan2(rayon, (col * rapportX) - xCentre);
        tabAtan2.push(angle);
        tabX2.push(rayon * Math.cos(angle));
        tabY2.push(rayon * Math.sin(angle));
      }
      // TODO faire la même chose dans l'autre sens
    Ceci entraine des changements dans la boucle de traitement comme suit
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
      while ((i -= 4) > 0)
      {
        col = i >> 2;
        //x = ((i / 4) % largeurTexte) * rayon * 2 / largeurTexte - xCentre;
        //y = Math.floor((i / 4) / largeurTexte) * rayon * 2 / hauteurTexte - yCentre;
        x = ((col) % largeurTexte) * rapportX - xCentre;
        y = Math.floor((col) / largeurTexte) * rapportY - yCentre;
        //angle = Math.atan2(rayon, x);
        //x2 = rayon * Math.cos(angle);
        //y2 = rayon * Math.sin(angle);
        col %= largeurTexte;
        angle = tabAtan2[col];
        x2 = tabX2[col];
        y2 = tabY2[col];
        //rx = rayon * Math.pow(Math.abs(x) / rayon, coefZoom + Number(attenuationZoomCentre) * (1 - coefZoom) * (1 - Math.abs(x) / rayon));
        rx = rayon * Math.pow(Math.abs(x) / rayon, coefZoom + coef * (1 - Math.abs(x) / rayon));
        ry = y2 / Math.sin(Math.acos(x2 / rx));
        angle = Math.atan2(y, x * ry / rx);
        //j = 4 * (xCentre + Math.round(rx * Math.cos(angle)) + (yCentre + Math.round(ry * Math.sin(angle))) * rayon * 2);
        j = 4 * (xCentre + Math.round(rx * Math.cos(angle)) + (yCentre + Math.round(ry * Math.sin(angle))) * _2r);
        if (pixels2[j + 3] === 0)
        {
          if (pixels[i + 3] !== 0)
          {
            pixels2[j] = pixels[i];
            pixels2[j + 1] = pixels[i + 1];
            pixels2[j + 2] = pixels[i + 2];
            pixels2[j + 3] = pixels[i + 3];
          }
        }
        else
        {
          pixels2[j + 3] = (pixels2[j + 3] + pixels[i + 3]) * 0.5;
        }
      }
      cvsDest.getContext("2d").putImageData(new ImageData(pixels2, rayon * 2), 0, 0);
    Remarques :
    • L'optimisation des calculs serait également à faire pour les lignes ;
    • Le gain de vitesse est bien plus conséquent, même si je ne l'ai pas franchement chiffré.


    Au final, je remets ci-dessous les bouts de code assemblés
    HTML :
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    <style>
    #texteCercle {
      border: 1px solid #069;
      border-radius: 50%;
    }
    </style>
    <canvas id="texteCercle" width="260" height="260"></canvas>
    Script :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    "use strict";
    function rendreTexteCercle(cvsDest, texte, police, couleur, taille, coefZoom, attenuationZoomCentre)
    {
      var cvs;
      var ctx;
      var angle;
      var i;
      var j;
      var largeurTexte;
      var hauteurTexte;
      var rayon;
      var rx;
      var ry;
      var x;
      var x2;
      var xCentre;
      var xTexte;
      var xTexte_droite;
      var y;
      var y2;
      var yCentre;
      var yTexte;
      var yTexte_bas;
      var pixels;
      var pixels2;
      const deb = new Date().getTime();
     
      cvs = document.createElement("canvas");
      // NOTA : le paramètre taille n'est pas utilisée, on récupère la taille du canvas destination
      // Calcul largeur au mieux
      const FONT_SIZE = cvsDest.height *1.54;  // 1.54 valeur empirique se rapproche de Loralina
      const oSpan = document.createElement("SPAN");
      oSpan.style.fontSize = FONT_SIZE + "px";
      oSpan.style.fontFamily = police;
      oSpan.style.position = "absolute";
      oSpan.style.top = "-2em";
      oSpan.textContent = texte;
      // ajout a document pour récup. dimensions
      document.body.appendChild(oSpan);
      const MAX_LARGEUR = oSpan.offsetWidth;
      const MAX_HEIGHT = oSpan.offsetHeight;
      // supprime plus besoin
      document.body.removeChild(oSpan);
     
      // init context2d
      cvs.width = MAX_LARGEUR
      cvs.height = FONT_SIZE;
      ctx = cvs.getContext("2d");
      ctx.textBaseline = "top";         // (!) on place le texte en haut
      ctx.font = FONT_SIZE + "px " + police;
      ctx.fillStyle = couleur;
      ctx.fillText(texte, 0, 0);
      // récup. des données
      pixels = ctx.getImageData(0, 0, MAX_LARGEUR, MAX_HEIGHT).data;
     
      // ajustage zone d'intérêt
      let px;
      let col;
      let offsetY;
      const rectTexte = {};
      const bufferLineSize = (MAX_LARGEUR << 2);
      // recherche 1st line à partir du début
      px = 3;
      while (pixels[px] === 0) {
        px += 4;
      }
      rectTexte.top = parseInt(px / bufferLineSize, 10);
      // recherche last line à partir de la fin
      px = pixels.length - 1;
      while (pixels[px] === 0) {
        px -= 4;
      }
      rectTexte.bottom = parseInt(px / bufferLineSize, 10);
      const MAX_DATA = rectTexte.bottom * bufferLineSize;
      // recherche bord gauche
      offsetY = rectTexte.top * bufferLineSize;
      col = 3;
      px = offsetY + col;
      while (pixels[px] === 0) {
        px += bufferLineSize;
        if (px > MAX_DATA) {
          px = (col += 4) + offsetY;
          // il faut bien s'arrêter un jour
          if (col > bufferLineSize) break;
        }
      }
      rectTexte.left = col >> 2;
      // recherche bord droit
      col = bufferLineSize - 1;
      px = offsetY + col;
      while (pixels[px] === 0) {
        px += bufferLineSize;
        if (px > MAX_DATA) {
          px = (col -= 4) + offsetY;
          // il faut bien s'arrêter un jour
          if (col < 0) break;
        }
      }
      rectTexte.right = col >> 2;
     
      xTexte = rectTexte.left;
      yTexte = rectTexte.top;
      largeurTexte = rectTexte.right - rectTexte.left;
      hauteurTexte = rectTexte.bottom - rectTexte.top;
     
      console.log("Time (1):", new Date().getTime() -deb);
      console.log("rectTexte :", rectTexte);  
      console.log("largeurTexte :", largeurTexte);
      console.log("hauteurTexte :", hauteurTexte);
     
      pixels = ctx.getImageData(xTexte, yTexte, largeurTexte, hauteurTexte).data;
     
      yCentre = xCentre = rayon = cvsDest.width * 0.5;
     
      pixels2 = new Uint8ClampedArray(rayon * rayon * 16);
      i = pixels.length;
      console.log("nbr datas :", i);
     
      // précalcul pour mise en cache
      const _2r = rayon * 2;
      const coef = Number(attenuationZoomCentre) * (1 - coefZoom);
      const rapportX = _2r / largeurTexte;
      const rapportY = _2r / hauteurTexte;
      // précalcul
      const tabAtan2 = [];
      const tabX2 = [];
      const tabY2 = [];
      for (col = 0; col < largeurTexte; col += 1) {
        angle = Math.atan2(rayon, (col * rapportX) - xCentre);
        tabAtan2.push(angle);
        tabX2.push(rayon * Math.cos(angle));
        tabY2.push(rayon * Math.sin(angle));
      }
      // TODO faire la même chose pour les lignes
     
      while ((i -= 4) > 0)
      {
        col = i >> 2;
        //x = ((i / 4) % largeurTexte) * rayon * 2 / largeurTexte - xCentre;
        //y = Math.floor((i / 4) / largeurTexte) * rayon * 2 / hauteurTexte - yCentre;
        x = ((col) % largeurTexte) * rapportX - xCentre;
        y = Math.floor((col) / largeurTexte) * rapportY - yCentre;
        //angle = Math.atan2(rayon, x);
        //x2 = rayon * Math.cos(angle);
        //y2 = rayon * Math.sin(angle);
        col %= largeurTexte;
        angle = tabAtan2[col];
        x2 = tabX2[col];
        y2 = tabY2[col];
        //rx = rayon * Math.pow(Math.abs(x) / rayon, coefZoom + Number(attenuationZoomCentre) * (1 - coefZoom) * (1 - Math.abs(x) / rayon));
        rx = rayon * Math.pow(Math.abs(x) / rayon, coefZoom + coef * (1 - Math.abs(x) / rayon));
        ry = y2 / Math.sin(Math.acos(x2 / rx));
        angle = Math.atan2(y, x * ry / rx);
        //j = 4 * (xCentre + Math.round(rx * Math.cos(angle)) + (yCentre + Math.round(ry * Math.sin(angle))) * rayon * 2);
        j = 4 * (xCentre + Math.round(rx * Math.cos(angle)) + (yCentre + Math.round(ry * Math.sin(angle))) * _2r);
        if (pixels2[j + 3] === 0)
        {
          if (pixels[i + 3] !== 0)
          {
            pixels2[j] = pixels[i];
            pixels2[j + 1] = pixels[i + 1];
            pixels2[j + 2] = pixels[i + 2];
            pixels2[j + 3] = pixels[i + 3];
          }
        }
        else
        {
          pixels2[j + 3] = (pixels2[j + 3] + pixels[i + 3]) * 0.5;
        }
      }
      cvsDest.getContext("2d").putImageData(new ImageData(pixels2, rayon * 2), 0, 0);
     
      console.log("Time (End):", new Date().getTime() -deb);
    }
    rendreTexteCercle(document.getElementById("texteCercle"),
      "CIRCLE",
      "impact",
      "#0099FF",
      400,        //si taille trop petite, le remplissage ne sera pas plein
      .8,         //[0,1], plus la valeur est petite plus la déformation est forte
      true);
    Pour conclure, encore bravo à toi Loralina pour ton code qui m'a donné l'envie de me remuer les neurones, exception faite de la trigo bien sûr, là je vais prendre un cachet.

    Je ne résiste pas à l'envie de vous mettre deux liens intéressants


    [EDIT] correction de quelques fautes après relecture !

  6. #6
    Membre émérite
    Femme Profil pro
    Autre
    Inscrit en
    Janvier 2017
    Messages
    340
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Janvier 2017
    Messages : 340
    Par défaut
    Bonjour,
    Merci melka one. Ca marche oui, mais mon script n'est pas joli joli.

    Merci NoSmoking pour toutes ces idées.
    A noter qu'entretemps, j'avais modifié mon message pour apporter quelques retouches au code.

    Citation Envoyé par NoSmoking Voir le message
    on peut également créer un élément temporaire, <span> par exemple afin de récupérer les dimensions au plus près.
    L'idée paraît bonne.

    Citation Envoyé par NoSmoking Voir le message
    Récupération de la zone d'intérêt
    Il est vrai que ta version présente deux optimisations de plus que ma seconde version (pour rappel, la première bouclait sur tous les points et n'était pas tout à fait correcte).

    Citation Envoyé par NoSmoking Voir le message
    Je te fais confiance pour tout ce qui trigo
    J'aurais dû mettre un schéma et des commentaires.
    Sans ça, mon code paraît complexe et ne donne pas envie d'être analysé.
    Voilà un schéma :
    Nom : schema.png
Affichages : 285
Taille : 23,0 Ko

    Citation Envoyé par NoSmoking Voir le message
    Il est avantageux de pré-calculer les valeurs
    Oui et justement ta version se rapproche d'une optimisation majeure à laquelle j'avais pensé : traiter les traits verticaux un à un (trait rouge sur le schéma).
    Il s'agirait de boucler sur X puis sur Y (j'ajoute une précision (21h43) : la boucle sur Y serait dans la boucle sur X).
    Pour chaque X, on définit les rayons X et Y de l'ellipse et la boucle sur Y placerait tous les points du trait vertical sur la partie correspondante de l'ellipse.
    La boucle sur X peut s'arrêter à la moitié de la largeur et on utilise la symétrie pour traiter le trait opposé.


    J'en profite pour dire que j'ai pensé à une variante que je préfère concernant mon effet de loupe qui accentue la déformation :
    Actuellement, on a un coefficient de 0 à 1 avec un paramètre booléen pour atténuer la déformation au centre.
    Voici la nouvelle version :
    - Le coefficient doit être supérieur ou égal à 1.
    - Plus besoin du booléen pour atténuer, car la déformation au centre sera plus douce.

    Au niveau du code, il faut remplacer la ligne avec le Math.pow par ceci :
    rx=rayon*(1-Math.pow(1-Math.abs(x)/rayon,coefZoom));

    Dans l'appel de la fonction, au lieu de 0.8, on peut mettre 1.12.
    Le résultat sera vraiment un cran au-dessus.


    Par ailleurs, j'attire l'attention sur un commentaire que j'avais rajouté :
    pixels2[j+3]=(pixels2[j+3]+pixels[i+3])*0.5; //j'ai simplifié, car, idéalement, pour un point donné, il faudrait faire une moyenne des points du texte source qui arrivent sur ce point
    Pour cela, au minimum, il faut mémoriser le nombre de points qui arrivent sur un point donné.
    On aurait peut-être même un meilleur lissage en n'arrondissant pas tout de suite les valeurs et en considérant la distance entre le point théorique et les différents points arrondis correspondants...

    Citation Envoyé par NoSmoking Voir le message
    [*]The world par Gérard Ferrandez, que je « suivais » à une époque et qui n'utilise pas le traitement sur les pixels ;
    Le rendu est esthétique.
    Sans regarder le code, le visuel semble correspondre au mappage d'une image sur une sphère.
    Si c'est bien ça, c'est un principe différent dans la mesure où seule une partie de l'image est visible alors que dans notre cas, il s'agit de tout mettre dans le cercle.
    Cela dit, il aurait peut-être été possible de partir sur une approche 3D, par exemple en réduisant d'abord tous les traits sur la hauteur pour que ça rentre dans le cercle, puis en faisant une projection du disque sur une sphère.
    En faisant varier notamment la distance focale, on pourrait ajuster l'effet de loupe.
    A voir le résultat quand même...

  7. #7
    Membre émérite
    Femme Profil pro
    Autre
    Inscrit en
    Janvier 2017
    Messages
    340
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Janvier 2017
    Messages : 340
    Par défaut
    Bonjour,
    Je reviens sur la sphère :
    Déjà, je ne pense pas que mon idée donnerait un bon résultat.
    Ensuite, la problématique de conception n'est pas tellement liée au fait qu'une partie de l'image serait cachée, car on peut très bien afficher l'image sur une demi-sphère, sans rien cacher donc, mais plus au fait que le mappage classique d'une image sur une sphère réunit les points du haut en un seul, de même en bas, ce qui donnerait une déformation très différente de l'effet recherché.
    Il faudrait donc envisager un algorithme différent.
    Là où je pense que la 3D et la sphère pourraient être utilisées plus facilement, c'est pour faire un effet de loupe comparable à mon Math.pow.

    Sinon, j'ai visionné de nouveau le lien de NoSmoking et, en observant mieux, je m'aperçois que ce n'est pas du tout le mappage classique !
    J'ai survolé un peu le code et j'ai l'impression que c'est juste une déformation, avec un effet style 3D, d'un disque à plat.
    Le code ne convertirait donc pas une image rectangulaire, mais déjà circulaire à la base (bon, ce n'est pas une image, mais des points, mais c'est pareil).
    A confirmer toutefois.
    Il est possible que les formules de calcul utilisées permettent d'effectuer un effet de loupe.

    Je termine en ajoutant un comparatif des deux versions de Math.pow en appliquant mon script à une image :
    Nom : effetLoupe.png
Affichages : 254
Taille : 34,8 Ko
    A vrai dire, je n'ai pas vraiment analysé pourquoi ma nouvelle formule donnait un résultat équilibré, mais ça semble bien être le cas.
    Concernant la ligne blanche verticale au milieu, je crois que c'est lié à un cas de division par 0 que je n'ai pas géré (à moins que ce ne soit un problème d'arrondi).

Discussions similaires

  1. Réponses: 4
    Dernier message: 15/11/2010, 10h32
  2. [Généralités] Rendre un logiciel sous forme de shareware
    Par omasabik dans le forum WinDev
    Réponses: 1
    Dernier message: 10/01/2010, 09h47
  3. Ecrire du texte sous forme de pixels dans un buffer
    Par kikekoikan dans le forum Windows
    Réponses: 12
    Dernier message: 22/12/2008, 10h22
  4. [Console] Affichage de texte sous forme matricielle
    Par krachik dans le forum Général Java
    Réponses: 5
    Dernier message: 01/05/2007, 21h12

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo