Zeichne einen Weihnachtsstern / stelliertes Dodekaeder


10

Papiersterne sind eine große Sache in meiner Familie zu Weihnachten, deshalb dachte ich, ein virtueller wäre cool.

Unten sehen Sie ein Bild eines regulären Dodekaeders (von https://en.wikipedia.org/wiki/Dodecahedron , das dem dort genannten Autor zugeschrieben wird).

Geben Sie hier die Bildbeschreibung ein

Der Prozess der Stellation (Wikipedia) bei Anwendung auf ein Polyeder umfasst das Erweitern der Flächen, bis sie andere Flächen kreuzen. Ausgehend vom regulären Dodekaeder erhalten wir also die folgenden Formen:

Kleines stelliertes Dodekaeder, großes Dodekaeder und großes stelliertes Dodekaeder

Bild von http://jwilson.coe.uga.edu/emat6680fa07/thrash/asn1/stellations.html

Geben Sie hier die Bildbeschreibung ein

Dies sind die drei möglichen Stellationen des Dodekaeders (Wolfram). Sie bilden eine natürliche Entwicklung vom Dodekaeder zum kleinen Dodekaeder, zum großen Dodekaeder und zum großen Dodekaeder, wenn wir die Gesichter immer weiter ausdehnen.

Aufgabe

Ihr Programm oder Ihre Funktion sollte eines der folgenden Polyeder anzeigen oder in eine Bilddatei ausgeben: Normales Dodekaeder, kleines Stern-Dodekaeder, großes Dodekaeder oder großes stelliertes Dodekaeder .

Das Farbschema sollte wie das zweite Bild oben sein. Jedes der sechs Paare gegenüberliegender Flächen muss eine der sechs Farben Rot, Gelb, Grün, Cyan, Blau und Magenta sein. Sie können Standardfarben mit diesen Namen in Ihrer Sprache oder deren Dokumentation verwenden oder die Farben FF0000, FFFF00, 00FF00, 00FFFF, 0000FF und FF00FF verwenden (Sie können diese abschwächen, indem Sie die Intensität auf ein Minimum von 75% reduzieren, falls gewünscht). zum Beispiel durch Reduzieren der Fs auf Cs.)

Beachten Sie, dass wir ein "Gesicht" als alle Bereiche in derselben Ebene definieren. Daher ist in den Bildern oben die Vorderseite gelb (und die parallele Rückseite wäre ebenfalls gelb).

Der Hintergrund sollte schwarz, grau oder weiß sein. Kanten können weggelassen werden, sollten aber beim Zeichnen schwarz sein.

Regeln

Das angezeigte Polyeder muss zwischen 500 und 1000 Pixel breit sein (Breite ist definiert als der maximale Abstand zwischen zwei angezeigten Scheitelpunkten.)

Das angezeigte Polyeder muss in perspektivischer Projektion (Blickwinkel mindestens 5 Breiten vom Polyeder entfernt) oder in orthografischer Projektion (effektiv eine perspektivische Projektion mit dem Blickwinkel im Unendlichen) sein.

Das Polyeder muss aus jedem Winkel sichtbar sein. (Es ist nicht akzeptabel, den einfachsten Winkel auszuwählen und eine fest codierte 2D-Form zu erstellen.) Der Winkel kann vom Benutzer auf eine der folgenden Arten festgelegt werden:

  1. Eingabe von drei Winkeln, die drei Umdrehungen entsprechen, von stdin oder als Funktions- oder Befehlszeilenparameter. Dies können entweder Euler-Winkel (bei denen die erste und die letzte Drehung ungefähr dieselbe Achse haben) oder Tait-Bryan-Winkel (bei denen jeweils eine Drehung um die x-, y- und z-Achse erfolgt) sein. Https://en.wikipedia.org/ wiki / Euler_angles (einfach ausgedrückt, alles geht so lange, wie sich jede Drehung um die x-, y- oder z-Achse und aufeinanderfolgende Drehungen um senkrechte Achsen drehen .)

  2. Möglichkeit für den Benutzer, das Polyeder in Schritten von nicht mehr als 10 Grad um die x- und y-Achse zu drehen und die Anzeige beliebig oft zu aktualisieren (unter der Annahme einer z-Achse senkrecht zum Bildschirm).

Das Polyeder muss fest sein, kein Drahtgitter.

Es sind keine Einbauten zum Zeichnen von Polyedern erlaubt (ich sehe dich an, Mathematica!)

Wertung

Das ist Codegolf. Der kürzeste Code in Bytes gewinnt.

Boni

Multiplizieren Sie Ihre Punktzahl mit 0,5, wenn Sie keine integrierten Funktionen für das 3D-Zeichnen verwenden.

Multiplizieren Sie Ihre Punktzahl mit 0,7, wenn Sie alle drei Sternbilder des Dodekaeders anzeigen können, die vom Benutzer durch eine von stdin eingegebene Ganzzahl 1-3 oder durch Funktions- oder Befehlszeilenparameter ausgewählt werden können.

Wenn Sie sich für beide Boni entscheiden, wird Ihre Punktzahl mit 0,5 * 0,7 = 0,35 multipliziert

Nützliche Informationen (Quellen wie unten)

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

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

Das Dodekaeder hat 20 Eckpunkte. 8 von ihnen bilden die Eckpunkte eines Würfels mit den folgenden kartesischen (x, y, z) Koordinaten:

(± 1, ± 1, ± 1)

Die restlichen 12 sind wie folgt (Phi ist der goldene Schnitt)

(0, ± 1 / φ, ± φ)

(± 1 / φ, ± φ, 0)

(± φ, 0, ± 1 / φ)

Die konvexe Hülle des kleinen Stern-Dodekaeders und des großen Dodekaeders ist offensichtlich ein reguläres Dodekaeder. Die äußeren Eckpunkte beschreiben ein Ikosaeder.

Laut Wikipedia können die 12 Eckpunkte eines Ikosaeders auf ähnliche Weise wie zyklische Permutationen von (0, ± 1, ± φ) beschrieben werden. Die äußeren Eckpunkte des kleinen Stern-Dodekahers und des großen Dodechaeders (im gleichen Maßstab wie das Dodekaeder oben) bilden ein größeres Ikosaeder, wobei die Koordinaten der Eckpunkte zyklische Permutationen von (0, ± φ ^ 2, ± φ) sind.

Die Winkel zwischen den Flächen für das Dodekaeder und das Ikosaeder betragen 2 Arctan (Phi) bzw. Arccos (- (√5) / 3).

Tipps zum Drehen finden Sie unter https://en.wikipedia.org/wiki/Rotation_matrix

EDIT: Aus Versehen habe ich das reguläre Dodekaeder zugelassen und kann es jetzt nicht zurückziehen. Der x0,7-Bonus für das Zeichnen aller drei sternförmigen Polyeder bleibt erhalten. Am Neujahrstag werde ich ein Kopfgeld von 100 für die Antwort ausgeben, die die meisten der vier Polyeder anzeigen kann, mit dem kürzesten Code als Gleichstand.


@ LegionMammal978-Builds zum Zeichnen von Polyedern (z dodecahedron. B. ) sind nicht zulässig . Einige Sprachen verfügen über Funktionen zum Erstellen von 3D-Modellen mit Befehlen wie triangle[[a,b,c],[p,q,r],[x,y,z]]. Diese Sprachen verfügen im Allgemeinen über integrierte Funktionen zum Drehen und Anzeigen des Modells, wobei automatisch darauf geachtet wird, dass keine verborgenen Gesichter angezeigt werden usw. Lösungen wie diese sind zulässig, ziehen jedoch keinen Bonus an. Der Zweck des Bonus besteht darin, Sprachen ohne diese Einrichtungen wettbewerbsfähig zu machen und auch interessantere Lösungen zu finden.
Level River St

@ LegionMammal978 haha, ich wusste, dass Mathematica die Sprache sein würde, die Probleme verursacht. Polyhedrondataist nicht zulässig, da es sich eindeutig um ein eingebautes Gerät zum Zeichnen von Polyedern handelt. Wenn Ihre Antwort keine eingebauten Elemente zum Zeichnen von Polyedern verwendet und den anderen Regeln entspricht, ist dies akzeptabel. Ihr Punkt scheint zu sein, dass Sie angesichts der Tatsache, dass Sie die Gesichter richtig einfärben müssen, Polyhedrondatasowieso nicht viel sparen würden, so dass dies in der Praxis eine etwas willkürliche Einschränkung sein kann. Ich stimme bis zu einem gewissen Grad zu, aber es ist für alle fairer, wenn ich es vermeide, Regeln nach dem Posten zu ändern.
Level River St

Antworten:


3

Python 2.7, 949 Bytes

