Нарисуй рождественскую звезду / звездчатый додекаэдр

10

Звезды из бумаги - большая вещь в моей семье на Рождество, поэтому я подумал, что виртуальная звезда будет крутой.

Ниже приведено изображение обычного додекаэдра (из https://en.wikipedia.org/wiki/Dodecahedron , приписанного упомянутому там автору).

введите описание изображения здесь

Процесс звездообразования (википедия) применительно к многограннику включает расширение граней до пересечения других граней. Таким образом, начиная с правильного додекаэдра, мы получаем следующие формы:

Маленький звездчатый додекаэдр, Большой додекаэдр и Большой звездчатый додекаэдр

Изображение с http://jwilson.coe.uga.edu/emat6680fa07/thrash/asn1/stellations.html

введите описание изображения здесь

Это три возможных Звездочки додекаэдра (Вольфрама). Они образуют естественную прогрессию от додекаэдра до маленького звездчатого додекаэдра, большого додекаэдра и большого звездчатого додекаэдра, поскольку мы расширяем грани все дальше и дальше наружу.

задача

Ваша программа или функция должна отображать или выводить в файл изображения один из следующих многогранников: обычный додекаэдр, малый звездчатый додекаэдр, большой додекаэдр или большой звездчатый додекаэдр .

Цветовая схема должна быть как на втором изображении выше. Каждая из шести пар противоположных граней должна быть одного из шести цветов: красного, желтого, зеленого, голубого, синего и пурпурного. Вы можете использовать цвета по умолчанию с этими именами на вашем языке или в его документации или использовать цвета FF0000, FFFF00, 00FF00, 00FFFF, 0000FF и FF00FF (вы можете уменьшить их, уменьшив интенсивность до 75%, если хотите, например, уменьшая F до C.)

Обратите внимание, что мы определяем «лицо» как все области в одной плоскости. Таким образом, на изображениях выше передняя грань желтая (и параллельная задняя грань также будет желтой).

Фон должен быть черным, серым или белым. Края могут быть опущены, но должны быть черными, если они нарисованы.

правила

Ширина отображаемого многогранника должна составлять от 500 до 1000 пикселей (ширина определяется как максимальное расстояние между любыми двумя отображаемыми вершинами.)

Отображаемый многогранник должен быть в перспективной проекции (точка обзора на расстоянии не менее 5 ширины от многогранника) или в ортографической проекции (фактически перспективная проекция с точкой обзора на бесконечности).

Многогранник должен отображаться под любым углом. (Недопустимо выбирать самый простой из возможных углов и создавать жестко заданную 2D-форму.) Угол может быть задан пользователем одним из следующих способов:

  1. Ввод трех углов, соответствующих трем поворотам, от стандартного ввода или в качестве параметров функции или командной строки. Это могут быть либо углы Эйлера (где первое и последнее вращение вокруг одной и той же оси), либо углы Тейта-Брайана (где по одному повороту вокруг оси x, y и z) https://en.wikipedia.org/ wiki / Euler_angles (проще говоря, все происходит до тех пор, пока каждое вращение происходит вокруг оси x, y или z, а последовательные вращения - вокруг перпендикулярных осей.)

  2. Возможность для пользователя поворачивать многогранник с шагом не более 10 градусов относительно осей x и y и обновлять отображение любое произвольное количество раз (при условии, что ось z перпендикулярна экрану).

Многогранник должен быть сплошным, а не каркасным.

Не допускаются встроенные функции для рисования многогранников (я смотрю на тебя, Mathematica!)

счет

Это кодегольф. Самый короткий код в байтах побеждает.

Бонусы

Умножьте ваш счет на 0,5, если вы не используете встроенные функции для 3D-рисования.

Умножьте ваш счет на 0,7, если вы можете отобразить все три звенья додекаэдра, выбираемые пользователем целым числом 1-3, введенным из стандартного ввода, или параметром функции или командной строки.

Если вы выберете оба бонуса, ваш счет будет умножен на 0,5 * 0,7 = 0,35.

Полезная информация (источники как ниже)

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

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

Додекаэдр имеет 20 вершин. 8 из них образуют вершины куба со следующими декартовыми (x, y, z) координатами:

(± 1, ± 1, ± 1)

Остальные 12 следующие (фи - золотое сечение)

(0, ± 1 / φ, ± φ)

(± 1 / φ, ± φ, 0)

(± φ, 0, ± 1 / φ)

Выпуклая оболочка маленького звездчатого додекаэдра и большого додекаэдра, очевидно, является правильным додекаэдром. Внешние вершины описывают икосаэдр.

Согласно Википедии 12 вершин икосаэдра можно описать аналогично циклическим перестановкам (0, ± 1, ± φ). Внешние вершины малого звездчатого додекагерона и большого додекаэдра (в том же масштабе, что и вышеупомянутый додекаэдр) образуют больший икосаэдр, где координатами вершин являются циклические перестановки (0, ± φ ^ 2, ± φ).

Углы между гранями для додекаэдра и икосаэдра составляют 2 арктана (фи) и арккос (- (√5) / 3) соответственно.

Советы по повороту см. На странице https://en.wikipedia.org/wiki/Rotation_matrix.

РЕДАКТИРОВАТЬ: по ошибке я допустил обычный додекаэдр, и не могу сейчас его отозвать. Бонус х0,7 за рисование всех трех звездчатых многогранников остается. В Новый год я выдам вознаграждение в 100 за ответ, который может отображать большинство из четырех многогранников с кратчайшим кодом в качестве разрыва связи.

Уровень реки St
источник
@ LegionMammal978 встроенные функции для рисования многогранников (например dodecahedron) запрещены. В некоторых языках есть средства для создания 3D-моделей с такими командами, как triangle[[a,b,c],[p,q,r],[x,y,z]]. Эти языки обычно имеют встроенные функции для вращения и отображения модели, автоматически заботящиеся о том, чтобы не отображать скрытые лица и т. Д. Решения, подобные этим, разрешены, но не будут привлекать бонус. Цель бонуса - позволить языкам, не имеющим этих возможностей, быть конкурентоспособными, а также привлекать более интересные решения.
Уровень Река St
@ LegionMammal978 хаха, я знал, что Mathematica будет языком, который вызывает проблемы. Polyhedrondataне допускается, так как он явно является встроенным для рисования многогранников. Если ваш ответ не использует встроенные функции для рисования многогранников и соответствует другим правилам, тогда это приемлемо. Похоже, ваша точка зрения заключается в том, что, учитывая тот факт, что вы должны правильно окрашивать лица, Polyhedrondataэто не сильно вас спасет, поэтому на практике это может быть несколько произвольным ограничением. Я согласен в некоторой степени, но это справедливее для всех, если я не буду менять правила после публикации.
Уровень Река St

Ответы:

3

Python 2.7, 949 байт

Вот решение для правильного додекаэдра, построенного с использованием matplotlib. Грубый набросок для кода без гольфа (здесь не показан) был обрисован в общих чертах ниже:

  • Создание вершин Создание ребер (на основе 3 ближайших соседей, модуль scipy.spatial.KDtree)
  • Создание граней на основе циклов графа длиной 5 (модуль networkx)
  • Сделайте нормальные лица (и выберите те, которые обращены наружу нормально, numpy.cross)
  • Создание окраски на основе нормалей лица
  • Построение с использованием 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()

введите описание изображения здесь

Willem
источник
3

Ruby, 784 байта * 0,5 * 0,7 = 274,4

Мой собственный ответ, поэтому не имеет права на мою награду.

Подходит как для не-3D встроенного бонуса, так и для получения бонуса всех звезд.

->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}

Ввод в качестве параметров функции

Целое число 0..3, соответствующее правильному додекаэдру, маленький звездчатый додекаэдр, большой звездчатый додекаэдр

Массив из трех целых чисел, соответствующих углам в градусах для поворотов вокруг осей x, y и x (снова) (правильные углы Эйлера, обеспечивающие любое вращение.)

Выведите файл, p.svgкоторый можно отобразить в веб-браузере.

объяснение

массивы x, y, z внизу кода содержат координаты внешних точек одной грани маленького звездчатого додекаэдра. Это может быть вписано в икосаэдр, 12 вершин которого определяются циклическими перестановками (+/- 377, + / - 233, + / - 0). Обратите внимание, что 377 и 233 являются последовательными числами Фибоначчи, и поэтому 377/233 является превосходным приближением к золотому сечению.

дополнительный массив w содержит координаты x, умноженные на -1, что эквивалентно отражению в плоскости x. Функция f вызывается 6 раз, по одному разу для каждого цвета, с различными циклическими перестановками x, y, z и w, y, z.

Три поворота передаются как параметры в n []. чтобы использовать sin и cos в Ruby, это необходимо сделать include Math. чтобы избежать этого, косинус и синус угла получаются путем возведения квадратного корня из -1 "i"в степень (угол в градусах / 90). Действительная и мнимая части этого числа сохраняются в k (косинус) и l ( синус)

Перед вращением меняются значения x и y. Затем матричное умножение применяется к значениям y и z, чтобы обеспечить вращение вокруг оси x. Обмен значениями позволяет выполнять три поворота в цикле.

