Zincir Dişli Bilimi: Zincir Tahrik Sistemini Animasyon


96

Bu meydan okumanın amacı bir animasyonunu üretmektir zincir tahrik kümesi oluşan sistemin, zincir dişlisi dişli bir araya bağlanmış zincirin .

Genel Gereksinimler

Programınıza üçlü olarak belirtilen zincir dişlileri listesi verilecektir (x, y, radius). Elde edilen zincir tahrik sistemi, bir ile birbirine bağlanır, bu zincir dişlisi, oluşan kapalı gergin zinciri , her biri üzerinden geçen amacıyla . Amacınız sistemi hareket halinde gösteren, sonsuz döngülü bir animasyon üretmektir . Örneğin, giriş verilen

(0, 0, 16),  (100, 0, 16),  (100, 100, 12),  (50, 50, 24),  (0, 100, 12)

, çıktı gibi bir şey olmalı

örnek 1.

Koordinat sistemi doğru x-ekseni noktaları ve y-ekseni yukarı işaret eden şekilde olması gerekmektedir. Yarıçapların 8'e eşit veya ondan daha büyük sayılar olduğunu varsayabilirsiniz (bunun neden daha sonra önemli olduğunu göreceğiz.) Ayrıca, en az iki dişlinin olduğunu ve dişlilerin birbiriyle kesişmediğini varsayabilirsiniz . birimlerGirişin çok kritik değil. Bu gönderideki tüm örnekler ve test senaryoları, giriş birimleri olarak pikselleri kullanır (bu nedenle, önceki şekildeki orta zincir dişlisinin yarıçapı 24 pikseldir;), bu birimlerden çok fazla sapma yapmamaya çalışın. Zorluğun geri kalanında, mekansal büyüklüklerin girdiyle aynı birimlerde verildiği anlaşılıyor - oranları doğru tutmaya dikkat edin! Çıkış boyutları tüm sistemin görünür olması için yeterince büyük, bütün dişlilerinin sınırlayıcı kutusundan biraz daha büyük olmalıdır. Özellikle, dişlilerin mutlak konumları çıkışı etkilememelidir; sadece onların göreceli konumları olmalıdır (bu nedenle, örneğin yukarıdaki örnekteki tüm dişlileri aynı miktarda kaydırırsak, çıktı aynı kalır.)

Zincir , tüm temas noktalarında geçtiği zincir dişlisine teğet olmalı ve her yerde düz olmalıdır . Zincir, bitişik zincir bölümlerinin (yani, iki zincir dişlisi arasındaki zincir dişlisinin aynı zincir dişlisi ile aynı olan kısımları) birbiriyle kesişmeyeceği şekilde zincir dişlilerinden geçmelidir .

Zincir Kavşağı.

Örneğin, yukarıdaki soldaki sistem geçerliyken, ortadaki değil, çünkü sol alt zincir dişlisinin üzerinden geçen iki bitişik zincir parçası kesişiyor. Bununla birlikte, doğru sistem dikkat olan iki kesişen zincir parçaları bitişik değildir, çünkü geçerli (bu sistem de, diğer iki daha farklı bir giriş tarafından üretilir.)

İşleri basitleştirmek için (r), hiçbir dişlinin , iki komşu dişlisinin dışbükey kabuğunu ya da her bir komşusunun ve diğer komşusunun dışbükey kabuğunu kesişmediğini varsayabilirsiniz . Başka bir deyişle, aşağıdaki diyagramdaki üst zincir dişlisi gölgeli bölgelerin hiçbiriyle kesişmeyebilir.

hariç tutma

Zincir parçaları, geçtiklerinden başka zincir dişlileriyle kesişebilir (örneğin son test durumundaki gibi). Bu durumda, zincir her zaman dişlilerin önünde görünmelidir.

Görsel Gereksinimler

Zincir , değişken genişlikte bir dizi bağlantıdan oluşmalıdır . Dar bağlantının genişliği yaklaşık 2 olmalı ve geniş bağlantının genişliği yaklaşık 5 olmalıdır . Her iki bağlantı türünün uzunluğu yaklaşık olarak eşit olmalıdır. dönemyani, geniş / dar bir çift bağlantı halkasının toplam uzunluğu, zincirin uzunluğuna tam sayıya uyan 4π'ye en yakın sayı olmalıdır. Örneğin, zincirin uzunluğu 1.000 ise, periyodu 12.5 olmalıdır, bu da 1000'de tam sayı sayısına (80) uyan 4π (12.566 ...) en yakın sayıdır. Dönemin zincir uzunluğuna tam sayıya uyması önemlidir, böylece zincirin etrafına sarıldığı noktada herhangi bir eser kalmaz.

Zincir


Bir yarıçap R dişlisi üç eş merkezli parçadan oluşmalıdır: merkezi bir aks , yaklaşık 3 yarıçapı bir daire olmalıdır; Sprocket'ın vücut yaklaşık yarıçaplı bir daire olmalıdır milinin etrafında R - 4.5; ve dişli çarkın gövdesi etrafında,
R - 1.5 civarında bir yarıçap dairesi olması gerekir . Jant ayrıca, dişlisinin yaklaşık 4 genişliğinde olmalıdır; Dişlerin ebatları ve aralıkları, zincir bağlantılarının ebatlarına uygun olmalıdır, böylece düzgün bir şekilde birleşirler.

zincir dişlisi

Zincir dişlilerinin dişlerinin süresi, yani zincir dişlisinin çevresi boyunca iki ardışık diş arasındaki mesafe, zincir periyoduyla eşleşmelidir. Süre yaklaşık 4π olduğundan ve zincir dişlisinin yarıçapının eşit olması garanti edildiğinden, zincir dişlisinin çevresine neredeyse tam sayıdaki bir sayıya uyması gerekir; Zincir dişlilerinin dişleri etrafından dolanır.

Zincir, zincir dişlisinin farklı parçaları ve arka plan için kolayca ayırt edilebildikleri sürece herhangi bir renk kombinasyonunu kullanabilirsiniz . Arkaplan şeffaf olabilir. Bu gönderideki örnekler zincir, zincir dişlisi aksı ve kenarı için ve zincir dişlisi gövdesi için kullanılır.Zincir rengi #202020Dişli Aks ve Jant Rengi #868481Zincir Dişlisi Gövde Rengi #646361

Animasyon Gereksinimleri

Birinci dişli dönmelidir giriş listesinde saat yönünde ; zincir dişlisinin geri kalanı buna göre dönmelidir. Zincir, saniyede yaklaşık 16π (yaklaşık 50) birim hızında hareket etmelidir ; kare hızı size kalmış, ancak animasyon yeterince yumuşak görünmelidir.

Animasyon sorunsuz döngü yapmalı .

Uygunluk

Bazı görsel nitelikler ve oranlar kasıtlı olarak yalnızca kabaca belirtilmiştir; bu özelliklerle tam olarak eşleşmeniz gerekmez . Programınızın çıktısı burada verilen örneklerin piksel piksel boyutunda bir kopyası olmak zorunda değildir, ancak benzer görünmesi gerekir. Özellikle, zincirin ve dişlilerin tam oranları ve zincir bağlantılarının ve dişlilerinin dişlerinin tam şekli esnektir.

İzlenecek en önemli noktalar şunlardır:

  • Zincir dişlilerinin üzerinden, giriş sırasına göre doğru yönden geçmelidir.
  • Zincir, tüm temas noktalarındaki dişlilere teğet olmalıdır.
  • Zincirin bağlantıları ve dişlilerin dişleri, en azından doğru mesafeyi ve fazı düzeltmek için düzgünce bağlanmalıdır.
  • Zincirin bağlantıları ve dişlilerin dişleri arasındaki boşluk, etraflarında dolandıkları noktada farkedilebilecek herhangi bir artefakt bulunmayacak şekilde olmalıdır.
  • Zincir dişlileri doğru yönde dönmelidir.
  • Animasyon sorunsuz döngü yapmalı.

Son bir not olarak, teknik olarak, bu zorluğun amacı, yaratıcı olmak ve daha ayrıntılı bir çıktı üretmek istiyorsanız, elbette, bunun için gitmeyi düşünüyorsanız, en kısa kodu yazmaktır!

Meydan okuma

Bir zincir listesi alarak ve yukarıda açıklandığı gibi ilgili zincir tahrik sistemi animasyonu üreten bir program veya fonksiyon yazın .

Giriş ve çıkış

Sen alabilir girişi yoluyla komut satırı aracılığıyla, STDIN olarak, fonksiyon argümanları veya kullanan eşdeğer bir yöntem . Girdi için uygun herhangi bir format kullanabilirsiniz , ancak gönderiminizde belirttiğinizden emin olun.

Gibi çıktı , belgeyi doğrudan animasyonu görüntülemek , bir üretirler animasyon dosyası (örneğin, animasyonlu GIF) veya üretmek çerçeve dosyalarının dizisini (ancak, bu durumda küçük bir ceza yoktur;. Aşağıya bakınız) dosya çıktısı kullanırsanız, kare sayısının makul olduğundan emin olun (bu gönderideki örnekler çok az kare kullanır;) kare sayısının minimum olması gerekmez, ancak çok fazla gereksiz kare üretmemelisiniz. Bir kare dizisi çıkarırsanız , gönderiminizde kare hızını belirttiğinizden emin olun .

Puan

Bu kod golfü . En kısa cevap bayt cinsinden kazanır.

+% 10 Ceza   Programınız çıktı olarak bir dizi animasyon üretiyorsa, animasyonu doğrudan görüntülemek veya tek bir animasyon dosyası oluşturmak yerine puanınıza% 10 ekleyin.

Test Kılıfları

Test 1

(0, 0, 26),  (120, 0, 26)

Test 1

Test 2

(100, 100, 60),  (220, 100, 14)

Test 2

Test 3

(100, 100, 16),  (100, 0, 24),  (0, 100, 24),  (0, 0, 16)

Test 3

Test 4

(0, 0, 60),  (44, 140, 16),  (-204, 140, 16),  (-160, 0, 60),  (-112, 188, 12),
(-190, 300, 30),  (30, 300, 30),  (-48, 188, 12)

Test 4

5. Test

(0, 128, 14),  (46.17, 63.55, 10),  (121.74, 39.55, 14),  (74.71, -24.28, 10),
(75.24, -103.55, 14),  (0, -78.56, 10),  (-75.24, -103.55, 14),  (-74.71, -24.28, 10),
(-121.74, 39.55, 14),  (-46.17, 63.55, 10)

5. Test

Test 6

(367, 151, 12),  (210, 75, 36),  (57, 286, 38),  (14, 181, 32),  (91, 124, 18),
(298, 366, 38),  (141, 3, 52),  (80, 179, 26),  (313, 32, 26),  (146, 280, 10),
(126, 253, 8),  (220, 184, 24),  (135, 332, 8),  (365, 296, 50),  (248, 217, 8),
(218, 392, 30)

Test 6



İyi eğlenceler!


38
Bu gifler çok tatmin edici +1
Adnan

24
Herhangi biri bu kodla başarılı bir şekilde yanıt verirse etkileneceğim.
DavidC

5
Gifleri nasıl yaptın? Ve bu ne zamandır işlerde?
J Atkin

10
@JAtkin Herkesin yapması gereken gibi: Bir çözüm yazdım :) Özellikleri soruyorsanız, bireysel kareler için Kahire'yi kullandım, sonra da gifleri oluşturmak için ImageMagick'i kullandım (BTW, eğer biri bu animasyonu üretmek isterse) yani, önce kareleri oluşturarak ve sonra onları animasyona dönüştürmek için harici bir araç kullanarak , yazıdaki araca bağımlılığı belirlediğiniz sürece, bununla tamamen iyiyim . Kullanıcıyı değil, aracı çağırması gereken program .)
Ell

5
@Anko İyi haber şu ki, bu konuda endişelenmenize gerek yok: bu durum girdide gerçekleşmeyeceğinin garantisidir; "hiçbir dişli dışbükey gövdesiyle kesişmez ..." kısmına bakın, görüntü üç gölgeli bölgelere sahip. Daha genel olarak, zincir, dişlilerin sırasına göre, bir zincir dişlisinin yanından birden fazla kez geçmiş gibi görünse bile, her bir tekerleği bir kez geçer.
Ell

Yanıtlar:


42

JavaScript (ES6), 2557 1915 1897 1681 bayt

Bu gerçekten süper sıkıcı değil golf ; küçültülmüş - kısmen elle - ama bu özel bir şey değil. Küçültmeden önce daha fazla golf oynadıysam, daha kısa olacağından şüphem yok, ama zaten bunun için yeterince zaman harcadım.

Düzenleme: Tamam, bu yüzden daha fazla zaman harcadım ve küçültmeden önce kodu daha fazla golf oynadım (bu sefer çok manuel olarak). Kod hala aynı yaklaşımı ve genel yapıyı kullanıyor, ama yine de 642 bayttan tasarruf ettim. Çok perişan değil, eğer kendim söylersem. Muhtemelen bazı bayt tasarruf fırsatlarını kaçırdım, fakat bu noktada nasıl çalıştığından bile emin değilim. Çıktı açısından farklı olan tek şey, şimdi daha kısaca yazılabilecek biraz farklı renkler kullanmasıdır.

Düzenleme 2 (daha sonra): 18 bayt kaydedildi. ConorO'Brien'e teşekkürler, tamamen özlediğimden açıkça görüldüğünü açıkça belirtti.

Düzenleme 3: Yani, kendi kodumu tersine çevireceğimi düşündüm, çünkü açıkçası, nasıl yaptığımı hatırlayamadım ve asılsız versiyonları kaybettim. Ben de gittim ve bakıyorum ve yeniden yapılandırıp biraz mikro-golf yaparak tasarruf için 316 bayt buldum.

