PageSourceSearch

https://cstimer.net/src/js/tools/image.js

js cstimer.net collected 2026-10-03 23:10:58 UTC 31,538 bytes, 1,084 lines download raw bytes

1"use strict";
2
3var image = (function() {
4
5	var img;
6	var hsq3 = Math.sqrt(3) / 2;
7	var PI = Math.PI;
8
9	var Rotate = $.ctxRotate;
10	var Transform = $.ctxTransform;
11	var drawPolygon = $.ctxDrawPolygon;
12
13	var clkImage = (function() {
14		function drawClock(svg, color, trans, time) {
15			var points = Transform(Rotate([
16				[1, 1, 0, -1, -1, -1, 1, 0],
17				[0, -1, -8, -1, 0, 1, 1, 0]
18			], time / 6 * PI), trans);
19			var x = points[0];
20			var y = points[1];
21			svg.addElem('<circle cx="' + x[7] + '" cy="' + y[7] + '" r="' + trans[0] * 9 + '" style="fill:' + color + '" />');
22			var path = [];
23			path.push('M' + x[0] + ' ' + y[0]);
24			path.push('Q' + x[1] + ' ' + y[1] + ',' + x[2] + ' ' + y[2]);
25			path.push('Q' + x[3] + ' ' + y[3] + ',' + x[4] + ' ' + y[4]);
26			path.push('C' + x[5] + ' ' + y[5] + ',' + x[6] + ' ' + y[6] + ',' + x[0] + ' ' + y[0]);
27			svg.addElem('<path d="' + path.join(' ') + '" style="fill:' + colors[3] + ';stroke:' + colors[0] + '" />');
28		}
29
30		function drawButton(svg, color, trans) {
31			var points = Transform([[0],[0]], trans);
32			svg.addElem('<circle cx="' + points[0][0] + '" cy="' + points[1][0] +
33				'" r="' + trans[0] * 3 + '" style="fill:' + color + ';stroke:#000;" />');
34		}
35
36		var width = 3;
37		var movere = /([UD][RL]|ALL|[UDRLy]|all)(?:(\d[+-]?)|\((\d[+-]?),(\d[+-]?)\))?/
38		var movestr = ['UR', 'DR', 'DL', 'UL', 'U', 'R', 'D', 'L', 'ALL']
39		var colors = ['#f00', '#37b', '#5cf', '#ff0', '#850'];
40
41		return function(svg, moveseq) {
42			colors = kernel.getProp('colclk').match(colre);
43			var moves = moveseq.split(/\s+/);
44			var moveArr = clock.moveArr;
45			var flip = 9;
46			var buttons = [0, 0, 0, 0];
47			var clks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
48			for (var i = 0; i < moves.length; i++) {
49				var m = movere.exec(moves[i]);
50				if (!m) {
51					continue;
52				}
53				if (m[0] == 'y2') {
54					flip = 9 - flip;
55					continue;
56				}
57				var axis = movestr.indexOf(m[1]) + flip;
58				if (m[2] == undefined && m[3] == undefined) {
59					buttons[axis % 9] = 1;
60					continue;
61				}
62				var power;
63				var actions = [];
64				if (m[1] == 'all') {
65					power = ~~m[2][0] * (m[2][1] == '+' ? -1 : 1) + 12;
66					actions.push(8 + 9 - flip, power);
67				} else if (m[2]) {
68					power = ~~m[2][0] * (m[2][1] == '+' ? 1 : -1) + 12;
69					actions.push(axis, power);
70				} else {
71					power = ~~m[3][0] * (m[3][1] == '+' ? 1 : -1) + 12;
72					actions.push(axis, power);
73					power = ~~m[4][0] * (m[4][1] == '+' ? -1 : 1) + 12;
74					axis = (10 - axis % 9) % 4 + 4 + 9 - flip;
75					actions.push(axis, power);
76				}
77				for (var k = 0; k < actions.length; k += 2) {
78					for (var j = 0; j < 14; j++) {
79						clks[j] = (clks[j] + moveArr[actions[k]][j] * actions[k + 1]) % 12;
80					}
81				}
82			}
83			clks = [clks[0], clks[3], clks[6], clks[1], clks[4], clks[7], clks[2], clks[5], clks[8],
84				12 - clks[2], clks[10], 12 - clks[8], clks[9], clks[11], clks[13], 12 - clks[0], clks[12], 12 - clks[6]
85			];
86			buttons = [buttons[3], buttons[2], buttons[0], buttons[1], 1 - buttons[0], 1 - buttons[1], 1 - buttons[3], 1 - buttons[2]];
87
88			svg.width = 6.25 * 20 * width;
89			svg.height = 3 * 20 * width;
90
91			var y = [10, 30, 50];
92			var x = [10, 30, 50, 75, 95, 115];
93			for (var ii = 0; ii < 18; ii++) {
94				var i = (ii + flip) % 18;
95				drawClock(svg, [colors[1], colors[2]][~~(ii / 9)], [width, x[~~(i / 3)], y[i % 3]], clks[ii]);
96			}
97
98			y = [20, 40];
99			x = [20, 40, 85, 105];
100			for (var i = 0; i < 8; i++) {
101				drawButton(svg, [colors[4], colors[3]][buttons[i]], [width, x[~~(i / 2)], y[i % 2]]);
102			}
103		};
104	})();
105
106	var sq1Image = (function() {
107
108		var sqa = hsq3 + 1;
109		var sqb = sqa * Math.sqrt(2);
110		var ep = [
111			[0, -0.5, 0.5],
112			[0, -sqa, -sqa]
113		];
114		var cp = [
115			[0, -0.5, -sqa, -sqa],
116			[0, -sqa, -sqa, -0.5]
117		];
118		var cpr = [
119			[0, -0.5, -sqa],
120			[0, -sqa, -sqa]
121		];
122		var cpl = [
123			[0, -sqa, -sqa],
124			[0, -sqa, -0.5]
125		];
126
127		var eps = Transform(ep, [0.66, 0, 0]);
128		var cps = Transform(cp, [0.66, 0, 0]);
129		var cprs = Transform(cpr, [0.66, 0, 0]);
130		var cpls = Transform(cpl, [0.66, 0, 0]);
131
132		var udcol = 'UD';
133		var ecol = 'R-B-L-F-F-L-B-R-';
134		var ccol = 'RBBLLFFRRFFLLBBR';
135		var colors = {
136			'U': '#ff0',
137			'R': '#f80',
138			'F': '#0f0',
139			'D': '#fff',
140			'L': '#f00',
141			'B': '#00f'
142		};
143
144		var width = 45;
145
146		var movere = /^\s*\(\s*(-?\d+),\s*(-?\d+)\s*\)\s*$/;
147
148		function doMove(move, sc) {
149			if (move[0] != 0) {
150				sc.doMove(move[0]);
151			}
152			if (move[1] != 0) {
153				sc.doMove(-move[1]);
154			}
155			if (move[2] != 0) {
156				sc.doMove(0);
157			}
158		}
159
160		function drawPosit(svg, sc, colors, sq2sc, half) {
161			for (var i = 0; i < (half ? 12 : 24); i++) {
162				var trans = i < 12 ? [width, sqb, sqb] : [width, sqb * 3, sqb];
163				var val = sc.pieceAt(i);
164				var colorUD = colors[udcol[val >= 8 ? 1 : 0]];
165				var cRot = -(i < 12 ? (i - 1) : (i - 6)) * PI / 6;
166				var eRot = -(i < 12 ? (i) : (i - 5)) * PI / 6;
167				if (val % 2 == 1) { // corner
168					if (sq2sc) {
169						var cLR = sq2sc.pieceAt(i) & 1;
170						cRot += cLR ? 0 : PI / 6;
171						drawPolygon(svg, colors[ccol[val - cLR]],
172							Rotate(cLR ? cpr : cpl, cRot), trans);
173						drawPolygon(svg, colorUD,
174							Rotate(cLR ? cprs : cpls, cRot), trans);
175					} else {
176						drawPolygon(svg, colors[ccol[val - 1]],
177							Rotate(cpr, cRot), trans);
178						drawPolygon(svg, colors[ccol[val]],
179							Rotate(cpl, cRot), trans);
180						drawPolygon(svg, colorUD,
181							Rotate(cps, cRot), trans);
182						i++;
183					}
184				} else { // edge
185					drawPolygon(svg, colors[ecol[val]],
186						Rotate(ep, eRot), trans);
187					drawPolygon(svg, colorUD,
188						Rotate(eps, eRot), trans);
189				}
190			}
191		}
192
193		function llImage(sc, sq2sc, img) {
194			var svg = new $.svg();
195			var cols = kernel.getProp('colsq1').match(colre);
196			colors = {
197				'U': cols[0],
198				'R': cols[1],
199				'F': cols[2],
200				'D': cols[3],
201				'L': cols[4],
202				'B': cols[5]
203			};
204			svg.width = 2 * sqb * width;
205			svg.height = 2 * sqb * width;
206			drawPosit(svg, sc, colors, sq2sc, true);
207			if (img) {
208				img.attr('src', 'data:image/svg+xml;base64,' + btoa(svg.render()));
209			}
210			return svg;
211		}
212
213		function scrImage(svg, moveseq, isSQ2) {
214			var cols = kernel.getProp('colsq1').match(colre);
215			colors = {
216				'U': cols[0],
217				'R': cols[1],
218				'F': cols[2],
219				'D': cols[3],
220				'L': cols[4],
221				'B': cols[5]
222			};
223			var sc = new sq1.SqCubie();
224			var sq2sc = null;
225			if (isSQ2) {
226				sq2sc = new sq1.SqCubie();
227				sq2sc.ul = sq2sc.ur = 0x010010;
228				sq2sc.dl = sq2sc.dr = 0x100100;
229			}
230			var moves = moveseq.split('/');
231			var tomove = [];
232			for (var i = 0; i < moves.length; i++) {
233				if (/^\s*$/.exec(moves[i])) {
234					tomove.push([0, 0, 1]);
235					continue;
236				}
237				var m = movere.exec(moves[i]);
238				tomove.push([(~~m[1] + 12) % 12, (~~m[2] + 12) % 12, 1]);
239			}
240			tomove.push([0, 0, 1]);
241			for (var i = 0; i < tomove.length; i++) {
242				doMove(tomove[i], sc);
243				sq2sc && doMove(tomove[i], sq2sc);
244			}
245
246			svg.width = 4 * sqb * width;
247			svg.height = 2 * sqb * width;
248
249			//draw middle
250			for (var i = 0; i < 2; i++) {
251				var trans = i == 0 ? [width, sqb, sqb + sqa] : [width, sqb * 3, sqb - sqa - 0.7];
252				drawPolygon(svg, colors['L'], [[-sqa, -sqa, -0.5, -0.5], [0, 0.7, 0.7, 0]], trans);
253				if (sc.ml == 0) {
254					drawPolygon(svg, colors['L'], [[sqa, sqa, -0.5, -0.5], [0, 0.7, 0.7, 0]], trans);
255				} else {
256					drawPolygon(svg, colors['R'], [[hsq3, hsq3, -0.5, -0.5], [0, 0.7, 0.7, 0]], trans);
257				}
258			}
259			//draw top and bottom
260			drawPosit(svg, sc, colors, sq2sc);
261
262			var recons = [];
263			for (var i = 0; i < moves.length; i++) {
264				if (/^\s*$/.exec(moves[i])) {
265					recons.push('/');
266					continue;
267				}
268				var m = movere.exec(moves[i]);
269				if (~~m[1]) {
270					recons.push('(' + m[1] + ',0)');
271				}
272				if (~~m[2]) {
273					recons.push('(0,' + m[2] + ')');
274				}
275				recons.push('/');
276			}
277			if (recons.at(-1) == '/') {
278				recons.pop();
279			} else {
280				recons.push('/');
281			}
282			return ["~", recons, 'sq1'];
283		}
284
285		return {
286			scrImage: scrImage,
287			llImage: llImage
288		}
289	})();
290
291	var nnnImage = (function() {
292		var width = 30;
293
294		var posit = [];
295		var colors = ['#ff0', '#fa0', '#00f', '#fff', '#f00', '#0d0'];
296
297		function face(svg, f, size) {
298			var offx = 10 / 9,
299				offy = 10 / 9;
300			if (f == 0) { //D
301				offx *= size;
302				offy *= size * 2;
303			} else if (f == 1) { //L
304				offx *= 0;
305				offy *= size;
306			} else if (f == 2) { //B
307				offx *= size * 3;
308				offy *= size;
309			} else if (f == 3) { //U
310				offx *= size;
311				offy *= 0;
312			} else if (f == 4) { //R
313				offx *= size * 2;
314				offy *= size;
315			} else if (f == 5) { //F
316				offx *= size;
317				offy *= size;
318			}
319
320			for (var i = 0; i < size; i++) {
321				var x = (f == 1 || f == 2) ? size - 1 - i : i;
322				for (var j = 0; j < size; j++) {
323					var y = (f == 0) ? size - 1 - j : j;
324					drawPolygon(svg, colors[posit[(f * size + y) * size + x]], [
325						[i, i, i + 1, i + 1],
326						[j, j + 1, j + 1, j]
327					], [width, offx + 0.1, offy + 0.1]);
328				}
329			}
330		}
331
332		/**
333		 *  f: face, [ D L B U R F ]
334		 *  d: which slice, in [0, size-1)
335		 *  q: [  2 ']
336		 */
337		function doslice(f, d, q, size) {
338			var f1, f2, f3, f4;
339			var s2 = size * size;
340			var c, i, j, k;
341			if (f > 5) f -= 6;
342			for (k = 0; k < q; k++) {
343				for (i = 0; i < size; i++) {
344					if (f == 0) {
345						f1 = 6 * s2 - size * d - size + i;
346						f2 = 2 * s2 - size * d - 1 - i;
347						f3 = 3 * s2 - size * d - 1 - i;
348						f4 = 5 * s2 - size * d - size + i;
349					} else if (f == 1) {
350						f1 = 3 * s2 + d + size * i;
351						f2 = 3 * s2 + d - size * (i + 1);
352						f3 = s2 + d - size * (i + 1);
353						f4 = 5 * s2 + d + size * i;
354					} else if (f == 2) {
355						f1 = 3 * s2 + d * size + i;
356						f2 = 4 * s2 + size - 1 - d + size * i;
357						f3 = d * size + size - 1 - i;
358						f4 = 2 * s2 - 1 - d - size * i;
359					} else if (f == 3) {
360						f1 = 4 * s2 + d * size + size - 1 - i;
361						f2 = 2 * s2 + d * size + i;
362						f3 = s2 + d * size + i;
363						f4 = 5 * s2 + d * size + size - 1 - i;
364					} else if (f == 4) {
365						f1 = 6 * s2 - 1 - d - size * i;
366						f2 = size - 1 - d + size * i;
367						f3 = 2 * s2 + size - 1 - d + size * i;
368						f4 = 4 * s2 - 1 - d - size * i;
369					} else if (f == 5) {
370						f1 = 4 * s2 - size - d * size + i;
371						f2 = 2 * s2 - size + d - size * i;
372						f3 = s2 - 1 - d * size - i;
373						f4 = 4 * s2 + d + size * i;
374					}
375					c = posit[f1];
376					posit[f1] = posit[f2];
377					posit[f2] = posit[f3];
378					posit[f3] = posit[f4];
379					posit[f4] = c;
380				}
381				if (d == 0) {
382					for (i = 0; i + i < size; i++) {
383						for (j = 0; j + j < size - 1; j++) {
384							f1 = f * s2 + i + j * size;
385							f3 = f * s2 + (size - 1 - i) + (size - 1 - j) * size;
386							if (f < 3) {
387								f2 = f * s2 + (size - 1 - j) + i * size;
388								f4 = f * s2 + j + (size - 1 - i) * size;
389							} else {
390								f4 = f * s2 + (size - 1 - j) + i * size;
391								f2 = f * s2 + j + (size - 1 - i) * size;
392							}
393							c = posit[f1];
394							posit[f1] = posit[f2];
395							posit[f2] = posit[f3];
396							posit[f3] = posit[f4];
397							posit[f4] = c;
398						}
399					}
400				}
401			}
402		}
403
404		function genPosit(size, moveseq) {
405			var cnt = 0;
406			posit = [];
407			for (var i = 0; i < 6; i++) {
408				for (var f = 0; f < size * size; f++) {
409					posit[cnt++] = i;
410				}
411			}
412			var moves = cubeutil.parseScramble(moveseq, "DLBURF", true);
413			for (var s = 0; s < moves.length; s++) {
414				for (var d = 0; d < moves[s][1]; d++) {
415					doslice(moves[s][0], d, moves[s][2], size)
416				}
417				if (moves[s][1] == -1) {
418					for (var d = 0; d < size - 1; d++) {
419						doslice(moves[s][0], d, -moves[s][2], size);
420					}
421					doslice((moves[s][0] + 3) % 6, 0, moves[s][2] + 4, size);
422				}
423			}
424			return posit;
425		}
426
427		function draw(svg, size, moveseq) {
428			genPosit(size, moveseq);
429
430			svg.width = (39 * size / 9 + 0.2) * width;
431			svg.height = (29 * size / 9 + 0.2) * width;
432
433			colors = kernel.getProp('colcube').match(colre);
434			for (var i = 0; i < 6; i++) {
435				face(svg, i, size);
436			}
437
438			var moves = moveseq.split(/\s+/);
439			var recons = [];
440			for (var i = 0; i < moves.length; i++) {
441				if (moves[i]) {
442					recons.push(moves[i]);
443				}
444			}
445			return ["~", recons, [size, size, size].join('')];
446		}
447
448		return {
449			draw: draw,
450			genPosit: genPosit
451		}
452	})();
453
454	var mrblImage = (function() {
455		// image for mirror block
456		var width = 30;
457		var posit = [];
458		var colors = [];
459
460		function getBoundOffset(f, size, i, j) {
461			if (i != 0 && i != size - 1 && j != 0 && j != size - 1) {
462				return [-1, -1, -1, -1];
463			}
464			//   0
465			// 2   3
466			//   1
467			var D = 0, L = 4, B = 8, U = 12, R = 16, F = 20;
468			var neighbor = [
469				[F + 1, B + 1, L + 1, R + 1],
470				[U + 2, D + 2, B + 3, F + 2],
471				[U + 0, D + 1, R + 3, L + 2],
472				[B + 0, F + 0, L + 0, R + 0],
473				[U + 3, D + 3, F + 3, B + 2],
474				[U + 1, D + 0, L + 3, R + 2]
475			][f];
476			var isBound = [i == 0, i == size - 1, j == 0, j == size - 1];
477			var ret = [-1, -1, -1, -1];
478			for (var i1 = 0; i1 < 4; i1++) {
479				if (!isBound[i1]) {
480					continue;
481				}
482				var idx = [size - 1 - j, j, i, size - 1 - i][i1];
483				var rij = [[0, idx], [size - 1, size - 1 - idx], [size - 1 - idx, 0], [idx, size - 1]][neighbor[i1] & 0x3];
484				var fidx = neighbor[i1] >> 2;
485				if (fidx == 1 || fidx == 2) {
486					rij[1] = size - 1 - rij[1];
487				}
488				if (fidx == 0) {
489					rij[0] = size - 1 - rij[0];
490				}
491				ret[i1] = posit[fidx * size * size + rij[0] * size + rij[1]];
492			}
493			return ret;
494		}
495
496		function face(svg, f, size) {
497			var offx = (size + 1) / size,
498				offy = (size + 1) / size;
499			if (f == 0) { //D
500				offx *= size;
501				offy *= size * 2;
502			} else if (f == 1) { //L
503				offx *= 0;
504				offy *= size;
505			} else if (f == 2) { //B
506				offx *= size * 3;
507				offy *= size;
508			} else if (f == 3) { //U
509				offx *= size;
510				offy *= 0;
511			} else if (f == 4) { //R
512				offx *= size * 2;
513				offy *= size;
514			} else if (f == 5) { //F
515				offx *= size;
516				offy *= size;
517			}
518			var heights = [0.45, 0.15, 0.3, -0.45, -0.15, -0.3];
519			for (var i = 0; i < size; i++) {
520				var x = (f == 1 || f == 2) ? size - 1 - i : i;
521				for (var j = 0; j < size; j++) {
522					var y = (f == 0) ? size - 1 - j : j;
523					var off = getBoundOffset(f, size, j, i);
524					for (var i1 = 0; i1 < 4; i1++) {
525						off[i1] = off[i1] == -1 ? 0 : heights[off[i1]];
526					}
527					drawPolygon(svg, colors[posit[(f * size + y) * size + x]], [
528						[i - off[2], i - off[2], i + 1 + off[3], i + 1 + off[3]],
529						[j - off[0], j + 1 + off[1], j + 1 + off[1], j - off[0]]
530					], [width, offx + 0.6, offy + 0.6]);
531				}
532			}
533		}
534
535		return function(svg, size, moveseq) {
536			posit = nnnImage.genPosit(size, moveseq);
537			svg.width = (4 * size + 4 + 0.2) * width;
538			svg.height = (3 * size + 3 + 0.2) * width;
539			colors = kernel.getProp('colcube').match(colre);
540			for (var i = 0; i < 6; i++) {
541				face(svg, i, size);
542			}
543		}
544	})();
545
546	var gearImage = (function() {
547
548		var moveMaps = [
549			[3, 2, 1, 0, 4, 11, 12, 13, 14, 15, 16, 5, 6, 7, 8, 9, 10, 68, 69, 19, 20, 89, 73, 74, 75, 94, 95, 82, 28, 29, 30, 100, 101, 25, 85, 86, 36, 37, 21, 90, 91, 92, 26, 27, 99, 45, 46, 47, 32, 33, 42, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 17, 18, 70, 71, 38, 22, 23, 24, 43, 44, 31, 79, 80, 81, 49, 50, 76, 34, 35, 87, 88, 72, 39, 40, 41, 77, 78, 48, 96, 97, 98, 83, 84, 93],
550			[0, 52, 2, 54, 38, 40, 41, 11, 59, 60, 61, 46, 47, 56, 14, 15, 16, 20, 19, 18, 17, 21, 28, 29, 30, 31, 32, 33, 22, 23, 24, 25, 26, 27, 34, 87, 36, 85, 55, 57, 58, 45, 99, 100, 101, 63, 64, 96, 48, 49, 50, 51, 1, 53, 3, 89, 97, 98, 62, 8, 9, 10, 91, 92, 5, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 37, 86, 35, 88, 4, 12, 13, 39, 93, 94, 95, 6, 7, 90, 42, 43, 44],
551			[0, 1, 52, 51, 72, 5, 6, 7, 74, 75, 14, 56, 57, 58, 80, 81, 65, 71, 18, 69, 20, 4, 15, 16, 73, 25, 26, 27, 9, 10, 22, 76, 77, 78, 37, 36, 35, 34, 38, 45, 46, 47, 48, 49, 50, 39, 40, 41, 42, 43, 44, 3, 2, 53, 54, 21, 11, 12, 13, 23, 24, 8, 62, 63, 64, 29, 30, 59, 68, 19, 70, 17, 55, 66, 67, 79, 31, 32, 33, 60, 61, 28, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101]
552		];
553
554		return function(svg, moveseq) {
555			var width = 30;
556			svg.width = (39 * 3 / 9 + 0.2) * width;
557			svg.height = (29 * 3 / 9 + 0.2) * width;
558			var state = [];
559			for (var i = 0; i < 17 * 6; i++) {
560				state[i] = ~~(i / 17);
561			}
562			moveseq.replace(/([URF])([23456]?)('?)/g, function(m, g1, g2, g3) {
563				var move = moveMaps["URF".indexOf(g1)];
564				var pow = ~~g2 || 1;
565				pow = g3 ? (12 - pow) : pow;
566				for (var p = 0; p < pow; p++) {
567					var tmp = [];
568					for (var i = 0; i < 17 * 6; i++) {
569						tmp[i] = state[move[i]];
570					}
571					state = tmp;
572				}
573			});
574			var colors = kernel.getProp('colcube').match(colre);
575			for (var f = 0; f < 6; f++) {
576				var offx = 10 / 3 * [1, 2, 1, 1, 0, 3][f],
577					offy = 10 / 3 * [0, 1, 1, 2, 1, 1][f];
578				for (var s = 0; s < 5; s++) {
579					var x = [0, 2, 0, 2, 1][s],
580						y = [0, 0, 2, 2, 1][s];
581					drawPolygon(svg, colors[(state[f * 17 + s] + 3) % 6], [
582						[x, x, x + 1, x + 1],
583						[y, y + 1, y + 1, y]
584					], [width, offx + 0.1, offy + 0.1]);
585				}
586				for (var s = 0; s < 4; s++) {
587					var c1 = state[f * 17 + s * 3 + 5];
588					var c2 = state[f * 17 + s * 3 + 6];
589					var c3 = state[f * 17 + s * 3 + 7];
590					var rot = [
591						[1, 0, 0, 0, 1, 0],
592						[0, -1, 3, 1, 0, 0],
593						[-1, 0, 3, 0, -1, 3],
594						[0, 1, 0, -1, 0, 3]
595					][s];
596					if (c1 == c2 && c2 == c3) {
597						drawPolygon(svg, colors[(c1 + 3) % 6], Transform([
598							[1, 1, 2, 2],
599							[0, 1, 1, 0]
600						], rot), [width, offx + 0.1, offy + 0.1]);
601					} else if (c1 == c2) {
602						drawPolygon(svg, colors[(c1 + 3) % 6], Transform([
603							[1, 1, 2, 1.5],
604							[0, 1, 1, 0]
605						], rot), [width, offx + 0.1, offy + 0.1]);
606						drawPolygon(svg, colors[(c3 + 3) % 6], Transform([
607							[1.5, 2, 2],
608							[0, 1, 0]
609						], rot), [width, offx + 0.1, offy + 0.1]);
610					} else {
611						drawPolygon(svg, colors[(c1 + 3) % 6], Transform([
612							[1, 1, 1.5],
613							[0, 1, 0]
614						], rot), [width, offx + 0.1, offy + 0.1]);
615						drawPolygon(svg, colors[(c3 + 3) % 6], Transform([
616							[1.5, 1, 2, 2],
617							[0, 1, 1, 0]
618						], rot), [width, offx + 0.1, offy + 0.1]);
619					}
620				}
621			}
622			return svg;
623		}
624	})();
625
626	/**
627	 *	last layer image
628	 *	pieces = U1U2...U9F1..F3R1..L3
629	 *	   B3 B2 B1
630	 *	L1 U1 U2 U3 R3
631	 *	L2 U4 U5 U6 R2
632	 *	L3 U7 U8 U9 R1
633	 *	   F1 F2 F3
634	 */
635	var llImage = (function() {
636		function drawImage(pieces, arrows, img) {
637			var svg = new $.svg();
638			var colors = kernel.getProp('colcube').match(colre);
639			var dim = 3;
640			if (pieces.length == 12) {
641				dim = 2;
642			} else if (pieces.length == 32) {
643				dim = 4;
644			}
645			var width = 50;
646			svg.width = (dim + 1.2) * width;
647			svg.height = (dim + 1.2) * width;
648			for (var i = 0; i < dim * dim; i++) {
649				var x = i % dim + 0.5;
650				var y = ~~(i / dim) + 0.5;
651				drawPolygon(svg, colors["DLBURF".indexOf(pieces[i])] || '#888', [
652					[x, x + 1, x + 1, x],
653					[y, y, y + 1, y + 1]
654				], [width, 0.1, 0.1]);
655			}
656			for (var i = 0; i < dim * 4; i++) {
657				var x = i % dim;
658				var rot = ~~(i / dim);
659				drawPolygon(svg, colors["DLBURF".indexOf(pieces[i + dim * dim])] || '#888', Rotate([
660					[x - dim / 2, x - dim / 2 + 1, (x - dim / 2 + 1) * 0.9, (x - dim / 2) * 0.9],
661					[dim / 2 + 0.05, dim / 2 + 0.05, dim / 2 + 0.5, dim / 2 + 0.5]
662				], -rot * PI / 2), [width, 0.6 + dim / 2, 0.6 + dim / 2]);
663			}
664			arrows = arrows || [];
665			for (var i = 0; i < arrows.length; i++) {
666				var arrow = arrows[i];
667				var x1, y1, x2, y2;
668				if (typeof arrow[0] === 'number') {
669					x1 = arrow[0] % dim + 1.1;
670					y1 = ~~(arrow[0] / dim) + 1.1;
671				} else {
672					x1 = arrow[0][0] + 1.1;
673					y1 = arrow[0][1] + 1.1;
674				}
675				if (typeof arrow[1] === 'number') {
676					x2 = arrow[1] % dim + 1.1;
677					y2 = ~~(arrow[1] / dim) + 1.1;
678				} else {
679					x2 = arrow[1][0] + 1.1;
680					y2 = arrow[1][1] + 1.1;
681				}
682				var length = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
683				drawPolygon(svg, '#000', Rotate([
684					[0.2, length - 0.4, length - 0.4, length - 0.1, length - 0.4, length - 0.4, 0.2],
685					[0.05, 0.05, 0.15, 0, -0.15, -0.05, -0.05]
686				], Math.atan2(y2 - y1, x2 - x1)), [width, x1, y1]);
687			}
688			if (img) {
689				img.attr('src', 'data:image/svg+xml;base64,' + btoa(svg.render()));
690			}
691			return svg;
692		}
693
694		function draw(size, moveseq, img) {
695			var state = nnnImage.genPosit(size, moveseq);
696			var pieces = [];
697			for (var i = 0; i < size * size; i++) {
698				pieces.push("DLBURF"[state[3 * size * size + i]]);
699			}
700			for (var j = 0; j < 4; j++) {
701				var offset = [5, 4, 2, 1][j] * size * size;
702				for (var i = 0; i < size; i++) {
703					var ii = [i, i, size - 1 - i, size - 1 - i][j];
704					pieces.push("DLBURF"[state[offset + ii]]);
705				}
706			}
707			return drawImage(pieces.join(''), [], img);
708		}
709
710		return {
711			drawImage: drawImage,
712			draw: draw
713		}
714	})();
715
716	/**
717	 *	cube image of URF faces
718	 *	pieces = U1U2...U9R1..R9F1..F9
719	 *	U1 U3 R3 R9
720	 *	U7 U9 R1 R7
721	 *	F1 F3
722	 * 	F7 F9
723	 */
724	var face3Image = (function() {
725		var width = 20;
726		var gap = 1;
727		var ftrans = [
728			[width * hsq3, -width * hsq3, (width * 3 + gap) * hsq3, width / 2, width / 2, 0],
729			[width * hsq3, 0, (width * 3 + gap * 2) * hsq3, -width / 2, width, width * 3 + gap * 1.5],
730			[width * hsq3, 0, 0, width / 2, width, width * 1.5 + gap * 1.5],
731		]
732		function drawImage(pieces, img) {
733			var svg = new $.svg();
734			var colors = kernel.getProp('colcube').match(colre);
735			svg.width = (6 * width + gap * 2) * hsq3;
736			svg.height = (6 * width + gap * 1.5);
737			for (var i = 0; i < 27; i++) {
738				var x = i % 3;
739				var y = ~~(i / 3) % 3;
740				drawPolygon(svg, colors["DLBURF".indexOf(pieces[i])] || '#888', [
741					[x, x + 1, x + 1, x],
742					[y, y, y + 1, y + 1]
743				], ftrans[~~(i / 9)]);
744			}
745			if (img) {
746				img.attr('src', 'data:image/svg+xml;base64,' + btoa(svg.render()));
747			}
748			return svg;
749		}
750		return drawImage;
751	})();
752
753	/**
754	 *  F1 R1 L1 F2 F3 F4 R L F5..
755	 */
756	var pyrllImage = (function() {
757		var width = 20;
758
759		function drawImage(pieces, img) {
760			var svg = new $.svg();
761			svg.width = 6 * hsq3 * width;
762			svg.height = 6 * hsq3 * width;
763			var colors = kernel.getProp('colpyr').match(colre);
764			var idx = 0;
765			for (var i = 0; i < 3; i++) {
766				for (var f = 0; f < 3; f++) {
767					for (var j = 0; j < (i * 2 + 1); j++) {
768						var piece;
769						var x = -hsq3 * i + hsq3 * j;
770						var y = i / 2;
771						if (j % 2 == 0) {
772							piece = [[x, x - hsq3, x + hsq3], [y, y + 0.5, y + 0.5]];
773						} else {
774							piece = [[x - hsq3, x, x + hsq3], [y, y + 0.5, y]];
775						}
776						drawPolygon(svg, colors["FLRD".indexOf(pieces[idx])] || '#888', Rotate(piece, PI / 3 * 4 * f), [width, 3 * hsq3, 3 + (6 * hsq3 - 4.5) / 2]);
777						idx++;
778					}
779				}
780			}
781			if (img) {
782				img.attr('src', 'data:image/svg+xml;base64,' + btoa(svg.render()));
783			}
784			return svg;
785		}
786		return drawImage;
787	})();
788
789	var polyhedronImage = (function() {
790		var puzzleCache = {};
791
792		return function(svg, type, moveseq, faceNameMask, minArea) {
793			var colors = [];
794			var moves = [];
795			minArea = minArea || 0;
796			var gap = 0.05;
797			var puzzle = puzzleCache[type];
798
799			var params = poly3d.getFamousPuzzle(type);
800			if (params != null) {
801				puzzle = puzzle || poly3d.makePuzzle.apply(poly3d, params.polyParam);
802				params.parser = params.parser || poly3d.makePuzzleParser(puzzle);
803				moves = params.parser.parseScramble(moveseq);
804				gap = params.pieceGap;
805				colors = params.colors;
806			} else {
807				debugger; // unknown puzzle
808			}
809
810			DEBUG && console.log('[polyhedron image] puzzle=', puzzle, 'moves=', moves);
811			puzzleCache[type] = puzzle;
812			var poly2d = poly3d.renderNet(puzzle, gap, minArea);
813			var sizes = poly2d[0];
814			var polys = poly2d[1];
815			var faces = poly2d[2];
816			var posit = [];
817			for (var i = 0; i < polys.length; i++) {
818				posit[i] = polys[i] && polys[i][2];
819			}
820			for (var midx = 0; midx < moves.length; midx++) {
821				var move = moves[midx];
822				var moveIdx = puzzle.getTwistyIdx(move[0]);
823				if (moveIdx == -1) {
824					debugger; // error, cannot find move permutations
825				}
826				var perm = puzzle.moveTable[moveIdx];
827				var maxPow = puzzle.twistyDetails[moveIdx][1];
828				var pow = (move[1] % maxPow + maxPow) % maxPow;
829				var posit2 = [];
830				for (var i = 0; i < posit.length; i++) {
831					var val = i;
832					for (var j = 0; j < pow; j++) {
833						val = perm[val] < 0 ? val : perm[val];
834					}
835					posit2[i] = posit[val];
836				}
837				posit = posit2;
838			}
839			if (type == 'skb') {
840				colors = $.col2std(kernel.getProp("colskb"), [0, 2, 4, 3, 5, 1])
841				var trans = [
842					[hsq3, -hsq3, hsq3 * 2, 0.5, 0.5, -1],
843					[hsq3, 0, 0, -0.5, 1, 2],
844					[hsq3, 0, 0, 0.5, 1, -2],
845					[hsq3, 0, 0, 0.5, 1, -2],
846					[hsq3, 0, 0, 0.5, 1, -2],
847					[hsq3, 0, 0, -0.5, 1, 2]
848				];
849				for (var i = 0; i < 6; i++) {
850					for (var j = 0; j < 6; j++) {
851						if (j % 3 != 2) {
852							trans[i][j] *= 8 / sizes[0];
853						}
854					}
855				}
856				for (var i = 0; i < polys.length; i++) {
857					if (!polys[i]) {
858						continue;
859					}
860					var poly = Transform(polys[i], trans[polys[i][2]]);
861					polys[i][0] = poly[0];
862					polys[i][1] = poly[1];
863				}
864				sizes = [8 * hsq3, 6];
865			}
866			var scale = Math.min(1.6 / sizes[0], 1.0 / sizes[1]) * 300;
867			svg.width = sizes[0] * scale;
868			svg.height = sizes[1] * scale;
869			for (var i = 0; i < colors.length; i++) {
870				colors[i] = '#' + colors[i].toString(16).padStart(6, '0');
871			}
872			for (var i = 0; i < posit.length; i++) {
873				polys[i] && drawPolygon(svg, colors[posit[i]], polys[i], [scale, 0, 0, 0, scale, 0]);
874			}
875			for (var i = 0; i < faces.length; i++) {
876				if ((faceNameMask >> i & 1) == 0) {
877					continue;
878				}
879				var face = faces[i];
880				svg.addText(face[2].toUpperCase(), [face[0] * scale, face[1] * scale], {
881					'font': '20px Arial',
882					'fill': kernel.getProp('col-font'),
883					'stroke': kernel.getProp('col-board'),
884					'stroke-width': '3px'
885				});
886			}
887			var recons = [];
888			for (var midx = 0; midx < moves.length; midx++) {
889				recons.push(params.parser.move2str(moves[midx]));
890			}
891			return ["~", recons, type];
892		}
893	})();
894
895
896	var sldImage = (function() {
897
898		return function(svg, type, size, moveseq) {
899			var width = 50;
900			var gap = 0.05;
901
902			var state = [];
903			var effect = [
904				[1, 0],
905				[0, 1],
906				[0, -1],
907				[-1, 0]
908			];
909			for (var i = 0; i < size * size; i++) {
910				state[i] = i;
911			}
912			var x = size - 1;
913			var y = size - 1;
914
915			var movere = /([ULRD\uFFEA\uFFE9\uFFEB\uFFEC])([\d]?)/;
916			moveseq = moveseq.split(' ');
917			for (var s = 0; s < moveseq.length; s++) {
918				var m = movere.exec(moveseq[s]);
919				if (!m) {
920					continue;
921				}
922				var turn = 'ULRD\uFFEA\uFFE9\uFFEB\uFFEC'.indexOf(m[1]) % 4;
923				var pow = ~~m[2] || 1;
924				var eff = effect[type == 'b' ? 3 - turn : turn];
925				for (var p = 0; p < pow; p++) {
926					mathlib.circle(state, x * size + y, (x + eff[0]) * size + y + eff[1]);
927					x += eff[0];
928					y += eff[1];
929				}
930			}
931
932			svg.width = (size + gap * 4) * width;
933			svg.height = (size + gap * 4) * width;
934
935			var cols = kernel.getProp('col15p').match(colre);
936			cols[size - 1] = cols.at(-1);
937			for (var i = 0; i < size; i++) {
938				for (var j = 0; j < size; j++) {
939					var val = state[j * size + i];
940					var colorIdx = Math.min(~~(val / size), val % size);
941					val++;
942					drawPolygon(svg, cols[colorIdx], [
943						[i + gap, i + gap, i + 1 - gap, i + 1 - gap],
944						[j + gap, j + 1 - gap, j + 1 - gap, j + gap]
945					], [width, gap * 2, gap * 2]);
946					if (val == size * size) {
947						continue;
948					}
949					svg.addText(val, [width * (i + 0.5 + gap * 2), width * (j + 0.5 + gap * 2)], {
950						'font': width * 0.6 + 'px Arial',
951						'fill': '#000'
952					});
953				}
954			}
955		}
956	})();
957
958	var types_nnn = ['222', '333', '444', '555', '666', '777', '888', '999', '101010', '111111'];
959
960	function renderSVG(svg, scramble) {
961		var type = scramble[0];
962		if (type == 'input') {
963			type = tools.scrambleType(scramble[1]);
964		}
965		type = tools.puzzleType(type);
966		var size = types_nnn.indexOf(type);
967		var recons = 0;
968		if (size >= 0) {
969			recons = nnnImage.draw(svg, size + 2, scramble[1]);
970		} else if (type == "cubennn") {
971			nnnImage.draw(svg, scramble[2], scramble[1]);
972		} else if (poly3d.udpolyre.exec(type)) {
973			var faceNameMask = 0;
974			if (/^prc|giga|mgm|klm$/.exec(type)) {
975				faceNameMask = 0x3;
976			} else if (type == 'fto') {
977				faceNameMask = 0xff;
978			} else if (type == 'ctico') {
979				faceNameMask = 0xfffff;
980			}
981			recons = polyhedronImage(svg, type, scramble[1], faceNameMask, type == 'klm' ? 0.1 : 0);
982		} else if (type == "sq1" || type == "sq2") {
983			recons = sq1Image.scrImage(svg, scramble[1], type == "sq2");
984		} else if (type == "clk") {
985			clkImage(svg, scramble[1]);
986		} else if (type == "gear") {
987			gearImage(svg, scramble[1]);
988		} else if (type == "mrbl") {
989			mrblImage(svg, 3, scramble[1]);
990		} else if (type == "15b" || type == "15p") {
991			sldImage(svg, type[2], 4, scramble[1]);
992		} else if (type == "8b" || type == "8p") {
993			sldImage(svg, type[1], 3, scramble[1]);
994		} else if (/^r(3(ni)?|23\d+w?|mngf)$/.exec(type)) {
995			var subScrs = [];
996			scramble[1].replaceAll(/(?:^|\n)\s*(\d+|3oh|pyr|skb|sq1|clk|mgm)\)\s*([^\0]*?)\s*(?=\n.*\)|$)/g, function(m, p1, p2) {
997				var subType = type.startsWith('r3') ? '333' :
998					type == 'rmngf' ? p1.replace('3oh', '333').padEnd(3, p1):
999					types_nnn[subScrs.length];
1000				subScrs.push([subType, p2, 0]);
1001			});
1002			var n_height = Math.ceil(Math.sqrt(subScrs.length));
1003			var n_width = Math.ceil(subScrs.length / n_height);
1004			var GRID_WIDTH = 240;
1005			var GRID_HEIGHT = 150;
1006			svg.width = n_width * GRID_WIDTH;
1007			svg.height = n_height * GRID_HEIGHT;
1008			for (var i = 0; i < subScrs.length; i++) {
1009				var x = i % n_width * GRID_WIDTH;
1010				var y = ~~(i / n_width) * GRID_HEIGHT;
1011				var subSvg = new $.svg();
1012				renderSVG(subSvg, subScrs[i]);
1013				svg.addElem(subSvg.renderGroup(x, y, GRID_WIDTH, GRID_HEIGHT));
1014				svg.addText((i + 1).toString(), [x, y], {
1015					'font': '40px Arial',
1016					'fill': kernel.getProp('col-font')
1017				}, 1);
1018			}
1019		} else {
1020			return -1;
1021		}
1022		return recons;
1023	}
1024
1025	function genImage(scramble, renderTool) {
1026		var svg = new $.svg();
1027		var recons = renderSVG(svg, scramble);
1028		if (recons == -1) { // not available
1029			return false;
1030		}
1031		if (!renderTool) {
1032			return svg;
1033		}
1034		var scale = Math.min(1.6 / svg.width, 1.0 / svg.height) * kernel.getProp('imgSize') * 0.6;
1035		img.attr('src', 'data:image/svg+xml;base64,' + btoa(svg.render()));
1036		img.width(svg.width * scale + 'em');
1037		img.height(svg.height * scale + 'em');
1038
1039		if (recons && kernel.getProp('imgRep')) {
1040			for (var i = 0; i < recons[1].length; i++) {
1041				recons[1][i] = recons[1][i] + '@' + (i + 1) * 1000;
1042			}
1043			recons[1] = recons[1].join(' ');
1044			img.click(function(recons) {
1045				replay.popupReplay.apply(null, recons);
1046			}.bind(null, recons));
1047		} else {
1048			img.click(function(svg, scale) {
1049				var popImg = $('<img style="display:block;">');
1050				popImg.attr('src', 'data:image/svg+xml;base64,' + btoa(svg.render()));
1051				popImg.css('object-fit', 'contain');
1052				kernel.showDialog([popImg, $.noop, undefined, $.noop], 'share', TOOLS_IMAGE);
1053			}.bind(null, svg, scale));
1054		}
1055		return true;
1056	}
1057
1058	ISCSTIMER && execMain(function() {
1059		function execFunc(fdiv) {
1060			if (!fdiv) {
1061				return;
1062			}
1063			img = img || $('<img style="display:block;">');
1064			fdiv.empty().append(img);
1065			if (!genImage(tools.getCurScramble(), true)) {
1066				fdiv.html(IMAGE_UNAVAILABLE);
1067			}
1068		}
1069
1070		$(function() {
1071			tools.regTool('image', TOOLS_IMAGE, execFunc);
1072		});
1073	});
1074
1075	var colre = /#[0-9a-fA-F]{3}/g;
1076
1077	return {
1078		draw: genImage,
1079		llImage: llImage,
1080		pyrllImage: pyrllImage,
1081		face3Image: face3Image,
1082		sqllImage: sq1Image.llImage
1083	}
1084})();

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.