Spirograph Zeit!


14

Ein Spirograph ist ein Spielzeug, das Hypotrochoide und Epitrochoide zeichnet. Bei dieser Herausforderung konzentrieren wir uns nur auf die Hypotrochoide.

Aus Wikipedia :

Ein Hypotrochoid ist ein Roulette-Spiel, das von einem Punkt gezeichnet wird, der an einem Kreis mit dem Radius r befestigt ist, der sich um die Innenseite eines festen Kreises mit dem Radius R dreht , wobei der Punkt einen Abstand d vom Mittelpunkt des inneren Kreises entfernt ist.

Die parametrischen Gleichungen für sie können wie folgt definiert werden:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Wobei θ der Winkel ist, der durch die Horizontale und den Mittelpunkt des Rollkreises gebildet wird.


Ihre Aufgabe ist es, ein Programm zu schreiben, das den Pfad zeichnet, der durch den oben definierten Punkt verfolgt wird. Als Eingabe erhalten Sie R , r und d , alle ganzen Zahlen zwischen 1 und 200 einschließlich.

Sie können diese Eingabe von stdin, Argumenten oder Benutzereingaben erhalten, sie kann jedoch nicht fest in das Programm codiert werden. Sie können es in der für Sie am besten geeigneten Form akzeptieren. als Zeichenketten, Ganzzahlen usw.

Annehmen:

  • Eingabeeinheiten werden in Pixel angegeben.
  • R > = r

Die Ausgabe sollte eine grafische Darstellung des durch die Eingabe definierten Hypotrochoids sein. Es ist keine ASCII- oder andere textbasierte Ausgabe zulässig. Dieses Bild kann in einer Datei gespeichert oder auf dem Bildschirm angezeigt werden. Fügen Sie einen Screenshot oder ein Bild der Ausgabe für eine Eingabe Ihrer Wahl hinzu.

Sie können beliebige Farben für den Pfad / Hintergrund auswählen, vorbehaltlich einer Kontrasteinschränkung. Die beiden Farben müssen mindestens die Hälfte der HSV-Wertkomponente voneinander entfernt haben. Wenn Sie beispielsweise das HSV messen [0...1], sollte es zumindest einen 0.5Unterschied geben. Dazwischen [0...255]sollte ein Mindestunterschied bestehen 128.


Dies ist ein Code Golf, Mindestgröße des Quellcodes in Bytes gewinnt.


Können wir annehmen R > roder R ≥ r? (Dasselbe für rund d.)
Martin Ender

10
Herzlichen Glückwunsch zum Posten der 2000. Frage! ;-)
Türklinke

@m.buettner R>=r, ist aber dnicht darauf beschränkt rund kann irgendwo im Bereich von 1-200 liegen.
Geobits

Über welche Art von Lösung sprechen wir?
Kyle Kanos

@KyleKanos Da die Eingabe in Pixeln erfolgt und jeder eine Obergrenze von 200 hat, sollte er niemals größer als 798 x 798 sein R=200, r=1, d=200. Sie können das Bild an die Eingabe anpassen, wenn Sie möchten, oder es auf einer konstanten Größe halten, solange alles sichtbar ist.
Geobits

Antworten:


8

Mathematica, 120 Bytes

f[R_,r_,d_]:=ParametricPlot[p#@t+#[-p*t/r]d&/@{Cos,Sin},{t,0,2r/GCD[p=R-r,r]Pi},PlotRange->400,ImageSize->800,Axes->0>1]

Ungolfed Code und Beispielausgabe: Bildbeschreibung hier eingeben

Wenn ich die Achsen in den Plot aufnehmen darf, kann ich weitere 9 Zeichen speichern.


5

JavaScript (ECMAScript 6) - 312 314 Zeichen

document.body.appendChild(e=document.createElement("canvas"))
v=e.getContext("2d")
n=(e.width=e.height=800)/2
M=Math
P=2*M.PI
t=0
p=prompt
r=p('r')
R=p('R')-r
d=p('d')
X=x=>n+R*M.cos(t)+d*M.cos(R/r*t)
Y=x=>n+R*M.sin(t)-d*M.sin(R/r*t)
v.beginPath()
v.moveTo(X(),Y())
for(;t<R*P;v.lineTo(X(),Y()))t+=P/2e4
v.stroke()

JSFIDDLE

Beispielausgabe

r = 1, R = 200, d = 30

Bildbeschreibung hier eingeben


Ich mag es, aber es ist irgendwie kaputt. Versuchen Sie die Beispiele in R.
edc65

Letzte Zeile könnte sein für (; t <R * P; v.lineTo (X (), Y ())) t + = P / R
edc65

@ edc65 Es ist nicht kaputt, es hat einfach nicht genug Iterationen durchgeführt, um in diesen Beispielen eine vollständige Rotation durchzuführen. Ich habe die Iterationen von 9 * PI auf R * 2 * PI erhöht und es sollte besser sein (allerdings habe ich das Inkrement bei PI / 1000 belassen, da es sonst für kleine Werte von R abbrechen würde).
MT0

3

Python: 579

Zusammenfassung

Dies ist in Anbetracht der Mathematica-Antwort überhaupt nicht konkurrenzfähig, aber ich habe beschlossen, es trotzdem zu posten, da die Bilder hübsch sind und jemanden inspirieren oder jemandem nützlich sein können. Weil es so viel größer ist, habe ich es im Grunde ungolfed gelassen. Das Programm erwartet eine Kommandozeilen-Eingabe von R, r, d.

Bildschirmfoto

Hier sind zwei Beispiele, eines für (5,3,5) und eines für (10,1,7). Beispiel 5-3-5 Beispiel 10-1-7

Code

import math
import matplotlib.pyplot as P
from matplotlib.path import Path as H
import matplotlib.patches as S
import sys
a=sys.argv
(R,r,d)=int(a[1]),int(a[2]),int(a[3])
v=[]
c=[]
c.append(H.MOVETO)
t=0
while(len(v)<3 or v.count(v[-1])+v.count(v[-2])<3):
 p=t*math.pi/1000
 t+=1
 z=(R-r)*p/r
 v.append((round((R-r)*math.cos(p)+d*math.cos(z),3),round((R-r)*math.sin(p)-d*math.sin(z),3)))
 c.append(H.LINETO)
c.pop()
v.append((0,0))
c.append(H.CLOSEPOLY)
f=P.figure()
x=f.add_subplot(111)
x.add_patch(S.PathPatch(H(v,c)))
l=R+d-r
x.set_xlim(-l-1,l+1)
x.set_ylim(-l-1,l+1)
P.show()

2
Kannst du das Verhältnis einstellen? Es scheint, dass das Bild vertikal komprimiert ist.
AL

3

Perl / Tk - 239 227

use Tk;($R,$r,$d)=@ARGV;$R-=$r;$s=$R+$d;$c=tkinit->Canvas(-width=>2*$s,-height=>2*$s)->pack;map{$a=$x;$b=$y;$x=$s+$R*cos($_/=100)+$d*cos$_*$R/$r;$y=$s+$R*sin($_)-$d*sin$_*$R/$r;$c->createLine($a,$b,$x,$y)if$a}0..628*$s;MainLoop

R = 120, r = 20, d = 40:

R = 120, r = 20, d = 40

R = 128, r = 90, d = 128:

R = 128, r = 90, d = 128

R = 179, r = 86, d = 98:

R = 179, r = 86, d = 98


2

Verarbeitung, 270

import java.util.Scanner;
void setup(){size(500, 500);}
Scanner s=new Scanner(System.in);
int R=s.nextInt(),r=s.nextInt(),d=s.nextInt();
void draw(){
  int t=width/2,q=(R-r);
  for(float i=0;i<R*PI;i+=PI/2e4)
    point(q*sin(i)-d*sin(i*q/r)+t,q*cos(i)+d*cos(i*q/r)+t);
}

Die Eingabe erfolgt über die Konsole, eine Nummer pro Zeile.

Screenshot für R = 65, r = 15, d = 24: Bildbeschreibung hier eingeben


2

GeoGebra, 87

Das heißt, wenn Sie GeoGebra als gültige Sprache betrachten.

R=2
r=1
d=1
D=R-r
Curve[D*cos(t)+d*cos(D*t/r),D*sin(t)-d*sin(D*t/r),t,0,2π*r/GCD[D,r]]

Akzeptiert Eingaben von der GeoGebra-Eingabeleiste im Format <variable>=<value>, z R=1000.

Beachten Sie, dass Sie möglicherweise die Zoomgröße manuell ändern müssen, um das gesamte Bild anzuzeigen.

Bildschirmfoto

(Das Ding am unteren Rand des Fensters ist die Eingabeleiste, über die ich gesprochen habe)

Probieren Sie es hier online aus .


1
Ich nehme an, das hat die gleiche Einschränkung wie Kyle Kanos, dass Sie die Größe nicht in Pixel angeben können.
Martin Ender

@ m.buettner Ja du hast recht ... habe das verpasst
user12205

2

HTML + Javascript 256 286 303

Bearbeiten 1. Aufruf von moveTo entfernt, funktioniert trotzdem. Könnte sparen mehr Schneiden beginPath, aber dann funktioniert es nur beim ersten Mal

Edit2 30 Bytes gespeichert dank @ ӍѲꝆΛҐӍΛПӍѲꝆΛҐӍΛ

<canvas id=c></canvas>R,r,d:<input oninput="n=400;c.width=c.height=t=n+n;v=c.getContext('2d');s=this.value.split(',');r=s[1],d=s[2],R=s[0]-r;v.beginPath();for(C=Math.cos,S=Math.sin;t>0;v.lineTo(n+R*C(t)+d*C(R/r*t),n+R*S(t)-d*S(R/r*t)),t-=.02);v.stroke()">

Prüfung

Geben Sie etwas in das Textfeld ein (durch Kommas getrennt) und drücken Sie die Tabulatortaste

R,r,d:<input onchange="n=400;c.width=c.height=t=n+n;v=c.getContext('2d');s=this.value.split(',');r=s[1],d=s[2],R=s[0]-r;v.beginPath();for(C=Math.cos,S=Math.sin;t>0;v.lineTo(n+R*C(t)+d*C(R/r*t),n+R*S(t)-d*S(R/r*t)),t-=.02);v.stroke()"><canvas id=c></canvas>


1
Könnten Sie der Zeichenfläche nicht einfach eine ID hinzufügen und diese ID global verwenden, anstatt querySelector verwenden zu müssen?
Mama Fun Roll

@ ӍѲꝆΛҐӍΛПӍѲꝆΛҐӍΛ yeeeeees ich könnte. Das war mir im Mai 2014 nicht bewusst
edc65

Wow das war viel mehr Bytes gespeichert als ich dachte.
Mama Fun Roll

2

R, 80 Bytes

f=function(R,r,d){a=0:1e5/1e2;D=R-r;z=D*exp(1i*a)+d*exp(-1i*D/r*a);plot(z,,'l')}

Will man jedoch "saubere" Zahlen (keine Achsen, keine Beschriftungen usw.), muss der Code etwas länger sein (88 Zeichen):

f=function(R,r,d)plot((D=R-r)*exp(1i*(a=0:1e5/1e2))+d*exp(-1i*D/r*a),,'l',,,,,,'','',,F)

Ein Codebeispiel mit der längeren Version von f:

f(R<-179,r<-86,d<-98);title(paste("R=",R,", r=",r," d=",d,sep=""))

Einige Beispielausgaben:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben


Hier werden die Eingabegrößen nicht in Pixel angegeben, oder? Das erste Beispiel sollte fast dreimal so groß sein wie das zweite.
Martin Ender

Warum all das ,?
Plannapus

Die Kommas wurden verwendet, um die Argumente zu trennen, von denen viele NULL (nichts) waren. Hier wurde ein Positionsargumentabgleich verwendet, um die Länge des Codes zu verringern. Dies ist natürlich eine schlechte Codierungspraxis. Der empfohlene Weg wäre, benannte Argumente wie type = "l", xlabel = "" usw. zu verwenden (und die redundanten Kommas zu entfernen!).
Feng

1

C # 813 war 999

Benötigt etwas Arbeit, um die Anzahl der Bytes zu reduzieren. Ich habe es geschafft, es ein wenig zu reduzieren. Es akzeptiert drei durch Leerzeichen getrennte Ganzzahlen von der Konsole.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
class P:Form
{
int R,r,d;
P(int x,int y,int z) {R=x;r=y;d=z;}
protected override void OnPaint(PaintEventArgs e)
{
if(r==0)return;
Graphics g=e.Graphics;
g.Clear(Color.Black);
int w=(int)this.Width/2;
int h=(int)this.Height/2;
List<PointF> z= new List<PointF>();
PointF pt;
double t,x,y;
double pi=Math.PI;
for (t=0;t<2*pi;t+=0.001F)
{
x=w+(R-r)*Math.Cos(t)+d*Math.Cos(((R-r)/r)*t);
y=h+(R-r)*Math.Sin(t)-d*Math.Sin(((R-r)/r)*t);
pt=new PointF((float)x,(float)y);
z.Add(pt);
}
g.DrawPolygon(Pens.Yellow,z.ToArray());
}
static void Main()
{
char[] d={' '};
string[] e = Console.ReadLine().Split(d);
Application.Run(new P(Int32.Parse(e[0]),Int32.Parse(e[1]),Int32.Parse(e[2])));
}
}

Ausgabebeispiel:

Spirograph


1

Shell-Skript + Gnuplot (153)

Der größte Aufwand besteht darin, die Achsen und Tics zu entfernen, die Größe und den Bereich festzulegen und die Präzision zu erhöhen. Zum Glück ist Gnuplot für das Golfen natürlich, so dass die meisten Befehle abgekürzt werden können. Um Zeichen zu speichern, muss die Ausgabe manuell in eine Bilddatei umgeleitet werden.

gnuplot<<E
se t pngc si 800,800
se pa
se sa 1e4
uns bor
uns tic
a=$1-$2
b=400
p[0:2*pi][-b:b][-b:b]a*cos($2*t)+$3*cos(a*t),a*sin($2*t)-$3*sin(a*t) not
E

Aufruf des Skripts mit spiro.sh 175 35 25>i.pnggives Bildbeschreibung hier eingeben


1

R, 169 Zeichen

f=function(R,r,d){png(w=2*R,h=2*R);par(mar=rep(0,4));t=seq(0,R*pi,.01);a=R-r;x=a*cos(t)+d*cos(t*a/r);y=a*sin(t)-d*sin(t*a/r);plot(x,y,t="l",xaxs="i",yaxs="i");dev.off()}

Eingerückt:

f=function(R,r,d){
    png(w=2*R,h=2*R) #Creates a png device of 2*R pixels by 2*R pixels
    par(mar=rep(0,4)) #Get rid of default blank margin
    t=seq(0,R*pi,.01) #theta
    a=R-r
    x=a*cos(t)+d*cos(t*a/r)
    y=a*sin(t)-d*sin(t*a/r)
    plot(x,y,t="l",xaxs="i",yaxs="i") #Plot spirograph is a plot that fits tightly to it (i. e. 2*R by 2*R)
    dev.off() #Close the png device.
}

Beispiele:

> f(65,15,24)

Bildbeschreibung hier eingeben

> f(120,20,40)

Bildbeschreibung hier eingeben

> f(175,35,25)

Bildbeschreibung hier eingeben


1

SmileBASIC, 96 Bytes

INPUT R,Q,D
M=R+MAX(Q,D)
S=R-Q@L
GPSET M+S*COS(I)+D*COS(S/Q*I),M+S*SIN(I)-D*SIN(S/Q*I)I=I+1GOTO@L

Eingabe: 50,30,50:

Bildbeschreibung hier eingeben


1

Befunge-98, 113 Bytes

&&:00p-10p&20p"PXIF"4(10g'd:*:I10v>H40gF1+:"}`"3**`>jvI@
1(4"TURT"p04/d'*g02I/g00*p03/d'*g<^-\0/g00*g01:Fg03H:<0P

Dieser Code basiert für einige trigonometrische Berechnungen auf dem Fingerabdruck von Fixed Point Maths (FIXP) und der Turtle Graphics (TURT). um den Pfad des Spirographen zu zeichnen.

Die Turtle-Grafiken in Befunge verhalten sich sehr ähnlich wie die Grafiken in der Programmiersprache Logo . Sie zeichnen mit einer 'Schildkröte' (die als Stift dient), die Sie um die Ausgabeoberfläche steuern. Dazu muss die Schildkröte in eine bestimmte Richtung ausgerichtet und dann angewiesen werden, sich eine bestimmte Strecke vorwärts zu bewegen.

Um mit diesem System arbeiten zu können, musste ich die ursprünglichen Spirographengleichungen etwas schildkrötenfreundlicher anpassen. Ich bin mir nicht sicher, ob dies der beste Ansatz ist, aber der Algorithmus, den ich mir ausgedacht habe, funktioniert in etwa so:

ratio = (R-r)/r
distance1 = sin(1°) * (R-r)
distance2 = sin(1° * ratio) * d
foreach angle in 0° .. 36000°:
  heading(angle)
  forward(distance1)
  heading(-ratio*angle)
  forward(distance2)

Beachten Sie, dass hierdurch der Pfad tatsächlich mit einer Art Zick-Zack-Muster gezeichnet wird. Sie bemerken dies jedoch nicht wirklich, es sei denn, Sie zoomen eng auf das Bild.

Hier ist ein Beispiel mit den Parametern R = 73, r = 51, d = 45.

Bildbeschreibung hier eingeben

Ich habe den Code mit CCBI getestet und cfunge getestet , die beide eine Ausgabe in Form eines SVG-Bildes erzeugen. Da es sich um ein skalierbares Vektorformat handelt, hat das resultierende Bild keine Pixelgröße als solches - es passt sich lediglich der Bildschirmgröße an (zumindest bei Anzeige in einem Browser). Das obige Beispiel ist eine Bildschirmaufnahme, die manuell zugeschnitten und skaliert wurde.

Theoretisch könnte der Code auch auf Rc / Funge funktionieren , aber in diesem Fall müssten Sie auf einem System mit XWindows laufen, da es versuchen würde, die Ausgabe in einem Fenster zu rendern.


0

wxMaxima : 110

f(R,r,d):=plot2d([parametric,(p:R-r)*cos(t)+d*cos(t*(p)/r),(p)*sin(t)-d*sin(t*(p)/r),[t,0,2*%pi*r/gcd(p,r)]]);

Dies wird in der interaktiven Sitzung über aufgerufen f(#,#,#). Betrachten Sie als Beispiel f(3,2,1):

Bildbeschreibung hier eingeben


Obwohl ich die hübsche Ausgabe mag, bin ich mir nicht sicher, wie dies "Ganzzahlen zwischen 1 und 200" oder "als Pixel angegeben" folgt.
Geobits

Die Eingabe kann ganzzahlig oder fließend sein, wxMaxima konvertiert in float, um seine Arbeit trotzdem zu erledigen. Ich aktualisiere ein Bild mit ganzen Zahlen. Ich muss auch mehr über die Eingabe als Pixel nachdenken.
Kyle Kanos

Ja, ich dachte, es würde sie intern konvertieren, und das ist kein Problem. Die ganzzahlige Einschränkung bei der Eingabe bestand hauptsächlich darin, geschlossene Schleifen zu vereinfachen (sie sehen imo einfach besser aus).
Geobits

0

Schläger

#lang racket/gui
(require 2htdp/image)

(define frame (new frame%
                   [label "Spirograph"]
                   [width 300]
                   [height 300]))

(define-values (R r d) (values 50 30 10)) ; these values can be adjusted;

(new canvas% [parent frame]
     [paint-callback
      (lambda (canvas dc)
        (send dc set-scale 3 3)
        (for ((t (in-range 0 (* 10(* R pi)) 1)))
          (define tr (degrees->radians t))
          (define a (- R r))
          (define x (+ (* a (cos tr))
                       (* d (cos (* tr (/ a r))))))
          (define y (- (* a (sin tr))
                       (* d (sin (* tr (/ a r))))))
          (send dc draw-ellipse (+ x 50) (+ y 50) 1 1)))])

(send frame show #t)

Ausgabe:

Bildbeschreibung hier eingeben

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.