Dessiner une étoile en Python

Résolu/Fermé
Miaourt - Modifié le 4 mars 2021 à 13:52
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 - 8 mars 2021 à 12:20
Bonjour à tous !

Je souhaite réaliser le contour d'une étoile avec le caractère "*" en Python.
L'étoile doit s'agrandir en fonction d'un nombre saisi par l'utilisateur au lancement du script, et c'est là qu'intervient mon problème : Je n'arrive pas à trouver la relation entre les lignes et les colonnes qui me permettrait de définir les colonnes automatiquement après la saisie d'un nombre de lignes par l'utilisateur.

Voici mon script :
from math import *

row = int(input("saisissez une valeur"))*4
col = round(row*1.55)
mid_row=row//2
mid_col=col//2
quart_row=row//4
three_quart_row=row*(3/4)
print(row)
print(col)

def ligne1():
    return i==quart_row and i+j<=mid_col or i==quart_row and j-i>=mid_col

def ligne2():
    return i==three_quart_row and i+j>=row+mid_col or i==three_quart_row and i-j>=quart_row

def diag1():
    return i+j==mid_col and i<=quart_row or i+j==mid_col and i>=mid_row

def diag2():
    return j-i==mid_col and i<=quart_row or j-i==mid_col and i>=mid_row

def diag3():
    return i+j==row+mid_col and i>=three_quart_row or i+j==row+mid_col and i<=mid_row

def diag4():
    return i-j==quart_row and i>=three_quart_row or i-j==quart_row and i<=mid_row

def isOutsidestar():
    return ligne1() or ligne2() or diag1() or diag2() or diag3() or diag4()

for i in range(row+1):
    for j in range(col):
        if (isOutsidestar()):
            print("*",end="")
        else:
            print(" ",end="")
    print("\r")


J'ai trouvé la l'équation col = row*1.55 au hasard, et ça ne fonctionne que pour row allant de 2 à 9, j'aimerais trouver une relation qui fonctionnerait peu importe la valeur de row, avez-vous une idée ?
Je débute en Python, donc si vous trouvez d'autres choses à améliorer dans mon code, je suis preneur !

D'avance merci !

EDIT : Ajout des balises de code (la coloration syntaxique).
Explications disponibles ici : ICI

Merci d'y penser dans tes prochains messages.

3 réponses

mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
4 mars 2021 à 15:38
Bonjour,

Là il faut faire un peu de trigonométrie.

Ton étoile peut être vue comme un hexagone (6 triangle équilatéraux) pour lequel on a greffé 6 triangles équilatéraux (de même taille) sur chacun de ses côté. Si on note L la longueur d'un côté d'un triangle équilatéral, ton étoile à donc ton étoile est de hauteur 4L et de largeur 3L.

Les angles d'un triangle équilatéral mesurent 60°. Cela signifie que tu ne peux pas monter/descendre de 1 caractère quand tu vas 1 caractère vers la droite le long d'un bord diagonal. Cela reviendrait à un angle de 45°. Tant que L est petit ce n'est pas très grave, car de toute façon comme on passe de R^2 (le plan) à N^2 (un espace discret en deux dimension dans lequel on écrit les caractère), les arrondis font que "ça marche". Mais bien évidemment, quand L devient plus grand, les arrondis ne suffisent plus à contrebalancer cet écart.

Or si on regarde ton code, le raisonnement est construit sur des triangles avec des angles de 60°. En effet, soit tel triangle équilatéral ABC et supposons que le bord AB soit de longueur L. Alors le projeté de C sur AB est de longueur L.cos(60°) = L / 2. C'est ce que tu as appelé
mid_row
. Le raisonnement est le même si AB est vertical (c'est ce que tu as appelé
mid_col
.

Il faudrait donc clarifier si ton étoile est construite selon des triangles isocèles (dont les angles associés au bord horizontal font 45°) ou si tu cherches vraiment à faire une étoile basée sur des triangles équilatéraux (auquel cas l'algorithme doit être repensé).

Personnellement, je commencerais par écrire une fonction qui permet de tracer un segment dont selon quatre entier (x1, y1, x2, y2)
  • Si le segment est plutôt horizontal (|x1-x2| <= |y1-y2|), pour chaque valeur de x allant de x1 à x2, on calcule le y correspondant.
  • Sinon, le segment est plutôt vertical (|x1-x2| > |y1-y2|), pour chaque valeur de y allant de y1 à y2, on calcule le x correspondant.


On obtient ainsi une liste de couples (x, y) qui définissent les cases de N^2 à écrire avec le caractère utilisé pour faire le trait (* dans ton cas). De cette liste on peut tirer ymax et xmax. Pour faire l'étoile on défini donc 12 segments avec les coordonnées appropriées qui forment bords de l'étoile. Il ne reste plus qu'à itérer pour chaque valeur de x allant de 0 à xmax et pour chaque valeur de y à ymax et regardé si le point (x, y) est dans la liste de couple pour décider quel caractère utiliser.

Optimisation: On peut même avoir un xmax qui dépend du y courant (en cherchant, pour la valeur de y courante, la plus grande valeur de x présente dans les couples), ce qui peut valoir le coup d'utiliser un dictionnaire qui à chaque valeur de y associe les valeurs de x (triées) associées à ces couples.

Bonne chance
1
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
Modifié le 8 mars 2021 à 13:25
Il faut être inscrit sur le site pour pouvoir clôturer, je m'en occupe.

Ensuite l'explication du triangle équilatéral vs isocèle. Imagine ce triangle


@
@@@
@@@@@


Sa base fait 5 caractères de large et ses côtés diagonaux 5.racine(2)/2 car les angles au niveau de la base font 45° (plus généralement pour une base de taille n, si l'angle forme 45°, un bord diagonal mesurera n.racine(2)/2 ~ 1.414.n. Bref, il n'est donc pas équilatéral. C'est un peu inévitable car tu dessines ton triangle dans une grille donc tu n'auras jamais un triangle purement équilatéral. Si on cherchait le "meilleur arrondi" ça coïncide à peu près pour de petite valeurs de n. Par contre cet arrondi devient faux pour de grande valeur de n. Tu peux regarder dans un logiciel comme paint, tu verras que par moment tu te déplaces de deux pixels verticalement quand tu avances d'un pixel horizontalement le long d'un bord diagonal...

Bonne continuation :)
1
Bonjour,

Merci pour cette réponse.
J'ai effectivement revu le code de 0 et je l'ai décomposé en 6 parties, en gérant l'espace entre les étoiles pour les diagonales.
Je n'ai par contre pas compris la seconde partie de ton explication, surement parce que je ne suis pas au niveau en maths, mais l'essentiel est que ça marche !

Merci encore !

PS: Je ne vois pas où clôturer le sujet ?
0