PageSourceSearch

https://westlygroup.com/wp-content/mu-plugins/westly-blocks/js/sector-wireframes.js?ver=1789481250

js westlygroup.com collected 2026-10-01 12:28:57 UTC 12,185 bytes, 359 lines download raw bytes

1/**
2 * Sector-grid wireframe icons — cube, wave, orbital, shield, neural, route.
3 *
4 * Direct vanilla-JS port of TWGWireframeCube/Wave/Orbital/Shield in
5 * export-westly/twg/viz.jsx. Same reasoning as hero-canvas.js: each is self-contained
6 * Canvas 2D (a draw loop plus a hover flag that speeds the animation up), no React
7 * dependency in the drawing logic itself.
8 *
9 * Matches each `<canvas class="sector-wireframe" data-wf="cube|wave|orbital|shield|neural|route">` to
10 * its draw function via the `data-wf` attribute, set by sector-grid.php in the same
11 * cube/wave/orbital/shield card order as the design's
12 * `[TWGWireframeCube, TWGWireframeWave, TWGWireframeOrbital, TWGWireframeShield][i]`.
13 *
14 * Round 24 (client revision): the grid went 4 -> 5 cards and the icon order changed to
15 * `[Wave, Neural, Cube, Route, Shield]` — see export-westly-v2/twg/pages-main.jsx.
16 * `neural` and `route` are ported from export-westly-v2/twg/viz.jsx, same self-contained
17 * Canvas 2D shape as the original four.
18 */
19( function () {
20	'use strict';
21
22	var RGB = '0,125,194'; // --twg-blue
23
24	function rotY( v, a ) {
25		var c = Math.cos( a ), s = Math.sin( a );
26		return [ v[ 0 ] * c + v[ 2 ] * s, v[ 1 ], -v[ 0 ] * s + v[ 2 ] * c ];
27	}
28	function rotX( v, a ) {
29		var c = Math.cos( a ), s = Math.sin( a );
30		return [ v[ 0 ], v[ 1 ] * c - v[ 2 ] * s, v[ 1 ] * s + v[ 2 ] * c ];
31	}
32
33	function drawCube( ctx, size, getTime ) {
34		var vertices = [ [ -1, -1, -1 ], [ 1, -1, -1 ], [ 1, 1, -1 ], [ -1, 1, -1 ], [ -1, -1, 1 ], [ 1, -1, 1 ], [ 1, 1, 1 ], [ -1, 1, 1 ] ];
35		var edges = [ [ 0, 1 ], [ 1, 2 ], [ 2, 3 ], [ 3, 0 ], [ 4, 5 ], [ 5, 6 ], [ 6, 7 ], [ 7, 4 ], [ 0, 4 ], [ 1, 5 ], [ 2, 6 ], [ 3, 7 ] ];
36		var time = getTime();
37		ctx.clearRect( 0, 0, size, size );
38		var sc = size * 0.22;
39		var projected = vertices.map( function ( v ) {
40			var r = rotY( v.slice(), time );
41			r = rotX( r, time * 0.7 );
42			var fov = 4, z = r[ 2 ] + fov, ps = fov / z;
43			return { x: r[ 0 ] * sc * ps + size / 2, y: r[ 1 ] * sc * ps + size / 2, s: ps };
44		} );
45		ctx.strokeStyle = 'rgba(' + RGB + ',0.45)';
46		ctx.lineWidth = 1;
47		edges.forEach( function ( e ) {
48			ctx.beginPath();
49			ctx.moveTo( projected[ e[ 0 ] ].x, projected[ e[ 0 ] ].y );
50			ctx.lineTo( projected[ e[ 1 ] ].x, projected[ e[ 1 ] ].y );
51			ctx.stroke();
52		} );
53		projected.forEach( function ( p ) {
54			ctx.fillStyle = 'rgba(' + RGB + ',0.6)';
55			ctx.beginPath();
56			ctx.arc( p.x, p.y, 1.5 * p.s, 0, Math.PI * 2 );
57			ctx.fill();
58		} );
59	}
60
61	function drawWave( ctx, size, getTime ) {
62		var time = getTime();
63		var cols = 20, rows = 12, cy = size / 2;
64		function point( i, j ) {
65			var x = ( i / cols ) * size * 0.85 + size * 0.075;
66			var z = ( j / rows - 0.5 ) * 2;
67			var xNorm = ( i / cols - 0.5 ) * 2;
68			var wave = Math.sin( xNorm * 3 + time ) * Math.cos( z * 2 + time * 0.7 ) * 0.35;
69			var y = cy + z * size * 0.25 - wave * size * 0.3;
70			return [ x, y ];
71		}
72		ctx.clearRect( 0, 0, size, size );
73		for ( var j = 0; j <= rows; j++ ) {
74			ctx.beginPath();
75			ctx.strokeStyle = 'rgba(' + RGB + ',' + ( 0.22 + ( j / rows ) * 0.2 ) + ')';
76			ctx.lineWidth = 0.8;
77			for ( var i = 0; i <= cols; i++ ) {
78				var p = point( i, j );
79				if ( i === 0 ) { ctx.moveTo( p[ 0 ], p[ 1 ] ); } else { ctx.lineTo( p[ 0 ], p[ 1 ] ); }
80			}
81			ctx.stroke();
82		}
83		for ( var i2 = 0; i2 <= cols; i2 += 2 ) {
84			ctx.beginPath();
85			ctx.strokeStyle = 'rgba(' + RGB + ',0.18)';
86			ctx.lineWidth = 0.6;
87			for ( var j2 = 0; j2 <= rows; j2++ ) {
88				var p2 = point( i2, j2 );
89				if ( j2 === 0 ) { ctx.moveTo( p2[ 0 ], p2[ 1 ] ); } else { ctx.lineTo( p2[ 0 ], p2[ 1 ] ); }
90			}
91			ctx.stroke();
92		}
93		for ( var j3 = 0; j3 <= rows; j3 += 2 ) {
94			for ( var i3 = 0; i3 <= cols; i3 += 2 ) {
95				var p3 = point( i3, j3 );
96				ctx.fillStyle = 'rgba(' + RGB + ',0.45)';
97				ctx.beginPath();
98				ctx.arc( p3[ 0 ], p3[ 1 ], 1, 0, Math.PI * 2 );
99				ctx.fill();
100			}
101		}
102	}
103
104	function drawOrbital( ctx, size, getTime ) {
105		var time = getTime();
106		var cx = size / 2, cy = size / 2, r = size * 0.3;
107		var rings = [
108			{ tiltX: 0.3, tiltZ: 0 },
109			{ tiltX: 0.8, tiltZ: Math.PI / 3 },
110			{ tiltX: 1.2, tiltZ: ( 2 * Math.PI ) / 3 },
111		];
112		ctx.clearRect( 0, 0, size, size );
113		ctx.strokeStyle = 'rgba(' + RGB + ',0.4)';
114		ctx.lineWidth = 1;
115		rings.forEach( function ( ring ) {
116			ctx.beginPath();
117			var steps = 64;
118			for ( var i = 0; i <= steps; i++ ) {
119				var angle = ( i / steps ) * Math.PI * 2 + time;
120				var x = Math.cos( angle ) * r;
121				var y = Math.sin( angle ) * r;
122				var cosT = Math.cos( ring.tiltX ), sinT = Math.sin( ring.tiltX );
123				var y2 = y * cosT, z2 = y * sinT;
124				var cosZ = Math.cos( ring.tiltZ + time * 0.3 ), sinZ = Math.sin( ring.tiltZ + time * 0.3 );
125				var xf = x * cosZ - y2 * sinZ;
126				var yf = x * sinZ + y2 * cosZ;
127				var fov = 4, zz = z2 + fov, ps = fov / zz;
128				var px = xf * ps + cx, py = yf * ps + cy;
129				if ( i === 0 ) { ctx.moveTo( px, py ); } else { ctx.lineTo( px, py ); }
130			}
131			ctx.stroke();
132		} );
133
134		ctx.fillStyle = 'rgba(' + RGB + ',0.6)';
135		ctx.beginPath();
136		ctx.arc( cx, cy, 2.5, 0, Math.PI * 2 );
137		ctx.fill();
138
139		rings.forEach( function ( ring, ri ) {
140			var angle = time * ( 1 + ri * 0.3 ) + ri * 2;
141			var x = Math.cos( angle ) * r;
142			var y = Math.sin( angle ) * r;
143			var cosT = Math.cos( ring.tiltX ), sinT = Math.sin( ring.tiltX );
144			var y2 = y * cosT, z2 = y * sinT;
145			var cosZ = Math.cos( ring.tiltZ + time * 0.3 ), sinZ = Math.sin( ring.tiltZ + time * 0.3 );
146			var xf = x * cosZ - y2 * sinZ;
147			var yf = x * sinZ + y2 * cosZ;
148			var fov = 4, zz = z2 + fov, ps = fov / zz;
149			ctx.fillStyle = 'rgba(' + RGB + ',0.7)';
150			ctx.beginPath();
151			ctx.arc( xf * ps + cx, yf * ps + cy, Math.abs( 1.8 * ps ), 0, Math.PI * 2 );
152			ctx.fill();
153		} );
154	}
155
156	function drawShield( ctx, size, getTime ) {
157		var time = getTime();
158		var cx = size / 2, cy = size / 2;
159
160		function shield( s ) {
161			var w = size * 0.30 * s, top = cy - size * 0.34 * s, bot = cy + size * 0.36 * s;
162			ctx.beginPath();
163			ctx.moveTo( cx - w, top );
164			ctx.lineTo( cx + w, top );
165			ctx.lineTo( cx + w, cy + size * 0.06 * s );
166			ctx.quadraticCurveTo( cx + w, bot - size * 0.04 * s, cx, bot );
167			ctx.quadraticCurveTo( cx - w, bot - size * 0.04 * s, cx - w, cy + size * 0.06 * s );
168			ctx.closePath();
169			ctx.stroke();
170		}
171
172		ctx.clearRect( 0, 0, size, size );
173
174		for ( var i = 0; i < 3; i++ ) {
175			var phase = ( time + i * 0.55 ) % 1.65;
176			var s = 0.42 + phase * 0.36;
177			var fade = Math.min( 1, phase / 0.3 ) * Math.max( 0, 1 - ( phase - 1.0 ) / 0.65 );
178			ctx.strokeStyle = 'rgba(' + RGB + ',' + ( 0.5 * fade ) + ')';
179			ctx.lineWidth = i === 0 ? 1.1 : 0.7;
180			shield( s );
181		}
182
183		var r = size * 0.11;
184		ctx.strokeStyle = 'rgba(' + RGB + ',0.5)';
185		ctx.lineWidth = 0.6;
186		var nodes = [];
187		for ( var n = 0; n < 6; n++ ) {
188			var a = ( Math.PI / 3 ) * n + time * 0.6;
189			nodes.push( [ cx + r * Math.cos( a ), cy + r * Math.sin( a ) ] );
190		}
191		nodes.forEach( function ( node, i2 ) {
192			var next = nodes[ ( i2 + 2 ) % 6 ];
193			ctx.beginPath(); ctx.moveTo( node[ 0 ], node[ 1 ] ); ctx.lineTo( next[ 0 ], next[ 1 ] ); ctx.stroke();
194			ctx.beginPath(); ctx.moveTo( node[ 0 ], node[ 1 ] ); ctx.lineTo( cx, cy ); ctx.stroke();
195		} );
196		ctx.fillStyle = 'rgba(' + RGB + ',0.65)';
197		nodes.forEach( function ( node ) {
198			ctx.beginPath(); ctx.arc( node[ 0 ], node[ 1 ], 1.3, 0, Math.PI * 2 ); ctx.fill();
199		} );
200		ctx.beginPath(); ctx.arc( cx, cy, 2.1, 0, Math.PI * 2 ); ctx.fill();
201	}
202
203	/**
204	 * Neural node lattice (AI) — port of TWGWireframeNeural, export-westly-v2/twg/viz.jsx.
205	 *
206	 * Four columns of 2/3/3/2 nodes, every node joined to every node in the next column.
207	 * Edge opacity pulses along the depth axis; node radius breathes on its own phase.
208	 */
209	function drawNeural( ctx, size, getTime ) {
210		var time = getTime();
211		var cols = [ 2, 3, 3, 2 ];
212		var nodes = [];
213		cols.forEach( function ( n, ci ) {
214			for ( var i = 0; i < n; i++ ) {
215				nodes.push( {
216					c: ci,
217					x: size * ( 0.17 + ci * 0.22 ),
218					y: size * ( 0.5 + ( i - ( n - 1 ) / 2 ) * 0.21 ),
219					p: ci * 0.7 + i * 0.5,
220				} );
221			}
222		} );
223
224		ctx.clearRect( 0, 0, size, size );
225		ctx.lineWidth = 1;
226
227		function column( index ) {
228			var out = [];
229			for ( var k = 0; k < nodes.length; k++ ) {
230				if ( nodes[ k ].c === index ) { out.push( nodes[ k ] ); }
231			}
232			return out;
233		}
234
235		for ( var ci = 0; ci < cols.length - 1; ci++ ) {
236			var a = column( ci );
237			var b = column( ci + 1 );
238			for ( var ai = 0; ai < a.length; ai++ ) {
239				for ( var bi = 0; bi < b.length; bi++ ) {
240					var pulse = 0.14 + 0.24 * Math.max( 0, Math.sin( time - ci * 0.9 - ( ai + bi ) * 0.35 ) );
241					ctx.strokeStyle = 'rgba(' + RGB + ',' + pulse.toFixed( 3 ) + ')';
242					ctx.beginPath();
243					ctx.moveTo( a[ ai ].x, a[ ai ].y );
244					ctx.lineTo( b[ bi ].x, b[ bi ].y );
245					ctx.stroke();
246				}
247			}
248		}
249
250		nodes.forEach( function ( n ) {
251			var g = 0.5 + 0.45 * Math.sin( time * 1.3 + n.p );
252			ctx.fillStyle = 'rgba(' + RGB + ',' + ( 0.4 + g * 0.5 ).toFixed( 3 ) + ')';
253			ctx.beginPath();
254			ctx.arc( n.x, n.y, 2.1 + g * 0.9, 0, Math.PI * 2 );
255			ctx.fill();
256		} );
257	}
258
259	/**
260	 * Route + wheel (mobility) — port of TWGWireframeRoute, export-westly-v2/twg/viz.
260jsx.
261	 *
262	 * A rotating six-spoke wheel over a travelling dotted path.
263	 */
264	function drawRoute( ctx, size, getTime ) {
265		var time = getTime();
266		var cx = size / 2, cy = size / 2, R = size * 0.3;
267
268		ctx.clearRect( 0, 0, size, size );
269		ctx.lineWidth = 1;
270
271		ctx.strokeStyle = 'rgba(' + RGB + ',0.42)';
272		ctx.beginPath(); ctx.arc( cx, cy, R, 0, Math.PI * 2 ); ctx.stroke();
273		ctx.beginPath(); ctx.arc( cx, cy, R * 0.34, 0, Math.PI * 2 ); ctx.stroke();
274
275		for ( var i = 0; i < 6; i++ ) {
276			var a = time + i * Math.PI / 3;
277			ctx.strokeStyle = 'rgba(' + RGB + ',' + ( 0.2 + 0.28 * Math.abs( Math.cos( a ) ) ).toFixed( 3 ) + ')';
278			ctx.beginPath();
279			ctx.moveTo( cx + Math.cos( a ) * R * 0.34, cy + Math.sin( a ) * R * 0.34 );
280			ctx.lineTo( cx + Math.cos( a ) * R, cy + Math.sin( a ) * R );
281			ctx.stroke();
282		}
283
284		var py = size * 0.88;
285		ctx.strokeStyle = 'rgba(' + RGB + ',0.18)';
286		ctx.beginPath();
287		ctx.moveTo( size * 0.08, py );
288		ctx.lineTo( size * 0.92, py );
289		ctx.stroke();
290
291		for ( var d = 0; d < 3; d++ ) {
292			var t = ( time * 0.22 + d / 3 ) % 1;
293			ctx.fillStyle = 'rgba(' + RGB + ',' + ( 0.65 - Math.abs( t - 0.5 ) * 0.7 ).toFixed( 3 ) + ')';
294			ctx.beginPath();
295			ctx.arc( size * 0.08 + t * size * 0.84, py, 1.9, 0, Math.PI * 2 );
296			ctx.fill();
297		}
298	}
299
300	var DRAWERS = {
301		cube: { fn: drawCube, slow: 0.004, fast: 0.012 },
302		wave: { fn: drawWave, slow: 0.015, fast: 0.04 },
303		orbital: { fn: drawOrbital, slow: 0.004, fast: 0.012 },
304		shield: { fn: drawShield, slow: 0.012, fast: 0.03 },
305		neural: { fn: drawNeural, slow: 0.018, fast: 0.05 },
306		route: { fn: drawRoute, slow: 0.016, fast: 0.045 },
307	};
308
309	function initCanvas( canvas ) {
310		var kind = canvas.getAttribute( 'data-wf' );
311		var spec = DRAWERS[ kind ];
312		if ( ! spec ) {
313			return;
314		}
315		var ctx = canvas.getContext( '2d' );
316		if ( ! ctx ) {
317			return;
318		}
319
320		// Animates for EVERY visitor, whatever their OS "Reduce Motion" setting — owner decision,
321		// 2026-09-15, matching the design, which never checks it. (Earlier versions returned
322		// before drawing, then drew a still frame, when prefers-reduced-motion matched.)
323
324		var size = canvas.offsetWidth || 64;
325		var dpr = window.devicePixelRatio || 1;
326		canvas.width = size * dpr;
327		canvas.height = size * dpr;
328		ctx.scale( dpr, dpr );
329
330		var hovered = false;
331		var time = 0;
332		var animId;
333
334		canvas.addEventListener( 'mouseenter', function () { hovered = true; } );
335		canvas.addEventListener( 'mouseleave', function () { hovered = false; } );
336
337		function tick() {
338			time += hovered ? spec.fast : spec.slow;
339			spec.fn( ctx, size, function () { return time; } );
340			animId = window.requestAnimationFrame( tick );
341		}
342		tick();
343
344		return function stop() { window.cancelAnimationFrame( animId ); };
345	}
346
347	function init() {
348		var canvases = document.querySelectorAll( '.sector-wireframe' );
349		for ( var i = 0; i < canvases.length; i++ ) {
350			initCanvas( canvases[ i ] );
351		}
352	}
353
354	if ( document.readyState === 'loading' ) {
355		document.addEventListener( 'DOMContentLoaded', init );
356	} else {
357		init();
358	}
359} )();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.