| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028 |
- #include "VectorAdapter.h"
- #include "../ScriptEngine.h"
- #include "WallpaperEngine/Data/Utils/SFINAE.h"
- #include "WallpaperEngine/Data/Utils/ScopeGuard.h"
- #include <variant>
- using namespace WallpaperEngine::Data::Utils;
- using namespace WallpaperEngine::Data::Model;
- using namespace WallpaperEngine::Scripting::Adapters;
- static uint32_t VectorInstanceId = 0;
- static uint32_t VectorAdapterInstanceId = 0;
- static constexpr int InvalidVectorInstanceId = 0;
- // magic value used to ensure the assigned opaque value we got back is valid
- #define VEC_OPAQUE_MAGIC 0xdeadbee0
- #define VEC_MAGIC_CHECK_EXCEPTION(container, components) \
- do { \
- if (!container || container->magic != (int)(VEC_OPAQUE_MAGIC + components)) { \
- return JS_ThrowTypeError (ctx, "invalid or mismatched vector%d instance", components); \
- } \
- } while (0)
- #define VEC_MAGIC_CHECK_ERROR(container, components) \
- do { \
- if (!container || container->magic != (int)(VEC_OPAQUE_MAGIC + components)) { \
- return -1; \
- } \
- } while (0)
- template <int components> std::map<uint32_t, VectorAdapter<components>&> vectorAdapterInstances;
- template <int components> struct VectorOpaqueContainer {
- int magic;
- VectorAdapter<components>& adapter;
- DynamicValue& value;
- uint32_t id;
- };
- template <int components> auto vector_new () -> decltype (auto) {
- static_assert (components >= 2 && components <= 4, "Unsupported vector type");
- if constexpr (components == 2) {
- return glm::vec2 {};
- } else if constexpr (components == 3) {
- return glm::vec3 {};
- } else if constexpr (components == 4) {
- return glm::vec4 {};
- }
- }
- template auto vector_new<2> () -> decltype (auto);
- template auto vector_new<3> () -> decltype (auto);
- template auto vector_new<4> () -> decltype (auto);
- template <int components> auto vector_new (float value) -> decltype (auto) {
- static_assert (components >= 2 && components <= 4, "Unsupported vector type");
- if constexpr (components == 2) {
- return glm::vec2 (value);
- } else if constexpr (components == 3) {
- return glm::vec3 (value);
- } else if constexpr (components == 4) {
- return glm::vec4 (value);
- }
- }
- template auto vector_new<2> (float value) -> decltype (auto);
- template auto vector_new<3> (float value) -> decltype (auto);
- template auto vector_new<4> (float value) -> decltype (auto);
- template <int components> auto vector_get (DynamicValue& value) -> decltype (auto) {
- static_assert (components >= 2 && components <= 4, "Unsupported vector type");
- if constexpr (components == 2) {
- return value.getVec2 ();
- } else if constexpr (components == 3) {
- return value.getVec3 ();
- } else if constexpr (components == 4) {
- return value.getVec4 ();
- }
- }
- template <int components> auto vector_get (JSContext* ctx, JSValue source) -> decltype (auto) {
- static_assert (components >= 2 && components <= 4, "Unsupported vector type");
- int tag = JS_VALUE_GET_TAG (source);
- if (tag == JS_TAG_INT) {
- int32_t value = 0;
- JS_ToInt32 (ctx, &value, source);
- return vector_new<components> (value);
- }
- if (JS_TAG_IS_FLOAT64 (tag)) {
- double value = 0.0f;
- JS_ToFloat64 (ctx, &value, source);
- return vector_new<components> (static_cast<float> (value));
- }
- if (tag == JS_TAG_OBJECT) {
- JSValue x = JS_GetPropertyStr (ctx, source, "x");
- JSValue y = JS_GetPropertyStr (ctx, source, "y");
- JSValue z = JS_GetPropertyStr (ctx, source, "z");
- JSValue w = JS_GetPropertyStr (ctx, source, "w");
- if (!JS_IsNumber (x) || !JS_IsNumber (y)) {
- throw std::runtime_error ("Unsupported type conversion for VectorAdapter");
- }
- // a wider vector handed to a narrower target just loses its extra components
- double xVal = 0.0f, yVal = 0.0f, zVal = 0.0f, wVal = 0.0f;
- JS_ToFloat64 (ctx, &xVal, x);
- JS_ToFloat64 (ctx, &yVal, y);
- if (JS_IsNumber (z)) {
- JS_ToFloat64 (ctx, &zVal, z);
- }
- if (JS_IsNumber (w)) {
- JS_ToFloat64 (ctx, &wVal, w);
- }
- if constexpr (components == 2) {
- return glm::vec2 (xVal, yVal);
- } else if constexpr (components == 3) {
- return glm::vec3 (xVal, yVal, zVal);
- } else if constexpr (components == 4) {
- return glm::vec4 (xVal, yVal, zVal, wVal);
- }
- }
- throw std::runtime_error ("Unsupported type conversion for VectorAdapter");
- }
- template auto vector_get<2> (JSContext* ctx, JSValue source) -> decltype (auto);
- template auto vector_get<3> (JSContext* ctx, JSValue source) -> decltype (auto);
- template auto vector_get<4> (JSContext* ctx, JSValue source) -> decltype (auto);
- template auto vector_get<2> (DynamicValue& value) -> decltype (auto);
- template auto vector_get<3> (DynamicValue& value) -> decltype (auto);
- template auto vector_get<4> (DynamicValue& value) -> decltype (auto);
- template <int components>
- JSValue vector_property_get (JSContext* ctx, JSValueConst obj_val, JSAtom atom, JSValueConst receiver) {
- JSClassID classId = 0;
- auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (obj_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- // Exotic get_property intercepts *every* lookup on this class, bypassing the prototype
- // chain - so methods (multiply, add, dot, ...), which live only on the prototype, must be
- // looked up manually below when the name isn't x/y/z/w.
- const char* name = JS_AtomToCString (ctx, atom);
- if (name != nullptr) {
- ScopeGuard guard ([=] { JS_FreeCString (ctx, name); });
- const auto value = vector_get<components> (container->value);
- if (strcmp (name, "x") == 0) {
- return JS_NewFloat64 (ctx, value.x);
- }
- if (strcmp (name, "y") == 0) {
- return JS_NewFloat64 (ctx, value.y);
- }
- if constexpr (components >= 3) {
- if (strcmp (name, "z") == 0) {
- return JS_NewFloat64 (ctx, value.z);
- }
- if constexpr (components >= 4) {
- if (strcmp (name, "w") == 0) {
- return JS_NewFloat64 (ctx, value.w);
- }
- }
- }
- }
- JSValue proto = JS_GetClassProto (ctx, classId);
- JSValue result = JS_GetProperty (ctx, proto, atom);
- JS_FreeValue (ctx, proto);
- return result;
- }
- template JSValue vector_property_get<2> (JSContext* ctx, JSValueConst obj_val, JSAtom atom, JSValueConst receiver);
- template JSValue vector_property_get<3> (JSContext* ctx, JSValueConst obj_val, JSAtom atom, JSValueConst receiver);
- template JSValue vector_property_get<4> (JSContext* ctx, JSValueConst obj_val, JSAtom atom, JSValueConst receiver);
- template <int components>
- int vector_property_set (
- JSContext* ctx, JSValueConst obj_val, JSAtom atom, JSValueConst val, JSValueConst receiver, int flags
- ) {
- JSClassID classId = 0;
- auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (obj_val, &classId));
- VEC_MAGIC_CHECK_ERROR (container, components);
- int tag = JS_VALUE_GET_TAG (val);
- // reading an undeclared property assigns undefined, keep the component as it was instead of throwing
- if (tag == JS_TAG_UNDEFINED || tag == JS_TAG_NULL) {
- return 0;
- }
- if (tag != JS_TAG_INT && !JS_TAG_IS_FLOAT64 (tag)) {
- return -1;
- }
- const char* name = JS_AtomToCString (ctx, atom);
- if (name == nullptr) {
- return -1;
- }
- ScopeGuard guard ([=] { JS_FreeCString (ctx, name); });
- auto vec = vector_get<components> (container->value);
- void* into = nullptr;
- if (strcmp (name, "x") == 0) {
- if constexpr (
- std::is_same_v<decltype (vec), glm::vec2> || std::is_same_v<decltype (vec), glm::vec3>
- || std::is_same_v<decltype (vec), glm::vec4>
- ) {
- into = &vec.x;
- } else if constexpr (std::is_same_v<decltype (vec), Color>) {
- into = &vec.r;
- }
- } else if (strcmp (name, "y") == 0) {
- if constexpr (
- std::is_same_v<decltype (vec), glm::vec2> || std::is_same_v<decltype (vec), glm::vec3>
- || std::is_same_v<decltype (vec), glm::vec4>
- ) {
- into = &vec.y;
- } else if constexpr (std::is_same_v<decltype (vec), Color>) {
- into = &vec.g;
- }
- } else if constexpr (components >= 3) {
- if (strcmp (name, "z") == 0) {
- if constexpr (std::is_same_v<decltype (vec), glm::vec3> || std::is_same_v<decltype (vec), glm::vec4>) {
- into = &vec.z;
- } else if constexpr (std::is_same_v<decltype (vec), Color>) {
- into = &vec.b;
- }
- } else if constexpr (components >= 4) {
- if (strcmp (name, "w") == 0) {
- if constexpr (std::is_same_v<decltype (vec), glm::vec4>) {
- into = &vec.w;
- } else if constexpr (std::is_same_v<decltype (vec), Color>) {
- into = &vec.a;
- }
- }
- }
- }
- if (into == nullptr) {
- return -1;
- }
- double value = 0;
- JS_ToFloat64 (ctx, &value, val);
- *static_cast<float*> (into) = static_cast<float> (value);
- container->value.update (vec, DynamicValue::UpdateSource::Script);
- return 0;
- }
- template int vector_property_set<2> (
- JSContext* ctx, JSValueConst obj_val, JSAtom atom, JSValueConst val, JSValueConst receiver, int flags
- );
- template int vector_property_set<3> (
- JSContext* ctx, JSValueConst obj_val, JSAtom atom, JSValueConst val, JSValueConst receiver, int flags
- );
- template int vector_property_set<4> (
- JSContext* ctx, JSValueConst obj_val, JSAtom atom, JSValueConst val, JSValueConst receiver, int flags
- );
- template <int components> JSValue vector_copy (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- JSClassID classId = 0;
- auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- return container->adapter.instantiate (container->value, true);
- }
- template JSValue vector_copy<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_copy<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_copy<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- bool vector_value_equals (JSContext* ctx, JSValue left, JSValue right) {
- if (JS_TAG_IS_FLOAT64 (JS_VALUE_GET_TAG (left)) || JS_TAG_IS_FLOAT64 (JS_VALUE_GET_TAG (right))) {
- double x1Val = 0.0f, x2Val = 0.0f;
- JS_ToFloat64 (ctx, &x1Val, left);
- JS_ToFloat64 (ctx, &x2Val, right);
- if (std::abs (x1Val - x2Val) > 0.00001) {
- return false;
- }
- } else {
- if (!JS_IsEqual (ctx, left, right)) {
- return false;
- }
- }
- return true;
- }
- template <int components> JSValue vector_equals (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- if (argc == 0) {
- return JS_FALSE;
- }
- JSClassID classId = 0;
- auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue other = argv[0];
- auto* otherContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (other, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (otherContainer, components);
- const auto vector = vector_get<components> (container->value);
- const auto otherVector = vector_get<components> (otherContainer->value);
- if constexpr (components == 2) {
- return vector.x == otherVector.x && vector.y == otherVector.y ? JS_TRUE : JS_FALSE;
- } else if constexpr (components == 3) {
- return vector.x == otherVector.x && vector.y == otherVector.y && vector.z == otherVector.z ? JS_TRUE : JS_FALSE;
- } else if constexpr (components == 4) {
- return vector.x == otherVector.x && vector.y == otherVector.y && vector.z == otherVector.z
- && vector.w == otherVector.w
- ? JS_TRUE
- : JS_FALSE;
- }
- return JS_FALSE;
- }
- template JSValue vector_equals<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_equals<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_equals<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_length (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- JSClassID classId = 0;
- auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- return JS_NewFloat64 (ctx, glm::length (vector_get<components> (container->value)));
- }
- template JSValue vector_length<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_length<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_length<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components>
- JSValue vector_constructor (JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv, int magic) {
- auto it = vectorAdapterInstances<components>.find (magic);
- if (it == vectorAdapterInstances<components>.end ()) {
- return JS_ThrowTypeError (ctx, "invalid vector%d constructor instance", components);
- }
- JSValue result = it->second.instantiate ();
- JSClassID classId = 0;
- auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (result, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- // `new Vec3()` with no args is valid and expected to default to a zero vector - already
- // zero-initialized above, so nothing further to do here.
- if (argc > 1) {
- // new Vec3(x, y, z): one number per component, anything not given stays 0
- double parts[4] = { 0.0, 0.0, 0.0, 0.0 };
- for (int i = 0; i < argc && i < components; i++) {
- JS_ToFloat64 (ctx, &parts[i], argv[i]);
- }
- if constexpr (components == 2) {
- container->value.update (glm::vec2 (parts[0], parts[1]), DynamicValue::UpdateSource::Initialization);
- } else if constexpr (components == 3) {
- container->value.update (glm::vec3 (parts[0], parts[1], parts[2]), DynamicValue::UpdateSource::Initialization);
- } else {
- container->value.update (
- glm::vec4 (parts[0], parts[1], parts[2], parts[3]), DynamicValue::UpdateSource::Initialization
- );
- }
- } else if (argc > 0) {
- container->value.update (vector_get<components> (ctx, argv[0]), DynamicValue::UpdateSource::Initialization);
- }
- return result;
- }
- template JSValue vector_constructor<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic);
- template JSValue vector_constructor<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic);
- template JSValue vector_constructor<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic);
- template <int components> void vector_finalizer (JSRuntime* rt, JSValueConst val) {
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (val, &classId));
- if (!container || container->magic != (int)(VEC_OPAQUE_MAGIC + components)) {
- return;
- }
- // free container and the associated DynamicValue if temporal
- if (container->id != InvalidVectorInstanceId) {
- container->adapter.free (container->id);
- }
- delete container;
- }
- template void vector_finalizer<2> (JSRuntime* rt, JSValueConst val);
- template void vector_finalizer<3> (JSRuntime* rt, JSValueConst val);
- template void vector_finalizer<4> (JSRuntime* rt, JSValueConst val);
- template <int components>
- JSValue vector_normalize (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate (container->value, true);
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- newContainer->value.update (
- glm::normalize (vector_get<components> (container->value)), DynamicValue::UpdateSource::Script
- );
- return newVector;
- }
- template JSValue vector_normalize<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_normalize<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_normalize<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_add (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- if (argc != 1) {
- return JS_UNDEFINED;
- }
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- vector_get<components> (ctx, argv[0]) + vector_get<components> (container->value),
- DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_add<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_add<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_add<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components>
- JSValue vector_subtract (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- if (argc != 1) {
- return JS_UNDEFINED;
- }
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- vector_get<components> (ctx, argv[0]) - vector_get<components> (container->value),
- DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_subtract<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_subtract<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_subtract<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components>
- JSValue vector_multiply (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- if (argc != 1) {
- return JS_UNDEFINED;
- }
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- vector_get<components> (ctx, argv[0]) * vector_get<components> (container->value),
- DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_multiply<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_multiply<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_multiply<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_divide (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- if (argc != 1) {
- return JS_UNDEFINED;
- }
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- vector_get<components> (ctx, argv[0]) / vector_get<components> (container->value),
- DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_divide<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_divide<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_divide<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_dot (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- if (argc != 1) {
- return JS_UNDEFINED;
- }
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- glm::dot (vector_get<components> (ctx, argv[0]), vector_get<components> (container->value)),
- DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_dot<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_dot<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_dot<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_cross (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- if (argc != 1) {
- return JS_UNDEFINED;
- }
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- glm::cross (vector_get<components> (ctx, argv[0]), vector_get<components> (container->value)),
- DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_cross<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_mix (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- if (argc != 2) {
- return JS_EXCEPTION;
- }
- if (!JS_IsNumber (argv[1])) {
- return JS_EXCEPTION;
- }
- double amount = 0.0f;
- JS_ToFloat64 (ctx, &amount, argv[1]);
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- glm::mix (vector_get<components> (ctx, argv[0]), vector_get<components> (container->value), amount),
- DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_mix<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_mix<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_mix<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_min (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- if (argc != 1) {
- return JS_UNDEFINED;
- }
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- glm::min (vector_get<components> (ctx, argv[0]), vector_get<components> (container->value)),
- DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_min<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_min<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_min<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_max (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- if (argc != 1) {
- return JS_UNDEFINED;
- }
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- glm::max (vector_get<components> (ctx, argv[0]), vector_get<components> (container->value)),
- DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_max<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_max<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_max<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_abs (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- glm::abs (vector_get<components> (container->value)), DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_abs<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_abs<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_abs<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_sign (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- glm::sign (vector_get<components> (container->value)), DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_sign<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_sign<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_sign<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_round (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- glm::round (vector_get<components> (container->value)), DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_round<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_round<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_round<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_floor (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (
- glm::floor (vector_get<components> (container->value)), DynamicValue::UpdateSource::Initialization
- );
- return newVector;
- }
- template JSValue vector_floor<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_floor<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_floor<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components> JSValue vector_ceil (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- JSValue newVector = container->adapter.instantiate ();
- const auto* newContainer = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (newVector, &classId));
- const auto vector = vector_get<components> (container->value);
- VEC_MAGIC_CHECK_EXCEPTION (newContainer, components);
- newContainer->value.update (glm::ceil (vector), DynamicValue::UpdateSource::Initialization);
- return newVector;
- }
- template JSValue vector_ceil<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_ceil<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_ceil<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components>
- JSValue vector_toString (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
- JSClassID classId = 0;
- const auto* container = static_cast<VectorOpaqueContainer<components>*> (JS_GetAnyOpaque (this_val, &classId));
- VEC_MAGIC_CHECK_EXCEPTION (container, components);
- return JS_NewString (ctx, container->value.toString ().c_str ());
- }
- template JSValue vector_toString<2> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_toString<3> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template JSValue vector_toString<4> (JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
- template <int components>
- VectorAdapter<components>::VectorAdapter (ScriptEngine& engine) :
- ObjectAdapter (engine), m_instanceId (++VectorAdapterInstanceId), m_name ("Vec" + std::to_string (components)),
- m_exoticMethods (
- {
- .get_property = vector_property_get<components>,
- .set_property = vector_property_set<components>,
- }
- ) {
- vectorAdapterInstances<components>.emplace (this->m_instanceId, *this);
- this->registerType (
- {
- .class_name = this->m_name.c_str (),
- .finalizer = vector_finalizer<components>,
- .exotic = &this->m_exoticMethods,
- }
- );
- m_prototype = JS_NewObject (this->m_engine.getContext ());
- JS_DupValue (this->m_engine.getContext (), m_prototype);
- JSValue ctor = JS_NewCFunctionMagic (
- this->m_engine.getContext (), vector_constructor<components>, this->m_name.c_str (), 1,
- JS_CFUNC_constructor_magic, this->m_instanceId
- );
- JS_SetConstructor (this->m_engine.getContext (), ctor, m_prototype);
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "copy",
- JS_NewCFunction (this->m_engine.getContext (), vector_copy<components>, "copy", 0), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "equals",
- JS_NewCFunction (this->m_engine.getContext (), vector_equals<components>, "equals", 1), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "length",
- JS_NewCFunction (this->m_engine.getContext (), vector_length<components>, "length", 0), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "lengthSqr",
- JS_NewCFunction (this->m_engine.getContext (), vector_length<components>, "lengthSqr", 0), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "normalize",
- JS_NewCFunction (this->m_engine.getContext (), vector_normalize<components>, "normalize", 0), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "add",
- JS_NewCFunction (this->m_engine.getContext (), vector_add<components>, "add", 1), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "subtract",
- JS_NewCFunction (this->m_engine.getContext (), vector_subtract<components>, "subtract", 1), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "multiply",
- JS_NewCFunction (this->m_engine.getContext (), vector_multiply<components>, "multiply", 1), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "divide",
- JS_NewCFunction (this->m_engine.getContext (), vector_divide<components>, "divide", 1), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "dot",
- JS_NewCFunction (this->m_engine.getContext (), vector_dot<components>, "dot", 1), JS_PROP_ENUMERABLE
- );
- if constexpr (components == 3) {
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "cross",
- JS_NewCFunction (this->m_engine.getContext (), vector_cross<components>, "cross", 1), JS_PROP_ENUMERABLE
- );
- }
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "mix",
- JS_NewCFunction (this->m_engine.getContext (), vector_mix<components>, "mix", 2), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "min",
- JS_NewCFunction (this->m_engine.getContext (), vector_min<components>, "min", 1), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "max",
- JS_NewCFunction (this->m_engine.getContext (), vector_max<components>, "max", 1), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "abs",
- JS_NewCFunction (this->m_engine.getContext (), vector_abs<components>, "abs", 0), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "sign",
- JS_NewCFunction (this->m_engine.getContext (), vector_sign<components>, "sign", 0), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "round",
- JS_NewCFunction (this->m_engine.getContext (), vector_round<components>, "round", 0), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "floor",
- JS_NewCFunction (this->m_engine.getContext (), vector_floor<components>, "floor", 0), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "ceil",
- JS_NewCFunction (this->m_engine.getContext (), vector_ceil<components>, "ceil", 0), JS_PROP_ENUMERABLE
- );
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), m_prototype, "toString",
- JS_NewCFunction (this->m_engine.getContext (), vector_toString<components>, "toString", 0), JS_PROP_ENUMERABLE
- );
- JS_SetClassProto (this->m_engine.getContext (), this->m_classId, m_prototype);
- // JS_SetConstructor only wires up prototype<->constructor for instanceof; without exposing
- // ctor as a global here, `new Vec3(...)` didn't exist, and scripts using it at module top
- // level (not inside a function) would throw ReferenceError during module evaluation, killing
- // that script before init()/update() ever ran.
- JS_DefinePropertyValueStr (
- this->m_engine.getContext (), this->m_engine.getGlobalThis (), this->m_name.c_str (), ctor, JS_PROP_ENUMERABLE
- );
- }
- template <int components> VectorAdapter<components>::~VectorAdapter () {
- vectorAdapterInstances<components>.erase (this->m_instanceId);
- // Runs after ScriptEngine has freed the JS runtime/context, so m_prototype and everything
- // else tied to it is already gone - nothing left to release here.
- }
- template <int components> JSValue VectorAdapter<components>::instantiate (ScriptableObject& object) {
- throw std::runtime_error ("Cannot create a Vector4 instance from a ScriptableObject");
- }
- template <int components> JSValue VectorAdapter<components>::instantiate (DynamicValue& value) {
- JSValue result = this->ObjectAdapter::instantiate (value);
- JS_SetOpaque (
- result,
- new VectorOpaqueContainer<components> {
- .magic = VEC_OPAQUE_MAGIC + components,
- .adapter = *this,
- .value = value,
- .id = InvalidVectorInstanceId,
- }
- );
- return result;
- }
- template <int components> JSValue VectorAdapter<components>::instantiate (DynamicValue& source, bool temporal) {
- auto value = std::make_unique<DynamicValue> (source);
- uint32_t id = ++VectorInstanceId;
- JSValue result = this->ObjectAdapter::instantiate (*value);
- JS_SetOpaque (
- result,
- new VectorOpaqueContainer<components> {
- .magic = VEC_OPAQUE_MAGIC + components,
- .adapter = *this,
- .value = *value,
- .id = id,
- }
- );
- this->m_values.emplace (id, std::move (value));
- return result;
- }
- template <int components> JSValue VectorAdapter<components>::instantiate () {
- auto value = std::make_unique<DynamicValue> (vector_new<components> ());
- uint32_t id = ++VectorInstanceId;
- JSValue result = this->ObjectAdapter::instantiate (*value);
- JS_SetOpaque (
- result,
- new VectorOpaqueContainer<components> {
- .magic = VEC_OPAQUE_MAGIC + components,
- .adapter = *this,
- .value = *value,
- .id = id,
- }
- );
- this->m_values.emplace (id, std::move (value));
- return result;
- }
- template <int components> void VectorAdapter<components>::free (uint32_t vectorId) {
- auto it = this->m_values.find (vectorId);
- if (it != this->m_values.end ()) {
- this->m_values.erase (it);
- }
- }
- namespace WallpaperEngine::Scripting::Adapters {
- template class VectorAdapter<2>;
- template class VectorAdapter<3>;
- template class VectorAdapter<4>;
- } // namespace WallpaperEngine::Scripting::Adapters
|