Czas spirografu!


14

Spirograf to zabawka, która rysuje hypotrochoidy i epitrochoidy. W tym wyzwaniu skupimy się na hypotrochoidach.

Z Wikipedii :

Hipotrochoid jest ruletką wyznaczoną przez punkt przymocowany do koła o promieniu r toczącym się wokół wnętrza stałego okręgu o promieniu R , gdzie punkt jest odległością d od środka wewnętrznego koła.

Równania parametryczne dla nich można zdefiniować jako:

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

Gdzie θ to kąt utworzony przez poziome i środek tocznego koła.


Twoim zadaniem jest napisanie programu, który narysuje ścieżkę wyznaczoną przez punkt zdefiniowany powyżej. Jako dane wejściowe otrzymasz R , r i d , wszystkie liczby całkowite od 1 do 200 włącznie.

Możesz otrzymać te dane wejściowe ze standardowego wejścia, argumentów lub danych wejściowych od użytkownika, ale nie można go zakodować na stałe w programie. Możesz zaakceptować to w dowolnej formie, która jest dla ciebie najwygodniejsza; jako ciągi, liczby całkowite itp.

Założyć:

  • Jednostki wejściowe podano w pikselach.
  • R > = r

Wyjście powinno być graficzną reprezentacją hypotrochoidu zdefiniowanego przez dane wejściowe. Wyjście ASCII lub inne dane tekstowe nie są dozwolone. Ten obraz można zapisać do pliku lub wyświetlić na ekranie. Dołącz zrzut ekranu lub obraz wyjścia dla wybranego wejścia.

Możesz wybrać dowolne kolory dla ścieżki / tła, z zastrzeżeniem ograniczenia kontrastu. Te dwa kolory muszą mieć składnik „Wartość” HSV co najmniej w połowie skali. Na przykład, jeśli mierzysz HSV [0...1], powinna istnieć przynajmniej 0.5różnica. Pomiędzy [0...255]powinna być minimalna 128różnica.


To jest golfowy kod, wygrywa minimalna wielkość kodu źródłowego w bajtach.


Czy możemy założyć R > rlub R ≥ r? (To samo dotyczy ri d.)
Martin Ender

10
Gratulujemy opublikowania 2000. pytania! ;-)
Klamka

@ m.buettner R>=r, ale dnie jest do tego ograniczony ri może znajdować się w dowolnym miejscu w zakresie 1-200.
Geobity

O jakiej rozdzielczości mówimy?
Kyle Kanos

@KyleKanos Ponieważ dane wejściowe są w pikselach, a każda z nich ma limit 200, nigdy nie powinno być większe niż 798 x 798 R=200, r=1, d=200. Możesz zmienić rozmiar obrazu na wejście, jeśli chcesz, lub utrzymać go w stałym rozmiarze, o ile wszystko jest widoczne.
Geobits

Odpowiedzi:


8

Mathematica, 120 bajtów

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]

Nieskluczony kod i przykładowe dane wyjściowe: enter image description here

Jeśli mogę dołączyć osie do fabuły, mogę zapisać kolejne 9 znaków.


5

JavaScript (ECMAScript 6) - 312 314 znaków

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

Przykładowy wynik

r = 1, R = 200, d = 30

wprowadź opis zdjęcia tutaj


Podoba mi się, ale ikt jest zepsuty. Spróbuj przykładów w R.
edc65

Ostatnia linia może być dla (; t <R * P; v.lineTo (X (), Y ())) t + = P / R
edc65

@ edc65 Nie jest zepsuty, po prostu nie wykonał wystarczającej liczby iteracji, aby wykonać pełny obrót w tych przykładach. Zwiększyłem iteracje z 9 * PI do R * 2 * PI i powinno być lepiej (jednak zostawiłem przyrost na PI / 1000, ponieważ w przeciwnym razie złamałby się dla małych wartości R).
MT0

3

Python: 579

streszczenie

Nie jest to wcale konkurencyjne, biorąc pod uwagę odpowiedź Mathematica, ale i tak zdecydowałem się opublikować, ponieważ zdjęcia są ładne i mogą kogoś zainspirować lub być przydatne dla kogoś. Ponieważ jest o wiele większy, zostawiłem go w zasadzie bez golfa. Program oczekuje wprowadzenia z wiersza poleceń R, r, d.

Zrzut ekranu

Oto dwa przykłady, jeden dla (5,3,5) i jeden dla (10,1,7) przykład 5-3-5 przykład 10-1-7

Kod

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
Czy możesz dostosować współczynnik? Wygląda na to, że obraz jest kompresowany w pionie.
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

Przetwarzanie, 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);
}

Wprowadzanie danych odbywa się za pomocą konsoli, po jednym numerze w wierszu.

Zrzut ekranu dla R = 65, r = 15, d = 24: wprowadź opis zdjęcia tutaj


2

GeoGebra, 87

To znaczy, jeśli uważasz GeoGebra za prawidłowy język.

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

Akceptuje dane wejściowe z paska wprowadzania GeoGebra, w formacie <variable>=<value>np R=1000.

Pamiętaj, że może być konieczna ręczna zmiana wielkości powiększenia, aby wyświetlić cały obraz.

zrzut ekranu

(Rzecz na dole okna to pasek wprowadzania, o którym mówiłem)

Wypróbuj online tutaj .


1
Przypuszczam, że ma to takie samo ograniczenie, jak oświadczenie Kyle'a Kanosa, że ​​nie możesz określić rozmiaru w pikselach?
Martin Ender

@ m.buettner Tak masz rację ... tęskniłeś
12205

2

HTML + JavaScript 256 286 303

Edytować Usunięto 1. połączenie z moveTo, i tak działa. Mógłby zaoszczędzić więcej cięcia beginPath, ale wtedy działa tylko za pierwszym razem

Edytuj2 30 bajtów zapisanych thx @ ӍѲꝆΛҐӍΛПҒЦꝆ

<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()">

Test

Wprowadź dane w polu tekstowym (oddzielone przecinkami), a następnie naciśnij klawisz Tab

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
Czy nie możesz po prostu dodać identyfikatora do obszaru roboczego i używać go globalnie zamiast konieczności korzystania z querySelector!
Mama Fun Roll

@ ҒЦꝆПҒЦꝆ yeeeeee Mógłbym. Jest to coś, o czym nie wiedziałem w maju 2014 r.
edc65

Wow, to o wiele więcej zapisanych bajtów, niż myślałem.
Mama Fun Roll

2

R, 80 bajtów

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

Jeśli jednak chcemy „czystych” cyfr (bez osi, bez etykiet itp.), Kod będzie musiał być nieco dłuższy (88 znaków):

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

Jeden przykład kodu wykorzystujący dłuższą wersję f:

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

Niektóre przykładowe dane wyjściowe:

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj


To nie bierze wielkości wejściowych w pikselach, prawda? Pierwszy przykład powinien być prawie trzy razy większy niż drugi.
Martin Ender

Dlaczego wszyscy ,??
plannapus

Przecinków użyto do oddzielenia argumentów, z których wiele miało wartość NULL (nic). W tym przypadku zastosowano dopasowanie pozycyjne argumentów w celu skrócenia długości kodu. Jest to oczywiście zła praktyka kodowania. Zalecanym sposobem byłoby użycie nazwanej listy argumentów, takiej jak type = "l", xlabel = "" itp. (I pozbyć się zbędnych przecinków!).
Feng

1

C # 813 wynosił 999

Potrzebuje trochę pracy, aby zmniejszyć liczbę bajtów. Udało mi się to trochę zmniejszyć. Akceptuje trzy rozdzielone spacjami liczby całkowite z konsoli.

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])));
}
}

Próbka wyjściowa:

Spirograf


1

skrypt powłoki + gnuplot (153)

Większość wysiłku polega na usunięciu osi i tików, ustawieniu rozmiaru i zakresu oraz zwiększeniu precyzji. Na szczęście gnuplot jest naturalny do gry w golfa, więc większość poleceń można skrócić. Aby zapisać znaki, dane wyjściowe należy ręcznie przekierować do pliku obrazu.

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

Wywołanie skryptu za pomocą spiro.sh 175 35 25>i.pngdaje wprowadź opis zdjęcia tutaj


1

R, 169 znaków

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

Zębaty:

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

Przykłady:

> f(65,15,24)

wprowadź opis zdjęcia tutaj

> f(120,20,40)

wprowadź opis zdjęcia tutaj

> f(175,35,25)

wprowadź opis zdjęcia tutaj


1

SmileBASIC, 96 bajtów

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

Dane wejściowe: 50,30,50:

wprowadź opis zdjęcia tutaj


1

Befunge-98, 113 bajtów

&&: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

W przypadku niektórych obliczeń trygonometrycznych ten kod opiera się na odcisku palca z matematyki stałej punktu (FIXP) oraz grafice żółwia (TURT) odcisku palca do rysowania ścieżki spirografu.

Grafika Turtle w Befunge jest bardzo podobna w zachowaniu do grafiki w języku programowania Logo . Rysujesz za pomocą „żółwia” (służącego jako długopis), którym kierujesz po powierzchni wyjściowej. Wymaga to skierowania żółwia w określonym kierunku, a następnie instruowania go, aby ruszył do przodu na określoną odległość.

Aby pracować z tym systemem, musiałem dostosować oryginalne równania spirografu do czegoś bardziej przyjaznego żółwiom. Nie jestem pewien, czy jest to najlepsze podejście, ale algorytm, który wymyśliłem, działa mniej więcej tak:

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)

Zauważ, że to w rzeczywistości rysuje ścieżkę z rodzajem zygzakowatego wzoru, ale tak naprawdę nie zauważasz, dopóki nie powiększysz dokładnie obrazu.

Oto przykład wykorzystujący parametry R = 73, r = 51, d = 45.

wprowadź opis zdjęcia tutaj

Przetestowałem kod za pomocą CCBI i cfunge , z których oba generują dane wyjściowe w postaci obrazu SVG. Ponieważ jest to skalowalny format wektorowy, powstały obraz nie ma rozmiaru piksela jako takiego - po prostu skaluje się w celu dopasowania do rozmiaru ekranu (przynajmniej podczas przeglądania w przeglądarce). Powyższy przykład to zrzut ekranu, który został ręcznie przycięty i skalowany.

Teoretycznie kod może również działać na Rc / Funge , ale w takim przypadku będziesz musiał uruchomić system z XWindows, ponieważ spróbuje wyrenderować dane wyjściowe w oknie.


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)]]);

Jest to wywoływane w sesji interaktywnej przez f(#,#,#). Jako przykład rozważ f(3,2,1):

wprowadź opis zdjęcia tutaj


Chociaż podoba mi się ładny wynik, nie jestem pewien, jak to się dzieje po „liczbach całkowitych od 1 do 200” lub „podanych jako piksele”.
Geobits

Dane wejściowe mogą być liczbami całkowitymi lub zmiennoprzecinkowymi, wxMaxima przekonwertuje na zmiennoprzecinkowe, aby wykonać swoją pracę, zaktualizuję obraz za pomocą liczb całkowitych. Będę musiał więcej myśleć o danych wejściowych jako pikselach.
Kyle Kanos

Tak, pomyślałem, że przekształci je wewnętrznie, i to nie jest problem. Ograniczeniem liczby całkowitej na wejściu było głównie ułatwienie uzyskania zamkniętych pętli (po prostu wyglądają lepiej imo).
Geobits

0

Rakieta

#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)

Wynik:

wprowadź opis zdjęcia tutaj

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.