Scripting: Expose some basic geometric utility types

This commit is contained in:
Vicki Pfau 2025-06-08 21:57:01 -07:00
parent 00246cda74
commit 467768d414
3 changed files with 191 additions and 0 deletions

View File

@ -11,9 +11,12 @@
CXX_GUARD_START
#include <mgba/script/macros.h>
#include <mgba-util/geometry.h>
#include <mgba-util/image.h>
mSCRIPT_DECLARE_STRUCT(mImage)
mSCRIPT_DECLARE_STRUCT(mRectangle);
mSCRIPT_DECLARE_STRUCT(mSize);
struct mScriptContext;
void mScriptContextAttachImage(struct mScriptContext* context);

View File

@ -101,6 +101,100 @@ mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager)
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, remove)
mSCRIPT_DEFINE_END;
static struct mScriptValue* _mRectangleNew(int32_t x, int32_t y, int32_t width, int32_t height) {
struct mRectangle* rect = malloc(sizeof(*rect));
rect->x = x;
rect->y = y;
rect->width = width;
rect->height = height;
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mRectangle));
result->value.opaque = rect;
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
return result;
}
static struct mScriptValue* _mRectangleCopy(const struct mRectangle* old) {
struct mRectangle* rect = malloc(sizeof(*rect));
memcpy(rect, old, sizeof(*rect));
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mRectangle));
result->value.opaque = rect;
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
return result;
}
static struct mScriptValue* _mRectangleSize(const struct mRectangle* rect) {
struct mSize* size = malloc(sizeof(*size));
size->width = rect->width;
size->height = rect->height;
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mSize));
result->value.opaque = size;
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
return result;
}
mSCRIPT_DECLARE_STRUCT_C_METHOD(mRectangle, W(mRectangle), copy, _mRectangleCopy, 0);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mRectangle, union, mRectangleUnion, 1, CS(mRectangle), other);
mSCRIPT_DECLARE_STRUCT_METHOD(mRectangle, BOOL, intersection, mRectangleIntersection, 1, CS(mRectangle), other);
mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(mRectangle, center, mRectangleCenter, 1, S(mRectangle), other);
mSCRIPT_DECLARE_STRUCT_METHOD(mRectangle, W(mSize), size, _mRectangleSize, 0);
mSCRIPT_DEFINE_STRUCT(mRectangle)
mSCRIPT_DEFINE_CLASS_DOCSTRING("A basic axis-aligned rectangle object")
mSCRIPT_DEFINE_DOCSTRING("Create a copy of this struct::mRectangle")
mSCRIPT_DEFINE_STRUCT_METHOD(mRectangle, copy)
mSCRIPT_DEFINE_DOCSTRING("The x coordinate of the top-left corner")
mSCRIPT_DEFINE_STRUCT_MEMBER(mRectangle, S32, x)
mSCRIPT_DEFINE_DOCSTRING("The y coordinate of the top-left corner")
mSCRIPT_DEFINE_STRUCT_MEMBER(mRectangle, S32, y)
mSCRIPT_DEFINE_DOCSTRING("The width of the rectangle")
mSCRIPT_DEFINE_STRUCT_MEMBER(mRectangle, S32, width)
mSCRIPT_DEFINE_DOCSTRING("The height of the rectangle")
mSCRIPT_DEFINE_STRUCT_MEMBER(mRectangle, S32, height)
mSCRIPT_DEFINE_DOCSTRING("Find the bounding box of the union of this and another rectangle")
mSCRIPT_DEFINE_STRUCT_METHOD(mRectangle, union)
mSCRIPT_DEFINE_DOCSTRING("Find the intersection of this and another rectangle. Returns false if the rectangles don't intersect")
mSCRIPT_DEFINE_STRUCT_METHOD(mRectangle, intersection)
mSCRIPT_DEFINE_DOCSTRING("Center another rectangle inside this one")
mSCRIPT_DEFINE_STRUCT_METHOD(mRectangle, center)
mSCRIPT_DEFINE_DOCSTRING("Return the size of this struct::mRectangle as a struct::mSize object")
mSCRIPT_DEFINE_STRUCT_METHOD(mRectangle, size)
mSCRIPT_DEFINE_END;
mSCRIPT_BIND_FUNCTION(mRectangleNew_Binding, W(mRectangle), _mRectangleNew, 4, S32, x, S32, y, S32, width, S32, height);
static struct mScriptValue* _mSizeNew(int32_t width, int32_t height) {
struct mSize* size = malloc(sizeof(*size));
size->width = width;
size->height = height;
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mSize));
result->value.opaque = size;
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
return result;
}
static struct mScriptValue* _mSizeCopy(const struct mSize* old) {
struct mSize* size = malloc(sizeof(*size));
memcpy(size, old, sizeof(*size));
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mSize));
result->value.opaque = size;
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
return result;
}
mSCRIPT_DECLARE_STRUCT_C_METHOD(mSize, W(mSize), copy, _mSizeCopy, 0);
mSCRIPT_DEFINE_STRUCT(mSize)
mSCRIPT_DEFINE_CLASS_DOCSTRING("A basic size (width/height) object")
mSCRIPT_DEFINE_DOCSTRING("Create a copy of this struct::mSize")
mSCRIPT_DEFINE_STRUCT_METHOD(mSize, copy)
mSCRIPT_DEFINE_DOCSTRING("The width")
mSCRIPT_DEFINE_STRUCT_MEMBER(mSize, S32, width)
mSCRIPT_DEFINE_DOCSTRING("The height")
mSCRIPT_DEFINE_STRUCT_MEMBER(mSize, S32, height)
mSCRIPT_DEFINE_END;
mSCRIPT_BIND_FUNCTION(mSizeNew_Binding, W(mSize), _mSizeNew, 2, S32, width, S32, height);
void mScriptContextAttachStdlib(struct mScriptContext* context) {
struct mScriptValue* lib;
@ -177,11 +271,15 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) {
mScriptContextExportNamespace(context, "util", (struct mScriptKVPair[]) {
mSCRIPT_KV_PAIR(makeBitmask, &mScriptMakeBitmask_Binding),
mSCRIPT_KV_PAIR(expandBitmask, &mScriptExpandBitmask_Binding),
mSCRIPT_KV_PAIR(newRectangle, &mRectangleNew_Binding),
mSCRIPT_KV_PAIR(newSize, &mSizeNew_Binding),
mSCRIPT_KV_SENTINEL
});
mScriptContextSetDocstring(context, "util", "Basic utility library");
mScriptContextSetDocstring(context, "util.makeBitmask", "Compile a list of bit indices into a bitmask");
mScriptContextSetDocstring(context, "util.expandBitmask", "Expand a bitmask into a list of bit indices");
mScriptContextSetDocstring(context, "util.newRectangle", "Create a new mRectangle");
mScriptContextSetDocstring(context, "util.newSize", "Create a new mSize");
struct mScriptValue* systemVersion = mScriptStringCreateFromUTF8(projectVersion);
struct mScriptValue* systemProgram = mScriptStringCreateFromUTF8(projectName);

View File

@ -169,10 +169,100 @@ M_TEST_DEFINE(callbackWeakref) {
mScriptContextDeinit(&context);
}
M_TEST_DEFINE(rectangle) {
SETUP_LUA;
struct mRectangle rect = {
.x = 1,
.y = 2,
.width = 3,
.height = 4,
};
struct mScriptValue rval = mSCRIPT_MAKE_S(mRectangle, &rect);
mScriptContextSetGlobal(&context, "testRect", &rval);
TEST_PROGRAM("assert(testRect)");
TEST_PROGRAM("assert(testRect.x == 1)");
TEST_PROGRAM("assert(testRect.y == 2)");
TEST_PROGRAM("assert(testRect.width == 3)");
TEST_PROGRAM("assert(testRect.height == 4)");
TEST_PROGRAM(
"testRect.x = 10\n"
"testRect.y = 11\n"
"testRect.width = 20\n"
"testRect.height = 21\n"
);
assert_int_equal(rect.x, 10);
assert_int_equal(rect.y, 11);
assert_int_equal(rect.width, 20);
assert_int_equal(rect.height, 21);
TEST_PROGRAM("newRect = util.newRectangle(5, 6, 10, 11)");
TEST_PROGRAM("assert(newRect.x == 5)");
TEST_PROGRAM("assert(newRect.y == 6)");
TEST_PROGRAM("assert(newRect.width == 10)");
TEST_PROGRAM("assert(newRect.height == 11)");
TEST_PROGRAM("testRect2 = testRect:copy()");
TEST_PROGRAM("assert(testRect2.x == 10)");
TEST_PROGRAM("assert(testRect2.y == 11)");
TEST_PROGRAM("assert(testRect2.width == 20)");
TEST_PROGRAM("assert(testRect2.height == 21)");
TEST_PROGRAM("assert(testRect2:intersection(newRect))");
TEST_PROGRAM("assert(testRect2.x == 10)");
TEST_PROGRAM("assert(testRect2.y == 11)");
TEST_PROGRAM("assert(testRect2.width == 5)");
TEST_PROGRAM("assert(testRect2.height == 6)");
TEST_PROGRAM("testRect2:union(testRect)");
TEST_PROGRAM("assert(testRect2.x == 10)");
TEST_PROGRAM("assert(testRect2.y == 11)");
TEST_PROGRAM("assert(testRect2.width == 20)");
TEST_PROGRAM("assert(testRect2.height == 21)");
TEST_PROGRAM("testSize = testRect:size()");
TEST_PROGRAM("assert(testRect.width == testSize.width)");
TEST_PROGRAM("assert(testRect.height == testSize.height)");
mScriptContextDeinit(&context);
}
M_TEST_DEFINE(size) {
SETUP_LUA;
struct mSize size = {
.width = 1,
.height = 2,
};
struct mScriptValue sval = mSCRIPT_MAKE_S(mSize, &size);
mScriptContextSetGlobal(&context, "testSize", &sval);
TEST_PROGRAM("assert(testSize)");
TEST_PROGRAM("assert(testSize.width == 1)");
TEST_PROGRAM("assert(testSize.height == 2)");
TEST_PROGRAM(
"testSize.width = 3\n"
"testSize.height = 4\n"
);
assert_int_equal(size.width, 3);
assert_int_equal(size.height, 4);
TEST_PROGRAM("newSize = util.newSize(5, 6)");
TEST_PROGRAM("assert(newSize.width == 5)");
TEST_PROGRAM("assert(newSize.height == 6)");
TEST_PROGRAM("testSize2 = testSize:copy()");
TEST_PROGRAM("assert(testSize2.width == 3)");
TEST_PROGRAM("assert(testSize2.height == 4)");
mScriptContextDeinit(&context);
}
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptStdlib,
cmocka_unit_test(bitMask),
cmocka_unit_test(bitUnmask),
cmocka_unit_test(callbacks),
cmocka_unit_test(oneshot),
cmocka_unit_test(callbackWeakref),
cmocka_unit_test(rectangle),
cmocka_unit_test(size),
)