meta-imp-segments.mkxl /size: 13 Kb    last modification: 2025-02-21 11:03
1%D \module
2%D   [       file=meta-imp-segments,
3%D        version=2024.07.31,
4%D          title=\METAPOST\ Graphics,
5%D       subtitle=Segment Numbers,
6%D         author=Hans Hagen,
7%D           date=\currentdate,
8%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
9%C
10%C This module is part of the \CONTEXT\ macro||package and is
11%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
12%C details.
13
14% I made this module for the 2024 \CONTEXT\ meeting in (watertower) Lutten NL for
15% the math & calculating devices day. Originally I just replaced the regular digits
16% but then found that we have official \UNICODE\ slot for them. Of course we could
17% have given all names like \quote {segment <chr>} but this is how it evolved.
18
19\startluacode
20    local newprivateslot = fonts.helpers.newprivateslot
21
22    -- We started out with this:
23
24    for i=10,15 do
25        newprivateslot("segment digit " .. i) -- A .. F
26    end
27
28    -- and then added a period.
29
30 -- newprivateslot("segment period")
31
32    -- but needed some more, so (in \TEX\ speak we have others):
33
34    newprivateslot("segment other .")
35    newprivateslot("segment other -")
36    newprivateslot("segment other :")
37
38    -- Just for fun this was also done:
39
40    newprivateslot("segment letter C")
41    newprivateslot("segment letter O")
42    newprivateslot("segment letter N")
43    newprivateslot("segment letter T")
44    newprivateslot("segment letter E")
45    newprivateslot("segment letter X")
46
47    -- which alternatively can be done in a loop:
48
49 -- for chr in string.gmatch("CONTEX") do
50 --     newprivateslot("segment letter " .. chr)
51 -- end ;
52
53    -- For the cyx 2024 talk I also needed:
54
55    newprivateslot("segment elements")
56    newprivateslot("segment diagonals")
57\stopluacode
58
59% Next we define the shapes in \METAFUN\ speak, Because the presentation looked
60% better with a bit bolder elements I added conbrol over the offset, slant an
61% weight. After all we can pass these as prt of the feature.
62
63\startMPcalculation{simplefun}
64
65    % We should do this a bit more abstract and take the line width into account
66    % but I leave that for now. It just evolved out of a half hour hack for Willi
67    % and Bruce.
68
69    path segment_glyphs[] ;
70    path segment_snippets[] ;
71
72    def InitializeSegments =
73
74        save xoffset ; xoffset := 0.1 ;
75        save yoffset ; yoffset := 0.1 ;
76        save slant   ; slant   := 0 ;
77        save weight  ; weight  := 1 ;
78
79      % xoffset := 0.11 ;
80      % yoffset := 0.11 ;
81      % slant   := 0.10 ;
82      % weight  := 2.00 ;
83
84        if hasparameter "mpsfont" "weight" :
85            weight := getparameterdefault "mpsfont" "weight" 1 ;
86        fi ;
87        if hasparameter "mpsfont" "slant" :
88            slant := getparameterdefault "mpsfont" "slant" 0 ;
89        fi ;
90        if hasparameter "mpsfont" "offset" :
91            xoffset := yoffset := getparameterdefault "mpsfont" "offset" 0.1 ;
92        fi ;
93
94        % The seven segments (not cf the official a-g setup:
95
96        segment_snippets[1] := (0+xoffset,2) -- (1-xoffset,2) ;
97        segment_snippets[2] := (0,1+yoffset) -- (0,2-yoffset) ;
98        segment_snippets[3] := (1,1+yoffset) -- (1,2-yoffset) ;
99        segment_snippets[4] := (0+xoffset,1) -- (1-xoffset,1) ;
100        segment_snippets[5] := (0,0+yoffset) -- (0,1-yoffset) ;
101        segment_snippets[6] := (1,0+yoffset) -- (1,1-yoffset) ;
102        segment_snippets[7] := (0+xoffset,0) -- (1-xoffset,0) ;
103
104        % The period: I tried a circle but  that loosk pretty bad due to the
105        % small scale. So
106
107        segment_snippets[20] := (1.30,  0   ) -- (1.30,  0.30) --
108                                (1.40,  0.30) -- (1.40,  0   ) -- cycle ;
109        segment_snippets[21] := (0.45,  0.30) -- (0.45,  0.60) --
110                                (0.55,  0.60) -- (0.55,  0.30) -- cycle ;
111        segment_snippets[22] := (0.45,2-0.30) -- (0.45,2-0.60) --
112                                (0.55,2-0.60) -- (0.55,2-0.30) -- cycle ;
113
114        % For playing around we have the two possible nine segment diagonals:
115
116      % segment_snippets[8 ] := (1/2+xoffset/2,1+yoffset) -- (1-xoffset,2-yoffset) ;
117      % segment_snippets[9 ] := (1/2-xoffset/2,1-yoffset) -- (0+xoffset,0+yoffset) ;
118      % segment_snippets[10] := (1/2-xoffset/2,1+yoffset) -- (0+xoffset,2-yoffset) ;
119      % segment_snippets[11] := (1/2+xoffset/2,1-yoffset) -- (1-xoffset,0+yoffset) ;
120
121        % Somewhat better when bolder: butonly used in talk:
122
123        segment_snippets[8 ] := (1/2+4xoffset/6,1+8yoffset/6) -- (1-8xoffset/6,2-8yoffset/6) ;
124        segment_snippets[9 ] := (1/2-4xoffset/6,1-8yoffset/6) -- (0+8xoffset/6,0  +8yoffset/6) ;
125        segment_snippets[10] := (1/2-4xoffset/6,1+8yoffset/6) -- (0+8xoffset/6,2  -8yoffset/6) ;
126        segment_snippets[11] := (1/2+4xoffset/6,1-8yoffset/6) -- (1-8xoffset/6,0+8yoffset/6) ;
127
128        % We can cheat but let's keep that as comment:
129
130      % segment_snippets[12] := (1/2,        1+yoffset) -- (1/2,        2-yoffset) ;
131      % segment_snippets[13] := (1/2,        1-yoffset) -- (1/2,          yoffset) ;
132      % segment_snippets[14] := (    xoffset,2)         -- (1/2-xoffset,2) ;
133      % segment_snippets[15] := (1/2+xoffset,2)         -- (  1-xoffset,2) ;
134
135        % Now we assemble the segments:
136
137        vardef combine_snippets(text t) =
138            for i=t : segment_snippets[i] && endfor nocycle
139        enddef ;
140
141        segment_glyphs[ 0] := combine_snippets(1, 2, 3,    5, 6, 7) ; % 0
142        segment_glyphs[ 1] := combine_snippets(      3,       6   ) ; % 1
143        segment_glyphs[ 2] := combine_snippets(1,    3, 4, 5,    7) ; % 2
144        segment_glyphs[ 3] := combine_snippets(1,    3, 4,    6, 7) ; % 3
145        segment_glyphs[ 4] := combine_snippets(   2, 3, 4,    6   ) ; % 4
146        segment_glyphs[ 5] := combine_snippets(1, 2,    4,    6, 7) ; % 5
147        segment_glyphs[ 6] := combine_snippets(1, 2,    4, 5, 6, 7) ; % 6
148        segment_glyphs[ 7] := combine_snippets(1,    3,       6   ) ; % 7
149        segment_glyphs[ 8] := combine_snippets(1, 2, 3, 4, 5, 6, 7) ; % 8
150        segment_glyphs[ 9] := combine_snippets(1, 2, 3, 4,    6, 7) ; % 9
151
152        segment_glyphs[10] := combine_snippets(1, 2, 3, 4, 5, 6   ) ; % A
153        segment_glyphs[11] := combine_snippets(   2,    4, 5, 6, 7) ; % B
154        segment_glyphs[12] := combine_snippets(1, 2,       5,    7) ; % C
155        segment_glyphs[13] := combine_snippets(      3, 4, 5, 6, 7) ; % D
156        segment_glyphs[14] := combine_snippets(1, 2,    4, 5,    7) ; % E
157        segment_glyphs[15] := combine_snippets(1, 2,    4, 5      ) ; % F
158
159        % Sometimes these are used ... but not here;
160
161      % segment_glyphs[6] := combine_snippets(   2,    4, 5, 6, 7) ;
162      % segment_glyphs[7] := combine_snippets(1, 2, 3,       6   ) ;
163      % segment_glyphs[9] := combine_snippets(1, 2, 3, 4,    6   ) ;
164
165        % The symbols:
166
167        segment_glyphs[45] := segment_snippets[4]     ; % minus
168        segment_glyphs[46] := segment_snippets[20]    ; % period
169        segment_glyphs[58] := combine_snippets(21,22) ; % colon
170
171        % Some letters:
172
173        segment_glyphs[67] := combine_snippets(1, 2,       5,    7              ) ; % C % can be different from digit C
174        segment_glyphs[69] := combine_snippets(1, 2,    4, 5,    7              ) ; % E % can be different from digit E
175        segment_glyphs[78] := combine_snippets(   2, 3,    5, 6,          10, 11) ; % N
176        segment_glyphs[79] := combine_snippets(1, 2, 3,    5, 6, 7              ) ; % O % can be different from digit 0
177        segment_glyphs[84] := combine_snippets(   2,    4, 5,    7,             ) ; % T % ugly
178        segment_glyphs[88] := combine_snippets(                     8, 9, 10, 11) ; % X % cheat
179
180        % The \quote {t} is ugly and will remain so, so not:
181
182      % segment_glyphs[84] := combine_snippets(12, 13, 14, 15) ; % T % also ugly
183
184        % The used segments positioned relative to each other. In a clock display the
185        % colon is a dedicated thing so it will not be in every segment. In that case
186        % the diagonals can be there.
187
188        segment_glyphs[100] := combine_snippets(1,2,3,4,5,6,7,20,21,22) ;
189        segment_glyphs[101] := combine_snippets(1,2,3,4,5,6,7,8,9,10,11) ;
190
191        if slant <> 0 :
192            for i=0 upto 88 :
193                if known segment_glyphs[i] :
194                    segment_glyphs[i] := segment_glyphs[i] slanted .1 ;
195                fi ;
196            endfor ;
197        fi ;
198
199    enddef ;
200
201    vardef Segment(expr i) =
202        numeric u ; u := 1 ;
203        draw image (
204            draw segment_glyphs[i]
205                scaled u
206                withpen pencircle scaled (weight*u/7.5) ;
207            ;
208        )
209        shifted (u/5+u/20,u/20)
210    enddef ;
211
212    lmt_registerglyphs [
213        name     = "segments",
214        units    = 3,
215        usecolor = true,
216        width    = 1.5,
217width    = 1.8,
218        height   = 2.1,
219        depth    = 0,
220        preamble = "InitializeSegments"
221    ] ;
222
223% set tounicode
224
225    for i=0 upto 9 :
226        lmt_registerglyph [
227            category  = "segments",
228            unicode   = 130032 + (i mod 10), % 0x1FBF0 .. 0x1FBF9
229            tounicode = (ASCII "0") + (i mod 10),
230            code      = "Segment(" & decimal i & ")",
231        ] ;
232    endfor ;
233
234    for i=(ASCII "A") upto (ASCII "F") :
235        lmt_registerglyph [
236            category  = "segments",
237            private   = "segment digit " & utfchr(i),
238            tounicode = i,
239            code      = "Segment(" & decimal (10 + i - ASCII "A") & ")",
240        ] ;
241    endfor ;
242
243    for i=45, 46, 58 :
244        lmt_registerglyph [
245            category  = "segments",
246            private   = "segment other " & utfchr(i),
247            tounicode = i,
248            code      = "Segment(" & (decimal i) & ")",
249        ] ;
250    endfor ;
251
252    lmt_registerglyph [ category = "segments", private = "segment elements",  code = "Segment(100)" ] ;
253    lmt_registerglyph [ category = "segments", private = "segment diagonals", code = "Segment(101)" ] ;
254
255    for i=67, 69, 78, 79, 84, 88 :
256        lmt_registerglyph [
257            category  = "segments",
258            private   = "segment letter " & utfchr(i),
259            tounicode = i,
260            code      = "Segment(" & (decimal i) & ")",
261        ] ;
262    endfor ;
263
264\stopMPcalculation
265
266\startluacode
267    fonts.handlers.otf.addfeature {
268        name    = "segmentdigits",
269        type    = "substitution",
270        nocheck = true,
271        data    = {
272            [0x30] = 0x1FBF0,
273            [0x31] = 0x1FBF1,
274            [0x32] = 0x1FBF2,
275            [0x33] = 0x1FBF3,
276            [0x34] = 0x1FBF4,
277            [0x35] = 0x1FBF5,
278            [0x36] = 0x1FBF6,
279            [0x37] = 0x1FBF7,
280            [0x38] = 0x1FBF8,
281            [0x39] = 0x1FBF9,
282
283            [0x41] = "segment digit A",
284            [0x42] = "segment digit B",
285            [0x43] = "segment digit C",
286            [0x44] = "segment digit D",
287            [0x45] = "segment digit E",
288            [0x46] = "segment digit F",
289
290         -- [0x2E] = "segment period",
291
292            [0x2D] = "segment other -",
293            [0x2E] = "segment other .",
294            [0x3A] = "segment other :",
295        }
296    }
297
298    -- todo: support a mix of gpos and gsub
299
300 -- local kern = { [fonts.helpers.privateslot("segment period")] = -1230 } -- well ...
301 -- local kern = { ["segment period"] = -1230 } -- well ...
302    local kern = { ["segment other ."] = -1230 } -- well ...
303
304    fonts.handlers.otf.addfeature {
305       name    = "segmentperiod",
306       type    = "kern",
307       nocheck = true,
308       data = {
309           [0x1FBF0] = kern, [0x1FBF1] = kern,
310           [0x1FBF2] = kern, [0x1FBF3] = kern,
311           [0x1FBF4] = kern, [0x1FBF5] = kern,
312           [0x1FBF6] = kern, [0x1FBF7] = kern,
313           [0x1FBF8] = kern, [0x1FBF9] = kern,
314
315           ["segment digit A"] = kern,
316           ["segment digit B"] = kern,
317           ["segment digit C"] = kern,
318           ["segment digit D"] = kern,
319           ["segment digit E"] = kern,
320           ["segment digit F"] = kern,
321       },
322   }
323
324    fonts.handlers.otf.addfeature {
325        name    = "segmentextras",
326        type    = "chainsubstitution",
327        prepend = 1,
328        nocheck = true,
329        lookups = {
330            {
331                type = "substitution",
332                data = {
333                    ["C"] = "segment letter C",
334                    ["O"] = "segment letter O",
335                    ["N"] = "segment letter N",
336                    ["T"] = "segment letter T",
337                    ["E"] = "segment letter E",
338                    ["X"] = "segment letter X",
339                },
340            },
341        },
342        data = {
343            rules = {
344                {
345                    current = { { "C" }, { "O" }, { "N" }, { "T" }, { "E" }, { "X" },  { "T" } },
346                    lookups = { 1, 1, 1, 1, 1, 1, 1 },
347                },
348            },
349        }
350    }
351
352
353\stopluacode
354
355\definefontfeature
356  [segments]
357  [metapost=segments]
358
359\definefontfeature
360  [boldsegments]
361  [metapost={category=segments,weight=2.0,offset=.2}]
362
363\definefontfeature
364  [segmentdigits]
365  [segmentdigits=yes,
366   segmentextras=yes,
367   segmentperiod=yes]
368
369\definefontfeature
370  [default]
371  [default]
372  [metapost={category=segments,weight=1.2}]
373%   [metapost=segments]
374
375\definehighlight
376  [Digits]
377  [style=\addfeature{segmentdigits}]
378
379\continueifinputfile{meta-imp-segments.mkxl}
380
381\setupbodyfont[dejavu]
382
383\showglyphs
384
385\startTEXpage[offset=1ts]
386
387    These old texies grew up with these segmented characters with funny descenders on
388    top of the baseline, but digits are fine:
389       \Digits{{\middlered-12345}{\middleblue.}{\middlegreen67890}}
390    also in
391        \Digits{12:34}.
392
393    We also provide some hexadecimal symbols but these are not in unicode:
394        \Digits{ABCDEF}.
395
396    And we have a bonus:
397        \Digits{like this CONTEXT here}.
398
399\stopTEXpage
400
401\stoptext
402
403
404