Sprocket Science: Hoạt hình một hệ thống ổ đĩa xích


97

Mục tiêu của thử thách này là sản xuất một hình ảnh động của hệ thống truyền động dây chuyền , bao gồm một bộ bánh răng xích được nối với nhau bằng một chuỗi .

yêu câu chung

Chương trình của bạn sẽ được cung cấp một danh sách các con quay , được chỉ định là (x, y, radius)bộ ba. Các kết quả hệ thống ổ đĩa chuỗi gồm những sprockets, kết nối với nhau bởi một chuỗi căng thẳng khép kín đi qua mỗi trong số họ, theo thứ tự . Mục tiêu của bạn là tạo ra một hình ảnh động vô hạn , hiển thị hệ thống đang chuyển động. Ví dụ, đưa ra đầu vào

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

, đầu ra sẽ trông giống như

ví dụ 1.

Hệ tọa độ phải sao cho trục x đúng, và trục y hướng lên. Bạn có thể giả sử rằng bán kính là các số chẵn lớn hơn hoặc bằng 8 (chúng ta sẽ thấy lý do tại sao điều này lại quan trọng sau này.) Bạn cũng có thể giả sử rằng có ít nhất hai con quay và các con quay không giao nhau. Các đơn vịcủa đầu vào không quá quan trọng. Tất cả các ví dụ và trường hợp thử nghiệm trong bài đăng này sử dụng pixel làm đơn vị đầu vào (vì vậy, ví dụ, bán kính của bánh xích giữa trong hình trước là 24 pixel;) cố gắng không đi quá nhiều so với các đơn vị này. Trong phần còn lại của thử thách, số lượng không gian được hiểu là được đưa ra trong cùng đơn vị với đầu vào, đảm bảo giữ đúng tỷ lệ! Các kích thước của đầu ra nên hơi lớn hơn so với khung giới hạn của tất cả các sprockets, đủ lớn để toàn bộ hệ thống là có thể nhìn thấy. Đặc biệt, các vị trí tuyệt đối của các con quay không được ảnh hưởng đến đầu ra; chỉ các vị trí tương đối của chúng nên (vì vậy, ví dụ, nếu chúng ta thay đổi tất cả các con quay trong ví dụ trên với cùng một lượng, thì đầu ra sẽ giữ nguyên.)

Chuỗi phải được tiếp xúc với các con quay mà nó đi qua tại tất cả các điểm tiếp xúc, và thẳng ở mọi nơi khác. Chuỗi phải vượt qua các đĩa xích sao cho các phân đoạn chuỗi liền kề (nghĩa là các phần của chuỗi giữa hai đĩa xích, gặp nhau tại cùng một bánh xích) không giao nhau.

Giao lộ chuỗi.

Ví dụ, trong khi hệ thống bên trái ở trên là hợp lệ, thì hệ thống ở giữa thì không, vì hai phân đoạn chuỗi liền kề đi qua bánh xích bên trái phía dưới giao nhau. Tuy nhiên, lưu ý rằng hệ thống bên phải hợp lệ, vì hai phân đoạn chuỗi giao nhau không liền kề (mặc dù hệ thống này được sản xuất bởi một đầu vào khác với hai hệ thống còn lại.)

Để giữ cho mọi thứ đơn giản (r), bạn có thể giả sử rằng không có bánh xích nào giao với vỏ lồi của hai con quay lân cận của nó, hoặc vỏ lồi của mỗi hàng xóm của nó và hàng xóm khác của chúng. Nói cách khác, bánh xích trên cùng trong sơ đồ bên dưới có thể không giao nhau với bất kỳ khu vực bóng mờ nào.

Loại trừ

Các phân đoạn chuỗi có thể giao nhau với các đĩa xích khác với các chuỗi mà chúng đi qua (chẳng hạn như trong trường hợp thử nghiệm cuối cùng). Trong trường hợp này, chuỗi phải luôn xuất hiện ở phía trước các con quay.

