Время спирографа!


14

Спирограф - это игрушка, которая рисует гипотрохоиды и эпитрохоиды. Для этой задачи мы просто сосредоточимся на гипотрохоидах.

Из Википедии :

Гипотрохоид - это рулетка, отслеживаемая точкой, прикрепленной к окружности радиуса r, катящейся внутри неподвижной окружности радиуса R , где точка - это расстояние d от центра внутренней окружности.

Параметрические уравнения для них могут быть определены как:

введите описание изображения здесь

введите описание изображения здесь

Где θ - угол, образованный горизонталью и центром круга качения.


Ваша задача - написать программу, которая нарисует путь, пройденный точкой, определенной выше. В качестве входных данных вы получите R , r и d , все целые числа от 1 до 200 включительно.

Вы можете получить этот ввод из стандартного ввода, аргументов или пользовательского ввода, но его нельзя жестко запрограммировать в программе. Вы можете принять его в любой удобной для вас форме; в виде строк, целых чисел и т. д.

Предполагать:

  • Единицы ввода даны в пикселях.
  • R > = r

Выходными данными должно быть графическое представление гипотрохоида, определенного входными данными. ASCII- или другой текстовый вывод не допускается. Это изображение может быть сохранено в файл или отображено на экране. Включите скриншот или изображение вывода для ввода по вашему выбору.

Вы можете выбрать любые цвета для траектории / фона, с учетом ограничения контраста. Два цвета должны иметь компонент «Значение» HSV как минимум на половине шкалы. Например, если вы измеряете ВПГ от [0...1], должна быть как минимум 0.5разница. Между [0...255]ними должна быть минимальная 128разница.


Это код гольфа, выигрывает минимальный размер исходного кода в байтах.


Можем ли мы предположить R > rили R ≥ r? (То же самое для rи d.)
— Мартин Эндер

10
Поздравляем с размещением 2000-го вопроса! ;-)
— Дверная ручка

@ m.buettner R>=r, но dне ограничен rи может находиться где угодно в диапазоне 1-200.
— Geobits

О каком разрешении мы говорим?
— Кайл Канос

@KyleKanos Так как ввод в пикселях, и каждый имеет ограничение 200, он не должен быть больше, чем 798x798, учитывая R=200, r=1, d=200. Вы можете изменить размер изображения для ввода, если хотите, или оставить его в постоянном размере, пока все это видно.
— Geobits

Ответы:


8

Mathematica, 120 байт

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]

Разобранный код и пример вывода: введите описание изображения здесь

Если я могу включить оси в график, я могу сохранить еще 9 символов.


5

JavaScript (ECMAScript 6) - 312 314 символов

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

Пример вывода

г = 1, R = 200, d = 30

введите описание изображения здесь


Мне нравится, но Ikt как-то сломан. Попробуйте примеры из R.
— edc65,

Последняя строка может быть для (; t <R * P; v.lineTo (X (), Y ())) t + = P / R
— edc65

@ edc65 Это не сломано, просто не было достаточно итераций, чтобы выполнить полный оборот в этих примерах. Я увеличил количество итераций с 9 * PI до R * 2 * PI, и это должно быть лучше (однако я оставил приращение на PI / 1000, так как в противном случае оно будет нарушаться при малых значениях R).
— MT0

3

Python: 579

Резюме

Это вообще неконкурентоспособно, учитывая ответ Mathematica, но я решил опубликовать его в любом случае, потому что картинки красивые, и это может вдохновить кого-то или быть полезным для кого-то. Поскольку это намного больше, я оставил это в основном безволосым. Программа ожидает ввода в командной строке R, r, d.

Скриншот

Вот два примера: один для (5,3,5) и один для (10,1,7) пример 5-3-5 пример 10-1-7

Код

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
Вы можете отрегулировать соотношение? Кажется, что изображение сжато по вертикали.
— 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

Обработка, 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);
}

Ввод вводится через консоль, по одному номеру в строке.

Снимок экрана для R = 65, r = 15, d = 24: введите описание изображения здесь


2

ГеоГебра, 87

То есть, если вы считаете GeoGebra допустимым языком.

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

Принимает ввод с панели ввода GeoGebra в формате <variable>=<value>, например R=1000.

Обратите внимание, что вам может потребоваться вручную изменить размер зума, чтобы просмотреть все изображение.

Скриншот

(В нижней части окна находится строка ввода, о которой я говорил)

Попробуйте это онлайн здесь .


1
Я предполагаю, что это имеет то же ограничение, что и представление Кайла Каноса, что вы не можете указать размер в пикселях?
— Мартин Эндер

@ m.buettner Да, ты прав ... пропустил это
— user12205

2

HTML + Javascript 256 286 303

Редактировать Удален первый вызов moveTo, все равно работает. Можно было бы сохранить больше резки beginPath, но тогда это работает только в первый раз

Edit2 30 байт сохранено 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()">

Тестовое задание

Поместите ввод в текстовое поле (через запятую), затем нажмите вкладку

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
Не могли бы вы просто добавить идентификатор на холст и использовать этот идентификатор глобально вместо использования querySelector!
— Mama Fun Roll

@ ӍѲꝆΛҐӍΛПҒЦꝆ дааааааааа, я мог. Это то, о чем я не знал в
— мае

Ничего себе, это было сэкономлено больше байтов, чем я думал.
— Mama Fun Roll

2

R, 80 байт

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

Однако, если вам нужны «чистые» цифры (без осей, без меток и т. Д.), Код должен быть немного длиннее (88 символов):

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

Один пример кода с использованием более длинной версии f:

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

Некоторые примеры выходных данных:

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь


Это не принимает входные размеры в пикселях, не так ли? Первый пример должен быть почти в три раза больше второго.
— Мартин Эндер

Почему все то ,??
— plannapus

Запятые были использованы для разделения аргументов, многие из которых были NULL (ничего). Здесь позиционное сопоставление аргументов использовалось для уменьшения длины кода. Это, конечно, плохая практика кодирования. Рекомендуемый способ - использовать список именованных аргументов, например type = "l", xlabel = "" и т. Д. (И избавиться от лишних запятых!).
— Фэн

1

C # 813, было 999

Требуется некоторая работа, чтобы уменьшить количество байтов. Мне удалось немного уменьшить его. Он принимает три целых числа, разделенных пробелом, из консоли.

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

Выходной образец:

спирограф


1

сценарий оболочки + gnuplot (153)

Большая часть усилий заключается в удалении осей и тиков, установке размера и диапазона и увеличении точности. К счастью, gnuplot естественен для игры в гольф, поэтому большинство команд можно сокращать. Чтобы сохранить символы, вывод должен быть перенаправлен в файл изображения вручную.

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

Вызов сценария с spiro.sh 175 35 25>i.pngдает введите описание изображения здесь


1

R, 169 символов

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

Отступ:

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

Примеры:

> f(65,15,24)

введите описание изображения здесь

> f(120,20,40)

введите описание изображения здесь

> f(175,35,25)

введите описание изображения здесь


1

SmileBASIC, 96 байт

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

Ввод: 50,30,50:

введите описание изображения здесь


1

Befunge-98, 113 байт

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

Этот код опирается на отпечаток FIXP для некоторых тригонометрических вычислений и отпечаток Turtle Graphics (TURT) для рисования пути спирографа.

Графика Turtle в Befunge очень похожа на графику в языке программирования Logo . Вы рисуете «черепахой» (служащей вашей ручкой), которой вы управляете по выходной поверхности. Это влечет за собой ориентацию черепахи в определенном направлении, а затем дает ей указание двигаться вперед на определенное расстояние.

Чтобы работать с этой системой, мне нужно было приспособить исходные уравнения спирографа к чему-то более дружественному для черепах. Я не уверен, что это лучший подход, но алгоритм, который я придумал, работает примерно так:

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)

Обратите внимание, что это на самом деле рисует путь с неким зигзагообразным узором, но вы на самом деле не заметите, если не приблизите изображение ближе.

Вот пример использования параметров R = 73, r = 51, d = 45.

введите описание изображения здесь

Я протестировал код с CCBI и cfunge , которые выдают результат в виде SVG-изображения. Поскольку это масштабируемый векторный формат, результирующее изображение не имеет размера пикселя как такового - оно просто масштабируется, чтобы соответствовать размеру экрана (по крайней мере, при просмотре в браузере). Пример выше - это снимок экрана, который был обрезан и масштабирован вручную.

Теоретически код также может работать на Rc / Funge , но в этом случае вам нужно будет работать в системе с XWindows, так как он попытается отобразить вывод в окне.


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

Это вызывается в интерактивной сессии через f(#,#,#). В качестве примера рассмотрим f(3,2,1):

введите описание изображения здесь


Хотя мне нравится симпатичный вывод, я не уверен, как это следует за «целыми числами от 1 до 200» или «задано в виде пикселей».
— Geobits

Входные данные могут быть целыми числами или числами с плавающей точкой, wxMaxima преобразует в число с плавающей точкой, чтобы выполнить свою работу в любом случае, я обновлю изображение, используя целые числа. Мне придется больше думать о вводе в виде пикселей.
— Кайл Канос

Да, я полагал, что это конвертирует их внутренне, и это не проблема. Целочисленное ограничение на вводе было главным образом для упрощения получения замкнутых циклов (они просто выглядят лучше, imo).
— Geobits

0

рэкет

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

Выход:

введите описание изображения здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.