Epicyclogons zeichnen

22

Ein Epizykloid ist die Kurve, die ein Punkt auf einem Kreis macht, wenn er um einen anderen Kreis rollt. Ein Zyklogon ist die Form, die ein Punkt auf einem regulären Polygon beim Rollen über eine Ebene macht. Ein Epizyklogon ist die Kurve, die durch einen Punkt auf einem regelmäßigen Polygon beim Umlaufen eines anderen Polygons gezeichnet wird.

Schreiben Sie ein Programm , das eine epicyclogon zieht gegeben 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)

Anmerkungen

  • Wenn rnegativ, sollte die Walze gegen den Uhrzeigersinn drehen .
  • Denn reine Umdrehung tritt auf, wenn die Linie, die die Schwerpunkte der beiden Formen verbindet, um volle 360 ​​Grad überstrichen wird. Dieser Begriff wird erweitert, um alle Werte von einzuschließen r. (In einer viertel Umdrehung wird die Verbindungslinie zwischen den Zentroiden um 90 Grad überstrichen.)
  • Diese Argumente sollten von der Kommandozeile kommen oder von Ihrem Programm abgefragt werden (zB mit Python's input()).
  • r1und r2sind relativ zueinander, nicht die Abmessungen des Bildes. Sie können also eine "Einheit" als eine beliebige Anzahl von tatsächlichen Pixeln festlegen.

Der Punkt, den Sie ausfindig machen müssen, ist einer der Eckpunkte der rollenden Form. Die Formen müssen mit diesem Scheitelpunkt beginnen, der einen stationären Scheitelpunkt und zwei benachbarte Seiten berührt:

Epizyklogon Beispiel

Die genauen Startscheitelpunkte und der Winkel des stationären Polygons spielen keine Rolle.

Ausgabe

Die Ausgabe sollte auf ein Bild erfolgen, das mindestens 600 x 600 Pixel groß ist (oder eine variable Dimension, die auf 600 festgelegt werden kann). Es muss die gesamte Epizyklogon-Kurve anzeigen, die von den Parametern angegeben wird und im Bild gut eingerahmt ist.

Die rollenden und stationären Polygone müssen ebenfalls gezeichnet werden (mit der Walze im Endzustand). Die beiden Formen und das Epizyklogon sollten drei deutlich unterschiedliche Farben haben.

Es muss auch eine einfache Möglichkeit geben, die Polygone nicht zu zeichnen (eine Änderung von truezu falseim Code reicht aus).

Bitte zeigen Sie uns mindestens 2 Ausgabebilder. Es ist in Ordnung, sie bei Bedarf zu verkleinern.

Wertung

Der kürzeste Code, der gültige Ausgabebilder erzeugt, gewinnt.

Boni

  • Mindestens 50 Byte, wenn die Ausgabe ein animiertes GIF (oder ähnliches) der Kurve ist, die gezeichnet wird.
  • Mindestens 150 Byte, wenn Sie den Wert 2 zulassen n1und n2annehmen, damit die Formen zu Liniensegmenten mit der Länge 2 * r1(oder r2) werden, die sich "umeinander rollen". Wie Sie mit dem rZeitpunkt n1und dem Zeitpunkt n22 umgehen, bleibt Ihnen überlassen, da sich die Zentroiden nicht wie in anderen Fällen umeinander drehen. (Überhaupt nicht "rollen" zählt nicht als Umgang damit.)

Da ich sehr gespannt darauf bin, dass diese neue Idee gut umgesetzt wird (und es ist nicht gerade ein Kinderspiel), werde ich dem Gewinner 150 Bounty Reps verleihen . Der Wettbewerb endet am selben Tag, an dem das Kopfgeld aufgebraucht ist.

Das Kopfgeld wird dem Gewinner nicht gutgeschrieben, wenn klar ist, dass er den größten Teil des Codes von einem anderen Beitrag umgeschrieben hat.

Bibliotheksfunktionen, die dies bereits tun (sofern vorhanden), sind nicht zulässig.

Hinweis: Dies ergab sich aus meinen verbleibenden Fragen , die jeder posten kann. Aber wenn niemand anderes sie postet, habe ich gute Chancen, dass ich es rechtzeitig tun werde. : P

Calvins Hobbys
quelle
Ich denke, entgegen dem Uhrzeigersinn sollte stattdessen positiv sein.
Soham Chowdhury
3
@SohamChowdhury Ich denke, es spielt kaum eine Rolle.
Calvins Hobbys
Eigentlich hast du recht. Haben Sie Beispielbilder? Ich habe keinen CDF-Player.
Soham Chowdhury
@githubphagocyte Ich sehe deinen Standpunkt. Fest.
Calvins Hobbys
@ MartinBüttner Nicht zu streng, es war nur das erste, woran ich dachte. Sie können bei Bedarf auf andere Weise zur Eingabe von Werten auffordern.
Calvins Hobbys

Antworten:

3

MATLAB: 735 Bytes - 200 Bonus = 535

Mein Programm behandelt den Fall n = 2 und zeichnet eine Echtzeitanimation. Es gibt ein paar Unterschiede zwischen der Golf- und der Ungolf-Version:

Die ungolfed-Version hat nur die Option, die Animation in einer Datei 'g.gif' zu speichern, indem savegif = 1der Code eingestellt wird. Es ist standardmäßig deaktiviert, da es aus folgenden Gründen ärgerlich sein kann:

  • Erstellung einer unerwünschten Datei
  • Mögliche Verzögerung
  • Ein Fehler wird generiert, wenn Sie mehrere Monitore haben und das Plot-Fenster nicht auf dem richtigen ist ... Die GIF-Speicherung musste in der Golf-Version gelöscht werden, da sie ungefähr 100 Bytes benötigte und die Größe des Bonus überschritt.

Die ungolfed Version zeichnet einen Kreis auf den Tracer Vertex. Es produziert auch mehr Frames und bewegt sich schneller (obwohl dies in der Golfversion durch Ändern der Zahlen angepasst werden kann).

Proben:

f(11,5,90,2,99,0) nach Programmende

Golfprobe

epic(1.3,4,2,6,6,1) mit gif ausgabe

ungolfed sample

Ungolfed Code

%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

Golf Code

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

Anleitung:

Speichern Sie die Funktion in einer Datei mit dem gleichen Namen, dh epic.moder f.m. Führen Sie es aus, indem Sie die Funktion von der Matlab-Konsole aus aufrufen.

Verwendung: epic(r, r1, r2, n1, n2, dispPoly) Dabei dispPolyhandelt es sich um eine boolesche Variable (null, wenn sie falsch ist, eine Zahl ungleich null, wenn sie wahr ist), die bestimmt, ob die Polygone gezeichnet werden sollen.

Bearbeiten: Bonus von 50 für animiertes Bild hinzugefügt.

Feersum
quelle
14

Java - 2.726 2.634 - 200 = 2434 Zeichen

Verbessert von 3800 ish Bytes

Vielen Dank an alle für Ihre Vorschläge (insbesondere Pseudonym117), hier ist die neue Version.

Ich habe eine Klasse P hinzugefügt, die die Punktklasse ist, und eine Klasse L, die ArrayList erweitert

Ich habe auch einige kleine logische Änderungen hinzugefügt.

Hier ist die Hauptklasse (nicht 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);}
    }
}

Und die Golfversion:

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

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

Und ich:

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

Ändern Sie int d auf 0 oder 1, um Polygone anzuzeigen

Argumente - 1 100 50 5 2

Bildbeschreibung hier eingeben

Argumente - 1,5 100 100 7 3

Bildbeschreibung hier eingeben

Argumente - 2 40 100 3 7

Bildbeschreibung hier eingeben

Stretch Maniac
quelle
Ist rwirklich 50 in all deinen Beispielen? Das würde bedeuten, dass die Walze ungefähr 50 Mal läuft.
Calvins Hobbys
@ Calvin'sHobbies neues Beispiel zeigt pi * 3
Stretch Maniac
1
@StretchManiac Das kann nicht richtig sein. 3π sollte etwas mehr als 9-mal um das stationäre Polygon führen.
Martin Ender
4
Es ist lustig, wie der Klassenname RotatingPolygonsGolfedim "Golf" -Code steht, während er nur RotatingPolygonsim normalen Code steht. ;)
Calvins Hobbys
1
Sie können einen guten Teil der Zeichen speichern, indem Sie Ihre Importe ändern, um * anstelle von bestimmten Klassen zu verwenden ...
Pseudonym117
12

Javascript, 1284 Zeichen (-200 = 1084 Zeichen)

Der minimierte Code ist

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