Yêu cầu trực quan

Chuỗi nên bao gồm một loạt các liên kết có chiều rộng xen kẽ. Chiều rộng của liên kết hẹp nên khoảng 2 và chiều rộng của liên kết rộng nên khoảng 5. Độ dài của cả hai loại liên kết nên bằng nhau. Thời kỳcủa chuỗi, nghĩa là tổng chiều dài của một cặp liên kết rộng / hẹp, phải là số gần nhất với 4π phù hợp với số nguyên lần của độ dài của chuỗi. Ví dụ: nếu độ dài của chuỗi là 1.000, thì chu kỳ của nó phải là 12,5, là số gần nhất với 4π (12,566 ...) phù hợp với số nguyên lần (80) trong 1.000. Điều quan trọng đối với giai đoạn phù hợp với số nguyên lần trong độ dài của chuỗi, do đó không có tạo tác nào tại điểm mà chuỗi quấn quanh.

Chuỗi


Một bánh răng có bán kính R nên bao gồm ba phần đồng tâm: một trục trung tâm , phải là một vòng tròn bán kính khoảng 3; các cơ thể của Sprocket , xung quanh trục, mà phải là một vòng tròn bán kính khoảng R - 4,5; và vành của bánh xích , xung quanh thân, phải là một vòng tròn bán kính khoảng
R - 1,5. Vành cũng phải chứa răng của bánh xích , có chiều rộng khoảng 4; kích thước và khoảng cách của răng phải khớp với kích thước của các liên kết chuỗi, sao cho chúng gọn gàng.

Bánh xích

Thời kỳ răng của bánh xích, nghĩa là khoảng cách giữa hai răng liên tiếp dọc theo chu vi của bánh xích, phải khớp với thời gian của chuỗi. Vì khoảng thời gian là khoảng 4π và vì bán kính của bánh xích được đảm bảo là chẵn, nên khoảng thời gian phải vừa với chu vi của bánh xích một số lần gần như nguyên, do đó không nên có bất kỳ tạo tác đáng chú ý nào tại điểm bánh xích quấn quanh.

Bạn có thể sử dụng bất kỳ sự kết hợp màu sắc nào cho chuỗi, các phần khác nhau của bánh xích và nền, miễn là chúng có thể dễ dàng phân biệt . Nền có thể trong suốt. Các ví dụ trong bài này sử dụng Chuỗi màu #202020cho chuỗi, Bánh xích và màu vành #868481cho trục và vành của bánh xích, và Sprocket Màu cơ thể #646361cho thân của bánh xích.

Yêu cầu hoạt hình

Các xích đầu tiên trong danh sách đầu vào nên xoay chiều kim đồng hồ ; phần còn lại của đĩa xích nên xoay tương ứng. Chuỗi phải di chuyển với tốc độ khoảng 16π (khoảng 50) đơn vị mỗi giây; tốc độ khung hình tùy thuộc vào bạn, nhưng hình ảnh động sẽ trông đủ mượt mà.

Các hình ảnh động nên lặp lại liền mạch .

Sự phù hợp

Một số thuộc tính và tỷ lệ hình ảnh được chỉ định một cách có chủ ý chỉ bằng cách bạn không cần phải khớp chính xác với chúng . Đầu ra của chương trình của bạn không phải là bản sao pixel-pixel của các ví dụ được đưa ra ở đây, nhưng nó sẽ trông tương tự. Cụ thể, tỷ lệ chính xác của chuỗi và xích, và hình dạng chính xác của các liên kết và răng của xích, là linh hoạt.

Những điểm quan trọng nhất cần tuân theo là:

  • Chuỗi phải vượt qua các con quay, theo thứ tự đầu vào, từ hướng chính xác.
  • Chuỗi phải được tiếp xúc với các con quay tại tất cả các điểm tiếp xúc.
  • Các liên kết của chuỗi và răng của các con quay cần được sắp xếp gọn gàng, ít nhất là để điều chỉnh khoảng cách và pha.
  • Khoảng cách giữa các liên kết của chuỗi và răng của các con quay, phải sao cho không có các tạo tác đáng chú ý tại điểm mà chúng quấn quanh.
  • Các con quay cần quay đúng hướng.
  • Các hình ảnh động nên lặp lại liền mạch.

Như một lưu ý cuối cùng, trong khi, về mặt kỹ thuật, mục tiêu của thử thách này là viết mã ngắn nhất, nếu bạn cảm thấy muốn sáng tạo và tạo ra một đầu ra phức tạp hơn, bằng mọi cách, hãy thực hiện nó!

Thử thách

Viết chương trình hoặc hàm , lấy danh sách các con quay và tạo ra một hình ảnh động hệ thống ổ đĩa chuỗi tương ứng, như được mô tả ở trên.

Đầu vào và đầu ra

Bạn có thể lấy đầu vào thông qua dòng lệnh , thông qua STDIN , làm đối số chức năng hoặc sử dụng một phương thức tương đương . Bạn có thể sử dụng bất kỳ định dạng thuận tiện cho đầu vào, nhưng hãy đảm bảo chỉ định nó trong bài viết của bạn.

đầu ra , bạn có thể hiển thị hình ảnh động trực tiếp , tạo tệp hoạt hình (ví dụ: GIF hoạt hình) hoặc tạo một chuỗi các tệp khung (tuy nhiên, có một hình phạt nhỏ trong trường hợp này; xem bên dưới.) Nếu bạn sử dụng đầu ra tệp, đảm bảo số lượng khung hình hợp lý (ví dụ trong bài đăng này sử dụng rất ít khung hình;) số lượng khung hình không nhất thiết phải tối thiểu, nhưng bạn không nên tạo quá nhiều khung hình thừa. Nếu bạn xuất một chuỗi các khung, hãy đảm bảo chỉ định tốc độ khung hình trong bài đăng của bạn.

Ghi bàn

Đây là mã golf . Các câu trả lời ngắn nhất , tính bằng byte, thắng.

+ Hình phạt 10%   Nếu chương trình của bạn tạo ra một chuỗi các khung làm đầu ra, thay vì hiển thị trực tiếp hoạt hình hoặc tạo một tệp hoạt hình duy nhất, hãy thêm 10% vào điểm số của bạn.

Các trường hợp thử nghiệm

Kiểm tra 1

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

Kiểm tra 1

Kiểm tra 2

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

Kiểm tra 2

Bài kiểm tra 3

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

Bài kiểm tra 3

Kiểm tra 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)

Kiểm tra 4

Kiểm tra 5

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

Kiểm tra 5

Kiểm tra 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)

Kiểm tra 6



Chúc vui vẻ!


38
Những gifs này rất thỏa mãn +1
Adnan

24
Tôi sẽ rất ấn tượng nếu có ai trả lời thành công với bất kỳ số lượng mã nào.
DavidC

5
Làm thế nào bạn thực hiện các gifs? Và điều này đã được thực hiện trong bao lâu?
J Atkin

10
@JAtkin Tương tự như mọi người khác nên: Tôi đã viết một giải pháp :) Nếu bạn hỏi về chi tiết cụ thể, tôi đã sử dụng Cairo cho từng khung hình và sau đó sử dụng ImageMagick để tạo gifs (BTW, nếu có ai muốn tạo hoạt hình này cách, ví dụ, bằng cách đầu tiên tạo ra các khung hình và sau đó sử dụng một công cụ bên ngoài để biến chúng thành hình ảnh động, tôi hoàn toàn tốt với điều đó, chừng nào bạn xác định sự phụ thuộc vào các công cụ trong bài viết của bạn. Chỉ cần được rõ ràng, đó là bạn chương trình sẽ gọi công cụ, không phải người dùng.)
Ell