Пока у нас только одно кольцо очков. Чтобы получить остальное, нам нужно найти центр пятиугольника / звезды. Это делается путем нахождения среднего значения координат 5 вершин, которые хранятся в p, q, r.

Как упоминалось ранее, для каждого цвета выполняется только один вызов функции. Знак r (среднее значение координат z и, следовательно, координаты грани) проверяется. Если оно положительное, лицо является передним и, следовательно, видимым. Если оно отрицательное, лицо является задним лицом. Он невидим, и у нас нет вызова функции для противоположного лица. Поэтому все три координаты должны быть инвертированы. Знак r хранится в е, чтобы облегчить это.

Грани построены из 5 треугольников, вершины которых являются линейными комбинациями внешних вершин малого звездчатого додекаэдра и центра грани. В случае малого звездчатого додекаэдра для вершин треугольников мы устанавливаем a = 1 и b = 0 (вклад 1 из x, y, z и 0 из p, q, r). Для 2 базовых вершин треугольника мы устанавливаем c = -0,382 (вклад 1 / золотое отношение ^ 2 из x, y, z) и d = 1,382 (вклад из p, q, r.) Причина отрицательного вклада что базовые вершины треугольника определены в терминах противоположных вершин, которые находятся на противоположной стороне лица. Полученные координаты умножаются на е при необходимости.

Четыре неназванных массива, значения которых назначены для a,b,c,dсодержания требуемых значений для обычного додекаэдра, малого звездчатого додекаэдра, большого додекаэдра и большого звездчатого додекаэдра, выбранных в соответствии с переменной tОбратите внимание, что для маленького звездчатого додекаэдра и большого додекаэдра a + b = c + d = 1. Отношение a + b = c + d применяется к другим фигурам, но применяется другой масштаб.

Строка кода SVG создается для каждого треугольника. Он содержит идентификатор, полученный из суммы координат z трех вершин треугольника, описание вершин трех координат треугольника и цвет. обратите внимание, что мы видим прямо вниз по оси z в ортографической проекции. Таким образом, 2D x = 3D x и 2D y = 3D y. Строка добавлена ​​вh.

наконец, после того, как все вызовы функций завершены, h сортируется таким образом, что треугольники с наибольшим значением z (спереди) отображаются последними, и все это сохраняется в виде файла svg с соответствующим текстом верхнего и нижнего колонтитула.

Неуправляемый в тестовой программе

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]

Вывод

для маленького звездчатого додекаэдра (скоро добавим несколько изображений других полигонов)

1,0,0,0 домашняя позиция

введите описание изображения здесь

1,30,0,0 повернуть на 30 градусов

введите описание изображения здесь

1,0,30,0 повернуть вправо на 30 градусов (примечание: для идеального вида сбоку поворот будет atan(1/golden ratio)= 31,7 градуса, следовательно, мы все еще можем увидеть небольшой кусочек синего цвета)

введите описание изображения здесь

1,0,20,0 повернуть вправо на 20 градусов

введите описание изображения здесь

1,60,10, -63 повернуть вниз, вправо и вверх (пример ориентации возможен только при 3 поворотах)

введите описание изображения здесь

0,30,0,0 обычный додекаэдр

введите описание изображения здесь

2,0,20,0 великий додекаэдр

введите описание изображения здесь

3,45,45,45 большой звездчатый додекаэдр введите описание изображения здесь

Уровень реки St
источник
3

Mathematica, 426 424 байта

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]

Использует встроенный Graphics3Dдля отображения формы. Однако большинство байтов занято сжатыми местоположениями вершин, которые затем Partitionпреобразуются в форму, которую можно использовать Polygon. В заключение:

Обратите внимание, что эту форму можно вращать, нажимая и перетаскивая.

LegionMammal978
источник
О боже, я собирался удалить обычный додекаэдр! Насколько я могу судить (я не знаю или у меня нет Mathematica), это соответствует правилам, поэтому +1.
Уровень Река St
@steveverrill Не думаю, что это слишком сильно изменит размер, но я бы предпочел не переписывать это с нуля.
LegionMammal978
Ваш ответ остается в силе, я не собираюсь менять правила, это было бы плохо. Тем не менее, в дополнение к бонусу 0,7 для трех звездчатых многогранников, я предложил награду за ответ, который может дать большинство из четырех многогранников. Если вы решили обновить свой ответ, я считаю, что вы можете сэкономить много байтов, генерируя координаты алгоритмически (см. Раздел полезной информации вопроса.)
Level River St
@steveverrill, я бы, но, видимо, расположение вершин включает в себя корни квартик, и я не могу найти шаблон.
LegionMammal978