%D \module
%D   [       file=meta-imp-segments,
%D        version=2024.07.31,
%D          title=\METAPOST\ Graphics,
%D       subtitle=Segment Numbers,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

% I made this module for the 2024 \CONTEXT\ meeting in (watertower) Lutten NL for
% the math & calculating devices day. Originally I just replaced the regular digits
% but then found that we have official \UNICODE\ slot for them. Of course we could
% have given all names like \quote {segment <chr>} but this is how it evolved.

\startluacode
    local newprivateslot = fonts.helpers.newprivateslot

    -- We started out with this:

    for i=10,15 do
        newprivateslot("segment digit " .. i) -- A .. F
    end

    -- and then added a period.

 -- newprivateslot("segment period")

    -- but needed some more, so (in \TEX\ speak we have others):

    newprivateslot("segment other .")
    newprivateslot("segment other -")
    newprivateslot("segment other :")

    -- Just for fun this was also done:

    newprivateslot("segment letter C")
    newprivateslot("segment letter O")
    newprivateslot("segment letter N")
    newprivateslot("segment letter T")
    newprivateslot("segment letter E")
    newprivateslot("segment letter X")

    -- which alternatively can be done in a loop:

 -- for chr in string.gmatch("CONTEX") do
 --     newprivateslot("segment letter " .. chr)
 -- end ;

    -- For the cyx 2024 talk I also needed:

    newprivateslot("segment elements")
    newprivateslot("segment diagonals")
\stopluacode

% Next we define the shapes in \METAFUN\ speak, Because the presentation looked
% better with a bit bolder elements I added conbrol over the offset, slant an
% weight. After all we can pass these as prt of the feature.

\startMPcalculation{simplefun}

    % We should do this a bit more abstract and take the line width into account
    % but I leave that for now. It just evolved out of a half hour hack for Willi
    % and Bruce.

    path segment_glyphs[] ;
    path segment_snippets[] ;

    def InitializeSegments =

        save xoffset ; xoffset := 0.1 ;
        save yoffset ; yoffset := 0.1 ;
        save slant   ; slant   := 0 ;
        save weight  ; weight  := 1 ;

      % xoffset := 0.11 ;
      % yoffset := 0.11 ;
      % slant   := 0.10 ;
      % weight  := 2.00 ;

        if hasparameter "mpsfont" "weight" :
            weight := getparameterdefault "mpsfont" "weight" 1 ;
        fi ;
        if hasparameter "mpsfont" "slant" :
            slant := getparameterdefault "mpsfont" "slant" 0 ;
        fi ;
        if hasparameter "mpsfont" "offset" :
            xoffset := yoffset := getparameterdefault "mpsfont" "offset" 0.1 ;
        fi ;

        % The seven segments (not cf the official a-g setup:

        segment_snippets[1] := (0+xoffset,2) -- (1-xoffset,2) ;
        segment_snippets[2] := (0,1+yoffset) -- (0,2-yoffset) ;
        segment_snippets[3] := (1,1+yoffset) -- (1,2-yoffset) ;
        segment_snippets[4] := (0+xoffset,1) -- (1-xoffset,1) ;
        segment_snippets[5] := (0,0+yoffset) -- (0,1-yoffset) ;
        segment_snippets[6] := (1,0+yoffset) -- (1,1-yoffset) ;
        segment_snippets[7] := (0+xoffset,0) -- (1-xoffset,0) ;

        % The period: I tried a circle but  that loosk pretty bad due to the
        % small scale. So

        segment_snippets[20] := (1.30,  0   ) -- (1.30,  0.30) --
                                (1.40,  0.30) -- (1.40,  0   ) -- cycle ;
        segment_snippets[21] := (0.45,  0.30) -- (0.45,  0.60) --
                                (0.55,  0.60) -- (0.55,  0.30) -- cycle ;
        segment_snippets[22] := (0.45,2-0.30) -- (0.45,2-0.60) --
                                (0.55,2-0.60) -- (0.55,2-0.30) -- cycle ;

        % For playing around we have the two possible nine segment diagonals:

      % segment_snippets[8 ] := (1/2+xoffset/2,1+yoffset) -- (1-xoffset,2-yoffset) ;
      % segment_snippets[9 ] := (1/2-xoffset/2,1-yoffset) -- (0+xoffset,0+yoffset) ;
      % segment_snippets[10] := (1/2-xoffset/2,1+yoffset) -- (0+xoffset,2-yoffset) ;
      % segment_snippets[11] := (1/2+xoffset/2,1-yoffset) -- (1-xoffset,0+yoffset) ;

        % Somewhat better when bolder: butonly used in talk:

        segment_snippets[8 ] := (1/2+4xoffset/6,1+8yoffset/6) -- (1-8xoffset/6,2-8yoffset/6) ;
        segment_snippets[9 ] := (1/2-4xoffset/6,1-8yoffset/6) -- (0+8xoffset/6,0  +8yoffset/6) ;
        segment_snippets[10] := (1/2-4xoffset/6,1+8yoffset/6) -- (0+8xoffset/6,2  -8yoffset/6) ;
        segment_snippets[11] := (1/2+4xoffset/6,1-8yoffset/6) -- (1-8xoffset/6,0+8yoffset/6) ;

        % We can cheat but let's keep that as comment:

      % segment_snippets[12] := (1/2,        1+yoffset) -- (1/2,        2-yoffset) ;
      % segment_snippets[13] := (1/2,        1-yoffset) -- (1/2,          yoffset) ;
      % segment_snippets[14] := (    xoffset,2)         -- (1/2-xoffset,2) ;
      % segment_snippets[15] := (1/2+xoffset,2)         -- (  1-xoffset,2) ;

        % Now we assemble the segments:

        vardef combine_snippets(text t) =
            for i=t : segment_snippets[i] && endfor nocycle
        enddef ;

        segment_glyphs[ 0] := combine_snippets(1, 2, 3,    5, 6, 7) ; % 0
        segment_glyphs[ 1] := combine_snippets(      3,       6   ) ; % 1
        segment_glyphs[ 2] := combine_snippets(1,    3, 4, 5,    7) ; % 2
        segment_glyphs[ 3] := combine_snippets(1,    3, 4,    6, 7) ; % 3
        segment_glyphs[ 4] := combine_snippets(   2, 3, 4,    6   ) ; % 4
        segment_glyphs[ 5] := combine_snippets(1, 2,    4,    6, 7) ; % 5
        segment_glyphs[ 6] := combine_snippets(1, 2,    4, 5, 6, 7) ; % 6
        segment_glyphs[ 7] := combine_snippets(1,    3,       6   ) ; % 7
        segment_glyphs[ 8] := combine_snippets(1, 2, 3, 4, 5, 6, 7) ; % 8
        segment_glyphs[ 9] := combine_snippets(1, 2, 3, 4,    6, 7) ; % 9

        segment_glyphs[10] := combine_snippets(1, 2, 3, 4, 5, 6   ) ; % A
        segment_glyphs[11] := combine_snippets(   2,    4, 5, 6, 7) ; % B
        segment_glyphs[12] := combine_snippets(1, 2,       5,    7) ; % C
        segment_glyphs[13] := combine_snippets(      3, 4, 5, 6, 7) ; % D
        segment_glyphs[14] := combine_snippets(1, 2,    4, 5,    7) ; % E
        segment_glyphs[15] := combine_snippets(1, 2,    4, 5      ) ; % F

        % Sometimes these are used ... but not here;

      % segment_glyphs[6] := combine_snippets(   2,    4, 5, 6, 7) ;
      % segment_glyphs[7] := combine_snippets(1, 2, 3,       6   ) ;
      % segment_glyphs[9] := combine_snippets(1, 2, 3, 4,    6   ) ;

        % The symbols:

        segment_glyphs[45] := segment_snippets[4]     ; % minus
        segment_glyphs[46] := segment_snippets[20]    ; % period
        segment_glyphs[58] := combine_snippets(21,22) ; % colon

        % Some letters:

        segment_glyphs[67] := combine_snippets(1, 2,       5,    7              ) ; % C % can be different from digit C
        segment_glyphs[69] := combine_snippets(1, 2,    4, 5,    7              ) ; % E % can be different from digit E
        segment_glyphs[78] := combine_snippets(   2, 3,    5, 6,          10, 11) ; % N
        segment_glyphs[79] := combine_snippets(1, 2, 3,    5, 6, 7              ) ; % O % can be different from digit 0
        segment_glyphs[84] := combine_snippets(   2,    4, 5,    7,             ) ; % T % ugly
        segment_glyphs[88] := combine_snippets(                     8, 9, 10, 11) ; % X % cheat

        % The \quote {t} is ugly and will remain so, so not:

      % segment_glyphs[84] := combine_snippets(12, 13, 14, 15) ; % T % also ugly

        % The used segments positioned relative to each other. In a clock display the
        % colon is a dedicated thing so it will not be in every segment. In that case
        % the diagonals can be there.

        segment_glyphs[100] := combine_snippets(1,2,3,4,5,6,7,20,21,22) ;
        segment_glyphs[101] := combine_snippets(1,2,3,4,5,6,7,8,9,10,11) ;

        if slant <> 0 :
            for i=0 upto 88 :
                if known segment_glyphs[i] :
                    segment_glyphs[i] := segment_glyphs[i] slanted .1 ;
                fi ;
            endfor ;
        fi ;

    enddef ;

    vardef Segment(expr i) =
        numeric u ; u := 1 ;
        draw image (
            draw segment_glyphs[i]
                scaled u
                withpen pencircle scaled (weight*u/7.5) ;
            ;
        )
        shifted (u/5+u/20,u/20)
    enddef ;

    lmt_registerglyphs [
        name     = "segments",
        units    = 3,
        usecolor = true,
        width    = 1.5,
width    = 1.8,
        height   = 2.1,
        depth    = 0,
        preamble = "InitializeSegments"
    ] ;

% set tounicode

    for i=0 upto 9 :
        lmt_registerglyph [
            category  = "segments",
            unicode   = 130032 + (i mod 10), % 0x1FBF0 .. 0x1FBF9
            tounicode = (ASCII "0") + (i mod 10),
            code      = "Segment(" & decimal i & ")",
        ] ;
    endfor ;

    for i=(ASCII "A") upto (ASCII "F") :
        lmt_registerglyph [
            category  = "segments",
            private   = "segment digit " & utfchr(i),
            tounicode = i,
            code      = "Segment(" & decimal (10 + i - ASCII "A") & ")",
        ] ;
    endfor ;

    for i=45, 46, 58 :
        lmt_registerglyph [
            category  = "segments",
            private   = "segment other " & utfchr(i),
            tounicode = i,
            code      = "Segment(" & (decimal i) & ")",
        ] ;
    endfor ;

    lmt_registerglyph [ category = "segments", private = "segment elements",  code = "Segment(100)" ] ;
    lmt_registerglyph [ category = "segments", private = "segment diagonals", code = "Segment(101)" ] ;

    for i=67, 69, 78, 79, 84, 88 :
        lmt_registerglyph [
            category  = "segments",
            private   = "segment letter " & utfchr(i),
            tounicode = i,
            code      = "Segment(" & (decimal i) & ")",
        ] ;
    endfor ;

\stopMPcalculation

\startluacode
    fonts.handlers.otf.addfeature {
        name    = "segmentdigits",
        type    = "substitution",
        nocheck = true,
        data    = {
            [0x30] = 0x1FBF0,
            [0x31] = 0x1FBF1,
            [0x32] = 0x1FBF2,
            [0x33] = 0x1FBF3,
            [0x34] = 0x1FBF4,
            [0x35] = 0x1FBF5,
            [0x36] = 0x1FBF6,
            [0x37] = 0x1FBF7,
            [0x38] = 0x1FBF8,
            [0x39] = 0x1FBF9,

            [0x41] = "segment digit A",
            [0x42] = "segment digit B",
            [0x43] = "segment digit C",
            [0x44] = "segment digit D",
            [0x45] = "segment digit E",
            [0x46] = "segment digit F",

         -- [0x2E] = "segment period",

            [0x2D] = "segment other -",
            [0x2E] = "segment other .",
            [0x3A] = "segment other :",
        }
    }

    -- todo: support a mix of gpos and gsub

 -- local kern = { [fonts.helpers.privateslot("segment period")] = -1230 } -- well ...
 -- local kern = { ["segment period"] = -1230 } -- well ...
    local kern = { ["segment other ."] = -1230 } -- well ...

    fonts.handlers.otf.addfeature {
       name    = "segmentperiod",
       type    = "kern",
       nocheck = true,
       data = {
           [0x1FBF0] = kern, [0x1FBF1] = kern,
           [0x1FBF2] = kern, [0x1FBF3] = kern,
           [0x1FBF4] = kern, [0x1FBF5] = kern,
           [0x1FBF6] = kern, [0x1FBF7] = kern,
           [0x1FBF8] = kern, [0x1FBF9] = kern,

           ["segment digit A"] = kern,
           ["segment digit B"] = kern,
           ["segment digit C"] = kern,
           ["segment digit D"] = kern,
           ["segment digit E"] = kern,
           ["segment digit F"] = kern,
       },
   }

    fonts.handlers.otf.addfeature {
        name    = "segmentextras",
        type    = "chainsubstitution",
        prepend = 1,
        nocheck = true,
        lookups = {
            {
                type = "substitution",
                data = {
                    ["C"] = "segment letter C",
                    ["O"] = "segment letter O",
                    ["N"] = "segment letter N",
                    ["T"] = "segment letter T",
                    ["E"] = "segment letter E",
                    ["X"] = "segment letter X",
                },
            },
        },
        data = {
            rules = {
                {
                    current = { { "C" }, { "O" }, { "N" }, { "T" }, { "E" }, { "X" },  { "T" } },
                    lookups = { 1, 1, 1, 1, 1, 1, 1 },
                },
            },
        }
    }


\stopluacode

\definefontfeature
  [segments]
  [metapost=segments]

\definefontfeature
  [boldsegments]
  [metapost={category=segments,weight=2.0,offset=.2}]

\definefontfeature
  [segmentdigits]
  [segmentdigits=yes,
   segmentextras=yes,
   segmentperiod=yes]

\definefontfeature
  [default]
  [default]
  [metapost={category=segments,weight=1.2}]
%   [metapost=segments]

\definehighlight
  [Digits]
  [style=\addfeature{segmentdigits}]

\continueifinputfile{meta-imp-segments.mkxl}

\setupbodyfont[dejavu]

\showglyphs

\startTEXpage[offset=1ts]

    These old texies grew up with these segmented characters with funny descenders on
    top of the baseline, but digits are fine:
       \Digits{{\middlered-12345}{\middleblue.}{\middlegreen67890}}
    also in
        \Digits{12:34}.

    We also provide some hexadecimal symbols but these are not in unicode:
        \Digits{ABCDEF}.

    And we have a bonus:
        \Digits{like this CONTEXT here}.

\stopTEXpage

\stoptext


