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.


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.


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


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

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


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


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 .


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>


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


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


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


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


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


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.


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


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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.