Skip to content

Instantly share code, notes, and snippets.

@mworzala
Created November 17, 2024 17:31
Show Gist options
  • Save mworzala/133181addcd835e9c900fae8ee575bd3 to your computer and use it in GitHub Desktop.
Save mworzala/133181addcd835e9c900fae8ee575bd3 to your computer and use it in GitHub Desktop.
idlc vector compilation
{
"@roblox/globaltype/vector": {
"documentation": "A 3 dimensional vector type i guess\n\n@see https://hollowcube.net/docs/luau/reference",
"code_sample": "",
"learn_more_link": "https://hollowcube.net/docs/luau/reference",
"keys": {
"X": "@roblox/globaltype/vector.X",
"Y": "@roblox/globaltype/vector.Y",
"Z": "@roblox/globaltype/vector.Z",
"Length": "@roblox/globaltype/vector.Length",
"Abs": "@roblox/globaltype/vector.Abs",
"Floor": "@roblox/globaltype/vector.Floor",
"Ceil": "@roblox/globaltype/vector.Ceil",
"Min": "@roblox/globaltype/vector.Min",
"Max": "@roblox/globaltype/vector.Max",
"Lerp": "@roblox/globaltype/vector.Lerp",
"Angle": "@roblox/globaltype/vector.Angle",
"Dot": "@roblox/globaltype/vector.Dot",
"Cross": "@roblox/globaltype/vector.Cross",
"FEqual": "@roblox/globaltype/vector.FEqual"
}
},
"@roblox/globaltype/vector.X": {
"documentation": "",
"code_sample": "",
"learn_more_link": ""
},
"@roblox/globaltype/vector.Y": {
"documentation": "",
"code_sample": "",
"learn_more_link": ""
},
"@roblox/globaltype/vector.Z": {
"documentation": "",
"code_sample": "",
"learn_more_link": ""
},
"@roblox/globaltype/vector.Length": {
"documentation": "",
"code_sample": "",
"learn_more_link": ""
},
"@roblox/globaltype/vector.Abs": {
"documentation": "",
"code_sample": "",
"learn_more_link": "",
"params": [],
"returns": [
"@roblox/globaltype/vector.Abs/return/0"
]
},
"@roblox/globaltype/vector.Abs/return/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Floor": {
"documentation": "",
"code_sample": "",
"learn_more_link": "",
"params": [],
"returns": [
"@roblox/globaltype/vector.Floor/return/0"
]
},
"@roblox/globaltype/vector.Floor/return/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Ceil": {
"documentation": "",
"code_sample": "",
"learn_more_link": "",
"params": [],
"returns": [
"@roblox/globaltype/vector.Ceil/return/0"
]
},
"@roblox/globaltype/vector.Ceil/return/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Min": {
"documentation": "",
"code_sample": "",
"learn_more_link": "",
"params": [
{
"name": "other",
"documentation": "@roblox/globaltype/vector.Min/param/0"
}
],
"returns": [
"@roblox/globaltype/vector.Min/return/0"
]
},
"@roblox/globaltype/vector.Min/param/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Min/return/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Max": {
"documentation": "",
"code_sample": "",
"learn_more_link": "",
"params": [
{
"name": "other",
"documentation": "@roblox/globaltype/vector.Max/param/0"
}
],
"returns": [
"@roblox/globaltype/vector.Max/return/0"
]
},
"@roblox/globaltype/vector.Max/param/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Max/return/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Lerp": {
"documentation": "",
"code_sample": "",
"learn_more_link": "",
"params": [
{
"name": "to",
"documentation": "@roblox/globaltype/vector.Lerp/param/0"
},
{
"name": "t",
"documentation": "@roblox/globaltype/vector.Lerp/param/1"
}
],
"returns": [
"@roblox/globaltype/vector.Lerp/return/0"
]
},
"@roblox/globaltype/vector.Lerp/param/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Lerp/param/1": {
"documentation": ""
},
"@roblox/globaltype/vector.Lerp/return/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Angle": {
"documentation": "",
"code_sample": "",
"learn_more_link": "",
"params": [
{
"name": "other",
"documentation": "@roblox/globaltype/vector.Angle/param/0"
},
{
"name": "axis",
"documentation": "@roblox/globaltype/vector.Angle/param/1"
}
],
"returns": [
"@roblox/globaltype/vector.Angle/return/0"
]
},
"@roblox/globaltype/vector.Angle/param/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Angle/param/1": {
"documentation": ""
},
"@roblox/globaltype/vector.Angle/return/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Dot": {
"documentation": "",
"code_sample": "",
"learn_more_link": "",
"params": [
{
"name": "other",
"documentation": "@roblox/globaltype/vector.Dot/param/0"
}
],
"returns": [
"@roblox/globaltype/vector.Dot/return/0"
]
},
"@roblox/globaltype/vector.Dot/param/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Dot/return/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Cross": {
"documentation": "",
"code_sample": "",
"learn_more_link": "",
"params": [
{
"name": "other",
"documentation": "@roblox/globaltype/vector.Cross/param/0"
}
],
"returns": [
"@roblox/globaltype/vector.Cross/return/0"
]
},
"@roblox/globaltype/vector.Cross/param/0": {
"documentation": ""
},
"@roblox/globaltype/vector.Cross/return/0": {
"documentation": ""
},
"@roblox/globaltype/vector.FEqual": {
"documentation": "",
"code_sample": "",
"learn_more_link": "",
"params": [
{
"name": "other",
"documentation": "@roblox/globaltype/vector.FEqual/param/0"
},
{
"name": "epsilon",
"documentation": "@roblox/globaltype/vector.FEqual/param/1"
}
],
"returns": [
"@roblox/globaltype/vector.FEqual/return/0"
]
},
"@roblox/globaltype/vector.FEqual/param/0": {
"documentation": ""
},
"@roblox/globaltype/vector.FEqual/param/1": {
"documentation": ""
},
"@roblox/globaltype/vector.FEqual/return/0": {
"documentation": ""
},
"@roblox/global/vec/param/0": {
"documentation": "The X component"
},
"@roblox/global/vec/param/1": {
"documentation": "The Y component"
},
"@roblox/global/vec/param/2": {
"documentation": "The Z component"
},
"@roblox/global/vec/return/0": {
"documentation": "The new vector instance"
},
"@roblox/global/vec": {
"documentation": "Creates a new vector instance from the given XYZ components.\n\n```",
"code_sample": "code sample here.",
"learn_more_link": "https://hollowcube.net/docs/luau/reference#vec",
"params": [
{
"name": "x",
"documentation": "@roblox/global/vec/param/0"
},
{
"name": "y",
"documentation": "@roblox/global/vec/param/1"
},
{
"name": "z",
"documentation": "@roblox/global/vec/param/2"
}
],
"returns": [
"@roblox/global/vec/return/0"
]
}
}
// GENERATED FROM IDL FILE. DO NOT MODIFY.
package net.hollowcube.mapmaker.scripting.api.math;
import net.hollowcube.luau.LuaState;
final class Vector$idl {
private static int __eq$vector(LuaState state) {
var result = Vector.__eq(state);
state.pushBoolean(result);
return 1;
}
private static int index$vector(LuaState state) {
var fieldName = state.checkStringArg(2);
return switch (fieldName) {
case "X" -> {
var result = Vector.getX(state);
state.pushNumber(result);
yield 1;
}
case "Y" -> {
var result = Vector.getY(state);
state.pushNumber(result);
yield 1;
}
case "Z" -> {
var result = Vector.getZ(state);
state.pushNumber(result);
yield 1;
}
case "Length" -> {
var result = Vector.getLength(state);
state.pushNumber(result);
yield 1;
}
default -> {
state.error("Unknown field: " + fieldName);
yield 0;
}
};
}
private static int namecall$vector(LuaState state) {
var methodName = state.nameCallAtom();
return switch (methodName) {
case "Abs" -> {
yield Vector.abs(state);
}
case "Floor" -> {
yield Vector.floor(state);
}
case "Ceil" -> {
yield Vector.ceil(state);
}
case "Min" -> {
yield Vector.min(state);
}
case "Max" -> {
yield Vector.max(state);
}
case "Lerp" -> {
yield Vector.lerp(state);
}
case "Angle" -> {
var result = Vector.angle(state);
state.pushNumber(result);
yield 1;
}
case "Dot" -> {
var result = Vector.dot(state);
state.pushNumber(result);
yield 1;
}
case "Cross" -> {
yield Vector.cross(state);
}
case "FEqual" -> {
var result = Vector.fEqual(state);
state.pushBoolean(result);
yield 1;
}
default -> {
state.error("Unknown method: vector." + methodName);
yield 0;
}
};
}
private static int vec$g(LuaState state) {
var arg0 = (float) state.checkNumberArg(1);
var arg1 = (float) state.checkNumberArg(2);
var arg2 = (float) state.checkNumberArg(3);
var result = Vector.vec(state, arg0, arg1, arg2);
return result;
}
public static void init$j(LuaState global) {
// builtin vector
global.newMetaTable("vector");
global.pushCFunction(Vector::__add, "__add");
global.setField(-2, "__add");
global.pushCFunction(Vector::__sub, "__sub");
global.setField(-2, "__sub");
global.pushCFunction(Vector::__mul, "__mul");
global.setField(-2, "__mul");
global.pushCFunction(Vector::__div, "__div");
global.setField(-2, "__div");
global.pushCFunction(Vector::__idiv, "__idiv");
global.setField(-2, "__idiv");
global.pushCFunction(Vector::__unm, "__unm");
global.setField(-2, "__unm");
global.pushCFunction(Vector$idl::__eq$vector, "__eq");
global.setField(-2, "__eq");
global.pushCFunction(Vector$idl::index$vector, "__index");
global.setField(-2, "__index");
global.pushCFunction(Vector$idl::namecall$vector, "__namecall");
global.setField(-2, "__namecall");
global.pop(1);
// builtin vector (replace metatable)
global.pushVector(0, 0, 0);
global.getMetaTable("vector");
global.setMetaTable(-2);
global.pop(1);
global.pushCFunction(Vector$idl::vec$g, "vec");
global.setGlobal("vec");
}
}
declare class vector {
X: number
Y: number
Z: number
Length: number
function Abs(): vector
function Floor(): vector
function Ceil(): vector
function Min(other: ...vector): vector
function Max(other: ...vector): vector
function Lerp(to: vector, t: number): vector
function Angle(other: vector, axis: vector?): number
function Dot(other: vector): number
function Cross(other: vector): vector
function __add(other: vector | number): vector
function __sub(other: vector | number): vector
function __mul(other: vector | number): vector
function __div(other: vector | number): vector
function __idiv(other: vector | number): vector
function __unm(): vector
function __eq(other: vector): boolean
function FEqual(other: vector, epsilon: number): boolean
}
declare function vec(x: number, y: number, z: number): vector
// A 3 dimensional vector type i guess
//
// @see https://hollowcube.net/docs/luau/reference
interface vector {
readonly X: number
// The Y component of the vector.
readonly Y: number
readonly Z: number
// The length of the vector
readonly Length: number
function Abs(): vector
function Floor(): vector
function Ceil(): vector
function Min(other: ...vector): vector
function Max(other: ...vector): vector
function Lerp(to: vector, t: number): vector
function Angle(other: vector, axis: vector?): number
function Dot(other: vector): number
function Cross(other: vector): vector
function __add(other: vector | number): vector
function __sub(other: vector | number): vector
function __mul(other: vector | number): vector
function __div(other: vector | number): vector
function __idiv(other: vector | number): vector
function __unm(): vector
function __eq(other: vector): boolean
function FEqual(other: vector, epsilon: number): boolean
}
// Creates a new vector instance from the given XYZ components.
//
// ```
// code sample here.
// ```
//
// @param x The X component
// @param y The Y component
// @param z The Z component
// @return The new vector instance
// @see https://hollowcube.net/docs/luau/reference#vec
function vec(x: number, y: number, z: number): vector
package net.hollowcube.mapmaker.scripting.api.math;
import net.hollowcube.luau.LuaState;
import net.hollowcube.luau.idl.RetCount;
import org.jetbrains.annotations.NotNull;
public final class Vector {
// Global constructor
public static @RetCount int vec(@NotNull LuaState state, float x, float y, float z) {
state.pushVector(x, y, z);
return 1;
}
// Methods
public static float getX(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
return vector[0];
}
public static float getY(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
return vector[1];
}
public static float getZ(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
return vector[2];
}
public static float getLength(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
return (float) Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
}
public static @RetCount int abs(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
state.pushVector(Math.abs(vector[0]), Math.abs(vector[1]), Math.abs(vector[2]));
return 1;
}
public static @RetCount int floor(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
state.pushVector((float) Math.floor(vector[0]), (float) Math.floor(vector[1]), (float) Math.floor(vector[2]));
return 1;
}
public static @RetCount int ceil(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
state.pushVector((float) Math.ceil(vector[0]), (float) Math.ceil(vector[1]), (float) Math.ceil(vector[2]));
return 1;
}
public static @RetCount int min(@NotNull LuaState state) {
int top = state.getTop();
float[] min = state.checkVectorArg(1);
for (int i = 2; i <= top; i++) {
float[] vector = state.checkVectorArg(i);
min[0] = Math.min(min[0], vector[0]);
min[1] = Math.min(min[1], vector[1]);
min[2] = Math.min(min[2], vector[2]);
}
state.pushVector(min[0], min[1], min[2]);
return 1;
}
public static @RetCount int max(@NotNull LuaState state) {
int top = state.getTop();
float[] max = state.checkVectorArg(1);
for (int i = 2; i <= top; i++) {
float[] vector = state.checkVectorArg(i);
max[0] = Math.max(max[0], vector[0]);
max[1] = Math.max(max[1], vector[1]);
max[2] = Math.max(max[2], vector[2]);
}
state.pushVector(max[0], max[1], max[2]);
return 1;
}
public static @RetCount int lerp(@NotNull LuaState state) {
float[] a = state.checkVectorArg(1);
float[] b = state.checkVectorArg(2);
float t = (float) state.checkNumberArg(3);
state.pushVector(a[0] + (b[0] - a[0]) * t,
a[1] + (b[1] - a[1]) * t,
a[2] + (b[2] - a[2]) * t);
return 1;
}
public static float angle(@NotNull LuaState state) {
state.checkVectorArg(1);
state.checkVectorArg(2);
state.checkVectorArg(3);
throw new UnsupportedOperationException();
}
public static float dot(@NotNull LuaState state) {
state.checkVectorArg(1);
throw new UnsupportedOperationException();
}
public static @RetCount int cross(@NotNull LuaState state) {
state.checkVectorArg(1);
throw new UnsupportedOperationException();
}
public static @RetCount int __add(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
if (state.isVector(2)) {
float[] other = state.toVector(2);
state.pushVector(vector[0] + other[0], vector[1] + other[1], vector[2] + other[2]);
return 1;
} else if (state.isNumber(2)) {
float other = (float) state.toNumber(2);
state.pushVector(vector[0] + other, vector[1] + other, vector[2] + other);
return 1;
} else {
state.typeError(1, "number or vector");
return 0;
}
}
public static @RetCount int __sub(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
if (state.isVector(2)) {
float[] other = state.toVector(2);
state.pushVector(vector[0] - other[0], vector[1] - other[1], vector[2] - other[2]);
return 1;
} else if (state.isNumber(2)) {
float other = (float) state.toNumber(2);
state.pushVector(vector[0] - other, vector[1] - other, vector[2] - other);
return 1;
} else {
state.typeError(1, "number or vector");
return 0;
}
}
public static @RetCount int __mul(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
if (state.isVector(2)) {
float[] other = state.toVector(2);
state.pushVector(vector[0] * other[0], vector[1] * other[1], vector[2] * other[2]);
return 1;
} else if (state.isNumber(2)) {
float other = (float) state.toNumber(2);
state.pushVector(vector[0] * other, vector[1] * other, vector[2] * other);
return 1;
} else {
state.typeError(1, "number or vector");
return 0;
}
}
public static @RetCount int __div(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
if (state.isVector(2)) {
float[] other = state.toVector(2);
state.pushVector(vector[0] / other[0], vector[1] / other[1], vector[2] / other[2]);
return 1;
} else if (state.isNumber(2)) {
float other = (float) state.toNumber(2);
state.pushVector(vector[0] / other, vector[1] / other, vector[2] / other);
return 1;
} else {
state.typeError(1, "number or vector");
return 0;
}
}
public static @RetCount int __idiv(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
if (state.isVector(2)) {
float[] other = state.toVector(2);
state.pushVector(floorDivF(vector[0], other[0]), floorDivF(vector[1], other[1]), floorDivF(vector[2], other[2]));
return 1;
} else if (state.isNumber(2)) {
float other = (float) state.toNumber(2);
state.pushVector(floorDivF(vector[0], other), floorDivF(vector[1], other), floorDivF(vector[2], other));
return 1;
} else {
state.typeError(1, "number or vector");
return 0;
}
}
public static @RetCount int __unm(@NotNull LuaState state) {
float[] vector = state.checkVectorArg(1);
state.pushVector(-vector[0], -vector[1], -vector[2]);
return 1;
}
public static boolean __eq(@NotNull LuaState state) {
float[] a = state.checkVectorArg(1);
float[] b = state.checkVectorArg(2);
return a[0] == b[0] && a[1] == b[1] && a[2] == b[2];
}
public static boolean fEqual(@NotNull LuaState state) {
float[] a = state.checkVectorArg(1);
float[] b = state.checkVectorArg(2);
float epsilon = (float) state.checkNumberArg(3);
return Math.abs(a[0] - b[0]) < epsilon &&
Math.abs(a[1] - b[1]) < epsilon &&
Math.abs(a[2] - b[2]) < epsilon;
}
private static float floorDivF(float a, float b) {
return (float) Math.floorDiv((int) a, (int) b);
}
}
package net.hollowcube.mapmaker.scripting.api.math;
import net.hollowcube.luau.LuaState;
import net.hollowcube.luau.idl.IDLInjector;
import net.hollowcube.mapmaker.scripting.util.LuaStateResolver;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.extension.ExtendWith;
import static net.hollowcube.mapmaker.scripting.util.LuaAssertions.assertDoesNotFail;
import static net.hollowcube.mapmaker.scripting.util.LuaAssertions.assertVectorEquals;
import static net.hollowcube.mapmaker.scripting.util.LuaTests.describe;
import static net.hollowcube.mapmaker.scripting.util.LuaTests.it;
@ExtendWith(LuaStateResolver.class)
class VectorTest {
@Test
void vecConstructor(@NotNull LuaState global) {
IDLInjector.inject(Vector.class, global);
// Worth testing this as a sanity check since it is used in all of the native tests.
var state = assertDoesNotFail(global, """
return vec(1, 2, 3)
""", 1);
assertVectorEquals(state, -1, 1, 2, 3);
}
@TestFactory
@NotNull Iterable<DynamicTest> luaTests() {
return describe("Vector", Vector.class,
it("should correctly return xyz components", """
local v = vec(4, 5, 6)
assertRawEquals(v.X, 4)
assertRawEquals(v.Y, 5)
assertRawEquals(v.Z, 6)
"""),
it("should correctly return length", """
local v = vec(3, 4, 5)
assertRawEquals(v.Length, 7.071067810058594)
"""),
it("should correctly add vectors & scalars", """
assertRawEquals(
vec(1, 2, 3) + vec(4, 5, 6),
vec(5, 7, 9)
)
assertRawEquals(
vec(1, 2, 3) + 1,
vec(2, 3, 4)
)
"""),
it("should correctly subtract vectors & scalars", """
assertRawEquals(
vec(1, 2, 3) - vec(1, 2, 3),
vec(0, 0, 0)
)
assertRawEquals(
vec(1, 2, 3) - 1,
vec(0, 1, 2)
)
"""),
it("should support abs call", """
assertRawEquals(
vec(-1, 2, -3):Abs(),
vec(1, 2, 3)
)
"""),
it("should support be inverted with unary minus", """
assertRawEquals(
-vec(-1, 2, -3),
vec(1, -2, 3)
)
""")
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment