latin: domino tile formatting and encoding
[sheet.git] / writing-latn.inc.pl
1 use 5.014;
2 use utf8;
3 use warnings;
4 use List::Util qw( pairs pairmap sum );
5
6 my %C = (
7         red    => '#EC1C24',
8         blue   => '#3953A3',
9         yellow => '#F9EC31',
10         black  => '#231F20',
11 );
12 my $U = 0;  # optional unicode alternatives
13
14 my @wrapstyle = (
15         'td { white-space: normal; word-spacing: 10em }',
16                 # force line break between words
17         '.sample { word-spacing: 0 }',
18         '.sample span { margin-right: 1ex; white-space: nowrap; display: inline-block }',
19                 # larger space between letters
20 );
21 my $spacestyle = '.sample span { margin-right: 0.5ex }';  # separate letters
22 my @tapstyle = (
23         @wrapstyle,
24         '{ line-height: 1ex }',
25         'td:not(.sample) { vertical-align: top }',
26         '.sample { font-size: 80% }',
27 );
28
29 my @hueorder = (
30         2,11,20,19,18,21,24,15,6,7,8,5,13, # red .. magenta, grey
31         1,10,9,12,3,4,0, 14,23,22,25,16,17,26, # dark, light hues
32 );
33
34 # Order to put similar sounds close to each other:
35 #         ┌ R Y G C B M X
36 #        ┌┼──────────────
37 #        W│ o e y h s f -
38 #         │muaixqgkdtbp l
39 #        K│ w n j c z v r
40
41 my @hueletters = ((26) x 27);
42 @hueletters[map { ord($_) - ord('a') } qw(
43         u a i x q g k d t b p m l   w n j c z v r   o e y h s f
44 )] = @hueorder;
45
46 sub disptap {
47         my $code = shift;
48         my ($prefix, @dots) = $code =~ m/\A(-?)(\d)(\d)/ or return $code;
49         if ($U) {
50                 # unicode glyph alternative as DOMINO TILE HORIZONTAL-0a-0b
51                 return $prefix . chr(0x1F031 + ($dots[0] * 7) + $dots[1]);
52         }
53         return $prefix . join(' ', map { '·' x $_ } @dots);
54 }
55
56 sub dispdomino {
57         my $code = shift;
58         my ($prefix, @dots) = $code =~ m/\A(-?)(\d)(\d)/ or return $code;
59         # unicode glyph alternative as DOMINO TILE HORIZONTAL-0a-0b
60         return $prefix . chr(0x1F031 + ($dots[0] * 7) + $dots[1]);
61 }
62
63 sub dispbar {
64         my $code = shift or return '';
65
66         return join '', pairmap {
67                 ($a =~ tr/123/❘❙❚/r) . ($b =~ tr/321/  /dr)
68         } split //, $code if $U;
69
70         my @cols = split //, $code;  # bar and space widths
71         my $width = sum(@cols);
72         return sprintf(
73                 '<svg width="%d" height="%d" viewBox="-.5 0 %d %d"><path d="%s"/></svg>',
74                 $width * 2, 14, $width, 7, join(' ',
75                         'M0,0',
76                         map {
77                                 join('m1,-7', ('v7') x $_->[0]),  # line per bar width
78                                 (map { sprintf 'm%d,-7', $_ + 1 } $_->[1] || ()),  # space forward
79                         }
80                         pairs @cols
81                 )
82         );
83 }
84
85 sub disphues {
86         my ($index, $hues, $opaque) = @_;
87         $index >= 0 or $index = 26;
88         my @lum = ($index % 3, $index / 3 % 3, $index / 9);  # hue opacities (0..2)x3
89         my @lumf = $opaque ? ('hsl(%s,100%%,50%%)', 'hsl(%s,100%%,25%%)') :
90                 ('hsl(%s,100%%,50%%)', 'hsla(%s,100%%,50%%,.5)');
91         return sprintf(
92                 '<svg width="16" height="16" viewBox="0 0 12 12">%s</svg>',
93                 join '', map {
94                         my $colf = $lumf[ $lum[$_] ];
95                         !$colf ? () : sprintf('<circle cx="%d" cy="%d" r="%d" fill="%s"/>',
96                                 5 + $_, $_ == 1 ? 7 : 5, 5, sprintf($colf, $hues->[$_])
97                         );
98                 } 0 .. 2
99         );
100 }
101
102 (
103 uppercase => {
104         list => [qw{ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z }],
105 },
106 lowercase => {
107         list => [qw{ a b c d e f g h i j k l m n o p q r s t u v w x y z }],
108 },
109 suetterlin => {
110         name => 'Sütterlin',
111         style => [
112                 '@font-face {
113                         font-family: Suetterlin; /* R. G. Arens */
114                         src: url("/suetterlin.ttf");
115                 }',
116                 'td { font-family: Suetterlin }',
117         ],
118         list => [qw{ a b c d e f g h i j k l m n o p q r ſ s t u v w x y z }],
119 },
120 roman => {
121         name => 'Old Roman Cursive',
122         style => [
123                 'svg path { stroke-linecap: round; stroke-linejoin: round }',
124                 '.sample span { margin-right: -10px }',
125         ],
126         list => [
127                 map {
128                         s{\A-?\K(\w.+)}
129                          {<svg width="20" height="20" viewBox="0 0 12 20"><path d="$1"/></svg>}r
130                 }
131                 "m2,4 c1,2 8,9 8,9 M2,15 6,9",
132                 "m2,4 c0,0 3,-2 4,1 1,2 0,9 3,9 1,-0 2,-1 2,-1 m-6,-2 c-5,4 -0,6 1,3",
133                 "m4,7 4,-2 m-4,4 c0,0 -2,7 3,6",
134                 "m3,2 c0,0 7,10 7,12 m-2,-4 c-5,2 -4,9 1,3",
135                 "m3,10 4,0 m2,-7 c0,0 -7,1 -5,16",
136                 "m4,11 5,-2 m-3,-4 7,-4 M0,18 c0,0 4,3 5,-3 2,-6 2,-10 2,-10",
137                 "m3,6 7,-2 m-7,4 c-2,5 4,9 6,6 l1,3",
138                 "m7,9 4,-0 m-8,0 c4,0 6,-1 5,6 M1,3 c2,-0 2,12 2,12",
139                 '>', # i = j
140                 "m6,8 -0,7",
141                 "-m9,5 -5,4 4,4 m-4,-10 -1,13",
142                 "m3,2 c0,0 -1,9 1,10 5,2 6,2 6,2",
143                 "m0,16 3,-10 4,6 2,-5 5,4",
144                 "m2,16 1,-9 5,8 2,-9",
145                 "m5,8 c-2,0 -2,6 1,5 4,-2 1,-5 1,-5",
146                 "m5,8 c0,0 -1,8 2,6 m-3,-7 5,3",
147                 "m3,6 9,12 m-9,-11 c-4,1 -3,4 -3,4 0,0 2,2 6,-1",
148                 "m0,6 c3,-1 3,-1 6,0 2,1 3,3 6,1 m-7,-1 -2,11",
149                 "m13,3 c0,0 -5,2 -8,4 -2,3 -1,5 -2,9 -1,1 -4,2 -4,2",
150                 "m2,7 8,0 m-4,1 c0,0 -1,8 3,5",
151                 '>', # u = v
152                 "m2,5 c0,0 3,5 6,3 3,-2 2,-3 2,-3",
153                 '-',
154                 "m3,19 c-1,-6 6,-17 6,-17 M1,8 c0,0 10,1 10,1",
155                 "-m0,7 c2,-3 5,-2 5,1 l0,11 c0,0 -0,-4 -0,-10 -0,-4 4,-4 6,-3",
156                 "-m3,6 c4,-1 3,3 3,3 -1,3 -2,5 -1,5 1,1 3,0 3,0",
157         ],
158 },
159 sutton => {
160         name => 'Sutton <abbr title="American Sign Lanugage">ASL</abbr>',
161         style => $spacestyle,
162         list => [
163                 # American manual alphabet in Sutton (U+1D800+) notation
164                 map { !!$_ && pack 'W*', map { hex "1D$_" } unpack '(A3)*', $_ } qw{
165                 8F7a9c    847a9c    86Da9c    801a9c    84Aa9c
166                 8CEa9c    8F0       815aa2    892a9c    892a9c9A2aac
167                 840a9c    8DCa9c    88Da9c
168                 819a9c    876a9c    840a9caA1 8F0a9caA1 81Aa9c
169                 903a9c    8FBa9c    815a9c    80Ea9c    887a9c
170                 806a9c    89Aa9c    800a9c945aaa
171                 0         965aa6
172         }],
173 },
174 unistrokes => {
175         name => 'Unistrokes',
176         url   => 'https://www.google.com/patents/US5596656', # by Xerox
177         style => 'svg path { stroke-linecap: round; stroke-linejoin: round }',
178         list => [
179                 map { '<svg width="14" height="16" viewBox="-1 -1 8 10">'.$_.'</svg>' }
180                 map {
181                         sprintf('<circle cx="%s" cy="%s" r="1"/>', m/\AM(\d+),(\d+)(.?)/) . # start point
182                         (!!$3 && qq(<path d="$_"/>))
183                 }
184                 'M3,8 V0',
185                 'M0,0 6,4 0,8',
186                 'M6,0 0,4 6,8',
187                 'M6,0 0,4 6,8',
188                 'M6,4 H0',
189                 'M6,0 0,0 0,8',
190                 'M0,8 6,8 6,0',
191                 'M0,0 6,0 6,8',
192                 'M3,0 V8',
193                 'M6,0 6,8 0,8',
194                 'M0,8 6,0',
195                 'M0,0 0,8 6,8',
196                 'M6,8 3,0 0,8',
197                 'M0,8 3,0 6,8',
198                 'M6,0 Q0,6 3,8 6,6 0,0',
199                 'M0,0 Q4,8 6,4 4,0 0,8',
200                 'M6,0 Q2,8 0,4 2,0 6,8',
201                 'M0,0 6,8',
202                 'M6,0 0,0 6,8 0,8',
203                 'M0,4 H6',
204                 'M6,0 3,8 0,0',
205                 'M0,0 3,8 6,0',
206                 'M0,0 0,8 6,0 6,8',
207                 'M0,0 Q6,6 3,8 0,6 6,0',
208                 'M6,0 0,8',
209                 'M0,0 6,0 0,8 6,8',
210                 'M3,4',
211         ],
212 },
213 edgewrite => {
214         name => 'EdgeWrite',
215         url   => 'http://depts.washington.edu/ewrite/', # patented US7729542
216         style => 'svg path { stroke-linecap: round; stroke-linejoin: round }',
217         list => [
218                 map { '<svg width="14" height="14" viewBox="-1 -1 10 10">'.$_.'</svg>' }
219                 map {
220                         my @route = split //;
221                         my @coords = map { $_ % 2 << 3, $_ >> 1 << 3 } @route; # x,y,
222                         sprintf('<circle cx="%s" cy="%s" r="1"/><path d="M%s"/>',
223                                 @coords[0, 1],  # start point
224                                 join(' ', map {
225                                         my $pos = join(',', @coords[$_*2, $_*2 + 1]);
226                                         $_ > 1 && $route[$_] == $route[$_ - 2] # curve back
227                                                 ? 'Q4,4 '.$pos.'L' : $pos
228                                 } 0 .. $#route),
229                         )
230                 }
231                 # corners (0..3) clockwise from top-left in order
232                 qw(
233                         213 0232 1023 1323 103 102 10132 0213 02 132 02123 023 20313 2031
234                         10231 0102 10131 201 1032 013 0231 021 02131 0312 0313 0123  01
235                 )
236         ],
237 },
238 #graffiti => {
239 #       name => 'Palm Graffiti',
240 #},
241 ita2 => {
242         name => '<abbr title="International Telegraph Alphabet">ITA</abbr>2',
243         style => [@wrapstyle, 'td { font-size: 50% }'],
244         list => [map { tr/01/○●/r =~ s/..\K/ /r } qw(
245                 11000 10011 01110 10010 10000 10110 01011 00101 01100 11010 11110 01001 00111
246                 00110 00011 01101 11101 01010 10100 00001 11100 01111 11001 10111 10101 10001
247                 00100
248         )],
249 },
250 moon => {
251         list => [
252                 map { qq(<svg width="14" height="14" viewBox="-.5 -.5 7 7"><path d="$_"/></svg>) }
253                 'M0,6 3,0 6,6',
254                 'M1,0 V4 A2,2 0,0,0 5,4',
255                 'M5,0 A4.5,3 0,0,0 5,6',
256                 'M1,0 A4.5,3 0,0,1 1,6',
257                 'M0,6 V0 H6',
258                 'M1,6 V2 A2,2 0,0,1 5,2',
259                 'M5,6 V2 A2,2 0,1,0 1,2',
260                 'M1.5,3 A1.5,1.5 0,0,0 4.5,3 1.5,1.5 0,0,0 1.5,3 M3,1.5 A1,1.5 0,0,0 3,4.5',
261                 'M3,0 V6',
262                 'M5,0 V4 A2,2 0,0,1 1,4',
263                 'M6,0 0,3 6,6',
264                 'M0,0 V6 H6',
265                 'M0,0 H6 V6',
266                 'M0,6 V2 L6,6 V0',
267                 'M0,3 A3,3 0,0,0 6,3 3,3 0,0,0 0,3',
268                 'M6,4 H2 A2,1 0,0,1 2,2',
269                 'M0,4 H4 A2,1 0,0,0 4,2',
270                 'M0,0 6,6',
271                 'M0,6 6,0',
272                 'M0,3 H6',
273                 'M0,0 V3 A3,3 0,0,0 6,3 V0',
274                 'M0,0 3,6 6,0',
275                 'M0,6 V3 A3,3 0,0,1 6,3 V6',
276                 'M0,0 6,3 0,6',
277                 'M6,0 V6 H0',
278                 'M0,0 H6 L2,6 H6',
279         ],
280         style => 'svg path { stroke-linecap: round; stroke-linejoin: round }',
281 },
282 braille => {
283         list => [qw{ ⠁ ⠃ ⠉ ⠙ ⠑ ⠋ ⠛ ⠓ ⠊ ⠚ ⠅ ⠇ ⠍ ⠝ ⠕ ⠏ ⠟ ⠗ ⠎ ⠞ ⠥ ⠧ ⠺ ⠭ ⠽ ⠵ }],
284 },
285 tactile => {
286         name => '5-point Tactile',
287         list => [
288                 map { '<svg width="9" height="12" viewBox="0 0 18 24">'.$_.'</svg>' }
289                 map {
290                         join '', map { sprintf '<circle cx="%d" cy="%d" r="4"/>',
291                                 !$_ ?  9 : $_ & 1 ? 4 : 14,
292                                 !$_ ? 12 : $_ < 3 ? 4 : 20,
293                         } split //
294                 }
295                 qw{
296                         4 234 012 14 0 014 023 12
297                         02 024 0134 23 013 03 01 123
298                         0124 13 04 1 34 0123 134 0234
299                         034 124
300                 }
301         ],
302 },
303 morse => {
304         style => $spacestyle,
305         list => [map {tr/.-/‧‑/r} qw{
306                 .- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. --
307                 -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --..
308         }],
309 },
310 tap => {
311         name => 'Tap code',
312         style => \@tapstyle,
313         list => [map { disptap($_) } qw{
314                 11 12 13 14 15 21 22 23  > 24 25 31 32
315                 33 34 35 41 42 43 44 45 51 52 53 54 55
316         }],
317 },
318 shorttap => {
319         name => 'Short Tap',
320         style => \@tapstyle,
321         list => [map { disptap($_) } qw{
322                 11 12 13 14 21 22 23 20 > 31 -13 32 33
323                 30 41 42 -13 43 40 10 51 52 53 50 -31 -40
324         }],
325         altlist => [map { disptap($_) } qw{
326                 10 14 -13 12 20 23 22 21 30 -34 13 33 32
327                 31 40 43 -13 42 41 11 50 -23 -50 -1341 -31 -41
328         }],
329 },
330 domino => {
331         name => 'Domino tiles',
332         list => [map { dispdomino($_) } qw{
333                 10 11 20 21 22 30 31 32 33 40 41 42 43
334                 44 50 51 52 53 54 55 60 61 62 63 64 65
335         }],
336 },
337 cards => {
338         style => 'td { font-family: Symbola, "DejaVu Sans", serif, sans }',
339         list => [(
340                 map { chr(0x1F0A0 + $_), sprintf('<b>%s</b>', chr(0x1F0B0 + $_)) }  # spades, hearts
341                 1 .. 11, 13, 14  # A 2-10 J Q K
342         ), '', chr(0x1F0CF), chr(0x1F0DF) ],
343 },
344 maritime => {
345         name => 'Maritime flags',
346         style => $spacestyle,
347         list => [
348                 # International Code of Signals, SVG fills
349                 map { !!$_ && '<svg width="20" height="20" viewBox="0 0 30 30">'.s/\n?\t+//gr.'</svg>' }
350                 split /\n\n/, qq{
351                         <path fill="$C{blue}" d="M0,0 h30 l-7.5,15 7.5,15 h-30 z"/>
352                         <path fill="white" d="M0,0 h15 v30 h-15"/>
353
354                         <path fill="$C{red}" d="M0,0 h30 l-7.5,15 7.5,15 h-30 z"/>
355
356                         <path fill="$C{blue}" d="M0,0 h30v30 h-30z"/>
357                         <path fill="white" d="M0,6  h30 v18 h-30" />
358                         <path fill="$C{red}" d="M0,12 h30 v6  h-30" />
359
360                         <path fill="$C{yellow}" d="M0,0 h30v30 h-30z"/>
361                         <path fill="$C{blue}" d="M0,6 h30 v18 h-30"/>
362
363                         <path fill="$C{red}" d="M0,0 h30v30 h-30z"/>
364                         <path fill="$C{blue}" d="M0,0 h30 v15 h-30"/>
365
366                         <path fill="white" d="M0,0 h30v30 h-30z"/>
367                         <path fill="$C{red}" d="M15,0 l15,15 -15,15 -15,-15"/>
368
369                         <path fill="$C{blue}" d="M0,0 h30v30 h-30z"/>
370                         <path fill="$C{yellow}" d="M 0,0 h5 v30 h-5"/>
371                         <path fill="$C{yellow}" d="M10,0 h5 v30 h-5"/>
372                         <path fill="$C{yellow}" d="M20,0 h5 v30 h-5"/>
373
374                         <path fill="$C{red}" d="M0,0 h30v30 h-30z"/>
375                         <path fill="white" d="M0,0 h15 v30 h-15"/>
376
377                         <path fill="$C{yellow}" d="M0,0 h30v30 h-30z"/>
378                         <circle fill="$C{black}" r="7.5" cx="15" cy="15"/>
379
380                         <path fill="$C{blue}" d="M0,0 h30v30 h-30z"/>
381                         <path fill="white" d="M0,10 h30 v10 h-30"/>
382
383                         <path fill="$C{blue}" d="M0,0 h30v30 h-30z"/>
384                         <path fill="$C{yellow}" d="M0,0 h15 v30 h-15"/>
385
386                         <path fill="$C{black}" d="M0,0 h30v30 h-30z"/>
387                         <path fill="$C{yellow}" d="M0,0 h15 v15 h-15 M15,15 h15 v15 h-15"/>
388
389                         <path fill="white" d="M0,0 h30v30 h-30z"/>
390                         <path fill="$C{blue}" d="M4,0h22l-11,11 M4,30h22l-11,-11
391                                 M0,4v22l11,-11 M30,4v22l-11,-11"/>
392
393                         <path fill="white" d="M0,0 h30v30 h-30z"/>
394                         <path fill="$C{blue}" d="
395                                 M0,0     h7.5v7.5h-7.5 m0,7.5h7.5v7.5h-7.5
396                                 m7.5,-15 h7.5v7.5h-7.5 m0,7.5h7.5v7.5h-7.5
397                                 m7.5,-30 h7.5v7.5h-7.5 m0,7.5h7.5v7.5h-7.5
398                                 m7.5,-15 h7.5v7.5h-7.5 m0,7.5h7.5v7.5h-7.5
399                         "/>
400
401                         <path fill="$C{yellow}" d="M0,0 h30v30 h-30z"/>
402                         <path fill="$C{red}" d="M0,0 h30 v30"/>
403
404                         <path fill="$C{blue}" d="M0,0 h30v30 h-30z"/>
405                         <path fill="white" d="M10,10 h10 v10 h-10"/>
406
407                         <path fill="$C{yellow}" d="M0,0 h30v30 h-30z"/>
408
409                         <path fill="$C{red}" d="M0,0 h30v30 h-30z"/>
410                         <path fill="$C{yellow}" d="M12.5,0 v30 h5 v-30"/>
411                         <path fill="$C{yellow}" d="M0,12.5 h30 v5 h-30"/>
412
413                         <path fill="white" d="M0,0 h30v30 h-30z"/>
414                         <path fill="$C{blue}" d="M10,10 h10 v10 h-10"/>
415
416                         <path fill="white" d="M0,0 h30v30 h-30z"/>
417                         <path fill="$C{red}" d="M0,0 h10 v30 h-10"/>
418                         <path fill="$C{blue}" d="M20,0 h10 v30 h-10"/>
419
420                         <path fill="white" d="M0,0 h30v30 h-30z"/>
421                         <path fill="$C{red}" d="M0,0 h15 v15 h-15 M15,15 h15 v15 h-15"/>
422
423                         <path fill="$C{red}" d="M0,0 h30v30 h-30z"/>
424                         <path fill="white" d="M4,0h22l-11,11 M4,30h22l-11,-11
425                                 M0,4v22l11,-11 M30,4v22l-11,-11"/>
426
427                         <path fill="$C{blue}" d="M0,0 h30v30 h-30z"/>
428                         <path fill="white" d="M5,5 h20 v20 h-20"/>
429                         <path fill="$C{red}" d="M10,10 h10 v10 h-10"/>
430
431                         <path fill="white" d="M0,0 h30v30 h-30z"/>
432                         <path fill="$C{blue}" d="M12.5,0 v30 h5 v-30"/>
433                         <path fill="$C{blue}" d="M0,12.5 h30 v5 h-30"/>
434
435                         <path fill="$C{red}" d="M0,0 h30v30 h-30z"/>
436                         <path fill="$C{yellow}" d="M0,0h6l-6,6 M12,0h6l-18,18v-6
437                                 M24,0h6l-30,30v-6 M30,6v6l-18,18h-6 M30,18v6l-6,6h-6"/>
438
439                         <path fill="$C{black}" d="M0,0 h30v30 h-30z"/>
440                         <path fill="$C{blue}" d="M30,0 v31 l-15,-15"/>
441                         <path fill="$C{yellow}" d="M0,0  h31 l-15,15"/>
442                         <path fill="$C{red}" d="M0,30 h31 l-15,-15"/>
443
444
445
446                         <path fill="$C{blue}" d="M0,5 30,15 0,25"/>
447                         <path fill="$C{yellow}" d="M0,9 20,15 0,21"/>
448
449                         <path fill="$C{blue}" d="M0,5 30,15 0,25"/>
450                         <path fill="white" d="M15,10 30,15 15,20"/>
451                 },
452         ],
453 },
454 flag => {
455         name => 'Flag semaphore',
456         list => [
457                 map {
458                         local $_ = $_;
459                         s/[1-4]\K(?=[4-9])/ /; # prevent unwanted vertical crossing
460                         tr/1-9/↙←↖↑↗→↘↓/;
461                         s{(\S)(?=.)}{<span style="position:absolute">$1</span>};
462                         $_
463                 }
464                 qw(
465                         1 2 3 4 5  6 7 21 31 46  14 51 16 17 23
466                         24 25 26 27 34  35 47 56 57 36  67
467                 )
468         ],
469 },
470 chappe => {
471         name => 'Chappe semaphore',
472         list => [
473                 map {
474                         my ($r, $pr, $pl) = split //, $_;
475                         /^\D$/ ? $_ : sprintf(
476                                 join('',
477                                         '<svg width="16" height="20" viewBox="0 0 10 15">',
478                                         '<path d="M5,6 v7"/>',
479                                         '<path d="M0,%s h10 %s" transform="rotate(%d 5 6)"/>',
480                                         '</svg>',
481                                 ),
482                                 ['6', '3v3', '9v-3']->[$pl],
483                                 [ '',  'v3',  'v-3']->[$pr],
484                                 $r * 45,
485                         );
486                 }
487                 # 360° rotation (0-7) and position state (0-2) of left and right bars
488                 qw(
489                   021 121 221 321 421 521 621 721
490                   > 022 122 222 322 011 111 211 311
491                   001 101 201 301 401 501 601 701 020
492                 )
493         ],
494 },
495 prussian => {
496         name => 'Prussian semaphore',
497         list => [
498                 map { /^\D+$/ ? $_ : sprintf
499                         join('',
500                                 '<svg width="10" height="20" viewBox="0 0 8 18">',
501                                 '<path d="M4,1 v18"/>',
502                                 (map {(
503                                         qq(<path d="M0 $_ h4" transform="rotate(%d 4 $_)"/>),
504                                         qq(<path d="M4 $_ h4" transform="rotate(-%d 4 $_)"/>),
505                                 )} 3, 7, 14),
506                                 '</svg>',
507                         ),
508                         map { ($_ - 2) * 45 % 360 } split //, $_
509                 }
510                 # rotation state (0-3) for left and right bar of 3 rows
511                 qw(
512                         003000 000200 203300 000030 033030 000130 000330 032330 > 031330
513                         022020 130120 001320 233010 030210 022310 203001 233001
514                         131001 231301 000202 023302 230003 032003 201003 101003
515                 )
516         ],
517 },
518 code39 => {
519         name => 'Code 39',
520         list => [map { dispbar($_) } qw(
521                 2111121121 1121121121 2121121111 1111221121 2111221111 1121221111
522                 1111122121 2111122111 1121122111 1111222111 2111111221 1121111221
523                 2121111211 1111211221 2111211211 1121211211 1111112221 2111112211
524                 1121112211 1111212211 2211111121 1221111121 2221111111 1211211121
525                 2211211111 1221211111            1221112111 0 1211212111
526         )], # ISO/IEC 16388
527 },
528 code93 => {
529         name => 'Code 93',
530         list => [map { dispbar($_) } qw(
531                 211113 211212 211311 221112 221211 231111 112113 112212 112311 122112
532                 132111 111123 111222 111321 121122 131121 212112 212211 211122 211221
533                 221121 222111 112122 112221 122121 123111        311211 0 111141
534         )],
535 },
536 code128 => {
537         name => 'Code 128',
538         list => [map { dispbar($_) } qw(
539                 111323 131123 131321 112313 132113 132311 211313 231113 231311 112133
540                 112331 132131 113123 113321 133121 313121 211331 231131 213113 213311
541                 213131 311123 311321 331121 312113 312311      212222 0 211412 23311120
542         )],
543 },
544 rm4scc => {
545         name => '<abbr title="Royal Mail 4-State Customer Code">RM4SCC</abbr>',
546         list => [
547                 map {
548                         my $len = length $_;
549                         !$len ? '' : sprintf(
550                                 '<svg width="%d" height="20" viewBox="0 0 %d 6">'
551                                 . '<path d="M1%s"/></svg>',
552                                 $len * 5, $len * 2,
553                                 join ' m2',
554                                 map { sprintf ',%dv%dm0,-%d',
555                                         ($_ & 1 ? 0 : 2),  2 + ($_ & 2) + ($_ & 1) * 2,
556                                         ($_ & 1 ? 0 : 2) + 2 + ($_ & 2) + ($_ & 1) * 2,
557                                 }
558                                 split //
559                         );
560                 }
561                 qw(
562                                                                 2121 2301
563                         0132 0312 0330 2112 2130 2310
564                         1023 1203 1221 3003 3021 3201
565                         1032 1212 1230 3012 3030 3210
566                         1122 1302 1320 3102 3120 3300
567                         0033
568                 ), # 0 for space
569                 '', 1, 3  # start/end
570         ],
571 },
572 rgbmap => {
573         name => 'RGBmap',
574         style => [
575                 'svg { isolation: isolate }',
576                 'svg circle { mix-blend-mode: screen }',
577                 '.sample { background: black }',
578         ],
579         list => [
580                 map { disphues($_, [0, 240, 120], 1) } # Red, Blue, Green
581                 @hueorder[23..25,20..22, 12, 6..11,0..5, 16..18, 13..15, 19, 26],
582         ],
583 },
584 cmymap => {
585         name => 'CMYmap',
586         style => [
587                 'svg { isolation: isolate }',  # mix on white
588                 'svg circle { mix-blend-mode: multiply }',
589                 '.sample { background: white }',
590         ],
591         list => [
592                 map { disphues($_, [180, 60, 300]) } # Cyan, Yellow, Magenta
593 #               @hueorder[13..18, 19, 0..11, 20..25, 12, 26],
594                 @hueletters
595         ],
596 },
597 dni => {
598         name => "D'ni",
599         style => [
600                 'svg { border: 1px solid currentColor }',
601                 '.sample span + span svg { border-left: 0 }',
602         ],
603         list => [
604                 map {
605                         state $v = [
606                                 '',
607                                 'M0,4 8,4',
608                                 'M0,8 Q4,4 8,8',
609                                 'M0,4 4,8 8,4',
610                                 'M2,0 2,4 8,4',
611                                 'M0,0 8,8 M0,8 8,0', # cross
612                                 'M3.5,4 h1', # dot
613                         ];
614                         state $h = [
615                                 '',
616                                 'M4,0 4,8',
617                                 'M0,0 Q4,4 0,8',
618                                 'M4,-.5 0,4 4,8.5',
619                                 'M4,8 4,2 8,2',
620                         ];
621                         sprintf(
622                                 '<svg width="16" height="16" viewBox="0 0 8 8"><path d="%s"/></svg>',
623                                 $h->[$_ % 5] . $v->[$_ / 5] || $v->[6],
624                         );
625                 } 0 .. 5*5
626         ],
627 },
628 pigpen => {
629         style => [
630                 'svg path { stroke-linecap: square }',
631                 '.sample svg { margin-right: 0.1em }',
632         ],
633         list => [
634                 map {
635                         qq(<svg width="12" height="12" viewBox="-.5 -.5 7 7">$_</svg>)
636                 }
637                 map {
638                         local $_ = $_;
639                         s/^H/mX,0/ or s/^V/m0,X/ or s/^/m0,0/;
640                         s/[hv]\K|X/6/g;
641                         s/(?:v|,[^0]).*?v\K/-/;
642                         s/(?:h|m[^0]).*?h\K/-/;
643                         m/h/ or s/v/l3,/g;
644                         m/v/ or s/h([^h]*)/l$1,3/g;
645                         my $dot = s/\.// && qq(<circle cx="3" cy="3" r="1"/>);
646                         qq(<path d="$_"/>$dot)
647                 }
648                 qw(
649                         Hvh  vhv  vh  hvh  vhvh  Hhvh  hv  Vvhv  Hhv
650                         Hvh. vhv. vh. hvh. vhvh. Hhvh. hv. Vvhv. Hhv.
651                         vv  hh  Hhh  Vvv
652                         vv. hh. Hhh. Vvv.
653                 ),
654         ],
655 },
656 nyctographs => {
657         style => [
658                 'svg path { stroke-linecap: round; stroke-linejoin: round }',
659                 '.sample svg {
660                         background: rgba(0,0,0, .1);
661                         padding: 0.1em;
662                         margin-right: 0.2em;
663                 }',
664         ],
665         list => [
666                 map { s/M[\d,\hM]+(?=[M"])//gr }  # clean up superfluous moves
667                 map { sprintf
668                         '<svg width="14" height="14" viewBox="-.5 -.5 5 5">'
669                         . '<path d="M0,0%s %s4,0 %s4,4 %s0,4 %s0,0"/></svg>',
670                         'h.5v.5h-.5v-.5',  # start anchor
671                         map { ['M', 'h0M', 'L']->[$_] }
672                         split //
673                 }
674                 # draw style (0=empty, 1=dot, 2=line connect) to right, down, left, up
675                 qw(
676                         0010 0112 2022 2220 2000 2012 0122 0202 0020 0220 0012 0022 2202
677                         0222 2222 0102 0200 2201 2002 2200 0100 0110 0120 2001 2010 2020
678                         0000
679                 ),
680         ],
681 },
682 chromacons => {
683         title => 'Colour Alphabet by Paul Green-Armytage (2010)',
684 #       style => '.sample { word-break: break-all }',
685         list => [
686                 map {
687                         sprintf('<span%s>%s</span>',
688                                 !!$_ && sprintf(' style="background:#%s" title="%s"', split /:/),
689                                 chr(8195) . (!$_ && chr(8203)) # em space (plus zwsp for spaces)
690                         );
691                 }
692                 qw{
693                         F0A3FF:Amethyst 0075DC:Blue     993F00:Caramel  4C005C:Damson
694                         191919:Ebony    005C31:Forest   2BCE48:Green    FFCC99:Honeydew
695                         808080:Iron     94FFB5:Jade     8F7C00:Khaki    9DCC00:Lime
696                         C20088:Mallow   003380:Navy     FFA405:Orpiment FFA8BB:Pink
697                         426600:Quagmire FF0010:Red      5EF1F2:Sky      00998F:Turquoise
698                         E0FF66:Uranium  740AFF:Violet   990000:Wine     FFFF80:Xanthin
699                         FFFF00:Yellow   FF5005:Zinnia   0
700                 }
701         ],
702 },
703 );