Dessinez une étoile de Noël / Dodécaèdre étoilé

10

Les étoiles en papier sont une grande chose dans ma famille à Noël, alors j'ai pensé qu'une virtuelle serait cool.

Ci-dessous est une image d'un dodécaèdre régulier (de https://en.wikipedia.org/wiki/Dodecahedron , attribué à l'auteur mentionné ici.)

entrez la description de l'image ici

Le processus de stellation (wikipedia) lorsqu'il est appliqué à un polyèdre implique d'étendre les faces jusqu'à ce qu'elles traversent d'autres faces. Ainsi en partant du dodécaèdre régulier, on obtient les formes suivantes:

Petit dodécaèdre étoilé, grand dodécaèdre et grand dodécaèdre étoilé

Image de http://jwilson.coe.uga.edu/emat6680fa07/thrash/asn1/stellations.html

entrez la description de l'image ici

Ce sont les trois stellations possibles du dodécaèdre (Wolfram). Ils forment une progression naturelle du dodécaèdre au petit dodécaèdre étoilé, au grand dodécaèdre et au grand dodécaèdre étoilé, à mesure que nous étendons les faces de plus en plus loin.

Tâche

Votre programme ou fonction doit afficher ou produire dans un fichier image l'un des polyèdres suivants: dodécaèdre ordinaire, petit dodécaèdre étoilé, grand dodécaèdre ou grand dodécaèdre étoilé .

Le jeu de couleurs doit être comme la deuxième image ci-dessus. Chacune des six paires de faces opposées doit être l'une des six couleurs Rouge, Jaune, Vert, Cyan, Bleu et Magenta. Vous pouvez utiliser des couleurs par défaut avec ces noms dans votre langue ou sa documentation, ou utiliser les couleurs FF0000, FFFF00, 00FF00, 00FFFF, 0000FF et FF00FF (vous pouvez les atténuer en réduisant l'intensité à un minimum de 75% si vous le souhaitez, par exemple en réduisant les F à C.)

Notez que nous définissons une "face" comme étant toutes les zones dans le même plan. Ainsi, dans les images au-dessus de la face avant est jaune (et la face arrière parallèle serait également jaune.)

Le fond doit être noir, gris ou blanc. Les bords peuvent être omis, mais doivent être noirs s'ils sont dessinés.

Règles

Le polyèdre affiché doit avoir une largeur comprise entre 500 et 1 000 pixels (la largeur est définie comme la distance maximale entre deux sommets affichés).

