Gasket Weaving - Zeichne einen Sierpiński-Knoten


33

Wenn eine ganze Zahl N> = 2 gegeben ist, erzeugen Sie ein Bild, das einen Sierpiński-Knoten vom Grad N zeigt.

Hier sind zum Beispiel Knoten der Grade 2, 3, 4 und 5:

Grad 2 Grad 3 Abschluss 4 Grad 5

Klicken Sie auf die Bilder, um sie in voller Größe anzuzeigen (je höher der Grad, desto größer das Bild).

Spezifikation

  1. Ein Sierpiński-Knoten vom Grad N wird unter Verwendung der Eckpunkte eines Sierpiński-Dreiecks vom Grad N als Orientierungspunkte gezeichnet. Ein Sierpiński-Dreieck vom Grad N besteht aus drei Sierpiński-Dreiecken vom Grad N-1, die zu einem größeren Dreieck angeordnet sind. Ein Sierpiński-Dreieck vom Grad 0 ist ein gleichseitiges Dreieck.
  2. Die kleinsten Komponentendreiecke haben eine Seitenlänge von 64, was dem Sierpiński-Dreieck, auf dem der Knoten basiert, eine Gesamtseitenlänge von 0 ergibt 64 * 2 ^ N
  3. Die Mitte des äußeren Dreiecks befindet sich in der Bildmitte. Dies ergibt oben und unten nicht den gleichen Leerraum.
  4. Der Ausgang ist ein quadratisches Bild der Seitenlänge in Decke (64 * 2 ^ N * 2 / ROOT3)dem Decke (x)ist ceiling(x)die kleinste ganze Zahl größer oder gleich x ist . Dies ist gerade groß genug, damit der obere Scheitelpunkt des darunter liegenden Sierpiński-Dreiecks im Bild enthalten ist, wenn sich die Mitte des Dreiecks in der Bildmitte befindet.
  5. Die einzelne Kurve muss streng abwechselnd über und unter sich verlaufen. Lösungen können zwischen unter dann über oder über dann unter wählen.
  6. Die Beispielbilder zeigen einen schwarzen Vordergrund und einen weißen Hintergrund. Sie können zwei verschiedene Farben auswählen. Anti-Aliasing ist zulässig, aber nicht erforderlich.
  7. Es darf keine Lücken geben, in denen sich zwei Bögen treffen oder die Kurve über oder unter sich verläuft.
  8. Die Ausgabe kann in eine beliebige Bilddatei im Rasterformat oder in eine beliebige Bilddatei im Vektorformat erfolgen, die eine korrekte Standardanzeigegröße enthält. Wenn Sie direkt auf dem Bildschirm anzeigen, muss dies in einer Form erfolgen, mit der durch Scrollen das gesamte Bild angezeigt werden kann, wenn es größer als der Bildschirm ist.

Bestimmung von Bogenmitte, Radius und Dicke

  1. Der Knoten ist als eine Reihe von Kreisbögen konstruiert, die sich an Punkten treffen, an denen ihre Tangenten parallel sind, um eine nahtlose Verbindung zu erzielen. Diese Bögen werden als ringförmige Sektoren (Bögen mit Dicke) angezeigt.
  2. Die Zentren dieser Bögen sind die Eckpunkte der kleinsten auf dem Kopf stehenden Dreiecke. Jeder solche Scheitelpunkt ist der Mittelpunkt genau eines Bogens.
  3. Jeder Bogen hat einen Radius von 64 * ROOT3 / 2
  4. Die Ausnahme besteht darin, dass die Bögen in den drei äußersten Dreiecken (an den Ecken des großen Dreiecks) einen Mittelpunkt haben, der der Mittelpunkt der beiden benachbarten inneren Scheitelpunkte ist, und somit einen Radius von 64 * (ROOT3 / 2-1 / 2)
  5. Jeder Bogen wird mit einer Gesamtdicke (Differenz zwischen Innenradius und Außenradius) von 64 * (ROOT3 / 2) / 4und den schwarzen Rändern von jeweils einer Dicke von dargestellt. 64 * (ROOT3 / 2) / 16Die Kurve muss diese Ränder haben und darf nicht nur ein fester Streifen sein.

Maßeinheiten

  1. Alle Abstände sind in Pixel angegeben (1 ist der horizontale oder vertikale Abstand zwischen 2 benachbarten Pixeln).
  2. Die Quadratwurzel von 3 muss auf 7 signifikante Stellen genau sein. Das heißt, Ihre Berechnungen müssen der Verwendung eines ROOT3 entsprechen, so dass1.7320505 <= ROOT3 < 1.7320515

Wertung

Der kürzeste Code in Bytes gewinnt.


Für diejenigen, die sich fragen, sind N = 0 und N = 1 nicht enthalten, da sie einem Kreis und einem Kleeblatt entsprechen, die nicht ganz mit dem Muster übereinstimmen, das für N> = 2 gilt. Ich würde erwarten, dass die meisten Ansätze für diese Herausforderung Sonderfallcode für 0 und 1 hinzufügen müssten, daher habe ich beschlossen, sie wegzulassen.