Hier ist die Lösung für das reguläre Dodekaeder, das mit matplotlib aufgezeichnet wurde. Der grobe Umriss für den ungolfed Code (hier nicht gezeigt) ist unten skizziert:

  • Scheitelpunkte erstellen Kanten erstellen (basierend auf 3 nächsten Nachbarn, Modul scipy.spatial.KDtree)
  • Erstellen Sie Gesichter basierend auf Grafikzyklen mit der Länge 5 (Modul networkx).
  • Machen Sie Facenormals (und wählen Sie diejenigen mit nach außen gerichteten normalen, numpy.cross)
  • Generieren Sie Farben basierend auf Gesichtsnormalen
  • Plotten mit 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()

Geben Sie hier die Bildbeschreibung ein


3

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

Meine eigene Antwort, daher nicht für mein Kopfgeld berechtigt.

Berechtigt sowohl für den eingebauten Nicht-3D-Bonus als auch für den Bonus zum Zeichnen aller Stellationen.

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

Eingabe als Funktionsparameter

Eine ganze Zahl 0..3, die einem regulären Dodekaeder, einem kleinen Dodekaeder und einem großen Dodekaeder entspricht

Eine Anordnung von drei ganzen Zahlen, die Gradwinkeln für Rotationen um die x-, y- und x-Achse (wieder) entsprechen (richtige Euler-Winkel, damit jede Rotation erreicht werden kann).

Geben Sie eine Datei aus p.svg, die in einem Webbrowser angezeigt werden kann.

Erläuterung

Die Arrays x, y, z am unteren Rand des Codes enthalten die Koordinaten der äußeren Punkte einer Seite eines kleinen Stern-Dodekaeders. Dies kann in das Ikosaeder eingeschrieben werden, dessen 12 Eckpunkte durch die zyklischen Permutationen von (+/- 377, + / - 233, + / - 0) definiert sind. Beachten Sie, dass 377 und 233 aufeinanderfolgende Fibonacci-Zahlen sind und daher 377/233 eine hervorragende Annäherung an den Goldenen Schnitt darstellt.

Ein zusätzliches Array w enthält die x-Koordinaten multipliziert mit -1, was der Reflexion in der x-Ebene entspricht. Die Funktion f wird 6 Mal aufgerufen, einmal für jede Farbe, mit den verschiedenen zyklischen Permutationen von x, y, z und w, y, z.

Die drei Umdrehungen werden als Parameter in n [] übergeben. Um sin und cos in Ruby zu verwenden, ist es notwendig, dies zu tun include Math. Um dies zu vermeiden, werden der Kosinus und der Sinus des Winkels erhalten, indem die Quadratwurzel von -1 "i"auf eine Potenz von (Winkel in Grad / 90) angehoben wird. Der Real- und Imaginärteil dieser Zahl werden in k (Kosinus) und l ( Sinus)

Vor der Drehung werden x- und y-Werte ausgetauscht. Dann wird eine Matrixmultiplikation auf die y- und z-Werte angewendet, um eine Drehung um die x-Achse zu ergeben. Durch den Austausch von Werten können die drei Umdrehungen in einer Schleife ausgeführt werden.

Bisher haben wir nur einen Punktering. Um den Rest zu erhalten, müssen wir das Zentrum des Fünfecks / Sterns finden. Dies erfolgt durch Ermitteln des Durchschnitts der Koordinaten der 5 Eckpunkte, die in p, q, r gespeichert sind.

Wie bereits erwähnt, wird nur ein Funktionsaufruf pro Farbe ausgeführt. Das Vorzeichen von r (der Durchschnitt der z-Koordinaten und damit die Koordinate des Gesichts) wird getestet. Wenn es positiv ist, ist das Gesicht eine Vorderseite und daher sichtbar. Wenn es negativ ist, ist das Gesicht eine Rückseite. Es ist unsichtbar und wir haben keinen Funktionsaufruf für das gegenüberliegende Gesicht. Daher müssen alle drei Koordinaten invertiert werden. Das Vorzeichen von r wird in e gespeichert, um dies zu erleichtern.

Das Gesicht besteht aus 5 Dreiecken, deren Eckpunkte lineare Kombinationen der äußeren Eckpunkte des kleinen sternförmigen Dodekaeders und der Mitte des Gesichts sind. Im Fall des kleinen sternförmigen Dodekaeders setzen wir für die Spitzen der Dreiecke a = 1 und b = 0 (Beitrag 1 von x, y, z und 0 von p, q, r). Für die 2 Basisscheitelpunkte des Dreiecks setzen wir c = -0,382 (Beitrag 1 / Goldener Schnitt ^ 2 von x, y, z) und d = 1,382 (Beitrag von p, q, r). Der Grund für den negativen Beitrag ist dass die Basisscheitelpunkte des Dreiecks in Bezug auf die gegenüberliegenden Spitzen definiert sind, die sich auf der gegenüberliegenden Seite der Fläche befinden. Die erhaltenen Koordinaten werden nach Bedarf mit e multipliziert.