Le polyèdre affiché doit être en projection en perspective (point de vue à au moins 5 largeurs du polyèdre), ou en projection orthographique (en fait une projection en perspective avec le point de vue à l'infini).

Le polyèdre doit pouvoir être affiché sous n'importe quel angle. (Il n'est pas acceptable de choisir l'angle le plus simple possible et de créer une forme 2D codée en dur.) L'angle peut être spécifié par l'utilisateur de l'une des manières suivantes:

  1. Saisie de trois angles correspondant à trois rotations, à partir de stdin, ou en tant que paramètres de fonction ou de ligne de commande. Il peut s'agir d'angles d'Euler (où les première et dernière rotations sont sur le même axe) ou d'angles de Tait-Bryan (où il y a une rotation chacun autour des axes x, y et z) https://en.wikipedia.org/ wiki / Euler_angles (en termes simples, tout se passe tant que chaque rotation concerne les axes x, y ou z et que les rotations consécutives concernent les axes perpendiculaires.)

  2. Possibilité pour l'utilisateur de faire pivoter le polyèdre par pas de plus de 10 degrés autour des axes x et y et de rafraîchir l'affichage, un nombre arbitraire de fois (en supposant que l'axe z est perpendiculaire à l'écran).

Le polyèdre doit être solide, pas filaire.

Aucune intégration pour dessiner des polyèdres n'est autorisée (je vous regarde, Mathematica!)

Notation

C'est du codegolf. Le code le plus court en octets gagne.

Bonus

Multipliez votre score par 0,5 si vous n'utilisez pas de fonctions intégrées pour le dessin 3D.

Multipliez votre score par 0,7 si vous pouvez afficher les trois stellations du dodécaèdre, sélectionnables par l'utilisateur par un entier 1-3 entré depuis stdin, ou par fonction ou paramètre de ligne de commande.

Si vous optez pour les deux bonus, votre score sera multiplié par 0,5 * 0,7 = 0,35

Informations utiles (sources comme ci-dessous)

https://en.wikipedia.org/wiki/Regular_dodecahedron

https://en.wikipedia.org/wiki/Regular_icosahedron

Le dodécaèdre a 20 sommets. 8 d'entre eux forment les sommets d'un cube avec les coordonnées cartésiennes (x, y, z) suivantes:

(± 1, ± 1, ± 1)

Les 12 autres sont les suivants (phi est le nombre d'or)

(0, ± 1 / φ, ± φ)

(± 1 / φ, ± φ, 0)

(± φ, 0, ± 1 / φ)

La coque convexe du petit dodécaèdre étoilé et du grand dodécaèdre est évidemment un dodécaèdre régulier. Les sommets extérieurs décrivent un icosaèdre.

Selon Wikipedia, les 12 sommets d'un icosaèdre peuvent être décrits de la même manière que les permutations cycliques de (0, ± 1, ± φ). Les sommets extérieurs du petit dodécahéron étoilé et du grand dodéchaèdre (à la même échelle que le dodécaèdre ci-dessus) forment un icosaèdre plus grand, où les coordonnées des sommets sont des permutations cycliques de (0, ± φ ^ 2, ± φ).

Les angles entre les faces pour le dodécaèdre et l'icosaèdre sont respectivement de 2 arctan (phi) et arccos (- (√5) / 3).

Pour des conseils sur la rotation, voir https://en.wikipedia.org/wiki/Rotation_matrix

EDIT: Par erreur, j'ai autorisé le dodécaèdre régulier, et je ne peux pas le rétracter maintenant. Le bonus x0.7 pour dessiner les trois polyèdres étoilés reste. Le jour du Nouvel An, j'émettrai une prime de 100 pour la réponse qui peut afficher le plus des quatre polyèdres, avec le code le plus court comme bris d'égalité.

Level River St
la source
Les légendes @ LegionMammal978 pour dessiner des polyèdres (par exemple dodecahedron) ne sont pas autorisées. Certaines langues ont des fonctionnalités pour construire des modèles 3D avec des commandes comme triangle[[a,b,c],[p,q,r],[x,y,z]]. Ces langues ont généralement des fonctions intégrées pour faire pivoter et afficher le modèle, en prenant automatiquement soin de ne pas afficher les faces cachées, etc. Des solutions comme celles-ci sont autorisées mais n'attirent pas le bonus. Le bonus a pour but de permettre aux langues qui ne disposent pas de ces équipements d'être compétitives, et également d'attirer des solutions plus intéressantes.
Level River St
@ LegionMammal978 haha, je savais que Mathematica serait le langage qui causait des problèmes. Polyhedrondatan'est pas autorisé car il s'agit clairement d'une fonction intégrée pour dessiner des polyèdres. Si votre réponse n'utilise pas de builtins pour dessiner des polyèdres et est conforme aux autres règles, alors elle est acceptable. Votre point semble être que, étant donné que vous devez colorer les visages correctement, Polyhedrondatacela ne vous épargnerait pas grand-chose de toute façon, donc cela peut en pratique être une restriction quelque peu arbitraire. Je suis d'accord dans une certaine mesure, mais c'est plus juste pour tous si j'évite de changer les règles après la publication.
Level River St

Réponses:

3

Python 2.7, 949 octets

Voici la solution pour le dodécaèdre régulier tracé à l'aide de matplotlib. Les grandes lignes du code non golfé (non illustré ici) ont été décrites ci-dessous:

  • Faire des sommets Faire des bords (basé sur 3 voisins les plus proches, module scipy.spatial.KDtree)
  • Créer des faces basées sur des cycles de graphes de longueur 5 (module networkx)
  • Créez des facenormals (et sélectionnez ceux dont la face vers l'extérieur est normale, numpy.cross)
  • Génère une coloration basée sur les normales du visage
  • Tracer avec matplotlib
import itertools as it
import numpy as np
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
v=[p for p in it.product((-1,1),(-1,1),(-1,1))]
g=.5+.5*5**.5
v.extend([p for p in it.product((0,),(-1/g,1/g),(-g,g))])
v.extend([p for p in it.product((-1/g,1/g),(-g,g),(0,))])
v.extend([p for p in it.product((-g,g),(0,),(-1/g,1/g))])
v=np.array(v)
g=[[12,14,5,9,1],[12,1,17,16,0],[12,0,8,4,14],[4,18,19,5,14],[4,8,10,6,18],[5,19,7,11,9],[7,15,13,3,11],[7,19,18,6,15],[6,10,2,13,15],[13,2,16,17,3],[3,17,1,9,11],[16,2,10,8,0]]
a=[2,1,0,3,4,5,0,1,2,3,4,5]
fig = plt.figure()
ax = fig.add_subplot((111),aspect='equal',projection='3d')
ax.set_xlim3d(-2, 2)
ax.set_ylim3d(-2, 2)
ax.set_zlim3d(-2, 2)
for f in range(12):
 c=Poly3DCollection([[tuple(y) for y in v[g[f],:]]], linewidths=1, alpha=1)
 c.set_facecolor([(0,0,1),(0,1,0),(0,1,1),(1,0,0),(1,0,1),(1,1,0)][a[f]])
 ax.add_collection3d(c)
ax.auto_scale_xyz
plt.show()

entrez la description de l'image ici

Willem
la source
3

Ruby, 784 octets * 0,5 * 0,7 = 274,4

Ma propre réponse, donc non éligible à ma prime.

Éligible à la fois au bonus intégré non 3D et au bonus de dessin toutes les stellations.

->t,n{o=[]
g=->a{a.reduce(:+)/5}
f=->u,v,w,m{x=u.dup;y=v.dup;z=w.dup
15.times{|i|k,l=("i".to_c**(n[i/5]/90.0)).rect
j=i%5
x[j],y[j],z[j]=y[j],x[j]*k+z[j]*l,z[j]*k-x[j]*l}
p=g[x];q=g[y];r=g[z]
a=[0,1,-i=0.382,-1][t]*e=r<=>0
b=[j=1+i,0,j,j][t]*e
c=[-i*j,-i,1,i][t]*e
d=[j*j,j,0,0][t]*e
5.times{|i|o<<"<path id=\"#{"%9.0f"%(z[i]*a+r*b+(z[i-2]+z[i-3])*c+2*r*d+999)}\"
d=\"M#{(x[i]*a+p*b)} #{(y[i]*a+q*b)}L#{(x[i-2]*c+p*d)} #{(y[i-2]*c+q*d)}L#{(x[i-3]*c+p*d)} #{(y[i-3]*c+q*d)}\"
fill=\"##{m}\"/>"}}
a=233
b=377
z=[0,a,b,a,0]
y=[a,b,0,-b,-a]
x=[b,0,-a,0,b]
w=[-b,0,a,0,-b]
f[x,y,z,'F0F']
f[w,y,z,'0F0']
f[y,z,x,'00F']
f[y,z,w,'FF0']
f[z,x,y,'F00']
f[z,w,y,'0FF']
s=File.open("p.svg","w")
s.puts'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-450 -450 900 900">',o.sort,'</svg>'
s.close}

Entrée comme paramètres de fonction

Un entier 0..3 correspondant au dodécaèdre régulier, au petit dodécaèdre étoilé, au grand dodécaèdre étoilé

Un tableau de trois nombres entiers correspondant à des angles de degrés pour les rotations autour des axes x, y et x (encore) (angles d'Euler appropriés, permettant d'obtenir toute rotation.)

Générez un fichier p.svgqui peut être affiché dans un navigateur Web.

Explication

les tableaux x, y, z au bas du code contiennent les coordonnées des points extérieurs d'une face d'un petit dodécaèdre étoilé. Cela peut être inscrit dans l'icosaèdre dont les 12 sommets sont définis par les permutations cycliques de (+/- 377, + / - 233, + / - 0). Notez que 377 et 233 sont des nombres de Fibonacci consécutifs et donc 377/233 est une excellente approximation du nombre d'or.

un tableau supplémentaire w contient les coordonnées x multipliées par -1, équivalent à une réflexion dans le plan x. La fonction f est appelée 6 fois, une fois pour chaque couleur, avec les différentes permutations cycliques de x, y, z et w, y, z.

Les trois rotations sont passées en paramètres dans n []. pour utiliser sin et cos en Ruby, il faut le faire include Math. pour éviter cela, le cosinus et le sinus de l'angle sont obtenus en élevant la racine carrée de -1 "i"à une puissance de (angle en degrés / 90) Les parties réelles et imaginaires de ce nombre sont stockées dans k (cosinus) et l ( sinus)

Avant la rotation, les valeurs x et y sont échangées. Ensuite, la multiplication matricielle est appliquée aux valeurs y et z pour donner une rotation autour de l'axe x. L'échange de valeurs permet d'effectuer les trois rotations en boucle.

Jusqu'à présent, nous n'avons qu'un seul anneau de points. Pour obtenir le reste, nous devons trouver le centre du pentagone / étoile. Cela se fait en trouvant la moyenne des coordonnées des 5 sommets, qui sont stockées dans p, q, r.

Comme mentionné précédemment, un seul appel de fonction par couleur est effectué. Le signe de r (la moyenne des coordonnées z, donc la coordonnée de la face) est testé. S'il est positif, le visage est une face avant et donc visible. S'il est négatif, le visage est un dos. Elle est invisible, et nous n'avons pas d'appel de fonction pour la face opposée. Par conséquent, les trois coordonnées doivent être inversées. Le signe de r est stocké dans e pour faciliter cela.

La face est constituée de 5 triangles, dont les sommets sont des combinaisons linéaires des sommets extérieurs du petit dodécaèdre étoilé et du centre de la face. Dans le cas du petit dodécaèdre étoilé, pour les pointes des triangles nous fixons a = 1 et b = 0 (contribution 1 de x, y, z et 0 de p, q, r). Pour les 2 sommets de base du triangle, nous fixons c = -0,382 (contribution 1 / nombre d'or ^ 2 de x, y, z) et d = 1,382 (contribution de p, q, r.) La raison de la contribution négative est que les sommets de base du triangle sont définis en termes de pointes opposées, qui sont du côté opposé de la face. Les coordonnées obtenues sont multipliées par e si nécessaire.

Les quatre tableaux sans nom dont les valeurs sont affectées pour a,b,c,dcontenir les valeurs requises pour le dodécaèdre régulier, le petit dodécaèdre étoilé, le grand dodécaèdre et le grand dodécaèdre étoilé, sélectionnés selon la variable tRemarque que pour le petit dodécaèdre étoilé et le grand dodécaèdre, a + b = c + d = 1. La relation a + b = c + d s'applique aux autres formes, mais une échelle différente est appliquée.

Une ligne de code svg est créée pour chaque triangle. Celui-ci contient un ID dérivé de la somme des coordonnées z des 3 sommets du triangle, une description des sommets des trois coordonnées du triangle et une couleur. notons que nous regardons directement vers le bas de l'axe z en projection orthographique. Ainsi 2D x = 3D x et 2D y = 3D y. La ligne est ajoutée àh.

enfin, une fois tous les appels de fonction terminés, h est trié de sorte que les triangles de la valeur z la plus élevée (devant) soient tracés en dernier, et le tout est enregistré sous forme de fichier svg avec le texte d'en-tête et de pied de page approprié.

Non testé dans le programme de test

h=->t,n{                                              #t=type of polygon,n=angles of rotation
o=[]                                                  #array for output
g=->a{a.reduce(:+)/5}                                 #auxiliary function for finding average of 5 points

f=->u,v,w,m{x=u.dup;y=v.dup;z=w.dup                   #function to take 5 points u,v,w and plot one face (5 triangles) of the output in colour m 

  15.times{|i|                                        #for each of 3 rotation angle and 5 points
    k,l=("i".to_c**(n[i/5]/90.0)).rect                #calculate the cos and sine of the angle, by raising sqrt(-1)="i" to a power
    j=i%5                                             #for each of the 5 points
    x[j],y[j],z[j]=y[j],x[j]*k+z[j]*l,z[j]*k-x[j]*l}  #swap x and y, then perform maxtrix rotation on (new) y and z.

  p=g[x];q=g[y];r=g[z]                                #find centre p,q,r of the face whose 5 points (in the case of small stellated dodecahedron) are in x,y,z

  e=r<=>0                                             #if r is positive, face is front. if negative, face is back, so we need to transform it to opposite face.
  a=[0,              1,    -0.382,    -1][t]*e        #contribution of 5 points x,y,z to triangle tip vertex coordinates
  b=[1.382,          0,     1.382,     1.382][t]*e    #contribution of centre p,q,r to triangle tip vertex coordinates
  c=[-0.528,        -0.382, 1,         0.382][t]*e    #contribution of 5 points x,y,z to coordinates of each triangle base vertex 
  d=[1.901,          1.382, 0,         0][t]*e        #contribution of centre p,q,r to coordinates of each triangle base vertex

  5.times{|i|
  o<<"<path id=\"#{"%9.0f"%(z[i]*a+r*b+(z[i-2]+z[i-3])*c+2*r*d+999)}\"
d=\"M#{(x[i]*a+p*b)} #{(y[i]*a+q*b)}L#{(x[i-2]*c+p*d)} #{(y[i-2]*c+q*d)}L#{(x[i-3]*c+p*d)} #{(y[i-3]*c+q*d)}\"
fill=\"##{m}\"/>"}                                    #write svg code for this triangle 
}

  a=233                                               #a,b =coordinate standard values 
  b=377
  z=[0,a,b,a,0]                                       #z coordinates for one face of stellated dodecahedron 
  y=[a,b,0,-b,-a]                                     #y coordinates
  x=[b,0,-a,0,b]                                      #x coordinates
  w=[-b,0,a,0,-b]                                     #alternate  x coordinates

  f[x,y,z,'F0F']                                      #call f
  f[w,y,z,'0F0']                                      #to plot
  f[y,z,x,'00F']                                      #each
  f[y,z,w,'FF0']                                      #face
  f[z,x,y,'F00']                                      #in
  f[z,w,y,'0FF']                                      #turn

  s=File.open("p.svg","w")                            #sort output in o, plot front triangles last
  s.puts'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-450 -450 900 900">',o.sort,'</svg>'
  s.close                                             #add header and footer, and save as svg
}

t=gets.to_i
n=[]
3.times{n<<gets.to_i}
h[t,n]

Production

pour les petits dodécaèdres étoilés (ajoutera bientôt des images des autres polygones)

1,0,0,0 position d'origine

entrez la description de l'image ici

1,30,0,0 pivoter vers le bas de 30 degrés

entrez la description de l'image ici

1,0,30,0 pivoter à droite de 30 degrés (note: pour une vue latérale parfaite, la rotation serait atan(1/golden ratio)= 31,7 degrés, donc nous pouvons encore voir un petit ruban de bleu)

entrez la description de l'image ici

1,0,20,0 pivoter à droite de 20 degrés

entrez la description de l'image ici

1,60,10, -63 rotation vers le bas, droite et haut (exemple d'orientation uniquement possible avec 3 rotations)

entrez la description de l'image ici

0,30,0,0 dodécaèdre régulier

entrez la description de l'image ici

2,0,20,0 grand dodécaèdre

entrez la description de l'image ici

3,45,45,45 grand dodécaèdre étoilé entrez la description de l'image ici

Level River St
la source
3

Mathematica, 426 424 octets

Graphics3D[{Red,Yellow,Green,Cyan,Blue,Magenta}~Riffle~(a=Partition)[Polygon/@Uncompress@"1:eJxtkjEKwkAURNeoySYgeAVP4QFsrcTGTiyUBcEith7A2wgKgpVH8/vgs2TYZmAyw9/5k784XDbHVwihnxisU39N9SiEdI8GO/uWHpXBtjFAgJ7HToFl5WabEdJ+anCqDb6dU9RP65NR59EnI0CZDAWYjFmomBmPCn3/hVVwc9s4xYd66wYqFJVvhMz75vWlHIkhG2HBDJ1V3kYps7z7jG6GomIu/QUJKTGkdtlX2pDM8m6pydyzHIOElBhyG6V9cxulzPldaVJ6lpuUkKUTzWcm+0obkrn0f3OT0rMc0jDkD37nlUo="~a~3~a~5,2],Boxed->1<0]

Utilise la fonction intégrée Graphics3Dpour afficher la forme. Cependant, la plupart des octets sont occupés par les emplacements des sommets compressés, qui sont ensuite Partitionédités sous une forme utilisable par Polygon. Finalement:

Notez que cette forme peut être pivotée en cliquant et en faisant glisser.

LegionMammal978
la source
OMG, j'allais supprimer le dodécaèdre ordinaire! Pour autant que je sache (je ne connais pas ou ne possède pas Mathematica), cela est conforme aux règles, donc +1.
Level River St,
@steveverrill Je ne pense pas que cela changerait trop la taille, mais je préférerais ne pas avoir à réécrire cela à partir de zéro.
LegionMammal978
Votre réponse reste valable, je ne vais pas changer les règles, ce serait une mauvaise forme. Cependant, en plus du bonus de 0,7 pour les trois polyèdres étoilés, j'ai offert une prime pour la réponse qui peut produire le plus des quatre polyèdres. Si vous avez décidé de mettre à jour votre réponse, je pense que vous pourriez économiser beaucoup d'octets en générant les coordonnées de manière algorithmique (voir la section info utile de la question.)
Level River St
@steveverrill, je le ferais, mais apparemment, les emplacements des sommets impliquent les racines des quartiques, et je ne trouve pas de motif.
LegionMammal978