@luminocity/lemonate-engine 26.3.15 → 26.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/items/AudioCollectionItem.d.ts +5 -1
- package/dist/items/MaterialCollectionItem.d.ts +0 -1
- package/dist/items/MeshCollectionItem.d.ts +5 -1
- package/dist/lemonate.es.js +47 -32
- package/dist/lemonate.js +47 -32
- package/dist/lemonate.min.js +4 -4
- package/dist/lemonate.umd.js +47 -32
- package/dist/player.zip +0 -0
- package/dist/subsystems/scripting/runtime/RtSceneObject.d.ts +6 -1
- package/package.json +2 -2
package/dist/lemonate.es.js
CHANGED
|
@@ -41290,7 +41290,7 @@ class Item extends EventEmitter {
|
|
|
41290
41290
|
}
|
|
41291
41291
|
}
|
|
41292
41292
|
|
|
41293
|
-
var engine$1 = "26.3.
|
|
41293
|
+
var engine$1 = "26.3.16";
|
|
41294
41294
|
var bullet = "3.26";
|
|
41295
41295
|
var lua = "5.4.3";
|
|
41296
41296
|
var packageVersionInfo = {
|
|
@@ -45772,17 +45772,20 @@ class AudioCollectionItem extends Item {
|
|
|
45772
45772
|
}
|
|
45773
45773
|
return null;
|
|
45774
45774
|
}
|
|
45775
|
-
|
|
45776
|
-
const
|
|
45777
|
-
if (this.listEntries) {
|
|
45778
|
-
for (const
|
|
45775
|
+
getDisplayEntries() {
|
|
45776
|
+
const entries = [];
|
|
45777
|
+
if (this.audioCollection && this.listEntries) {
|
|
45778
|
+
for (const [id, audioItem] of this.audioCollection.entries()) {
|
|
45779
|
+
const listEntry = this.listEntries.get(id);
|
|
45780
|
+
if (!listEntry)
|
|
45781
|
+
continue;
|
|
45779
45782
|
const name = this._getDisplayNameFromListEntry(listEntry);
|
|
45780
45783
|
if (name) {
|
|
45781
|
-
|
|
45784
|
+
entries.push({ value: name, label: name, item: audioItem.item });
|
|
45782
45785
|
}
|
|
45783
45786
|
}
|
|
45784
45787
|
}
|
|
45785
|
-
return
|
|
45788
|
+
return entries;
|
|
45786
45789
|
}
|
|
45787
45790
|
getDisplayNameFromAudio(audio) {
|
|
45788
45791
|
if (!this.audioCollection || !this.listEntries)
|
|
@@ -81828,16 +81831,6 @@ class MaterialCollectionItem extends Item {
|
|
|
81828
81831
|
}
|
|
81829
81832
|
return null;
|
|
81830
81833
|
}
|
|
81831
|
-
getDisplayNames() {
|
|
81832
|
-
const names = [];
|
|
81833
|
-
for (const listEntry of this.listEntries.values()) {
|
|
81834
|
-
const name = this._getDisplayNameFromListEntry(listEntry);
|
|
81835
|
-
if (name) {
|
|
81836
|
-
names.push(name);
|
|
81837
|
-
}
|
|
81838
|
-
}
|
|
81839
|
-
return names.filter(Boolean);
|
|
81840
|
-
}
|
|
81841
81834
|
getDisplayNameFromMaterial(material) {
|
|
81842
81835
|
for (const [id, materialCollectionMaterial] of this.materials.entries()) {
|
|
81843
81836
|
if (material === materialCollectionMaterial) {
|
|
@@ -82069,17 +82062,20 @@ class MeshCollectionItem extends Item {
|
|
|
82069
82062
|
}
|
|
82070
82063
|
return undefined;
|
|
82071
82064
|
}
|
|
82072
|
-
|
|
82073
|
-
const
|
|
82074
|
-
if (this.listEntries) {
|
|
82075
|
-
for (const
|
|
82065
|
+
getDisplayEntries() {
|
|
82066
|
+
const entries = [];
|
|
82067
|
+
if (this.meshes && this.listEntries) {
|
|
82068
|
+
for (const [id, meshItem] of this.meshes.entries()) {
|
|
82069
|
+
const listEntry = this.listEntries.get(id);
|
|
82070
|
+
if (!listEntry)
|
|
82071
|
+
continue;
|
|
82076
82072
|
const name = this._getDisplayNameFromListEntry(listEntry);
|
|
82077
82073
|
if (name) {
|
|
82078
|
-
|
|
82074
|
+
entries.push({ value: name, label: name, item: meshItem.item });
|
|
82079
82075
|
}
|
|
82080
82076
|
}
|
|
82081
82077
|
}
|
|
82082
|
-
return
|
|
82078
|
+
return entries;
|
|
82083
82079
|
}
|
|
82084
82080
|
getDisplayNameFromMesh(mesh) {
|
|
82085
82081
|
if (this.meshes && this.listEntries) {
|
|
@@ -106477,10 +106473,10 @@ class SgPhysicsItem extends SgItem {
|
|
|
106477
106473
|
if (!this.customMeshColliderMeshCollection) {
|
|
106478
106474
|
return;
|
|
106479
106475
|
}
|
|
106480
|
-
const
|
|
106476
|
+
const entries = this.customMeshColliderMeshCollection.getDisplayEntries();
|
|
106481
106477
|
const meshCollectionEntry = this.getField("ColliderMeshCollectionEntry");
|
|
106482
106478
|
if (meshCollectionEntry) {
|
|
106483
|
-
meshCollectionEntry.options =
|
|
106479
|
+
meshCollectionEntry.options = entries;
|
|
106484
106480
|
}
|
|
106485
106481
|
}
|
|
106486
106482
|
_setMeshItemFromColliderMeshCollection() {
|
|
@@ -107241,10 +107237,10 @@ class SgMesh extends SgPhysicsItem {
|
|
|
107241
107237
|
if (!this.meshCollection) {
|
|
107242
107238
|
return;
|
|
107243
107239
|
}
|
|
107244
|
-
const
|
|
107240
|
+
const entries = this.meshCollection.getDisplayEntries();
|
|
107245
107241
|
const meshCollectionEntry = this.getField("MeshCollectionEntry");
|
|
107246
107242
|
if (meshCollectionEntry)
|
|
107247
|
-
meshCollectionEntry.options =
|
|
107243
|
+
meshCollectionEntry.options = entries;
|
|
107248
107244
|
}
|
|
107249
107245
|
_setMeshItemFromMeshCollection() {
|
|
107250
107246
|
if (!this.meshCollection)
|
|
@@ -108207,10 +108203,10 @@ class SgPoints extends SgItem {
|
|
|
108207
108203
|
if (!this.meshCollection) {
|
|
108208
108204
|
return;
|
|
108209
108205
|
}
|
|
108210
|
-
const
|
|
108206
|
+
const entries = this.meshCollection.getDisplayEntries();
|
|
108211
108207
|
const meshCollectionEntry = this.getField("MeshCollectionEntry");
|
|
108212
108208
|
if (meshCollectionEntry)
|
|
108213
|
-
meshCollectionEntry.options =
|
|
108209
|
+
meshCollectionEntry.options = entries;
|
|
108214
108210
|
}
|
|
108215
108211
|
destroy(noDispose = false, withChildren = false, noRemoveUpdateListener = false) {
|
|
108216
108212
|
super.destroy(true, withChildren, noRemoveUpdateListener);
|
|
@@ -119923,10 +119919,10 @@ class SgAudio extends SgItem {
|
|
|
119923
119919
|
if (!this.audioCollection) {
|
|
119924
119920
|
return;
|
|
119925
119921
|
}
|
|
119926
|
-
const
|
|
119922
|
+
const entries = this.audioCollection.getDisplayEntries();
|
|
119927
119923
|
const audioCollectionEntry = this.getField("AudioCollectionEntry");
|
|
119928
119924
|
if (audioCollectionEntry)
|
|
119929
|
-
audioCollectionEntry.options =
|
|
119925
|
+
audioCollectionEntry.options = entries;
|
|
119930
119926
|
}
|
|
119931
119927
|
_setAudioItemFromAudioCollection() {
|
|
119932
119928
|
if (!this.audioCollection)
|
|
@@ -131547,6 +131543,8 @@ var lua_api_math = "\n--- @module math\n\nmath.deg2Rad = math.pi / 180\nmath.rad
|
|
|
131547
131543
|
|
|
131548
131544
|
var lua_api_math_complex = "--[[\n\nLUA MODULE\n\n complex v$(_VERSION) - complex numbers implemented as Lua tables\n\nSYNOPSIS\n\n local complex = require 'complex'\n local cx1 = complex \"2+3i\" -- or complex.new(2, 3)\n local cx2 = complex \"3+2i\"\n __assertTrace( complex.add(cx1,cx2) == complex \"5+5i\" )\n __assertTrace( tostring(cx1) == \"2+3i\" )\n\nDESCRIPTION\n\n 'complex' provides common tasks with complex numbers\n\n function complex.to( arg ); complex( arg )\n returns a complex number on success, nil on failure\n arg := number or { number,number } or ( \"(-)<number>\" and/or \"(+/-)<number>i\" )\n e.g. 5; {2,3}; \"2\", \"2+i\", \"-2i\", \"2^2*3+1/3i\"\n note: 'i' is always in the numerator, spaces are not allowed\n\n A complex number is defined as Cartesian complex number\n complex number := { real_part, imaginary_part } .\n This gives fast access to both parts of the number for calculation.\n The access is faster than in a hash table\n The metatable is just an add on. When it comes to speed, one is faster using a direct function call.\n\nAPI\n\n See code and test_complex.lua.\n\nDEPENDENCIES\n\n None (other than Lua 5.1 or 5.2).\n\nHOME PAGE\n\n http://luamatrix.luaforge.net\n http://lua-users.org/wiki/LuaMatrix\n\nDOWNLOAD/INSTALL\n\n ./util.mk\n cd tmp/*\n luarocks make\n\n Licensed under the same terms as Lua itself.\n\n Developers:\n Michael Lutz (chillcode)\n David Manura http://lua-users.org/wiki/DavidManura (maintainer)\n--]]\n\n--/////////////--\n--// complex //--\n--/////////////--\n---@class Complex\n---@operator +(Complex a, Complex b): Complex # Add two complex numbers\n---@operator -(Complex a, Complex b): Complex # Subtract two complex numbers\n---@operator *(Complex a, Complex b|number): Complex # Multiply complex by another complex or scalar\n---@operator /(Complex a, Complex b|number): Complex # Divide complex by another complex or scalar\n---@operator ^(Complex a, number|'*'): Complex # Power or conjugate (when `'*'`)\n---@operator -(): Complex # Unary minus\n---@operator ==(Complex a, Complex b): boolean # Check equality between two complex numbers\n---@operator ..(Complex a, Complex b): string # Concatenate two complex numbers as strings\n-- link to complex table\nlocal complex = {\n _TYPE='module',\n _NAME='complex',\n _VERSION='0.3.3.20111212',\n _isComplex=true\n}\n\n-- link to complex metatable\nlocal complex_meta = {}\n\n-- helper functions for parsing complex number strings.\nlocal function parse_scalar(s, pos0)\n local x, n, pos = s:match('^([+-]?[%d%.]+)(.?)()', pos0)\n if not x then return end\n if n == 'e' or n == 'E' then\n local x2, n2, pos2 = s:match('^([+-]?%d+)(.?)()', pos)\n if not x2 then error 'number format error' end\n x = tonumber(x..n..x2)\n if not x then error 'number format error' end\n return x, n2, pos2\n else\n x = tonumber(x)\n if not x then error 'number format error' end\n return x, n, pos\n end\nend\nlocal function parse_component(s, pos0)\n local x, n, pos = parse_scalar(s, pos0)\n if not x then\n local x2, n2, pos2 = s:match('^([+-]?)(i)()$', pos0)\n if not x2 then error 'number format error' end\n return (x2=='-' and -1 or 1), n2, pos2\n end\n if n == '/' then\n local x2, n2, pos2 = parse_scalar(s, pos)\n x = x / x2\n return x, n2, pos2\n end\n return x, n, pos\nend\nlocal function parse_complex(s)\n local x, n, pos = parse_component(s, 1)\n if n == '+' or n == '-' then\n local x2, n2, pos2 = parse_component(s, pos)\n if n2 ~= 'i' or pos2 ~= #s+1 then error 'number format error' end\n if n == '-' then x2 = - x2 end\n return x, x2\n elseif n == '' then\n return x, 0\n elseif n == 'i' then\n if pos ~= #s+1 then error 'number format error' end\n return 0, x\n else\n error 'number format error'\n end\nend\n\n-- complex.to( arg )\n-- return a complex number on success\n-- return nil on failure\nfunction complex.to( num )\n -- check for table type\n if type( num ) == \"table\" then\n -- check for a complex number\n if getmetatable( num ) == complex_meta then\n return num\n end\n local real,imag = tonumber( num[1] ),tonumber( num[2] )\n if real and imag then\n return Class.__setmetatable( { real,imag }, complex_meta )\n end\n return\n end\n -- check for number\n local isnum = tonumber( num )\n if isnum then\n return Class.__setmetatable( { isnum,0 }, complex_meta )\n end\n if type( num ) == \"string\" then\n local real, imag = parse_complex(num)\n return Class.__setmetatable( { real, imag }, complex_meta )\n end\nend\n\n-- complex( arg )\n-- same as complex.to( arg )\n-- set __call behaviour of complex\nClass.__setmetatable( complex, { __call = function( _,num ) return complex.to( num ) end } )\n\n-- complex.new( real, complex )\n-- fast function to get a complex number, not invoking any checks\nfunction complex.new( ... )\n return Class.__setmetatable( { ... }, complex_meta )\nend\n\n-- complex.type( arg )\n-- is argument of type complex\nfunction complex.type( arg )\n if getmetatable( arg ) == complex_meta then\n return \"complex\"\n end\nend\n\n-- complex.convpolar( r, phi )\n-- convert polar coordinates ( r*e^(i*phi) ) to carthesic complex number\n-- r (radius) is a number\n-- phi (angle) must be in radians; e.g. [0 - 2pi]\nfunction complex.convpolar( radius, phi )\n return Class.__setmetatable( { radius * math.cos( phi ), radius * math.sin( phi ) }, complex_meta )\nend\n\n-- complex.convpolardeg( r, phi )\n-- convert polar coordinates ( r*e^(i*phi) ) to carthesic complex number\n-- r (radius) is a number\n-- phi must be in degrees; e.g. [0 - 360 deg]\nfunction complex.convpolardeg( radius, phi )\n phi = phi/180 * math.pi\n return Class.__setmetatable( { radius * math.cos( phi ), radius * math.sin( phi ) }, complex_meta )\nend\n\n--// complex number functions only\n\n-- complex.tostring( cx [, formatstr] )\n-- to string or real number\n-- takes a complex number and returns its string value or real number value\nfunction complex.tostring( cx,formatstr )\n local real,imag = cx[1],cx[2]\n if formatstr then\n if imag == 0 then\n return string.format( formatstr, real )\n elseif real == 0 then\n return string.format( formatstr, imag )..\"i\"\n elseif imag > 0 then\n return string.format( formatstr, real )..\"+\"..string.format( formatstr, imag )..\"i\"\n end\n return string.format( formatstr, real )..string.format( formatstr, imag )..\"i\"\n end\n if imag == 0 then\n return real\n elseif real == 0 then\n return ((imag==1 and \"\") or (imag==-1 and \"-\") or imag)..\"i\"\n elseif imag > 0 then\n return real..\"+\"..(imag==1 and \"\" or imag)..\"i\"\n end\n return real..(imag==-1 and \"-\" or imag)..\"i\"\nend\n\n-- complex.print( cx [, formatstr] )\n-- print a complex number\nfunction complex.print( ... )\n print( complex.tostring( ... ) )\nend\n\n-- complex.polar( cx )\n-- from complex number to polar coordinates\n-- output in radians; [-pi,+pi]\n-- returns r (radius), phi (angle)\nfunction complex.polar( cx )\n return math.sqrt( cx[1]^2 + cx[2]^2 ), math.atan2( cx[2], cx[1] )\nend\n\n-- complex.polardeg( cx )\n-- from complex number to polar coordinates\n-- output in degrees; [-180, 180 deg]\n-- returns r (radius), phi (angle)\nfunction complex.polardeg( cx )\n return math.sqrt( cx[1]^2 + cx[2]^2 ), math.atan2( cx[2], cx[1] ) / math.pi * 180\nend\n\n-- complex.norm2( cx )\n-- multiply with conjugate, function returning a scalar number\n-- norm2(x + i*y) returns x^2 + y^2\nfunction complex.norm2( cx )\n return cx[1]^2 + cx[2]^2\nend\n\n-- complex.abs( cx )\n-- get the absolute value of a complex number\nfunction complex.abs( cx )\n return math.sqrt( cx[1]^2 + cx[2]^2 )\nend\n\n-- complex.get( cx )\n-- returns real_part, imaginary_part\nfunction complex.get( cx )\n return cx[1],cx[2]\nend\n\n-- complex.set( cx, real, imag )\n-- sets real_part = real and imaginary_part = imag\nfunction complex.set( cx,real,imag )\n cx[1],cx[2] = real,imag\nend\n\n-- complex.is( cx, real, imag )\n-- returns true if, real_part = real and imaginary_part = imag\n-- else returns false\nfunction complex.is( cx,real,imag )\n if cx[1] == real and cx[2] == imag then\n return true\n end\n return false\nend\n\n--// functions returning a new complex number\n\n-- complex.copy( cx )\n-- copy complex number\nfunction complex.copy( cx )\n return Class.__setmetatable( { cx[1],cx[2] }, complex_meta )\nend\n\n-- complex.add( cx1, cx2 )\n-- add two numbers; cx1 + cx2\nfunction complex.add( cx1,cx2 )\n return Class.__setmetatable( { cx1[1]+cx2[1], cx1[2]+cx2[2] }, complex_meta )\nend\n\n-- complex.sub( cx1, cx2 )\n-- subtract two numbers; cx1 - cx2\nfunction complex.sub( cx1,cx2 )\n return Class.__setmetatable( { cx1[1]-cx2[1], cx1[2]-cx2[2] }, complex_meta )\nend\n\n-- complex.mul( cx1, cx2 )\n-- multiply two numbers; cx1 * cx2\nfunction complex.mul( cx1,cx2 )\n return Class.__setmetatable( { cx1[1]*cx2[1] - cx1[2]*cx2[2],cx1[1]*cx2[2] + cx1[2]*cx2[1] }, complex_meta )\nend\n\n-- complex.mulnum( cx, num )\n-- multiply complex with number; cx1 * num\nfunction complex.mulnum( cx,num )\n return Class.__setmetatable( { cx[1]*num,cx[2]*num }, complex_meta )\nend\n\n-- complex.div( cx1, cx2 )\n-- divide 2 numbers; cx1 / cx2\nfunction complex.div( cx1,cx2 )\n -- get complex value\n local val = cx2[1]^2 + cx2[2]^2\n -- multiply cx1 with conjugate complex of cx2 and divide through val\n return Class.__setmetatable( { (cx1[1]*cx2[1]+cx1[2]*cx2[2])/val,(cx1[2]*cx2[1]-cx1[1]*cx2[2])/val }, complex_meta )\nend\n\n-- complex.divnum( cx, num )\n-- divide through a number\nfunction complex.divnum( cx,num )\n return Class.__setmetatable( { cx[1]/num,cx[2]/num }, complex_meta )\nend\n\n-- complex.pow( cx, num )\n-- get the power of a complex number\nfunction complex.pow( cx,num )\n if math.floor( num ) == num then\n if num < 0 then\n local val = cx[1]^2 + cx[2]^2\n cx = { cx[1]/val,-cx[2]/val }\n num = -num\n end\n local real,imag = cx[1],cx[2]\n for i = 2,num do\n real,imag = real*cx[1] - imag*cx[2],real*cx[2] + imag*cx[1]\n end\n return Class.__setmetatable( { real,imag }, complex_meta )\n end\n -- we calculate the polar complex number now\n -- since then we have the versatility to calc any potenz of the complex number\n -- then we convert it back to a carthesic complex number, we loose precision here\n local length,phi = math.sqrt( cx[1]^2 + cx[2]^2 )^num, math.atan2( cx[2], cx[1] )*num\n return Class.__setmetatable( { length * math.cos( phi ), length * math.sin( phi ) }, complex_meta )\nend\n\n-- complex.sqrt( cx )\n-- get the first squareroot of a complex number, more accurate than cx^.5\nfunction complex.sqrt( cx )\n local len = math.sqrt( cx[1]^2+cx[2]^2 )\n local sign = (cx[2]<0 and -1) or 1\n return Class.__setmetatable( { math.sqrt((cx[1]+len)/2), sign*math.sqrt((len-cx[1])/2) }, complex_meta )\nend\n\n-- complex.ln( cx )\n-- natural logarithm of cx\nfunction complex.ln( cx )\n return Class.__setmetatable( { math.log(math.sqrt( cx[1]^2 + cx[2]^2 )),\n math.atan2( cx[2], cx[1] ) }, complex_meta )\nend\n\n-- complex.exp( cx )\n-- exponent of cx (e^cx)\nfunction complex.exp( cx )\n local expreal = math.exp(cx[1])\n return Class.__setmetatable( { expreal*math.cos(cx[2]), expreal*math.sin(cx[2]) }, complex_meta )\nend\n\n-- complex.conjugate( cx )\n-- get conjugate complex of number\nfunction complex.conjugate( cx )\n return Class.__setmetatable( { cx[1], -cx[2] }, complex_meta )\nend\n\n-- complex.round( cx [,idp] )\n-- round complex numbers, by default to 0 decimal points\nfunction complex.round( cx,idp )\n local mult = 10^( idp or 0 )\n return Class.__setmetatable( { math.floor( cx[1] * mult + 0.5 ) / mult,\n math.floor( cx[2] * mult + 0.5 ) / mult }, complex_meta )\nend\n\n--// variables\ncomplex.zero = complex.new(0, 0)\ncomplex.one = complex.new(1, 0)\n\n--// metatable functions\n\ncomplex_meta.__add = function( cx1,cx2 )\n local cx1,cx2 = complex.to( cx1 ),complex.to( cx2 )\n return complex.add( cx1,cx2 )\nend\ncomplex_meta.__sub = function( cx1,cx2 )\n local cx1,cx2 = complex.to( cx1 ),complex.to( cx2 )\n return complex.sub( cx1,cx2 )\nend\ncomplex_meta.__mul = function( cx1,cx2 )\n local cx1,cx2 = complex.to( cx1 ),complex.to( cx2 )\n return complex.mul( cx1,cx2 )\nend\ncomplex_meta.__div = function( cx1,cx2 )\n local cx1,cx2 = complex.to( cx1 ),complex.to( cx2 )\n return complex.div( cx1,cx2 )\nend\ncomplex_meta.__pow = function( cx,num )\n if num == \"*\" then\n return complex.conjugate( cx )\n end\n return complex.pow( cx,num )\nend\ncomplex_meta.__unm = function( cx )\n return Class.__setmetatable( { -cx[1], -cx[2] }, complex_meta )\nend\ncomplex_meta.__eq = function( cx1,cx2 )\n if cx1[1] == cx2[1] and cx1[2] == cx2[2] then\n return true\n end\n return false\nend\ncomplex_meta.__tostring = function( cx )\n return tostring( complex.tostring( cx ) )\nend\ncomplex_meta.__concat = function( cx,cx2 )\n return tostring(cx)..tostring(cx2)\nend\n-- cx( cx, formatstr )\ncomplex_meta.__call = function( ... )\n print( complex.tostring( ... ) )\nend\ncomplex_meta.__index = {}\nfor k,v in pairs( complex ) do\n complex_meta.__index[k] = v\nend\n\nreturn complex\n\n--///////////////--\n--// chillcode //--\n--///////////////--\n";
|
|
131549
131545
|
|
|
131546
|
+
var lua_api_math_bounds = "\nlocal Vector3 = require('engine/math/vector3')\n\nlocal zero = Vector3.zero\nlocal v3 = Vector3.new\n\n--- @class Bounds\n--- @field center Vector3 The center point of the bounds.\n--- @field extents Vector3 The half-size of the bounds in each dimension.\n--- @field _isBounds boolean Internal flag to identify the object as a Bounds instance.\nlocal Bounds = {\n\t-- NOTE: don't use Vector3.zero here; some methods mutate extents in-place (e.g. expand()).\n\t-- If Vector3.zero is a shared singleton, that would be a global-mutation footgun.\n\tcenter = v3(0, 0, 0),\n\textents = v3(0, 0, 0),\n\t_isBounds = true,\n\t_type = \"Bounds\"\n}\n\nClass.__setmetatable(Bounds, Bounds)\n\nlocal get = {}\nlocal set = {}\n\nBounds.__index = function(t, k)\n\tlocal var = rawget(Bounds, k)\n\n\tif var == nil then\n\t\tvar = rawget(get, k)\n\n\t\tif var ~= nil then\n\t\t\treturn var(t)\n\t\tend\n\tend\n\n\treturn var\nend\n\n--- Creates a new Bounds instance.\n--- @param center Vector3 The center of the bounds.\n--- @param size Vector3 The total size of the bounds.\n--- @return Bounds A new Bounds object.\nfunction Bounds.new(center, size)\n\tlocal bd = {center = v3(0, 0, 0), extents = v3(0, 0, 0), _isBounds = true, _type = \"Bounds\"}\n\tbd.center = center\n\tbd.extents = size * 0.5\n\tClass.__setmetatable(bd, Bounds)\n\treturn bd\nend\n\n--- Gets the center and size of the bounds as separate values.\n--- @return number cx The X component of the center\n--- @return number cy The Y component of the center\n--- @return number cz The Z component of the center\n--- @return number sx The X component of the size\n--- @return number sy The Y component of the size\n--- @return number sz The Z component of the size\nfunction Bounds:get()\n\tlocal size = self:getSize()\n\tlocal center = self.center\n\treturn center.x, center.y, center.z, size.x, size.y, size.z\nend\n\n--- Gets the size (full extents * 2) of the bounds.\n--- @return Vector3 The full size of the bounds.\nfunction Bounds:getSize()\n\treturn self.extents * 2\nend\n\n--- Sets the size of the bounds.\n--- @param value Vector3 The new size of the bounds.\nfunction Bounds:setSize(value)\n\tself.extents = value * 0.5\nend\n\n--- Gets the minimum corner of the bounds.\n--- @return Vector3 The minimum corner position.\nfunction Bounds:getMin()\n\treturn self.center - self.extents\nend\n\n--- Sets the minimum corner, adjusting the bounds accordingly.\n--- @param value Vector3 The new minimum corner.\nfunction Bounds:setMin(value)\n\tself:setMinMax(value, self:getMax())\nend\n\n--- Gets the maximum corner of the bounds.\n--- @return Vector3 The maximum corner position.\nfunction Bounds:getMax()\n\treturn self.center + self.extents\nend\n\n--- Sets the maximum corner, adjusting the bounds accordingly.\n--- @param value Vector3 The new maximum corner.\nfunction Bounds:setMax(value)\n\tself:setMinMax(self:getMin(), value)\nend\n\n--- Sets both the minimum and maximum corners, recalculating center and extents.\n--- @param min Vector3 The new minimum corner.\n--- @param max Vector3 The new maximum corner.\nfunction Bounds:setMinMax(min, max)\n\t-- Normalize, so callers can pass corners in any order without producing negative extents.\n\tlocal realMin = Vector3.min(min, max)\n\tlocal realMax = Vector3.max(min, max)\n\n\tself.extents = (realMax - realMin) * 0.5\n\tself.center = realMin + self.extents\nend\n\n--- Expands the bounds to encapsulate a given point.\n--- @param point Vector3 The point to include within the bounds.\nfunction Bounds:encapsulate(point)\n\tself:setMinMax(Vector3.min(self:getMin(), point), Vector3.max(self:getMax(), point))\nend\n\n--- Expands the bounds by a given amount.\n--- @param amount number|Vector3 The amount to expand by (uniformly if number, per axis if Vector3).\nfunction Bounds:expand(amount)\n\tlocal t = type(amount)\n\n\tif t == \"number\" then\n\t\tamount = amount * 0.5\n\t\tself.extents:add(Vector3.new(amount, amount, amount))\n\telse\n\t\tself.extents:add(amount * 0.5)\n\tend\nend\n\n--- Checks if this bounds intersects with another.\n--- @param bounds Bounds The other bounds to check against.\n--- @return boolean True if the bounds intersect.\nfunction Bounds:intersects(bounds)\n\t-- Fast path that avoids temporary Vector3 allocations.\n\tlocal c1 = self.center\n\tlocal e1 = self.extents\n\tlocal c2 = bounds.center\n\tlocal e2 = bounds.extents\n\n\tlocal min1x, max1x = c1.x - e1.x, c1.x + e1.x\n\tlocal min1y, max1y = c1.y - e1.y, c1.y + e1.y\n\tlocal min1z, max1z = c1.z - e1.z, c1.z + e1.z\n\n\tlocal min2x, max2x = c2.x - e2.x, c2.x + e2.x\n\tlocal min2y, max2y = c2.y - e2.y, c2.y + e2.y\n\tlocal min2z, max2z = c2.z - e2.z, c2.z + e2.z\n\n\treturn min1x <= max2x and max1x >= min2x\n\t and min1y <= max2y and max1y >= min2y\n\t and min1z <= max2z and max1z >= min2z\nend\n\n--- Checks if a point is contained within the bounds.\n--- @param p Vector3 The point to check.\n--- @return boolean True if the point is inside the bounds.\nfunction Bounds:contains(p)\n\t-- Fast path that avoids temporary Vector3 allocations.\n\tlocal c = self.center\n\tlocal e = self.extents\n\n\tlocal minx, maxx = c.x - e.x, c.x + e.x\n\tlocal miny, maxy = c.y - e.y, c.y + e.y\n\tlocal minz, maxz = c.z - e.z, c.z + e.z\n\n\tif p.x < minx or p.y < miny or p.z < minz or p.x > maxx or p.y > maxy or p.z > maxz then\n\t\treturn false\n\tend\n\n\treturn true\nend\n\n--- Converts the bounds to a string representation.\n--- @return string The formatted string representation.\nBounds.__tostring = function(self)\n\treturn string.format(\"Center: %s, Extents %s\", tostring(self.center), tostring(self.extents))\nend\n\n--- Compares two bounds for equality.\n--- @param a Bounds The first bounds.\n--- @param b Bounds The second bounds.\n--- @return boolean True if both bounds have the same center and extents.\nBounds.__eq = function(a, b)\n\treturn a.center == b.center and a.extents == b.extents\nend\n\nget.size = Bounds.getSize\nget.min = Bounds.getMin\nget.max = Bounds.getMax\n\nreturn Bounds\n";
|
|
131547
|
+
|
|
131550
131548
|
var lua_api_math_matrix = "--[[\n\nLUA MODULE\n\n matrix v$(_VERSION) - matrix functions implemented with Lua tables\n\nSYNOPSIS\n\n local matrix = require 'matrix'\n m1 = matrix{{8,4,1},{6,8,3}}\n m2 = matrix{{-8,1,3},{5,2,1}}\n __assertTrace(m1 + m2 == matrix{{0,5,4},{11,10,4}})\n\nDESCRIPTION\n\n With simple matrices this script is quite useful, though for more\n exact calculations, one would probably use a program like Matlab instead.\n Matrices of size 100x100 can still be handled very well.\n The error for the determinant and the inverted matrix is around 10^-9\n with a 100x100 matrix and an element range from -100 to 100.\n\n Characteristics:\n\n\t- functions called via matrix.<function> should be able to handle\n\t any table matrix of structure t[i][j] = value\n\t- can handle a type of complex matrix\n\t- can handle symbolic matrices. (Symbolic matrices cannot be\n\t used with complex matrices.)\n\t- arithmetic functions do not change the matrix itself\n\t but build and return a new matrix\n\t- functions are intended to be light on checks\n\t since one gets a Lua error on incorrect use anyways\n\t- uses mainly Gauss-Jordan elimination\n\t- for Lua tables optimised determinant calculation (fast)\n\t but not invoking any checks for special types of matrices\n\t- vectors can be set up via vec1 = matrix{{ 1,2,3 }}^'T' or matrix{1,2,3}\n\t- vectors can be multiplied to a scalar via num = vec1^'T' * vec2\n\t where num will be a matrix with the result in mtx[1][1],\n\t or use num = vec1:scalar( vec2 ), where num is a number\n\nAPI\n\n\tmatrix function list:\n\n\tmatrix.add\n\tmatrix.columns\n\tmatrix.concath\n\tmatrix.concatv\n\tmatrix.copy\n\tmatrix.cross\n\tmatrix.det\n\tmatrix.div\n\tmatrix.divnum\n\tmatrix.dogauss\n\tmatrix.elementstostring\n\tmatrix.getelement\n\tmatrix.gsub\n\tmatrix.invert\n\tmatrix.ipairs\n\tmatrix.latex\n\tmatrix.len\n\tmatrix.mul\n\tmatrix.mulnum\n\tmatrix:new\n\tmatrix.normf\n\tmatrix.normmax\n\tmatrix.pow\n\tmatrix.print\n\tmatrix.random\n\tmatrix.replace\n\tmatrix.root\n\tmatrix.rotl\n\tmatrix.rotr\n\tmatrix.round\n\tmatrix.rows\n\tmatrix.scalar\n\tmatrix.setelement\n\tmatrix.size\n\tmatrix.solve\n\tmatrix.sqrt\n\tmatrix.sub\n\tmatrix.subm\n\tmatrix.tostring\n\tmatrix.transpose\n\tmatrix.type\n\n\tSee code and test_matrix.lua.\n\nDEPENDENCIES\n\n None (other than Lua 5.1 or 5.2). May be used with complex.lua.\n\nHOME PAGE\n\n http://luamatrix.luaforge.net\n http://lua-users.org/wiki/LuaMatrix\n\nDOWNLOAD/INSTALL\n\n ./util.mk\n cd tmp/*\n luarocks make\n\nLICENSE\n\n Licensed under the same terms as Lua itself.\n\n Developers:\n Michael Lutz (chillcode) - original author\n David Manura http://lua-users.org/wiki/DavidManura\n--]]\n\n--////////////\n--// matrix //\n--////////////\n\nlocal Vector3 = require 'engine/math/vector3'\nlocal Vector4 = require 'engine/math/vector4'\n\n---@class Matrix\n---@operator +(Matrix a, Matrix b): Matrix # Add two matrices component-wise\n---@operator -(Matrix a, Matrix b): Matrix # Subtract two matrices component-wise\n---@operator *(Matrix a, Matrix b|number): Matrix # Multiply matrix by another matrix or scalar\n---@operator /(Matrix a, Matrix b|number): Matrix # Divide matrix by another matrix or scalar\n---@operator ^(Matrix a, number|'T'|'*'): Matrix # Power, transpose ('T') or complex conjugate ('*')\n---@operator -(): Matrix # Unary minus\n---@operator ==(Matrix a, Matrix b): boolean # Check equality between matrices\nlocal Matrix = {\n _isMatrix = true,\n _type = \"Matrix\"\n}\n\n-- access to the metatable we set at the end of the file\nlocal matrix_meta = {}\n\n--/////////////////////////////\n--// Get 'new' matrix object //\n--/////////////////////////////\n\n--- Create a new 4×4 matrix.\n---\n--- If no data is provided, this returns the 4×4 identity matrix :math:`I`.\n--- If an array of numbers is provided, it fills the matrix row by row.\n---\n---@param data table optional array of 16 numbers to fill the matrix\n---@return Matrix new 4×4 matrix\nfunction Matrix.new4x4(data)\n if data then\n return Matrix:new(4, 4, data)\n else\n return Matrix:new(4, \"I\")\n end\nend\n\n--- Create a new 3×3 matrix.\n---\n--- If no data is provided, this returns the 3×3 identity matrix :math:`I`.\n--- If an array of numbers is provided, it fills the matrix row by row.\n---\n---@param data table optional array of 9 numbers to fill the matrix\n---@return Matrix new 3×3 matrix\nfunction Matrix.new3x3(data)\n if data then\n return Matrix:new(3, 3, data)\n else\n return Matrix:new(3, \"I\")\n end\nend\n\n--- Create a new matrix\n---\n--- Usage patterns:\n---\n--- - `Matrix:new(rows, columns, data)` \n--- Create a matrix of size `rows × columns`. \n--- If *data* is a number, all values are initialized with that number. \n--- If *data* is a table, values are filled row by row.\n---\n--- - `Matrix:new(rows, \"I\")` \n--- Create an identity matrix of size `rows × rows`.\n---\n--- - `Matrix:new(table)` \n--- If *table* is a 2D array (table of tables), wrap it as a `Matrix`. \n--- If *table* is a simple array `{x, y, z}`, create a 3×1 vector matrix.\n---\n---@param rows number|table Number of rows or an initializer table\n---@param columns number|string Number of columns, or `\"I\"` for identity\n---@param data number|table Fill value (number) or table of values\n---@return Matrix new matrix\nfunction Matrix:new( rows, columns, data )\n -- check for given matrix\n if type( rows ) == \"table\" then\n -- check for vector\n if type(rows[1]) ~= \"table\" then -- expect a vector\n return Class.__setmetatable( {{rows[1]},{rows[2]},{rows[3]}},matrix_meta )\n end\n return Class.__setmetatable( rows,matrix_meta )\n end\n -- get matrix table\n local mtx = { _isMatrix = true, _type = \"Matrix\"}\n local data = data or 0\n -- build identity matrix of given rows\n if columns == \"I\" then\n for i = 1,rows do\n mtx[i] = {}\n for j = 1,rows do\n if i == j then\n mtx[i][j] = 1\n else\n mtx[i][j] = 0\n end\n end\n end\n -- build new matrix\n else\n if type(data) == 'table' then\n for i = 1,rows do\n mtx[i] = {}\n for j = 1,columns do\n mtx[i][j] = data[(j-1)*columns+i]\n end\n end\n else\n for i = 1,rows do\n mtx[i] = {}\n for j = 1,columns do\n mtx[i][j] = data\n end\n end\n end\n end\n -- return matrix with shared metatable\n return Class.__setmetatable( mtx,matrix_meta )\nend\n\n--// matrix ( rows [, comlumns [, value]] )\n-- set __call behaviour of matrix\n-- for matrix( ... ) as matrix.new( ... )\nClass.__setmetatable( Matrix, { __call = function( ... ) return Matrix.new( ... ) end } )\n\n\n-- functions are designed to be light on checks\n-- so we get Lua errors instead on wrong input\n-- matrix.<functions> should handle any table of structure t[i][j] = value\n-- we always return a matrix with scripts metatable\n-- cause its faster than setmetatable( mtx, getmetatable( input matrix ) )\n\n--///////////////////////////////\n--// matrix 'matrix' functions //\n--///////////////////////////////\n\n--// for real, complex and symbolic matrices //--\n\n-- note: real and complex matrices may be added, subtracted, etc.\n--\t\treal and symbolic matrices may also be added, subtracted, etc.\n--\t\tbut one should avoid using symbolic matrices with complex ones\n--\t\tsince it is not clear which metatable then is used\n--- Add two matrices element-wise.\n---\n--- The result is defined as :math:`C = A + B` with :math:`c_{ij} = a_{ij} + b_{ij}`.\n--- Matrix :math:`B` may be of bigger size than matrix :math:`A`. \n---\n---@param A Matrix first matrix :math:`A`\n---@param B Matrix second matrix :math:`B`\n---@return Matrix result of addition :math:`A + B`\nfunction Matrix.add(A, B)\n local mtx = {}\n for i = 1, #A do\n local m3i = {}\n mtx[i] = m3i\n for j = 1, #A[1] do\n m3i[j] = A[i][j] + B[i][j]\n end\n end\n return Class.__setmetatable(mtx, matrix_meta)\nend\n\n--- Subtract two matrices element-wise.\n---\n--- The result is defined as :math:`C = A - B` with :math:`c_{ij} = a_{ij} - b_{ij}`.\n--- Matrix :math:`B` may be of bigger size than matrix :math:`A`. \n---\n---@param A Matrix first matrix :math:`A`\n---@param B Matrix second matrix :math:`B`\n---@return Matrix result of subtraction :math:`A - B`\nfunction Matrix.sub(A, B)\n local mtx = {}\n for i = 1, #A do\n local m3i = {}\n mtx[i] = m3i\n for j = 1, #A[1] do\n m3i[j] = A[i][j] - B[i][j]\n end\n end\n return Class.__setmetatable(mtx, matrix_meta)\nend\n\n--- Multiply two matrices.\n---\n--- The number of columns of matrix :math:`A` must equal the number of rows of matrix :math:`B`: \n--- The result is defined as :math:`C = A \\cdot B` with :math:`c_{ij} = \\sum_k a_{ik} b_{kj}`.\n---\n---@param A Matrix first matrix :math:`A`\n---@param B Matrix second matrix :math:`B`\n---@return Matrix result of multiplication :math:`A \\cdot B`\nfunction Matrix.mul(A, B)\n local mtx = {}\n for i = 1, #A do\n mtx[i] = {}\n for j = 1, #B[1] do\n local num = A[i][1] * B[1][j]\n for n = 2, #A[1] do\n num = num + A[i][n] * B[n][j]\n end\n mtx[i][j] = num\n end\n end\n return Class.__setmetatable(mtx, matrix_meta)\nend\n\n--- Divide two matrices.\n---\n--- Division is defined as multiplication with the inverse of the second matrix: \n--- :math:`A / B = A \\cdot B^{-1}`.\n--- The number of columns of matrix :math:`A` must equal the number of rows of matrix :math:`B`: \n--- The matrix :math:`B` must be square and non-singular, to be invertible. \n--- On failure, returns :math:`B` and the rank of :math:`B`.\n---\n---@param A Matrix numerator matrix :math:`A`\n---@param B Matrix denominator matrix :math:`B`\n---@return Matrix|nil result of division :math:`A \\cdot B^{-1}`, or `nil` if singular\n---@return number? rank of `m2` if inversion fails\nfunction Matrix.div(A, B)\n local rank; B, rank = Matrix.invert(B)\n if not B then return B, rank end -- singular\n return Matrix.mul(A, B)\nend\n\n--- Multiply a matrix with a scalar.\n---\n--- The scalar may be a real number or a complex number. \n--- Strings are first attempted to be parsed as complex numbers; if that fails,\n--- they are treated as symbols. \n--- The result is defined as :math:`C = \\alpha \\cdot A` with :math:`c_{ij} = \\alpha \\, a_{ij}`.\n---\n---@param A Matrix matrix :math:`A`\n---@param num number|string scalar multiplier :math:`\\alpha`\n---@return Matrix result of scalar multiplication :math:`\\alpha \\cdot A`\nfunction Matrix.mulnum(A, num)\n local mtx = {}\n for i = 1, #A do\n mtx[i] = {}\n for j = 1, #A[1] do\n mtx[i][j] = A[i][j] * num\n end\n end\n return Class.__setmetatable(mtx, matrix_meta)\nend\n\n--- Divide a matrix by a scalar.\n---\n--- The scalar may be a real number or a complex number. \n--- Strings are first attempted to be parsed as complex numbers; if that fails,\n--- they are treated as symbols. \n--- The result is defined as :math:`C = A / \\alpha` with :math:`c_{ij} = a_{ij} / \\alpha`.\n---\n---@param A Matrix matrix :math:`A`\n---@param num number|string scalar divisor :math:`\\alpha`\n---@return Matrix result of division :math:`A / \\alpha`\nfunction Matrix.divnum(A, num)\n local mtx = {}\n for i = 1, #A do\n local mtxi = {}\n mtx[i] = mtxi\n for j = 1, #A[1] do\n mtxi[j] = A[i][j] / num\n end\n end\n return Class.__setmetatable(mtx, matrix_meta)\nend\n\n--- Multiply a 3×3 matrix with a Vector3.\n---\n--- The result is a Vector3 :math:`v' = A \\cdot v`.\n---\n---@param vec Vector3 vector :math:`v`\n---@return Vector3 result of multiplication\nfunction Matrix:mulVec3(vec)\n local A = self\n return Vector3.new(\n A[1][1] * vec.x + A[1][2] * vec.y + A[1][3] * vec.z,\n A[2][1] * vec.x + A[2][2] * vec.y + A[2][3] * vec.z,\n A[3][1] * vec.x + A[3][2] * vec.y + A[3][3] * vec.z\n )\nend\n\n--- Multiply a 4×4 matrix with a Vector4.\n---\n--- The result is a Vector4 :math:`v' = A \\cdot v`.\n---\n---@param vec Vector4 vector :math:`v`\n---@return Vector4 result of multiplication\nfunction Matrix:mulVec4(vec)\n local A = self\n return Vector4.new(\n A[1][1] * vec.x + A[1][2] * vec.y + A[1][3] * vec.z + A[1][4] * vec.w,\n A[2][1] * vec.x + A[2][2] * vec.y + A[2][3] * vec.z + A[2][4] * vec.w,\n A[3][1] * vec.x + A[3][2] * vec.y + A[3][3] * vec.z + A[3][4] * vec.w,\n A[4][1] * vec.x + A[4][2] * vec.y + A[4][3] * vec.z + A[4][4] * vec.w\n )\nend\n\n--// for real and complex matrices only //--\n\n--- Raise a matrix to an integer power.\n---\n--- The matrix must be square. If the exponent is negative, the matrix is inverted first.\n--- If inversion fails, returns `nil` and the rank of the matrix.\n---\n---@param m1 Matrix matrix to raise :math:`M`\n---@param num number integer exponent :math:`n`\n---@return Matrix|nil result of :math:`M^n`, or `nil` if singular\n---@return integer? rank of the matrix when inversion fails\nfunction Matrix.pow( m1, num )\n __assertTrace(num == math.floor(num), \"exponent not an integer\")\n if num == 0 then\n return Matrix:new( #m1,\"I\" )\n end\n if num < 0 then\n local rank; m1,rank = Matrix.invert( m1 )\n if not m1 then return m1, rank end -- singular\n num = -num\n end\n local mtx = Matrix.copy( m1 )\n for i = 2,num\tdo\n mtx = Matrix.mul( mtx,m1 )\n end\n return mtx\nend\n\nlocal function number_norm2(x)\n return x * x\nend\n\n--- Calculate the determinant :math:`\\det(A)` of a square matrix :math:`A`.\n---\n--- The matrix must be square :math:`n \\times n`. \n--- Works for symbolic matrices up to 3×3, and numeric matrices of any size.\n--- The determinant is a scalar value that represents the scaling factor of the linear transformation defined by the matrix.\n--- Geometrically, it represents the area (for 2×2) or volume (for 3×3) spanned by the columns of the matrix.\n---\n---@param A Matrix square matrix :math:`A`\n---@return number determinant :math:`\\det(A)`\nfunction Matrix.det( A )\n\n -- check if matrix is quadratic\n __assertTrace(#A == #A[1], \"matrix not square\")\n\n local size = #A\n\n if size == 1 then\n return A[1][1]\n end\n\n if size == 2 then\n return A[1][1]*A[2][2] - A[2][1]*A[1][2]\n end\n\n if size == 3 then\n return ( A[1][1]*A[2][2]*A[3][3] + A[1][2]*A[2][3]*A[3][1] + A[1][3]*A[2][1]*A[3][2]\n - A[1][3]*A[2][2]*A[3][1] - A[1][1]*A[2][3]*A[3][2] - A[1][2]*A[2][1]*A[3][3] )\n end\n\n --// no symbolic matrix supported below here\n local e = A[1][1]\n local zero = type(e) == \"table\" and e.zero or 0\n local norm2 = type(e) == \"table\" and e.norm2 or number_norm2\n\n --// matrix is bigger than 3x3\n -- get determinant\n -- using Gauss elimination and Laplace\n -- start eliminating from below better for removals\n -- get copy of matrix, set initial determinant\n local mtx = Matrix.copy( m1 )\n local det = 1\n -- get det up to the last element\n for j = 1,#mtx[1] do\n -- get smallest element so that |factor| > 1\n -- and set it as last element\n local rows = #mtx\n local subdet,xrow\n for i = 1,rows do\n -- get element\n local e = mtx[i][j]\n -- if no subdet has been found\n if not subdet then\n -- check if element it is not zero\n if e ~= zero then\n -- use element as new subdet\n subdet,xrow = e,i\n end\n -- check for elements nearest to 1 or -1\n elseif e ~= zero and math.abs(norm2(e)-1) < math.abs(norm2(subdet)-1) then\n subdet,xrow = e,i\n end\n end\n -- only cary on if subdet is found\n if subdet then\n -- check if xrow is the last row,\n -- else switch lines and multiply det by -1\n if xrow ~= rows then\n mtx[rows],mtx[xrow] = mtx[xrow],mtx[rows]\n det = -det\n end\n -- traverse all fields setting element to zero\n -- we don't set to zero cause we don't use that column anymore then anyways\n for i = 1,rows-1 do\n -- factor is the dividor of the first element\n -- if element is not already zero\n if mtx[i][j] ~= zero then\n local factor = mtx[i][j]/subdet\n -- update all remaining fields of the matrix, with value from xrow\n for n = j+1,#mtx[1] do\n mtx[i][n] = mtx[i][n] - factor * mtx[rows][n]\n end\n end\n end\n -- update determinant and remove row\n if math.fmod( rows,2 ) == 0 then\n det = -det\n end\n det = det * subdet\n table.remove( mtx )\n else\n -- break here table det is 0\n return det * 0\n end\n end\n -- det ready to return\n return det\nend\n\nlocal pivotOk = function( mtx,i,j,norm2 )\n local iMin\n local normMin = math.huge\n for _i = i,#mtx do\n local e = mtx[_i][j]\n local norm = math.abs(norm2(e))\n if norm > 0 and norm < normMin then\n iMin = _i\n normMin = norm\n end\n end\n if iMin then\n if iMin ~= i then\n mtx[i],mtx[iMin] = mtx[iMin],mtx[i]\n end\n return true\n end\n return false\nend\n\nlocal function copy(x)\n return type(x) == \"table\" and x.copy(x) or x\nend\n\n--- Perform Gauss-Jordan elimination on a matrix.\n---\n--- This function modifies the matrix in-place to convert it to reduced row echelon form.\n--- Uses partial pivoting for numerical stability.\n---\n---@param mtx Matrix matrix to transform (modified in-place)\n---@return boolean true on success\n---@return integer? rank of the matrix on failure\nfunction Matrix.dogauss( mtx )\n local e = mtx[1][1]\n local zero = type(e) == \"table\" and e.zero or 0\n local one = type(e) == \"table\" and e.one or 1\n local norm2 = type(e) == \"table\" and e.norm2 or number_norm2\n\n local rows,columns = #mtx,#mtx[1]\n -- stairs left -> right\n for j = 1,rows do\n -- check if element can be setted to one\n if pivotOk( mtx,j,j,norm2 ) then\n -- start parsing rows\n for i = j+1,rows do\n -- check if element is not already zero\n if mtx[i][j] ~= zero then\n -- we may add x*otherline row, to set element to zero\n -- tozero - x*mtx[j][j] = 0; x = tozero/mtx[j][j]\n local factor = mtx[i][j]/mtx[j][j]\n mtx[i][j] = copy(zero)\n for _j = j+1,columns do\n mtx[i][_j] = mtx[i][_j] - factor * mtx[j][_j]\n end\n end\n end\n else\n -- return false and the rank of the matrix\n return false,j-1\n end\n end\n -- stairs right <- left\n for j = rows,1,-1 do\n -- set element to one\n -- do division here\n local div = mtx[j][j]\n for _j = j+1,columns do\n mtx[j][_j] = mtx[j][_j] / div\n end\n -- start parsing rows\n for i = j-1,1,-1 do\n -- check if element is not already zero\n if mtx[i][j] ~= zero then\n local factor = mtx[i][j]\n for _j = j+1,columns do\n mtx[i][_j] = mtx[i][_j] - factor * mtx[j][_j]\n end\n mtx[i][j] = copy(zero)\n end\n end\n mtx[j][j] = copy(one)\n end\n return true\nend\n\n--- Compute the inverse of a matrix.\n---\n--- The inverse :math:`M^{-1}` of a matrix :math:`M` is defined such that \n--- :math:`M \\cdot M^{-1} = I`, where :math:`I` is the identity matrix.\n--- The matrix :math:`M` must be square and non-singular.\n---\n--- On success: returns the inverted matrix. \n--- On failure: returns `nil` and the rank of the matrix.\n---@param M Matrix Matrix to invert\n---@return Matrix|nil inverted matrix or nil if singular\n---@return integer? rank of the matrix when singular\nfunction Matrix.invert( M )\n __assertTrace(#M == #M[1], \"matrix not square\")\n local mtx = Matrix.copy( M )\n local ident = Class.__setmetatable( {},matrix_meta )\n local e = M[1][1]\n local zero = type(e) == \"table\" and e.zero or 0\n local one = type(e) == \"table\" and e.one or 1\n for i = 1,#M do\n local identi = {}\n ident[i] = identi\n for j = 1,#M do\n identi[j] = copy((i == j) and one or zero)\n end\n end\n mtx = Matrix.concath( mtx,ident )\n local done,rank = Matrix.dogauss( mtx )\n if done then\n return Matrix.subm( mtx, 1,(#mtx[1]/2)+1,#mtx,#mtx[1] )\n else\n return nil,rank\n end\nend\n\nlocal function get_abs_avg( m1, m2 )\n local dist = 0\n local e = m1[1][1]\n local abs = type(e) == \"table\" and e.abs or math.abs\n for i=1,#m1 do\n for j=1,#m1[1] do\n dist = dist + abs(m1[i][j]-m2[i][j])\n end\n end\n -- norm by numbers of entries\n return dist/(#m1*2)\nend\n\n--- Calculate the square root of a matrix using the Denman-Beavers iteration.\n--- Calculate the square root of a matrix using the Denman-Beavers iteration.\n---\n--- The matrix must be square and invertible. Uses the iteration:\n--- :math:`Y_{k+1} = \\frac{1}{2}(Y_k + Z_k^{-1})`, :math:`Z_{k+1} = \\frac{1}{2}(Z_k + Y_k^{-1})`\n---\n---@param m1 Matrix square matrix :math:`M`\n---@param iters number? maximum number of iterations (default: auto until convergence)\n---@return Matrix square root :math:`M^{1/2}`\n---@return Matrix inverse square root :math:`M^{-1/2}`\n---@return number average error between :math:`(M^{1/2})^2` and :math:`M`\nfunction Matrix.sqrt( m1, iters )\n __assertTrace(#m1 == #m1[1], \"matrix not square\")\n local iters = iters or math.huge\n local y = Matrix.copy( m1 )\n local z = Matrix(#y, 'I')\n local dist = math.huge\n -- iterate, and get the average error\n for n=1,iters do\n local lasty,lastz = y,z\n -- calc square root\n -- y, z = (1/2)*(y + z^-1), (1/2)*(z + y^-1)\n y, z = Matrix.divnum((Matrix.add(y,Matrix.invert(z))),2),\n Matrix.divnum((Matrix.add(z,Matrix.invert(y))),2)\n local dist1 = get_abs_avg(y,lasty)\n if iters == math.huge then\n if dist1 >= dist then\n return lasty,lastz,get_abs_avg(Matrix.mul(lasty,lasty),m1)\n end\n end\n dist = dist1\n end\n return y,z,get_abs_avg(matrix.mul(y,y),m1)\nend\n\n--- Calculate the nth root of a matrix.\n---\n--- The matrix must be square and invertible. Uses iterative computation.\n---\n---@param m1 Matrix square matrix :math:`M`\n---@param root number the root degree :math:`n`\n---@param iters number? maximum number of iterations\n---@return Matrix nth root :math:`M^{1/n}`\n---@return Matrix inverse nth root\n---@return number average error\nfunction Matrix.root( m1, root, iters )\n __assertTrace(#m1 == #m1[1], \"matrix not square\")\n local iters = iters or math.huge\n local mx = Matrix.copy( m1 )\n local my = Matrix.mul(mx:invert(),mx:pow(root-1))\n local dist = math.huge\n -- iterate, and get the average error\n for n=1,iters do\n local lastx,lasty = mx,my\n -- calc root of matrix\n --mx,my = ((p-1)*mx + my^-1)/p,\n --\t((((p-1)*my + mx^-1)/p)*my^-1)^(p-2) *\n --\t((p-1)*my + mx^-1)/p\n mx,my = mx:mulnum(root-1):add(my:invert()):divnum(root),\n my:mulnum(root-1):add(mx:invert()):divnum(root)\n :mul(my:invert():pow(root-2)):mul(my:mulnum(root-1)\n :add(mx:invert())):divnum(root)\n local dist1 = get_abs_avg(mx,lastx)\n if iters == math.huge then\n if dist1 >= dist then\n return lastx,lasty,get_abs_avg(matrix.pow(lastx,root),m1)\n end\n end\n dist = dist1\n end\n return mx,my,get_abs_avg(Matrix.pow(mx,root),m1)\nend\n\n\n-- Norm functions --\n\n--- Calculate the Frobenius norm of a matrix.\n---\n--- The Frobenius norm is defined as :math:`\\|A\\|_F = \\sqrt{\\sum_{i,j} |a_{ij}|^2}`.\n--- Works for numeric, complex, and symbolic matrices.\n---\n---@param mtx Matrix matrix :math:`A`\n---@return number the Frobenius norm :math:`\\|A\\|_F`\nfunction Matrix.normf(mtx)\n local mtype = Matrix.type(mtx)\n local result = 0\n for i = 1,#mtx do\n for j = 1,#mtx[1] do\n local e = mtx[i][j]\n if mtype ~= \"number\" then e = e:abs() end\n result = result + e^2\n end\n end\n local sqrt = (type(result) == \"number\") and math.sqrt or result.sqrt\n return sqrt(result)\nend\n\n--- Calculate the max (infinity) norm of a matrix.\n---\n--- The max norm is defined as :math:`\\|A\\|_\\infty = \\max_i \\sum_j |a_{ij}|`.\n--- Does not work with symbolic matrices.\n---\n---@param mtx Matrix matrix :math:`A`\n---@return number the max norm :math:`\\|A\\|_\\infty`\nfunction Matrix.normmax(mtx)\n local abs = (Matrix.type(mtx) == \"number\") and math.abs or mtx[1][1].abs\n local result = 0\n for i = 1,#mtx do\n for j = 1,#mtx[1] do\n local e = abs(mtx[i][j])\n if e > result then result = e end\n end\n end\n return result\nend\n\n\n-- Functions changing the matrix itself\n\nlocal numround = function( num,mult )\n return math.floor( num * mult + 0.5 ) / mult\nend\nlocal tround = function( t,mult )\n for i,v in ipairs(t) do\n t[i] = math.floor( v * mult + 0.5 ) / mult\n end\n return t\nend\n\n--- Round all elements of a matrix.\n---\n--- Rounds each element to the specified number of decimal places.\n--- Modifies the matrix in-place and returns it.\n---\n---@param mtx Matrix matrix to round (modified in-place)\n---@param idp number? number of decimal places (default: 0)\n---@return Matrix the modified matrix\nfunction Matrix.round( mtx, idp )\n local mult = 10^( idp or 0 )\n local fround = Matrix.type( mtx ) == \"number\" and numround or tround\n for i = 1,#mtx do\n for j = 1,#mtx[1] do\n mtx[i][j] = fround(mtx[i][j],mult)\n end\n end\n return mtx\nend\n\nlocal numfill = function( _,start,stop,idp )\n return math.random( start,stop ) / idp\nend\nlocal tfill = function( t,start,stop,idp )\n for i in ipairs(t) do\n t[i] = math.random( start,stop ) / idp\n end\n return t\nend\n\n--- Fill a matrix with random values.\n---\n--- Modifies the matrix in-place with random values in the specified range.\n---\n---@param mtx Matrix matrix to fill with random values (modified in-place)\n---@param start number? minimum value (default: -10)\n---@param stop number? maximum value (default: 10)\n---@param idp number? decimal precision divisor (default: 1)\n---@return Matrix the modified matrix\nfunction Matrix.random( mtx,start,stop,idp )\n local start,stop,idp = start or -10,stop or 10,idp or 1\n local ffill = Matrix.type( mtx ) == \"number\" and numfill or tfill\n for i = 1,#mtx do\n for j = 1,#mtx[1] do\n mtx[i][j] = ffill( mtx[i][j], start, stop, idp )\n end\n end\n return mtx\nend\n\n\n-- Object Utility Functions --\n\n-- for all types and matrices --\n\n--- Get the type of a matrix.\n---\n--- Returns the element type of the matrix: \"number\" for numeric matrices,\n--- \"complex\" for complex matrices, \"symbol\" for symbolic matrices, or \"tensor\" for tensor matrices.\n---\n---@param mtx Matrix matrix to check\n---@return string the matrix type: \"number\", \"complex\", \"symbol\", or \"tensor\"\nfunction Matrix.type( mtx )\n local e = mtx[1][1]\n if type(e) == \"table\" then\n if e.type then\n return e:type()\n end\n return \"tensor\"\n end\n return \"number\"\nend\n\n-- local functions to copy matrix values\nlocal num_copy = function( num )\n return num\nend\nlocal t_copy = function( t )\n local newt = Class.__setmetatable( {}, getmetatable( t ) )\n for i,v in ipairs( t ) do\n newt[i] = v\n end\n return newt\nend\n\n--- Create a deep copy of a matrix.\n---\n--- Creates a new matrix with the same values as the original.\n--- Works for numeric, complex, and symbolic matrices.\n---\n---@param m1 Matrix matrix to copy\n---@return Matrix a new matrix that is a copy of the original\nfunction Matrix.copy( m1 )\n local docopy = Matrix.type( m1 ) == \"number\" and num_copy or t_copy\n local mtx = {}\n for i = 1,#m1[1] do\n mtx[i] = {}\n for j = 1,#m1 do\n mtx[i][j] = docopy( m1[i][j] )\n end\n end\n return Class.__setmetatable( mtx, matrix_meta )\nend\n\n--- Transpose a matrix.\n---\n--- The transpose of a matrix :math:`A` is a new matrix :math:`A^T` obtained by \n--- flipping rows and columns, so that the element at row :math:`i`, column :math:`j` \n--- in :math:`A` becomes the element at row :math:`j`, column :math:`i` in :math:`A^T`.\n---\n---@param A Matrix matrix to transpose :math:`A`\n---@return Matrix transposed matrix :math:`A^T`\nfunction Matrix.transpose(A)\n local docopy = Matrix.type(A) == \"number\" and num_copy or t_copy\n local mtx = {}\n for i = 1, #A[1] do\n mtx[i] = {}\n for j = 1, #A do\n mtx[i][j] = docopy(A[j][i])\n end\n end\n return Class.__setmetatable(mtx, matrix_meta)\nend\n\n--- Extract a submatrix from a matrix.\n---\n--- Extracts rows from i1 to i2 and columns from j1 to j2.\n---\n---@param m1 Matrix source matrix\n---@param i1 number starting row index\n---@param j1 number starting column index\n---@param i2 number ending row index\n---@param j2 number ending column index\n---@return Matrix the extracted submatrix\nfunction Matrix.subm( m1,i1,j1,i2,j2 )\n local docopy = Matrix.type( m1 ) == \"number\" and num_copy or t_copy\n local mtx = {}\n for i = i1,i2 do\n local _i = i-i1+1\n mtx[_i] = {}\n for j = j1,j2 do\n local _j = j-j1+1\n mtx[_i][_j] = docopy( m1[i][j] )\n end\n end\n return Class.__setmetatable( mtx, matrix_meta )\nend\n\n--- Concatenate two matrices horizontally.\n---\n--- Both matrices must have the same number of rows.\n--- Returns a new matrix with m1 on the left and m2 on the right.\n---\n---@param m1 Matrix left matrix\n---@param m2 Matrix right matrix\n---@return Matrix concatenated matrix\nfunction Matrix.concath( m1,m2 )\n __assertTrace(#m1 == #m2, \"matrix size mismatch\")\n local docopy = Matrix.type( m1 ) == \"number\" and num_copy or t_copy\n local mtx = {}\n local offset = #m1[1]\n for i = 1,#m1 do\n mtx[i] = {}\n for j = 1,offset do\n mtx[i][j] = docopy( m1[i][j] )\n end\n for j = 1,#m2[1] do\n mtx[i][j+offset] = docopy( m2[i][j] )\n end\n end\n return Class.__setmetatable( mtx, matrix_meta )\nend\n\n--- Concatenate two matrices vertically.\n---\n--- Both matrices must have the same number of columns.\n--- Returns a new matrix with m1 on top and m2 on bottom.\n---\n---@param m1 Matrix top matrix\n---@param m2 Matrix bottom matrix\n---@return Matrix concatenated matrix\nfunction Matrix.concatv( m1,m2 )\n assert(#m1[1] == #m2[1], \"matrix size mismatch\")\n local docopy = Matrix.type( m1 ) == \"number\" and num_copy or t_copy\n local mtx = {}\n for i = 1,#m1 do\n mtx[i] = {}\n for j = 1,#m1[1] do\n mtx[i][j] = docopy( m1[i][j] )\n end\n end\n local offset = #mtx\n for i = 1,#m2 do\n local _i = i + offset\n mtx[_i] = {}\n for j = 1,#m2[1] do\n mtx[_i][j] = docopy( m2[i][j] )\n end\n end\n return Class.__setmetatable( mtx, matrix_meta )\nend\n\n--- Rotate a matrix 90 degrees to the left.\n---\n--- Returns a new matrix that is rotated 90 degrees counterclockwise.\n---\n---@param m1 Matrix matrix to rotate\n---@return Matrix rotated matrix\nfunction Matrix.rotl( m1 )\n local mtx = Matrix:new( #m1[1],#m1 )\n local docopy = Matrix.type( m1 ) == \"number\" and num_copy or t_copy\n for i = 1,#m1 do\n for j = 1,#m1[1] do\n mtx[#m1[1]-j+1][i] = docopy( m1[i][j] )\n end\n end\n return mtx\nend\n\n--- Rotate a matrix 90 degrees to the right.\n---\n--- Returns a new matrix that is rotated 90 degrees clockwise.\n---\n---@param m1 Matrix matrix to rotate\n---@return Matrix rotated matrix\nfunction Matrix.rotr( m1 )\n local mtx = Matrix:new( #m1[1],#m1 )\n local docopy = Matrix.type( m1 ) == \"number\" and num_copy or t_copy\n for i = 1,#m1 do\n for j = 1,#m1[1] do\n mtx[j][#m1-i+1] = docopy( m1[i][j] )\n end\n end\n return mtx\nend\n\nlocal function tensor_tostring( t,fstr )\n if not fstr then return \"[\"..table.concat(t,\",\")..\"]\" end\n local tval = {}\n for i,v in ipairs( t ) do\n tval[i] = string.format( fstr,v )\n end\n return \"[\"..table.concat(tval,\",\")..\"]\"\nend\nlocal function number_tostring( e,fstr )\n return fstr and string.format( fstr,e ) or e\nend\n\n--- Convert a matrix to a string representation.\n---\n--- Returns a human-readable string representation of the matrix.\n---\n---@param mtx Matrix matrix to convert\n---@param formatstr string? optional format string for numbers (e.g., \"%.2f\")\n---@return string string representation of the matrix\nfunction Matrix.tostring( mtx, formatstr )\n local ts = {}\n local mtype = Matrix.type( mtx )\n local e = mtx[1][1]\n local tostring = mtype == \"tensor\" and tensor_tostring or\n type(e) == \"table\" and e.tostring or number_tostring\n for i = 1,#mtx do\n local tstr = {}\n for j = 1,#mtx[1] do\n tstr[j] = tostring(mtx[i][j],formatstr)\n end\n ts[i] = table.concat(tstr, \"\\t\")\n end\n return table.concat(ts, \"\\n\")\nend\n\n--- Print a matrix to the console.\n---\n--- Prints a human-readable string representation of the matrix to stdout.\n---\n---@param ... Matrix matrix(s) to print, optionally followed by format string\nfunction Matrix.print( ... )\n print( Matrix.tostring( ... ) )\nend\n\n--- Generate a LaTeX representation of a matrix.\n---\n--- Returns a LaTeX string that can be used in mathematical documents.\n---\n---@param mtx Matrix matrix to convert\n---@param align string? alignment option: \"c\" (center), \"l\" (left), \"r\" (right)\n---@return string LaTeX representation of the matrix\nfunction Matrix.latex( mtx, align )\n -- align : option to align the elements\n --\t\tc = center; l = left; r = right\n --\t\t\\usepackage{dcolumn}; D{.}{,}{-1}; aligns number by . replaces it with ,\n local align = align or \"c\"\n local str = \"$\\\\left( \\\\begin{array}{\"..string.rep( align, #mtx[1] )..\"}\\n\"\n local getstr = matrix.type( mtx ) == \"tensor\" and tensor_tostring or number_tostring\n for i = 1,#mtx do\n str = str..\"\\t\"..getstr(mtx[i][1])\n for j = 2,#mtx[1] do\n str = str..\" & \"..getstr(mtx[i][j])\n end\n -- close line\n if i == #mtx then\n str = str..\"\\n\"\n else\n str = str..\" \\\\\\\\\\n\"\n end\n end\n return str..\"\\\\end{array} \\\\right)$\"\nend\n\n\n-- Functions not changing the matrix\n\n--- Get the number of rows in a matrix.\n---\n---@param mtx Matrix matrix\n---@return number number of rows\nfunction Matrix.rows( mtx )\n return #mtx\nend\n\n--- Get the number of columns in a matrix.\n---\n---@param mtx Matrix matrix\n---@return number number of columns\nfunction Matrix.columns( mtx )\n return #mtx[1]\nend\n\n--- Get the size of a matrix.\n---\n--- Returns the number of rows and columns. For tensors, also returns the depth.\n---\n---@param mtx Matrix matrix\n---@return number number of rows\n---@return number number of columns\n---@return number? tensor depth (only for tensors)\nfunction Matrix.size( mtx )\n if Matrix.type( mtx ) == \"tensor\" then\n return #mtx,#mtx[1],#mtx[1][1]\n end\n return #mtx,#mtx[1]\nend\n\n--- Get an element from a matrix.\n---\n--- Returns the element at row i, column j.\n---\n---@param mtx Matrix matrix\n---@param i number row index\n---@param j number column index\n---@return any the element at (i, j), or nil if out of bounds\nfunction Matrix.getelement( mtx,i,j )\n if mtx[i] and mtx[i][j] then\n return mtx[i][j]\n end\nend\n\n--- Set an element in a matrix.\n---\n--- Sets the value at row i, column j.\n---\n---@param mtx Matrix matrix (modified in-place)\n---@param i number row index\n---@param j number column index\n---@param value any value to set\n---@return number 1 on success, nil on failure\nfunction Matrix.setelement( mtx,i,j,value )\n if Matrix.getelement( mtx,i,j ) then\n -- check if value type is number\n mtx[i][j] = value\n return 1\n end\nend\n\n--- Iterate over matrix elements.\n---\n--- Returns an iterator function for use in for loops.\n--- Iterates through all elements row by row.\n---\n---@param mtx Matrix matrix to iterate\n---@return function iterator function\nfunction Matrix.ipairs( mtx )\n local i,j,rows,columns = 1,0,#mtx,#mtx[1]\n local function iter()\n j = j + 1\n if j > columns then -- return first element from next row\n i,j = i + 1,1\n end\n if i <= rows then\n return i,j\n end\n end\n return iter\nend\n\n\n-- matrix 'vector' functions --\n\n-- a vector is defined as a 3x1 matrix\n-- get a vector; vec = matrix{{ 1,2,3 }}^'T'\n\n--- Calculate the scalar (dot) product of two vectors.\n---\n--- Both arguments must be 3x1 matrices (column vectors).\n--- The result is :math:`a \\cdot b = a_x b_x + a_y b_y + a_z b_z`.\n---\n---@param m1 Matrix first vector (3x1)\n---@param m2 Matrix second vector (3x1)\n---@return number the scalar product\nfunction Matrix.scalar( m1, m2 )\n return m1[1][1]*m2[1][1] + m1[2][1]*m2[2][1] + m1[3][1]*m2[3][1]\nend\n\n--- Calculate the cross product of two vectors.\n---\n--- Both arguments must be 3x1 matrices (column vectors).\n--- Returns a new vector perpendicular to both input vectors.\n---\n---@param m1 Matrix first vector (3x1)\n---@param m2 Matrix second vector (3x1)\n---@return Matrix cross product vector (3x1)\nfunction Matrix.cross( m1, m2 )\n local mtx = {}\n mtx[1] = { m1[2][1]*m2[3][1] - m1[3][1]*m2[2][1] }\n mtx[2] = { m1[3][1]*m2[1][1] - m1[1][1]*m2[3][1] }\n mtx[3] = { m1[1][1]*m2[2][1] - m1[2][1]*m2[1][1] }\n return Class.__setmetatable( mtx, matrix_meta )\nend\n\n--- Calculate the length (magnitude) of a vector.\n---\n--- The argument must be a 3x1 matrix (column vector).\n--- Returns :math:`\\sqrt{x^2 + y^2 + z^2}`.\n---\n---@param m1 Matrix vector (3x1)\n---@return number the vector length\nfunction Matrix.len( m1 )\n return math.sqrt( m1[1][1]^2 + m1[2][1]^2 + m1[3][1]^2 )\nend\n\n\n--- Replace each element of a matrix using a function.\n---\n--- Applies a function to each element, returning a new matrix with the results.\n---\n---@param m1 Matrix matrix to transform\n---@param func function function to apply to each element: func(element, ...) -> newvalue\n---@param ... any additional arguments to pass to the function\n---@return Matrix new matrix with transformed elements\nfunction Matrix.replace( m1, func, ... )\n local mtx = {}\n for i = 1,#m1 do\n local m1i = m1[i]\n local mtxi = {}\n for j = 1,#m1i do\n mtxi[j] = func( m1i[j], ... )\n end\n mtx[i] = mtxi\n end\n return Class.__setmetatable( mtx, matrix_meta )\nend\n\n--- Convert all matrix elements to strings.\n---\n--- Returns a new matrix with all elements converted to string representation.\n---\n---@param mtx Matrix matrix to convert\n---@return Matrix new matrix with string elements\nfunction Matrix.elementstostrings( mtx )\n local e = mtx[1][1]\n local tostring = type(e) == \"table\" and e.tostring or tostring\n return Matrix.replace(mtx, tostring)\nend\n\n--- Solve a symbolic matrix to numeric values.\n---\n--- Converts a symbolic matrix to a numeric matrix by evaluating the symbolic expressions.\n--- Only works with matrices of type \"symbol\".\n---\n---@param m1 Matrix symbolic matrix\n---@return Matrix numeric matrix\nfunction Matrix.solve( m1 )\n assert( Matrix.type( m1 ) == \"symbol\", \"matrix not of type 'symbol'\" )\n local mtx = {}\n for i = 1,#m1 do\n mtx[i] = {}\n for j = 1,#m1[1] do\n mtx[i][j] = tonumber( loadstring( \"return \"..m1[i][j][1] )() )\n end\n end\n return Class.__setmetatable( mtx, matrix_meta )\nend\n\n--////////////////////////--\n--// METATABLE HANDLING //--\n--////////////////////////--\n\n--// MetaTable\n-- as we declaired on top of the page\n-- local/shared metatable\n-- matrix_meta\n\n-- note '...' is always faster than 'arg1,arg2,...' if it can be used\n\n-- Set add \"+\" behaviour\nmatrix_meta.__add = function( ... )\n return Matrix.add( ... )\nend\n\n-- Set subtract \"-\" behaviour\nmatrix_meta.__sub = function( ... )\n return Matrix.sub( ... )\nend\n\n-- Set multiply \"*\" behaviour\nmatrix_meta.__mul = function( m1,m2 )\n if getmetatable( m1 ) ~= matrix_meta then\n return matrix.mulnum( m2,m1 )\n elseif getmetatable( m2 ) ~= matrix_meta then\n return matrix.mulnum( m1,m2 )\n end\n return matrix.mul( m1,m2 )\nend\n\n-- Set division \"/\" behaviour\nmatrix_meta.__div = function( m1,m2 )\n if getmetatable( m1 ) ~= matrix_meta then\n return Matrix.mulnum( matrix.invert(m2),m1 )\n elseif getmetatable( m2 ) ~= matrix_meta then\n return Matrix.divnum( m1,m2 )\n end\n return Matrix.div( m1,m2 )\nend\n\n-- Set unary minus \"-\" behavior\nmatrix_meta.__unm = function( mtx )\n return Matrix.mulnum( mtx,-1 )\nend\n\n-- Set power \"^\" behaviour\n-- if opt is any integer number will do mtx^opt\n-- (returning nil if answer doesn't exist)\n-- if opt is 'T' then it will return the transpose matrix\n-- only for complex:\n-- if opt is '*' then it returns the complex conjugate matrix\nlocal option = {\n -- only for complex\n [\"*\"] = function( m1 ) return Matrix.conjugate( m1 ) end,\n -- for both\n [\"T\"] = function( m1 ) return Matrix.transpose( m1 ) end,\n}\nmatrix_meta.__pow = function( m1, opt )\n return option[opt] and option[opt]( m1 ) or Matrix.pow( m1,opt )\nend\n\n-- Set equal \"==\" behaviour\nmatrix_meta.__eq = function( m1, m2 )\n -- check same type\n if Matrix.type( m1 ) ~= Matrix.type( m2 ) then\n return false\n end\n -- check same size\n if #m1 ~= #m2 or #m1[1] ~= #m2[1] then\n return false\n end\n -- check elements equal\n for i = 1,#m1 do\n for j = 1,#m1[1] do\n if m1[i][j] ~= m2[i][j] then\n return false\n end\n end\n end\n return true\nend\n\n-- Set tostring \"tostring( mtx )\" behaviour\nmatrix_meta.__tostring = function( ... )\n return Matrix.tostring( ... )\nend\n\n-- set __call \"mtx( [formatstr] )\" behaviour, mtx [, formatstr]\nmatrix_meta.__call = function( ... )\n Matrix.print( ... )\nend\n\n--// __index handling\nmatrix_meta.__index = {}\nfor k,v in pairs( Matrix ) do\n matrix_meta.__index[k] = v\nend\n\n\n--/////////////////////////////////\n--// symbol class implementation\n--/////////////////////////////////\n\n-- access to the symbolic metatable\nlocal symbol_meta = {}; symbol_meta.__index = symbol_meta\nlocal symbol = symbol_meta\n\nfunction symbol_meta.new(o)\n return Class.__setmetatable({tostring(o)}, symbol_meta)\nend\nsymbol_meta.to = symbol_meta.new\n\n-- symbol( arg )\n-- same as symbol.to( arg )\n-- set __call behaviour of symbol\nClass.__setmetatable( symbol_meta, { __call = function( _,s ) return symbol_meta.to( s ) end } )\n\n\n-- Converts object to string, optionally with formatting.\nfunction symbol_meta.tostring( e,fstr )\n return string.format( fstr,e[1] )\nend\n\n-- Returns \"symbol\" if object is a symbol type, else nothing.\nfunction symbol_meta:type()\n if getmetatable(self) == symbol_meta then\n return \"symbol\"\n end\nend\n\n-- Performs string.gsub on symbol.\n-- for use in matrix.replace\nfunction symbol_meta:gsub(from, to)\n return symbol.to( string.gsub( self[1],from,to ) )\nend\n\n-- creates function that replaces one letter by something else\n-- makereplacer( \"a\",4,\"b\",7, ... )(x)\n-- will replace a with 4 and b with 7 in symbol x.\n-- for use in matrix.replace\nfunction symbol_meta.makereplacer( ... )\n local tosub = {}\n local args = {...}\n for i = 1,#args,2 do\n tosub[args[i]] = args[i+1]\n end\n local function func( a ) return tosub[a] or a end\n return function(sym)\n return symbol.to( string.gsub( sym[1], \"%a\", func ) )\n end\nend\n\n-- applies abs function to symbol\nfunction symbol_meta.abs(a)\n return symbol.to(\"(\" .. a[1] .. \"):abs()\")\nend\n\n-- applies sqrt function to symbol\nfunction symbol_meta.sqrt(a)\n return symbol.to(\"(\" .. a[1] .. \"):sqrt()\")\nend\n\nfunction symbol_meta.__add(a,b)\n return symbol.to(a .. \"+\" .. b)\nend\n\nfunction symbol_meta.__sub(a,b)\n return symbol.to(a .. \"-\" .. b)\nend\n\nfunction symbol_meta.__mul(a,b)\n return symbol.to(\"(\" .. a .. \")*(\" .. b .. \")\")\nend\n\nfunction symbol_meta.__div(a,b)\n return symbol.to(\"(\" .. a .. \")/(\" .. b .. \")\")\nend\n\nfunction symbol_meta.__pow(a,b)\n return symbol.to(\"(\" .. a .. \")^(\" .. b .. \")\")\nend\n\nfunction symbol_meta.__eq(a,b)\n return a[1] == b[1]\nend\n\nfunction symbol_meta.__tostring(a)\n return a[1]\nend\n\nfunction symbol_meta.__concat(a,b)\n return tostring(a) .. tostring(b)\nend\n\nMatrix.symbol = symbol\n\nreturn Matrix";
|
|
131551
131549
|
|
|
131552
131550
|
var lua_api_math_perlin = "\n--- @module Perlin\nlocal Perlin = {}\n\n-- 1. Internal State\nlocal p = {} -- The permutation table\n\nlocal gradients = {\n {1, 1}, {-1, 1}, {1, -1}, {-1, -1},\n {1, 0}, {-1, 0}, {0, 1}, {0, -1}\n}\n\n-- 2. Custom Random Generator (Linear Congruential Generator)\n-- We use this instead of math.random to ensure results are identical\n-- across all Lua versions and operating systems.\nlocal seed_val = 0\n\nlocal function lcg_random()\n -- A simple LCG formula: (a * seed + c) % m\n seed_val = (seed_val * 1103515245 + 12345) % 2147483648\n return seed_val\nend\n\nlocal function dot_grid_gradient(ix, iy, x, y)\n -- Hash coordinates using the permutation table\n -- We mask with 255 (0xFF) to keep indices within bounds\n local hash = p[(p[(ix % 256) + 1] + (iy % 256)) % 256 + 1]\n\n -- Select gradient vector\n local g = gradients[(hash % 8) + 1]\n\n return x * g[1] + y * g[2]\nend\n\nlocal function fade(t)\n return t * t * t * (t * (t * 6 - 15) + 10)\nend\n\nlocal function lerp(a, b, t)\n return a + t * (b - a)\nend\n\n--- Set perlin noise seed. Perlin noise is initially seeded with seed value 0\n---@param newSeed number the new seed value for perlin noise.\nfunction Perlin.setSeed(newSeed)\n seed_val = newSeed or 0\n\n -- Reset table with 0-255\n for i = 1, 256 do\n p[i] = i - 1\n end\n\n -- Shuffle table deterministically using our custom LCG\n for i = 256, 2, -1 do\n -- Get a random index from 1 to i\n local r = (lcg_random() % i) + 1\n p[i], p[r] = p[r], p[i]\n end\nend\n\n--- Create a perlin noise value for x and y coordinates\n---@param x number the X coordinate for the perlin noise\n---@param y number the Y coordinate for the perlin noise\n---@return number the perlin noise value\nfunction Perlin.noise(x, y)\n -- Determine grid cell coordinates\n local x0 = math.floor(x)\n local x1 = x0 + 1\n local y0 = math.floor(y)\n local y1 = y0 + 1\n\n -- Determine interpolation weights (coords relative to cell)\n local sx = x - x0\n local sy = y - y0\n\n -- Interpolate between grid point gradients\n local n0 = dot_grid_gradient(x0, y0, sx, sy)\n local n1 = dot_grid_gradient(x1, y0, sx - 1, sy)\n local ix0 = lerp(n0, n1, fade(sx))\n\n local n0_top = dot_grid_gradient(x0, y1, sx, sy - 1)\n local n1_top = dot_grid_gradient(x1, y1, sx - 1, sy - 1)\n local ix1 = lerp(n0_top, n1_top, fade(sx))\n\n return lerp(ix0, ix1, fade(sy))\nend\n\n-- Initialize with a default seed so it works immediately\nPerlin.setSeed(0)\n\nreturn Perlin\n";
|
|
@@ -131579,7 +131577,7 @@ var lua_api_renderer = "\nlocal _internal = require('engine/_internal');\nlocal
|
|
|
131579
131577
|
|
|
131580
131578
|
var lua_api_sceneentry = "\nlocal Class = require('engine/class')\nlocal _internal = require('engine/_internal');\n\n--- @class SceneEntry\nlocal SceneEntry = Class.new()\n\nSceneEntry._id = nil\n\n--- Hook for the Class type when a new object is created\n---@private\nfunction SceneEntry:__new(id)\n self._id = id\nend\n\n--- Load the scene\nfunction SceneEntry:load()\n self:setLoaded(true)\nend\n\n--- Unload the scene\nfunction SceneEntry:unload()\n self:setLoaded(false)\nend\n\n--- Activate the scene, meaning it will be rendered\nfunction SceneEntry:activate()\n self:setActivated(true)\nend\n\n--- Deactivate the scene, meaning it will not be rendered anymore\nfunction SceneEntry:deactivate()\n self:setActivated(false)\nend\n\n--- Set the activation state of the scene. An active scene will be rendered, an inactive one won't\n---@param value boolean true or false\nfunction SceneEntry:setActivated(value)\n _internal.sendMessage('sceneentry.setActivated', { id = self._id, value = value })\nend\n\n--- Set the loading state of the scene. An unloaded scene will not be rendered and freed from memory\n---@param value boolean true or false\nfunction SceneEntry:setLoaded(value)\n _internal.sendMessage('sceneentry.setLoaded', { id = self._id, value = value })\nend\n\n--- Returns the activation state of the scene\n---@return boolean Activation state\nfunction SceneEntry:isActive()\n return _internal.sendMessage('sceneentry.isActive', { id = self._id })\nend\n\n--- Returns the loading state of the scene\n---@return boolean Loading state\nfunction SceneEntry:isLoaded()\n return _internal.sendMessage('sceneentry.isLoaded', { id = self._id })\nend\n\nreturn SceneEntry\n";
|
|
131581
131579
|
|
|
131582
|
-
var lua_api_sceneobject = "\nlocal _internal = require('engine/_internal');\nlocal Object = require 'engine/object'\nlocal handle = require 'engine/handle'\nlocal Transform = require 'engine/math/transform'\nlocal Vector3 = require 'engine/math/vector3'\nlocal Tools = require 'engine/tools'\nlocal Promise = require 'engine/promise'\n\n------------------------------------------------------------------\n-- SceneObject class\n------------------------------------------------------------------\n\n--- @class SceneObject\nlocal SceneObject = Class.new(Object)\nSceneObject._isSceneObject = true\nSceneObject._type = \"SceneObject\"\n\n--- Enumeration of the available types to use with SceneObject.create()\n--- @enum Type\nSceneObject.Type = {\n Audio = \"SgAudio\",\n AudioListener = \"AudioListener\",\n Billboard = \"SgBillboard\",\n Box = \"SgBox\",\n Camera = \"SgCamera\",\n CircleShadow = \"SgCircleShadow\",\n Cone = \"SgCone\",\n Cylinder = \"SgCylinder\",\n GaussianSplats = \"SgGaussianSplats\",\n Group = \"SgGroup\",\n Lightsource = \"SgLightsource\",\n Mesh = \"SgMesh\",\n Particles = \"SgParticles\",\n Plane = \"SgPlane\",\n Points = \"SgPoints\",\n PositionalAudio = \"SgPositionalAudio\",\n Prefab = \"SgPrefab\",\n Ring = \"SgRing\",\n Scene = \"Scene\",\n Sky = \"SgSky\",\n Sphere = \"SgSphere\",\n Text = \"SgText\",\n Torus = \"SgTorus\",\n Vehicle = \"SgVehicle\"\n}\n\n---@private\nfunction SceneObject:__new(handle, nodeId)\n Object.__new(self, handle)\n self._type = \"SceneObject\"\n self._isSceneObject = true\n self._nodeId = nodeId\nend\n\nfunction capitalizeFirstLetter(str)\n local firstChar = str:sub(1, 1)\n if firstChar:lower() == firstChar then\n firstChar = firstChar:upper()\n return firstChar .. str:sub(2)\n end\n return str\nend\n\n---@private\nfunction SceneObject:__customIndex(key)\n -- any private properties of scene objects start with an underscore. if so, just use rawget, otherwise\n -- get the value of the field from the engine and return it\n local value = rawget(self, key)\n if value or type(key) ~= 'string' or string.sub(key, 1, 1) == '_' then\n return value\n else\n return self:get(key)\n end\nend\n\n---@private\nfunction SceneObject:__customNewIndex(key, value)\n -- any private properties of scene objects start with an underscore. if so, just use rawset, otherwise\n -- write the value to the field in the engine\n if type(key) ~= 'string' or string.sub(key, 1, 1) == '_' then\n rawset(self, key, value)\n else\n self:set(key, value)\n end\nend\n\n--- Finds all objects with the given name.\n---\n--- The search can be restricted to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search covers the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findAllByName(name root?)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findAllByName(name, root?)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param name string Name of the objects in the scenegraph.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return table A table containing all found objects, or an empty table if none are found.\n--- @usage\n--- -- Global search\n--- local objects = SceneObject.findAllByName(\"Enemy\")\n--- for _, obj in ipairs(objects) do\n--- print(\"Found object with type:\", obj:getType())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local level = SceneObject.findByName(\"Level1\")\n--- local objects = SceneObject.findAllByName(\"Enemy\", level)\n--- for _, obj in ipairs(objects) do\n--- print(\"Found object under parent with type:\", obj:getType())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local level = SceneObject.findByName(\"Level1\")\n--- local enemies = level:findAllByName(\"Enemy\")\n--- for _, obj in ipairs(enemies) do\n--- print(\"Found enemy under level:\", obj:getName())\n--- end\nfunction SceneObject.findAllByName(self, name, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided (and self is not the class)\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n name, root = self, name\n end\n \n __assertTrace(name, \"name is empty\")\n \n local objects = _internal.sendMessage('sceneobject.findAllByName', { \n name = name, \n root = root and root._handle or nil \n })\n \n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n \n return objects\nend\n\n--- Finds the first occurrence of an object with the given name.\n---\n--- The search can be restricted to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search covers the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findByName(name root)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findByName(name root)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param name string Name of the object in the scenegraph.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return Object|nil The first object found with the given name, or `nil` if not found.\n--- @usage\n--- -- Global search\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- print(\"Found object:\", obj:getName())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local obj = SceneObject.findByName(\"Player\", parentNode)\n--- if obj then\n--- print(\"Found object under parent:\", obj:getName())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local player = SceneObject.findByName(\"Player\")\n--- local weapon = player:findByName(\"Weapon\")\n--- if weapon then\n--- print(\"Found weapon under player:\", weapon:getName())\n--- end\nfunction SceneObject.findByName(self, name, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n name, root = self, name\n end\n \n __assertTrace(name, \"name is empty\")\n \n return handle.createObject(_internal.sendMessage('sceneobject.findByName', { \n name = name, \n root = root and root._handle or nil \n }))\nend\n\n---Find an object by its unique ID and return it.\n---\n--- @param id string The unique ID of the object.\n--- @return Object The found object or nil if not found.\n--- @usage\n--- local obj = SceneObject.findById(\"2135a78324fe\")\n--- if obj then\n--- print(\"Found object: \", obj:getName())\n--- end\nfunction SceneObject.findById(self, id)\n if type(self) == 'string' then\n id = self\n end\n if not id then\n return nil\n end\n return handle.createObject(_internal.sendMessage('sceneobject.findById', { id=id }))\nend\n\n--- Finds all objects that have the specified tag.\n---\n--- The search can be limited to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search scans the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findByTag(tag, root)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findByTag(tag, root)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param tag string The tag to look for.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return table A table of objects that have the given tag. May be empty if no matches are found.\n--- @usage\n--- -- Global search\n--- local enemies = SceneObject.findByTag(\"Enemy\")\n--- for _, enemy in ipairs(enemies) do\n--- print(\"Found enemy:\", enemy:getName())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local tagged = SceneObject.findByTag(\"Interactable\", parentNode)\n--- for _, obj in ipairs(tagged) do\n--- print(\"Found interactable under parent:\", obj:getName())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local level = SceneObject.findByName(\"Level1\")\n--- local enemies = level:findByTag(\"Enemy\")\n--- for _, enemy in ipairs(enemies) do\n--- print(\"Found enemy under level:\", enemy:getName())\n--- end\nfunction SceneObject.findByTag(self, tag, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided (and self is not the class)\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n tag, root = self, tag\n end\n \n __assertTrace(tag, \"tag is empty\")\n \n local objects = _internal.sendMessage('sceneobject.findByTag', { \n tag = tag, \n root = root and root._handle or nil \n })\n \n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n \n return objects\nend\n\n---Create a new object in the scenegraph. This is a generic function, for specialized versions, see the respective Sg<name> classes like SgBox.\n--- @param objectType string Type of object (e.g., SceneObject.Type.Sphere).\n--- @param options table The options to use. Common options are listed below. Additional type-specific options are documented in the specialized functions (e.g., SgBox.create, SgSphere.create).\n--- @param parent Object the parent object to add the new one to or nil to put it at root level\n--- @return Promise a promise resolving to a SceneObject\n--- @usage\n--- -- Options can be omitted. This example shows the defaults,\n--- -- only specify the ones you want different.\n--- SceneObject.create(SceneObject.Type.Sphere, {\n--- active = true,\n--- static = false,\n--- name = \"\",\n--- transform = Transform.new(),\n--- layers = {0},\n--- tags = {},\n--- receiveShadow = false,\n--- castShadow = false,\n--- -- Type-specific options (e.g., for Sphere):\n--- radius = 1,\n--- widthSegments = 8,\n--- heightSegments = 6\n--- }, nil):next(function(obj)\n--- obj:setActive(true)\n--- end)\nfunction SceneObject.create(self, objectType, options, parent)\n if (type(self) == \"string\") then\n objectType, options, parent = self, objectType, options\n end\n\n __assertTrace(type(objectType) == 'string', 'objectType must be a string')\n __assertTrace(parent == nil or (type(parent) == 'table' and parent._isSceneObject), 'parent must be a SceneObject')\n\n local p = _internal.sendMessage('sceneobject.create', {\n type = objectType,\n options = options,\n parent = parent and parent._handle or nil\n })\n return Promise:new(p)\nend\n\n--- Get the ID of the scene object.\n--- @return string The unique ID of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- print(\"Object ID:\", obj:getId())\nfunction SceneObject:getId()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self._nodeId\nend\n\n--- Get the name of the scene object.\n--- @return string The user-defined name of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- print(\"Object name:\", obj:getName())\nfunction SceneObject:getName()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getName', { obj=self._handle })\nend\n\n--- Return the display name of the scene object.\n--- In case the object has no user-given name, a name will be generated\n--- in the format \"type <id>\"\n--- @return string Displayname\n--- @usage\n--- local obj = SceneObject.findByName(\"Enemy\")\n--- print(\"Display name:\", obj:getDisplayName())\nfunction SceneObject:getDisplayName()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getDisplayName', { obj=self._handle })\nend\n\n--- Return the type of the object\n--- @return string Type\n--- @usage\n--- local obj = SceneObject.findByName(\"Wall\")\n--- print(\"Object type:\", obj:getType())\nfunction SceneObject:getType()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getType', { obj=self._handle })\nend\n\n--- Set a field of the scene object to a new value.\n---\n--- @param name string The name of the field.\n--- @param value any The new value to set.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- obj:set(\"Brightness\", \"High\")\nfunction SceneObject:set(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get a field value from the scene object.\n---\n--- @param name string The name of the field.\n--- @return string The value of the field.\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local intensity = obj:get(\"Intensity\")\n--- print(\"Intensity: \" .. intensity)\nfunction SceneObject:get(name)\n\n -- This is a replacement for the old get() function without parameters that would just return a node reference\n -- It needs to stay in place as long as there are still scripts that depend on it.\n if name == nil then\n return self\n end\n\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n local field = _internal.sendMessage('sceneobject.get', { obj=self._handle, name=name })\n if field == nil then\n return nil\n end\n return toType(field.value, field.type)\nend\n\n--- Set a field of the scene object to a new value.\n---@deprecated 25.11 Use ``set`` which is the new shorter version\n--- @param name string The name of the field.\n--- @param value any The new value to set.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- obj:setFieldValue(\"Brightness\", \"High\")\nfunction SceneObject:setFieldValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get a field value from the scene object.\n---@deprecated 25.11 Use ``get`` which is the new shorter version\n--- @param name string The name of the field.\n--- @return string The value of the field.\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local intensity = obj:getFieldValue(\"Intensity\")\n--- print(\"Intensity: \" .. intensity)\nfunction SceneObject:getFieldValue(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n local field = _internal.sendMessage('sceneobject.get', { obj=self._handle, name=name })\n if field == nil then\n return nil\n end\n return toType(field.value, field.type)\nend\n\n--- Animate a field from its current value to a new value.\n---\n--- @param name string The name of the field\n--- @param value any The new value to animate to.\n--- @param duration number The duration in milliseconds for the animation to take\n--- @param ease string The type of easing to use. Possible values: 'linear', 'easeInOutQuad'\n--- @return number animation id. Can be used to cancel animation using cancelAnimation(id)\nfunction SceneObject:animateFieldValue(name, value, duration, ease)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not (type(value) == 'table'), \"Cannot animate objects!\");\n name = capitalizeFirstLetter(name)\n return _internal.sendMessage('sceneobject.animateFieldValue', { obj=self._handle, name=name, value=value, duration=duration, ease=ease })\nend\n\n--- Cancel a running animation\n---\n--- @param name string The name of the field\nfunction SceneObject:cancelAnimation(id)\n _internal.sendMessage('sceneobject.cancelAnimation', { obj=self._handle, id=id })\nend\n\n--- Set a field of the scene object to a new color value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value Color The new color value.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- obj:setFieldColorValue(\"Color\", Color.new(1, 0, 0, 1)) -- Sets the object to red.\nfunction SceneObject:setFieldColorValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(value._isColor, \"value is not a Color\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value:toData() })\nend\n\n--- Set a field of the scene object to a new number value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value number The new number value.\nfunction SceneObject:setFieldNumberValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(value) == \"number\", \"value must be a number\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n---Set a field of the scene object to a new enum value\n--- @param name string\n--- @param value number\nfunction SceneObject:setFieldEnumValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n---Set a field of the scene object to a new boolean value\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string\n--- @param value boolean\nfunction SceneObject:setFieldBooleanValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if value then value = true else value = false end\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get the layers of the scene object\n--- @return table A table containing layer indices\nfunction SceneObject:getLayers()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local internal_layers = self:getFieldValue(\"Layers\")\n __assertTrace(type(internal_layers) == \"table\", \"Expected 'Layers' to be a table\")\n\n -- convert internal 0-based indices to frontend 1-based indices\n local layers = {}\n for _, layer_idx in ipairs(internal_layers) do\n table.insert(layers, layer_idx + 1)\n end\n\n return layers\nend\n\n--- Set the layers of the scene object.\n--- Layers control rendering visibility and interaction with other objects.\n---\n--- @param layers table A table containing layer indices.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- obj:setLayers({1, 3, 5}) -- Assigns the object to layers 1, 3, and 5.\nfunction SceneObject:setLayers(layers)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(layers) == \"table\", \"Layers must be provided as a table\")\n self:setFieldValue(\"Layers\", layers)\nend\n\n--- Get the value of a scene object's field as color value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return Color value of the field\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local color = obj:getFieldColorValue(\"Color\")\n--- print(\"Color: \" .. color)\nfunction SceneObject:getFieldColorValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as number value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return number value of the field\nfunction SceneObject:getFieldNumberValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as boolean value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return boolean value of the field\nfunction SceneObject:getFieldBooleanValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as string value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return string value of the field\nfunction SceneObject:getFieldEnumValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as string value\n--- @return string value of the field\nfunction SceneObject:getFieldEnumOptions(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getFieldOptions', { obj=self._handle, name=name })\nend\n\n--- Set a field of the scene object to a new boolean value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value boolean The new boolean value.\n--- @usage\n--- local obj = SceneObject.findByName(\"Door\")\n--- obj:setFieldBooleanValue(\"PhysicsEnabled\", true)\nfunction SceneObject:setFieldBooleanValue(name, value)\n self:setFieldValue(name, not not value)\nend\n\n---Get a link of the scene object to a new value\n---@deprecated 25.11 Use ``get`` which handles items now too\n--- @param name string name of the link field\n--- @return number handle of the linked item or nil if not set\nfunction SceneObject:getLinkItem(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n return handle.createObject(_internal.sendMessage('sceneobject.getLinkItem', { obj=self._handle, name=name }))\nend\n\n---Set a link from the scene object\n---@deprecated 25.11 Use ``set`` which handles items now too\n--- @param name string name of the link field\n--- @param item string value can be either an item ID or an Item object\nfunction SceneObject:setLinkItem(name, item)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not item or (item and item._handle and item._isItem), \"item has to be an object of Item class or nil!\");\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.setLinkItem', { obj=self._handle, name=name, item=item })\nend\n\n--- Set the position of the scene object.\n--- Sets the object's local position to the given coordinates.\n---\n--- @param x number Position along the X-axis.\n--- @param y number Position along the Y-axis.\n--- @param z number Position along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- obj:setPosition(0, 5, 0) -- Places the object at (0, 5, 0)\n--- end\nfunction SceneObject:setPosition(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n\n _internal.sendMessage('sceneobject.setPosition', {\n obj = self._handle,\n x = x,\n y = y,\n z = z\n })\nend\n\n--- Translate (move) the scene object by an offset.\n--- Adds the given offset to the object's current local position.\n---\n--- @param x number Offset along the X-axis.\n--- @param y number Offset along the Y-axis.\n--- @param z number Offset along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- obj:translate(0, 1, 0) -- Moves the object up by 1 unit from its current position\n--- end\nfunction SceneObject:translate(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.translate', {\n obj = self._handle,\n x = x,\n y = y,\n z = z\n })\nend\n\n--- Rotate the scene object.\n--- Rotation is applied in degrees and follows Euler angles (XYZ order).\n---\n--- @param x number Rotation around the X-axis in degrees.\n--- @param y number Rotation around the Y-axis in degrees.\n--- @param z number Rotation around the Z-axis in degrees.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- if obj then\n--- obj:rotate(45, 0, 0) -- Rotates 45 degrees around the X-axis\n--- end\nfunction SceneObject:rotate(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.rotate', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Scale the scene object.\n---\n--- @param x number Scale factor along the X-axis.\n--- @param y number Scale factor along the Y-axis.\n--- @param z number Scale factor along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Tree\")\n--- if obj then\n--- obj:scale(2, 2, 2) -- Doubles the size of the object\n--- end\nfunction SceneObject:scale(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.scale', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Get the local transform of a scene object.\n--- The transform contains position, rotation, and scale relative to the object's parent.\n---\n--- @param cacheLocal boolean if set to true, the transform will be cached locally for the future for performance reasons. This should only be used if no other script changes the transform\n--- @return Transform The object's local transform.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- local transform = obj:getTransform()\n--- print(\"Local position:\", transform.position)\nfunction SceneObject:getTransform(cacheLocal)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if self._transform == nil or not cacheLocal then\n self._transform = Transform.fromData(_internal.sendMessage('sceneobject.getTransform', { obj=self._handle }))\n end\n return self._transform:clone()\nend\n\n--- Apply a function to modify the object's transform, then update it.\n--- This allows making changes to the transform while ensuring they are properly applied back.\n---\n--- @param func function A function that takes a Transform object as its only argument.\n--- @param cacheLocal boolean if set to true, the transform will be cached locally for the future for performance reasons. This should only be used if no other script changes the transform\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- obj:withTransform(function(transform)\n--- transform.position.x = transform.position.x + 1 -- Move object 1 unit to the right\n--- end)\nfunction SceneObject:withTransform(func, cacheLocal)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(func) == 'function', \"argument func is not a function\")\n local transform = self:getTransform(cacheLocal)\n func(transform)\n self:setTransform(transform)\nend\n\n--- Get the world transform of a scene object.\n--- The world transform includes absolute position, rotation, and scale in the scene.\n---\n--- @return Transform The object's world transform.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- local worldTransform = obj:getWorldTransform()\n--- print(\"World position:\", worldTransform.position)\nfunction SceneObject:getWorldTransform()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Transform.fromData(_internal.sendMessage('sceneobject.getWorldTransform', { obj=self._handle }))\nend\n\n--- Get the forward direction vector in world space.\n--- Returns the local -Z axis transformed by the object's rotation.\n---\n--- @return Vector3 The forward direction in world space.\nfunction SceneObject:getForward()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getForward', { obj=self._handle }))\nend\n\n--- Get the right direction vector in world space.\n--- Returns the local +X axis transformed by the object's rotation.\n---\n--- @return Vector3 The right direction in world space.\nfunction SceneObject:getRight()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getRight', { obj=self._handle }))\nend\n\n--- Get the up direction vector in world space.\n--- Returns the local +Y axis transformed by the object's rotation.\n---\n--- @return Vector3 The up direction in world space.\nfunction SceneObject:getUp()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getUp', { obj=self._handle }))\nend\n\n--- Set the local transform of a scene object.\n--- Updates the object's position, rotation, and scale relative to its parent.\n---\n--- @param transform Transform The new local transform to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Door\")\n--- local newTransform = Transform.new()\n--- newTransform.position = Vector3.new(0, 2, 0) -- Move the object up 2 units\n--- obj:setTransform(newTransform)\nfunction SceneObject:setTransform(transform)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(transform and transform._isTransform, \"transform has to be an object of type Transform\")\n self._transform = transform\n _internal.sendMessage('sceneobject.setTransform', { obj=self._handle, transform=self._transform:toData() })\nend\n\n--- Set the physics position of the scene object.\n---\n--- This directly updates the position of the object inside the physics engine.\n--- The position is expressed in world coordinates.\n--- Only works for objects added to the physics engine\n--- @param position Vector3 New world position of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:setPhysicsPosition(Vector3.new(0, 5, 0))\nfunction SceneObject:setPhysicsPosition(position)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(position and position._isVector3, \"position has to be an object of type Vector3\")\n _internal.sendMessage('sceneobject.setPhysicsPosition', { obj=self._handle, position=position:toData() })\nend\n\n--- Set the physics rotation of the scene object.\n---\n--- This directly updates the rotation of the object inside the physics engine.\n--- The rotation is expressed in world space.\n--- Only works for objects added to the physics engine\n--- @param rotation Quaternion New world rotation of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:setPhysicsRotation(Quaternion.fromEuler(0, math.pi, 0))\nfunction SceneObject:setPhysicsRotation(rotation)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(rotation and rotation._isQuaternion, \"rotation has to be an object of type Quaternion\")\n _internal.sendMessage('sceneobject.setPhysicsRotation', { obj=self._handle, rotation=rotation:toData() })\nend\n\nfunction SceneObject:setPhysicsRotation(rotation)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(rotation and rotation._isQuaternion, \"rotation has to be an object of type Quaternion\")\n _internal.sendMessage('sceneobject.setPhysicsRotation', { obj=self._handle, rotation=rotation:toData() })\nend\n\n--- Get the linear velocity of the scene object.\n--- This function returns the object's velocity in world coordinates.\n--- Only works for objects added to the physics engine with `watchLinearVelocity` enabled.\n---\n--- @return Vector3 The linear velocity of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Ball\")\n--- local velocity = obj:getLinearVelocity()\n--- print(\"Velocity:\", velocity)\nfunction SceneObject:getLinearVelocity()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getLinearVelocity', { obj=self._handle }))\nend\n\n--- Set the linear velocity of the scene object.\n--- This function assigns a new velocity to the object, but only works if the object is in the physics engine.\n---\n--- @param velocity Vector3 The new linear velocity to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Car\")\n--- obj:setLinearVelocity(Vector3.new(10, 0, 0)) -- Move object at speed 10 in the X direction.\nfunction SceneObject:setLinearVelocity(velocity)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(velocity and velocity._isVector3, \"velocity must be an object of type Vector3\")\n local data = velocity:toData()\n _internal.sendMessage('sceneobject.setLinearVelocity', { obj=self._handle, velocity=data })\nend\n\n--- Get the angular velocity of the scene object.\n--- Only works for objects added to the physics engine with `watchAngularVelocity` enabled.\n---\n--- @return Vector3 The angular velocity of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Wheel\")\n--- local angVel = obj:getAngularVelocity()\n--- print(\"Angular velocity:\", angVel)\nfunction SceneObject:getAngularVelocity()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getAngularVelocity', { obj=self._handle }))\nend\n\n--- Set the angular velocity of the scene object.\n---\n--- @param velocity Vector3 The new angular velocity to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Propeller\")\n--- obj:setAngularVelocity(Vector3.new(0, 10, 0)) -- Rotate around Y-axis.\nfunction SceneObject:setAngularVelocity(velocity)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(velocity and velocity._isVector3, \"velocity must be an object of type Vector3\")\n local data = velocity:toData()\n _internal.sendMessage('sceneobject.setAngularVelocity', { obj=self._handle, velocity=data })\nend\n\n--- Apply an impulse to the scene object.\n---\n--- An **impulse** is a sudden force applied to an object that immediately\n--- changes its velocity. This is different from a continuous force, which\n--- is applied over time.\n---\n--- This function only works on objects that are physics-enabled.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @param relPos Vector3 The relative position where the impulse is applied. Default is (0, 0, 0)\nfunction SceneObject:applyImpulse(impulse, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n local impulseData = impulse:toData()\n local relPosData = relPos:toData()\n _internal.sendMessage('sceneobject.applyImpulse', { obj=self._handle, impulse=impulseData, relPos=relPosData })\nend\n\n--- Apply an impulse to the scene object that respects constraints.\n---\n--- Only works on physics-enabled objects.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @param relPos Vector3 The relative position where the impulse is applied. Default is (0, 0, 0)\nfunction SceneObject:applyPushImpulse(impulse, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyPushImpulse', { obj=self._handle, impulse=impulse:toData(), relPos=relPos:toData() })\nend\n\n--- Apply an impulse to the center of the object.\n---\n--- Applies a sudden velocity change at the object's center. Only works on physics-enabled objects.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:applyCentralImpulse(Vector3.new(0, 5, 0)) -- Apply upward impulse.\nfunction SceneObject:applyCentralImpulse(impulse)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyCentralImpulse', { obj=self._handle, impulse=impulse:toData() })\nend\n\n--- Apply a central impulse that respects constraints.\n---\n--- Only works on physics-enabled objects and respects any joint or constraint applied.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:applyCentralPushImpulse(Vector3.new(0, 5, 0)) -- Apply upward impulse.\nfunction SceneObject:applyCentralPushImpulse(impulse)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyCentralPushImpulse', { obj=self._handle, impulse=impulse:toData() })\nend\n\n--- Apply a force to the scene object.\n---\n--- Unlike impulses, **forces** are applied over time, influencing the object's acceleration.\n--- Only works on physics-enabled objects.\n---\n--- @param force Vector3 The force vector to apply.\n--- @param relPos Vector3 The relative position where the force is applied. Default is (0, 0, 0)\nfunction SceneObject:applyForce(force, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(force and force._isVector3, \"force must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyForce', { obj=self._handle, force=force:toData(), relPos=relPos:toData() })\nend\n\n--- Clear any forces on a scene object.\n--- Only works on physics-enabled objects.\n--- @usage\n--- local obj = SceneObject.findByName(\"Ball\")\n--- obj:clearForces()\nfunction SceneObject:clearForces()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.clearForces', { obj=self._handle })\nend\n\n--- Delete the scene object from the scene.\n---\n--- @usage\n--- local obj = scene:findByName(\"TemporaryObject\")\n--- if obj then\n--- obj:delete()\n--- end\nfunction SceneObject:delete()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.delete', { obj=self._handle })\n if self._handle then\n handle.invalidate(self._handle)\n end\n self._handle = nil\nend\n\n--- Clone a scene object to a new position and return it.\n---\n--- The position can be provided as:\n--- 1. Separate `x, y, z` coordinates.\n--- 2. A `Vector3` containing the position.\n---\n--- If no position is provided, the clone will appear in the same location as the original.\n---\n--- @param x number | Vector3 The X-coordinate of the new position, or a `Vector3` containing `x, y, z`. If a `Vector3` is passed, `y` and `z` should be omitted.\n--- @param y number (optional) The Y-coordinate of the new position. Omit this if passing a `Vector3`.\n--- @param z number (optional) The Z-coordinate of the new position. Omit this if passing a `Vector3`.\n--- @param parent SceneObject (optional) The parent object to attach the new object to.\n--- @return Promise A promise resolving to a SceneObject.\n---\n--- @usage\n--- local obj = scene:findByName(\"Tree\")\n--- obj:clone(5, 0, 2):next(function(clone)\n--- clone:setActive(true) -- Activate the cloned object\n--- end)\n---\n--- local pos = Vector3.new(3, 1, 0)\n--- obj:clone(pos):next(function(clone)\n--- clone:setFieldValue(\"Name\", \"ClonedTree\") -- Rename the cloned object\n--- end)\n---\n--- local parentObj = scene:findByName(\"Group\")\n--- obj:clone(pos, parentObj):next(function(clone)\n--- clone:setFieldValue(\"Name\", \"AnotherClonedTree\") -- Rename the cloned object\n--- end)\nfunction SceneObject:clone(x, y, z, parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n\n -- Handle case where first argument is a Vector3 and second argument is the parent\n if type(x) == \"table\" and x._isVector3 then\n x, y, z, parent = x.x, x.y, x.z, y -- Shift arguments\n end\n\n local p = _internal.sendMessage('sceneobject.clone', {\n obj = self._handle,\n x = x,\n y = y,\n z = z,\n parent = parent and parent._handle or nil\n })\n\n return Promise:new(p)\nend\n\n--- Create a new object of same type from this one. If no options are specified this will be basically a clone.\n--- If an options table is passed, this will specify all fields with their values that should be changed.\n--- At the very least, this should contain a new Transform, otherwise the new object will be at the exact location\n--- of the old one.\n--- @param options table the table of fields to set on the new object\n--- @param parent SceneObject (optional) The parent object to attach the new object to.\n--- @return Promise A promise resolving to a SceneObject.\n---\n--- @usage\n--- local obj = SceneObject.findByName(\"Tree\")\n--- local parentObj = SceneObject.findByName(\"Group\")\n--- local clone = await(obj:createFromThis({\n--- transform = Transform.new(Vector3.new(4, 6, 8)),\n--- active = true,\n--- name = \"Cloned tree\"\n--- }, parentObj)\nfunction SceneObject:createFromThis(options, parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(parent == nil or parent._isSceneObject, \"parent is not nil or a SceneObject\")\n\n local p = _internal.sendMessage('sceneobject.createFromThis', {\n obj = self._handle,\n options = options,\n parent = parent and parent._handle or nil\n })\n\n return Promise:new(p)\nend\n\n--- Move the scene object to a different parent. This parent can not be a child of the scene object\n--- @param parent SceneObject (optional) The parent object to attach the new object to. if nil, the object will be moved to the scenegraph root\nfunction SceneObject:moveToParent(parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not parent or parent._isSceneObject, \"parent is not a SceneObject\")\n _internal.sendMessage('sceneobject.moveToParent', { obj = self._handle, parent = parent and parent._handle or nil })\nend\n\n--- Set the active state of the scene object.\n--- @param value boolean True to activate, false to deactivate.\nfunction SceneObject:setActive(value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n self:setFieldValue(\"Active\", value)\nend\n\n--- Get the active state of the scene object.\n--- @return boolean True if the object is active, false otherwise.\nfunction SceneObject:getActive()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getFieldBooleanValue(\"Active\")\nend\n\n--- Check if the object has a specific tag.\n--- @param tagName string The tag name to check.\n--- @return boolean True if the object has the tag, false otherwise.\nfunction SceneObject:hasTag(tagName)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local tags = self:getFieldValue(\"Tags\")\n for _, value in pairs(tags) do\n if value == tagName then\n return true\n end\n end\n return false\nend\n\n--- Get a list of all tags assigned to the object.\n--- @return table An array of tag names.\nfunction SceneObject:getTags()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getFieldValue(\"Tags\")\nend\n\n--- Assign a set of tags to the object.\n--- @param tags table An array of tag names.\nfunction SceneObject:setTags(tags)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n Tools.validateArrayOfStrings(tags)\n self:setFieldValue(\"Tags\", tags)\nend\n\n--- Add a tag to the object.\n--- @param tag string The tag to add.\nfunction SceneObject:addTag(tag)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(tag) == 'string', \"tag needs to be a string\")\n local tags = self:getTags()\n table.insert(tags, tag)\n self:setTags(tags)\nend\n\n--- Remove all tags from the object.\nfunction SceneObject:clearTags()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n self:setTags({})\nend\n\n--- Take a snapshot of the object and its children, if specified.\n--- This allows restoring the object to a previous state.\n--- @param recursive boolean Whether to include child objects in the snapshot.\n--- @return number The snapshot handle.\nfunction SceneObject:takeSnapshot(recursive)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.takeSnapshot', { obj=self._handle, recursive=recursive })\nend\n\n--- Restore a snapshot, reverting any changes made after the snapshot was taken.\n--- @param snapshot number The snapshot handle to restore.\nfunction SceneObject:rollbackSnapshot(snapshot)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.rollbackSnapshot', { obj=self._handle, snapshot=snapshot })\nend\n\n--- Check if the object is enabled.\n--- @return boolean True if the object is enabled, false otherwise.\nfunction SceneObject:isEnabled()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.isEnabled', { obj=self._handle })\nend\n\n--- Enable the object.\nfunction SceneObject:enable()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.enable', { obj=self._handle })\nend\n\n--- Disable the object.\nfunction SceneObject:disable()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.disable', { obj=self._handle })\nend\n\n--- Get the material assigned to the object.\n--- @return Material The material, or nil if none is assigned.\nfunction SceneObject:getMaterial()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getLinkItem(\"Material\")\nend\n\n--- Set the object's material.\n--- @param mat Material The material to set or nil to clear the material\nfunction SceneObject:setMaterial(mat)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(mat == nil or (type(mat) == 'table' and mat._isMaterial), \"self is not a SceneObject\")\n self:setLinkItem(\"Material\", mat)\nend\n\n--- Get all behaviours attached to this object.\n---@deprecated 25.12 use getBehaviours.\n--- @return table A list of Behaviour objects.\nfunction SceneObject:getEntities()\n return self:getBehaviours()\nend\n\n--- Get all behaviours attached to this object.\n--- @return table A list of Behaviour objects.\nfunction SceneObject:getBehaviours()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if not self._nodeId then\n error(\"This scene object does not have a node Id. Cannot get its behaviours.\")\n end\n return Behaviour.getByNodeId(self._nodeId)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:getBehaviourByScriptName(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if not self._nodeId then\n error(\"This scene object does not have a node Id. Cannot get its entities.\")\n end\n return Behaviour.getByNodeIdAndScriptName(self._nodeId, name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n---@deprecated\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:getEntityByScriptName(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:behaviour(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:entity(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get the parent of the object.\n--- @return SceneObject The parent object, or nil if none exists.\nfunction SceneObject:getParent()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return handle.createObject(_internal.sendMessage('sceneobject.getParent', { obj=self._handle }))\nend\n\n--- Get the children of the object.\n--- @return table A table of child objects.\nfunction SceneObject:getChildren()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local objects = _internal.sendMessage('sceneobject.getChildren', { obj=self._handle })\n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n return objects\nend\n\n--- Enable or disable automatic recreation of the scene object between play and stop.\n---\n--- When `value` is true, the object will be automatically recreated every time\n--- the scene starts or stops. This is useful for objects that need to reset\n--- physics, state, or procedural components at each run.\n---\n--- @param value boolean Enable (`true`) or disable (`false`) automatic recreation.\nfunction SceneObject:setAutoRecreate(value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.setAutoRecreate', { obj=self._handle, value=value })\nend\n\n--- Immediately recreate the scene object.\n---\n--- This forces the object to reset its state, physics, and components as if\n--- it was newly instantiated in the scene.\nfunction SceneObject:recreate()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.recreate', { obj=self._handle })\nend\n\nreturn SceneObject\n";
|
|
131580
|
+
var lua_api_sceneobject = "\nlocal _internal = require('engine/_internal');\nlocal Object = require 'engine/object'\nlocal handle = require 'engine/handle'\nlocal Transform = require 'engine/math/transform'\nlocal Vector3 = require 'engine/math/vector3'\nlocal Bounds = require 'engine/math/bounds'\nlocal Tools = require 'engine/tools'\nlocal Promise = require 'engine/promise'\n\n------------------------------------------------------------------\n-- SceneObject class\n------------------------------------------------------------------\n\n--- @class SceneObject\nlocal SceneObject = Class.new(Object)\nSceneObject._isSceneObject = true\nSceneObject._type = \"SceneObject\"\n\n--- Enumeration of the available types to use with SceneObject.create()\n--- @enum Type\nSceneObject.Type = {\n Audio = \"SgAudio\",\n AudioListener = \"AudioListener\",\n Billboard = \"SgBillboard\",\n Box = \"SgBox\",\n Camera = \"SgCamera\",\n CircleShadow = \"SgCircleShadow\",\n Cone = \"SgCone\",\n Cylinder = \"SgCylinder\",\n GaussianSplats = \"SgGaussianSplats\",\n Group = \"SgGroup\",\n Lightsource = \"SgLightsource\",\n Mesh = \"SgMesh\",\n Particles = \"SgParticles\",\n Plane = \"SgPlane\",\n Points = \"SgPoints\",\n PositionalAudio = \"SgPositionalAudio\",\n Prefab = \"SgPrefab\",\n Ring = \"SgRing\",\n Scene = \"Scene\",\n Sky = \"SgSky\",\n Sphere = \"SgSphere\",\n Text = \"SgText\",\n Torus = \"SgTorus\",\n Vehicle = \"SgVehicle\"\n}\n\n---@private\nfunction SceneObject:__new(handle, nodeId)\n Object.__new(self, handle)\n self._type = \"SceneObject\"\n self._isSceneObject = true\n self._nodeId = nodeId\nend\n\nfunction capitalizeFirstLetter(str)\n local firstChar = str:sub(1, 1)\n if firstChar:lower() == firstChar then\n firstChar = firstChar:upper()\n return firstChar .. str:sub(2)\n end\n return str\nend\n\n---@private\nfunction SceneObject:__customIndex(key)\n -- any private properties of scene objects start with an underscore. if so, just use rawget, otherwise\n -- get the value of the field from the engine and return it\n local value = rawget(self, key)\n if value or type(key) ~= 'string' or string.sub(key, 1, 1) == '_' then\n return value\n else\n return self:get(key)\n end\nend\n\n---@private\nfunction SceneObject:__customNewIndex(key, value)\n -- any private properties of scene objects start with an underscore. if so, just use rawset, otherwise\n -- write the value to the field in the engine\n if type(key) ~= 'string' or string.sub(key, 1, 1) == '_' then\n rawset(self, key, value)\n else\n self:set(key, value)\n end\nend\n\n--- Finds all objects with the given name.\n---\n--- The search can be restricted to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search covers the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findAllByName(name root?)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findAllByName(name, root?)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param name string Name of the objects in the scenegraph.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return table A table containing all found objects, or an empty table if none are found.\n--- @usage\n--- -- Global search\n--- local objects = SceneObject.findAllByName(\"Enemy\")\n--- for _, obj in ipairs(objects) do\n--- print(\"Found object with type:\", obj:getType())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local level = SceneObject.findByName(\"Level1\")\n--- local objects = SceneObject.findAllByName(\"Enemy\", level)\n--- for _, obj in ipairs(objects) do\n--- print(\"Found object under parent with type:\", obj:getType())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local level = SceneObject.findByName(\"Level1\")\n--- local enemies = level:findAllByName(\"Enemy\")\n--- for _, obj in ipairs(enemies) do\n--- print(\"Found enemy under level:\", obj:getName())\n--- end\nfunction SceneObject.findAllByName(self, name, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided (and self is not the class)\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n name, root = self, name\n end\n \n __assertTrace(name, \"name is empty\")\n \n local objects = _internal.sendMessage('sceneobject.findAllByName', { \n name = name, \n root = root and root._handle or nil \n })\n \n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n \n return objects\nend\n\n--- Finds the first occurrence of an object with the given name.\n---\n--- The search can be restricted to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search covers the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findByName(name root)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findByName(name root)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param name string Name of the object in the scenegraph.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return Object|nil The first object found with the given name, or `nil` if not found.\n--- @usage\n--- -- Global search\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- print(\"Found object:\", obj:getName())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local obj = SceneObject.findByName(\"Player\", parentNode)\n--- if obj then\n--- print(\"Found object under parent:\", obj:getName())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local player = SceneObject.findByName(\"Player\")\n--- local weapon = player:findByName(\"Weapon\")\n--- if weapon then\n--- print(\"Found weapon under player:\", weapon:getName())\n--- end\nfunction SceneObject.findByName(self, name, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n name, root = self, name\n end\n \n __assertTrace(name, \"name is empty\")\n \n return handle.createObject(_internal.sendMessage('sceneobject.findByName', { \n name = name, \n root = root and root._handle or nil \n }))\nend\n\n---Find an object by its unique ID and return it.\n---\n--- @param id string The unique ID of the object.\n--- @return Object The found object or nil if not found.\n--- @usage\n--- local obj = SceneObject.findById(\"2135a78324fe\")\n--- if obj then\n--- print(\"Found object: \", obj:getName())\n--- end\nfunction SceneObject.findById(self, id)\n if type(self) == 'string' then\n id = self\n end\n if not id then\n return nil\n end\n return handle.createObject(_internal.sendMessage('sceneobject.findById', { id=id }))\nend\n\n--- Finds all objects that have the specified tag.\n---\n--- The search can be limited to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search scans the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findByTag(tag, root)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findByTag(tag, root)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param tag string The tag to look for.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return table A table of objects that have the given tag. May be empty if no matches are found.\n--- @usage\n--- -- Global search\n--- local enemies = SceneObject.findByTag(\"Enemy\")\n--- for _, enemy in ipairs(enemies) do\n--- print(\"Found enemy:\", enemy:getName())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local tagged = SceneObject.findByTag(\"Interactable\", parentNode)\n--- for _, obj in ipairs(tagged) do\n--- print(\"Found interactable under parent:\", obj:getName())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local level = SceneObject.findByName(\"Level1\")\n--- local enemies = level:findByTag(\"Enemy\")\n--- for _, enemy in ipairs(enemies) do\n--- print(\"Found enemy under level:\", enemy:getName())\n--- end\nfunction SceneObject.findByTag(self, tag, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided (and self is not the class)\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n tag, root = self, tag\n end\n \n __assertTrace(tag, \"tag is empty\")\n \n local objects = _internal.sendMessage('sceneobject.findByTag', { \n tag = tag, \n root = root and root._handle or nil \n })\n \n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n \n return objects\nend\n\n---Create a new object in the scenegraph. This is a generic function, for specialized versions, see the respective Sg<name> classes like SgBox.\n--- @param objectType string Type of object (e.g., SceneObject.Type.Sphere).\n--- @param options table The options to use. Common options are listed below. Additional type-specific options are documented in the specialized functions (e.g., SgBox.create, SgSphere.create).\n--- @param parent Object the parent object to add the new one to or nil to put it at root level\n--- @return Promise a promise resolving to a SceneObject\n--- @usage\n--- -- Options can be omitted. This example shows the defaults,\n--- -- only specify the ones you want different.\n--- SceneObject.create(SceneObject.Type.Sphere, {\n--- active = true,\n--- static = false,\n--- name = \"\",\n--- transform = Transform.new(),\n--- layers = {0},\n--- tags = {},\n--- receiveShadow = false,\n--- castShadow = false,\n--- -- Type-specific options (e.g., for Sphere):\n--- radius = 1,\n--- widthSegments = 8,\n--- heightSegments = 6\n--- }, nil):next(function(obj)\n--- obj:setActive(true)\n--- end)\nfunction SceneObject.create(self, objectType, options, parent)\n if (type(self) == \"string\") then\n objectType, options, parent = self, objectType, options\n end\n\n __assertTrace(type(objectType) == 'string', 'objectType must be a string')\n __assertTrace(parent == nil or (type(parent) == 'table' and parent._isSceneObject), 'parent must be a SceneObject')\n\n local p = _internal.sendMessage('sceneobject.create', {\n type = objectType,\n options = options,\n parent = parent and parent._handle or nil\n })\n return Promise:new(p)\nend\n\n--- Get the ID of the scene object.\n--- @return string The unique ID of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- print(\"Object ID:\", obj:getId())\nfunction SceneObject:getId()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self._nodeId\nend\n\n--- Get the name of the scene object.\n--- @return string The user-defined name of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- print(\"Object name:\", obj:getName())\nfunction SceneObject:getName()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getName', { obj=self._handle })\nend\n\n--- Return the display name of the scene object.\n--- In case the object has no user-given name, a name will be generated\n--- in the format \"type <id>\"\n--- @return string Displayname\n--- @usage\n--- local obj = SceneObject.findByName(\"Enemy\")\n--- print(\"Display name:\", obj:getDisplayName())\nfunction SceneObject:getDisplayName()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getDisplayName', { obj=self._handle })\nend\n\n--- Return the type of the object\n--- @return string Type\n--- @usage\n--- local obj = SceneObject.findByName(\"Wall\")\n--- print(\"Object type:\", obj:getType())\nfunction SceneObject:getType()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getType', { obj=self._handle })\nend\n\n--- Set a field of the scene object to a new value.\n---\n--- @param name string The name of the field.\n--- @param value any The new value to set.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- obj:set(\"Brightness\", \"High\")\nfunction SceneObject:set(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get a field value from the scene object.\n---\n--- @param name string The name of the field.\n--- @return string The value of the field.\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local intensity = obj:get(\"Intensity\")\n--- print(\"Intensity: \" .. intensity)\nfunction SceneObject:get(name)\n\n -- This is a replacement for the old get() function without parameters that would just return a node reference\n -- It needs to stay in place as long as there are still scripts that depend on it.\n if name == nil then\n return self\n end\n\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n local field = _internal.sendMessage('sceneobject.get', { obj=self._handle, name=name })\n if field == nil then\n return nil\n end\n return toType(field.value, field.type)\nend\n\n--- Set a field of the scene object to a new value.\n---@deprecated 25.11 Use ``set`` which is the new shorter version\n--- @param name string The name of the field.\n--- @param value any The new value to set.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- obj:setFieldValue(\"Brightness\", \"High\")\nfunction SceneObject:setFieldValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get a field value from the scene object.\n---@deprecated 25.11 Use ``get`` which is the new shorter version\n--- @param name string The name of the field.\n--- @return string The value of the field.\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local intensity = obj:getFieldValue(\"Intensity\")\n--- print(\"Intensity: \" .. intensity)\nfunction SceneObject:getFieldValue(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n local field = _internal.sendMessage('sceneobject.get', { obj=self._handle, name=name })\n if field == nil then\n return nil\n end\n return toType(field.value, field.type)\nend\n\n--- Animate a field from its current value to a new value.\n---\n--- @param name string The name of the field\n--- @param value any The new value to animate to.\n--- @param duration number The duration in milliseconds for the animation to take\n--- @param ease string The type of easing to use. Possible values: 'linear', 'easeInOutQuad'\n--- @return number animation id. Can be used to cancel animation using cancelAnimation(id)\nfunction SceneObject:animateFieldValue(name, value, duration, ease)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not (type(value) == 'table'), \"Cannot animate objects!\");\n name = capitalizeFirstLetter(name)\n return _internal.sendMessage('sceneobject.animateFieldValue', { obj=self._handle, name=name, value=value, duration=duration, ease=ease })\nend\n\n--- Cancel a running animation\n---\n--- @param name string The name of the field\nfunction SceneObject:cancelAnimation(id)\n _internal.sendMessage('sceneobject.cancelAnimation', { obj=self._handle, id=id })\nend\n\n--- Set a field of the scene object to a new color value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value Color The new color value.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- obj:setFieldColorValue(\"Color\", Color.new(1, 0, 0, 1)) -- Sets the object to red.\nfunction SceneObject:setFieldColorValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(value._isColor, \"value is not a Color\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value:toData() })\nend\n\n--- Set a field of the scene object to a new number value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value number The new number value.\nfunction SceneObject:setFieldNumberValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(value) == \"number\", \"value must be a number\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n---Set a field of the scene object to a new enum value\n--- @param name string\n--- @param value number\nfunction SceneObject:setFieldEnumValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n---Set a field of the scene object to a new boolean value\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string\n--- @param value boolean\nfunction SceneObject:setFieldBooleanValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if value then value = true else value = false end\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get the layers of the scene object\n--- @return table A table containing layer indices\nfunction SceneObject:getLayers()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local internal_layers = self:getFieldValue(\"Layers\")\n __assertTrace(type(internal_layers) == \"table\", \"Expected 'Layers' to be a table\")\n\n -- convert internal 0-based indices to frontend 1-based indices\n local layers = {}\n for _, layer_idx in ipairs(internal_layers) do\n table.insert(layers, layer_idx + 1)\n end\n\n return layers\nend\n\n--- Set the layers of the scene object.\n--- Layers control rendering visibility and interaction with other objects.\n---\n--- @param layers table A table containing layer indices.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- obj:setLayers({1, 3, 5}) -- Assigns the object to layers 1, 3, and 5.\nfunction SceneObject:setLayers(layers)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(layers) == \"table\", \"Layers must be provided as a table\")\n self:setFieldValue(\"Layers\", layers)\nend\n\n--- Get the value of a scene object's field as color value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return Color value of the field\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local color = obj:getFieldColorValue(\"Color\")\n--- print(\"Color: \" .. color)\nfunction SceneObject:getFieldColorValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as number value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return number value of the field\nfunction SceneObject:getFieldNumberValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as boolean value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return boolean value of the field\nfunction SceneObject:getFieldBooleanValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as string value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return string value of the field\nfunction SceneObject:getFieldEnumValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as string value\n--- @return string value of the field\nfunction SceneObject:getFieldEnumOptions(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getFieldOptions', { obj=self._handle, name=name })\nend\n\n--- Set a field of the scene object to a new boolean value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value boolean The new boolean value.\n--- @usage\n--- local obj = SceneObject.findByName(\"Door\")\n--- obj:setFieldBooleanValue(\"PhysicsEnabled\", true)\nfunction SceneObject:setFieldBooleanValue(name, value)\n self:setFieldValue(name, not not value)\nend\n\n---Get a link of the scene object to a new value\n---@deprecated 25.11 Use ``get`` which handles items now too\n--- @param name string name of the link field\n--- @return number handle of the linked item or nil if not set\nfunction SceneObject:getLinkItem(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n return handle.createObject(_internal.sendMessage('sceneobject.getLinkItem', { obj=self._handle, name=name }))\nend\n\n---Set a link from the scene object\n---@deprecated 25.11 Use ``set`` which handles items now too\n--- @param name string name of the link field\n--- @param item string value can be either an item ID or an Item object\nfunction SceneObject:setLinkItem(name, item)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not item or (item and item._handle and item._isItem), \"item has to be an object of Item class or nil!\");\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.setLinkItem', { obj=self._handle, name=name, item=item })\nend\n\n--- Set the position of the scene object.\n--- Sets the object's local position to the given coordinates.\n---\n--- @param x number Position along the X-axis.\n--- @param y number Position along the Y-axis.\n--- @param z number Position along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- obj:setPosition(0, 5, 0) -- Places the object at (0, 5, 0)\n--- end\nfunction SceneObject:setPosition(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n\n _internal.sendMessage('sceneobject.setPosition', {\n obj = self._handle,\n x = x,\n y = y,\n z = z\n })\nend\n\n--- Translate (move) the scene object by an offset.\n--- Adds the given offset to the object's current local position.\n---\n--- @param x number Offset along the X-axis.\n--- @param y number Offset along the Y-axis.\n--- @param z number Offset along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- obj:translate(0, 1, 0) -- Moves the object up by 1 unit from its current position\n--- end\nfunction SceneObject:translate(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.translate', {\n obj = self._handle,\n x = x,\n y = y,\n z = z\n })\nend\n\n--- Rotate the scene object.\n--- Rotation is applied in degrees and follows Euler angles (XYZ order).\n---\n--- @param x number Rotation around the X-axis in degrees.\n--- @param y number Rotation around the Y-axis in degrees.\n--- @param z number Rotation around the Z-axis in degrees.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- if obj then\n--- obj:rotate(45, 0, 0) -- Rotates 45 degrees around the X-axis\n--- end\nfunction SceneObject:rotate(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.rotate', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Scale the scene object.\n---\n--- @param x number Scale factor along the X-axis.\n--- @param y number Scale factor along the Y-axis.\n--- @param z number Scale factor along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Tree\")\n--- if obj then\n--- obj:scale(2, 2, 2) -- Doubles the size of the object\n--- end\nfunction SceneObject:scale(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.scale', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Get the local transform of a scene object.\n--- The transform contains position, rotation, and scale relative to the object's parent.\n---\n--- @param cacheLocal boolean if set to true, the transform will be cached locally for the future for performance reasons. This should only be used if no other script changes the transform\n--- @return Transform The object's local transform.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- local transform = obj:getTransform()\n--- print(\"Local position:\", transform.position)\nfunction SceneObject:getTransform(cacheLocal)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if self._transform == nil or not cacheLocal then\n self._transform = Transform.fromData(_internal.sendMessage('sceneobject.getTransform', { obj=self._handle }))\n end\n return self._transform:clone()\nend\n\n--- Apply a function to modify the object's transform, then update it.\n--- This allows making changes to the transform while ensuring they are properly applied back.\n---\n--- @param func function A function that takes a Transform object as its only argument.\n--- @param cacheLocal boolean if set to true, the transform will be cached locally for the future for performance reasons. This should only be used if no other script changes the transform\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- obj:withTransform(function(transform)\n--- transform.position.x = transform.position.x + 1 -- Move object 1 unit to the right\n--- end)\nfunction SceneObject:withTransform(func, cacheLocal)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(func) == 'function', \"argument func is not a function\")\n local transform = self:getTransform(cacheLocal)\n func(transform)\n self:setTransform(transform)\nend\n\n--- Get the world transform of a scene object.\n--- The world transform includes absolute position, rotation, and scale in the scene.\n---\n--- @return Transform The object's world transform.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- local worldTransform = obj:getWorldTransform()\n--- print(\"World position:\", worldTransform.position)\nfunction SceneObject:getWorldTransform()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Transform.fromData(_internal.sendMessage('sceneobject.getWorldTransform', { obj=self._handle }))\nend\n\n--- Get the forward direction vector in world space.\n--- Returns the local -Z axis transformed by the object's rotation.\n---\n--- @return Vector3 The forward direction in world space.\nfunction SceneObject:getForward()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getForward', { obj=self._handle }))\nend\n\n--- Get the right direction vector in world space.\n--- Returns the local +X axis transformed by the object's rotation.\n---\n--- @return Vector3 The right direction in world space.\nfunction SceneObject:getRight()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getRight', { obj=self._handle }))\nend\n\n--- Get the up direction vector in world space.\n--- Returns the local +Y axis transformed by the object's rotation.\n---\n--- @return Vector3 The up direction in world space.\nfunction SceneObject:getUp()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getUp', { obj=self._handle }))\nend\n\n--- Set the local transform of a scene object.\n--- Updates the object's position, rotation, and scale relative to its parent.\n---\n--- @param transform Transform The new local transform to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Door\")\n--- local newTransform = Transform.new()\n--- newTransform.position = Vector3.new(0, 2, 0) -- Move the object up 2 units\n--- obj:setTransform(newTransform)\nfunction SceneObject:setTransform(transform)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(transform and transform._isTransform, \"transform has to be an object of type Transform\")\n self._transform = transform\n _internal.sendMessage('sceneobject.setTransform', { obj=self._handle, transform=self._transform:toData() })\nend\n\n--- Set the physics position of the scene object.\n---\n--- This directly updates the position of the object inside the physics engine.\n--- The position is expressed in world coordinates.\n--- Only works for objects added to the physics engine\n--- @param position Vector3 New world position of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:setPhysicsPosition(Vector3.new(0, 5, 0))\nfunction SceneObject:setPhysicsPosition(position)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(position and position._isVector3, \"position has to be an object of type Vector3\")\n _internal.sendMessage('sceneobject.setPhysicsPosition', { obj=self._handle, position=position:toData() })\nend\n\n--- Set the physics rotation of the scene object.\n---\n--- This directly updates the rotation of the object inside the physics engine.\n--- The rotation is expressed in world space.\n--- Only works for objects added to the physics engine\n--- @param rotation Quaternion New world rotation of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:setPhysicsRotation(Quaternion.fromEuler(0, math.pi, 0))\nfunction SceneObject:setPhysicsRotation(rotation)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(rotation and rotation._isQuaternion, \"rotation has to be an object of type Quaternion\")\n _internal.sendMessage('sceneobject.setPhysicsRotation', { obj=self._handle, rotation=rotation:toData() })\nend\n\nfunction SceneObject:setPhysicsRotation(rotation)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(rotation and rotation._isQuaternion, \"rotation has to be an object of type Quaternion\")\n _internal.sendMessage('sceneobject.setPhysicsRotation', { obj=self._handle, rotation=rotation:toData() })\nend\n\n--- Get the linear velocity of the scene object.\n--- This function returns the object's velocity in world coordinates.\n--- Only works for objects added to the physics engine with `watchLinearVelocity` enabled.\n---\n--- @return Vector3 The linear velocity of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Ball\")\n--- local velocity = obj:getLinearVelocity()\n--- print(\"Velocity:\", velocity)\nfunction SceneObject:getLinearVelocity()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getLinearVelocity', { obj=self._handle }))\nend\n\n--- Set the linear velocity of the scene object.\n--- This function assigns a new velocity to the object, but only works if the object is in the physics engine.\n---\n--- @param velocity Vector3 The new linear velocity to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Car\")\n--- obj:setLinearVelocity(Vector3.new(10, 0, 0)) -- Move object at speed 10 in the X direction.\nfunction SceneObject:setLinearVelocity(velocity)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(velocity and velocity._isVector3, \"velocity must be an object of type Vector3\")\n local data = velocity:toData()\n _internal.sendMessage('sceneobject.setLinearVelocity', { obj=self._handle, velocity=data })\nend\n\n--- Get the angular velocity of the scene object.\n--- Only works for objects added to the physics engine with `watchAngularVelocity` enabled.\n---\n--- @return Vector3 The angular velocity of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Wheel\")\n--- local angVel = obj:getAngularVelocity()\n--- print(\"Angular velocity:\", angVel)\nfunction SceneObject:getAngularVelocity()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getAngularVelocity', { obj=self._handle }))\nend\n\n--- Set the angular velocity of the scene object.\n---\n--- @param velocity Vector3 The new angular velocity to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Propeller\")\n--- obj:setAngularVelocity(Vector3.new(0, 10, 0)) -- Rotate around Y-axis.\nfunction SceneObject:setAngularVelocity(velocity)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(velocity and velocity._isVector3, \"velocity must be an object of type Vector3\")\n local data = velocity:toData()\n _internal.sendMessage('sceneobject.setAngularVelocity', { obj=self._handle, velocity=data })\nend\n\n--- Apply an impulse to the scene object.\n---\n--- An **impulse** is a sudden force applied to an object that immediately\n--- changes its velocity. This is different from a continuous force, which\n--- is applied over time.\n---\n--- This function only works on objects that are physics-enabled.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @param relPos Vector3 The relative position where the impulse is applied. Default is (0, 0, 0)\nfunction SceneObject:applyImpulse(impulse, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n local impulseData = impulse:toData()\n local relPosData = relPos:toData()\n _internal.sendMessage('sceneobject.applyImpulse', { obj=self._handle, impulse=impulseData, relPos=relPosData })\nend\n\n--- Apply an impulse to the scene object that respects constraints.\n---\n--- Only works on physics-enabled objects.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @param relPos Vector3 The relative position where the impulse is applied. Default is (0, 0, 0)\nfunction SceneObject:applyPushImpulse(impulse, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyPushImpulse', { obj=self._handle, impulse=impulse:toData(), relPos=relPos:toData() })\nend\n\n--- Apply an impulse to the center of the object.\n---\n--- Applies a sudden velocity change at the object's center. Only works on physics-enabled objects.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:applyCentralImpulse(Vector3.new(0, 5, 0)) -- Apply upward impulse.\nfunction SceneObject:applyCentralImpulse(impulse)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyCentralImpulse', { obj=self._handle, impulse=impulse:toData() })\nend\n\n--- Apply a central impulse that respects constraints.\n---\n--- Only works on physics-enabled objects and respects any joint or constraint applied.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:applyCentralPushImpulse(Vector3.new(0, 5, 0)) -- Apply upward impulse.\nfunction SceneObject:applyCentralPushImpulse(impulse)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyCentralPushImpulse', { obj=self._handle, impulse=impulse:toData() })\nend\n\n--- Apply a force to the scene object.\n---\n--- Unlike impulses, **forces** are applied over time, influencing the object's acceleration.\n--- Only works on physics-enabled objects.\n---\n--- @param force Vector3 The force vector to apply.\n--- @param relPos Vector3 The relative position where the force is applied. Default is (0, 0, 0)\nfunction SceneObject:applyForce(force, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(force and force._isVector3, \"force must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyForce', { obj=self._handle, force=force:toData(), relPos=relPos:toData() })\nend\n\n--- Clear any forces on a scene object.\n--- Only works on physics-enabled objects.\n--- @usage\n--- local obj = SceneObject.findByName(\"Ball\")\n--- obj:clearForces()\nfunction SceneObject:clearForces()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.clearForces', { obj=self._handle })\nend\n\n--- Delete the scene object from the scene.\n---\n--- @usage\n--- local obj = scene:findByName(\"TemporaryObject\")\n--- if obj then\n--- obj:delete()\n--- end\nfunction SceneObject:delete()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.delete', { obj=self._handle })\n if self._handle then\n handle.invalidate(self._handle)\n end\n self._handle = nil\nend\n\n--- Clone a scene object to a new position and return it.\n---\n--- The position can be provided as:\n--- 1. Separate `x, y, z` coordinates.\n--- 2. A `Vector3` containing the position.\n---\n--- If no position is provided, the clone will appear in the same location as the original.\n---\n--- @param x number | Vector3 The X-coordinate of the new position, or a `Vector3` containing `x, y, z`. If a `Vector3` is passed, `y` and `z` should be omitted.\n--- @param y number (optional) The Y-coordinate of the new position. Omit this if passing a `Vector3`.\n--- @param z number (optional) The Z-coordinate of the new position. Omit this if passing a `Vector3`.\n--- @param parent SceneObject (optional) The parent object to attach the new object to.\n--- @return Promise A promise resolving to a SceneObject.\n---\n--- @usage\n--- local obj = scene:findByName(\"Tree\")\n--- obj:clone(5, 0, 2):next(function(clone)\n--- clone:setActive(true) -- Activate the cloned object\n--- end)\n---\n--- local pos = Vector3.new(3, 1, 0)\n--- obj:clone(pos):next(function(clone)\n--- clone:setFieldValue(\"Name\", \"ClonedTree\") -- Rename the cloned object\n--- end)\n---\n--- local parentObj = scene:findByName(\"Group\")\n--- obj:clone(pos, parentObj):next(function(clone)\n--- clone:setFieldValue(\"Name\", \"AnotherClonedTree\") -- Rename the cloned object\n--- end)\nfunction SceneObject:clone(x, y, z, parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n\n -- Handle case where first argument is a Vector3 and second argument is the parent\n if type(x) == \"table\" and x._isVector3 then\n x, y, z, parent = x.x, x.y, x.z, y -- Shift arguments\n end\n\n local p = _internal.sendMessage('sceneobject.clone', {\n obj = self._handle,\n x = x,\n y = y,\n z = z,\n parent = parent and parent._handle or nil\n })\n\n return Promise:new(p)\nend\n\n--- Create a new object of same type from this one. If no options are specified this will be basically a clone.\n--- If an options table is passed, this will specify all fields with their values that should be changed.\n--- At the very least, this should contain a new Transform, otherwise the new object will be at the exact location\n--- of the old one.\n--- @param options table the table of fields to set on the new object\n--- @param parent SceneObject (optional) The parent object to attach the new object to.\n--- @return Promise A promise resolving to a SceneObject.\n---\n--- @usage\n--- local obj = SceneObject.findByName(\"Tree\")\n--- local parentObj = SceneObject.findByName(\"Group\")\n--- local clone = await(obj:createFromThis({\n--- transform = Transform.new(Vector3.new(4, 6, 8)),\n--- active = true,\n--- name = \"Cloned tree\"\n--- }, parentObj)\nfunction SceneObject:createFromThis(options, parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(parent == nil or parent._isSceneObject, \"parent is not nil or a SceneObject\")\n\n local p = _internal.sendMessage('sceneobject.createFromThis', {\n obj = self._handle,\n options = options,\n parent = parent and parent._handle or nil\n })\n\n return Promise:new(p)\nend\n\n--- Move the scene object to a different parent. This parent can not be a child of the scene object\n--- @param parent SceneObject (optional) The parent object to attach the new object to. if nil, the object will be moved to the scenegraph root\nfunction SceneObject:moveToParent(parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not parent or parent._isSceneObject, \"parent is not a SceneObject\")\n _internal.sendMessage('sceneobject.moveToParent', { obj = self._handle, parent = parent and parent._handle or nil })\nend\n\n--- Set the active state of the scene object.\n--- @param value boolean True to activate, false to deactivate.\nfunction SceneObject:setActive(value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n self:setFieldValue(\"Active\", value)\nend\n\n--- Get the active state of the scene object.\n--- @return boolean True if the object is active, false otherwise.\nfunction SceneObject:getActive()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getFieldBooleanValue(\"Active\")\nend\n\n--- Check if the object has a specific tag.\n--- @param tagName string The tag name to check.\n--- @return boolean True if the object has the tag, false otherwise.\nfunction SceneObject:hasTag(tagName)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local tags = self:getFieldValue(\"Tags\")\n for _, value in pairs(tags) do\n if value == tagName then\n return true\n end\n end\n return false\nend\n\n--- Get a list of all tags assigned to the object.\n--- @return table An array of tag names.\nfunction SceneObject:getTags()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getFieldValue(\"Tags\")\nend\n\n--- Assign a set of tags to the object.\n--- @param tags table An array of tag names.\nfunction SceneObject:setTags(tags)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n Tools.validateArrayOfStrings(tags)\n self:setFieldValue(\"Tags\", tags)\nend\n\n--- Add a tag to the object.\n--- @param tag string The tag to add.\nfunction SceneObject:addTag(tag)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(tag) == 'string', \"tag needs to be a string\")\n local tags = self:getTags()\n table.insert(tags, tag)\n self:setTags(tags)\nend\n\n--- Remove all tags from the object.\nfunction SceneObject:clearTags()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n self:setTags({})\nend\n\n--- Take a snapshot of the object and its children, if specified.\n--- This allows restoring the object to a previous state.\n--- @param recursive boolean Whether to include child objects in the snapshot.\n--- @return number The snapshot handle.\nfunction SceneObject:takeSnapshot(recursive)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.takeSnapshot', { obj=self._handle, recursive=recursive })\nend\n\n--- Restore a snapshot, reverting any changes made after the snapshot was taken.\n--- @param snapshot number The snapshot handle to restore.\nfunction SceneObject:rollbackSnapshot(snapshot)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.rollbackSnapshot', { obj=self._handle, snapshot=snapshot })\nend\n\n--- Check if the object is enabled.\n--- @return boolean True if the object is enabled, false otherwise.\nfunction SceneObject:isEnabled()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.isEnabled', { obj=self._handle })\nend\n\n--- Enable the object.\nfunction SceneObject:enable()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.enable', { obj=self._handle })\nend\n\n--- Disable the object.\nfunction SceneObject:disable()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.disable', { obj=self._handle })\nend\n\n--- Get the material assigned to the object.\n--- @return Material The material, or nil if none is assigned.\nfunction SceneObject:getMaterial()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getLinkItem(\"Material\")\nend\n\n--- Set the object's material.\n--- @param mat Material The material to set or nil to clear the material\nfunction SceneObject:setMaterial(mat)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(mat == nil or (type(mat) == 'table' and mat._isMaterial), \"self is not a SceneObject\")\n self:setLinkItem(\"Material\", mat)\nend\n\n--- Get all behaviours attached to this object.\n---@deprecated 25.12 use getBehaviours.\n--- @return table A list of Behaviour objects.\nfunction SceneObject:getEntities()\n return self:getBehaviours()\nend\n\n--- Get all behaviours attached to this object.\n--- @return table A list of Behaviour objects.\nfunction SceneObject:getBehaviours()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if not self._nodeId then\n error(\"This scene object does not have a node Id. Cannot get its behaviours.\")\n end\n return Behaviour.getByNodeId(self._nodeId)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:getBehaviourByScriptName(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if not self._nodeId then\n error(\"This scene object does not have a node Id. Cannot get its entities.\")\n end\n return Behaviour.getByNodeIdAndScriptName(self._nodeId, name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n---@deprecated\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:getEntityByScriptName(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:behaviour(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:entity(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get the parent of the object.\n--- @return SceneObject The parent object, or nil if none exists.\nfunction SceneObject:getParent()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return handle.createObject(_internal.sendMessage('sceneobject.getParent', { obj=self._handle }))\nend\n\n--- Get the children of the object.\n--- @return table A table of child objects.\nfunction SceneObject:getChildren()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local objects = _internal.sendMessage('sceneobject.getChildren', { obj=self._handle })\n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n return objects\nend\n\n--- Enable or disable automatic recreation of the scene object between play and stop.\n---\n--- When `value` is true, the object will be automatically recreated every time\n--- the scene starts or stops. This is useful for objects that need to reset\n--- physics, state, or procedural components at each run.\n---\n--- @param value boolean Enable (`true`) or disable (`false`) automatic recreation.\nfunction SceneObject:setAutoRecreate(value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.setAutoRecreate', { obj=self._handle, value=value })\nend\n\n--- Immediately recreate the scene object.\n---\n--- This forces the object to reset its state, physics, and components as if\n--- it was newly instantiated in the scene.\nfunction SceneObject:recreate()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.recreate', { obj=self._handle })\nend\n\n--- Return the bounds of the scene object\n---@return Bounds the bounds of the object\nfunction SceneObject:getBounds()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local bounds = _internal.sendMessage('sceneobject.getBounds', { obj=self._handle })\n local center = Vector3.new(bounds.center.x, bounds.center.y, bounds.center.z)\n local extents = Vector3.new(bounds.extents.x, bounds.extents.y, bounds.extents.z)\n return Bounds.new(center, extents)\nend\n\nreturn SceneObject\n";
|
|
131583
131581
|
|
|
131584
131582
|
var lua_api_sceneobjects_audio = "\nlocal _internal = require('engine/_internal')\nlocal SceneObject = require('engine/sceneobject')\n\n------------------------------------------------------------------\n-- SgAudio class\n------------------------------------------------------------------\n\n--- @class SgAudio\nlocal SgAudio = Class.new(SceneObject)\n\n--- @private\nfunction SgAudio:__new(handle, nodeId)\n SceneObject.__new(self, handle, nodeId)\n self._type = \"SgAudio\"\n self._isSgAudio = true\nend\n\n--- Create a new audio object in the scenegraph.\n---@param options table A table of options to fill the parameters\n---@param parent SceneObject The parent scene object to add this to\n---@return Promise a promise which will resolve to the created object\n---@usage\n--- -- Options can be omitted. This example shows the defaults,\n--- -- only specify the ones you want different.\n--- SgAudio.create({\n--- active = true,\n--- name = \"\",\n--- transform = Transform.new(),\n--- layers = {0},\n--- tags = {},\n--- autoplay = false,\n--- loop = false,\n--- volume = 1,\n--- panning = 0,\n--- playbackRate = 1,\n--- offset = 0\n--- })\nfunction SgAudio.create(self, options, parent)\n -- check if static function was called as method. if so, shift the parameters\n if not (type(self) == 'table' and type(self.new) == 'function') then\n parent, options = options, self\n end\n return SceneObject.create(\"SgAudio\", options, parent)\nend\n\n--- playback the audio\n---@param force boolean Force the sound to replay even if it is already playing\nfunction SgAudio:play(force)\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n _internal.sendMessage('sgaudio.play', { obj=self._handle, force=force })\nend\n\n--- stop the audio\nfunction SgAudio:stop()\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n _internal.sendMessage('sgaudio.stop', { obj=self._handle })\nend\n\n--- Checks if the audio is playing\n---@return boolean true, if sound is playing, false otherwise\nfunction SgAudio:isPlaying()\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n return _internal.sendMessage('sgaudio.isPlaying', { obj=self._handle })\nend\n\nfunction SgAudio:setPlaybackRate(value)\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n self:setFieldNumberValue(\"PlaybackRate\", value)\nend\n\nfunction SgAudio:getPlaybackRate()\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n return self:getFieldNumberValue(\"PlaybackRate\");\nend\n\nfunction SgAudio:getAudioCollectionEntry()\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n return self:getFieldValue(\"AudioCollectionEntry\")\nend\n\nfunction SgAudio:setAudioCollectionEntry(name)\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n self:setFieldValue(\"AudioCollectionEntry\", name)\nend\n\nreturn SgAudio\n";
|
|
131585
131583
|
|
|
@@ -134278,6 +134276,22 @@ class RtSceneObject extends RtBase {
|
|
|
134278
134276
|
obj.recreate(true);
|
|
134279
134277
|
}
|
|
134280
134278
|
}
|
|
134279
|
+
getBounds(params) {
|
|
134280
|
+
const obj = this.runtime.getObject(params.obj);
|
|
134281
|
+
if (!obj)
|
|
134282
|
+
return null;
|
|
134283
|
+
if (obj instanceof SgItem) {
|
|
134284
|
+
const bbox = obj.getBoundingBox();
|
|
134285
|
+
const center = new Vector3();
|
|
134286
|
+
const size = new Vector3();
|
|
134287
|
+
bbox.getCenter(center);
|
|
134288
|
+
bbox.getSize(size);
|
|
134289
|
+
return {
|
|
134290
|
+
center: center,
|
|
134291
|
+
extents: size.multiplyScalar(0.5)
|
|
134292
|
+
};
|
|
134293
|
+
}
|
|
134294
|
+
}
|
|
134281
134295
|
}
|
|
134282
134296
|
|
|
134283
134297
|
// -----------------------------------------------------------------------------------------------------------
|
|
@@ -136281,6 +136295,7 @@ class ScriptEngine {
|
|
|
136281
136295
|
"engine/math/transform": lua_api_math_transform,
|
|
136282
136296
|
"engine/math/matrix": lua_api_math_matrix,
|
|
136283
136297
|
"engine/math/complex": lua_api_math_complex,
|
|
136298
|
+
"engine/math/bounds": lua_api_math_bounds,
|
|
136284
136299
|
"engine/msgpack": lua_api_msgpack,
|
|
136285
136300
|
"engine/object": lua_api_object,
|
|
136286
136301
|
"engine/physics": lua_api_physics,
|