Tag: telidon

  • The glorious futility of generating NAPLPS in 2023

    Yeah! Actual real NAPLPS made by me!

    NAPLPS — an almost-forgotten videotex vector graphics format with a regrettable pronunciation (/nap-lips/, no really) — was really hard to create. Back in the early days when it was a worthwhile Canadian initiative called Telidon (see Inter/Access’s exhibit Remember Tomorrow: A Telidon Story) it required a custom video workstation costing $$$$$$. It got cheaper by the time the 1990s rolled round, but it was never easy and so interest waned.

    I don’t claim what I made is particularly interesting:

    a lo-res red maple leaf in the bottom left corner of a black screen
    suspiciously canadian

    but even decoding the tutorial and standards material was hard. NAPLPS made heavy use of bitfields interleaved and packed into 7 and 8-bit characters. It was kind of a clever idea (lower resolution data could be packed into fewer bytes) but the implementation is quite unpleasant.

    A few of the references/tools/resources I relied on:

    Here’s the fragment of code I wrote to generate the NAPLPS:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # draw a disappointing maple leaf in NAPLPS - scruss, 2023-09
    
    # stylized maple leaf polygon, quite similar to
    # the coordinates used in the Canadian flag ...
    maple = [
        [62, 2],
        [62, 35],
        [94, 31],
        [91, 41],
        [122, 66],
        [113, 70],
        [119, 90],
        [100, 86],
        [97, 96],
        [77, 74],
        [85, 114],
        [73, 108],
        [62, 130],
        [51, 108],
        [39, 114],
        [47, 74],
        [27, 96],
        [24, 86],
        [5, 90],
        [11, 70],
        [2, 66],
        [33, 41],
        [30, 31],
        [62, 35],
    ]
    
    
    def colour(r, g, b):
        # r, g and b are limited to the range 0-3
        return chr(0o74) + chr(
            64
            + ((g & 2) << 4)
            + ((r & 2) << 3)
            + ((b & 2) << 2)
            + ((g & 1) << 2)
            + ((r & 1) << 1)
            + (b & 1)
        )
    
    
    def coord(x, y):
        # if you stick with 256 x 192 integer coordinates this should be okay
        xsign = 0
        ysign = 0
        if x < 0:
            xsign = 1
            x = x * -1
            x = ((x ^ 255) + 1) & 255
        if y < 0:
            ysign = 1
            y = y * -1
            y = ((y ^ 255) + 1) & 255
        return (
            chr(
                64
                + (xsign << 5)
                + ((x & 0xC0) >> 3)
                + (ysign << 2)
                + ((y & 0xC0) >> 6)
            )
            + chr(64 + ((x & 0x38)) + ((y & 0x38) >> 3))
            + chr(64 + ((x & 7) << 3) + (y & 7))
        )
    
    
    f = open("maple.nap", "w")
    f.write(chr(0x18) + chr(0x1B))  # preamble
    
    f.write(chr(0o16))  # SO: into graphics mode
    
    f.write(colour(0, 0, 0))  # black
    f.write(chr(0o40) + chr(0o120))  # clear screen to current colour
    
    f.write(colour(3, 0, 0))  # red
    
    # *** STALK ***
    f.write(
        chr(0o44) + coord(maple[0][0], maple[0][1])
    )  # point set absolute
    f.write(
        chr(0o51)
        + coord(maple[1][0] - maple[0][0], maple[1][1] - maple[0][1])
    )  # line relative
    
    # *** LEAF ***
    f.write(
        chr(0o67) + coord(maple[1][0], maple[1][1])
    )  # set polygon filled
    # append all the relative leaf vertices
    for i in range(2, len(maple)):
        f.write(
            coord(
                maple[i][0] - maple[i - 1][0], maple[i][1] - maple[i - 1][1]
            )
        )
    
    f.write(chr(0x0F) + chr(0x1A))  # postamble
    f.close()
    

    There are a couple of perhaps useful routines in there:

    1. colour(r, g, b) spits out the code for two bits per component RGB. Inputs are limited to the range 0–3 without error checking
    2. coord(x, y) converts integer coordinates to a NAPLPS output stream. Best limited to a 256 × 192 size. Will also work with positive/negative relative coordinates.

    Here’s the generated file: