Desenhe uma estrela de Natal / dodecaedro estrelado

10

Estrelas de papel são uma coisa importante em minha família no Natal, então pensei que uma virtual seria legal.

Abaixo está uma imagem de um dodecaedro regular (de https://en.wikipedia.org/wiki/Dodecahedron , atribuído ao autor mencionado lá.)

insira a descrição da imagem aqui

O processo de stellation (wikipedia) quando aplicado a um poliedro envolve estender as faces até cruzar outras faces. Assim, começando com o dodecaedro regular, obtemos as seguintes formas:

Dodecaedro estrelado pequeno, dodecaedro grande e dodecaedro estrelado grande

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

insira a descrição da imagem aqui

Estas são as três stellations possíveis do dodecaedro (Wolfram). Eles formam uma progressão natural do dodecaedro para o pequeno dodecaedro estrelado, o grande dodecaedro e o dodecaedro estrelado, à medida que estendemos as faces cada vez mais.

Tarefa

Seu programa ou função deve exibir ou imprimir em um arquivo de imagem um dos seguintes poliedros: dodecaedro regular, dodecaedro estrelado pequeno, dodecaedro ótimo ou dodecaedro estrelado grande .

O esquema de cores deve ser como a segunda imagem acima. Cada um dos seis pares de faces opostas deve ser uma das seis cores vermelho, amarelo, verde, ciano, azul e magenta. Você pode usar cores padrão com esses nomes em seu idioma ou em sua documentação ou usar as cores FF0000, FFFF00, 00FF00, 00FFFF, 0000FF e FF00FF (você pode diminuir o tom reduzindo a intensidade para um mínimo de 75%, se desejar, por exemplo, reduzindo os Fs para os Cs.)

Observe que definimos uma "face" como sendo todas as áreas no mesmo plano. Assim, nas imagens acima, a face frontal é amarela (e a face traseira paralela também seria amarela.)

O plano de fundo deve ser preto, cinza ou branco. As arestas podem ser omitidas, mas devem ser pretas se desenhadas.

Regras

O poliedro exibido deve ter entre 500 e 1000 pixels de largura (largura é definida como a distância máxima entre dois vértices exibidos).

O poliedro exibido deve estar em projeção em perspectiva (ponto de vista a pelo menos 5 larguras do poliedro) ou projeção ortográfica (efetivamente uma projeção em perspectiva com o ponto de vista no infinito).

O poliedro deve ser exibido de qualquer ângulo. (Não é aceitável escolher o ângulo mais fácil possível e criar uma forma 2D codificada.) O ângulo pode ser especificado pelo usuário de uma das seguintes maneiras:

  1. Entrada de três ângulos correspondentes a três rotações, a partir de stdin, ou como parâmetros de função ou linha de comando. Podem ser ângulos de Euler (onde a primeira e a última rotação são sobre o mesmo eixo) ou ângulos de Tait-Bryan (onde há uma rotação cada um sobre o eixo x, ye z) https://en.wikipedia.org/ wiki / Euler_angles (simplesmente, qualquer coisa vale desde que cada rotação seja sobre o eixo x, y ou z e as rotações consecutivas sejam sobre eixos perpendiculares.)

  2. Facilidade para o usuário girar o poliedro em etapas não superiores a 10 graus sobre os eixos xey e atualizar a exibição, qualquer número arbitrário de vezes (assumindo o eixo z perpendicular à tela).

O poliedro deve ser sólido, não com estrutura de arame.

Não são permitidos componentes internos para desenhar poliedros (estou olhando para você, Mathematica!)

Pontuação

Isso é codegolf. O menor código em bytes vence.

Bónus

Multiplique sua pontuação por 0,5 se você não usar os recursos internos para desenho 3D.

Multiplique sua pontuação por 0,7 se conseguir exibir todas as três estrelações do dodecaedro, selecionáveis ​​pelo usuário por um número inteiro 1-3 digitado a partir de stdin ou por função ou parâmetro de linha de comando.

Se você optar pelos dois bônus, sua pontuação será multiplicada por 0,5 * 0,7 = 0,35

Informações úteis (fontes como abaixo)

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

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

O dodecaedro possui 20 vértices. 8 deles formam os vértices de um cubo com as seguintes coordenadas cartesianas (x, y, z):

(± 1, ± 1, ± 1)

Os 12 restantes são os seguintes (phi é a proporção áurea)

(0, ± 1 / φ, ± φ)

(± 1 / φ, ± φ, 0)

(± φ, 0, ± 1 / φ)

O casco convexo do pequeno dodecaedro estrelado e do grande dodecaedro é obviamente um dodecaedro regular. Os vértices externos descrevem um icosaedro.

De acordo com a Wikipedia, os 12 vértices de um icosaedro podem ser descritos de maneira semelhante às permutações cíclicas de (0, ± 1, ± φ). Os vértices externos do pequeno dodecaheron estrelado e do grande dodecedro (na mesma escala do dodecaedro acima) formam um icosaedro maior, onde as coordenadas dos vértices são permutações cíclicas de (0, ± φ ^ 2, ± φ).

Os ângulos entre as faces do dodecaedro e do icosaedro são 2 arctan (phi) e arccos (- (√5) / 3), respectivamente.

Para obter dicas sobre rotação, consulte https://en.wikipedia.org/wiki/Rotation_matrix

EDIT: Por engano, eu permiti o dodecaedro regular e não posso retirá-lo agora. O bônus x0,7 para desenhar todos os três poliedros estrelados permanece. No dia de Ano Novo, emitirei uma recompensa de 100 pela resposta que pode exibir a maioria dos quatro poliedros, com o menor código possível no desempate.

Level River St
fonte
@ Os legins LegionMammal978 para desenhar poliedros (por exemplo dodecahedron) não são permitidos. Alguns idiomas têm recursos para a construção de modelos 3D com comandos como triangle[[a,b,c],[p,q,r],[x,y,z]]. Esses idiomas geralmente têm recursos internos para girar e exibir o modelo, cuidando automaticamente para não exibir faces ocultas, etc. Soluções como essas são permitidas, mas não atrairão o bônus. O objetivo do bônus é permitir que idiomas que não possuem essas facilidades sejam competitivos e também atrair soluções mais interessantes.
Level River St
@ LegionMammal978 haha, eu sabia que o Mathematica seria a linguagem que causava problemas. Polyhedrondatanão é permitido, pois é claramente um componente interno para o desenho de poliedros. Se a sua resposta não usar os componentes internos para desenhar poliedros e cumprir as outras regras, será aceitável. Parece que seu argumento é que, dado que você precisa colorir os rostos corretamente, Polyhedrondataisso não pouparia muito, de modo que, na prática, pode ser uma restrição um tanto arbitrária. Eu concordo até certo ponto, mas é mais justo para todos se eu evitar alterar as regras após a postagem.
Level River St

Respostas:

3

Python 2.7, 949 bytes

Aqui está a solução para o dodecaedro comum plotado usando matplotlib. A descrição geral do código não-destruído (não mostrado aqui) foi descrita abaixo:

  • Criar vértices Criar arestas (com base nos 3 vizinhos mais próximos, módulo scipy.spatial.KDtree)
  • Faça rostos com base em ciclos gráficos com comprimento 5 (módulo de redex)
  • Faça normais faciais (e selecione aquelas com a face externa normal, numpy.cross)
  • Gere cores com base nas normais do rosto
  • Plotagem usando 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()

insira a descrição da imagem aqui

Willem
fonte
3

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

Minha própria resposta, portanto, não é elegível para minha recompensa.

Qualificado para o bônus embutido não 3D e para o bônus de desenho de todas as 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}