Vollständiger Code ist

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

Eine Fiedel, die Routine in ihrer ganzen Vielschichtigkeit zu betrachten (und Animation zu demonstrieren), findet sich bei

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

Das Skript definiert eine aufgerufene Funktion epi, die die fünf im OP aufgelisteten Parameter akzeptiert. epiGibt eine Funktion mit der Signatur zurück, (e,t,isCCW,flags)die Argumente akzeptiert:

  • e - ein Verweis auf ein 600x600 HTML5 Canvas-Element, auf dem gerendert werden soll
  • t- der Gesamtwinkel (im Bogenmaß), den der Schwerpunkt des zweiten Polygons um den Schwerpunkt des ersten Polygons legen soll. Das übergebene Argument sollte das 2-fache der Anzahl der Umdrehungen nicht überschreiten, an die übergeben wurde epi.
  • isCCW - Boolescher Wert, der angibt, ob der Trace im Gegenuhrzeigersinn (im Gegensatz zu einem im Uhrzeigersinn) fortgesetzt werden soll.
  • flags - eine Reihe von Bit-Flags, die angeben, welche Elemente gerendert werden sollen
    • Bit 1 - Polygon 1 rendern, falls gesetzt
    • Bit 2 - Trace rendern, falls gesetzt
    • Bit 3 - Polygon 2 rendern, falls gesetzt

Die Funktion kann beliebig oft mit unterschiedlichen Argumenten aufgerufen werden.

Einige Notizen:

  • Die Routine behandelt die entarteten Fälle wo n1 = 2und / oder n2 = 2. Beim Animieren verursachen bestimmte Längenkombinationen plötzliche schnelle Fortschritte in der Spur. Dies liegt daran, dass die Animationsframes durch den Winkel zum Schwerpunkt des zweiten Polygons indiziert werden und der Schwerpunkt von d theta poly2 / d theta singulär wird, wenn sich der Schwerpunkt von 2-seitigem poly2 in der Nähe eines Scheitelpunkts von 2-seitigem poly1 befindet Dies hat jedoch keine Auswirkungen auf die Ablaufverfolgung.

  • Die Parameternamen in epiwerden verwirrend erscheinen, da ich während der gesamten Entwicklung Polygon 1 als "2" und Polygon 2 als "1" bezeichnet habe. Als ich die Inkonsistenz zwischen meiner Konvention und der des OP erkannte, anstatt alle Indizes im Code zu vertauschen, vertauschte ich einfach die Reihenfolge der Argumente in epi.

  • Die obige Fiedel importiert jQuery, aber dies dient dazu, die Benutzeroberfläche zu handhaben. Die epiFunktion hat keine Bibliotheksabhängigkeiten.

  • Der Code verarbeitet CCW-Spuren durch einfaches Umkehren der Y-Achse. Dies ist etwas unelegant, da Polygon 2 während der CCW-Kurven in einer Y-invertierten Position startet, aber niemand sagte, die Routine müsse elegant sein. ;)

COTO
quelle
1
Schön! Ich habe festgestellt, dass der Vollbild-Link am einfachsten zu handhaben ist: jsfiddle.net/7rv751jy/embedded/result
Calvins Hobbys
Eine winzige Beschwerde ist, dass der Tracer-Vertex nicht auf einem stationären Vertex beginnt.
Calvins Hobbys
Ha. Ich habe das in den technischen Daten völlig übersehen. Ich sage 'Ha', weil der Code ursprünglich (versehentlich) der Spezifikation entsprach, aber ich habe den Trace-Vertex geändert, weil ich dachte, der Trace würde besser aussehen, wenn er sofort gestartet würde. Ich habe den Code so aktualisiert, dass er den Spezifikationen entspricht, und den Link zur Geige auf eine Vollbild-Version aktualisiert, die den Spezifikationen entspricht. Als Bonus wird ein Charakter von der Gesamtzahl gestrichen.
COTO
Wie kann ich es etwas beschleunigen? JS noob hier.
Soham Chowdhury
@SohamChowdhury: Ändere den Code nt = ~~(t*(r_>rC?r_:rC)+1)auf nt = ~~(t*(r_>rC?r_:rC)/10+1)und es sollte die Sache etwas beschleunigen.
COTO