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