Entrada como parâmetros de função

Um número inteiro 0..3 correspondente ao dodecaedro regular, pequeno dodecaedro estrelado, grande dodecaedro estrelado

Uma matriz de três números inteiros correspondentes a ângulos de graus para rotações em torno dos eixos x, ye ex (novamente) (ângulos de Euler adequados, permitindo que qualquer rotação seja alcançada).

Saída de um arquivo p.svgque pode ser exibido em um navegador da web.

Explicação

as matrizes x, y, z na parte inferior do código contêm as coordenadas dos pontos externos de uma face de um pequeno dodecaedro estrelado. Isso pode ser inscrito no icosaedro cujos 12 vértices são definidos pelas permutações cíclicas de (+/- 377, + / - 233, + / - 0). Observe que 377 e 233 são números consecutivos de Fibonacci e, portanto, 377/233 é uma excelente aproximação à proporção áurea.

uma matriz adicional w contém as coordenadas x multiplicadas por -1, equivalentes à reflexão no plano x. A função f é chamada 6 vezes, uma vez para cada cor, com as diferentes permutações cíclicas de x, y, z e w, y, z.

As três rotações são passadas como parâmetros em n []. Para usar sin e cos em Ruby, é necessário include Math. para evitar isso, o cosseno e o seno do ângulo são obtidos elevando a raiz quadrada de -1 "i"a uma potência de (ângulo em graus / 90). As partes reais e imaginárias desse número são armazenadas em k (cosseno) e l ( seno)

Antes da rotação, os valores x e y são trocados. A multiplicação da matriz é aplicada aos valores de y e z para dar uma rotação em torno do eixo x. A troca de valores permite que as três rotações sejam realizadas em loop.

Até agora, só temos um anel de pontos. Para obter o resto, precisamos encontrar o centro do pentágono / estrela. Isso é feito encontrando a média das coordenadas dos 5 vértices, que são armazenados em p, q, r.

Como mencionado anteriormente, apenas uma chamada de função por cor é feita. O sinal de r (a média das coordenadas z e, portanto, a coordenada da face) é testado. Se for positivo, o rosto é um rosto frontal e, portanto, visível. Se for negativo, o rosto é um verso. É invisível e não temos chamada de função para a face oposta. Portanto, todas as três coordenadas devem ser invertidas. O sinal de r é armazenado em e para facilitar isso.

A face é construída de 5 triângulos, cujos vértices são combinações lineares dos vértices externos do pequeno dodecaedro estrelado e do centro da face. No caso do pequeno dodecaedro estrelado, para as pontas dos triângulos, definimos a = 1 eb = 0 (contribuição 1 de x, y, z e 0 de p, q, r). Para os 2 vértices base do triângulo, definimos c = -0,382 (contribuição 1 / proporção áurea ^ 2 de x, y, z) ed = 1,382 (contribuição de p, q, r.) A razão da contribuição negativa é que os vértices base do triângulo são definidos em termos das pontas opostas, que estão no lado oposto da face. As coordenadas obtidas são multiplicadas por e conforme necessário.

As quatro matrizes sem nome cujos valores são atribuídos para a,b,c,dconter os valores exigidos para o dodecaedro regular, pequeno dodecaedro estrelado, ótimo dodecaedro e ótimo dodecaedro estrelado, selecionados de acordo com a variável tObserve que para o pequeno dodecaedro estrelado e o grande dodecaedro, a + b = c + d = 1. A relação a + b = c + d se aplica às outras formas, mas uma escala diferente é aplicada.

Uma linha de código svg é criada para cada triângulo. Ele contém um ID derivado da soma das coordenadas z dos 3 vértices do triângulo, uma descrição dos vértices das três coordenadas do triângulo e uma cor. note que vemos diretamente o eixo z na projeção ortográfica. Assim 2D x = 3D x e 2D y = 3D y. A linha é adicionada ah.

finalmente, depois que todas as chamadas de função são concluídas, h é classificado de forma que os triângulos de maior valor z (na frente) sejam plotados por último e a coisa toda seja salva como um arquivo svg com o texto apropriado do cabeçalho e rodapé.

Ungolfed in program program

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]

Resultado

para pequenos dodecaedros estrelados (em breve adicionaremos algumas imagens dos outros polígonos)

1,0,0,0 posição inicial

insira a descrição da imagem aqui

1,30,0,0 gire 30 graus

insira a descrição da imagem aqui

1,0,30,0 gire 30 graus para a direita (nota: para uma vista lateral perfeita, a rotação seria atan(1/golden ratio)= 31,7 graus, portanto ainda podemos ver uma pequena lasca de azul)

insira a descrição da imagem aqui

1,0,20,0 gire para a direita 20 graus

insira a descrição da imagem aqui

1,60,10, -63 gire para baixo, para a direita e para cima (exemplo de orientação possível apenas com 3 rotações)

insira a descrição da imagem aqui

0,30,0,0 dodecaedro regular

insira a descrição da imagem aqui

2,0,20,0 ótimo dodecaedro

insira a descrição da imagem aqui

3,45,45,45 grande dodecaedro estrelado insira a descrição da imagem aqui

Level River St
fonte
3

Mathematica, 426 424 bytes

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]

Usa o interno Graphics3Dpara exibir a forma. A maioria dos bytes é ocupada pelos locais dos vértices compactados, no entanto, os quais são então Partitioneditados em um formato utilizável por Polygon. Finalmente:

Observe que essa forma pode ser girada clicando e arrastando.

LegionMammal978
fonte
OMG, eu estava indo para excluir dodecaedro regular! Tanto quanto eu posso dizer (eu não conheço ou não tenho o Mathematica), isso está em conformidade com as regras, então +1.
Level River St
@ steveverrill Eu não acho que isso mudaria muito o tamanho, mas eu preferiria não ter que reescrever isso do zero.
usar o seguinte
Sua resposta permanece válida, não vou mudar as regras, seria uma forma ruim. No entanto, além do bônus de 0,7 para os três poliedros estrelados, ofereci uma recompensa pela resposta que pode produzir o máximo dos quatro poliedros. Se você se decidir atualizar sua resposta, eu acho que você pode salvar um monte de bytes, gerando as coordenadas através de algoritmos (consulte a seção de informações úteis sobre a questão.)
Nível River St
@ steveverrill, eu gostaria, mas aparentemente os locais dos vértices envolvem as raízes dos quartics, e não consigo encontrar um padrão.
usar o seguinte