5
@Anko Tin tốt là bạn không phải lo lắng về điều đó: tình huống này được đảm bảo không xảy ra trong đầu vào; xem phần "không có bánh răng cắt ngang thân tàu lồi ...", phần có hình ảnh với ba vùng bóng mờ. Tổng quát hơn, chuỗi chỉ vượt qua mỗi bánh xích một lần, theo thứ tự của con quay, ngay cả khi có vẻ như nó vượt qua gần một bánh xích nhiều hơn một lần.
Ell

Câu trả lời:


42

JavaScript (ES6), 2557 1915 1897 1681 byte

Đây không phải là siêu lừa đảo thực sự; nó được thu nhỏ - một phần bằng tay - nhưng điều đó không có gì đặc biệt. Không thể nghi ngờ gì ngắn hơn nếu tôi đánh gôn nhiều hơn trước khi rút ngắn, nhưng tôi đã dành (hơn) đủ thời gian cho việc này rồi.

Chỉnh sửa: Ok, vì vậy tôi đã dành nhiều thời gian hơn cho nó và chơi mã nhiều hơn trước khi rút gọn (lần này rất thủ công). Mã vẫn sử dụng cùng một cách tiếp cận và cấu trúc tổng thể, nhưng ngay cả như vậy tôi vẫn tiết kiệm được 642 byte. Không quá tồi tàn, nếu tôi tự nói như vậy. Có lẽ đã bỏ lỡ một số cơ hội tiết kiệm byte, nhưng tại thời điểm này, ngay cả tôi cũng không chắc nó hoạt động như thế nào nữa. Điều duy nhất khác biệt về mặt đầu ra, là bây giờ nó sử dụng các màu hơi khác nhau có thể được viết chặt chẽ hơn.

Chỉnh sửa 2 (nhiều sau): Đã lưu 18 byte. Cảm ơn ConorO'Brien trong các bình luận đã chỉ ra điều hiển nhiên rõ ràng mà tôi hoàn toàn bỏ qua.

Chỉnh sửa 3: Vì vậy, tôi đã hình dung tôi đảo ngược mã của chính mình, bởi vì, thành thật mà nói, tôi không thể nhớ mình đã làm như thế nào và tôi đã mất các phiên bản không được chỉnh sửa. Vì vậy, tôi đã đi qua, và lo và tìm thấy thêm 316 byte để tiết kiệm bằng cách tái cấu trúc và thực hiện một số môn đánh gôn vi mô.

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

Hàm trên nối thêm một phần tử SVG (bao gồm cả hình động) vào tài liệu. Ví dụ: để hiển thị trường hợp thử nghiệm thứ 2:

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

Có vẻ như là một điều trị - ít nhất là ở đây trong Chrome.

Hãy thử nó trong đoạn trích dưới đây (nhấp vào nút sẽ rút ra từng trường hợp thử nghiệm của OP).

Các mã vẽ chuỗi và răng bánh răng như một nét đứt. Sau đó, nó sử dụng animatecác yếu tố để làm động các stroke-dashoffsetthuộc tính. Phần tử SVG kết quả là khép kín; không có hoạt hình dựa trên JS hoặc kiểu CSS.

Để làm cho mọi thứ xếp thành hàng độc đáo, mỗi vòng răng của bánh răng thực sự được vẽ như một đường dẫn gồm hai cung, vì vậy đường dẫn có thể bắt đầu ngay tại điểm tiếp tuyến nơi chuỗi chạm vào. Điều này làm cho nó đơn giản hơn rất nhiều để xếp hàng.

Hơn nữa, dường như có rất nhiều lỗi làm tròn khi sử dụng nét đứt của SVG. Ít nhất, đó là những gì tôi thấy; chuỗi càng dài, nó càng bị hỏng với mỗi thiết bị kế tiếp nhau. Vì vậy, để giảm thiểu vấn đề, chuỗi thực sự được tạo thành từ một số đường dẫn. Mỗi đường dẫn bao gồm một phân đoạn xung quanh một bánh răng và một đường thẳng đến bánh răng tiếp theo. Dấu gạch ngang của họ được tính toán để phù hợp. Tuy nhiên, phần "bên trong" mỏng của chuỗi chỉ là một đường vòng lặp duy nhất, vì nó không hoạt hình.


2
Trông thật tuyệt! Kudos để trả lời một thách thức cũ (ish)!
Ell

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

1
@Flambino, tôi thích giải pháp của bạn cho thách thức này và tôi đã thực sự xin lỗi rằng bạn bị mất nguồn gốc, tôi thực hiện một số enginnering ngược lại để phục hồi nó, nó có thể được tìm thấy ở đây: gist.github.com/micnic/6aec085d63320229a778c6775ec7f9aa cũng tôi minified nó thủ công đến 1665 byte (có thể rút ngắn thêm, nhưng hôm nay tôi lười)
micnic

1
@micnic Cảm ơn! Tôi sẽ phải kiểm tra xem! Và đừng lo lắng, tôi cũng đã xoay sở để thiết kế ngược lại, vì vậy tôi có một phiên bản dễ đọc hơn. Nhưng, dang, 16 byte ít hơn? Thanh danh! Tôi chắc chắn sẽ cho nó một cái nhìn khi tôi có thể tìm thấy thời gian
Flambino

1
@Flambino, về cơ bản, tác động lớn nhất đến kích thước tập tin là cấu trúc svg, tôi đã không đặt sự gợi ý trong một <g>, mà đặt nó trực tiếp vào thư mục gốc của Svg . Cũng tìm thấy một nơi mà bạn đã chuyển đổi cờ quét và cờ vòng cung lớn từ boolean sang số bằng cách sử dụng 1*x, nhưng bạn có thể sử dụng+x
micnic

40

C # 3566 byte

Không chơi gôn chút nào, nhưng hoạt động (tôi nghĩ)

Ungolfed trong lịch sử chỉnh sửa.

Sử dụng Magick.NET để kết xuất gif.

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

Lớp P có chức năng F; Thí dụ:

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

nhập mô tả hình ảnh ở đây


2
Cảm ơn bạn đã đăng một phiên bản golf! Một ngụy biện nhỏ: bánh xích đầu tiên trong gif của bạn quay ngược chiều kim đồng hồ; bánh xích đầu tiên phải luôn luôn xoay theo chiều kim đồng hồ.
Ell

Tôi mới chỉ thấy C # đi qua, nhưng bạn có cần công cụ publicsửa đổi trước mỗi trường trong lớp không?
J Atkin

1
@JAtkin thực sự, những thứ đó là không cần thiết theo như tôi có thể nói. Trong các vấn đề khác, PointF thực sự là System.Drawing.PointF (tương tự như Danh sách, Màu sắc và Toán học), do đó, usingnên bao gồm các mệnh đề tương ứng hoặc các loại đủ điều kiện khi được sử dụng và tham chiếu đến System.Drawing nên được lưu ý trong câu trả lời (có nên thêm vào số điểm tôi không biết). Dù sao thì câu trả lời ấn tượng.
VisualMelon 4/12/2015

@JAtkin Tôi có hai lớp, S và P, vì vậy các trường trong S đều công khai. Không chắc chắn nếu chúng thực sự cần thiết, nhưng tôi nghĩ vậy ..
TFeld

3

JavaScript (ES6) 1626 byte

Giải pháp này là kết quả của kỹ thuật đảo ngược của giải pháp @ Flambino, tôi đăng nó với sự đồng ý của anh ấy.

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

Phiên bản chưa được chỉnh sửa:

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
Bạn có thể lưu hơn 250 byte bằng công cụ này .
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.