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