/*************************************************************************/ /* variant_setget.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "variant_setget.h" struct VariantSetterGetterInfo { void (*setter)(Variant *base, const Variant *value, bool &valid); void (*getter)(const Variant *base, Variant *value); Variant::ValidatedSetter validated_setter; Variant::ValidatedGetter validated_getter; Variant::PTRSetter ptr_setter; Variant::PTRGetter ptr_getter; Variant::Type member_type; }; static LocalVector variant_setters_getters[Variant::VARIANT_MAX]; static LocalVector variant_setters_getters_names[Variant::VARIANT_MAX]; //one next to another to make it cache friendly template static void register_member(Variant::Type p_type, const StringName &p_member) { VariantSetterGetterInfo sgi; sgi.setter = T::set; sgi.validated_setter = T::validated_set; sgi.ptr_setter = T::ptr_set; sgi.getter = T::get; sgi.validated_getter = T::validated_get; sgi.ptr_getter = T::ptr_get; sgi.member_type = T::get_type(); variant_setters_getters[p_type].push_back(sgi); variant_setters_getters_names[p_type].push_back(p_member); } void register_named_setters_getters() { #define REGISTER_MEMBER(m_base_type, m_member) register_member(GetTypeInfo::VARIANT_TYPE, #m_member) REGISTER_MEMBER(Vector2, x); REGISTER_MEMBER(Vector2, y); REGISTER_MEMBER(Vector2i, x); REGISTER_MEMBER(Vector2i, y); REGISTER_MEMBER(Vector3, x); REGISTER_MEMBER(Vector3, y); REGISTER_MEMBER(Vector3, z); REGISTER_MEMBER(Vector3i, x); REGISTER_MEMBER(Vector3i, y); REGISTER_MEMBER(Vector3i, z); REGISTER_MEMBER(Vector4, x); REGISTER_MEMBER(Vector4, y); REGISTER_MEMBER(Vector4, z); REGISTER_MEMBER(Vector4, w); REGISTER_MEMBER(Vector4i, x); REGISTER_MEMBER(Vector4i, y); REGISTER_MEMBER(Vector4i, z); REGISTER_MEMBER(Vector4i, w); REGISTER_MEMBER(Rect2, position); REGISTER_MEMBER(Rect2, size); REGISTER_MEMBER(Rect2, end); REGISTER_MEMBER(Rect2i, position); REGISTER_MEMBER(Rect2i, size); REGISTER_MEMBER(Rect2i, end); REGISTER_MEMBER(AABB, position); REGISTER_MEMBER(AABB, size); REGISTER_MEMBER(AABB, end); REGISTER_MEMBER(Transform2D, x); REGISTER_MEMBER(Transform2D, y); REGISTER_MEMBER(Transform2D, origin); REGISTER_MEMBER(Plane, x); REGISTER_MEMBER(Plane, y); REGISTER_MEMBER(Plane, z); REGISTER_MEMBER(Plane, d); REGISTER_MEMBER(Plane, normal); REGISTER_MEMBER(Quaternion, x); REGISTER_MEMBER(Quaternion, y); REGISTER_MEMBER(Quaternion, z); REGISTER_MEMBER(Quaternion, w); REGISTER_MEMBER(Basis, x); REGISTER_MEMBER(Basis, y); REGISTER_MEMBER(Basis, z); REGISTER_MEMBER(Transform3D, basis); REGISTER_MEMBER(Transform3D, origin); REGISTER_MEMBER(Projection, x); REGISTER_MEMBER(Projection, y); REGISTER_MEMBER(Projection, z); REGISTER_MEMBER(Projection, w); REGISTER_MEMBER(Color, r); REGISTER_MEMBER(Color, g); REGISTER_MEMBER(Color, b); REGISTER_MEMBER(Color, a); REGISTER_MEMBER(Color, r8); REGISTER_MEMBER(Color, g8); REGISTER_MEMBER(Color, b8); REGISTER_MEMBER(Color, a8); REGISTER_MEMBER(Color, h); REGISTER_MEMBER(Color, s); REGISTER_MEMBER(Color, v); } void unregister_named_setters_getters() { for (int i = 0; i < Variant::VARIANT_MAX; i++) { variant_setters_getters[i].clear(); variant_setters_getters_names[i].clear(); } } bool Variant::has_member(Variant::Type p_type, const StringName &p_member) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { if (variant_setters_getters_names[p_type][i] == p_member) { return true; } } return false; } Variant::Type Variant::get_member_type(Variant::Type p_type, const StringName &p_member) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Variant::VARIANT_MAX); for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { if (variant_setters_getters_names[p_type][i] == p_member) { return variant_setters_getters[p_type][i].member_type; } } return Variant::NIL; } void Variant::get_member_list(Variant::Type p_type, List *r_members) { for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { r_members->push_back(variant_setters_getters_names[p_type][i]); } } int Variant::get_member_count(Type p_type) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, -1); return variant_setters_getters_names[p_type].size(); } Variant::ValidatedSetter Variant::get_member_validated_setter(Variant::Type p_type, const StringName &p_member) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { if (variant_setters_getters_names[p_type][i] == p_member) { return variant_setters_getters[p_type][i].validated_setter; } } return nullptr; } Variant::ValidatedGetter Variant::get_member_validated_getter(Variant::Type p_type, const StringName &p_member) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { if (variant_setters_getters_names[p_type][i] == p_member) { return variant_setters_getters[p_type][i].validated_getter; } } return nullptr; } Variant::PTRSetter Variant::get_member_ptr_setter(Variant::Type p_type, const StringName &p_member) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { if (variant_setters_getters_names[p_type][i] == p_member) { return variant_setters_getters[p_type][i].ptr_setter; } } return nullptr; } Variant::PTRGetter Variant::get_member_ptr_getter(Variant::Type p_type, const StringName &p_member) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); for (uint32_t i = 0; i < variant_setters_getters_names[p_type].size(); i++) { if (variant_setters_getters_names[p_type][i] == p_member) { return variant_setters_getters[p_type][i].ptr_getter; } } return nullptr; } void Variant::set_named(const StringName &p_member, const Variant &p_value, bool &r_valid) { uint32_t s = variant_setters_getters[type].size(); if (s) { for (uint32_t i = 0; i < s; i++) { if (variant_setters_getters_names[type][i] == p_member) { variant_setters_getters[type][i].setter(this, &p_value, r_valid); return; } } r_valid = false; } else if (type == Variant::OBJECT) { Object *obj = get_validated_object(); if (!obj) { r_valid = false; } else { obj->set(p_member, p_value, &r_valid); return; } } else if (type == Variant::DICTIONARY) { Variant *v = VariantGetInternalPtr::get_ptr(this)->getptr(p_member); if (v) { *v = p_value; r_valid = true; } else { VariantGetInternalPtr::get_ptr(this)->operator[](p_member) = p_value; r_valid = true; } } else { r_valid = false; } } Variant Variant::get_named(const StringName &p_member, bool &r_valid) const { Variant ret; uint32_t s = variant_setters_getters[type].size(); if (s) { for (uint32_t i = 0; i < s; i++) { if (variant_setters_getters_names[type][i] == p_member) { variant_setters_getters[type][i].getter(this, &ret); r_valid = true; return ret; } } r_valid = false; } else if (type == Variant::OBJECT) { Object *obj = get_validated_object(); if (!obj) { r_valid = false; return "Instance base is null."; } else { return obj->get(p_member, &r_valid); } } else if (type == Variant::DICTIONARY) { const Variant *v = VariantGetInternalPtr::get_ptr(this)->getptr(p_member); if (v) { r_valid = true; return *v; } else { r_valid = false; } } else { r_valid = false; } return ret; } /**** INDEXED SETTERS AND GETTERS ****/ #ifdef DEBUG_ENABLED #define OOB_TEST(m_idx, m_v) \ ERR_FAIL_INDEX(m_idx, m_v) #else #define OOB_TEST(m_idx, m_v) #endif #ifdef DEBUG_ENABLED #define NULL_TEST(m_key) \ ERR_FAIL_COND(!m_key) #else #define NULL_TEST(m_key) #endif #define INDEXED_SETGET_STRUCT_TYPED(m_base_type, m_elem_type) \ struct VariantIndexedSetGet_##m_base_type { \ static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ int64_t size = VariantGetInternalPtr::get_ptr(base)->size(); \ if (index < 0) { \ index += size; \ } \ if (index < 0 || index >= size) { \ *oob = true; \ return; \ } \ VariantTypeAdjust::adjust(value); \ *VariantGetInternalPtr::get_ptr(value) = (*VariantGetInternalPtr::get_ptr(base))[index]; \ *oob = false; \ } \ static void ptr_get(const void *base, int64_t index, void *member) { \ /* avoid ptrconvert for performance*/ \ const m_base_type &v = *reinterpret_cast(base); \ if (index < 0) \ index += v.size(); \ OOB_TEST(index, v.size()); \ PtrToArg::encode(v[index], member); \ } \ static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ if (value->get_type() != GetTypeInfo::VARIANT_TYPE) { \ *oob = false; \ *valid = false; \ return; \ } \ int64_t size = VariantGetInternalPtr::get_ptr(base)->size(); \ if (index < 0) { \ index += size; \ } \ if (index < 0 || index >= size) { \ *oob = true; \ *valid = false; \ return; \ } \ (*VariantGetInternalPtr::get_ptr(base)).write[index] = *VariantGetInternalPtr::get_ptr(value); \ *oob = false; \ *valid = true; \ } \ static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ int64_t size = VariantGetInternalPtr::get_ptr(base)->size(); \ if (index < 0) { \ index += size; \ } \ if (index < 0 || index >= size) { \ *oob = true; \ return; \ } \ (*VariantGetInternalPtr::get_ptr(base)).write[index] = *VariantGetInternalPtr::get_ptr(value); \ *oob = false; \ } \ static void ptr_set(void *base, int64_t index, const void *member) { \ /* avoid ptrconvert for performance*/ \ m_base_type &v = *reinterpret_cast(base); \ if (index < 0) \ index += v.size(); \ OOB_TEST(index, v.size()); \ v.write[index] = PtrToArg::convert(member); \ } \ static Variant::Type get_index_type() { return GetTypeInfo::VARIANT_TYPE; } \ static uint64_t get_indexed_size(const Variant *base) { return VariantGetInternalPtr::get_ptr(base)->size(); } \ }; #define INDEXED_SETGET_STRUCT_TYPED_NUMERIC(m_base_type, m_elem_type, m_assign_type) \ struct VariantIndexedSetGet_##m_base_type { \ static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ int64_t size = VariantGetInternalPtr::get_ptr(base)->size(); \ if (index < 0) { \ index += size; \ } \ if (index < 0 || index >= size) { \ *oob = true; \ return; \ } \ VariantTypeAdjust::adjust(value); \ *VariantGetInternalPtr::get_ptr(value) = (*VariantGetInternalPtr::get_ptr(base))[index]; \ *oob = false; \ } \ static void ptr_get(const void *base, int64_t index, void *member) { \ /* avoid ptrconvert for performance*/ \ const m_base_type &v = *reinterpret_cast(base); \ if (index < 0) \ index += v.size(); \ OOB_TEST(index, v.size()); \ PtrToArg::encode(v[index], member); \ } \ static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ int64_t size = VariantGetInternalPtr::get_ptr(base)->size(); \ if (index < 0) { \ index += size; \ } \ if (index < 0 || index >= size) { \ *oob = true; \ *valid = false; \ return; \ } \ m_assign_type num; \ if (value->get_type() == Variant::INT) { \ num = (m_assign_type)*VariantGetInternalPtr::get_ptr(value); \ } else if (value->get_type() == Variant::FLOAT) { \ num = (m_assign_type)*VariantGetInternalPtr::get_ptr(value); \ } else { \ *oob = false; \ *valid = false; \ return; \ } \ (*VariantGetInternalPtr::get_ptr(base)).write[index] = num; \ *oob = false; \ *valid = true; \ } \ static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ int64_t size = VariantGetInternalPtr::get_ptr(base)->size(); \ if (index < 0) { \ index += size; \ } \ if (index < 0 || index >= size) { \ *oob = true; \ return; \ } \ (*VariantGetInternalPtr::get_ptr(base)).write[index] = *VariantGetInternalPtr::get_ptr(value); \ *oob = false; \ } \ static void ptr_set(void *base, int64_t index, const void *member) { \ /* avoid ptrconvert for performance*/ \ m_base_type &v = *reinterpret_cast(base); \ if (index < 0) \ index += v.size(); \ OOB_TEST(index, v.size()); \ v.write[index] = PtrToArg::convert(member); \ } \ static Variant::Type get_index_type() { return GetTypeInfo::VARIANT_TYPE; } \ static uint64_t get_indexed_size(const Variant *base) { return VariantGetInternalPtr::get_ptr(base)->size(); } \ }; #define INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(m_base_type, m_elem_type, m_assign_type, m_max) \ struct VariantIndexedSetGet_##m_base_type { \ static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ if (index < 0 || index >= m_max) { \ *oob = true; \ return; \ } \ VariantTypeAdjust::adjust(value); \ *VariantGetInternalPtr::get_ptr(value) = (*VariantGetInternalPtr::get_ptr(base))[index]; \ *oob = false; \ } \ static void ptr_get(const void *base, int64_t index, void *member) { \ /* avoid ptrconvert for performance*/ \ const m_base_type &v = *reinterpret_cast(base); \ OOB_TEST(index, m_max); \ PtrToArg::encode(v[index], member); \ } \ static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ if (index < 0 || index >= m_max) { \ *oob = true; \ *valid = false; \ return; \ } \ m_assign_type num; \ if (value->get_type() == Variant::INT) { \ num = (m_assign_type)*VariantGetInternalPtr::get_ptr(value); \ } else if (value->get_type() == Variant::FLOAT) { \ num = (m_assign_type)*VariantGetInternalPtr::get_ptr(value); \ } else { \ *oob = false; \ *valid = false; \ return; \ } \ (*VariantGetInternalPtr::get_ptr(base))[index] = num; \ *oob = false; \ *valid = true; \ } \ static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ if (index < 0 || index >= m_max) { \ *oob = true; \ return; \ } \ (*VariantGetInternalPtr::get_ptr(base))[index] = *VariantGetInternalPtr::get_ptr(value); \ *oob = false; \ } \ static void ptr_set(void *base, int64_t index, const void *member) { \ /* avoid ptrconvert for performance*/ \ m_base_type &v = *reinterpret_cast(base); \ OOB_TEST(index, m_max); \ v[index] = PtrToArg::convert(member); \ } \ static Variant::Type get_index_type() { return GetTypeInfo::VARIANT_TYPE; } \ static uint64_t get_indexed_size(const Variant *base) { return m_max; } \ }; #define INDEXED_SETGET_STRUCT_BULTIN_ACCESSOR(m_base_type, m_elem_type, m_accessor, m_max) \ struct VariantIndexedSetGet_##m_base_type { \ static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ if (index < 0 || index >= m_max) { \ *oob = true; \ return; \ } \ VariantTypeAdjust::adjust(value); \ *VariantGetInternalPtr::get_ptr(value) = (*VariantGetInternalPtr::get_ptr(base))m_accessor[index]; \ *oob = false; \ } \ static void ptr_get(const void *base, int64_t index, void *member) { \ /* avoid ptrconvert for performance*/ \ const m_base_type &v = *reinterpret_cast(base); \ OOB_TEST(index, m_max); \ PtrToArg::encode(v m_accessor[index], member); \ } \ static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ if (value->get_type() != GetTypeInfo::VARIANT_TYPE) { \ *oob = false; \ *valid = false; \ } \ if (index < 0 || index >= m_max) { \ *oob = true; \ *valid = false; \ return; \ } \ (*VariantGetInternalPtr::get_ptr(base)) m_accessor[index] = *VariantGetInternalPtr::get_ptr(value); \ *oob = false; \ *valid = true; \ } \ static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ if (index < 0 || index >= m_max) { \ *oob = true; \ return; \ } \ (*VariantGetInternalPtr::get_ptr(base)) m_accessor[index] = *VariantGetInternalPtr::get_ptr(value); \ *oob = false; \ } \ static void ptr_set(void *base, int64_t index, const void *member) { \ /* avoid ptrconvert for performance*/ \ m_base_type &v = *reinterpret_cast(base); \ OOB_TEST(index, m_max); \ v m_accessor[index] = PtrToArg::convert(member); \ } \ static Variant::Type get_index_type() { return GetTypeInfo::VARIANT_TYPE; } \ static uint64_t get_indexed_size(const Variant *base) { return m_max; } \ }; #define INDEXED_SETGET_STRUCT_BULTIN_FUNC(m_base_type, m_elem_type, m_set, m_get, m_max) \ struct VariantIndexedSetGet_##m_base_type { \ static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ if (index < 0 || index >= m_max) { \ *oob = true; \ return; \ } \ VariantTypeAdjust::adjust(value); \ *VariantGetInternalPtr::get_ptr(value) = VariantGetInternalPtr::get_ptr(base)->m_get(index); \ *oob = false; \ } \ static void ptr_get(const void *base, int64_t index, void *member) { \ /* avoid ptrconvert for performance*/ \ const m_base_type &v = *reinterpret_cast(base); \ OOB_TEST(index, m_max); \ PtrToArg::encode(v.m_get(index), member); \ } \ static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ if (value->get_type() != GetTypeInfo::VARIANT_TYPE) { \ *oob = false; \ *valid = false; \ } \ if (index < 0 || index >= m_max) { \ *oob = true; \ *valid = false; \ return; \ } \ VariantGetInternalPtr::get_ptr(base)->m_set(index, *VariantGetInternalPtr::get_ptr(value)); \ *oob = false; \ *valid = true; \ } \ static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ if (index < 0 || index >= m_max) { \ *oob = true; \ return; \ } \ VariantGetInternalPtr::get_ptr(base)->m_set(index, *VariantGetInternalPtr::get_ptr(value)); \ *oob = false; \ } \ static void ptr_set(void *base, int64_t index, const void *member) { \ /* avoid ptrconvert for performance*/ \ m_base_type &v = *reinterpret_cast(base); \ OOB_TEST(index, m_max); \ v.m_set(index, PtrToArg::convert(member)); \ } \ static Variant::Type get_index_type() { return GetTypeInfo::VARIANT_TYPE; } \ static uint64_t get_indexed_size(const Variant *base) { return m_max; } \ }; struct VariantIndexedSetGet_Array { static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { int64_t size = VariantGetInternalPtr::get_ptr(base)->size(); if (index < 0) { index += size; } if (index < 0 || index >= size) { *oob = true; return; } *value = (*VariantGetInternalPtr::get_ptr(base))[index]; *oob = false; } static void ptr_get(const void *base, int64_t index, void *member) { /* avoid ptrconvert for performance*/ const Array &v = *reinterpret_cast(base); if (index < 0) { index += v.size(); } OOB_TEST(index, v.size()); PtrToArg::encode(v[index], member); } static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { if (VariantGetInternalPtr::get_ptr(base)->is_read_only()) { *valid = false; *oob = true; return; } int64_t size = VariantGetInternalPtr::get_ptr(base)->size(); if (index < 0) { index += size; } if (index < 0 || index >= size) { *oob = true; *valid = false; return; } VariantGetInternalPtr::get_ptr(base)->set(index, *value); *oob = false; *valid = true; } static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { if (VariantGetInternalPtr::get_ptr(base)->is_read_only()) { *oob = true; return; } int64_t size = VariantGetInternalPtr::get_ptr(base)->size(); if (index < 0) { index += size; } if (index < 0 || index >= size) { *oob = true; return; } VariantGetInternalPtr::get_ptr(base)->set(index, *value); *oob = false; } static void ptr_set(void *base, int64_t index, const void *member) { /* avoid ptrconvert for performance*/ Array &v = *reinterpret_cast(base); if (index < 0) { index += v.size(); } OOB_TEST(index, v.size()); v.set(index, PtrToArg::convert(member)); } static Variant::Type get_index_type() { return Variant::NIL; } static uint64_t get_indexed_size(const Variant *base) { return 0; } }; struct VariantIndexedSetGet_String { static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { int64_t length = VariantGetInternalPtr::get_ptr(base)->length(); if (index < 0) { index += length; } if (index < 0 || index >= length) { *oob = true; return; } char32_t result = (*VariantGetInternalPtr::get_ptr(base))[index]; *value = String(&result, 1); *oob = false; } static void ptr_get(const void *base, int64_t index, void *member) { /* avoid ptrconvert for performance*/ const String &v = *reinterpret_cast(base); if (index < 0) { index += v.length(); } OOB_TEST(index, v.length()); char32_t c = v[index]; PtrToArg::encode(String(&c, 1), member); } static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { if (value->get_type() != Variant::STRING) { *oob = false; *valid = false; return; } int64_t length = VariantGetInternalPtr::get_ptr(base)->length(); if (index < 0) { index += length; } if (index < 0 || index >= length) { *oob = true; *valid = false; return; } String *b = VariantGetInternalPtr::get_ptr(base); const String *v = VariantInternal::get_string(value); if (v->length() == 0) { b->remove_at(index); } else { b->set(index, v->get(0)); } *oob = false; *valid = true; } static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { int64_t length = VariantGetInternalPtr::get_ptr(base)->length(); if (index < 0) { index += length; } if (index < 0 || index >= length) { *oob = true; return; } String *b = VariantGetInternalPtr::get_ptr(base); const String *v = VariantInternal::get_string(value); if (v->length() == 0) { b->remove_at(index); } else { b->set(index, v->get(0)); } *oob = false; } static void ptr_set(void *base, int64_t index, const void *member) { /* avoid ptrconvert for performance*/ String &v = *reinterpret_cast(base); if (index < 0) { index += v.length(); } OOB_TEST(index, v.length()); const String &m = *reinterpret_cast(member); if (unlikely(m.length() == 0)) { v.remove_at(index); } else { v.set(index, m.unicode_at(0)); } } static Variant::Type get_index_type() { return Variant::STRING; } static uint64_t get_indexed_size(const Variant *base) { return VariantInternal::get_string(base)->length(); } }; #define INDEXED_SETGET_STRUCT_DICT(m_base_type) \ struct VariantIndexedSetGet_##m_base_type { \ static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \ const Variant *ptr = VariantGetInternalPtr::get_ptr(base)->getptr(index); \ if (!ptr) { \ *oob = true; \ return; \ } \ *value = *ptr; \ *oob = false; \ } \ static void ptr_get(const void *base, int64_t index, void *member) { \ /* avoid ptrconvert for performance*/ \ const m_base_type &v = *reinterpret_cast(base); \ const Variant *ptr = v.getptr(index); \ NULL_TEST(ptr); \ PtrToArg::encode(*ptr, member); \ } \ static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ if (VariantGetInternalPtr::get_ptr(base)->is_read_only()) { \ *valid = false; \ *oob = true; \ return; \ } \ (*VariantGetInternalPtr::get_ptr(base))[index] = *value; \ *oob = false; \ *valid = true; \ } \ static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ if (VariantGetInternalPtr::get_ptr(base)->is_read_only()) { \ *oob = true; \ return; \ } \ (*VariantGetInternalPtr::get_ptr(base))[index] = *value; \ *oob = false; \ } \ static void ptr_set(void *base, int64_t index, const void *member) { \ m_base_type &v = *reinterpret_cast(base); \ v[index] = PtrToArg::convert(member); \ } \ static Variant::Type get_index_type() { return Variant::NIL; } \ static uint64_t get_indexed_size(const Variant *base) { return VariantGetInternalPtr::get_ptr(base)->size(); } \ }; INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector2, double, real_t, 2) INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector2i, int64_t, int32_t, 2) INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector3, double, real_t, 3) INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Vector3i, int64_t, int32_t, 3) INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Quaternion, double, real_t, 4) INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Color, double, float, 4) INDEXED_SETGET_STRUCT_BULTIN_ACCESSOR(Transform2D, Vector2, .columns, 3) INDEXED_SETGET_STRUCT_BULTIN_FUNC(Basis, Vector3, set_column, get_column, 3) INDEXED_SETGET_STRUCT_BULTIN_ACCESSOR(Projection, Vector4, .matrix, 4) INDEXED_SETGET_STRUCT_TYPED_NUMERIC(PackedByteArray, int64_t, uint8_t) INDEXED_SETGET_STRUCT_TYPED_NUMERIC(PackedInt32Array, int64_t, int32_t) INDEXED_SETGET_STRUCT_TYPED_NUMERIC(PackedInt64Array, int64_t, int64_t) INDEXED_SETGET_STRUCT_TYPED_NUMERIC(PackedFloat32Array, double, float) INDEXED_SETGET_STRUCT_TYPED_NUMERIC(PackedFloat64Array, double, double) INDEXED_SETGET_STRUCT_TYPED(PackedVector2Array, Vector2) INDEXED_SETGET_STRUCT_TYPED(PackedVector3Array, Vector3) INDEXED_SETGET_STRUCT_TYPED(PackedStringArray, String) INDEXED_SETGET_STRUCT_TYPED(PackedColorArray, Color) INDEXED_SETGET_STRUCT_DICT(Dictionary) struct VariantIndexedSetterGetterInfo { void (*setter)(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) = nullptr; void (*getter)(const Variant *base, int64_t index, Variant *value, bool *oob) = nullptr; Variant::ValidatedIndexedSetter validated_setter = nullptr; Variant::ValidatedIndexedGetter validated_getter = nullptr; Variant::PTRIndexedSetter ptr_setter = nullptr; Variant::PTRIndexedGetter ptr_getter = nullptr; uint64_t (*get_indexed_size)(const Variant *base) = nullptr; Variant::Type index_type; bool valid = false; }; static VariantIndexedSetterGetterInfo variant_indexed_setters_getters[Variant::VARIANT_MAX]; template static void register_indexed_member(Variant::Type p_type) { VariantIndexedSetterGetterInfo &sgi = variant_indexed_setters_getters[p_type]; sgi.setter = T::set; sgi.validated_setter = T::validated_set; sgi.ptr_setter = T::ptr_set; sgi.getter = T::get; sgi.validated_getter = T::get; sgi.ptr_getter = T::ptr_get; sgi.index_type = T::get_index_type(); sgi.get_indexed_size = T::get_indexed_size; sgi.valid = true; } void register_indexed_setters_getters() { #define REGISTER_INDEXED_MEMBER(m_base_type) register_indexed_member(GetTypeInfo::VARIANT_TYPE) REGISTER_INDEXED_MEMBER(String); REGISTER_INDEXED_MEMBER(Vector2); REGISTER_INDEXED_MEMBER(Vector2i); REGISTER_INDEXED_MEMBER(Vector3); REGISTER_INDEXED_MEMBER(Vector3i); REGISTER_INDEXED_MEMBER(Quaternion); REGISTER_INDEXED_MEMBER(Color); REGISTER_INDEXED_MEMBER(Transform2D); REGISTER_INDEXED_MEMBER(Basis); REGISTER_INDEXED_MEMBER(Projection); REGISTER_INDEXED_MEMBER(PackedByteArray); REGISTER_INDEXED_MEMBER(PackedInt32Array); REGISTER_INDEXED_MEMBER(PackedInt64Array); REGISTER_INDEXED_MEMBER(PackedFloat32Array); REGISTER_INDEXED_MEMBER(PackedFloat64Array); REGISTER_INDEXED_MEMBER(PackedVector2Array); REGISTER_INDEXED_MEMBER(PackedVector3Array); REGISTER_INDEXED_MEMBER(PackedStringArray); REGISTER_INDEXED_MEMBER(PackedColorArray); REGISTER_INDEXED_MEMBER(Array); REGISTER_INDEXED_MEMBER(Dictionary); } static void unregister_indexed_setters_getters() { } bool Variant::has_indexing(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); return variant_indexed_setters_getters[p_type].valid; } Variant::Type Variant::get_indexed_element_type(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, Variant::VARIANT_MAX); return variant_indexed_setters_getters[p_type].index_type; } Variant::ValidatedIndexedSetter Variant::get_member_validated_indexed_setter(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); return variant_indexed_setters_getters[p_type].validated_setter; } Variant::ValidatedIndexedGetter Variant::get_member_validated_indexed_getter(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); return variant_indexed_setters_getters[p_type].validated_getter; } Variant::PTRIndexedSetter Variant::get_member_ptr_indexed_setter(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); return variant_indexed_setters_getters[p_type].ptr_setter; } Variant::PTRIndexedGetter Variant::get_member_ptr_indexed_getter(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); return variant_indexed_setters_getters[p_type].ptr_getter; } void Variant::set_indexed(int64_t p_index, const Variant &p_value, bool &r_valid, bool &r_oob) { if (likely(variant_indexed_setters_getters[type].valid)) { variant_indexed_setters_getters[type].setter(this, p_index, &p_value, &r_valid, &r_oob); } else { r_valid = false; r_oob = false; } } Variant Variant::get_indexed(int64_t p_index, bool &r_valid, bool &r_oob) const { if (likely(variant_indexed_setters_getters[type].valid)) { Variant ret; variant_indexed_setters_getters[type].getter(this, p_index, &ret, &r_oob); r_valid = !r_oob; return ret; } else { r_valid = false; r_oob = false; return Variant(); } } uint64_t Variant::get_indexed_size() const { if (likely(variant_indexed_setters_getters[type].valid && variant_indexed_setters_getters[type].get_indexed_size)) { return variant_indexed_setters_getters[type].get_indexed_size(this); } else { return 0; } } struct VariantKeyedSetGetDictionary { static void get(const Variant *base, const Variant *key, Variant *value, bool *r_valid) { const Variant *ptr = VariantGetInternalPtr::get_ptr(base)->getptr(*key); if (!ptr) { *r_valid = false; return; } *value = *ptr; *r_valid = true; } static void ptr_get(const void *base, const void *key, void *value) { /* avoid ptrconvert for performance*/ const Dictionary &v = *reinterpret_cast(base); const Variant *ptr = v.getptr(PtrToArg::convert(key)); NULL_TEST(ptr); PtrToArg::encode(*ptr, value); } static void set(Variant *base, const Variant *key, const Variant *value, bool *r_valid) { if (VariantGetInternalPtr::get_ptr(base)->is_read_only()) { *r_valid = false; return; } (*VariantGetInternalPtr::get_ptr(base))[*key] = *value; *r_valid = true; } static void ptr_set(void *base, const void *key, const void *value) { Dictionary &v = *reinterpret_cast(base); v[PtrToArg::convert(key)] = PtrToArg::convert(value); } static bool has(const Variant *base, const Variant *key, bool *r_valid) { *r_valid = true; return VariantGetInternalPtr::get_ptr(base)->has(*key); } static uint32_t ptr_has(const void *base, const void *key) { /* avoid ptrconvert for performance*/ const Dictionary &v = *reinterpret_cast(base); return v.has(PtrToArg::convert(key)); } }; struct VariantKeyedSetGetObject { static void get(const Variant *base, const Variant *key, Variant *value, bool *r_valid) { Object *obj = base->get_validated_object(); if (!obj) { *r_valid = false; *value = Variant(); return; } *value = obj->getvar(*key, r_valid); } static void ptr_get(const void *base, const void *key, void *value) { const Object *obj = PtrToArg::convert(base); NULL_TEST(obj); Variant v = obj->getvar(PtrToArg::convert(key)); PtrToArg::encode(v, value); } static void set(Variant *base, const Variant *key, const Variant *value, bool *r_valid) { Object *obj = base->get_validated_object(); if (!obj) { *r_valid = false; return; } obj->setvar(*key, *value, r_valid); } static void ptr_set(void *base, const void *key, const void *value) { Object *obj = PtrToArg::convert(base); NULL_TEST(obj); obj->setvar(PtrToArg::convert(key), PtrToArg::convert(value)); } static bool has(const Variant *base, const Variant *key, bool *r_valid) { Object *obj = base->get_validated_object(); if (!obj) { *r_valid = false; return false; } *r_valid = true; bool exists; obj->getvar(*key, &exists); return exists; } static uint32_t ptr_has(const void *base, const void *key) { const Object *obj = PtrToArg::convert(base); ERR_FAIL_COND_V(!obj, false); bool valid; obj->getvar(PtrToArg::convert(key), &valid); return valid; } }; struct VariantKeyedSetterGetterInfo { Variant::ValidatedKeyedSetter validated_setter = nullptr; Variant::ValidatedKeyedGetter validated_getter = nullptr; Variant::ValidatedKeyedChecker validated_checker = nullptr; Variant::PTRKeyedSetter ptr_setter = nullptr; Variant::PTRKeyedGetter ptr_getter = nullptr; Variant::PTRKeyedChecker ptr_checker = nullptr; bool valid = false; }; static VariantKeyedSetterGetterInfo variant_keyed_setters_getters[Variant::VARIANT_MAX]; template static void register_keyed_member(Variant::Type p_type) { VariantKeyedSetterGetterInfo &sgi = variant_keyed_setters_getters[p_type]; sgi.validated_setter = T::set; sgi.ptr_setter = T::ptr_set; sgi.validated_getter = T::get; sgi.ptr_getter = T::ptr_get; sgi.validated_checker = T::has; sgi.ptr_checker = T::ptr_has; sgi.valid = true; } static void register_keyed_setters_getters() { register_keyed_member(Variant::DICTIONARY); register_keyed_member(Variant::OBJECT); } bool Variant::is_keyed(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, false); return variant_keyed_setters_getters[p_type].valid; } Variant::ValidatedKeyedSetter Variant::get_member_validated_keyed_setter(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, nullptr); return variant_keyed_setters_getters[p_type].validated_setter; } Variant::ValidatedKeyedGetter Variant::get_member_validated_keyed_getter(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, nullptr); return variant_keyed_setters_getters[p_type].validated_getter; } Variant::ValidatedKeyedChecker Variant::get_member_validated_keyed_checker(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, nullptr); return variant_keyed_setters_getters[p_type].validated_checker; } Variant::PTRKeyedSetter Variant::get_member_ptr_keyed_setter(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, nullptr); return variant_keyed_setters_getters[p_type].ptr_setter; } Variant::PTRKeyedGetter Variant::get_member_ptr_keyed_getter(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, nullptr); return variant_keyed_setters_getters[p_type].ptr_getter; } Variant::PTRKeyedChecker Variant::get_member_ptr_keyed_checker(Variant::Type p_type) { ERR_FAIL_INDEX_V(p_type, VARIANT_MAX, nullptr); return variant_keyed_setters_getters[p_type].ptr_checker; } void Variant::set_keyed(const Variant &p_key, const Variant &p_value, bool &r_valid) { if (likely(variant_keyed_setters_getters[type].valid)) { variant_keyed_setters_getters[type].validated_setter(this, &p_key, &p_value, &r_valid); } else { r_valid = false; } } Variant Variant::get_keyed(const Variant &p_key, bool &r_valid) const { if (likely(variant_keyed_setters_getters[type].valid)) { Variant ret; variant_keyed_setters_getters[type].validated_getter(this, &p_key, &ret, &r_valid); return ret; } else { r_valid = false; return Variant(); } } bool Variant::has_key(const Variant &p_key, bool &r_valid) const { if (likely(variant_keyed_setters_getters[type].valid)) { return variant_keyed_setters_getters[type].validated_checker(this, &p_key, &r_valid); } else { r_valid = false; return false; } } void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) { if (type == DICTIONARY || type == OBJECT) { bool valid; set_keyed(p_index, p_value, valid); if (r_valid) { *r_valid = valid; } } else { bool valid = false; if (p_index.get_type() == STRING_NAME) { set_named(*VariantGetInternalPtr::get_ptr(&p_index), p_value, valid); } else if (p_index.get_type() == INT) { bool obb; set_indexed(*VariantGetInternalPtr::get_ptr(&p_index), p_value, valid, obb); if (obb) { valid = false; } } else if (p_index.get_type() == STRING) { // less efficient version of named set_named(*VariantGetInternalPtr::get_ptr(&p_index), p_value, valid); } else if (p_index.get_type() == FLOAT) { // less efficient version of indexed bool obb; set_indexed(*VariantGetInternalPtr::get_ptr(&p_index), p_value, valid, obb); if (obb) { valid = false; } } if (r_valid) { *r_valid = valid; } } } Variant Variant::get(const Variant &p_index, bool *r_valid) const { Variant ret; if (type == DICTIONARY || type == OBJECT) { bool valid; ret = get_keyed(p_index, valid); if (r_valid) { *r_valid = valid; } } else { bool valid = false; if (p_index.get_type() == STRING_NAME) { ret = get_named(*VariantGetInternalPtr::get_ptr(&p_index), valid); } else if (p_index.get_type() == INT) { bool obb; ret = get_indexed(*VariantGetInternalPtr::get_ptr(&p_index), valid, obb); if (obb) { valid = false; } } else if (p_index.get_type() == STRING) { // less efficient version of named ret = get_named(*VariantGetInternalPtr::get_ptr(&p_index), valid); } else if (p_index.get_type() == FLOAT) { // less efficient version of indexed bool obb; ret = get_indexed(*VariantGetInternalPtr::get_ptr(&p_index), valid, obb); if (obb) { valid = false; } } if (r_valid) { *r_valid = valid; } } return ret; } void Variant::get_property_list(List *p_list) const { if (type == DICTIONARY) { const Dictionary *dic = reinterpret_cast(_data._mem); List keys; dic->get_key_list(&keys); for (const Variant &E : keys) { if (E.get_type() == Variant::STRING) { p_list->push_back(PropertyInfo(Variant::STRING, E)); } } } else if (type == OBJECT) { Object *obj = get_validated_object(); ERR_FAIL_COND(!obj); obj->get_property_list(p_list); } else { List members; get_member_list(type, &members); for (const StringName &E : members) { PropertyInfo pi; pi.name = E; pi.type = get_member_type(type, E); p_list->push_back(pi); } } } bool Variant::iter_init(Variant &r_iter, bool &valid) const { valid = true; switch (type) { case INT: { r_iter = 0; return _data._int > 0; } break; case FLOAT: { r_iter = 0; return _data._float > 0.0; } break; case VECTOR2: { double from = reinterpret_cast(_data._mem)->x; double to = reinterpret_cast(_data._mem)->y; r_iter = from; return from < to; } break; case VECTOR2I: { int64_t from = reinterpret_cast(_data._mem)->x; int64_t to = reinterpret_cast(_data._mem)->y; r_iter = from; return from < to; } break; case VECTOR3: { double from = reinterpret_cast(_data._mem)->x; double to = reinterpret_cast(_data._mem)->y; double step = reinterpret_cast(_data._mem)->z; r_iter = from; if (from == to) { return false; } else if (from < to) { return step > 0; } return step < 0; } break; case VECTOR3I: { int64_t from = reinterpret_cast(_data._mem)->x; int64_t to = reinterpret_cast(_data._mem)->y; int64_t step = reinterpret_cast(_data._mem)->z; r_iter = from; if (from == to) { return false; } else if (from < to) { return step > 0; } return step < 0; } break; case OBJECT: { if (!_get_obj().obj) { valid = false; return false; } #ifdef DEBUG_ENABLED if (EngineDebugger::is_active() && !_get_obj().id.is_ref_counted() && ObjectDB::get_instance(_get_obj().id) == nullptr) { valid = false; return false; } #endif Callable::CallError ce; ce.error = Callable::CallError::CALL_OK; Array ref; ref.push_back(r_iter); Variant vref = ref; const Variant *refp[] = { &vref }; Variant ret = _get_obj().obj->callp(CoreStringNames::get_singleton()->_iter_init, refp, 1, ce); if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) { valid = false; return false; } r_iter = ref[0]; return ret; } break; case STRING: { const String *str = reinterpret_cast(_data._mem); if (str->is_empty()) { return false; } r_iter = 0; return true; } break; case DICTIONARY: { const Dictionary *dic = reinterpret_cast(_data._mem); if (dic->is_empty()) { return false; } const Variant *next = dic->next(nullptr); r_iter = *next; return true; } break; case ARRAY: { const Array *arr = reinterpret_cast(_data._mem); if (arr->is_empty()) { return false; } r_iter = 0; return true; } break; case PACKED_BYTE_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); if (arr->size() == 0) { return false; } r_iter = 0; return true; } break; case PACKED_INT32_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); if (arr->size() == 0) { return false; } r_iter = 0; return true; } break; case PACKED_INT64_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); if (arr->size() == 0) { return false; } r_iter = 0; return true; } break; case PACKED_FLOAT32_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); if (arr->size() == 0) { return false; } r_iter = 0; return true; } break; case PACKED_FLOAT64_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); if (arr->size() == 0) { return false; } r_iter = 0; return true; } break; case PACKED_STRING_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); if (arr->size() == 0) { return false; } r_iter = 0; return true; } break; case PACKED_VECTOR2_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); if (arr->size() == 0) { return false; } r_iter = 0; return true; } break; case PACKED_VECTOR3_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); if (arr->size() == 0) { return false; } r_iter = 0; return true; } break; case PACKED_COLOR_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); if (arr->size() == 0) { return false; } r_iter = 0; return true; } break; default: { } } valid = false; return false; } bool Variant::iter_next(Variant &r_iter, bool &valid) const { valid = true; switch (type) { case INT: { int64_t idx = r_iter; idx++; if (idx >= _data._int) { return false; } r_iter = idx; return true; } break; case FLOAT: { int64_t idx = r_iter; idx++; if (idx >= _data._float) { return false; } r_iter = idx; return true; } break; case VECTOR2: { double to = reinterpret_cast(_data._mem)->y; double idx = r_iter; idx++; if (idx >= to) { return false; } r_iter = idx; return true; } break; case VECTOR2I: { int64_t to = reinterpret_cast(_data._mem)->y; int64_t idx = r_iter; idx++; if (idx >= to) { return false; } r_iter = idx; return true; } break; case VECTOR3: { double to = reinterpret_cast(_data._mem)->y; double step = reinterpret_cast(_data._mem)->z; double idx = r_iter; idx += step; if (step < 0 && idx <= to) { return false; } if (step > 0 && idx >= to) { return false; } r_iter = idx; return true; } break; case VECTOR3I: { int64_t to = reinterpret_cast(_data._mem)->y; int64_t step = reinterpret_cast(_data._mem)->z; int64_t idx = r_iter; idx += step; if (step < 0 && idx <= to) { return false; } if (step > 0 && idx >= to) { return false; } r_iter = idx; return true; } break; case OBJECT: { if (!_get_obj().obj) { valid = false; return false; } #ifdef DEBUG_ENABLED if (EngineDebugger::is_active() && !_get_obj().id.is_ref_counted() && ObjectDB::get_instance(_get_obj().id) == nullptr) { valid = false; return false; } #endif Callable::CallError ce; ce.error = Callable::CallError::CALL_OK; Array ref; ref.push_back(r_iter); Variant vref = ref; const Variant *refp[] = { &vref }; Variant ret = _get_obj().obj->callp(CoreStringNames::get_singleton()->_iter_next, refp, 1, ce); if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) { valid = false; return false; } r_iter = ref[0]; return ret; } break; case STRING: { const String *str = reinterpret_cast(_data._mem); int idx = r_iter; idx++; if (idx >= str->length()) { return false; } r_iter = idx; return true; } break; case DICTIONARY: { const Dictionary *dic = reinterpret_cast(_data._mem); const Variant *next = dic->next(&r_iter); if (!next) { return false; } r_iter = *next; return true; } break; case ARRAY: { const Array *arr = reinterpret_cast(_data._mem); int idx = r_iter; idx++; if (idx >= arr->size()) { return false; } r_iter = idx; return true; } break; case PACKED_BYTE_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; idx++; if (idx >= arr->size()) { return false; } r_iter = idx; return true; } break; case PACKED_INT32_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int32_t idx = r_iter; idx++; if (idx >= arr->size()) { return false; } r_iter = idx; return true; } break; case PACKED_INT64_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int64_t idx = r_iter; idx++; if (idx >= arr->size()) { return false; } r_iter = idx; return true; } break; case PACKED_FLOAT32_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; idx++; if (idx >= arr->size()) { return false; } r_iter = idx; return true; } break; case PACKED_FLOAT64_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; idx++; if (idx >= arr->size()) { return false; } r_iter = idx; return true; } break; case PACKED_STRING_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; idx++; if (idx >= arr->size()) { return false; } r_iter = idx; return true; } break; case PACKED_VECTOR2_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; idx++; if (idx >= arr->size()) { return false; } r_iter = idx; return true; } break; case PACKED_VECTOR3_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; idx++; if (idx >= arr->size()) { return false; } r_iter = idx; return true; } break; case PACKED_COLOR_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; idx++; if (idx >= arr->size()) { return false; } r_iter = idx; return true; } break; default: { } } valid = false; return false; } Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const { r_valid = true; switch (type) { case INT: { return r_iter; } break; case FLOAT: { return r_iter; } break; case VECTOR2: { return r_iter; } break; case VECTOR2I: { return r_iter; } break; case VECTOR3: { return r_iter; } break; case VECTOR3I: { return r_iter; } break; case OBJECT: { if (!_get_obj().obj) { r_valid = false; return Variant(); } #ifdef DEBUG_ENABLED if (EngineDebugger::is_active() && !_get_obj().id.is_ref_counted() && ObjectDB::get_instance(_get_obj().id) == nullptr) { r_valid = false; return Variant(); } #endif Callable::CallError ce; ce.error = Callable::CallError::CALL_OK; const Variant *refp[] = { &r_iter }; Variant ret = _get_obj().obj->callp(CoreStringNames::get_singleton()->_iter_get, refp, 1, ce); if (ce.error != Callable::CallError::CALL_OK) { r_valid = false; return Variant(); } //r_iter=ref[0]; return ret; } break; case STRING: { const String *str = reinterpret_cast(_data._mem); return str->substr(r_iter, 1); } break; case DICTIONARY: { return r_iter; //iterator is the same as the key } break; case ARRAY: { const Array *arr = reinterpret_cast(_data._mem); int idx = r_iter; #ifdef DEBUG_ENABLED if (idx < 0 || idx >= arr->size()) { r_valid = false; return Variant(); } #endif return arr->get(idx); } break; case PACKED_BYTE_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; #ifdef DEBUG_ENABLED if (idx < 0 || idx >= arr->size()) { r_valid = false; return Variant(); } #endif return arr->get(idx); } break; case PACKED_INT32_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int32_t idx = r_iter; #ifdef DEBUG_ENABLED if (idx < 0 || idx >= arr->size()) { r_valid = false; return Variant(); } #endif return arr->get(idx); } break; case PACKED_INT64_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int64_t idx = r_iter; #ifdef DEBUG_ENABLED if (idx < 0 || idx >= arr->size()) { r_valid = false; return Variant(); } #endif return arr->get(idx); } break; case PACKED_FLOAT32_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; #ifdef DEBUG_ENABLED if (idx < 0 || idx >= arr->size()) { r_valid = false; return Variant(); } #endif return arr->get(idx); } break; case PACKED_FLOAT64_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; #ifdef DEBUG_ENABLED if (idx < 0 || idx >= arr->size()) { r_valid = false; return Variant(); } #endif return arr->get(idx); } break; case PACKED_STRING_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; #ifdef DEBUG_ENABLED if (idx < 0 || idx >= arr->size()) { r_valid = false; return Variant(); } #endif return arr->get(idx); } break; case PACKED_VECTOR2_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; #ifdef DEBUG_ENABLED if (idx < 0 || idx >= arr->size()) { r_valid = false; return Variant(); } #endif return arr->get(idx); } break; case PACKED_VECTOR3_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; #ifdef DEBUG_ENABLED if (idx < 0 || idx >= arr->size()) { r_valid = false; return Variant(); } #endif return arr->get(idx); } break; case PACKED_COLOR_ARRAY: { const Vector *arr = &PackedArrayRef::get_array(_data.packed_array); int idx = r_iter; #ifdef DEBUG_ENABLED if (idx < 0 || idx >= arr->size()) { r_valid = false; return Variant(); } #endif return arr->get(idx); } break; default: { } } r_valid = false; return Variant(); } Variant Variant::duplicate(bool p_deep) const { return recursive_duplicate(p_deep, 0); } Variant Variant::recursive_duplicate(bool p_deep, int recursion_count) const { switch (type) { case OBJECT: { /* breaks stuff :( if (p_deep && !_get_obj().ref.is_null()) { Ref resource = _get_obj().ref; if (resource.is_valid()) { return resource->duplicate(true); } } */ return *this; } break; case DICTIONARY: return operator Dictionary().recursive_duplicate(p_deep, recursion_count); case ARRAY: return operator Array().recursive_duplicate(p_deep, recursion_count); case PACKED_BYTE_ARRAY: return operator Vector().duplicate(); case PACKED_INT32_ARRAY: return operator Vector().duplicate(); case PACKED_INT64_ARRAY: return operator Vector().duplicate(); case PACKED_FLOAT32_ARRAY: return operator Vector().duplicate(); case PACKED_FLOAT64_ARRAY: return operator Vector().duplicate(); case PACKED_STRING_ARRAY: return operator Vector().duplicate(); case PACKED_VECTOR2_ARRAY: return operator Vector().duplicate(); case PACKED_VECTOR3_ARRAY: return operator Vector().duplicate(); case PACKED_COLOR_ARRAY: return operator Vector().duplicate(); default: return *this; } } void Variant::sub(const Variant &a, const Variant &b, Variant &r_dst) { if (a.type != b.type) { return; } switch (a.type) { case NIL: { r_dst = Variant(); } return; case INT: { int64_t va = a._data._int; int64_t vb = b._data._int; r_dst = int(va - vb); } return; case FLOAT: { double ra = a._data._float; double rb = b._data._float; r_dst = ra - rb; } return; case VECTOR2: { r_dst = *reinterpret_cast(a._data._mem) - *reinterpret_cast(b._data._mem); } return; case VECTOR2I: { int32_t vax = reinterpret_cast(a._data._mem)->x; int32_t vbx = reinterpret_cast(b._data._mem)->x; int32_t vay = reinterpret_cast(a._data._mem)->y; int32_t vby = reinterpret_cast(b._data._mem)->y; r_dst = Vector2i(int32_t(vax - vbx), int32_t(vay - vby)); } return; case RECT2: { const Rect2 *ra = reinterpret_cast(a._data._mem); const Rect2 *rb = reinterpret_cast(b._data._mem); r_dst = Rect2(ra->position - rb->position, ra->size - rb->size); } return; case RECT2I: { const Rect2i *ra = reinterpret_cast(a._data._mem); const Rect2i *rb = reinterpret_cast(b._data._mem); int32_t vax = ra->position.x; int32_t vay = ra->position.y; int32_t vbx = ra->size.x; int32_t vby = ra->size.y; int32_t vcx = rb->position.x; int32_t vcy = rb->position.y; int32_t vdx = rb->size.x; int32_t vdy = rb->size.y; r_dst = Rect2i(int32_t(vax - vbx), int32_t(vay - vby), int32_t(vcx - vdx), int32_t(vcy - vdy)); } return; case VECTOR3: { r_dst = *reinterpret_cast(a._data._mem) - *reinterpret_cast(b._data._mem); } return; case VECTOR3I: { int32_t vax = reinterpret_cast(a._data._mem)->x; int32_t vbx = reinterpret_cast(b._data._mem)->x; int32_t vay = reinterpret_cast(a._data._mem)->y; int32_t vby = reinterpret_cast(b._data._mem)->y; int32_t vaz = reinterpret_cast(a._data._mem)->z; int32_t vbz = reinterpret_cast(b._data._mem)->z; r_dst = Vector3i(int32_t(vax - vbx), int32_t(vay - vby), int32_t(vaz - vbz)); } return; case AABB: { const ::AABB *ra = reinterpret_cast(a._data._mem); const ::AABB *rb = reinterpret_cast(b._data._mem); r_dst = ::AABB(ra->position - rb->position, ra->size - rb->size); } return; case QUATERNION: { Quaternion empty_rot; const Quaternion *qa = reinterpret_cast(a._data._mem); const Quaternion *qb = reinterpret_cast(b._data._mem); r_dst = (*qb).inverse() * *qa; } return; case COLOR: { const Color *ca = reinterpret_cast(a._data._mem); const Color *cb = reinterpret_cast(b._data._mem); float new_r = ca->r - cb->r; float new_g = ca->g - cb->g; float new_b = ca->b - cb->b; float new_a = ca->a - cb->a; new_r = new_r > 1.0 ? 1.0 : new_r; new_g = new_g > 1.0 ? 1.0 : new_g; new_b = new_b > 1.0 ? 1.0 : new_b; new_a = new_a > 1.0 ? 1.0 : new_a; r_dst = Color(new_r, new_g, new_b, new_a); } return; default: { r_dst = a; } return; } } void Variant::blend(const Variant &a, const Variant &b, float c, Variant &r_dst) { if (a.type != b.type) { if (a.is_num() && b.is_num()) { real_t va = a; real_t vb = b; r_dst = va + vb * c; } else { r_dst = a; } return; } switch (a.type) { case NIL: { r_dst = Variant(); } return; case INT: { int64_t va = a._data._int; int64_t vb = b._data._int; r_dst = int(va + vb * c + 0.5); } return; case FLOAT: { double ra = a._data._float; double rb = b._data._float; r_dst = ra + rb * c; } return; case VECTOR2: { r_dst = *reinterpret_cast(a._data._mem) + *reinterpret_cast(b._data._mem) * c; } return; case VECTOR2I: { int32_t vax = reinterpret_cast(a._data._mem)->x; int32_t vbx = reinterpret_cast(b._data._mem)->x; int32_t vay = reinterpret_cast(a._data._mem)->y; int32_t vby = reinterpret_cast(b._data._mem)->y; r_dst = Vector2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5)); } return; case RECT2: { const Rect2 *ra = reinterpret_cast(a._data._mem); const Rect2 *rb = reinterpret_cast(b._data._mem); r_dst = Rect2(ra->position + rb->position * c, ra->size + rb->size * c); } return; case RECT2I: { const Rect2i *ra = reinterpret_cast(a._data._mem); const Rect2i *rb = reinterpret_cast(b._data._mem); int32_t vax = ra->position.x; int32_t vay = ra->position.y; int32_t vbx = ra->size.x; int32_t vby = ra->size.y; int32_t vcx = rb->position.x; int32_t vcy = rb->position.y; int32_t vdx = rb->size.x; int32_t vdy = rb->size.y; r_dst = Rect2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vcx + vdx * c + 0.5), int32_t(vcy + vdy * c + 0.5)); } return; case VECTOR3: { r_dst = *reinterpret_cast(a._data._mem) + *reinterpret_cast(b._data._mem) * c; } return; case VECTOR3I: { int32_t vax = reinterpret_cast(a._data._mem)->x; int32_t vbx = reinterpret_cast(b._data._mem)->x; int32_t vay = reinterpret_cast(a._data._mem)->y; int32_t vby = reinterpret_cast(b._data._mem)->y; int32_t vaz = reinterpret_cast(a._data._mem)->z; int32_t vbz = reinterpret_cast(b._data._mem)->z; r_dst = Vector3i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vaz + vbz * c + 0.5)); } return; case AABB: { const ::AABB *ra = reinterpret_cast(a._data._mem); const ::AABB *rb = reinterpret_cast(b._data._mem); r_dst = ::AABB(ra->position + rb->position * c, ra->size + rb->size * c); } return; case QUATERNION: { Quaternion empty_rot; const Quaternion *qa = reinterpret_cast(a._data._mem); const Quaternion *qb = reinterpret_cast(b._data._mem); r_dst = *qa * empty_rot.slerp(*qb, c); } return; case COLOR: { const Color *ca = reinterpret_cast(a._data._mem); const Color *cb = reinterpret_cast(b._data._mem); float new_r = ca->r + cb->r * c; float new_g = ca->g + cb->g * c; float new_b = ca->b + cb->b * c; float new_a = ca->a + cb->a * c; new_r = new_r > 1.0 ? 1.0 : new_r; new_g = new_g > 1.0 ? 1.0 : new_g; new_b = new_b > 1.0 ? 1.0 : new_b; new_a = new_a > 1.0 ? 1.0 : new_a; r_dst = Color(new_r, new_g, new_b, new_a); } return; default: { r_dst = c < 0.5 ? a : b; } return; } } void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst) { if (a.type != b.type) { if (a.is_num() && b.is_num()) { //not as efficient but.. real_t va = a; real_t vb = b; r_dst = va + (vb - va) * c; } else { r_dst = a; } return; } switch (a.type) { case NIL: { r_dst = Variant(); } return; case BOOL: { r_dst = a; } return; case INT: { int64_t va = a._data._int; int64_t vb = b._data._int; r_dst = int(va + (vb - va) * c); } return; case FLOAT: { real_t va = a._data._float; real_t vb = b._data._float; r_dst = va + (vb - va) * c; } return; case STRING: { //this is pretty funny and bizarre, but artists like to use it for typewriter effects String sa = *reinterpret_cast(a._data._mem); String sb = *reinterpret_cast(b._data._mem); String dst; int sa_len = sa.length(); int sb_len = sb.length(); int csize = sa_len + (sb_len - sa_len) * c; if (csize == 0) { r_dst = ""; return; } dst.resize(csize + 1); dst[csize] = 0; int split = csize / 2; for (int i = 0; i < csize; i++) { char32_t chr = ' '; if (i < split) { if (i < sa.length()) { chr = sa[i]; } else if (i < sb.length()) { chr = sb[i]; } } else { if (i < sb.length()) { chr = sb[i]; } else if (i < sa.length()) { chr = sa[i]; } } dst[i] = chr; } r_dst = dst; } return; case VECTOR2: { r_dst = reinterpret_cast(a._data._mem)->lerp(*reinterpret_cast(b._data._mem), c); } return; case VECTOR2I: { int32_t vax = reinterpret_cast(a._data._mem)->x; int32_t vbx = reinterpret_cast(b._data._mem)->x; int32_t vay = reinterpret_cast(a._data._mem)->y; int32_t vby = reinterpret_cast(b._data._mem)->y; r_dst = Vector2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5)); } return; case RECT2: { r_dst = Rect2(reinterpret_cast(a._data._mem)->position.lerp(reinterpret_cast(b._data._mem)->position, c), reinterpret_cast(a._data._mem)->size.lerp(reinterpret_cast(b._data._mem)->size, c)); } return; case RECT2I: { const Rect2i *ra = reinterpret_cast(a._data._mem); const Rect2i *rb = reinterpret_cast(b._data._mem); int32_t vax = ra->position.x; int32_t vay = ra->position.y; int32_t vbx = ra->size.x; int32_t vby = ra->size.y; int32_t vcx = rb->position.x; int32_t vcy = rb->position.y; int32_t vdx = rb->size.x; int32_t vdy = rb->size.y; r_dst = Rect2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vcx + vdx * c + 0.5), int32_t(vcy + vdy * c + 0.5)); } return; case VECTOR3: { r_dst = reinterpret_cast(a._data._mem)->lerp(*reinterpret_cast(b._data._mem), c); } return; case VECTOR3I: { int32_t vax = reinterpret_cast(a._data._mem)->x; int32_t vbx = reinterpret_cast(b._data._mem)->x; int32_t vay = reinterpret_cast(a._data._mem)->y; int32_t vby = reinterpret_cast(b._data._mem)->y; int32_t vaz = reinterpret_cast(a._data._mem)->z; int32_t vbz = reinterpret_cast(b._data._mem)->z; r_dst = Vector3i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vaz + vbz * c + 0.5)); } return; case TRANSFORM2D: { r_dst = a._data._transform2d->interpolate_with(*b._data._transform2d, c); } return; case PLANE: { r_dst = a; } return; case QUATERNION: { r_dst = reinterpret_cast(a._data._mem)->slerp(*reinterpret_cast(b._data._mem), c); } return; case AABB: { r_dst = ::AABB(a._data._aabb->position.lerp(b._data._aabb->position, c), a._data._aabb->size.lerp(b._data._aabb->size, c)); } return; case BASIS: { r_dst = a._data._basis->lerp(*b._data._basis, c); } return; case TRANSFORM3D: { r_dst = a._data._transform3d->interpolate_with(*b._data._transform3d, c); } return; case COLOR: { r_dst = reinterpret_cast(a._data._mem)->lerp(*reinterpret_cast(b._data._mem), c); } return; case STRING_NAME: { r_dst = a; } return; case NODE_PATH: { r_dst = a; } return; case RID: { r_dst = a; } return; case OBJECT: { r_dst = a; } return; case DICTIONARY: { } return; case ARRAY: { r_dst = a; } return; case PACKED_BYTE_ARRAY: { r_dst = a; } return; case PACKED_INT32_ARRAY: { const Vector *arr_a = &PackedArrayRef::get_array(a._data.packed_array); const Vector *arr_b = &PackedArrayRef::get_array(b._data.packed_array); int32_t sz = arr_a->size(); if (sz == 0 || arr_b->size() != sz) { r_dst = a; } else { Vector v; v.resize(sz); { int32_t *vw = v.ptrw(); const int32_t *ar = arr_a->ptr(); const int32_t *br = arr_b->ptr(); Variant va; for (int32_t i = 0; i < sz; i++) { Variant::interpolate(ar[i], br[i], c, va); vw[i] = va; } } r_dst = v; } } return; case PACKED_INT64_ARRAY: { const Vector *arr_a = &PackedArrayRef::get_array(a._data.packed_array); const Vector *arr_b = &PackedArrayRef::get_array(b._data.packed_array); int64_t sz = arr_a->size(); if (sz == 0 || arr_b->size() != sz) { r_dst = a; } else { Vector v; v.resize(sz); { int64_t *vw = v.ptrw(); const int64_t *ar = arr_a->ptr(); const int64_t *br = arr_b->ptr(); Variant va; for (int64_t i = 0; i < sz; i++) { Variant::interpolate(ar[i], br[i], c, va); vw[i] = va; } } r_dst = v; } } return; case PACKED_FLOAT32_ARRAY: { const Vector *arr_a = &PackedArrayRef::get_array(a._data.packed_array); const Vector *arr_b = &PackedArrayRef::get_array(b._data.packed_array); int sz = arr_a->size(); if (sz == 0 || arr_b->size() != sz) { r_dst = a; } else { Vector v; v.resize(sz); { float *vw = v.ptrw(); const float *ar = arr_a->ptr(); const float *br = arr_b->ptr(); Variant va; for (int i = 0; i < sz; i++) { Variant::interpolate(ar[i], br[i], c, va); vw[i] = va; } } r_dst = v; } } return; case PACKED_FLOAT64_ARRAY: { const Vector *arr_a = &PackedArrayRef::get_array(a._data.packed_array); const Vector *arr_b = &PackedArrayRef::get_array(b._data.packed_array); int sz = arr_a->size(); if (sz == 0 || arr_b->size() != sz) { r_dst = a; } else { Vector v; v.resize(sz); { double *vw = v.ptrw(); const double *ar = arr_a->ptr(); const double *br = arr_b->ptr(); Variant va; for (int i = 0; i < sz; i++) { Variant::interpolate(ar[i], br[i], c, va); vw[i] = va; } } r_dst = v; } } return; case PACKED_STRING_ARRAY: { r_dst = a; } return; case PACKED_VECTOR2_ARRAY: { const Vector *arr_a = &PackedArrayRef::get_array(a._data.packed_array); const Vector *arr_b = &PackedArrayRef::get_array(b._data.packed_array); int sz = arr_a->size(); if (sz == 0 || arr_b->size() != sz) { r_dst = a; } else { Vector v; v.resize(sz); { Vector2 *vw = v.ptrw(); const Vector2 *ar = arr_a->ptr(); const Vector2 *br = arr_b->ptr(); for (int i = 0; i < sz; i++) { vw[i] = ar[i].lerp(br[i], c); } } r_dst = v; } } return; case PACKED_VECTOR3_ARRAY: { const Vector *arr_a = &PackedArrayRef::get_array(a._data.packed_array); const Vector *arr_b = &PackedArrayRef::get_array(b._data.packed_array); int sz = arr_a->size(); if (sz == 0 || arr_b->size() != sz) { r_dst = a; } else { Vector v; v.resize(sz); { Vector3 *vw = v.ptrw(); const Vector3 *ar = arr_a->ptr(); const Vector3 *br = arr_b->ptr(); for (int i = 0; i < sz; i++) { vw[i] = ar[i].lerp(br[i], c); } } r_dst = v; } } return; case PACKED_COLOR_ARRAY: { const Vector *arr_a = &PackedArrayRef::get_array(a._data.packed_array); const Vector *arr_b = &PackedArrayRef::get_array(b._data.packed_array); int sz = arr_a->size(); if (sz == 0 || arr_b->size() != sz) { r_dst = a; } else { Vector v; v.resize(sz); { Color *vw = v.ptrw(); const Color *ar = arr_a->ptr(); const Color *br = arr_b->ptr(); for (int i = 0; i < sz; i++) { vw[i] = ar[i].lerp(br[i], c); } } r_dst = v; } } return; default: { r_dst = a; } } } void Variant::_register_variant_setters_getters() { register_named_setters_getters(); register_indexed_setters_getters(); register_keyed_setters_getters(); } void Variant::_unregister_variant_setters_getters() { unregister_named_setters_getters(); unregister_indexed_setters_getters(); }