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


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.