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