Die vier unbenannten Arrays, deren Werte so zugewiesen sind, a,b,c,ddass sie die erforderlichen Werte für das reguläre Dodekaeder, das kleine Dodekaeder, das große Dodekaeder und das große Dodekaeder enthalten, werden gemäß der Variablen ausgewählt. tBeachten Sie, dass für das kleine Dodekaeder und das große Dodekaeder a + b = c + d = 1. Die Beziehung a + b = c + d gilt für die anderen Formen, es wird jedoch eine andere Skala angewendet.

Für jedes Dreieck wird eine Zeile SVG-Code erstellt. Diese enthält eine ID, die aus der Summe der z-Koordinaten der drei Eckpunkte des Dreiecks, einer Beschreibung der Eckpunkte der drei Koordinaten des Dreiecks und einer Farbe abgeleitet ist. Beachten Sie, dass wir in der orthografischen Projektion direkt entlang der z-Achse schauen. Somit ist 2D x = 3D x und 2D y = 3D y. Die Zeile wird hinzugefügth.

Nachdem alle Funktionsaufrufe abgeschlossen sind, wird h so sortiert, dass die Dreiecke mit dem höchsten z-Wert (vorne) zuletzt gezeichnet werden, und das Ganze wird als SVG-Datei mit dem entsprechenden Kopf- und Fußzeilentext gespeichert.

Ungolfed im Testprogramm

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]

Ausgabe

für kleine sternförmige Dodekaeder (wird bald einige Bilder der anderen Polygone hinzufügen)

1,0,0,0 Ausgangsposition

Geben Sie hier die Bildbeschreibung ein

1,30,0,0 um 30 Grad nach unten drehen

Geben Sie hier die Bildbeschreibung ein

1,0,30,0 um 30 Grad nach rechts drehen (Hinweis: Für eine perfekte Seitenansicht wäre die Drehung atan(1/golden ratio)= 31,7 Grad, daher sehen wir immer noch einen kleinen blauen Streifen)

Geben Sie hier die Bildbeschreibung ein

1,0,20,0 um 20 Grad nach rechts drehen

Geben Sie hier die Bildbeschreibung ein

1,60,10, -63 nach unten, rechts und oben drehen (Orientierungsbeispiel nur mit 3 Umdrehungen möglich)

Geben Sie hier die Bildbeschreibung ein

0,30,0,0 reguläres Dodekaeder

Geben Sie hier die Bildbeschreibung ein

2,0,20,0 großes Dodekaeder

Geben Sie hier die Bildbeschreibung ein

3,45,45,45 großes Stern-Dodekaeder Geben Sie hier die Bildbeschreibung ein


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]

Verwendet die integrierte Funktion Graphics3D, um die Form anzuzeigen. Die meisten Bytes werden jedoch von den komprimierten Scheitelpunktpositionen belegt, die dann Partitionin eine Form gebracht werden, die von verwendet werden kann Polygon. Schließlich:

Beachten Sie, dass diese Form durch Klicken und Ziehen gedreht werden kann.


OMG, ich wollte reguläres Dodekaeder löschen! Soweit ich das beurteilen kann (ich kenne oder habe Mathematica nicht), entspricht dies den Regeln, also +1.
Level River St

@steveverrill Ich denke nicht, dass es die Größe zu sehr verändern würde, aber ich würde es vorziehen, dies nicht von Grund auf neu schreiben zu müssen.
LegionMammal978

Ihre Antwort bleibt gültig, ich werde die Regeln nicht ändern, es wäre eine schlechte Form. Zusätzlich zu dem Bonus von 0,7 für die dreistufigen Polyeder habe ich eine Prämie für die Antwort angeboten, mit der die meisten der vier Polyeder erzeugt werden können. Wenn Sie sich entschieden haben, Ihre Antwort zu aktualisieren, können Sie wahrscheinlich viele Bytes sparen, indem Sie die Koordinaten algorithmisch generieren (siehe den nützlichen Info-Abschnitt der Frage).
Level River St

@steveverrill, ich würde, aber anscheinend beinhalten die Scheitelpunkte die Wurzeln von Quartics, und ich kann kein Muster finden.
LegionMammal978
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.