mirror of
https://gitlab.com/c3d/db48x.git
synced 2024-09-29 05:36:58 +02:00
array: Graphic rendering of vectors and arrays
Graphic rendering of arrays and vectors like on the HP50 Fixes: #47 Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
This commit is contained in:
parent
f29346e830
commit
05d690712a
9 changed files with 259 additions and 15 deletions
BIN
images/vector-horizontal.png
Normal file
BIN
images/vector-horizontal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
images/vector-vertical.png
Normal file
BIN
images/vector-vertical.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
177
src/array.cc
177
src/array.cc
|
@ -28,8 +28,10 @@
|
|||
// ****************************************************************************
|
||||
|
||||
#include "array.h"
|
||||
|
||||
#include "arithmetic.h"
|
||||
#include "functions.h"
|
||||
#include "grob.h"
|
||||
|
||||
|
||||
RECORDER(matrix, 16, "Determinant computation");
|
||||
|
@ -70,6 +72,181 @@ HELP_BODY(array)
|
|||
}
|
||||
|
||||
|
||||
using pixsize = grob::pixsize;
|
||||
|
||||
|
||||
static pixsize row_height(size_t r, size_t rows, size_t cols)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Compute the height of a row (max of height of all grobs)
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
size_t nitems = rows * cols;
|
||||
pixsize rh = 0;
|
||||
for (size_t c = 0; c < cols; c++)
|
||||
{
|
||||
size_t i = r * cols + c;
|
||||
grob_p colitem = grob_p(rt.stack(nitems + ~i));
|
||||
ASSERT(colitem);
|
||||
pixsize h = colitem->height();
|
||||
if (rh < h)
|
||||
rh = h;
|
||||
}
|
||||
return rh;
|
||||
}
|
||||
|
||||
|
||||
static pixsize col_width(size_t c, size_t rows, size_t cols)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Compute the height of a row (max of height of all grobs)
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
size_t nitems = rows * cols;
|
||||
pixsize cw = 0;
|
||||
for (size_t r = 0; r < rows; r++)
|
||||
{
|
||||
size_t i = r * cols + c;
|
||||
grob_p colitem = grob_p(rt.stack(nitems + ~i));
|
||||
pixsize w = colitem->width();
|
||||
if (cw < w)
|
||||
cw = w;
|
||||
}
|
||||
return cw;
|
||||
}
|
||||
|
||||
|
||||
GRAPH_BODY(array)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Render an array graphically
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
array_g a = o;
|
||||
if (!a)
|
||||
return nullptr;
|
||||
|
||||
size_t rows = 0;
|
||||
size_t cols = 0;
|
||||
bool mat = false;
|
||||
bool vec = false;
|
||||
if (a->is_matrix(&rows, &cols))
|
||||
{
|
||||
mat = true;
|
||||
}
|
||||
else if (a->is_vector(&cols))
|
||||
{
|
||||
vec = true;
|
||||
rows = 1;
|
||||
if (Settings.VerticalVectors())
|
||||
{
|
||||
rows = cols;
|
||||
cols = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback if we don't have a proper matrix or vector, e.g. non-rect
|
||||
if (!mat && !vec)
|
||||
return object::do_graph(a, g);
|
||||
|
||||
// Convert all elements to graphical equivalent
|
||||
size_t nitems = rows * cols;
|
||||
for (size_t i = 0; i < nitems; i++)
|
||||
{
|
||||
object_p item = rt.stack(i);
|
||||
grob_g grob = item->graph(g);
|
||||
if (!grob || grob->type() != ID_grob)
|
||||
{
|
||||
// Ran into a problem with one rendering, e.g. out of memory
|
||||
// Fallback to rendering as text.
|
||||
record(matrix_error, "Problem graphing %zu in %zu x %zu",
|
||||
i, rows, cols);
|
||||
rt.drop(nitems);
|
||||
return object::do_graph(a, g);
|
||||
}
|
||||
rt.stack(i, grob);
|
||||
}
|
||||
|
||||
// Compute the width, starting with 8 pixels for borders
|
||||
pixsize bw = mat ? 4 : 2;
|
||||
pixsize sw = 2;
|
||||
pixsize gap = 12;
|
||||
pixsize gw = 4 * bw + (cols - 1) * gap + 2 * sw;
|
||||
pixsize gh = 0;
|
||||
|
||||
// Compute the width as the sum of the max width of all columns
|
||||
for (size_t c = 0; c < cols; c++)
|
||||
{
|
||||
size_t cw = col_width(c, rows, cols);
|
||||
gw += cw;
|
||||
if (gw > g.maxw)
|
||||
{
|
||||
rt.drop(nitems);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the height as the sum of the max height of all rows
|
||||
for (size_t r = 0; r < rows; r++)
|
||||
{
|
||||
size_t rh = row_height(r, rows, cols);
|
||||
gh += rh;
|
||||
if (gh > g.maxh)
|
||||
{
|
||||
rt.drop(nitems);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the resulting graph
|
||||
grob_g result = g.grob(gw, gh);
|
||||
if (!result)
|
||||
{
|
||||
rt.drop(nitems);
|
||||
return nullptr;
|
||||
}
|
||||
surface rs = result->pixels();
|
||||
rs.fill(0, 0, gw, gh, g.background);
|
||||
|
||||
// Dimensions of porder and adjust
|
||||
coord xl = 0;
|
||||
coord xr = coord(gw)-2;
|
||||
coord yt = 0;
|
||||
coord yb = coord(gh)-4;
|
||||
|
||||
// Draw all items inside the matrix
|
||||
coord yi = yt;
|
||||
for (size_t r = 0; r < rows; r++)
|
||||
{
|
||||
pixsize rh = row_height(r, rows, cols);
|
||||
coord xi = xl + 2 * bw + sw;
|
||||
for (size_t c = 0; c < cols; c++)
|
||||
{
|
||||
pixsize cw = col_width(c, rows, cols);
|
||||
size_t i = r * cols + c;
|
||||
grob_p colitem = grob_p(rt.stack(nitems + ~i));
|
||||
surface is = colitem->pixels();
|
||||
pixsize iw = is.width();
|
||||
pixsize ih = is.height();
|
||||
rs.copy(is, xi + (cw - iw)/2, yi + (rh - ih)/2);
|
||||
xi += cw + gap;
|
||||
}
|
||||
yi += rh;
|
||||
}
|
||||
|
||||
// Add the borders
|
||||
for (coord y = 1; y < yb; y++)
|
||||
{
|
||||
rs.fill(xl, y, xl+bw, y, g.foreground);
|
||||
rs.fill(xr-bw, y, xr, y, g.foreground);
|
||||
}
|
||||
rs.fill(xl, yt, xl+2*bw, yt+1, g.foreground);
|
||||
rs.fill(xr-2*bw, yt, xr, yt+1, g.foreground);
|
||||
rs.fill(xl, yb, xl+2*bw, yb+1, g.foreground);
|
||||
rs.fill(xr-2*bw, yb, xr, yb+1, g.foreground);
|
||||
|
||||
rt.drop(nitems);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
//
|
||||
|
|
|
@ -93,6 +93,7 @@ struct array : list
|
|||
OBJECT_DECL(array);
|
||||
PARSE_DECL(array);
|
||||
RENDER_DECL(array);
|
||||
GRAPH_DECL(array);
|
||||
HELP_DECL(array);
|
||||
};
|
||||
|
||||
|
|
|
@ -373,8 +373,8 @@ struct blitter
|
|||
{
|
||||
surface(pixword *p, size w, size h, size scanline)
|
||||
: pixels(p),
|
||||
width(w),
|
||||
height(h),
|
||||
w(w),
|
||||
h(h),
|
||||
scanline(scanline),
|
||||
drawable(w, h)
|
||||
{
|
||||
|
@ -408,7 +408,7 @@ struct blitter
|
|||
// --------------------------------------------------------------------
|
||||
{
|
||||
drawable = r;
|
||||
drawable &= rect(width, height);
|
||||
drawable &= rect(w, h);
|
||||
}
|
||||
|
||||
void clip(coord x1, coord y1, coord x2, coord y2)
|
||||
|
@ -432,7 +432,23 @@ struct blitter
|
|||
// Return total drawing area
|
||||
// --------------------------------------------------------------------
|
||||
{
|
||||
return rect(width, height);
|
||||
return rect(w, h);
|
||||
}
|
||||
|
||||
size width() const
|
||||
// --------------------------------------------------------------------
|
||||
// Total drawing width
|
||||
// --------------------------------------------------------------------
|
||||
{
|
||||
return w;
|
||||
}
|
||||
|
||||
size height() const
|
||||
// --------------------------------------------------------------------
|
||||
// Total drawing height
|
||||
// --------------------------------------------------------------------
|
||||
{
|
||||
return h;
|
||||
}
|
||||
|
||||
template <clipping Clip = FILL_SAFE>
|
||||
|
@ -526,8 +542,8 @@ struct blitter
|
|||
// Copy a rectangular area from the source
|
||||
// --------------------------------------------------------------------
|
||||
{
|
||||
size w = src.width;
|
||||
size h = src.height;
|
||||
size w = src.w;
|
||||
size h = src.h;
|
||||
rect dest(x, y, x + w - 1, y + h - 1);
|
||||
blit<Clip>(*this, src, dest, point(), blitop_source, clr);
|
||||
}
|
||||
|
@ -685,8 +701,8 @@ struct blitter
|
|||
protected:
|
||||
friend struct blitter;
|
||||
pixword *pixels; // Word-aligned address of surface buffer
|
||||
size width; // Pixel width of buffer
|
||||
size height; // Pixel height of buffer
|
||||
size w; // Pixel width of buffer
|
||||
size h; // Pixel height of buffer
|
||||
size scanline; // Scanline for the buffer (can be > width)
|
||||
rect drawable; // Draw area (clipping outside)
|
||||
};
|
||||
|
@ -1501,7 +1517,7 @@ void blitter::blit(Dst &dst,
|
|||
}
|
||||
|
||||
ASSERT(dp >= dst.pixels);
|
||||
ASSERT(dp <= dst.pixels + (dst.scanline * dst.height + (BPW-1)) / BPW);
|
||||
ASSERT(dp <= dst.pixels + (dst.scanline * dst.h + (BPW-1)) / BPW);
|
||||
pixword ddata = dp[0];
|
||||
pixword tdata = op(ddata, sdata, cdata);
|
||||
*dp = (tdata & dmask) | (ddata & ~dmask);
|
||||
|
@ -1545,7 +1561,7 @@ inline void blitter::surface<blitter::MONOCHROME_REVERSE>::horizontal_adjust(
|
|||
// On the DM42, we need horizontal adjustment for coordinates
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
size w = width - 1;
|
||||
size w = width() - 1;
|
||||
coord ox1 = w - x2;
|
||||
x2 = w - x1;
|
||||
x1 = ox1;
|
||||
|
|
|
@ -710,6 +710,8 @@ FLAG(NumberedVariables, NoNumberedVariables)
|
|||
FLAG(UseCrossForMultiplication, UseDotForMultiplication)
|
||||
FLAG(HardwareFloatingPoint, SoftwareFloatingPoint)
|
||||
FLAG(NoAngleUnits, SetAngleUnits)
|
||||
FLAG(VerticalLists, HorizontalLists)
|
||||
FLAG(VerticalVectors, HorizontalVectors)
|
||||
|
||||
ALIAS(HardwareFloatingPoint, "HFP")
|
||||
ALIAS(HardwareFloatingPoint, "HardFP")
|
||||
|
|
52
src/tests.cc
52
src/tests.cc
|
@ -5297,8 +5297,53 @@ void tests::graphic_expressions_rendering()
|
|||
.test(CLEAR, EXIT, EXIT)
|
||||
.test("1 'X' +", ENTER, B, C, E, "3 X 3", LSHIFT, B, MUL, ADD)
|
||||
.test(ALPHA, X, NOSHIFT, J, K, L, ADD, C, B, C, B)
|
||||
.wait(100)
|
||||
.image_noheader("reduced");
|
||||
|
||||
step("Constants")
|
||||
.test(CLEAR, LSHIFT, I, F1, F1, F2, F3)
|
||||
.image_noheader("constants");
|
||||
|
||||
step("Vector")
|
||||
.test(CLEAR, LSHIFT, KEY9, "1 2 3", ENTER, EXIT)
|
||||
.wait(100)
|
||||
.image_noheader("vector-horizontal");
|
||||
step("Vector vertical rendering")
|
||||
.test("VerticalVectors", ENTER)
|
||||
.wait(100)
|
||||
.image_noheader("vector-vertical");
|
||||
step("Vector horizontal rendering")
|
||||
.test("HorizontalVectors", ENTER)
|
||||
.wait(100)
|
||||
.image_noheader("vector-horizontal");
|
||||
|
||||
step("Matrix")
|
||||
.test(CLEAR, LSHIFT, KEY9,
|
||||
LSHIFT, KEY9, "1 2 3 4", DOWN,
|
||||
LSHIFT, KEY9, "4 5 6 7", DOWN,
|
||||
LSHIFT, KEY9, "8 9 10 11", DOWN,
|
||||
LSHIFT, KEY9, "12 13 14 18", ENTER, EXIT)
|
||||
.wait(100)
|
||||
.image_noheader("matrix");
|
||||
step("Matrix with smaller size")
|
||||
.test(13, DIV, ENTER, MUL)
|
||||
.wait(100)
|
||||
.image_noheader("matrix-smaller");
|
||||
|
||||
step("Lists")
|
||||
.test(CLEAR, RSHIFT, SPACE, "1 2 \"ABC\"", ENTER, EXIT)
|
||||
.wait(100)
|
||||
.image_noheader("list-horizontal");
|
||||
step("List vertical")
|
||||
.test("VerticalLists", ENTER)
|
||||
.test(CLEAR, RSHIFT, SPACE, "1 2 \"ABC\"", ENTER, EXIT)
|
||||
.wait(100)
|
||||
.image_noheader("list-vertical");
|
||||
step("List horizontal")
|
||||
.test("HorizontalLists", ENTER)
|
||||
.test(CLEAR, RSHIFT, SPACE, "1 2 \"ABC\"", ENTER, EXIT)
|
||||
.wait(100)
|
||||
.image_noheader("list-horizontal");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -6650,13 +6695,14 @@ tests &tests::image(cstring file, int x, int y, int w, int h)
|
|||
}
|
||||
|
||||
|
||||
tests &tests::image_noheader(cstring name)
|
||||
tests &tests::image_noheader(cstring name, uint ignoremenus)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Image, skipping the header area
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
const int header_h = 20;
|
||||
return image(name, 0, header_h, LCD_W, LCD_H - header_h);
|
||||
const int menu_h = 22 * ignoremenus;
|
||||
return image(name, 0, header_h, LCD_W, LCD_H - header_h - menu_h);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -294,7 +294,7 @@ public:
|
|||
tests &expect(unsigned long long output);
|
||||
tests &match(cstring regexp);
|
||||
tests &image(cstring name, int x=0, int y=0, int w=LCD_W, int h=LCD_H);
|
||||
tests &image_noheader(cstring name);
|
||||
tests &image_noheader(cstring name, uint ignoremenus=0);
|
||||
tests &type(object::id ty);
|
||||
tests &shift(bool s);
|
||||
tests &xshift(bool x);
|
||||
|
|
|
@ -1250,6 +1250,8 @@ static flag_conversion flag_conversions[] =
|
|||
{ -56, object::ID_BeepOff },
|
||||
{ -64, object::ID_IndexWrapped },
|
||||
{ -65, object::ID_MultiLineStack },
|
||||
{ -97, object::ID_VerticalLists },
|
||||
{ -98, object::ID_VerticalVectors },
|
||||
{ -103, object::ID_ComplexResults },
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue