Menggambar Epicyclogons

22

Sebuah epicycloid adalah kurva titik pada lingkaran membuat seperti gulungan sekitar lingkaran lain. Sebuah cyclogon adalah bentuk titik pada poligon beraturan membuat seperti gulungan di pesawat. Sebuah epicyclogon adalah kurva dilacak oleh titik pada satu poligon beraturan seperti gulungan sekitar lain.

Menulis sebuah program yang menarik sebuah epicyclogon diberikan 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)

Catatan

  • Bila rnegatif, roller harus berlawanan arah jarum jam .
  • Sebab r, satu revolusi terjadi ketika garis yang menghubungkan centroid dari dua bentuk menyapu 360 derajat penuh. Gagasan ini diperluas untuk mencakup semua nilai r. (Jadi dalam seperempat revolusi garis yang menghubungkan centroid menyapu 90 derajat.)
  • Argumen ini harus berasal dari baris perintah atau program Anda harus meminta mereka (misalnya dengan Python input()).
  • r1dan r2relatif satu sama lain, bukan dimensi gambar. Jadi, Anda dapat mengatur satu "unit" menjadi sejumlah piksel aktual.

Poin yang harus Anda lacak adalah salah satu simpul dari bentuk penggulungan. Bentuk harus dimulai dengan titik ini menyentuh titik diam dan dua sisi yang berdekatan:

contoh epicyclogon

Verteks awal yang tepat dan sudut poligon stasioner tidak masalah.

Keluaran

Output harus pergi ke gambar yang setidaknya 600x600 piksel (atau beberapa dimensi variabel daripada yang dapat diatur ke 600). Itu harus menunjukkan seluruh kurva epicyclogon yang ditentukan oleh parameter, dibingkai dengan baik dalam gambar.

Poligon bergulir dan stasioner juga harus digambar (dengan roller dalam keadaan akhir itu). Dua bentuk dan epicyclogon haruslah tiga warna yang sangat berbeda.

Harus juga ada cara sederhana untuk tidak menggambar poligon (perubahan trueke falsedalam kode sudah mencukupi).

Tolong tunjukkan kepada kami setidaknya 2 gambar output. Tidak masalah untuk mengecilkannya jika perlu.

Mencetak gol

Kode terpendek yang menghasilkan gambar output yang valid menang.

Bonus

  • Mengurangi 50 byte jika outputnya berupa animasi gif (atau serupa) dari kurva yang sedang digambar.
  • Minus 150 byte jika Anda membiarkan n1dan n2mengambil nilai 2 sehingga bentuk menjadi segmen garis panjang 2 * r1(atau r2), "berguling" di sekitar satu sama lain. Bagaimana Anda menangani rkapan n1dan n22 tergantung Anda karena centroid tidak berputar satu sama lain seperti yang mereka lakukan dalam kasus lain. (Tidak "menggulung" sama sekali tidak termasuk penanganannya.)

Karena saya sangat ingin melihat ide novel ini dieksekusi dengan baik (dan itu bukan cakewalk), saya akan memberikan 150 perwakilan hadiah kepada pemenang. Kontes akan berakhir pada hari yang sama ketika hadiah habis.

Hadiah tidak akan diberikan kepada pemenang jika jelas bahwa mereka hanya menulis ulang sebagian besar kode dari pengajuan lain.

Fungsi perpustakaan yang sudah melakukan ini (jika ada) tidak diizinkan.

Catatan: Ini datang dari pertanyaan sisa saya yang bebas dikirim oleh siapa pun. Tetapi jika tidak ada orang lain yang mempostingnya, ada kemungkinan saya akan tepat waktu. : P

Hobi Calvin
sumber
Saya pikir sebaliknya harus positif.
Soham Chowdhury
3
@SohamChowdhury Saya pikir itu tidak masalah.
Calvin Hobbies
Kamu benar, sebenarnya. Apakah Anda memiliki contoh gambar? Saya tidak memiliki pemutar CDF.
Soham Chowdhury
@githubphagocyte Saya mengerti maksud Anda. Tetap.
Calvin Hobbies
@ MartinBüttner Tidak terlalu ketat, itu hanya hal pertama yang saya pikirkan. Anda bisa meminta nilai dengan cara lain jika perlu.
Calvin Hobi

Jawaban:

3

MATLAB: 735 byte - 200 bonus = 535

Program saya menangani case n = 2 dan menggambar animasi real-time. Ada beberapa perbedaan antara versi golf dan ungolfed:

Versi ungolfed hanya memiliki opsi untuk menyimpan animasi ke file 'g.gif', dengan menetapkan savegif = 1kode. Secara default tidak aktif karena dapat mengganggu karena beberapa alasan:

  • Pembuatan file yang tidak diinginkan
  • Kemungkinan jeda
  • Galat dihasilkan jika Anda memiliki beberapa monitor dan jendela plot tidak di sebelah kanan ... Penghematan gif harus dijatuhkan dalam versi golf karena memerlukan waktu sekitar 100 byte, melebihi ukuran bonus.

Versi ungolfed menggambar lingkaran pada vertebra pelacak. Ini juga menghasilkan lebih banyak bingkai dan bergerak lebih cepat (meskipun ini dapat disesuaikan dalam versi golf dengan mengubah angka).

Sampel:

f(11,5,90,2,99,0) setelah penghentian program

sampel golf

epic(1.3,4,2,6,6,1) dengan output gif

sampel yang tidak serigala

Kode tidak dikunci

%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

Kode golf

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

Instruksi:

Simpan fungsi ke file dengan nama yang sama, yaitu epic.matau f.m. Jalankan dengan memanggil fungsi dari konsol Matlab.

Penggunaan: di epic(r, r1, r2, n1, n2, dispPoly) mana dispPolyvariabel Boolean (nol jika salah, angka bukan nol jika benar) menentukan apakah akan menggambar poligon.

Sunting: Menambahkan bonus 50 untuk gambar animasi.

feersum
sumber
14

Java - 2.726 2.634 - 200 = 2434 karakter

Ditingkatkan dari 3800 byte ish

Terima kasih untuk semua saran Anda (terutama pseudonim117), ini adalah versi baru.

Saya menambahkan kelas P yang merupakan kelas titik dan kelas L yang memperluas ArrayList

Saya juga menambahkan beberapa perubahan logika kecil.

Ini kelas utamanya (bukan golf):

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

Dan versi golfnya:

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

Serta kelas 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;}}

Dan saya:

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

Ubah int d ke 0 atau 1 untuk menampilkan poligon

argumen - 1 100 50 5 2

masukkan deskripsi gambar di sini

args - 1.5 100 100 7 3

masukkan deskripsi gambar di sini

args - 2 40 100 3 7

masukkan deskripsi gambar di sini

Regangkan Maniac
sumber
Apakah rbenar-benar 50 dalam semua contoh Anda? Itu berarti roller berjalan sekitar 50 kali.
Calvin Hobi
@ Calvin'sHobbies contoh baru menunjukkan pi * 3
Stretch Maniac
1
@ StretchManiac Itu tidak benar. 3π akan membawa Anda sedikit lebih dari 9 kali di sekitar poligon stasioner.
Martin Ender
4
Sangat lucu bagaimana nama kelas RotatingPolygonsGolfeddalam kode "golf" sementara itu hanya RotatingPolygonsdalam kode normal. ;)
Hobi Calvin
1
Anda dapat menyimpan banyak karakter hanya dengan mengubah impor Anda untuk menggunakan * alih-alih kelas tertentu ...
pseudonym117
12

Javascript, 1284 karakter (-200 = 1084 karakter)

Kode yang diperkecil adalah

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

Kode lengkapnya adalah

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

Sebuah biola untuk dilihat rutin dalam semua kemuliaan poligoni (dan untuk menunjukkan animasi) ditemukan di

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

Script mendefinisikan fungsi yang disebut epiyang menerima lima parameter yang tercantum dalam OP. epimengembalikan fungsi dengan tanda tangan (e,t,isCCW,flags)yang menerima argumen:

  • e - referensi ke elemen kanvas 600x600 HTML5 yang akan di-render
  • t- sudut total (dalam radian) bahwa centroid dari poligon kedua harus menyapu sekitar centroid yang pertama. Argumen yang diteruskan tidak boleh melebihi 2 pi kali jumlah rotasi yang dilewati epi.
  • isCCW - boolean yang menunjukkan apakah jejak harus dilanjutkan dengan arah berlawanan arah jarum jam (berlawanan dengan arah jarum jam)
  • flags - satu set flag bit yang menunjukkan elemen mana yang harus dirender
    • bit 1 - render poligon 1 jika disetel
    • bit 2 - render jejak jika disetel
    • bit 3 - render poligon 2 jika diatur

Fungsi ini dapat dipanggil berapa kali dengan berbagai set argumen.

Beberapa Catatan:

  • Rutin menangani kasus yang merosot di mana n1 = 2dan / atau n2 = 2. Saat menjiwai, kombinasi panjang tertentu akan menyebabkan kemajuan cepat yang mendadak dalam jejak. Ini karena frame animasi diindeks oleh sudut ke centroid dari poligon kedua, dan d theta poly2 / d theta centroid menjadi singular dalam kasus-kasus di mana centroid dari 2-sided poly 2 berada di dekat sebuah simpul 2-sided poly 1 Namun, ini tidak mempengaruhi jejak.

  • Nama-nama parameter dalam epiakan tampak membingungkan karena selama pengembangan, saya menyebut poligon 1 sebagai "2", dan poligon 2 sebagai "1". Ketika saya menyadari ketidakkonsistenan antara konvensi saya dan OP, daripada menukar semua indeks dalam kode, saya hanya bertukar urutan argumen di epi.

  • Biola di atas mengimpor jQuery, tetapi ini untuk menangani UI. The epiFungsi tidak memiliki dependensi perpustakaan.

  • Kode ini menangani jejak CCW hanya dengan membalik sumbu Y. Ini agak tidak tepat karena poligon 2 dimulai pada posisi terbalik-Y selama jejak CCW, tetapi tidak ada yang mengatakan rutinitas harus elegan. ;)

COTO
sumber
1
Indah! Saya menemukan tautan layar penuh paling mudah digunakan: jsfiddle.net/7rv751jy/embedded/result
Calvin Hobbies
Satu keluhan kecil adalah bahwa pelacak titik tidak memulai pada titik diam.
Calvin Hobbies
Ha. Saya benar-benar mengabaikan hal itu dalam spesifikasi. Saya katakan 'Ha' karena kode awalnya (tidak sengaja) untuk spec, tapi saya mengubah jejak vertex karena saya pikir jejak akan terlihat lebih baik jika dimulai segera. Saya telah memperbarui kode agar sesuai dengan spesifikasi, dan memperbarui tautan ke biola ke versi yang memenuhi spesifikasi layar penuh. Sebagai bonus, itu mengetuk satu karakter dari jumlah total.
COTO
Bagaimana saya bisa mempercepatnya sedikit? JS noob di sini.
Soham Chowdhury
@SohamChowdhury: Perubahan kode nt = ~~(t*(r_>rC?r_:rC)+1)untuk nt = ~~(t*(r_>rC?r_:rC)/10+1)dan harus mempercepat hal-hal sedikit.
COTO