¡Tiempo de espirógrafo!


14

Un espirógrafo es un juguete que dibuja hipotrocoides y epitrocoides. Para este desafío, nos centraremos en los hipotrocoides.

De Wikipedia :

Un hipotrocoide es una ruleta trazada por un punto unido a un círculo de radio r que rueda alrededor del interior de un círculo fijo de radio R , donde el punto está a una distancia d del centro del círculo interior.

Las ecuaciones paramétricas para ellos se pueden definir como:

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Donde θ es el ángulo formado por la horizontal y el centro del círculo rodante.


Su tarea es escribir un programa que dibuje el camino trazado por el punto definido anteriormente. Como entrada, se le dará R , r y d , todos los enteros entre 1 y 200 inclusive.

Puede recibir esta entrada de stdin, argumentos o entrada del usuario, pero no se puede codificar en el programa. Puede aceptarlo de la forma que le resulte más conveniente; como cadenas, enteros, etc.

Asumir:

  • Las unidades de entrada se dan en píxeles.
  • R > = r

La salida debe ser una representación gráfica del hipotrocoide definido por la entrada. No se permite ninguna salida ASCII u otra basada en texto. Esta imagen se puede guardar en un archivo o mostrarse en la pantalla. Incluya una captura de pantalla o imagen de la salida para una entrada de su elección.

Puede elegir cualquier color que desee para la ruta / fondo, sujeto a una restricción de contraste. Los dos colores deben tener el componente 'Valor' de HSV al menos a la mitad de la escala. Por ejemplo, si está midiendo HSV desde [0...1], debería haber al menos una 0.5diferencia. Entre [0...255]debería haber una mínima 128diferencia.


Este es un código de golf, el tamaño mínimo del código fuente en bytes gana.


¿Podemos asumir R > ro R ≥ r? (Lo mismo para ry d.)
Martin Ender

10
¡Felicitaciones por publicar la pregunta número 2000! ;-)
Pomo de la puerta

@ m.buettner R>=r, pero dno está limitado a r, y puede estar en cualquier lugar en el rango de 1-200.
Geobits

¿De qué tipo de resolución estamos hablando?
Kyle Kanos

@KyleKanos Dado que la entrada está en píxeles y cada uno tiene un límite de 200, nunca debería ser mayor que 798x798, dado R=200, r=1, d=200. Puede ajustar el tamaño de la imagen a la entrada si lo desea, o mantenerla en un tamaño constante, siempre que esté visible.
Geobits

Respuestas:


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 no protegido y salida de ejemplo: ingrese la descripción de la imagen aquí

Si puedo incluir los ejes en la trama, puedo guardar otros 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

Salida de ejemplo

r = 1, R = 200, d = 30

ingrese la descripción de la imagen aquí


Me gusta, pero ikt está roto de alguna manera. Pruebe los ejemplos en R.
edc65

La última línea podría ser para (; t <R * P; v.lineTo (X (), Y ())) t + = P / R
edc65

@ edc65 No está roto, simplemente no estaba haciendo suficientes iteraciones para hacer una rotación completa en esos ejemplos. He aumentado las iteraciones de 9 * PI a R * 2 * PI y debería ser mejor (sin embargo, he dejado el incremento en PI / 1000 ya que de lo contrario se rompería para valores pequeños de R).
MT0

3

Python: 579

Resumen

Esto no es competitivo en absoluto dada la respuesta de Mathematica, pero decidí publicarlo de todos modos porque las imágenes son bonitas y pueden inspirar a alguien o ser útiles para alguien. Debido a que es mucho más grande, lo dejé básicamente sin golf. El programa espera entrada de línea de comandos de R, r, d.

Captura de pantalla

Aquí hay dos ejemplos, uno para (5,3,5) y otro para (10,1,7) ejemplo 5-3-5 ejemplo 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
¿Puedes ajustar la relación? Parece que la imagen 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

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

La entrada se ingresa a través de la consola, un número por línea.

Captura de pantalla para R = 65, r = 15, d = 24: ingrese la descripción de la imagen aquí


2

GeoGebra, 87

Es decir, si considera que GeoGebra es un 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]]

Acepta la entrada de la barra de entrada de GeoGebra, en el formato <variable>=<value>, por ejemplo R=1000.

Tenga en cuenta que es posible que deba cambiar manualmente el tamaño del zoom para ver la imagen completa.

captura de pantalla

(Lo que está en la parte inferior de la ventana es la barra de entrada de la que estaba hablando)

Pruébelo en línea aquí .


1
Supongo que esto tiene la misma limitación que la presentación de Kyle Kanos: ¿no puede especificar el tamaño en píxeles?
Martin Ender

@ m.buettner Sí, tienes razón ... te perdiste eso
usuario12205

2

HTML + Javascript 256286303

Editar Se eliminó la primera llamada a moveTo, funciona de todos modos. Podría ahorrar más corte beginPath, pero solo funciona la primera vez

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

Prueba

Ponga la entrada en el cuadro de texto (separados por comas) y luego presione la pestaña

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
¡No podría simplemente agregar una identificación al lienzo y usar esa identificación globalmente en lugar de tener que usar querySelector!
Mama Fun Roll

@ ӍѲꝆΛҐӍΛПҒЦꝆ yeeeeees que pude. Es algo de lo que no estaba al tanto en mayo 2014
edc65

Wow, eso fue mucho más bytes guardados de lo que pensaba.
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')}

Sin embargo, si uno quiere figuras 'limpias' (sin ejes, sin etiquetas, etc.), entonces el código tendrá que ser un poco más largo (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)

Un ejemplo de código que usa la versión más larga de f:

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

Algunas salidas de ejemplo:

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí


Esto no toma los tamaños de entrada en píxeles, ¿verdad? El primer ejemplo debería ser casi tres veces más grande que el segundo.
Martin Ender

¿Por qué todo el ,??
plannapus

Las comas se utilizaron para separar los argumentos, muchos de los cuales eran NULL (nada). Aquí se utilizó la coincidencia de argumentos posicionales para reducir la longitud del código. Esto, por supuesto, es una mala práctica de codificación. La forma recomendada sería utilizar una lista de argumentos con nombre, como type = "l", xlabel = "", etc. (¡y deshacerse de las comas redundantes!).
Feng

1

C # 813, era 999

Necesita algo de trabajo para reducir el recuento de bytes. Logré reducirlo un poco. Acepta tres enteros separados por espacios de la consola.

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

Muestra de salida:

Espirógrafo


1

script de shell + gnuplot (153)

La mayor parte del esfuerzo es eliminar los ejes y tics, establecer el tamaño y el rango, y aumentar la precisión. Afortunadamente, gnuplot es natural para el golf, por lo que la mayoría de los comandos se pueden abreviar. Para guardar caracteres, la salida debe ser redirigida a un archivo de imagen 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

Llamar al guión con spiro.sh 175 35 25>i.pngda ingrese la descripción de la imagen aquí


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

Sangrado:

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

Ejemplos:

> f(65,15,24)

ingrese la descripción de la imagen aquí

> f(120,20,40)

ingrese la descripción de la imagen aquí

> f(175,35,25)

ingrese la descripción de la imagen aquí


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:

ingrese la descripción de la imagen aquí


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

Este código se basa en la huella dactilar de Matemáticas de punto fijo (FIXP) para algunos cálculos trigonométricos, y los gráficos de tortuga (TURT) huella dactilar para dibujar la trayectoria del espirógrafo.

Los gráficos de tortuga en Befunge son muy similares en comportamiento a los gráficos en el lenguaje de programación Logo . Dibujas con una 'tortuga' (que sirve como bolígrafo), que giras alrededor de la superficie de salida. Esto implica orientar a la tortuga en una dirección particular y luego indicarle que avance una cierta distancia.

Para trabajar con este sistema, necesitaba ajustar las ecuaciones del espirógrafo original en algo un poco más amigable para las tortugas. No estoy seguro de si este es el mejor enfoque, pero el algoritmo que se me ocurrió funciona de esta manera:

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)

Tenga en cuenta que esto realmente dibuja el camino con una especie de patrón de zig-zag, pero realmente no se da cuenta a menos que acerque la imagen de cerca.

Aquí hay un ejemplo usando los parámetros R = 73, r = 51, d = 45.

ingrese la descripción de la imagen aquí

He probado el código con CCBI y cfunge , que producen resultados en forma de imagen SVG. Dado que este es un formato vectorial escalable, la imagen resultante no tiene un tamaño de píxel como tal, solo se escala para adaptarse al tamaño de la pantalla (al menos cuando se ve en un navegador). El ejemplo anterior es una captura de pantalla que se ha recortado y escalado manualmente.

En teoría, el código también podría funcionar en Rc / Funge , pero en ese caso necesitaría ejecutarse en un sistema con XWindows, ya que intentará representar la salida en una ventana.


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

Esto se llama en la sesión interactiva a través de f(#,#,#). Como muestra, considere f(3,2,1):

ingrese la descripción de la imagen aquí


Si bien me gusta la salida bonita, no estoy seguro de cómo esto sigue a "enteros entre 1 y 200" o "dado como píxeles".
Geobits

La entrada puede ser enteros o flotantes, wxMaxima se convertirá en flotante para hacer su trabajo de todos modos, actualizaré una imagen usando enteros. También tendré que pensar más en la entrada como píxeles.
Kyle Kanos

Sí, pensé que los convertiría internamente, y eso no es un problema. La restricción de enteros en la entrada fue principalmente para hacer que los bucles cerrados fueran más fáciles (simplemente se ven mejor en mi opinión).
Geobits

0

Raqueta

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

Salida:

ingrese la descripción de la imagen aquí

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.