Créer un motif en utilisant le modulo

a marqué ce sujet comme résolu.
Banni

@ρττ J'aimerais ajouter la possibilité de faire avec les cercles, puis-je m'inspirer de ton algorithme ? Merci !

Oui, bien sûr. J'ai nettoyé le code un peu. L'idée est de transformer la description des deux points sur le cercle entre lesquels tracer. On nous donne une description sous la forme de deux angles (start, end), et on transforme cette description en une autre, sous la forme (m,θ) avec {m-θ, m+θ} = {start, end}. L'angle m est un milieu de start et end, et l'angle θ est la distance à parcourir des deux côtés de ce milieu. On joue ensuite avec les symétries (par exemple (m,θ) est équivalent à (m,-θ)) pour trouver une représentation avec θ positif et le plus petit possible, afin de tracer l'arc de cercle dans le bon sens (on le trace uniquement à l'intérieur du disque).

PS :

Cette image sera constituée d'un cercle, et de segments dans ce cercle.

Juste comme ça, un mot désignant ces segments est « corde ».

Merci, j'ai parfaitement compris comment tu fais ! Seulement, ce qui m'embête, c'est ces points start et end… Sachant que je part d'un modulo et d'un facteur, comment les déterminer ?

1
2
3
4
5
6
def build_circles(modulo, factor):
    # Calcule les coordonnées des cercles
    circles = []
    for index in range(modulo):
        start = ?
        end = ?

C'est sympa ça, tu pourrais montrer le code à un moment ?

Grimur

Bien sûr ! Voilà un petit repository. ;)

Démo de Trekke

+0 -0

Sympa ce petit atelier (tu devrais d'ailleurs ajouter ce tag au sujet). J'ai fait mon implémentation en Vala, avec Cairo. Je me suis même amusé à rajouter des paramètres pour la couleur du motif. J'ai parfois un petit bug, très bizarre : avec certains modulo, la création du contexte Cairo fait tout planter (alors qu'elle ne dépends pas du tout de ce paramètre). Mais sinon ça marche et c'est très zouli.

Table de 64 modulo 97

Table de 68 module 87

Et si jamais ça intéresse quelqu'un, j'ai posté mon code. Je vais essayer de voir si je peut générer une vidéo maintenant.

Cette image sera constituée d'un cercle, et de segments dans ce cercle.

Juste comme ça, un mot désignant ces segments est « corde ».

ρττ

Je l'ai changé. J'ai juste gardé segment une fois, le temps d'introduire le terme de corde. C'est vrai qu'ici, utiliser le bon terme ne bloquera absolument pas les débutants.

J'ai aussi rajouté quelques détails sur ton implémentation avec des arcs de cercle. Je ne voulais pas donner les équations, donc dîtes-moi si ça vous semble suffisant (et si je ne me suis pas trompé). Je me suis aussi permis de reprendre une de tes figures (en te donnant le crédit). Si ce n'est pas ok, dis-le moi, et je l'enleverai.

Banni

Sachant que je part d'un modulo et d'un facteur, comment les déterminer ?

Il faut faire avancer start par pas de 2π/modulo et end un certain nombre de fois plus vite. Donc, en prenant step = 2*pi / modulo, ça donne start = index*step et end = factor*start. Je ne sais pas comment tu as organisé ton code, mais cette partie est commune aux segments et aux cercles.


@Rockaround : C'est surtout Holosmos qui a eu cette idée, j'ai juste implémenté ce que j'avais compris. À un moment, tu as écrit « La distance entre A et le centre du cercle initial » mais je crois que tu voulais parler de C.

Voici quelque chose de similaire. Prenons deux axes perpendiculaires, un point A sur le premier et un point B sur le deuxième. On fait bouger ces deux points à la même vitesse, en choisissant une direction pour chacun (éloignez A de l'origine et approchez-en B pour l'exemple). En fonction du temps, les points A et B varient. Cela donne une droite (AB) qui varie en fonction de t ∈ ℝ. Si on trace cette droite pour chaque t, ça couvre une certaine région du plan. La frontière entre la région couverte et la région non couverte est une parabole.


