Rysowanie epicyklonów

22

Epicykloida jest krzywa punktu na kole, jak to czyni rolki wokół drugiego okręgu. Cyclogon jest kształt punktem na wielokąta foremnego sprawia, gdyż rzuca w całej płaszczyźnie. NaEpicyclogon jest krzywa śledzone przez punkt na jednej wielokąta foremnego, jak toczy się wokół drugiego.

Napisz program, który rysuje epicyclogon podane r, r1, r2, n1, n2:

r = number of clockwise revolutions rolling polygon makes around stationary polygon (any real number as limited by float values) 
r1 = distance from center of stationary polygon to each of its vertices (positive real number)
r2 = distance from center of rolling polygon to each of its vertices (positive real number)
n1 = number of sides stationary polygon has (integer greater than 2)
n2 = number of sides rolling polygon has (integer greater than 2)

Notatki

  • Kiedy rjest ujemny, wałek powinien odejść kierunku przeciwnym ruchu wskazówek zegara .
  • Bo rjeden obrót ma miejsce, gdy linia łącząca centroidy dwóch kształtów rozciąga się o pełne 360 ​​stopni. Pojęcie to zostało rozszerzone o wszystkie wartości r. (Tak więc w ćwierć obrotu linia łącząca centroidy rozciąga się o 90 stopni.)
  • Argumenty te powinny pochodzić z wiersza poleceń lub twój program powinien je o nie poprosić (np. W języku Python input()).
  • r1i r2są względem siebie, a nie wymiary obrazu. Możesz więc ustawić jedną „jednostkę” na dowolną liczbę rzeczywistych pikseli.

Punkt, który musisz prześledzić, jest jednym z wierzchołków tocznego kształtu. Kształty muszą zaczynać się od tego wierzchołka, który dotyka nieruchomego wierzchołka i sąsiaduje z dwoma bokami:

przykład epicyclogon

Dokładne początkowe wierzchołki i kąt stacjonarnego wielokąta nie mają znaczenia.

Wydajność

Dane wyjściowe powinny przejść do obrazu o rozmiarze co najmniej 600 x 600 pikseli (lub innym zmiennym wymiarze niż można ustawić na 600). Musi pokazywać całą krzywą epicyklogonu określoną parametrami, dobrze wykadrowaną na obrazie.

Toczone i stacjonarne wielokąty muszą być również narysowane (z rolką w jej końcowym stanie). Dwa kształty i epicyclogon powinny mieć trzy zauważalnie różne kolory.

Musi również istnieć prosty sposób, aby nie rysować wielokątów (zmiana truenafalse w kodzie).

Pokaż nam co najmniej 2 obrazy wyjściowe. W razie potrzeby można je zmniejszyć.

Punktacja

Wygrywa najkrótszy kod, który produkuje prawidłowe obrazy wyjściowe.

Bonusy

  • Minus 50 bajtów, jeśli wynikiem jest animowany gif (lub podobny) rysowanej krzywej.
  • Minus 150 bajtów, jeśli pozwolisz n1i n2przyjmiesz wartość 2, aby kształty stały się odcinkami linii o długości 2 * r1(lub r2), „toczącymi się” wokół siebie. To, jak poradzisz sobie, rkiedy n1i kiedy n2masz 2 lata, zależy od ciebie, ponieważ centroidy nie obracają się wokół siebie, tak jak w innych przypadkach. (Brak „toczenia” w ogóle nie liczy się jako obsługa).

Ponieważ bardzo chętnie widzę, jak ten nowatorski pomysł został wykonany dobrze (i nie jest to dokładnie bułka z masłem), nagrodzę zwycięzcę 150 repozytorium nagród . Konkurs zakończy się tego samego dnia, w którym skończy się nagroda.

Nagroda nie zostanie przyznana zwycięzcy, jeśli jest jasne, że po prostu przepisali większość kodu z innego zgłoszenia.

Funkcje biblioteczne, które już to robią (jeśli takie istnieją) są niedozwolone.

Uwaga: Pochodzi z moich pozostałych pytań, które każdy może swobodnie publikować. Ale jeśli nikt inny ich nie opublikuje, istnieje duża szansa, że ​​zdążę. : P

