Draw into the whole window, not its top-left 320x200
The graphics verbs documented a coordinate transform that did not exist. With SCALE off a coordinate went straight to akgl_draw_* as a pixel address, so an 800x600 window drew a C128 listing into its corner and left the rest unused -- while the chapter said coordinates were 320x200 and stretching to fit was the host's business. akbasic_GraphicsBackend gains a size entry point, require_graphics() asks it before every verb that draws so a resized window is honoured between two statements, and 320x200 becomes the fallback for a backend that leaves it NULL. It is the record's one optional member, so a host written against the old header keeps the behaviour it had. SCALE now maps onto the device, and RGR(1)/RGR(2) report the drawing surface so a program can use a window whose size it did not choose. RGR(0) is BASIC 7.0's own field, the GRAPHIC mode. SCALE also mapped xmax onto the width rather than onto the last pixel, so SCALE 1, 319, 199 followed by DRAW 1, 319, 199 drew nothing at all -- one pixel past the surface. Fixed in the same line, because it is what makes "SCALE gives a C128 listing the whole window" true rather than nearly true. The akgl test renders against a 128x128 target, deliberately smaller than the old constants: a SCALE still dividing by them misses it entirely rather than landing somewhere plausible. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,12 +24,18 @@
|
||||
#include <akbasic/types.h>
|
||||
|
||||
/**
|
||||
* @brief The coordinate space the backend receives, in C128 hi-res pixels.
|
||||
* @brief The coordinate space assumed when the device will not say how big it is.
|
||||
*
|
||||
* The interpreter does not own the renderer and cannot ask how big it is, so it
|
||||
* emits the coordinates the program wrote -- BASIC 7.0's 320x200 hi-res screen,
|
||||
* or whatever SCALE maps onto it. Stretching that to the window is the host's
|
||||
* business, the same way the host owns the window in the first place.
|
||||
* A coordinate reaching the backend is a *device pixel*: `DRAW 1, 799, 599` lands
|
||||
* at the bottom-right of an 800x600 window, and every pixel of that window is
|
||||
* addressable from BASIC. The interpreter still does not own the renderer -- it
|
||||
* asks, through akbasic_GraphicsBackend::size(), and the backend that does own
|
||||
* one answers.
|
||||
*
|
||||
* These constants are what is assumed when nobody answers: a backend that leaves
|
||||
* `size` NULL, or reports a nonsense size, gets BASIC 7.0's 320x200 hi-res
|
||||
* screen. That is the right fallback rather than a guess, because it is the space
|
||||
* a C128 listing was written for.
|
||||
*/
|
||||
#define AKBASIC_GRAPHICS_WIDTH 320
|
||||
#define AKBASIC_GRAPHICS_HEIGHT 200
|
||||
@@ -70,6 +76,8 @@ typedef struct
|
||||
int linewidth; /* WIDTH: 1 or 2 pixels per drawn line */
|
||||
double xmax; /* user-coordinate maxima SCALE maps from */
|
||||
double ymax;
|
||||
int devwidth; /* what the device last said it is, in pixels; */
|
||||
int devheight; /* the AKBASIC_GRAPHICS_* fallback until then */
|
||||
} akbasic_GraphicsState;
|
||||
|
||||
/**
|
||||
@@ -89,6 +97,21 @@ typedef struct akbasic_GraphicsBackend
|
||||
{
|
||||
void *self;
|
||||
|
||||
/**
|
||||
* How big the drawing surface is, in pixels.
|
||||
*
|
||||
* **Optional, and the only optional entry point in the record.** It was
|
||||
* added after the rest, so a host that filled one of these in by hand
|
||||
* before it existed leaves it NULL and keeps the 320x200 fallback rather
|
||||
* than crashing; and a backend drawing somewhere with no meaningful size
|
||||
* has an honest way to say so. Everything else here is required.
|
||||
*
|
||||
* Called before each verb draws rather than once at attach, because a
|
||||
* window is resizable and a script that runs for an hour should not be
|
||||
* drawing to the size the window was when it started.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*size)(struct akbasic_GraphicsBackend *self, int *width, int *height);
|
||||
|
||||
/** Plot one pixel. */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*point)(struct akbasic_GraphicsBackend *self, double x, double y, akbasic_Color color);
|
||||
/** Draw a line between two points. */
|
||||
|
||||
@@ -56,10 +56,10 @@
|
||||
*
|
||||
* `MOVSPR n, angle # speed` takes 0-15 on a C128 and the reference manual does
|
||||
* not say what a unit is in pixels; it says 15 is fastest and 0 stops. So the
|
||||
* mapping is ours: one unit is #AKBASIC_SPRITE_SPEED_PIXELS_PER_SECOND pixels
|
||||
* per second in BASIC's 320x200 space, which puts speed 15 at four seconds to
|
||||
* cross the screen. Recorded as a deviation in TODO.md section 5 rather than
|
||||
* presented as fidelity.
|
||||
* mapping is ours: one unit is #AKBASIC_SPRITE_SPEED_PIXELS_PER_SECOND device
|
||||
* pixels per second, which puts speed 15 at four seconds to cross a 320-pixel
|
||||
* screen and proportionally longer across a wider one. Recorded as a deviation
|
||||
* in TODO.md section 5 rather than presented as fidelity.
|
||||
*/
|
||||
#define AKBASIC_SPRITE_MAX_SPEED 15
|
||||
/** @brief Pixels per second one unit of MOVSPR speed is worth. See #AKBASIC_SPRITE_MAX_SPEED. */
|
||||
@@ -83,7 +83,7 @@ typedef struct
|
||||
bool yexpand;
|
||||
bool multicolour;
|
||||
int colorindex; /* 1-16, the sprite's own colour */
|
||||
double x; /* position in BASIC's 320x200 space */
|
||||
double x; /* position in device pixels; see graphics.h */
|
||||
double y;
|
||||
double angle; /* MOVSPR's continuous motion: degrees */
|
||||
int speed; /* clockwise from vertical, and 0-15 */
|
||||
@@ -159,7 +159,7 @@ typedef struct akbasic_SpriteBackend
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*define_file)(struct akbasic_SpriteBackend *self, int n, const char *path, const char *root);
|
||||
/** Show or hide sprite @p n. */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*show)(struct akbasic_SpriteBackend *self, int n, bool visible);
|
||||
/** Put sprite @p n at (@p x, @p y) in BASIC's 320x200 space. */
|
||||
/** Put sprite @p n at (@p x, @p y), in device pixels. */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*move)(struct akbasic_SpriteBackend *self, int n, double x, double y);
|
||||
/**
|
||||
* Set sprite @p n's own colour, its expansion bits and its priority.
|
||||
|
||||
Reference in New Issue
Block a user