Je comprends bien que toutes les cordes tracées pour un facteur donné sont tangentes à une certain courbe « principale » (une cardioïde dans le cas d'un facteur double). Mais les formes qui apparaissent en traçant seulement quelques cordes régulièrement m'intriguent. D'autres formes ressemblant aux courbes principales d'autres facteurs apparaissent, il y a des « sous-figures » qui ont des symétries que n'a pas la figure totale…

Je continuerai à chercher et rédigerai plus tard. Rapidement, en notant $m$ le modulo et $f$ le facteur, il me semble pertinent de mettre $m$ sous la forme $k(f-1)-n$. Lorsque $f$ est inversible modulo $m$, on dessine les mêmes cordes en remplaçant $f$ par son inverse. Et aussi, la figure se superpose à elle-même lorsqu'on la fait tourner de $\frac{1}{\gcd(m,f-1)}$ tour, en plus de la symétrie selon l'axe correspondant à l'angle nul qui est toujours présente.

+1 -0

Il faut faire avancer start par pas de 2π/modulo et end un certain nombre de fois plus vite. Donc, en prenant step = 2*pi / modulo, ça donne start = index*step et end = factor*start. Je ne sais pas comment tu as organisé ton code, mais cette partie est commune aux segments et aux cercles.

ρττ

Non justement il n'y a rien de commun, il faut que je repense mon code parce que là ça ne fonctionne pas trop :

Première image avec des arcs générée avec Trekke

Merci de ton aide!

Banni

Pourrais-tu nous montrer ton code (sur ton git il n'y a pas cette partie) ?

Je ne sais pas comment tu procèdes, mais j'imagine que tu as une fonction qui prend deux angles sur le grand cercle, et trace l'arc correspondant. Cette partie fonctionne-t elle comme il faut ? Car là, les arcs que tu traces ne correspondent pas à deux angles sur le grand cercle, donc à mon avis il faut commencer par faire ça correctement.

Ce que je vois, c'est deux fonctions, une une pour les segments et une pour les arcs, qui prennent en paramètre deux angles et tracent pour la première un segment et pour la deuxième un arc, entre les points correspondant à ces deux angles sur le cercle unité. Ensuite, on fait une fonction qui retourne, pour un modulo et un facteur donnés, la liste (plutôt itérable avec yield en python (stream)) contenant les angles correspondants aux points à relier. Puis on mappe la fonction qu'on veut sur la liste.

Bonsoir !

Je viens apporter ma modeste participation, rien de bien folichon juste le programme de base en Python (en utilisant PyGame), mais je voulais absolument faire cet atelier car j'avais déjà vu la vidéo de Micmaths et j'ai adoré le côté pratique de ce topic. Du coup mieux vaut faire quelque chose de basique, que de ne rien faire.

Le lien vers le repo pour le code source : https://github.com/napnac/zds-prog/tree/master/modulo_drawing

N'hésitez pas à faire des remarques sur le code (même s'il n'est pas bien gros). ;)

Quelques cas particuliers, abordés ou pas dans la vidéo de Micmaths. La plupart de ce qui suit correspond à des pensées furtives, non formalisées. Il est tout à fait possible que je rate une explication évidente, et je le poste plus comme une ouverture à la discussion que comme des explications. Ceci étant dit, voyant voir ces cas partiuliers.

Vous pouvez essayer par exemple 300 points avec un facteur de 149, qui va vous donner un quadrillage, ou avec un facteur de 199, qui va vous donner un maillage triangulaire. Pour comprendre ce qui ce passe, calculez $149^2 [300]$ et $199^2 [300]$. De la même manière, en prenant un facteur de 150, tous les points sont reliés uniquement à deux points sur le cercle, selon leur parité. Et en prenant 300, tous les points sont reliés à un point unique.

Vous pouvez aussi essayer avec des facteurs plus complexes, mais il est beaucoup plus dur de ce rendre compte de ce qui se passe, et c'est beaucoup moins plaisant esthétiquement. Par exemple, $107^4 [300]$ vaut 1, et puisque $107^2 [300]$ ne vaut pas 1 (mais 49), tous les traits d'un cercle à 300 points avec un facteur 107 font partie d'un motif à 4 cotés (ces cotés peuvent se croiser).

On pourrait donc raisonnablement ce demander si il existe un facteur tel qu'en partant d'un point, on n'y retourne que lorsqu'on est déjà passé par tous les autres points. En applicant le même raisonnement, il suffit de regarder les résultats de $X^{299} [300]$, pour voir si l'un d'entre eux vaut 1 (où X est le facteur, entre 2 et 300). La réponse, c'est que ça n'existe pas si l'on a 300 points sur le cercle.

Si vous avez écrit un script pour calculer ces modulos, vous vous êtes sûrement rendu compte que parfois, ils valaient 0, et ce quelle que soit la puissance entière strictement supérieure à 1 associée. Avec 300 points sur ce cercle, c'est le cas pour 30, 60, 90, 120, 150, 180, 210, 240, et 270. Que peut-on en déduire sur ce qui sera affiché ? Simplement que tous les points vont être reliés à un point qui sera lui même relié à… lui même. Et donc, on va avoir un certains nombres de points auxquels tous les autres seront reliés. Comment peut-on savoir combien de points agiront comme "aimants" ? Simplement en multipliant le facteur jusqu'à ce qu'il soit égal à 0, modulo le nombre de points du cercle. Pour 30, on va donc avoir 10 "aimants", "centres", ou peu importe. pour 60, on en aura plus que 5 (60, 120, 180, 240 et 0). Pour 90, on aura donc 90, 180, 270, 60, 150, 240, 30, 120, 210 et 0. Soit 10 centres. Comment peut-on trouver ce nombre directement, sans passer par cette phase de calcul qui pourrait se réveler fastidieuse ? Il faut remarquer que ce que l'on cherche, c'est un couple d'entier naturel (n,m) tels que $Facteur*n=Nb_{points}^{Cercle}*m$ avec n aussi petit que possible. Ça se simplifie un peu en $\frac{n}{m}=\frac{Nb_{points}^{Cercle}}{Facteur}$. En reprenant les valeurs précédentes, pour un facteur 30 avec 300 points, on trouve facilement 10. C'est tout aussi facile pour 60 avec 300 points. Par contre, en divisant 300 par 90, si on simplifie la fraction, on se retrouve avec $10/3$, ce qui donne 10 "centres".

C'est surtout pour le paragraphe directement au dessus que je disais que c'était simplement des pensées désordonnées. Il est surement possible de formaliser ça pour expliquer de manière claire et succinte ce qui se passe, mais là je vais aller dormir.

Bonjour,

Je me permets de remonter ce sujet pour vous proposer une variation.

Au lieu de relier chaque point à un autre point du même cercle, je vous propose de faire plusieurs cercles. L'idée ici est faire voyager un point à diffeŕentes vitesse sur différents cercle, et de les relier. Un exemple valant mieux qu'un discours, voici ce que ça donne, avec 100 points sur le cercle extérieur et 50 sur celui intérieur.

2circles_100_50

Ici, c'est la même chose avec respectivement 210 et 70 points. On peut remarquer que le nombre de "ventricules" semble être le ratio entre le nombre de points auquel on retranche 1. Ça devrait vous rappeler le cas précédent ;)

2circles_210_70

Attention, si vous choisissez deux nombres qui ne sont pas multiples l'un de l'autre, il vous faudra continuer à tracer plus longtemps q'une révolution, sous peine d'obtenir quelque chose comme ça.

2circles_200_70

Je vous propose aussi, si vous voulez vous amuser quelques minutes de plus de

  • pouvoir décentrer le cercle intérieur

2circles_100_50_notcentered

  • pouvoir mettre plus de deux cercles

3circles_100_50_10

(Vous pouvez voir sur l'image du dessus que je trace mes cercles comme un cochon, en reliant tous les points, et que ça ne marche pas quand il n'y en a que 10. Faîtes mieux !)

3circles_150_50_25

  • Couper les traits quand ils pénétrent un cercle interieur.

Je n'ai pas d'image ici, puisque je ne l'ai pas fait, mais ça peut être un bon exercice pour vous entraîner avec vos formules d'interception de droites et de cercles. Là comme ça je dirai qu'il faut chercher l'équation de la droite à partir des points initiaux et finaux, et ensuite la ou les coordonnées des points d'intersection éventuels. Il y a peut-être plus facile et rapide, n'hésitez pas à essayer.

Volontiers. Quelle partie te pose problème ?

L'idée est un peu similaire. Avant, si on avait un facteur 3, on reliait les points 1 et 3, 2 et 6, 3 et 9, etc. Maintenant, on a deux cercles. Appelons les A et B, et on relie les points A1 et B1, A2 et B2, etc… Vu qu'ils n'ont pas le même nombre de points, il faut quand même faire les opérations de modulo à un moment donné.

Est-ce plus clair ?

Chouette atelier !
Petite animation développée avec Processing 3 :

0        0

Dans cet exemple, il y a 2400 points pour un facteur f variable de 2 à 4.

Code source :

// Installer gifAnimation : https://github.com/extrapixel/gif-animation/tree/3.0
import gifAnimation.*;
GifMaker gifExport;

// N points sur le cercle
int N = 2400, f = 2, i;

float theta_i, theta_f;
float R = 80.0, dtheta = TWO_PI/N;

void setup()
{
    size(175, 175);
    background(0);

    gifExport = new GifMaker(this, "cercle.gif");  // Création du GIF
    gifExport.setRepeat(0);                        // Animation sans fin
    gifExport.setTransparent(0, 0, 0);             // La couleur noire devient transparente
}

void draw()
{
    i = 1 + frameCount%N;
    translate(width/2, height/2);

    if (i == N)
    {
        f++;

        if (f == 5)
        {
            gifExport.setDelay(2000);  // Délai (en ms) avant la prochaine frame
            gifExport.addFrame();
            gifExport.finish();        // Exporte le GIF dans le dossier
            noLoop();
            saveFrame("end.png");      // Sauvegarde le rendu final
        }
    }

    switch (f)
    {
        case 2:
            stroke(color(0, 255, 255, 1.5)); break;

        case 3:
            stroke(color(0, 255, 255, 1.5)); break;

        case 4:
            stroke(color(127, 0, 255, 5));
    }

    theta_i = (i - 1) * dtheta;
    theta_f = (f*(i - 1)%N) * dtheta;
    line(R*cos(theta_i), R*sin(theta_i), R*cos(theta_f), R*sin(theta_f));

    if (f != 3)
    {
        theta_i += PI;
        theta_f += PI;
        line(R*cos(theta_i), R*sin(theta_i), R*cos(theta_f), R*sin(theta_f));
    }

    if (frameCount%120 == 0)
    {
        gifExport.setDelay(130);
        gifExport.addFrame();
    }
}

(EDIT) En s’amusant à chipoter au rayon R et aux formules :

N = 1200, f = 2 → 4
N = 1200, f = 2 → 4

Code source :

import gifAnimation.*;
GifMaker gifExport;

int N = 1200, f = 2, i;

float theta_i, theta_f;
float R = 150.0, dtheta = TWO_PI/N;

void setup()
{
    size(400, 400);
    frameRate(120);
    background(0);

    gifExport = new GifMaker(this, "cercle.gif");
    gifExport.setRepeat(0);
}

void draw()
{
    i = 1 + frameCount%N;
    translate(width/2, height/2);
    rotate(-HALF_PI);

    stroke(color(255, 255, 255, 20));

    if (R > 49)
    {
        R = (float)(R - 0.1);
    }
    else
    {
        gifExport.setDelay(2000);
        gifExport.addFrame();
        gifExport.finish();

        noLoop();
        saveFrame("end.png");
        print("end");
    }

    theta_i = (i - 1) * dtheta;
    theta_f = (f*(i - 1)%N) * dtheta;
    line(R*cos(theta_i-i/N), R*sin(theta_i), R*cos(theta_f)*cos(PI*i/N), R*sin(theta_f));

    theta_i += PI;
    theta_f += PI;
    line(R*cos(theta_i-i/N), R*sin(theta_i), R*cos(theta_f)*cos(PI*i/N), R*sin(theta_f));

    if (frameCount%29 == 0 && frameCount > 50)
    {
        gifExport.setDelay(100);
        gifExport.addFrame();
    }
}
« La coquille d'escargot »
« La coquille d'escargot »

Code source :

int N = 1200, f = 2, i;
float dtheta = TWO_PI/N;
float theta_i, theta_f, R = 230;

void setup()
{
    size(500, 500);
    frameRate(120); // Accélère l'animation

    background(0);
    stroke(color(255, 255, 255, 30));
}

void draw()
{
    i = 1 + frameCount%N;
    translate(width/2, height/2);

    theta_i = (i - 1) * dtheta + f*i;
    theta_f = (f*i%N) * dtheta;
    line(R*cos(theta_i), R*sin(theta_i), R*cos(theta_f), R*sin(theta_f));

    theta_i += PI;
    theta_f += PI;
    line(R*cos(theta_i), R*sin(theta_i), R*cos(theta_f), R*sin(theta_f));

    R -= 0.2;
    if (R <= 0)
    {
        noLoop();
        saveFrame("end.png");
        print("end");
    }
}
+5 -0

Chouette atelier !
Petite animation développée avec Processing 3 :

0        0

Dans cet exemple, il y a 2400 points pour un facteur f variable de 2 à 4.

Code source :

// Installer gifAnimation : https://github.com/extrapixel/gif-animation/tree/3.0
import gifAnimation.*;
GifMaker gifExport;

// N points sur le cercle
int N = 2400, f = 2, i;

float theta_i, theta_f;
float R = 80.0, dtheta = TWO_PI/N;

void setup()
{
    size(175, 175);
    background(0);

    gifExport = new GifMaker(this, "cercle.gif");  // Création du GIF
    gifExport.setRepeat(0);                        // Animation sans fin
    gifExport.setTransparent(0, 0, 0);             // La couleur noire devient transparente
}

void draw()
{
    i = 1 + frameCount%N;
    translate(width/2, height/2);

    if (i == N)
    {
        f++;

        if (f == 5)
        {
            gifExport.setDelay(2000);  // Délai (en ms) avant la prochaine frame
            gifExport.addFrame();
            gifExport.finish();        // Exporte le GIF dans le dossier
            noLoop();
            saveFrame("end.png");      // Sauvegarde le rendu final
        }
    }

    switch (f)
    {
        case 2:
            stroke(color(0, 255, 255, 1.5)); break;

        case 3:
            stroke(color(0, 255, 255, 1.5)); break;

        case 4:
            stroke(color(127, 0, 255, 5));
    }

    theta_i = (i - 1) * dtheta;
    theta_f = (f*(i - 1)%N) * dtheta;
    line(R*cos(theta_i), R*sin(theta_i), R*cos(theta_f), R*sin(theta_f));

    if (f != 3)
    {
        theta_i += PI;
        theta_f += PI;
        line(R*cos(theta_i), R*sin(theta_i), R*cos(theta_f), R*sin(theta_f));
    }

    if (frameCount%120 == 0)
    {
        gifExport.setDelay(130);
        gifExport.addFrame();
    }
}

(EDIT) En s’amusant à chipoter au rayon R et aux formules :

N = 1200, f = 2 → 4
N = 1200, f = 2 → 4

Code source :

import gifAnimation.*;
GifMaker gifExport;

int N = 1200, f = 2, i;

float theta_i, theta_f;
float R = 150.0, dtheta = TWO_PI/N;

void setup()
{
    size(400, 400);
    frameRate(120);
    background(0);

    gifExport = new GifMaker(this, "cercle.gif");
    gifExport.setRepeat(0);
}

void draw()
{
    i = 1 + frameCount%N;
    translate(width/2, height/2);
    rotate(-HALF_PI);

    stroke(color(255, 255, 255, 20));

    if (R > 49)
    {
        R = (float)(R - 0.1);
    }
    else
    {
        gifExport.setDelay(2000);
        gifExport.addFrame();
        gifExport.finish();

        noLoop();
        saveFrame("end.png");
        print("end");
    }

    theta_i = (i - 1) * dtheta;
    theta_f = (f*(i - 1)%N) * dtheta;
    line(R*cos(theta_i-i/N), R*sin(theta_i), R*cos(theta_f)*cos(PI*i/N), R*sin(theta_f));

    theta_i += PI;
    theta_f += PI;
    line(R*cos(theta_i-i/N), R*sin(theta_i), R*cos(theta_f)*cos(PI*i/N), R*sin(theta_f));

    if (frameCount%29 == 0 && frameCount > 50)
    {
        gifExport.setDelay(100);
        gifExport.addFrame();
    }
}
« La coquille d'escargot »
« La coquille d'escargot »

Code source :

int N = 1200, f = 2, i;
float dtheta = TWO_PI/N;
float theta_i, theta_f, R = 230;

void setup()
{
    size(500, 500);
    frameRate(120); // Accélère l'animation

    background(0);
    stroke(color(255, 255, 255, 30));
}

void draw()
{
    i = 1 + frameCount%N;
    translate(width/2, height/2);

    theta_i = (i - 1) * dtheta + f*i;
    theta_f = (f*i%N) * dtheta;
    line(R*cos(theta_i), R*sin(theta_i), R*cos(theta_f), R*sin(theta_f));

    theta_i += PI;
    theta_f += PI;
    line(R*cos(theta_i), R*sin(theta_i), R*cos(theta_f), R*sin(theta_f));

    R -= 0.2;
    if (R <= 0)
    {
        noLoop();
        saveFrame("end.png");
        print("end");
    }
}

Nuage

Sympa :o

Banni

Chouette atelier !
Petite animation développée avec Processing 3 :

0        0

Dans cet exemple, il y a 2400 points pour un facteur f variable de 2 à 4.

Code source :

// Installer gifAnimation : https://github.com/extrapixel/gif-animation/tree/3.0
import gifAnimation.*;
GifMaker gifExport;

// N points sur le cercle
int N = 2400, f = 2, i;

float theta_i, theta_f;
float R = 80.0, dtheta = TWO_PI/N;

void setup()
{
    size(175, 175);
    background(0);

    gifExport = new GifMaker(this, "cercle.gif");  // Création du GIF
    gifExport.setRepeat(0);                        // Animation sans fin
    gifExport.setTransparent(0, 0, 0);             // La couleur noire devient transparente
}

void draw()
{
    i = 1 + frameCount%N;
    translate(width/2, height/2);

    if (i == N)
    {
        f++;

        if (f == 5)
        {
            gifExport.setDelay(2000);  // Délai (en ms) avant la prochaine frame
            gifExport.addFrame();
            gifExport.finish();        // Exporte le GIF dans le dossier
            noLoop();
            saveFrame("end.png");      // Sauvegarde le rendu final
        }
    }

    switch (f)
    {
        case 2:
            stroke(color(0, 255, 255, 1.5)); break;

        case 3:
            stroke(color(0, 255, 255, 1.5)); break;

        case 4:
            stroke(color(127, 0, 255, 5));
    }

    theta_i = (i - 1) * dtheta;
    theta_f = (f*(i - 1)%N) * dtheta;
    line(R*cos(theta_i), R*sin(theta_i), R*cos(theta_f), R*sin(theta_f));

    if (f != 3)
    {
        theta_i += PI;
        theta_f += PI;
        line(R*cos(theta_i), R*sin(theta_i), R*cos(theta_f), R*sin(theta_f));
    }

    if (frameCount%120 == 0)
    {
        gifExport.setDelay(130);
        gifExport.addFrame();
    }
}

(EDIT) En s’amusant à chipoter au rayon R et aux formules :

N = 1200, f = 2 → 4
N = 1200, f = 2 → 4

Code source :

import gifAnimation.*;
GifMaker gifExport;

int N = 1200, f = 2, i;

float theta_i, theta_f;
float R = 150.0, dtheta = TWO_PI/N;

void setup()
{
    size(400, 400);
    frameRate(120);
    background(0);

    gifExport = new GifMaker(this, "cercle.gif");
    gifExport.setRepeat(0);
}

void draw()
{
    i = 1 + frameCount%N;
    translate(width/2, height/2);
    rotate(-HALF_PI);

    stroke(color(255, 255, 255, 20));

    if (R > 49)
    {
        R = (float)(R - 0.1);
    }
    else
    {
        gifExport.setDelay(2000);
        gifExport.addFrame();
        gifExport.finish();

        noLoop();
        saveFrame("end.png");
        print("end");
    }

    theta_i = (i - 1) * dtheta;
    theta_f = (f*(i - 1)%N) * dtheta;
    line(R*cos(theta_i-i/N), R*sin(theta_i), R*cos(theta_f)*cos(PI*i/N), R*sin(theta_f));

    theta_i += PI;
    theta_f += PI;
    line(R*cos(theta_i-i/N), R*sin(theta_i), R*cos(theta_f)*cos(PI*i/N), R*sin(theta_f));

    if (frameCount%29 == 0 && frameCount > 50)
    {
        gifExport.setDelay(100);
        gifExport.addFrame();
    }
}
« La coquille d'escargot »
« La coquille d'escargot »

Code source :

int N = 1200, f = 2, i;
float dtheta = TWO_PI/N;
float theta_i, theta_f, R = 230;

void setup()
{
    size(500, 500);
    frameRate(120); // Accélère l'animation

    background(0);
    stroke(color(255, 255, 255, 30));
}

void draw()
{
    i = 1 + frameCount%N;
    translate(width/2, height/2);

    theta_i = (i - 1) * dtheta + f*i;
    theta_f = (f*i%N) * dtheta;
    line(R*cos(theta_i), R*sin(theta_i), R*cos(theta_f), R*sin(theta_f));

    theta_i += PI;
    theta_f += PI;
    line(R*cos(theta_i), R*sin(theta_i), R*cos(theta_f), R*sin(theta_f));

    R -= 0.2;
    if (R <= 0)
    {
        noLoop();
        saveFrame("end.png");
        print("end");
    }
}

Nuage

Sympa :o

A-312

Tout à fait !

Merci pour les encouragements !
Les codes sources ci-dessous sont en PHP.

Au lieu du contour d’un cercle, voici le résultat avec celui d’une étoile :

Les segments ont été tracés avec un facteur f nul et un décalage constant de $N1/2.
Autrement dit, chaque segment relie le point i au point i + $N1/2, avec $N1 = 300.
Le contour de l’étoile a été réalisé mathématiquement : une série de segments où seul un vecteur unité change de direction de segments en segments.
Beaucoup plus de maths qu’on ne le pense…

 

<?php

include 'notices.php';

$arr = [];

$i  = 0;
$S  = 10;  // Nombre de segments dans le contour
$NE = 30;  // Nombre d'échantillons à enregistrer dans $arr

// Angle directeur
$theta = pi()/10;

$point_courant = [0, 200];
$v_dir_unite = [sin($theta), -cos($theta)]; // Attention : vecteur unité !
$distance = 150/cos($theta);

for ($c = 0; $c < $S; $c++)
{
    // Calcul point final
    $point_final = [$point_courant[0] + $distance*$v_dir_unite[0], $point_courant[1] + $distance*$v_dir_unite[1]];

    $incx = ($point_final[0] - $point_courant[0])/$NE;
    $incy = ($point_final[1] - $point_courant[1])/$NE;
    
    for ($t = 0; $t < $NE; $t++)
    {
        $arr[$i] = $point_courant;
        $i++;
        $point_courant[0] += $incx;
        $point_courant[1] += $incy;
    }

    // Évolution des paramètres
    if ($c%2 === 0)
    {
        $theta += 0.4*pi();
    }
    else
    {
        $theta += 1.2*pi();
    }
    $v_dir_unite = [sin($theta), -cos($theta)];
}

$N1 = count($arr);

// Création de l'image

list($width, $height) = [450, 450];
$im = imagecreatetruecolor($width, $height);

list($px, $py) = [$width/2, $height/2];

$cyan = imagecolorallocate($im, 0, 255, 255);
$gold = imagecolorallocatealpha($im, 255,  255, 0, 90);

foreach ($arr as $i => $p)
{
    list($cx, $cy)    =  $p;
    list($fcx, $fcy)  =  $arr[($i + $N1/2)%$N1];

    imagesetpixel($im, $px + $cx, $py - $cy, $cyan);
    imageline($im, $px + $cx, $py - $cy, $px + $fcx, $py - $fcy, $gold);
}

header('Content-type: image/png');
imagepng($im);

Le facteur f = 2/5 a été choisi pour obtenir six « pétales » (600 points) :

Note : Le code de cette image ne contient pas de modulo % parce qu’il est implicitement présent mathématiquement dans les fonctions sinus et cosinus.

 

<?php

include 'notices.php';

$width = 450;
$height = 450;

$im = imagecreatetruecolor($width, $height);

$px = $width/2;
$py = $height/2;

$R1 = 200; // Rayon cercle extérieur
$R2 = 180; // Rayon cercle intérieur

$N1 = 600;

// dth : angle entre deux points consécutifs
$dth1 = 2*pi()/$N1;

for ($i1 = 1; $i1 <= $N1; $i1++)
{
    $th_i = pi()/6 + 5*$i1*$dth1;
    $th_f = pi()/6 + 2*$i1*$dth1;

    $color = imagecolorallocatealpha($im, 255*(1-abs(sin($th_i - $th_f))),  180*abs(sin($th_i - $th_f)), 255, 107);
    imageline($im, $px + $R1*cos($th_i), $py + $R1*sin($th_i), $px + $R2*cos($th_f), $py + $R2*sin($th_f), $color);
    
    $th_i += pi();
    $th_f += pi();
    imageline($im, $px + $R1*cos($th_i), $py + $R1*sin($th_i), $px + $R2*cos($th_f), $py + $R2*sin($th_f), $color);
}

header('Content-type: image/png');
imagepng($im);

notices.php :

<?php

// Transforme les notices et avertissements en erreurs fatales
// qui bloquent le script. Pratique pour éviter l'affichage de 100000000 notices...

function errHandle($errNo, $errStr, $errFile, $errLine)
{
    $msg = "$errStr in $errFile on line $errLine";
    if ($errNo == E_NOTICE || $errNo == E_WARNING)
    {
        throw new ErrorException($msg, $errNo);
    }
    exit($msg);
}

set_error_handler('errHandle');

@Rockaround : comment as-tu réalisé tes images ?
Lorsque je les télécharge, je tombe sur un mystérieux code EPS :

%!PS-Adobe-2.0 EPSF-2.0
%%BoundingBox: -1751 -2751 2251 1251
%%HiResBoundingBox: -1750.999 -2750.999 2251.000 1251.000
%%Creator: vplot.py
%%DocumentFonts: Geneva
%%EndComments
/fontsize 12 def
/csize {1 mul} def
/Geneva fontsize selectfont
/cshift fontsize neg def
/vshift fontsize -2 div def
/L {lineto} bind def
/M {moveto} bind def
/S {stroke} bind def
/CS {closepath stroke} bind def
/RP {reversepath} bind def
/F {closepath fill} bind def
/X {currentpoint stroke moveto} bind def
/N {newpath} bind def
/C {setrgbcolor} bind def
/R {rmoveto} bind def
/V {rlineto} bind def
/DON {[3 2] 0 setdash} bind def 
/DOFF {[] 0 setdash} bind def
/Cshow { currentpoint S M
  dup stringwidth pop -2 div cshift R 0 -2 R show } def   
/Rshow { dup stringwidth pop neg vshift R -4 2 R show } def    
1 setlinewidth
%%EndProlog
  1.00 setlinewidth
gsave
 0.000  0.000  0.000 C
  2.00 setlinewidth
N   250.00 -2750.00 M
  250.00 -2750.00 L S
 0.000  0.000  0.000 C
grestore
gsave
 0.000  0.000  0.000 C
  2.00 setlinewidth
N   375.58 -2746.05 M
  500.67 -2734.23 L S
 0.000  0.000  0.000 C
grestore

Oui, c’est d’ailleurs ce qui rend la forme assez difficile à maîtriser.
Je n’ai pas encore réussi à obtenir de dessins symétriques pour un facteur f non nul.
Je pense que je pourrais aussi changer la paramétrisation du contour.
Si je repère les points avec un angle en coordonnées polaires, je pense que ça sera plus simple.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte