Tempo Spirograph!

14

Um Spirograph é um brinquedo que desenha hipotrocóides e epitrocóides. Para esse desafio, focaremos apenas os hipotrocóides.

Da Wikipedia :

Um hipotrocóide é uma roleta traçada por um ponto anexado a um círculo de raio r rolando ao redor de um círculo fixo de raio R , onde o ponto é uma distância d do centro do círculo interior.

As equações paramétricas para eles podem ser definidas como:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Onde θ é o ângulo formado pela horizontal e pelo centro do círculo rotativo.


Sua tarefa é escrever um programa que desenhe o caminho traçado pelo ponto definido acima. Como entrada, você será dado R , r , e d , todos os inteiros entre 1 e 200, inclusive.

Você pode receber essa entrada de stdin, argumentos ou entrada do usuário, mas não pode ser codificada no programa. Você pode aceitá-lo da forma que for mais conveniente para você; como cadeias, números inteiros etc.

Presumir:

  • As unidades de entrada são fornecidas em pixels.
  • R > = r

A saída deve ser uma representação gráfica do hipotrochoide definido pela entrada. Nenhuma saída ASCII ou outra baseada em texto é permitida. Esta imagem pode ser salva em um arquivo ou exibida na tela. Inclua uma captura de tela ou imagem da saída para uma entrada de sua escolha.

Você pode escolher as cores que desejar para o caminho / fundo, sujeito a uma restrição de contraste. As duas cores devem ter o componente 'Valor' do HSV com pelo menos metade da escala. Por exemplo, se você estiver medindo o HSV [0...1], deve haver pelo menos 0.5diferença. Entre [0...255]deve haver uma 128diferença mínima .


Este é um código de golfe, o tamanho mínimo do código-fonte em bytes vence.

Geobits
fonte
Podemos assumir R > rou R ≥ r? (Mesmo para re d.)
Martin Ender
10
Parabéns por postar a 2000ª pergunta! ;-)
Maçaneta da porta
@ m.buettner R>=r, mas dnão está restrito re pode estar em qualquer lugar no intervalo de 1 a 200.
Geobits 23/05
De que tipo de resolução estamos falando?
Kyle Kanos
@KyleKanos Como a entrada é em pixels e cada um tem um limite de 200, nunca deve ser maior que 798x798, dado R=200, r=1, d=200. Você pode dimensionar a imagem para a entrada, se desejar, ou mantê-la em um tamanho constante, desde que tudo esteja visível.
Geobits

Respostas:

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]

Código não destruído e exemplo de saída: insira a descrição da imagem aqui

Se eu puder incluir os eixos no gráfico, posso salvar outros 9 caracteres.

Martin Ender
fonte
5

JavaScript (ECMAScript 6) - 312 314 caracteres

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

Saída de exemplo

r = 1, R = 200, d = 30

insira a descrição da imagem aqui

MT0
fonte
Eu gosto, mas o ikt está quebrado de alguma forma. Tente os exemplos em R.
edc65
A última linha pode ser para (; t <R * P; v.lineTo (X (), Y ())) t + = P / R
edc65
@ edc65 Não está quebrado, apenas não estava fazendo iterações suficientes para fazer uma rotação completa nesses exemplos. Aumentei as iterações de 9 * PI para R * 2 * PI e deve ser melhor (no entanto, deixei o incremento em PI / 1000, caso contrário, seria interrompido por pequenos valores de R).
MT0
3

Python: 579

Sumário

Isso não é nada competitivo, dada a resposta do Mathematica, mas decidi publicá-la de qualquer maneira, porque as imagens são bonitas e podem inspirar alguém ou ser útil a alguém. Por ser muito maior, eu o deixei basicamente não destruído. O programa espera a entrada da linha de comando de R, r, d.

Captura de tela

Aqui estão dois exemplos, um para (5,3,5) e outro para (10,1,7) exemplo 5-3-5 exemplo 10-1-7

Código

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()
RT
fonte
2
Você pode ajustar a proporção? Parece que a imagem está comprimida verticalmente.
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

core1024
fonte
2

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

A entrada é inserida via console, um número por linha.

Screenshot para R = 65, r = 15, d = 24: insira a descrição da imagem aqui

segfaultd
fonte
2

GeoGebra, 87

Ou seja, se você considera o GeoGebra um idioma válido.

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

Aceita entrada da barra de entrada GeoGebra, no formato <variable>=<value>, por exemplo R=1000.

Observe que pode ser necessário alterar manualmente o tamanho do zoom para visualizar a imagem inteira.

captura de tela

(A coisa na parte inferior da janela é a barra de entrada que eu estava falando)

Experimente online aqui .

user12205
fonte
1
Suponho que isso tenha a mesma limitação da submissão de Kyle Kanos, que você não pode especificar o tamanho em pixels?
Martin Ender
@ m.buettner Sim, você está certo ... perdeu isso #
user12205
2

HTML + Javascript 256286303

Editar Removida a 1ª chamada para moveTo, funciona de qualquer maneira. Poderia economizar mais beginPath de corte, mas funciona apenas na primeira vez

Edit2 30 bytes salvos 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()">

Teste

Coloque a entrada na caixa de texto (separada por vírgula) e pressione 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>

edc65
fonte
1
Você não poderia simplesmente adicionar um ID à tela e usá-lo globalmente, em vez de precisar usar o querySelector!
Mama Fun Roll
@ EeПҒЦꝆ yeeeeees eu poderia. É algo que eu não estava ciente de maio 2014
edc65
Uau, isso foi muito mais bytes salvos do que eu pensava.
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')}

No entanto, se alguém quiser figuras 'limpas' (sem eixos, sem etiquetas etc.), o código terá que ser um pouco mais longo (88 caracteres):

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

Um exemplo de código usando a versão mais longa de f:

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

Alguns exemplos de saídas:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Feng
fonte
Isso não aceita os tamanhos de entrada em pixels, não é? O primeiro exemplo deve ser quase três vezes maior que o segundo.
Martin Ender
Por que todas as ,??
plannapus
As vírgulas foram usadas para separar os argumentos, muitos dos quais eram NULL (nada). Aqui, a correspondência de argumentos posicionais foi usada para reduzir o comprimento do código. É claro que isso é uma prática ruim de codificação. A maneira recomendada seria usar a lista de argumentos nomeados, como type = "l", xlabel = "", etc (e se livrar das vírgulas redundantes!).
Feng
1

C # 813, era 999

Precisa de algum trabalho para reduzir a contagem de bytes. Consegui reduzir um pouco. Ele aceita três números inteiros separados por espaço do console.

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

Amostra de saída:

Spirograph

bacchusbeale
fonte
1

shell script + gnuplot (153)

A maior parte do esforço é remover os eixos e tiques, definir o tamanho e o alcance e aumentar a precisão. Felizmente, o gnuplot é natural para o golfe, então a maioria dos comandos pode ser abreviada. Para salvar caracteres, a saída deve ser redirecionada para um arquivo de imagem manualmente.

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

Chamando o script com spiro.sh 175 35 25>i.pnginsira a descrição da imagem aqui

orion
fonte
1

R, 169 caracteres

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

Recuado:

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

Exemplos:

> f(65,15,24)

insira a descrição da imagem aqui

> f(120,20,40)

insira a descrição da imagem aqui

> f(175,35,25)

insira a descrição da imagem aqui

plannapus
fonte
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

Entrada: 50,30,50:

insira a descrição da imagem aqui

12Me21
fonte
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

Esse código se baseia na impressão digital FIXP (Fixed Point Maths) para alguns cálculos trigonométricos e no Turtle Graphics (TURT) impressão digital para desenhar o caminho do espirógrafo.

Os gráficos Turtle em Befunge são muito semelhantes no comportamento aos gráficos na linguagem de programação Logo . Você desenha com uma 'tartaruga' (servindo de caneta), que você orienta em torno da superfície de saída. Isso implica orientar a tartaruga em uma direção específica e instruí-la a avançar uma certa distância.

Para trabalhar com esse sistema, eu precisava ajustar as equações espirográficas originais em algo um pouco mais amigável para as tartarugas. Não tenho certeza se essa é a melhor abordagem, mas o algoritmo que criei funciona mais ou menos assim:

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)

Observe que isso realmente desenha o caminho com um tipo de padrão em zigue-zague, mas você não percebe a menos que aumente o zoom na imagem.

Aqui está um exemplo usando os parâmetros R = 73, r = 51, d = 45.

insira a descrição da imagem aqui

Eu testei o código com CCBI e cfunge , os quais produzem saída na forma de uma imagem SVG. Como esse é um formato vetorial escalável, a imagem resultante não possui um tamanho de pixel como tal - apenas se ajusta ao tamanho da tela (pelo menos quando visualizada em um navegador). O exemplo acima é uma captura de tela que foi cortada e dimensionada manualmente.

Em teoria, o código também poderia funcionar no Rc / Funge , mas nesse caso você precisaria rodar em um sistema com o XWindows, pois tentaria renderizar a saída em uma janela.

James Holderness
fonte
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)]]);

Isso é chamado na sessão interativa via f(#,#,#). Como uma amostra, considere f(3,2,1):

insira a descrição da imagem aqui

Kyle Kanos
fonte
Enquanto eu gosto da saída bonita, não tenho certeza de como isso segue "números inteiros entre 1 e 200" ou "dados como pixels".
Geobits 23/05
A entrada pode ser números inteiros ou flutuantes, o wxMaxima será convertido em flutuante para fazer seu trabalho de qualquer maneira; atualizarei uma imagem usando números inteiros. Vou ter que pensar mais sobre a entrada como pixels também.
Kyle Kanos
Sim, imaginei que os converteria internamente, e isso não é um problema. A restrição de número inteiro na entrada era principalmente para facilitar os loops fechados (eles parecem melhores na imo).
Geobits
0

Raquete

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

Resultado:

insira a descrição da imagem aqui

rnso
fonte