Pantalla meteorológica de Apple II (parte 2)

En la primera parte de la pantalla meteorológica de Apple II, examiné rápidamente cómo se toman y redactan los datos. Ahora es el momento de hacer algo con él en la parte 2. En el orden de las funciones, primero hago las partes del texto y, aunque es muy similar al proceso por el que pasa la imagen del radar, es monocromático y un poco más simple de explicar. Antes de empezar a entender cómo funciona, debería explicar cómo comparto la pantalla del Apple II.

El modo de alta resolución en Apple II le brinda 280 × 160 con 4 líneas de texto a continuación, o 280 × 192 en pantalla completa. Usaré una pantalla completa de 280 × 192, porque por muy útil que sea el texto, también mostrará el flujo interminable de charlas durante una actualización, y dado que el modo de video a todo color del diseño de Apple II es un truco NTSC, no es. todo tan hermoso en un televisor estándar al mismo tiempo que muestra gráficos en color. También dividí la pantalla en "bloques" de 35 píxeles de ancho cada uno, esto se debió a mi sistema de codificación.

Sin ingresar un ASCII alto o caracteres especiales, decidí usar un sistema de numeración básico de tipo 36, solo necesitamos 35 caracteres únicos para representar cualquier píxel en una línea dentro de un bloque. El valor de 1 significa que el primer píxel en un bloque dado y así sucesivamente hasta que Z sea el número de píxel 35 en un bloque, y hasta 8 bloques para toda la pantalla. Los separadores de bloques y otras funciones se pueden enviar en minúsculas, lo que crea un sistema de texto simple y agradable. Por ejemplo, si la manzana recibió bbb123Z, eso significa saltar al bloque 4 y poner un píxel en 1, 2, 3 y 35 dentro de ese bloque (y si no hay píxeles para esa línea dada, se envía).

Mirando text.lua ...

-- Weather Underground to Apple //
-- 2011 Kevin Dady
--
-- Text to Graphics:
-- take text from web.data
-- make text images with image magick
-- phrase *.xpm files to apple //
-- send text images
-- end
text = {}
text.data = {}
text.data.input  = {}
text.data.packed = {{},{},{},{}}
text.data.apl2   = {{},{},{}}

text.createIMG = function()
if web.data[1] == nil then
web.data[1] = "No Advisories"
end
if web.data[3] == nil then
web.data[3] = web.data[7]
end
-- create the bottom text
cmd.imageMGK(" -background black -fill white -font req/VeraMoBd.ttf"..
" -dither none -map req/mono.xpm -size 279x53 -pointsize 11 -gravity West"..
" caption:'".. web.data[2].."n"..web.data[6].."n"..web.data[5].."n"..web.data[3].. "'"..
" temp/textBTM.xpm")
-- create the text for advisories
cmd.imageMGK(" -background black -fill white -font req/VeraMoBd.ttf"..
" -dither none -map req/mono.xpm -size 139x64 -pointsize 11 -gravity Center"..
" caption:'".. web.data[1].."'"..
" temp/textALT.xpm")
-- create the text for temperature
cmd.imageMGK(" -background black -fill white -font req/VeraMoBd.ttf"..
" -dither none -map req/mono.xpm -size 139x74 -pointsize 36 -gravity Center"..
" caption:'".. web.data[4].."'"..
" temp/textTMP.xpm")
end

text.convertIMG = function()
local files  = {"textTMP.xpm","textALT.xpm","textBTM.xpm"}
local footer = {82,72,61}
local width  = {140,140,280}
-- read the files into a table one at a time
for img = 1, 3 do
local file = io.open("temp/".. files[img],"r")
table.insert(text.data.input, img, {})
for line in file:lines() do
table.insert(text.data.input[img], tostring(line))
end
file:close()
-- remove header
for y = 1, 7 do
table.remove(text.data.input[img], 1)
end
-- remove footer
table.remove(text.data.input[img], footer[img] - 7)
-- remove non pixel data
for y = 1, #text.data.input[img] do
text.data.input[img][y] = string.sub(text.data.input[img][y], 2, width[img])
end
end
end

text.sortIMG = function()
local newChar = ""
for img = 1, 3 do
-- need to convert the strings into tables
for y = 1, #text.data.input[img] do
table.insert(text.data.apl2[img], {})
-- for each column in the current row
for x = 1, #text.data.input[img][y] do
-- read the character at that Y,X point
newChar = string.sub(text.data.input[img][y], x,x)
if newChar == "." then
table.insert(text.data.apl2[img][y], x) -- pixel
end
end
end
end
end

text.packageIMG = function()
for img = 1, 2 do
for y = 1, #text.data.input[img] do
local one   = ""
local two   = ""
local three = ""
local four  = ""
for x = 1, #text.data.apl2[img][y] do
if text.data.apl2[img][y][x] <= 35 then
one = one .. string.sub(graphicsKey, text.data.apl2[img][y][x], text.data.apl2[img][y][x])
elseif text.data.apl2[img][y][x] <= 70 then
two = two .. string.sub(graphicsKey, text.data.apl2[img][y][x] - 35, text.data.apl2[img][y][x] - 35)
elseif text.data.apl2[img][y][x] <= 105 then
three = three .. string.sub(graphicsKey, text.data.apl2[img][y][x] - 70, text.data.apl2[img][y][x] - 70)
else
four = four .. string.sub(graphicsKey, text.data.apl2[img][y][x] - 105, text.data.apl2[img][y][x] - 105)
end
end
if one == "" and two == "" and three == "" and four == "" then
table.insert(text.data.packed[img], "n")
else table.insert(text.data.packed[img],one.."b"..two.."b"..three.."b"..four)
end
end
end
for y = 1, #text.data.input[3] do
local one   = ""
local two   = ""
local three = ""
local four  = ""
local five  = ""
local six   = ""
local seven = ""
local eight = ""
for x = 1, #text.data.apl2[3][y] do
if text.data.apl2[3][y][x] <= 35 then
one = one .. string.sub(graphicsKey, text.data.apl2[3][y][x], text.data.apl2[3][y][x])
elseif text.data.apl2[3][y][x] <= 70 then
two = two .. string.sub(graphicsKey, text.data.apl2[3][y][x] - 35, text.data.apl2[3][y][x] - 35)
elseif text.data.apl2[3][y][x] <= 105 then
three = three .. string.sub(graphicsKey, text.data.apl2[3][y][x] - 70, text.data.apl2[3][y][x] - 70)
elseif text.data.apl2[3][y][x] <= 140 then
four = four .. string.sub(graphicsKey, text.data.apl2[3][y][x] - 105, text.data.apl2[3][y][x] - 105)
elseif text.data.apl2[3][y][x] <=175 then
five = five .. string.sub(graphicsKey, text.data.apl2[3][y][x] - 140, text.data.apl2[3][y][x] -140)
elseif text.data.apl2[3][y][x] <= 210 then
six = six .. string.sub(graphicsKey, text.data.apl2[3][y][x] - 175, text.data.apl2[3][y][x] - 175)
elseif text.data.apl2[3][y][x] <= 245 then
seven = seven .. string.sub(graphicsKey, text.data.apl2[3][y][x] - 210, text.data.apl2[3][y][x] - 210)
else
eight = eight .. string.sub(graphicsKey, text.data.apl2[3][y][x] - 245, text.data.apl2[3][y][x] - 245)
end
end
if one == "" and two == "" and three == "" and four == "" and five == "" and six == "" and seven == "" and eight == "" then
table.insert(text.data.packed[3], "n")
else table.insert(text.data.packed[3],one.."b"..two.."b"..three.."b"..four.."b"..five.."b"..six.."b"..seven.."b"..eight)
end
end
end

text.sendIMG = function()
for img = 1, 3 do
for y = 1, #text.data.packed[img] do
cmd.sjinn(text.data.packed[img][y])
end
cmd.sleep(2)
end
end

Empiezo en text.createIMG () marcando algunas cosas, una si hay advertencias, y si no pongo una etiqueta sin avisos, en segundo lugar buscando "rompevientos", no siempre hay "rompevientos" y si no lugar. "Punto de rocío" en su lugar para que no tengamos líneas vacías. una vez que estuvo listo, enviamos el script a imagemagick para hacer 3 gráficos que contienen texto en blanco y negro. uno para el texto extenso a continuación, uno para las advertencias y otro para la temperatura.

text.convertIMG () lee los archivos XPM generados por imagemagick y realiza una limpieza. Comienza pirateando el encabezado y el pie de página del archivo de imagen y elimina el formato de línea de cada línea de la imagen.

text.sortIMG () toma los datos de cadena sobrantes y escanea cada carácter en cada línea, en cuyo caso un píxel blanco está representado por "." (punto) y un píxel negro (que no nos importa) está representado por "" (espacio). Cada vez que se encuentra un período, su posición x se agrega al final de una matriz. Antes del final de la imagen, nos queda una matriz que tiene una subtabla para cada línea y contiene valores X para cada píxel en cada línea, por ejemplo:

datos = {}

datos[1] = {1,2,12,80}

datos[2] = {140,143,144,150}

Hay 3 imágenes para procesar y son de diferentes tamaños, aunque para mi guión realmente no importa qué tan altas sean, lo que nos importa es el ancho. text.packageIMG () lee cada imagen y las divide en bloques, el texto de temperatura y el texto de aviso tienen 140 píxeles de ancho y consumen bloques de 4, 35 píxeles, por lo que cada valor en cada línea se lee de nuestras tablas anteriores y tiene algunos. matemáticas básicas hechas en ellos. Si un valor es mayor que 35, por ejemplo, entonces su bloque 2, el valor tiene 35 restado y ese es nuestro valor de bloque (36 - 35 = bloque 2 píxel 1). El texto a continuación es el gráfico más ancho, ocupando todo el ancho de la pantalla, pero es exactamente lo mismo, solo distribuido en 8 bloques. Una vez que tenemos nuestros bloques como cadenas codificadas, se empaquetan en una sola cadena por línea de la imagen con una "b" que separa cada bloque.

radar.lua hace casi lo mismo, excepto que, en lugar de hacer gráficos, difumina la imagen de radar descargada anteriormente.

-- Weather Underground to Apple //
-- 2011 Kevin Dady
--
-- Radar processing:
-- feed jpeg to image magick
-- phrase output.xpm to color tables
-- package for apple //
-- send radar

radar = {}

radar.data = {}
radar.data.input  = {}  -- raw file data table dump
radar.data.packed = {}  -- packed apple data
radar.data.apl2 = {{},{},{},{},{},{}} -- black, green, violet, orange, blue, white

radar.img = {}
radar.img.header = 11 -- xpm file header # of lines
radar.img.w = 141
radar.img.h = 141

radar.convertIMG = function()

cmd.imageMGK(" temp/radar.jpg -level 0%,70%,1 -dither none -map req/apple.xpm temp/output.xpm")
-- read the file into a table
local file = io.open("temp/output.xpm","r")
for line in file:lines() do
table.insert(radar.data.input, tostring(line))
end
file:close()

-- remove header
for i = 1, radar.img.header do -- hardcode
table.remove(radar.data.input, 1)
end

-- remove footer
table.remove(radar.data.input, radar.img.w)

-- remove non color data
for i = 1, #radar.data.input do
radar.data.input[i] = string.sub(radar.data.input[i], 2, radar.img.h)
end
-- only deal with odd rows, due to the even / odd, bit / line, funny way apple 2's display highres colors.
-- if we leave them all in the image there is a gret chance of 2 colors phasing into another,
-- since we are going to loose pixel resolution anyway, we can cut that down some by deleting every other line
-- giving 140x70 also making transfer size smaller.
local keep ={}
for i = 1, #radar.data.input do
if (i % 2) == 1 then table.insert(keep, radar.data.input[i]) end
end
radar.data.input = keep
end

-- " " = 0 Apple color black (1)
-- "X" = 1 Apple color green
-- "o" = 2 Apple color violet
-- "." = 5 Apple color orange
-- "O" = 6 Apple color blue
-- "+" = 7 Apple color white (2)

radar.sortIMG = function()
local newChar = ""
-- need to convert the strings into tables
for y = 1, #radar.data.input do
-- add a new "line" string to each color table
for color = 1, 6 do
table.insert(radar.data.apl2[color], {})
end
-- for each column in the current row
for x = 1, #radar.data.input[y] do
-- read the character at that Y,X point
newChar = string.sub(radar.data.input[y], x,x)
-- assign each character a individual table value
if newChar == " " then table.insert(radar.data.apl2[1][y], x) -- black
elseif newChar == "X" then table.insert(radar.data.apl2[4][y], x) -- GREEN
elseif newChar == "o" then table.insert(radar.data.apl2[3][y], x) -- violet
elseif newChar == "." then table.insert(radar.data.apl2[2][y], x) -- ORANGE
elseif newChar == "O" then table.insert(radar.data.apl2[5][y], x) -- blue
elseif newChar == "+" then table.insert(radar.data.apl2[6][y], x) -- white
end
end
end
end

radar.packageIMG = function()
for color = 1, 5 do    -- ignore white, white takes a long time to draw since it makes up most of the graphic
for y = 1, #radar.data.apl2[color] do
local one   = ""
local two   = ""
local three = ""
local four  = ""
for x = 1, #radar.data.apl2[color][y] do
if radar.data.apl2[color][y][x] <= 35 then
one = one .. string.sub(graphicsKey, radar.data.apl2[color][y][x], radar.data.apl2[color][y][x])
elseif radar.data.apl2[color][y][x] <= 70 then
two = two .. string.sub(graphicsKey, radar.data.apl2[color][y][x] - 35, radar.data.apl2[color][y][x] - 35)
elseif radar.data.apl2[color][y][x] <= 105 then
three = three .. string.sub(graphicsKey, radar.data.apl2[color][y][x] - 70, radar.data.apl2[color][y][x] - 70)
else
four = four .. string.sub(graphicsKey, radar.data.apl2[color][y][x] - 105, radar.data.apl2[color][y][x] - 105)
end
end

if one == "" and two == "" and three == "" and four == "" then
table.insert(radar.data.packed, "n")
else table.insert(radar.data.packed,one.."b"..two.."b"..three.."b"..four)
end
end
end
end

radar.send = function()
for line = 1, 350 do
cmd.sjinn(radar.data.packed[line])
end
end

Imagemagick se usa para cancelar la imagen del radar a 6 de los 8 colores "disponibles", el Apple II solo tiene 6 colores únicos en modo de alta resolución, y los otros 2 son 2 negros y 2 blancos. Esto se refiere a cómo funciona la manzana. hace color, mencioné anteriormente que es un truco que básicamente usa patrones de bits y la fase del estallido de color para generar diferentes colores. El efecto final siempre es interesante porque nunca se puede poner un color específico en todos y cada uno de los puntos de cada línea.

Para ayudar a reducir los artefactos entre líneas y reducir a la mitad la transmisión de datos, utilizo solo las líneas planas de la imagen, lo que reduce la resolución de mi radar de 140 × 140 a 140 × 70, pero debido a la preocupación anterior. del sistema de video de Apple, habría perdido la mayor parte de esa resolución de todos modos.

El archivo XPM de salida tiene su encabezado, pie de página y formato de línea eliminado y cada color se divide en tablas individuales, a partir de ahí el proceso es el mismo, leyendo cada imagen monocromática, empaquetándola en líneas definidas por bloques y empaquetando para que Apple las consuma . . Ignoro el blanco y solo dibujo eso en la pantalla porque constituye la mayor parte de la imagen, lo que ahorra tiempo, y los datos finales son 5 copias de la línea / bloque de la imagen codificadas, cada una actuando como una máscara para su color único.

La mayor parte se hizo para mirar los scripts de lua, así que únete a mí para la parte 3 (el final) donde exploraremos el software que usa Apple II.

La Parte 1 y la Parte 3 también están disponibles.

  • jef dice:

    Sería útil que pusiera el enlace a la parte 1 en el texto. Gracias.

Manuel Gómez
Manuel Gómez

Deja una respuesta

Tu dirección de correo electrónico no será publicada.