1+ <!DOCTYPE html>
2+ < html >
3+ < head >
4+ < meta charset ="utf-8 ">
5+ < meta name ="viewport " content ="width=device-width, initial-scale=1 ">
6+ < title > Icosahedron</ title >
7+ < style type ="text/css ">
8+ body {
9+ position : absolute;
10+ left : 0 ;
11+ top : 0 ;
12+ width : 100% ;
13+ height : 100% ;
14+ margin : 0 ;
15+ background-color : # 000 ;
16+ color : # eee ;
17+ }
18+ canvas {
19+ position : absolute;
20+ left : 0 ;
21+ top : 0 ;
22+ image-rendering : pixelated;
23+ image-rendering : optimizeSpeed;
24+ }
25+ span {
26+ position : absolute;
27+ bottom : 0 ;
28+ right : 0 ;
29+ }
30+ </ style >
31+ </ head >
32+ < body >
33+ < canvas id ="c " width ="128 " height ="128 "> </ canvas >
34+ < span id ="fps "> </ span >
35+ < script type ="text/javascript "> "use strict" ;
36+ const side = Math . min ( c . width , c . height ) * .5 ,
37+ phi = Math . sqrt ( 1.25 ) + .5 ,
38+ tau = Math . PI * 2 ,
39+ p = side * .5 ,
40+ q = p * phi ,
41+ sin60 = Math . sqrt ( .75 ) ,
42+ rng = x => m => ( x = x * 1664525 + 1 >>> 0 ) * 2.3283064365386963e-10 * m ,
43+ r = rng ( + new Date ) ,
44+ t = c . getContext ( '2d' , { alpha : false } ) ,
45+ xyz = [ r ( tau ) - Math . PI , r ( tau ) - Math . PI , r ( tau ) - Math . PI ] ,
46+ ring = [ 2 , 8 , 4 , 6 , 11 , 7 , 10 , 3 , 9 , 5 ] ;
47+ function rot ( x , y , z ) {
48+ const cx = Math . cos ( x ) , sx = Math . sin ( x ) , cy = Math . cos ( y ) , sy = Math . sin ( y ) , cz = Math . cos ( z ) , sz = Math . sin ( z ) ,
49+ cxsy = cx * sy , sxsy = sx * sy ;
50+ return [
51+ [ cx * cy , cxsy * sz - sx * cz , cxsy * cz + sx * sz ] ,
52+ [ sx * cy , sxsy * sz + cx * cz , sxsy * cz - cx * sz ] ,
53+ [ - sy , cy * sz , cy * cz ]
54+ ] ;
55+ }
56+ let timer , then = 0 , dist = side * 4 , lx = r ( tau ) - Math . PI , ly = r ( tau ) - Math . PI , lz = r ( tau ) - Math . PI ; //, n = 1, avg = 24;
57+ t . translate ( side , side ) ;
58+ t . strokeStyle = '#fff' ;
59+ function drawFace ( a , b , c ) {
60+ const ux = a [ 0 ] - b [ 0 ] , uy = a [ 1 ] - b [ 1 ] ,
61+ vx = b [ 0 ] - c [ 0 ] , vy = b [ 1 ] - c [ 1 ] ,
62+ z = ux * vy - vx * uy ;
63+ if ( z < 0 ) {
64+ const uz = a [ 2 ] - b [ 2 ] , vz = b [ 2 ] - c [ 2 ] ,
65+ x = uy * vz - vy * uz ,
66+ y = uz * vx - vz * ux ,
67+ m = 200 / Math . sqrt ( x * x + y * y + z * z ) ,
68+ w = y * - .5 - z ,
69+ v = x * sin60 ;
70+ t . fillStyle = `rgba(${ ( w - v ) * m } ${ ( w + v ) * m } ${ ( y - z ) * m } / .8)` ;
71+ t . beginPath ( ) ;
72+ t . moveTo ( ...a ) ;
73+ t . lineTo ( ...b ) ;
74+ t . lineTo ( ...c ) ;
75+ t . closePath ( ) ;
76+ t . fill ( ) ;
77+ }
78+ }
79+ function draw ( now ) {
80+ timer = requestAnimationFrame ( draw ) ;
81+ if ( timer & 3 )
82+ return ;
83+ t . clearRect ( - side , - side , c . width , c . height ) ;
84+ /*
85+ t.beginPath();
86+ t.moveTo(0, -20);
87+ t.lineTo(16, -3);
88+ t.lineTo(6, -3);
89+ t.lineTo(6, 15);
90+ t.lineTo(-6, 15);
91+ t.lineTo(-6, -3);
92+ t.lineTo(-16, -3);
93+ t.closePath();
94+ t.fillStyle = 'rgba(255, 255, 255, 0.75)';
95+ t.fill();
96+ //*/
97+ {
98+ const tx = Math . sin ( ly ) * .01 + lx * .998 ;
99+ ly = Math . sin ( lz ) * .01 + ly * .998 ;
100+ lz = Math . sin ( lx ) * .01 + lz * .998 ;
101+ lx = tx ;
102+ }
103+ xyz [ 0 ] = lx * .01 + xyz [ 0 ] * .998 ;
104+ xyz [ 1 ] = ly * .01 + xyz [ 1 ] * .998 ;
105+ xyz [ 2 ] = lz * .01 + xyz [ 2 ] * .998 ;
106+ const rmat = rot ( ...xyz ) ,
107+ projected = [ ] ;
108+ for ( let i = 0 ; i < 3 ; ++ i ) {
109+ const j = i ? i - 1 : 2 ,
110+ ax = p * rmat [ 0 ] [ i ] , bx = q * rmat [ 0 ] [ j ] ,
111+ ay = p * rmat [ 1 ] [ i ] , by = q * rmat [ 1 ] [ j ] ,
112+ az = p * rmat [ 2 ] [ i ] , bz = q * rmat [ 2 ] [ j ] ;
113+ let x = ax + bx , y = ay + by , z = az + bz ,
114+ scale = dist / ( z + dist ) ;
115+ projected . push ( [ x * scale , y * scale , z ] ) ;
116+ scale = dist / ( z - dist ) ;
117+ projected . push ( [ x * scale , y * scale , - z ] ) ;
118+ x = bx - ax ; y = by - ay ; z = bz - az ;
119+ scale = dist / ( z + dist ) ;
120+ projected . push ( [ x * scale , y * scale , z ] ) ;
121+ scale = dist / ( z - dist ) ;
122+ projected . push ( [ x * scale , y * scale , - z ] ) ;
123+ }
124+ //*
125+ t . beginPath ( ) ;
126+ t . fillStyle = '#fff' ;
127+ for ( let i = 0 ; i < 12 ; i += 3 ) {
128+ let j = i + 2 , k = i ? i - 4 : 8 ;
129+ t . moveTo ( ...projected [ i ] ) ;
130+ t . lineTo ( ...projected [ k ] ) ;
131+ t . lineTo ( ...projected [ j ] ) ;
132+ t . lineTo ( ...projected [ i ] ) ;
133+ t . lineTo ( ...projected [ k + 3 ] ) ;
134+ t . lineTo ( ...projected [ j ] ) ;
135+ ++ i ;
136+ ++ j ;
137+ t . moveTo ( ...projected [ i ] ) ;
138+ t . lineTo ( ...projected [ k + 1 ] ) ;
139+ t . lineTo ( ...projected [ j ] ) ;
140+ t . lineTo ( ...projected [ i ] ) ;
141+ t . lineTo ( ...projected [ k + 2 ] ) ;
142+ t . lineTo ( ...projected [ j ] ) ;
143+ }
144+ t . stroke ( ) ;
145+ //*/
146+ const u = projected [ 0 ] , v = projected [ 1 ] ;
147+ for ( let i = 0 ; i < 5 ; ++ i ) {
148+ const j = i ? i - 1 : 4 ,
149+ a = projected [ ring [ i ] ] ,
150+ b = projected [ ring [ j ] ] ,
151+ c = projected [ ring [ i + 5 ] ] ,
152+ d = projected [ ring [ j + 5 ] ] ;
153+ drawFace ( u , a , b ) ;
154+ drawFace ( d , b , a ) ;
155+ drawFace ( v , d , c ) ;
156+ drawFace ( a , c , d ) ;
157+ }
158+ if ( timer & 4 )
159+ fps . textContent = Math . round ( 1000 / ( now - then ) )
160+ + 'fps, Euler angles: (' + xyz . map ( x => x . toFixed ( 2 ) ) . join ( ', ' )
161+ + '), delta: (' + lx . toFixed ( 2 ) + ', ' + ly . toFixed ( 2 ) + ', ' + lz . toFixed ( 2 ) + ')' ;
162+ then = now ;
163+ }
164+ document . body . onclick = ( ) => timer ? ( cancelAnimationFrame ( timer ) , timer = 0 ) : requestAnimationFrame ( draw ) ;
165+ requestAnimationFrame ( draw ) ;
166+ </ script >
167+ </ body >
168+ </ html >
0 commit comments