Час спірографа!


14

Спірограф - це іграшка, яка малює гіпотрохоїди та епітрохоїди. Для цього завдання ми просто зосередимось на гіпотрохоїдах.

З Вікіпедії :

Гіпотрохоїд - це рулетка, простежена точкою, приєднаною до кола радіуса r, котиться навколо внутрішньої сторони нерухомого кола радіуса R , де точка - відстань d від центру внутрішнього кола.

Параметричні рівняння для них можна визначити як:

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

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

Де θ - кут, утворений горизонталлю та центром кола кочення.


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

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

Припустимо:

  • Одиниці введення наведені в пікселях.
  • R > = r

Вихідні дані повинні бути графічним зображенням гіпотрохоїда, визначеного введенням. Не допускається жодне ASCII- або інше текстове виведення. Це зображення можна зберегти у файл або відобразити на екрані. Додайте скріншот або зображення виводу для введення на ваш вибір.

Ви можете обрати будь-які кольори, які вам подобаються для шляху / фону, за умови обмеження контрасту. У двох кольорах повинен бути компонент «Значення» HSV не менше половини шкали. Наприклад, якщо ви вимірюєте показник HSV [0...1], має бути принаймні 0.5різниця. Між [0...255]ними має бути мінімальна 128різниця.


Це гольф коду, мінімальний розмір вихідного коду в байтах виграє.


Чи можемо ми припустити R > rчи R ≥ r? (Те саме для rі d.)
Мартін Ендер

10
Вітаємо з розміщенням 2000-го питання! ;-)
Дверна ручка

@ m.buettner R>=r, але dне обмежений rі може бути в будь-якому місці діапазону 1-200.
Геобіць

Про яку резолюцію ми говоримо?
Кайл Канос

@KyleKanos Оскільки вхід знаходиться в пікселях і кожен має обмеження в 200, він ніколи не повинен бути більшим за 798x798 R=200, r=1, d=200. Ви можете розмістити зображення на вхід, якщо хочете, або зберегти його в постійному розмірі, доки воно все видно.
Геобіць

Відповіді:


8

Математика, 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]

Невикольований код та приклад виведення: enter image description here

Якщо я можу включити осі в сюжет, я можу зберегти ще 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

Приклад Вихід

r = 1, R = 200, d = 30

enter image description here


Мені це подобається, але 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

Пітон: 579

Підсумок

Це взагалі не є конкурентоспроможним з огляду на відповідь Mathematica, але я все-таки вирішив опублікувати його, оскільки фотографії гарні, і це може когось надихнути або комусь бути корисним. Оскільки вона набагато більша, я в основному залишив її без волі. Програма очікує введення командного рядка R, r, d.

Знімок екрана

Ось два приклади, один для (5,3,5) та один для (10,1,7) example 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, але тоді воно працює лише в перший раз

Редагувати2 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

@ ӍѲꝆΛҐӍΛПҒЦꝆ yeeeeees я міг би. Це те, про що я не знав у травні 2014 року
edc65

Нічого собі, це було збережено більше байтів, ніж я думав.
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=""))

Деякі приклади виходів:

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

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

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


Це не приймає вхідні розміри в пікселях, чи не так? Перший приклад повинен бути майже втричі більшим, ніж другий.
Мартін Ендер

Чому все ,??
планнапус

Коми використовувались для розділення аргументів, багато з яких були 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.pngGive введіть тут опис зображення


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) для деяких тригонометричних обчислень та графіку черепах (TURT) відбиток пальця для малювання контуру спірографа.

Графіка черепах у Бефунге дуже схожа за поведінкою на графіку на мові програмування Лого . Ви малюєте за допомогою «черепашки» (яка виступає ручкою), яку ви орієнтуєте навколо вихідної поверхні. Це тягне за собою орієнтування черепахи в певному напрямку, а потім вказівку їй рухатися вперед на певну відстань.

Для того, щоб працювати з цією системою, мені потрібно було налаштувати оригінальні рівняння спірографа на щось трохи зручніше для черепах. Я не впевнений, що це найкращий підхід, але алгоритм, який я придумав, працює приблизно так:

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" або "задано у вигляді пікселів".
Геобіць

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

Так, я вважав, що це перетворить їх внутрішньо, і це не проблема. Ціле обмеження на вході полягало в основному, щоб легше було замкнутих циклів (вони просто виглядають краще imo).
Геобіт

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.