Hobby Calvina
źródło
Myślę, że zamiast tego powinno być pozytywne.
Soham Chowdhury
3
@SohamChowdhury Myślę, że to nie ma znaczenia.
Calvin's Hobbies
Właściwie masz rację. Czy masz jakieś przykładowe obrazy? Nie mam odtwarzacza CDF.
Soham Chowdhury
@githubphagocyte Widzę twój punkt widzenia. Naprawiony.
Calvin's Hobbies
@ MartinBüttner Niezbyt surowe, to była tylko pierwsza rzecz, o której myślałem. W razie potrzeby możesz poprosić o wartości w inny sposób.
Calvin's Hobbies

Odpowiedzi:

3

MATLAB: 735 bajtów - 200 bonusów = 535

Mój program obsługuje przypadek n = 2 i rysuje animację w czasie rzeczywistym. Istnieje kilka różnic między wersjami golfową i nie golfową:

Wersja bez golfa ma tylko opcję zapisania animacji do pliku „g.gif”, poprzez ustawienie savegif = 1w kodzie. Domyślnie jest wyłączone, ponieważ może być denerwujące z kilku powodów:

  • Tworzenie niechcianego pliku
  • Możliwe opóźnienie
  • Błąd jest generowany, jeśli masz wiele monitorów, a okno fabuły nie jest prawe ... Oszczędność gifów musiała zostać zmniejszona w wersji golfowej, ponieważ zajęło to około 100 bajtów, przekraczając wielkość premii.

Wersja bez golfa rysuje okrąg na wierzchołku znacznika. Tworzy także więcej klatek i porusza się szybciej (choć można to zmienić w wersji golfowej, zmieniając liczby).

Próbki:

f(11,5,90,2,99,0) po zakończeniu programu

próbka golfa

epic(1.3,4,2,6,6,1) z wyjściem gif

próbka bez golfa

Nieskluczony kod

%epicyclogon animation outputs to 'g.gif' if savegif=1 as well as animating in real time

function[] = epic(r,r1,r2,n1,n2,dispPoly)

savegif = 0;  %set to 1 to write .gif

cs = @(a) [cos(a);sin(a)];
vert = @(r, n, v) r * cs(2*pi*v/n);
polyPt = @(l, s, n, r) vert(r, n, floor(l/s)) + mod(l/s,1)*(vert(r, n, floor(l/s)+1) - vert(r, n, floor(l/s)));
polyPt2 = @(i, f, n, r) vert(r, n, i) + f*(vert(r, n, i+1) - vert(r, n, i));
rotm = @(a) [cos(a) -sin(a);sin(a) cos(a)];
arrpluspt = @(a, p) a + kron(p, ones(1,length(a)));
arg = @(p) atan2(p(2), p(1));

E = 1e-9;

dispPoly = dispPoly / dispPoly;

sgn = sign(-r);
r = abs(r);

s1 = 2*r1*sin(pi/n1);
s2 = 2*r2*sin(pi/n2);

%d1 = (r1*r1 - s1*s1*.25)^.5;
d2 = (r2*r2 - s2*s2*.25)^.5;

plotmax = r1+2*r2;

astep = .05; %determines amount of frames per rotation
delay = .01; % time per frame

l = 0;

lRem = 0;
lr = 0;

P1 = vert(r1, n1, 1:n1+1) * dispPoly; 
trace = [];

first = 1;
while 1

    if lr %exists while rotating about a corner of the stationary
        rotA = 2*pi/n1;
    else
        rotA = 2*pi/n2;
    end
    rotPt = polyPt(l, s1, n1, r1);
    lb = l + lRem;
    side1 = floor(l / s1 - E);
    side1up = side1 + lr;
    p2cen = polyPt2(side1, lb/s1 -side1 - .5 * s2/s1, n1, r1) + d2 * cs(2*pi*(side1+.5)/n1);
    if first
        p2cen0 = p2cen;
        r = r + arg(p2cen0)/(2*pi);
    end

    for a = 0:astep:rotA    
        P2 = vert(r2, n2, 0:n2);
        P2 = rotm( pi +pi/n1 -pi/n2   +2*pi*side1/n1) * P2;
        P2 = arrpluspt(P2, p2cen);
        P2 = arrpluspt(P2, -rotPt);
        P2 = rotm(a) * P2;
        P2 = arrpluspt(P2, rotPt);
        trV = mod(floor(l/s2 + E) + lr, n2) + 1;

        cen = rotm(a) * (p2cen - rotPt) + rotPt;
        trace = [trace,P2(:,trV)]; 

        plot(P1(1,:), sgn*P1(2,:), P2(1,:)*dispPoly, sgn*P2(2,:)*dispPoly, trace(1,:),sgn*trace(2,:),P2(1,trV), sgn*P2(2,trV),'o');

        %plot(P1(1,:), P1(2,:), P2(1,:), P2(2,:), trace(1,:),trace(2,:),...
        %[0,p2cen0(1)],[0,p2cen0(2)],[0,cen(1)],[0,cen(2)], P2(1,trV), P2(2,trV),'o');

        axis([-plotmax,plotmax,-plotmax,plotmax]);
        axis square
        figure(1);
       if savegif
           drawnow
           frame = getframe(1); % plot window must be on same monitor!
           img = frame2im(frame);
           [img1,img2] = rgb2ind(img,256);
       end
       if first
           if savegif
               imwrite(img1,img2,'g','gif','DelayTime',2*delay); %control animation speed(but not really)
           end
           first = 0;
       else
           if savegif
               imwrite(img1,img2,'g','gif','WriteMode','append','DelayTime', 2*delay);
           end
       end
       pause(.01);

        adf = mod(arg(cen) - r*2*pi, 2*pi);
        if adf < astep & l/(n1*s1) + .5 > r
            return
        end

    end

%cleanup for next iteration 
    jump = lRem + ~lr * s2; 
    lnex = l + jump; 

    if floor(lnex / s1 - E) > side1up 
        lnex = s1*(side1up+1);
        lRem = jump - (lnex - l);
        lr = 1;
    else    
        lRem = 0;
        lr = 0;
    end
    l = lnex;
end

Kod do gry w golfa

function[]=f(r,h,H,n,N,d)
P=pi;T=2*P;F=@floor;C=@(a)[cos(a);sin(a)];g=@(i,f,n,r)r*C(T*i/n)*(1-f)+f*r*C(T*(i+1)/n);R=@(a)[C(a),C(a+P/2)];W=@(a,p)[a(1,:)+p(1);a(2,:)+p(2)];b=@(p)atan2(p(2),p(1));E=1e-9;d=d/d;S=1-2*(r>0);r=-r*S;x=2*h*sin(P/n);X=2*H*sin(P/N);M=h+2*H;l=0;z=0;L=0;A=h*C(T*(0:n)/n)*d;t=[];while 1
v=l/x;D=F(v-E);q=g(D,v-D,n,h);Z=D+L;c=g(D,v+z/x-D-.5*X/x,n,h)+H*cos(P/N)*C(T*D/n+P/n);r=r+~(l+L)*b(c)/T;for a=0:.1:T/(L*n+~L*N)
O=@(p)W(R(a)*W(p,-q),q);B=O(W(R(P+P/n-P/N+T*D/n)*H*C(T*(0:N)/N),c));t=[t,B(:,mod(F(l/X+E)+L,N)+1)];plot(A(1,:),S*A(2,:),d*B(1,:),d*S*B(2,:),t(1,:),t(2,:)*S)
axis([-M,M,-M,M],'square');pause(.1);if.1>mod(b(O(c))-r*T,T)&v/n+.5>r
return;end;end;j=z+~L*X;J=l+j;L=F(J/x-E)>Z;l=L*x*(Z+1)+~L*J;z=L*(J-l);end

Instrukcje:

Zapisz funkcję w pliku o tej samej nazwie, tj . epic.mLub f.m. Uruchom go, wywołując funkcję z konsoli Matlab.

Zastosowanie: epic(r, r1, r2, n1, n2, dispPoly) gdzie dispPolyjest zmienną logiczną (zero, jeśli fałsz, liczba niezerowa, jeśli prawda) określająca, czy narysować wielokąty.

Edycja: Dodano premię 50 za animowany obraz.

feersum
źródło
14

Java - 2726 2634 - 200 = 2434 znaków

Poprawiono z 3800 bajtów ish

Dziękujemy wszystkim za sugestie (zwłaszcza pseudonim 117), oto nowa wersja.

Dodałem klasę P, która jest klasą punktową i klasę L, która rozszerza ArrayList

Dodałem także kilka drobnych zmian logicznych.

Oto główna klasa (nie golfowa):

import java.awt.*;
import java.awt.geom.*;

import javax.swing.*;
public class Polygons2 extends JPanel{
    public static void main(String[] args) throws InterruptedException{new Polygons2(args);}
    double q=Math.PI*2;
    int d=1;
    public Polygons2(String[] args) throws InterruptedException{
        double revolutions=Double.valueOf(args[0])*q;
        double stationaryRadius = Double.valueOf(args[1]);
        double rollingRadius = Double.valueOf(args[2]);
        int stationarySides = Integer.valueOf(args[3]);
        int rollingSides = Integer.valueOf(args[4]);    
        double dist = stationaryRadius+rollingRadius+70;
        P sp = new P(dist,dist);
        P rp = new P(sp.x,sp.y-rollingRadius-stationaryRadius);
        //get points for rolling polygon and stationary polygon
        int key=0;
        for(double stationaryAngle=-q/4;stationaryAngle<q-q/4;stationaryAngle+=q/stationarySides){
            P p=new P(Math.cos(stationaryAngle)*stationaryRadius+sp.x,Math.sin(stationaryAngle)*stationaryRadius+sp.y);
            p.k=key;key++;
            stationaryPoints.add(p);
        }
        for(double rollingAngle=q/4;rollingAngle<q+q/4;rollingAngle+=q/rollingSides){
            P p=new P(Math.cos(rollingAngle)*rollingRadius+rp.x,Math.sin(rollingAngle)*rollingRadius + rp.y);
            p.k=key;key++;
            rollingPoints.add(p);
        }
        double g=(q/2)-((q/2-(q/rollingSides))/2) - ((q/2-(q/stationarySides))/2)-.05;
        for(P p:rollingPoints){p.r(getPoint(0), g);}
        //set up JFrame
        JFrame f = new JFrame();
        f.add(this);
        f.setSize((int)dist*2+60,(int)dist*2+60);
        f.setVisible(true);
        int[] pKeys= new int[]{stationaryPoints.get(0).k,rollingPoints.get(0).k};
        int index=1;
        P rc = rollingPoints.c();
        P sc =stationaryPoints.c();
        double currentRadian=Math.atan2(rc.y-sc.y,rc.x-sc.x);
        double totalRadian = 0;
        while(Math.abs(totalRadian)<revolutions){
            P rc2 = rollingPoints.c();
            P sc2 =stationaryPoints.c();
            double angle = Math.atan2(rc2.y-sc2.y,rc2.x-sc2.x);
            if(currentRadian-angle<2){totalRadian+=(angle-currentRadian);}
            currentRadian=angle;
            L clone=(L)path.clone();
            clone.add(new P(rollingPoints.get(1).x,rollingPoints.get(1).y));
            path = clone;
            for(P p:rollingPoints){
                p.r(getPoint(pKeys[index]),.01);
                int size = stationaryPoints.size();
                for(int i=0;i<size;i++){
                    P stationaryPointAtI = stationaryPoints.get(i);
                    P nextPoint=null;
                    if(i==size-1){nextPoint=stationaryPoints.get(0);}
                    else{nextPoint=stationaryPoints.get(i+1);}
                    if(p.b(stationaryPointAtI, nextPoint)==1&&containsKey(pKeys,p.k)==0){
                        //rolling point is between 2 stationary points
                        if(index==1){index=0;}else{index=1;}
                        pKeys[index]=p.k;
                    }
                    int size2=rollingPoints.size();
                    for(int h=0;h<size2;h++){
                        P nextPoint2=null;
                        if(h==size2-1){nextPoint2=rollingPoints.get(0);}
                        else{nextPoint2=rollingPoints.get(h+1);}
                        if(stationaryPointAtI.b(rollingPoints.get(h), nextPoint2)==1&&containsKey(pKeys,stationaryPointAtI.k)==0){
                            //stationary point is between 2 rolling points
                            if(index==1){index=0;}else{index=1;}
                            pKeys[index]=stationaryPointAtI.k;
                        }
                    }
                }
            }
            repaint();
            Thread.sleep(5);
        }
    }
    volatile L path = new L();
    L rollingPoints = new L();
    L stationaryPoints = new L();
    P getPoint(int key){
        for(P p:rollingPoints){if(p.k==key){return p;}}
        for(P p:stationaryPoints){if(p.k==key){return p;}}
        return null;
    }
    int containsKey(int[] keys,int key){
        for(int i:keys){if(key==i){return 1;}}
        return 0;
    }
    @Override
    public void paintComponent(Graphics g){
        Path2D.Double sPath = new Path2D.Double();
        sPath.moveTo(stationaryPoints.get(0).x, stationaryPoints.get(0).y);
        for(P p:stationaryPoints){
            sPath.lineTo(p.x, p.y);
        }
        sPath.closePath();
        Path2D.Double rPath = new Path2D.Double();
        rPath.moveTo(rollingPoints.get(0).x, rollingPoints.get(0).y);
        for(P p:rollingPoints){
            rPath.lineTo(p.x, p.y);
        }
        rPath.closePath();
        g.setColor(Color.white);
        g.fillRect(0,0,getWidth(),getHeight());
        Graphics2D t = (Graphics2D)g;
        if(d==1){
        t.setColor(Color.black);
        t.draw(sPath);
        t.setColor(Color.blue);
        t.draw(rPath);
        }
        g.setColor(Color.green);
        for(P p:path){g.fillOval((int)p.x-1, (int)p.y-1, 2, 2);}
    }
}

I wersja w golfa:

import java.awt.*;import java.awt.geom.*;import javax.swing.*;import static java.lang.Math.*;class Polygons2Golfed extends JPanel{public static void main(String[]a)throws Exception{new Polygons2Golfed(a);}double q=PI*2;int d=1;public Polygons2Golfed(String[]a)throws Exception{double b,c,f;b=Double.valueOf(a[1]);c=Double.valueOf(a[2]);int d,e;d=Integer.valueOf(a[3]);e=Integer.valueOf(a[4]);f=b+c+100;P o=new P(f,f);P r=new P(o.x,o.y-c-b);int s=0;for(double u=-q/4;u<q-q/4;u+=q/d){P p=new P(cos(u)*b+o.x,sin(u)*b+o.y);p.k=s;s++;l.add(p);}for(double u=q/4;u<q+q/4;u+=q/e){P p=new P(cos(u)*c+r.x,sin(u)*c+r.y);p.k=s;s++;k.add(p);}double g=q/e/2+q/d/2-.05;for(P p:k){p.r(v(0),g);}JFrame j=new JFrame();j.add(this);j.setSize((int)f*2+60,(int)f*2+60);j.setVisible(true);m=new int[]{l.get(0).k,k.get(0).k};int ad=1;P rc=k.c();P sc=l.c();double ab,ac;ab=atan2(rc.y-sc.y,rc.x-sc.x);ac=0;while(abs(ac)<Double.valueOf(a[0])*q){P rc2=k.c();P sc2=l.c();double ah=atan2(rc2.y-sc2.y,rc2.x-sc2.x);if(ab-ah<2)ac+=(ah-ab);ab=ah;L ag=(L)n.clone();ag.add(new P(k.get(1).x,k.get(1).y));n=ag;for(P p:k){p.r(v(m[ad]),.01);int af=l.size();for(int i=0;i<af;i++){P aa=l.get(i);P w=null;if(i==af-1){w=l.get(0);}else{w=l.get(i+1);}if(p.b(aa, w)==1&&w(p.k)==0){if(ad==1)ad=0;else ad=1;m[ad]=p.k;}int ae=k.size();for(int h=0;h<ae;h++){P u=null;if(h==ae-1)u=k.get(0);else u=k.get(h+1);if(aa.b(k.get(h),u)==1&&w(aa.k)==0){if(ad==1)ad=0;else ad=1;m[ad]=aa.k;}}}}repaint();Thread.sleep(5);}}L n=new L();L k=new L();L l=new L();P v(int key){for(P p:k){if(p.k==key)return p;}for(P p:l){if(p.k==key)return p;}return null;}int[]m;int w(int key){for(int i:m){if(key==i)return 1;}return 0;}@Override public void paintComponent(Graphics g){Path2D.Double aq=new Path2D.Double();aq.moveTo(l.get(0).x,l.get(0).y);for(P p:l){aq.lineTo(p.x, p.y);}aq.closePath();Path2D.Double aw=new Path2D.Double();aw.moveTo(k.get(0).x, k.get(0).y);for(P p:k){aw.lineTo(p.x, p.y);}aw.closePath();g.setColor(Color.white);g.fillRect(0,0,getWidth(),getHeight());Graphics2D t=(Graphics2D)g;if(d==1){t.setColor(Color.black);t.draw(aq);t.setColor(Color.blue);t.draw(aw);}g.setColor(Color.green);for(P p:n){g.fillOval((int)p.x-1,(int)p.y-1,2,2);}}}

Jak również klasy P:

import java.awt.geom.*;class P{double x,y;public P(double a,double b){x=a;y=b;}int k;void r(P c,double g){double a,r;a=Math.atan2(y-c.y,x-c.x)+g;r=Math.sqrt((c.x-x)*(c.x-x)+(c.y-y)*(c.y-y));x=Math.cos(a)*r+c.x;y=Math.sin(a)*r+c.y;}public int b(P a,P b){if(Line2D.ptSegDist(a.x,a.y,b.x,b.y,x,y)<.5)return 1;return 0;}}

I ja:

import java.util.*;public class L extends ArrayList<P>{public P c(){double x,y;x=0;y=0;for(P p:this){x+=p.x;y+=p.y;}return new P(x/size(),y/size());}}

Zmień int d na 0 lub 1, aby wyświetlić wielokąty

argumenty - 1 100 50 5 2

wprowadź opis zdjęcia tutaj

argumenty - 1,5 100 100 7 3

wprowadź opis zdjęcia tutaj

args - 2 40 100 3 7

wprowadź opis zdjęcia tutaj

Stretch Maniac
źródło
Czy rnaprawdę 50 we wszystkich twoich przykładach? Oznaczałoby to, że walec porusza się około 50 razy.
Calvin's Hobbies,
@ Nowy przykład Calvina hobby pokazuje pi * 3
Stretch Maniac
1
@StretchManiac To nie może mieć racji. 3π powinien zabrać cię nieco ponad 9 razy wokół stacjonarnego wielokąta.
Martin Ender
4
To zabawne, jak nazwa klasy znajduje się RotatingPolygonsGolfedw kodzie „golfowym”, podczas gdy jest po prostu RotatingPolygonsw normalnym kodzie. ;)
Hobby Calvina
1
możesz zaoszczędzić sporo znaków, po prostu zmieniając import, aby używać * zamiast określonych klas ...
pseudonim117,
12

JavaScript, 1284 znaki (-200 = 1084 znaki)

Kod minimalny to

function epi(B,r2,r1,n2,n1){K=Math;function C(t){return K.cos(t)}function S(t){return K.sin(t)}function A(y,x){return K.atan2(y,x)}P=K.PI;v=[[],[]];w=[[],[]];z=[];function Z(x,y,j){c=C(t=f*H+P/2);s=S(t);v[j][n]=c*x-s*y;w[j][n]=s*x+c*y;}function E(i){return{x:r1*S(t=p-i*q),y:r1*C(t)};}function D(x,y,X,Y,t){L=A(m.y,m.x);M=K.sqrt(m.x*m.x+m.y*m.y);N=K.sqrt(X*X+Y*Y);O=~~(t*(M>N?M:N)+1);for(i=J;i<=O;i++){J=1;z[n]=f*H+P+t*i/O;Z(x+M*C(T=L+t*i/O),y+M*S(T),0);Z(x+N*C(T=A(Y,X)+t*i/O),y+N*S(T),1);n++}}function F(x,y,n,r,L,s){I.strokeStyle=s;I.beginPath();for(i=0;i<n;i++)I[i?'lineTo':'moveTo'](x+r*C(t=L+(1-2*i)*P/n),y+r*S(t)*W);I.closePath();I.stroke()}p=P/n1;q=2*p;u=P/n2;H=2*u;s2=r2*S(u);g=f=l=n=J=h=0;R=300;while(l<=(B*2+1)*P/H){o=E(0);m=E(h);m.y-=o.y;m.x-=o.x;if(g<s2){D(g,-r2*C(u),-o.x,-o.y,q);h=(h+1)%n1;g+=2*r1*S(p)}else{m.x+=g-s2;D(s2,-r2*C(u),-o.x+g-s2,-o.y,H);g-=s2*2;f=(f+1)%n2;l++}}return function(e_,t,aa,W_){W=aa?-1:1;I=(e=e_).getContext('2d');I.fillStyle='black';I.fillRect(0,0,600,600);W_&1&&F(R,R,n2,r2,0,'white');T=A(w[1][0],v[1][0]);U=V=0;I.strokeStyle='teal';I.beginPath();I.moveTo(R+v[0][0],R+w[0][0]*W);while(U<t){_=A(w[1][V+1],v[1][V+1]);U+=_-T+(_+1<T?2*P:0);T=_;V++;I.lineTo(R+v[0][V],R+w[0][V]*W)}W_&2&&I.stroke();W_&4&&F(R+v[1][V],R+w[1][V]*W,n1,r1,z[V],'red')}}

Pełny kod to

function epi( nr, r2, r1, n2, n1 ) {
function C( t )
    { return Math.cos( t ); }
function S( t )
    { return Math.sin( t ); }
function A( dy, dx )
    { return Math.atan2( dy, dx ); }

var iCCW, e, t_, xs = [[],[]], ys = [[],[]], ts = [], n = 0, iArc0 = 0;

function addpt( x, y, iBin ) {
    var c_ = C(t_ = iFrame*t2 + Math.PI/2 ),
        s_ = S(t_);

    xs[iBin][n] = c_*x-s_*y;
    ys[iBin][n] = s_*x+c_*y;
}

function poly1pt( iP )
    { return { x: r1*S(t_ = t1b2-iP*t1), y: r1*C(t_) }; }

function arc1( P_Arc_, xP_, yP_, xC_, yC_, t ) {
    var dx_, dy_, dxC, dyC;
    var t0 = A( dy_ = P_Arc_.y, dx_ = P_Arc_.x ),
        r_ = Math.sqrt( dx_*dx_ + dy_*dy_ ),
        t0C = A( dyC = yC_, dxC = xC_ ),
        rC = Math.sqrt( dxC*dxC + dyC*dyC ),
        nt = ~~(t*(r_>rC?r_:rC)+1);

    for( var i = iArc0; i <= nt; i++ ) {
        iArc0 = 1;
        ts[n] = iFrame*t2 + Math.PI + t*i/nt;
        addpt( xP_ + r_*C(t_ = t0+t*i/nt), yP_ + r_*S(t_), 0 );
        addpt( xP_ + rC*C(t_ = t0C+t*i/nt), yP_ + rC*S(t_), 1 );
        n++;
    }
}

function poly( x,y, n, r, t0, sColor ) {
    var Cx = e.getContext('2d');
    Cx.strokeStyle = sColor;
    Cx.beginPath();
    for( var i = 0; i < n; i++ )
        Cx[i ? 'lineTo' : 'moveTo']( x + r*C(t_ = t0+(1-2*i)*Math.PI/n), y + r*S(t_)*iCCW );

    Cx.closePath();
    Cx.stroke();
}

var t1b2 = Math.PI/n1,
    t1 = 2*t1b2,
    t2b2 = Math.PI/n2,
    t2 = 2*t2b2,
    s1 = 2*r1*S(t1b2),
    s2 = 2*r2*S(t2b2),
    xPivot = 0,
    iPivot = 0,
    iFrame = 0,
    P_Pivot, P_Arc,
    nFrame = 0;

while( nFrame <= (nr*2+1)*Math.PI/t2 ) {
    P_Pivot = poly1pt( 0 );
    P_Arc = poly1pt( iPivot );
    if( xPivot < s2/2 ) {
        P_Arc.x -= P_Pivot.x;
        P_Arc.y -= P_Pivot.y;
        arc1( P_Arc, xPivot, -r2*C(t2b2), -P_Pivot.x, -P_Pivot.y, t1 );
        iPivot = (iPivot+1) %n1;
        xPivot += s1;
    } else {
        P_Arc.x -= (P_Pivot.x - (xPivot - s2/2));
        P_Arc.y -= P_Pivot.y;
        arc1( P_Arc, s2/2, -r2*C(t2b2), -P_Pivot.x + xPivot - s2/2, -P_Pivot.y, t2 );
        xPivot -= s2;
        iFrame = (iFrame+1) %n2;
        nFrame++;
    }
}

function renderTo( eCanvas, t, isCCW, sWhat ) {
    iCCW = isCCW ? -1 : 1;
    var Cx = (e = eCanvas).getContext('2d');
    Cx.fillStyle = 'black';
    Cx.fillRect( 0,0, 600,600 );

    if( sWhat &1 )
        poly( 300,300, n2, r2, 0, 'white' );

    var tRef = A( ys[1][0], xs[1][0] ),
        tCum = 0,
        i0 = 0;

    Cx.strokeStyle = 'green';
    Cx.beginPath();
    Cx.moveTo( 300+xs[0][0], 300+ys[0][0]*iCCW );
    while( tCum < t ) {
        t_ = A( ys[1][i0+1], xs[1][i0+1] );
        tCum += t_ - tRef + (t_ - tRef < -1 ? 2*Math.PI : 0);
        tRef = t_;
        i0++;
        Cx.lineTo( 300+xs[0][i0], 300+ys[0][i0]*iCCW );
    }
    if( sWhat &2 )
        Cx.stroke();
    if( sWhat &4 )
        poly( 300+xs[1][i0], 300+ys[1][i0]*iCCW, n1, r1, ts[i0], 'red' );
}

return renderTo;
}

Można znaleźć skrzypce, aby zobaczyć rutynę w całej jej wielobocznej chwale (i aby zademonstrować animację)

http://jsfiddle.net/7rv751jy/2/embedded/result/

Skrypt definiuje wywoływaną funkcję, epiktóra akceptuje pięć wymienionych parametrów w PO. epizwraca funkcję z podpisem, (e,t,isCCW,flags)która akceptuje argumenty:

  • e - odniesienie do elementu płótna HTML5 o wymiarach 600 x 600, na którym ma być renderowany
  • t- całkowity kąt (w radianach), jaki środek ciężkości drugiego wielokąta powinien omiatać wokół środka ciężkości pierwszego. Podany argument nie powinien przekraczać 2 pi razy liczby przekazanych obrotów epi.
  • isCCW - wartość logiczna wskazująca, czy śledzenie powinno przebiegać w kierunku przeciwnym do ruchu wskazówek zegara (a nie w kierunku zgodnym z ruchem wskazówek zegara)
  • flags - zestaw flag bitowych wskazujących, które elementy powinny być renderowane
    • bit 1 - renderowanie wielokąta 1, jeśli jest ustawiony
    • bit 2 - renderuj ślad, jeśli jest ustawiony
    • bit 3 - renderowanie wielokąta 2, jeśli jest ustawiony

Funkcję można wywołać dowolną liczbę razy przy użyciu różnych zestawów argumentów.

Niektóre uwagi:

  • Procedura obsługuje zdegenerowane przypadki, w których n1 = 2i / lub n2 = 2. Podczas animacji niektóre kombinacje długości powodują nagłe szybkie postępy w śladzie. Wynika to z faktu, że ramki animacji są indeksowane pod kątem do środka ciężkości drugiego wielokąta, a środek masy theta poly2 / d theta staje się liczbą pojedynczą w przypadkach, gdy środek ciężkości dwustronnego poli 2 znajduje się w pobliżu wierzchołka dwustronnego poli 1 Nie wpływa to jednak na ślad.

  • Nazwy parametrów epibędą wydawać się mylące, ponieważ przez cały czas rozwoju nazwałem wielokąt 1 „2”, a wielokąt 2 „1”. Kiedy zdałem sobie sprawę z niespójności między moją konwencją a konwencją PO, zamiast zamiany wszystkich wskaźników w kodzie, po prostu zamieniłem kolejność argumentów w epi.

  • Skrzypce powyżej importuje jQuery, ale służy to do obsługi interfejsu użytkownika. epiFunkcja nie ma zależności bibliotecznych.

  • Kod obsługuje ślady CCW, po prostu odwracając oś Y. Jest to nieco nieeleganckie, ponieważ wielokąt 2 zaczyna się w pozycji odwróconej Y podczas śladów CCW, ale nikt nie powiedział, że rutyna musi być elegancka. ;)

COTO
źródło
1
Piękny! Odkryłem, że link do pełnego ekranu jest najłatwiejszy do pracy z: jsfiddle.net/7rv751jy/embedded/result
Calvin's Hobbies
Jednym drobnym zarzutem jest to, że wierzchołek znacznika nie zaczyna się na stacjonarnym wierzchołku.
Calvin's Hobbies
Ha. Całkowicie przeoczyłem to w specyfikacjach. Mówię „Ha”, ponieważ kod był pierwotnie (nieumyślnie) sprecyzowany, ale zmieniłem wierzchołek śledzenia, ponieważ doszedłem do wniosku, że ślad wyglądałby lepiej, gdyby zaczął się natychmiast. Zaktualizowałem kod, aby był zgodny ze specyfikacją, i zaktualizowałem link do skrzypce do wersji pełnoekranowej zgodnej ze specyfikacją. Jako bonus powala jedną postać z ogólnej liczby.
COTO
Jak mogę to trochę przyspieszyć? JS noob tutaj.
Soham Chowdhury
@SohamChowdhury: Zmień kod nt = ~~(t*(r_>rC?r_:rC)+1)na nt = ~~(t*(r_>rC?r_:rC)/10+1)i powinno to nieco przyspieszyć.
COTO