1
Wäre es hilfreich, ein Diagramm zu haben, das zeigt, worauf sich alle Zahlen beziehen?
Trichoplax

Sind 7 signifikante Zahlen für kleine Details wie die Dicke der Linie usw. wirklich notwendig, bevor ich meine Antwort- / Add-Eckpunkte spiele? Eine Genauigkeit wie "7 signifikante Zahlen oder 1 Pixel, je nachdem, welcher Wert größer ist" erscheint angemessener.
Level River St

@LevelRiverSt Da die Größe des Bilds mit der Eingabe skaliert, reichen nicht einmal 7 signifikante Zahlen für eine Pixelgenauigkeit von 1 für ein größeres N aus. Nach einigen Diskussionen im Chat habe ich mich auf 7 signifikante Zahlen festgelegt, sodass alle Antworten gleich bleiben Standard.
Trichoplax

Ja, es ist für die Skalierung des Bildes für größere N erforderlich. 7 signifikante Zahlen auf einem Bild mit 1000000 x 1000000 entsprechen 0,1 Pixeln, aber mit Zwischenberechnungen könnte es schlimmer sein. Ich denke nur, stroke-width:3.464102und ähnlich ist ein bisschen übertrieben, wenn die Idee war, 1 Pixel Genauigkeit zu bekommen. Ich werde es jedoch so aufnehmen, wenn das die Regel ist.
Level River St

Antworten:


27

Rubin, 1168 932

Ein Fehler aus der letzten Nacht wurde korrigiert. Nach Abklärung muss noch mehr Golf gespielt werden.

Dies ist (derzeit) ein vollständiges Programm, das eine Zahl von stdin akzeptiert und eine svgDatei an stdout ausgibt . Ich habe mich für svg entschieden, weil ich wusste, dass es möglich ist, alle Anforderungen der Frage zu erfüllen, aber es gab einige Probleme. Insbesondere unterstützt SVG nur Kreisbögen als Teil des pathObjekts und definiert sie nicht anhand ihres Mittelpunkts, sondern anhand der beiden Endpunkte.

Code