R=g=>{with(Math){V=(x,y,o)=>o={x,y,l:sqrt(x*x+y*y),a:v=>V(x+v.x,y+v.y),s:v=>o.a(v.m(-1)),m:f=>V(x*f,y*f),t:r=>V(x*cos(r)-y*sin(r),x*sin(r)+y*cos(r)),c:v=>x*v.y-y*v.x,toString:_=>x+','+y};a='appendChild',b='setAttribute';S=(e,a)=>Object.keys(a).map(n=>e[b](n,a[n]))&&e;T=(t,a)=>S(k.createElementNS('http://www.w3.org/2000/svg',t),a);C=(e,a)=>S(e.cloneNode(),a);P=a=>T('path',(a.fill='none',a));w=h=-(x=y=1/0);G=g.map((a,g)=>(g=V(...a))&&(u=(g.r=a[2])+5,x=min(x,g.x-u),y=min(y,g.y-u),w=max(w,g.x+u),h=max(h,g.y+u))&&g);k=document;I=k[a].bind(k.body[a](T('svg',{width:w-x,height:h-y}))[a](T('g',{transform:`translate(${-x},${h})scale(1,-1)`})));L=(c)=>(h=G.length)&&G.map((g,i)=>c(G[i],G[i?i-1:h-1],G[(i+1)%h]))&&L;l='';L((g,p,n)=>g.f=p.s(g).c(n.s(g))>0)((g,a,n)=>{d=g.s(n),y=x=1/d.l;g.f!=n.f?(a=asin((g.r+n.r)*x),g.f?(x=-x,a=-a):(y=-y)):(a=asin((g.r-n.r)*x),g.f&&(x=y=-x,a=-a));t=d.t(a+PI/2);g.o=t.m(x*g.r).a(g);n.i=t.m(y*n.r).a(n)})((g,p,n)=>{z='#888';d=(l,s,e)=>`A${g.r},${g.r} 0 ${1*l},${1*s} ${e}`;e=(f,r)=>T('circle',{cx:g.x,cy:g.y,r,fill:f});g.k=p.o.s(n.i).l<g.i.s(g.o).l;w=d(g.k,!g.f,g.o);g.j=`${w}L${n.i}`;l+=g.j;I(e(z,g.r-1.5));g.g=I(P({d:`M${g.i}${w}${d(!g.k,!g.f,g.i)}`,stroke:z,'stroke-width':5}));g.h=I(C(g.g,{d:`M${g.i}${g.j}`,stroke:'#222'}));I(e('#666',g.r-4.5));I(e(z,3))});t=e=>e.getTotalLength(),u='stroke-dasharray',v='stroke-dashoffset',f=G[0];l=I(C(f.h,{d:'M'+f.i+l,'stroke-width':2}));s=f.w=t(l)/round(t(l)/(4*PI))/2;X=8*s;Y=f.v=0;L((g,p)=>{g.g[b](u,s);g.h[b](u,s);g==f||(g.w=p.w+t(p.h),g.v=p.v+t(p.h));g.g[b](v,g.w);g.h[b](v,g.v);g.h[a](C(g.g[a](T('animate',{attributeName:v,from:g.w+X,to:g.w+Y,repeatCount:'indefinite',dur:'1s'})),{from:g.v+X,to:g.v+Y}))})}}

Yukarıdaki işlev, belgeye bir SVG öğesi (animasyonlar dahil) ekler. 2. test durumunu görüntülemek için:

R([[100, 100, 60],  [220, 100, 14]]);

Bir tedavi gibi görünüyor - en azından burada Chrome'da.