n=gets.to_i
r=64*w=0.75**0.5
m=1<<n-2
z=128*m/w
a=(s="<path style='fill:none;stroke:black;stroke-width:3.464102' transform='translate(%f %f)'
")%[0,r-r*m*8/3]+"d='M18.11943,-2A#{b=r-6*w-32} #{b} 0 0,0 #{-b} 0#{k='A%f %f 0 0 '%([58*w]*2)}0 0,38.71692
M28.58980,1.968882#{l='A%f %f 0 0 '%([70*w]*2)}0 #{c=r+6*w-32} 0A#{c} #{c} 0 0,0 #{-c} 0#{l}0 -9 44.65423'/>"
p=2
m.times{|i|(i*2+1).times{|j|(p>>j)%8%3==2&&a<<s%[128*(j-i),r*3+r*i*4-r*m*8/3]+
"d='M-55,44.65423#{k}0 11.5,25.11473#{l}1 35.41020,1.968882
M-64,51.48786#{l}0 20.5,30.31089#{k}1 36.82830,13.17993
M-82.17170,-2.408529#{l}1 -11.5,25.11473#{k}0 0,38.71692
M-81.52984 8.35435#{k}1 -20.5,30.31089#{l}0 -9,44.65423
M9,44.65423#{k}0 81.52984,8.35435
M0,51.48786#{l}0 91.17169,13.17993'/>"}
p^=p*4}
puts "<svg xmlns='http://www.w3.org/2000/svg' viewBox='#{-z} #{-z} #{e=2*z+1} #{e}' width='#{e}px' height='#{e}px'>"+
"<g transform='rotate(%d)'>#{a}</g>"*3%[0,120,240]+"</svg>"

Ausgang N = 4

durch Stapelaustausch neu skaliert. Sieht viel besser aus als Original.

Bildbeschreibung hier eingeben Erläuterung

Zuerst habe ich etwas wie http://euler.nmt.edu/~jstarret/sierpinski.html betrachtet, bei dem das Dreieck in drei verschiedenfarbige Stränge unterteilt ist, von denen jeder einen Pfad von einer Ecke zur anderen bildet. Die unvollständigen Kreise werden dort als unvollständige Sechsecke dargestellt. Wenn Sie Kreise in die Sechsecke einschreiben, sollte der Kreisradius sqrt(3)/2das Doppelte der Seitenlänge betragen. Die Stränge könnten wie gezeigt rekursiv aufgebaut werden, aber es gibt eine zusätzliche Komplikation, da die Ecken abgerundet werden müssen und es schwierig ist, die Richtung der Krümmung zu bestimmen, weshalb ich diesen Ansatz nicht gewählt habe.

Was ich tat, war das Folgende.

Im Bild unten sehen Sie horizontale Verdrehungen, die zu den N = 2 Einheiten (in Grün) gehören, die in einem Sierpinski-Dreieck angeordnet sind, und zusätzliche Überbrückungsverdrehungen (in Blau).

Es ist allgemein bekannt, dass die ungeraden Zahlen auf Pascals Dreieck ein Sierpinski-Dreieck bilden. Ein Sierpinski-Dreieck aus Binärziffern kann auf analoge Weise erhalten werden, indem mit der Zahl begonnen p=1und iterativ mit xoriert wird p<<1.

Ich habe diesen Ansatz geändert, angefangen bei p=2und iterativ mit xoring p*4. Dies ergibt ein Sierpinski-Dreieck, das sich mit Spalten von Nullen abwechselt.

Jetzt können wir p mit der rechten Maustaste verschieben und die letzten drei Bits mit prüfen %8. Wenn dies der 010Fall ist, müssen wir eine grüne Drehung zeichnen, die zu einer N = 2-Einheit gehört. Wenn sie es sind 101, müssen wir eine blaue Brücke ziehen. Um beide Zahlen zusammen zu testen, finden wir das Modulo %3und wenn dies 2 ist, müssen wir den Twist zeichnen.

Zusätzlich zu den horizontalen Drehungen erstellen wir zwei Kopien, die um 120 und 240 Grad gedreht wurden, um die diagonalen Drehungen zu zeichnen und das Bild zu vervollständigen. Alles, was bleibt, ist die Ecken hinzuzufügen.

Kommentierter Code

n=gets.to_i

#r=vertical distance between rows 
r=64*w=0.75**0.5

#m=number of rows of horizontal twists
m=1<<n-2

#z=half the size of the viewport
z=128*m/w

#s=SVG common to all paths
s="<path style='fill:none;stroke:black;stroke-width:3.464102' transform='translate(%f %f)'
"

#initialize a with SVG to draw top corner loop. Set k and l to the SVG common to all arcs of 58*w and 70*w radius 
a=s%[0,r-r*m*8/3]+
"d='M18.11943,-2A#{b=r-6*w-32} #{b} 0 0,0 #{-b} 0#{k='A%f %f 0 0 '%([58*w]*2)}0 0,38.71692
M28.58980,1.968882#{l='A%f %f 0 0 '%([70*w]*2)}0 #{c=r+6*w-32} 0A#{c} #{c} 0 0,0 #{-c} 0#{l}0 -9 44.65423'/>"

#p is the pattern variable, top row of twists has one twist so set to binary 00000010
p=2

#loop vertically and horizontally
m.times{|i|
 (i*2+1).times{|j|

   #leftshift p. if 3 digits inspected are 010 or 101 
   (p>>j)%8%3==2&&

   #append to a, the common parts of a path...
   a<<s%[128*(j-i),r*3+r*i*4-r*m*8/3]+

   #...and the SVG for the front strand and left and right parts of the back strand (each strand has 2 borders)
"d='M-55,44.65423#{k}0 11.5,25.11473#{l}1 35.41020,1.968882
M-64,51.48786#{l}0 20.5,30.31089#{k}1 36.82830,13.17993
M-82.17170,-2.408529#{l}1 -11.5,25.11473#{k}0 0,38.71692
M-81.52984 8.35435#{k}1 -20.5,30.31089#{l}0 -9,44.65423
M9,44.65423#{k}0 81.52984,8.35435
M0,51.48786#{l}0 91.17169,13.17993'/>"}

#modify the pattern by xoring with 4 times itself for the next row
p^=p*4}

#output complete SVG of correct size with three copies of the finished pattern rotated through 0,120,240 degrees.
puts "<svg xmlns='http://www.w3.org/2000/svg' viewBox='#{-z} #{-z} #{e=2*z+1} #{e}' width='#{e}px' height='#{e}px'>"+
"<g transform='rotate(%d)'>#{a}</g>"*3%[0,120,240]+"</svg>"

Bildbeschreibung hier eingeben


Wenn Sie sagen "Sieht viel besser aus als das Original", ist es möglicherweise wert, etwas wie "(klicken Sie auf das Bild, um es in voller Größe zu sehen)" für alle hinzuzufügen, die es nicht merken.
Trichoplax

@trichoplax es war mir nicht eingefallen auf das bild zu klicken. Dies ist jedoch eine PNG-Datei, da beim Stapelaustausch keine SVG-Bilder akzeptiert werden und die Ränder daher absichtlich unscharf sind. Meine lokale SVG-Datei hat viel schärfere Kanten und sieht viel besser aus.
Level River St

@trichoplax Schnellkorrektur auf Bildgröße gemacht. Wird an einem anderen Tag mehr Golf spielen.
Level River St

1
+1 großartige Arbeit. Besonders gut gefällt mir die ausführliche Erklärung mit dem farbcodierten Diagramm.
Trichoplax

1
Der Hyperlink ist tot.
mbomb007
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.