Aşağıdaki pasajda deneyin (düğmelere tıklamak, OP'nin test durumlarının her birini çizecektir).

Kod, zinciri ve dişli dişlerini kesikli darbeler olarak çizer. Daha sonra niteliği animatecanlandırmak için öğeler kullanır stroke-dashoffset. Sonuçta ortaya çıkan SVG elemanı kendi kendine yeterlidir; JS güdümlü animasyon veya CSS stil yoktur.

İşlerin iyi bir şekilde sıralanmasını sağlamak için, her dişlinin diş halkası aslında iki yaydan oluşan bir yol olarak çizilir, böylece yol tam zincirin temas ettiği teğet noktasından başlayabilir. Bu, hizalamak için çok daha kolay hale getirir.

Ayrıca, SVG'nin kesikli vuruşlarını kullanırken çok fazla yuvarlama hatası var gibi görünüyor. En azından ben öyle gördüm; zincir ne kadar uzun olursa, arka arkaya gelen her dişliyle o kadar kötü çalışır. Böylece sorunu en aza indirmek için, zincir aslında birkaç yoldan oluşur. Her yol bir dişli etrafındaki yaylı bir bölüm ve bir sonraki vitese giden düz bir hattan oluşur. İlk ofsetleri eşleşecek şekilde hesaplanır. Bununla birlikte, zincirin ince "iç" kısmı, animasyon olmadığı için sadece tek bir döngü yoludur.


2
Harika görünüyor! Eski (ish) mücadelesine cevap verdiğin için teşekkürler!
Ell

1
-2 bayt:R=g=>...
Conor O'Brien,

1
@Flambino, bu zorluk için çözümünüzü sevdim ve orijinal kaynağını kaybettiniz, gerçekten kurtarmak için bazı ters mühendislik çalışmaları yaptım, burada bulunabilir: gist.github.com/micnic/6aec085d63320229a778c6775ec7f9aa ayrıca küçültdüm manuel olarak 1665 bayta kadar (küçültülebilir, ancak bugün tembelim)
micnic

1
@micnic Teşekkürler! Bunu kontrol etmem gerekecek! Merak etme, ben de tersine çevirmeyi başardım, bu yüzden daha okunaklı bir sürümüm var. Ama, smaç, 16 bayt daha az? Kudos! Zaman bulabildiğim zaman kesinlikle bir göz
atacağım

1
@Flambino, dosya boyutu üzerinde esasen en büyük etki svg yapısıydı, ben bir şey koymadım, <g>doğrudan svg kök dizinine koydum. Ayrıca kullandığınız numaraya boole gelen süpürme bayrağı ve büyük yay bayrağını dönüştürülmüş bir yer bulduk 1*x, ancak kullanabilirsiniz+x
micnic

40

C # 3566 bayt

Hiç golf oynamıyorum ama çalışıyor (sanırım)

Düzenleme tarihinde Ungolfed.

GIF işlemek için Magick.NET kullanır.

class S{public float x,y,r;public bool c;public double i,o,a=0,l=0;public S(float X,float Y,float R){x=X;y=Y;r=R;}}class P{List<S>q=new List<S>();float x=float.MaxValue,X=float.MinValue,y=float.MaxValue,Y=float.MinValue,z=0,Z=0,N;int w=0,h=0;Color c=Color.FromArgb(32,32,32);Pen p,o;Brush b,n,m;List<PointF>C;double l;void F(float[][]s){p=new Pen(c,2);o=new Pen(c,5);b=new SolidBrush(c);n=new SolidBrush(Color.FromArgb(134,132,129));m=new SolidBrush(Color.FromArgb(100,99,97));for(int i=0;i<s.Length;i++){float[]S=s[i];q.Add(new S(S[0],S[1],S[2]));if(S[0]-S[2]<x)x=S[0]-S[2];if(S[1]-S[2]<y)y=S[1]-S[2];if(S[0]+S[2]>X)X=S[0]+S[2];if(S[1]+S[2]>Y)Y=S[1]+S[2];}q[0].c=true;z=-x+16;Z=-y+16;w=(int)(X-x+32);h=(int)(Y-y+32);for(int i=0;i<=q.Count;i++)H(q[i%q.Count],q[(i+1)%q.Count],q[(i+2)%q.Count]);C=new List<PointF>();for(int i=0;i<q.Count;i++){S g=q[i],k=q[(i+1)%q.Count];if(g.c)for(double a=g.i;a<g.i+D(g.o,g.i);a+=Math.PI/(2*g.r)){C.Add(new PointF((float)(g.x+z+g.r*Math.Cos(a)),(float)(g.y+Z+g.r*Math.Sin(a))));}else
for(double a=g.o+D(g.i,g.o);a>g.o;a-=Math.PI/(2*g.r)){C.Add(new PointF((float)(g.x+z+g.r*Math.Cos(a)),(float)(g.y+Z+g.r*Math.Sin(a))));}C.Add(new PointF((float)(g.x+z+g.r*Math.Cos(g.o)),(float)(g.y+Z+g.r*Math.Sin(g.o))));C.Add(new PointF((float)(k.x+z+k.r*Math.Cos(k.i)),(float)(k.y+Z+k.r*Math.Sin(k.i))));k.l=E(C);}l=E(C);N=(float)(K(l)/10.0);o.DashPattern=new float[]{N,N};double u=q[0].i;for(int i=0;i<q.Count;i++){S g=q[i];double L=g.l/(N*5);g.a=g.i+((1-(L%2))/g.r*Math.PI*2)*(g.c?1:-1);}List<MagickImage>I=new List<MagickImage>();for(int i=0;i<t;i++){using(Bitmap B=new Bitmap(w,h)){using(Graphics g=Graphics.FromImage(B)){g.Clear(Color.White);g.SmoothingMode=System.Drawing.Drawing2D.SmoothingMode.AntiAlias;foreach(S U in q){float R=U.x+z,L=U.y+Z,d=7+2*U.r;PointF[]f=new PointF[4];for(double a=(i*(4.0/t));a<2*U.r;a+=4){double v=U.a+((U.c?-a:a)/U.r*Math.PI),j=Math.PI/U.r*(U.c?1:-1),V=v+j,W=V+j,r=U.r+3.5;f[0]=new PointF(R,L);f[1]=new PointF(R+(float)(r*Math.Cos(v)),L+(float)(r*Math.Sin(v)));f[2]=new PointF(R+(float)(r*Math.Cos(V)),L+(float)(r*Math.Sin(V)));f[3]=new PointF(R+(float)(r*Math.Cos(W)),L+(float)(r*Math.Sin(W)));g.FillPolygon(n,f);}d=2*(U.r-1.5f);g.FillEllipse(n,R-d/2,L-d/2,d,d);d=2*(U.r-4.5f);g.FillEllipse(m,R-d/2,L-d/2,d,d);d=6;g.FillEllipse(n,R-d/2,L-d/2,d,d);}g.DrawLines(p,C.ToArray());o.DashOffset=(N*2.0f/t)*i;g.DrawLines(o,C.ToArray());B.RotateFlip(RotateFlipType.RotateNoneFlipY);B.Save(i+".png",ImageFormat.Png);I.Add(new MagickImage(B));}}}using(MagickImageCollection collection=new MagickImageCollection()){foreach(MagickImage i in I){i.AnimationDelay=5;collection.Add(i);}QuantizeSettings Q=new QuantizeSettings();Q.Colors=256;collection.Quantize(Q);collection.Optimize();collection.Write("1.gif");}}int t=5;double D(double a,double b){double P=Math.PI,r=a-b;while(r<0)r+=2*P;return r%(2*P);}double E(List<PointF> c){double u=0;for(int i=0;i<c.Count-1;i++){PointF s=c[i];PointF t=c[i+1];double x=s.X-t.X,y=s.Y-t.Y;u+=Math.Sqrt(x*x+y*y);}return u;}double K(double L){double P=4*Math.PI;int i=(int)(L/P);float a=(float)L/i,b=(float)L/(i+1);if(Math.Abs(P-a)<Math.Abs(P-b))return a;return b;}void H(S a,S b,S c){double A=0,r=0,d=b.x-a.x,e=b.y-a.y,f=Math.Atan2(e,d)+Math.PI/2,g=Math.Atan2(e,d)-Math.PI/2,h=Math.Atan2(-e,-d)-Math.PI/2,i=Math.Atan2(-e,-d)+Math.PI/2;double k=c.x-b.x,n=c.y-b.y,l=Math.Sqrt(d*d+e*e);A=D(Math.Atan2(n,k),Math.Atan2(-e,-d));bool x=A>Math.PI!=a.c;b.c=x!=a.c;if(a.r!=b.r)r=a.r+(x?b.r:-b.r);f-=Math.Asin(r/l);g+=Math.Asin(r/l);h+=Math.Asin(r/l);i-=Math.Asin(r/l);b.i=x==a.c?h:i;a.o=a.c?g:f;}}

P sınıfı bir fonksiyona sahiptir; Örnek:

static void Main(string[]a){
P p=new P();
float[][]s=new float[][]{
new float[]{10,200,20},
new float[]{240,200,20},
new float[]{190,170,10},
new float[]{190,150,10},
new float[]{210,120,20},
new float[]{190,90,10},
new float[]{160,0,20},
new float[]{130,170,10},
new float[]{110,170,10},
new float[]{80,0,20},
new float[]{50,170,10}
};
p.F(s);}

görüntü tanımını buraya girin


2
Golf versiyonunu gönderdiğiniz için teşekkür ederiz! Küçük bir kelime oyunu: gif'inizin ilk dişlisi saat yönünün tersine döner; ilk zincir dişlisi daima saat yönünde dönmelidir.
Ell

Sadece C # geçişini gördüm, ancak publicsınıfınızdaki her alanın önündeki değiştiriciye ihtiyacınız var mı?
J Atkin

1
@Jtkin gerçekten, bunların hepsi söyleyebileceğim kadar gereksiz. Diğer konularda, PointF gerçekten System.Drawing.PointF'dir (Liste, Renk ve Matematik için benzer), bu nedenle karşılık gelen usingmaddeler dahil edilmeli veya kullanıldığında tam olarak nitelendirilmiş tipler dahil edilmeli ve System.Drawing referansı belirtilmelidir. cevabında (bilmiyorum skoru eklemek gerekir). Etkileyici cevap zaten.
VisualMelon

@JAtkin İki sınıfım var, S ve P, yani S alanlarının tümü halka açık. Kesinlikle gerekli olup olmadığından emin değilim, ama bence öyle ..
TFeld

3

JavaScript (ES6) 1626 bayt

Bu çözüm @ Flambino'nun çözümünün tersine mühendislik sonucudur, onayıyla gönderiyorum.

R=g=>{with(Math){v='stroke';j=v+'-dasharray';q=v+'-dashoffset';m='appendChild';n='getTotalLength';b='setAttribute';z='#888';k=document;V=(x,y,r,o)=>o={x,y,r,l:sqrt(x*x+y*y),a:v=>V(x+v.x,y+v.y),s:v=>o.a(v.m(-1)),m:f=>V(x*f,y*f),t:r=>V(x*cos(r)-y*sin(r),x*sin(r)+y*cos(r)),c:v=>x*v.y-y*v.x,toString:_=>x+','+y};S=(e,a)=>Object.keys(a).map(n=>e[b](n,a[n]))&&e;T=(t,a)=>S(k.createElementNS('http://www.w3.org/2000/svg',t),a);C=(e,a)=>S(e.cloneNode(),a);w=h=-(x=y=1/0);G=g.map((a,g)=>(g=V(...a))&&(u=(g.r=a[2])+5,x=min(x,g.x-u),y=min(y,g.y-u),w=max(w,g.x+u),h=max(h,g.y+u))&&g);f=G[0];w-=x;h-=y;s=T('svg',{width:w,height:h,viewBox:x+' '+y+' '+w+' '+h,transform:'scale(1,-1)'});c='';L=(c)=>(h=G.length)&&G.map((g,i)=>c(G[i],G[(h+i-1)%h],G[(i+1)%h]))&&L;L((g,p,n)=>g.w=(p.s(g).c(n.s(g))>0))((g,p,n)=>{d=g.s(n),y=x=1/d.l;g.w!=n.w?(p=asin((g.r+n.r)*x),g.w?(x=-x,p=-p):(y=-y)):(p=asin((g.r-n.r)*x),g.w&&(x=y=-x,p=-p));t=d.t(p+PI/2);g.o=t.m(x*g.r).a(g);n.i=t.m(y*n.r).a(n)})((g,p,n)=>{l=(p.o.s(n.i).l<g.i.s(g.o).l);d=(l,e)=>`A${g.r} ${g.r} 0 ${+l} ${+!g.w} ${e}`;a=d(l,g.o);e=(f,r)=>T('circle',{cx:g.x,cy:g.y,r,fill:f});c+=a+'L'+n.i;s[m](e(z,g.r-1.5));s[m](e('#666',g.r-4.5));s[m](e(z,3));g.p=s[m](C(g.e=s[m](T('path',{d:'M'+g.i+a+d(!l,g.i),fill:'none',[v]:z,[v+'-width']:5})),{d:'M'+g.i+a+'L'+n.i,[v]:'#222'}))});c=C(f.p,{d:'M'+f.i+c,[v+'-width']:2});g=c[n]();y=8*(x=g/round(g/(4*PI))/2);f.g=x;f.h=0;L((g,p)=>{g!=f&&(g.g=p.g+p.p[n](),g.h=p.h+p.p[n]());S(g.p,{[j]:x,[q]:g.h})[m](C(S(g.e,{[j]:x,[q]:g.g})[m](T('animate',{attributeName:[q],from:g.g+y,to:g.g,repeatCount:'indefinite',dur:'1s'})),{from:g.h+y,to:g.h}))});k.body[m](s)[m](c)}}

Ungolfed versiyonu:

class Vector {

    constructor(x, y) {
        this.x = x;
        this.y = y;
        this.length = Math.sqrt(x * x + y * y);
    }

    add(vector) {

        return new Vector(this.x + vector.x, this.y + vector.y);
    }

    subtract(vector) {

        return new Vector(this.x - vector.x, this.y - vector.y);
    }

    multiply(scalar) {

        return new Vector(this.x * scalar, this.y * scalar);
    }

    rotate(radians) {

        const cos = Math.cos(radians);
        const sin = Math.sin(radians);

        return new Vector(this.x * cos - this.y * sin, this.x * sin + this.y * cos);
    }

    cross(vector) {

        return this.x * vector.y - this.y * vector.x;
    }

    toString() {

        return `${this.x},${this.y}`;
    }
}

class Gear {

    constructor(x, y, radius) {
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    getVector() {

        return new Vector(this.x, this.y);
    }
}

const setAttributes = (element, attributes) => {

    Object.keys(attributes).forEach((attribute) => {
        element.setAttribute(attribute, attributes[attribute]);
    });
};

const createElement = (tagName, attributes) => {

    const element = document.createElementNS('http://www.w3.org/2000/svg', tagName);

    setAttributes(element, attributes);

    return element;
};

const cloneElement = (element, attributes) => {

    const clone = element.cloneNode();

    setAttributes(clone, attributes);

    return clone;
};

const createPath = (attributes) => {

    return createElement('path', {
        ...attributes,
        fill: 'none'
    });
};

const createCircle = (cx, cy, r, fill) => {

    return createElement('circle', {
        cx,
        cy,
        r,
        fill
    });
};

const loopGears = (gears, callback) => {

    const length = gears.length;

    gears.forEach((gear, index) => {

        const prevGear = gears[(length + index - 1) % length];
        const nextGear = gears[(index + 1) % length];

        callback(gear, prevGear, nextGear);
    });
};

const arcDescription = (radius, largeArcFlag, sweepFlag, endVector) => {

    return `A${radius} ${radius} 0 ${+largeArcFlag} ${+sweepFlag} ${endVector}`;
};

const renderGears = (data) => {

    let x = Infinity;
    let y = Infinity;
    let w = -Infinity;
    let h = -Infinity;

    const gears = data.map((params) => {

        const gear = new Gear(...params);
        const unit = params[2] + 5;

        x = Math.min(x, gear.x - unit);
        y = Math.min(y, gear.y - unit);
        w = Math.max(w, gear.x + unit);
        h = Math.max(h, gear.y + unit);

        return gear;
    });

    const firstGear = gears[0];

    w -= x;
    h -= y;

    const svg = createElement('svg', {
        width: w,
        height: h,
        viewBox: `${x} ${y} ${w} ${h}`,
        transform: `scale(1,-1)`
    });

    let chainPath = '';

    loopGears(gears, (gear, prevGear, nextGear) => {

        const gearVector = gear.getVector();
        const prevGearVector = prevGear.getVector().subtract(gearVector);
        const nextGearVector = nextGear.getVector().subtract(gearVector);

        gear.sweep = (prevGearVector.cross(nextGearVector) > 0);
    });

    loopGears(gears, (gear, prevGear, nextGear) => {

        const diffVector = gear.getVector().subtract(nextGear.getVector());

        let angle = 0;
        let x = 1 / diffVector.length;
        let y = x;

        if (gear.sweep === nextGear.sweep) {

            angle = Math.asin((gear.radius - nextGear.radius) * x);

            if (gear.sweep) {
                x = -x;
                y = -y;
                angle = -angle;
            }
        } else {

            angle = Math.asin((gear.radius + nextGear.radius) * x);

            if (gear.sweep) {
                x = -x;
                angle = -angle;
            } else {
                y = -y;
            }
        }

        const perpendicularVector = diffVector.rotate(angle + Math.PI / 2);

        gear.out = perpendicularVector.multiply(x * gear.radius).add(gear.getVector());
        nextGear.in = perpendicularVector.multiply(y * nextGear.radius).add(nextGear.getVector());
    });

    loopGears(gears, (gear, prevGear, nextGear) => {

        const largeArcFlag = (prevGear.out.subtract(nextGear.in).length < gear.in.subtract(gear.out).length);
        const arcPath = arcDescription(gear.radius, largeArcFlag, !gear.sweep, gear.out);

        const gearExterior = createCircle(gear.x, gear.y, gear.radius - 1.5, '#888');
        const gearInterior = createCircle(gear.x, gear.y, gear.radius - 4.5, '#666');
        const gearCenter = createCircle(gear.x, gear.y, 3, '#888');

        const gearTeeth = createPath({
            d: `M${gear.in}${arcPath}${arcDescription(gear.radius, !largeArcFlag, !gear.sweep, gear.in)}`,
            stroke: '#888',
            'stroke-width': 5
        });

        const chainParts = cloneElement(gearTeeth, {
            d: `M${gear.in}${arcPath}L${nextGear.in}`,
            stroke: '#222'
        });

        gear.teeth = gearTeeth;
        gear.chainParts = chainParts;

        chainPath += `${arcPath}L${nextGear.in}`;

        svg.appendChild(gearExterior);
        svg.appendChild(gearInterior);
        svg.appendChild(gearCenter);
        svg.appendChild(gearTeeth);
        svg.appendChild(chainParts);
    });

    const chain = cloneElement(firstGear.chainParts, {
        d: 'M' + firstGear.in + chainPath,
        'stroke-width': 2
    });

    const chainLength = chain.getTotalLength();
    const chainUnit = chainLength / Math.round(chainLength / (4 * Math.PI)) / 2;
    const animationOffset = 8 * chainUnit;

    loopGears(gears, (gear, prevGear) => {

        if (gear === firstGear) {
            gear.teethOffset = chainUnit;
            gear.chainOffset = 0;
        } else {
            gear.teethOffset = prevGear.teethOffset + prevGear.chainParts.getTotalLength();
            gear.chainOffset = prevGear.chainOffset + prevGear.chainParts.getTotalLength();
        }

        setAttributes(gear.teeth, {
            'stroke-dasharray': chainUnit,
            'stroke-dashoffset': gear.teethOffset
        });

        setAttributes(gear.chainParts, {
            'stroke-dasharray': chainUnit,
            'stroke-dashoffset': gear.chainOffset
        });

        const animate = createElement('animate', {
            attributeName: 'stroke-dashoffset',
            from: gear.teethOffset + animationOffset,
            to: gear.teethOffset,
            repeatCount: 'indefinite',
            dur: '1s'
        });

        const cloneAnimate = cloneElement(animate, {
            from: gear.chainOffset + animationOffset,
            to: gear.chainOffset
        });

        gear.teeth.appendChild(animate);
        gear.chainParts.appendChild(cloneAnimate);
    });

    svg.appendChild(chain);
    document.body.appendChild(svg);
};

var testCases = [
    [[0, 0, 16],  [100, 0, 16],  [100, 100, 12],  [50, 50, 24],  [0, 100, 12]],
    [[0, 0, 26],  [120, 0, 26]],
    [[100, 100, 60],  [220, 100, 14]],
    [[100, 100, 16],  [100, 0, 24],  [0, 100, 24],  [0, 0, 16]],
    [[0, 0, 60],  [44, 140, 16],  [-204, 140, 16],  [-160, 0, 60],  [-112, 188, 12], [-190, 300, 30],  [30, 300, 30],  [-48, 188, 12]],
    [[0, 128, 14],  [46.17, 63.55, 10],  [121.74, 39.55, 14],  [74.71, -24.28, 10], [75.24, -103.55, 14],  [0, -78.56, 10],  [-75.24, -103.55, 14],  [-74.71, -24.28, 10], [-121.74, 39.55, 14],  [-46.17, 63.55, 10]],
    [[367, 151, 12],  [210, 75, 36],  [57, 286, 38],  [14, 181, 32],  [91, 124, 18], [298, 366, 38],  [141, 3, 52],  [80, 179, 26],  [313, 32, 26],  [146, 280, 10], [126, 253, 8],  [220, 184, 24],  [135, 332, 8],  [365, 296, 50],  [248, 217, 8], [218, 392, 30]]
];

function clear() {
    var buttons = document.createElement('div');
    document.body.innerHTML = "";
    document.body.appendChild(buttons);
    testCases.forEach(function (data, i) {
        var button = document.createElement('button');
        button.innerHTML = String(i);
        button.onclick = function () {
            clear();
            renderGears(data);
            return false;
        };
        buttons.appendChild(button);
    });
}

clear();


1
Bu aracı kullanarak 250 bayttan fazla tasarruf edebilirsiniz .
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.