From 1a7201e93893bb57379e572e0370c77b3476198f Mon Sep 17 00:00:00 2001 From: Relintai Date: Sun, 7 Jan 2024 10:56:15 +0100 Subject: [PATCH] Make the doc generator use the lite version. --- tools/doc/compile_linux.sh | 8 +- tools/doc/main.cpp | 2 +- tools/doc/sfwl.cpp | 26108 ++++++++++++++++++++++++++ tools/doc/sfwl.h | 18718 ++++++++++++++++++ tools/merger/sfwl/sfwl_full.cpp.inl | 176 +- tools/merger/sfwl/sfwl_full.h.inl | 4 +- 6 files changed, 44835 insertions(+), 181 deletions(-) create mode 100644 tools/doc/sfwl.cpp create mode 100644 tools/doc/sfwl.h diff --git a/tools/doc/compile_linux.sh b/tools/doc/compile_linux.sh index 03290e2..7945699 100755 --- a/tools/doc/compile_linux.sh +++ b/tools/doc/compile_linux.sh @@ -1,11 +1,11 @@ -cp -u ../merger/out/core/sfw.h sfw.h -cp -u ../merger/out/core/sfw.cpp sfw.cpp +cp -u ../merger/out/sfwl_full/sfwl.h sfwl.h +cp -u ../merger/out/sfwl_full/sfwl.cpp sfwl.cpp -ccache g++ -Wall -g -c sfw.cpp -o sfw.o +ccache g++ -Wall -g -c sfwl.cpp -o sfwl.o ccache g++ -Wall -g -c main.cpp -o main.o #-static-libgcc -static-libstdc++ -ccache g++ -Wall -lm -ldl -lpthread -lX11 -g sfw.o main.o -o game +ccache g++ -Wall -lm -ldl -lpthread -lX11 -g sfwl.o main.o -o game diff --git a/tools/doc/main.cpp b/tools/doc/main.cpp index e0b3ece..3202e13 100644 --- a/tools/doc/main.cpp +++ b/tools/doc/main.cpp @@ -1,5 +1,5 @@ -#include "sfw.h" +#include "sfwl.h" void print_list(const List &list) { for (const List::Element *E = list.front(); E; E = E->next()) { diff --git a/tools/doc/sfwl.cpp b/tools/doc/sfwl.cpp new file mode 100644 index 0000000..70476a9 --- /dev/null +++ b/tools/doc/sfwl.cpp @@ -0,0 +1,26108 @@ + + + +// https://github.com/Relintai/sfw + + +/* +Copyright (c) 2023-present Péter Magyar. + +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. +*/ + +// TODO gather all licenses to here + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SFWL_H +#include "sfwl.h" +#endif + +//=================== CORE SECTION =================== + +#line 1 "sfwl/core/ucaps.h" +#ifndef UCAPS_H +#define UCAPS_H + +/*************************************************************************/ +/* ucaps.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +//satan invented unicode? +#define CAPS_LEN 666 + +static const int caps_table[CAPS_LEN][2] = { + { 0x0061, 0x0041 }, + { 0x0062, 0x0042 }, + { 0x0063, 0x0043 }, + { 0x0064, 0x0044 }, + { 0x0065, 0x0045 }, + { 0x0066, 0x0046 }, + { 0x0067, 0x0047 }, + { 0x0068, 0x0048 }, + { 0x0069, 0x0049 }, + { 0x006A, 0x004A }, + { 0x006B, 0x004B }, + { 0x006C, 0x004C }, + { 0x006D, 0x004D }, + { 0x006E, 0x004E }, + { 0x006F, 0x004F }, + { 0x0070, 0x0050 }, + { 0x0071, 0x0051 }, + { 0x0072, 0x0052 }, + { 0x0073, 0x0053 }, + { 0x0074, 0x0054 }, + { 0x0075, 0x0055 }, + { 0x0076, 0x0056 }, + { 0x0077, 0x0057 }, + { 0x0078, 0x0058 }, + { 0x0079, 0x0059 }, + { 0x007A, 0x005A }, + { 0x00E0, 0x00C0 }, + { 0x00E1, 0x00C1 }, + { 0x00E2, 0x00C2 }, + { 0x00E3, 0x00C3 }, + { 0x00E4, 0x00C4 }, + { 0x00E5, 0x00C5 }, + { 0x00E6, 0x00C6 }, + { 0x00E7, 0x00C7 }, + { 0x00E8, 0x00C8 }, + { 0x00E9, 0x00C9 }, + { 0x00EA, 0x00CA }, + { 0x00EB, 0x00CB }, + { 0x00EC, 0x00CC }, + { 0x00ED, 0x00CD }, + { 0x00EE, 0x00CE }, + { 0x00EF, 0x00CF }, + { 0x00F0, 0x00D0 }, + { 0x00F1, 0x00D1 }, + { 0x00F2, 0x00D2 }, + { 0x00F3, 0x00D3 }, + { 0x00F4, 0x00D4 }, + { 0x00F5, 0x00D5 }, + { 0x00F6, 0x00D6 }, + { 0x00F8, 0x00D8 }, + { 0x00F9, 0x00D9 }, + { 0x00FA, 0x00DA }, + { 0x00FB, 0x00DB }, + { 0x00FC, 0x00DC }, + { 0x00FD, 0x00DD }, + { 0x00FE, 0x00DE }, + { 0x00FF, 0x0178 }, + { 0x0101, 0x0100 }, + { 0x0103, 0x0102 }, + { 0x0105, 0x0104 }, + { 0x0107, 0x0106 }, + { 0x0109, 0x0108 }, + { 0x010B, 0x010A }, + { 0x010D, 0x010C }, + { 0x010F, 0x010E }, + { 0x0111, 0x0110 }, + { 0x0113, 0x0112 }, + { 0x0115, 0x0114 }, + { 0x0117, 0x0116 }, + { 0x0119, 0x0118 }, + { 0x011B, 0x011A }, + { 0x011D, 0x011C }, + { 0x011F, 0x011E }, + { 0x0121, 0x0120 }, + { 0x0123, 0x0122 }, + { 0x0125, 0x0124 }, + { 0x0127, 0x0126 }, + { 0x0129, 0x0128 }, + { 0x012B, 0x012A }, + { 0x012D, 0x012C }, + { 0x012F, 0x012E }, + { 0x0131, 0x0049 }, + { 0x0133, 0x0132 }, + { 0x0135, 0x0134 }, + { 0x0137, 0x0136 }, + { 0x013A, 0x0139 }, + { 0x013C, 0x013B }, + { 0x013E, 0x013D }, + { 0x0140, 0x013F }, + { 0x0142, 0x0141 }, + { 0x0144, 0x0143 }, + { 0x0146, 0x0145 }, + { 0x0148, 0x0147 }, + { 0x014B, 0x014A }, + { 0x014D, 0x014C }, + { 0x014F, 0x014E }, + { 0x0151, 0x0150 }, + { 0x0153, 0x0152 }, + { 0x0155, 0x0154 }, + { 0x0157, 0x0156 }, + { 0x0159, 0x0158 }, + { 0x015B, 0x015A }, + { 0x015D, 0x015C }, + { 0x015F, 0x015E }, + { 0x0161, 0x0160 }, + { 0x0163, 0x0162 }, + { 0x0165, 0x0164 }, + { 0x0167, 0x0166 }, + { 0x0169, 0x0168 }, + { 0x016B, 0x016A }, + { 0x016D, 0x016C }, + { 0x016F, 0x016E }, + { 0x0171, 0x0170 }, + { 0x0173, 0x0172 }, + { 0x0175, 0x0174 }, + { 0x0177, 0x0176 }, + { 0x017A, 0x0179 }, + { 0x017C, 0x017B }, + { 0x017E, 0x017D }, + { 0x0183, 0x0182 }, + { 0x0185, 0x0184 }, + { 0x0188, 0x0187 }, + { 0x018C, 0x018B }, + { 0x0192, 0x0191 }, + { 0x0199, 0x0198 }, + { 0x01A1, 0x01A0 }, + { 0x01A3, 0x01A2 }, + { 0x01A5, 0x01A4 }, + { 0x01A8, 0x01A7 }, + { 0x01AD, 0x01AC }, + { 0x01B0, 0x01AF }, + { 0x01B4, 0x01B3 }, + { 0x01B6, 0x01B5 }, + { 0x01B9, 0x01B8 }, + { 0x01BD, 0x01BC }, + { 0x01C6, 0x01C4 }, + { 0x01C9, 0x01C7 }, + { 0x01CC, 0x01CA }, + { 0x01CE, 0x01CD }, + { 0x01D0, 0x01CF }, + { 0x01D2, 0x01D1 }, + { 0x01D4, 0x01D3 }, + { 0x01D6, 0x01D5 }, + { 0x01D8, 0x01D7 }, + { 0x01DA, 0x01D9 }, + { 0x01DC, 0x01DB }, + { 0x01DF, 0x01DE }, + { 0x01E1, 0x01E0 }, + { 0x01E3, 0x01E2 }, + { 0x01E5, 0x01E4 }, + { 0x01E7, 0x01E6 }, + { 0x01E9, 0x01E8 }, + { 0x01EB, 0x01EA }, + { 0x01ED, 0x01EC }, + { 0x01EF, 0x01EE }, + { 0x01F3, 0x01F1 }, + { 0x01F5, 0x01F4 }, + { 0x01FB, 0x01FA }, + { 0x01FD, 0x01FC }, + { 0x01FF, 0x01FE }, + { 0x0201, 0x0200 }, + { 0x0203, 0x0202 }, + { 0x0205, 0x0204 }, + { 0x0207, 0x0206 }, + { 0x0209, 0x0208 }, + { 0x020B, 0x020A }, + { 0x020D, 0x020C }, + { 0x020F, 0x020E }, + { 0x0211, 0x0210 }, + { 0x0213, 0x0212 }, + { 0x0215, 0x0214 }, + { 0x0217, 0x0216 }, + { 0x0253, 0x0181 }, + { 0x0254, 0x0186 }, + { 0x0257, 0x018A }, + { 0x0258, 0x018E }, + { 0x0259, 0x018F }, + { 0x025B, 0x0190 }, + { 0x0260, 0x0193 }, + { 0x0263, 0x0194 }, + { 0x0268, 0x0197 }, + { 0x0269, 0x0196 }, + { 0x026F, 0x019C }, + { 0x0272, 0x019D }, + { 0x0275, 0x019F }, + { 0x0283, 0x01A9 }, + { 0x0288, 0x01AE }, + { 0x028A, 0x01B1 }, + { 0x028B, 0x01B2 }, + { 0x0292, 0x01B7 }, + { 0x03AC, 0x0386 }, + { 0x03AD, 0x0388 }, + { 0x03AE, 0x0389 }, + { 0x03AF, 0x038A }, + { 0x03B1, 0x0391 }, + { 0x03B2, 0x0392 }, + { 0x03B3, 0x0393 }, + { 0x03B4, 0x0394 }, + { 0x03B5, 0x0395 }, + { 0x03B6, 0x0396 }, + { 0x03B7, 0x0397 }, + { 0x03B8, 0x0398 }, + { 0x03B9, 0x0399 }, + { 0x03BA, 0x039A }, + { 0x03BB, 0x039B }, + { 0x03BC, 0x039C }, + { 0x03BD, 0x039D }, + { 0x03BE, 0x039E }, + { 0x03BF, 0x039F }, + { 0x03C0, 0x03A0 }, + { 0x03C1, 0x03A1 }, + { 0x03C3, 0x03A3 }, + { 0x03C4, 0x03A4 }, + { 0x03C5, 0x03A5 }, + { 0x03C6, 0x03A6 }, + { 0x03C7, 0x03A7 }, + { 0x03C8, 0x03A8 }, + { 0x03C9, 0x03A9 }, + { 0x03CA, 0x03AA }, + { 0x03CB, 0x03AB }, + { 0x03CC, 0x038C }, + { 0x03CD, 0x038E }, + { 0x03CE, 0x038F }, + { 0x03E3, 0x03E2 }, + { 0x03E5, 0x03E4 }, + { 0x03E7, 0x03E6 }, + { 0x03E9, 0x03E8 }, + { 0x03EB, 0x03EA }, + { 0x03ED, 0x03EC }, + { 0x03EF, 0x03EE }, + { 0x0430, 0x0410 }, + { 0x0431, 0x0411 }, + { 0x0432, 0x0412 }, + { 0x0433, 0x0413 }, + { 0x0434, 0x0414 }, + { 0x0435, 0x0415 }, + { 0x0436, 0x0416 }, + { 0x0437, 0x0417 }, + { 0x0438, 0x0418 }, + { 0x0439, 0x0419 }, + { 0x043A, 0x041A }, + { 0x043B, 0x041B }, + { 0x043C, 0x041C }, + { 0x043D, 0x041D }, + { 0x043E, 0x041E }, + { 0x043F, 0x041F }, + { 0x0440, 0x0420 }, + { 0x0441, 0x0421 }, + { 0x0442, 0x0422 }, + { 0x0443, 0x0423 }, + { 0x0444, 0x0424 }, + { 0x0445, 0x0425 }, + { 0x0446, 0x0426 }, + { 0x0447, 0x0427 }, + { 0x0448, 0x0428 }, + { 0x0449, 0x0429 }, + { 0x044A, 0x042A }, + { 0x044B, 0x042B }, + { 0x044C, 0x042C }, + { 0x044D, 0x042D }, + { 0x044E, 0x042E }, + { 0x044F, 0x042F }, + { 0x0451, 0x0401 }, + { 0x0452, 0x0402 }, + { 0x0453, 0x0403 }, + { 0x0454, 0x0404 }, + { 0x0455, 0x0405 }, + { 0x0456, 0x0406 }, + { 0x0457, 0x0407 }, + { 0x0458, 0x0408 }, + { 0x0459, 0x0409 }, + { 0x045A, 0x040A }, + { 0x045B, 0x040B }, + { 0x045C, 0x040C }, + { 0x045E, 0x040E }, + { 0x045F, 0x040F }, + { 0x0461, 0x0460 }, + { 0x0463, 0x0462 }, + { 0x0465, 0x0464 }, + { 0x0467, 0x0466 }, + { 0x0469, 0x0468 }, + { 0x046B, 0x046A }, + { 0x046D, 0x046C }, + { 0x046F, 0x046E }, + { 0x0471, 0x0470 }, + { 0x0473, 0x0472 }, + { 0x0475, 0x0474 }, + { 0x0477, 0x0476 }, + { 0x0479, 0x0478 }, + { 0x047B, 0x047A }, + { 0x047D, 0x047C }, + { 0x047F, 0x047E }, + { 0x0481, 0x0480 }, + { 0x0491, 0x0490 }, + { 0x0493, 0x0492 }, + { 0x0495, 0x0494 }, + { 0x0497, 0x0496 }, + { 0x0499, 0x0498 }, + { 0x049B, 0x049A }, + { 0x049D, 0x049C }, + { 0x049F, 0x049E }, + { 0x04A1, 0x04A0 }, + { 0x04A3, 0x04A2 }, + { 0x04A5, 0x04A4 }, + { 0x04A7, 0x04A6 }, + { 0x04A9, 0x04A8 }, + { 0x04AB, 0x04AA }, + { 0x04AD, 0x04AC }, + { 0x04AF, 0x04AE }, + { 0x04B1, 0x04B0 }, + { 0x04B3, 0x04B2 }, + { 0x04B5, 0x04B4 }, + { 0x04B7, 0x04B6 }, + { 0x04B9, 0x04B8 }, + { 0x04BB, 0x04BA }, + { 0x04BD, 0x04BC }, + { 0x04BF, 0x04BE }, + { 0x04C2, 0x04C1 }, + { 0x04C4, 0x04C3 }, + { 0x04C8, 0x04C7 }, + { 0x04CC, 0x04CB }, + { 0x04D1, 0x04D0 }, + { 0x04D3, 0x04D2 }, + { 0x04D5, 0x04D4 }, + { 0x04D7, 0x04D6 }, + { 0x04D9, 0x04D8 }, + { 0x04DB, 0x04DA }, + { 0x04DD, 0x04DC }, + { 0x04DF, 0x04DE }, + { 0x04E1, 0x04E0 }, + { 0x04E3, 0x04E2 }, + { 0x04E5, 0x04E4 }, + { 0x04E7, 0x04E6 }, + { 0x04E9, 0x04E8 }, + { 0x04EB, 0x04EA }, + { 0x04EF, 0x04EE }, + { 0x04F1, 0x04F0 }, + { 0x04F3, 0x04F2 }, + { 0x04F5, 0x04F4 }, + { 0x04F9, 0x04F8 }, + { 0x0561, 0x0531 }, + { 0x0562, 0x0532 }, + { 0x0563, 0x0533 }, + { 0x0564, 0x0534 }, + { 0x0565, 0x0535 }, + { 0x0566, 0x0536 }, + { 0x0567, 0x0537 }, + { 0x0568, 0x0538 }, + { 0x0569, 0x0539 }, + { 0x056A, 0x053A }, + { 0x056B, 0x053B }, + { 0x056C, 0x053C }, + { 0x056D, 0x053D }, + { 0x056E, 0x053E }, + { 0x056F, 0x053F }, + { 0x0570, 0x0540 }, + { 0x0571, 0x0541 }, + { 0x0572, 0x0542 }, + { 0x0573, 0x0543 }, + { 0x0574, 0x0544 }, + { 0x0575, 0x0545 }, + { 0x0576, 0x0546 }, + { 0x0577, 0x0547 }, + { 0x0578, 0x0548 }, + { 0x0579, 0x0549 }, + { 0x057A, 0x054A }, + { 0x057B, 0x054B }, + { 0x057C, 0x054C }, + { 0x057D, 0x054D }, + { 0x057E, 0x054E }, + { 0x057F, 0x054F }, + { 0x0580, 0x0550 }, + { 0x0581, 0x0551 }, + { 0x0582, 0x0552 }, + { 0x0583, 0x0553 }, + { 0x0584, 0x0554 }, + { 0x0585, 0x0555 }, + { 0x0586, 0x0556 }, + { 0x10D0, 0x10A0 }, + { 0x10D1, 0x10A1 }, + { 0x10D2, 0x10A2 }, + { 0x10D3, 0x10A3 }, + { 0x10D4, 0x10A4 }, + { 0x10D5, 0x10A5 }, + { 0x10D6, 0x10A6 }, + { 0x10D7, 0x10A7 }, + { 0x10D8, 0x10A8 }, + { 0x10D9, 0x10A9 }, + { 0x10DA, 0x10AA }, + { 0x10DB, 0x10AB }, + { 0x10DC, 0x10AC }, + { 0x10DD, 0x10AD }, + { 0x10DE, 0x10AE }, + { 0x10DF, 0x10AF }, + { 0x10E0, 0x10B0 }, + { 0x10E1, 0x10B1 }, + { 0x10E2, 0x10B2 }, + { 0x10E3, 0x10B3 }, + { 0x10E4, 0x10B4 }, + { 0x10E5, 0x10B5 }, + { 0x10E6, 0x10B6 }, + { 0x10E7, 0x10B7 }, + { 0x10E8, 0x10B8 }, + { 0x10E9, 0x10B9 }, + { 0x10EA, 0x10BA }, + { 0x10EB, 0x10BB }, + { 0x10EC, 0x10BC }, + { 0x10ED, 0x10BD }, + { 0x10EE, 0x10BE }, + { 0x10EF, 0x10BF }, + { 0x10F0, 0x10C0 }, + { 0x10F1, 0x10C1 }, + { 0x10F2, 0x10C2 }, + { 0x10F3, 0x10C3 }, + { 0x10F4, 0x10C4 }, + { 0x10F5, 0x10C5 }, + { 0x1E01, 0x1E00 }, + { 0x1E03, 0x1E02 }, + { 0x1E05, 0x1E04 }, + { 0x1E07, 0x1E06 }, + { 0x1E09, 0x1E08 }, + { 0x1E0B, 0x1E0A }, + { 0x1E0D, 0x1E0C }, + { 0x1E0F, 0x1E0E }, + { 0x1E11, 0x1E10 }, + { 0x1E13, 0x1E12 }, + { 0x1E15, 0x1E14 }, + { 0x1E17, 0x1E16 }, + { 0x1E19, 0x1E18 }, + { 0x1E1B, 0x1E1A }, + { 0x1E1D, 0x1E1C }, + { 0x1E1F, 0x1E1E }, + { 0x1E21, 0x1E20 }, + { 0x1E23, 0x1E22 }, + { 0x1E25, 0x1E24 }, + { 0x1E27, 0x1E26 }, + { 0x1E29, 0x1E28 }, + { 0x1E2B, 0x1E2A }, + { 0x1E2D, 0x1E2C }, + { 0x1E2F, 0x1E2E }, + { 0x1E31, 0x1E30 }, + { 0x1E33, 0x1E32 }, + { 0x1E35, 0x1E34 }, + { 0x1E37, 0x1E36 }, + { 0x1E39, 0x1E38 }, + { 0x1E3B, 0x1E3A }, + { 0x1E3D, 0x1E3C }, + { 0x1E3F, 0x1E3E }, + { 0x1E41, 0x1E40 }, + { 0x1E43, 0x1E42 }, + { 0x1E45, 0x1E44 }, + { 0x1E47, 0x1E46 }, + { 0x1E49, 0x1E48 }, + { 0x1E4B, 0x1E4A }, + { 0x1E4D, 0x1E4C }, + { 0x1E4F, 0x1E4E }, + { 0x1E51, 0x1E50 }, + { 0x1E53, 0x1E52 }, + { 0x1E55, 0x1E54 }, + { 0x1E57, 0x1E56 }, + { 0x1E59, 0x1E58 }, + { 0x1E5B, 0x1E5A }, + { 0x1E5D, 0x1E5C }, + { 0x1E5F, 0x1E5E }, + { 0x1E61, 0x1E60 }, + { 0x1E63, 0x1E62 }, + { 0x1E65, 0x1E64 }, + { 0x1E67, 0x1E66 }, + { 0x1E69, 0x1E68 }, + { 0x1E6B, 0x1E6A }, + { 0x1E6D, 0x1E6C }, + { 0x1E6F, 0x1E6E }, + { 0x1E71, 0x1E70 }, + { 0x1E73, 0x1E72 }, + { 0x1E75, 0x1E74 }, + { 0x1E77, 0x1E76 }, + { 0x1E79, 0x1E78 }, + { 0x1E7B, 0x1E7A }, + { 0x1E7D, 0x1E7C }, + { 0x1E7F, 0x1E7E }, + { 0x1E81, 0x1E80 }, + { 0x1E83, 0x1E82 }, + { 0x1E85, 0x1E84 }, + { 0x1E87, 0x1E86 }, + { 0x1E89, 0x1E88 }, + { 0x1E8B, 0x1E8A }, + { 0x1E8D, 0x1E8C }, + { 0x1E8F, 0x1E8E }, + { 0x1E91, 0x1E90 }, + { 0x1E93, 0x1E92 }, + { 0x1E95, 0x1E94 }, + { 0x1EA1, 0x1EA0 }, + { 0x1EA3, 0x1EA2 }, + { 0x1EA5, 0x1EA4 }, + { 0x1EA7, 0x1EA6 }, + { 0x1EA9, 0x1EA8 }, + { 0x1EAB, 0x1EAA }, + { 0x1EAD, 0x1EAC }, + { 0x1EAF, 0x1EAE }, + { 0x1EB1, 0x1EB0 }, + { 0x1EB3, 0x1EB2 }, + { 0x1EB5, 0x1EB4 }, + { 0x1EB7, 0x1EB6 }, + { 0x1EB9, 0x1EB8 }, + { 0x1EBB, 0x1EBA }, + { 0x1EBD, 0x1EBC }, + { 0x1EBF, 0x1EBE }, + { 0x1EC1, 0x1EC0 }, + { 0x1EC3, 0x1EC2 }, + { 0x1EC5, 0x1EC4 }, + { 0x1EC7, 0x1EC6 }, + { 0x1EC9, 0x1EC8 }, + { 0x1ECB, 0x1ECA }, + { 0x1ECD, 0x1ECC }, + { 0x1ECF, 0x1ECE }, + { 0x1ED1, 0x1ED0 }, + { 0x1ED3, 0x1ED2 }, + { 0x1ED5, 0x1ED4 }, + { 0x1ED7, 0x1ED6 }, + { 0x1ED9, 0x1ED8 }, + { 0x1EDB, 0x1EDA }, + { 0x1EDD, 0x1EDC }, + { 0x1EDF, 0x1EDE }, + { 0x1EE1, 0x1EE0 }, + { 0x1EE3, 0x1EE2 }, + { 0x1EE5, 0x1EE4 }, + { 0x1EE7, 0x1EE6 }, + { 0x1EE9, 0x1EE8 }, + { 0x1EEB, 0x1EEA }, + { 0x1EED, 0x1EEC }, + { 0x1EEF, 0x1EEE }, + { 0x1EF1, 0x1EF0 }, + { 0x1EF3, 0x1EF2 }, + { 0x1EF5, 0x1EF4 }, + { 0x1EF7, 0x1EF6 }, + { 0x1EF9, 0x1EF8 }, + { 0x1F00, 0x1F08 }, + { 0x1F01, 0x1F09 }, + { 0x1F02, 0x1F0A }, + { 0x1F03, 0x1F0B }, + { 0x1F04, 0x1F0C }, + { 0x1F05, 0x1F0D }, + { 0x1F06, 0x1F0E }, + { 0x1F07, 0x1F0F }, + { 0x1F10, 0x1F18 }, + { 0x1F11, 0x1F19 }, + { 0x1F12, 0x1F1A }, + { 0x1F13, 0x1F1B }, + { 0x1F14, 0x1F1C }, + { 0x1F15, 0x1F1D }, + { 0x1F20, 0x1F28 }, + { 0x1F21, 0x1F29 }, + { 0x1F22, 0x1F2A }, + { 0x1F23, 0x1F2B }, + { 0x1F24, 0x1F2C }, + { 0x1F25, 0x1F2D }, + { 0x1F26, 0x1F2E }, + { 0x1F27, 0x1F2F }, + { 0x1F30, 0x1F38 }, + { 0x1F31, 0x1F39 }, + { 0x1F32, 0x1F3A }, + { 0x1F33, 0x1F3B }, + { 0x1F34, 0x1F3C }, + { 0x1F35, 0x1F3D }, + { 0x1F36, 0x1F3E }, + { 0x1F37, 0x1F3F }, + { 0x1F40, 0x1F48 }, + { 0x1F41, 0x1F49 }, + { 0x1F42, 0x1F4A }, + { 0x1F43, 0x1F4B }, + { 0x1F44, 0x1F4C }, + { 0x1F45, 0x1F4D }, + { 0x1F51, 0x1F59 }, + { 0x1F53, 0x1F5B }, + { 0x1F55, 0x1F5D }, + { 0x1F57, 0x1F5F }, + { 0x1F60, 0x1F68 }, + { 0x1F61, 0x1F69 }, + { 0x1F62, 0x1F6A }, + { 0x1F63, 0x1F6B }, + { 0x1F64, 0x1F6C }, + { 0x1F65, 0x1F6D }, + { 0x1F66, 0x1F6E }, + { 0x1F67, 0x1F6F }, + { 0x1F80, 0x1F88 }, + { 0x1F81, 0x1F89 }, + { 0x1F82, 0x1F8A }, + { 0x1F83, 0x1F8B }, + { 0x1F84, 0x1F8C }, + { 0x1F85, 0x1F8D }, + { 0x1F86, 0x1F8E }, + { 0x1F87, 0x1F8F }, + { 0x1F90, 0x1F98 }, + { 0x1F91, 0x1F99 }, + { 0x1F92, 0x1F9A }, + { 0x1F93, 0x1F9B }, + { 0x1F94, 0x1F9C }, + { 0x1F95, 0x1F9D }, + { 0x1F96, 0x1F9E }, + { 0x1F97, 0x1F9F }, + { 0x1FA0, 0x1FA8 }, + { 0x1FA1, 0x1FA9 }, + { 0x1FA2, 0x1FAA }, + { 0x1FA3, 0x1FAB }, + { 0x1FA4, 0x1FAC }, + { 0x1FA5, 0x1FAD }, + { 0x1FA6, 0x1FAE }, + { 0x1FA7, 0x1FAF }, + { 0x1FB0, 0x1FB8 }, + { 0x1FB1, 0x1FB9 }, + { 0x1FD0, 0x1FD8 }, + { 0x1FD1, 0x1FD9 }, + { 0x1FE0, 0x1FE8 }, + { 0x1FE1, 0x1FE9 }, + { 0x24D0, 0x24B6 }, + { 0x24D1, 0x24B7 }, + { 0x24D2, 0x24B8 }, + { 0x24D3, 0x24B9 }, + { 0x24D4, 0x24BA }, + { 0x24D5, 0x24BB }, + { 0x24D6, 0x24BC }, + { 0x24D7, 0x24BD }, + { 0x24D8, 0x24BE }, + { 0x24D9, 0x24BF }, + { 0x24DA, 0x24C0 }, + { 0x24DB, 0x24C1 }, + { 0x24DC, 0x24C2 }, + { 0x24DD, 0x24C3 }, + { 0x24DE, 0x24C4 }, + { 0x24DF, 0x24C5 }, + { 0x24E0, 0x24C6 }, + { 0x24E1, 0x24C7 }, + { 0x24E2, 0x24C8 }, + { 0x24E3, 0x24C9 }, + { 0x24E4, 0x24CA }, + { 0x24E5, 0x24CB }, + { 0x24E6, 0x24CC }, + { 0x24E7, 0x24CD }, + { 0x24E8, 0x24CE }, + { 0x24E9, 0x24CF }, + { 0xFF41, 0xFF21 }, + { 0xFF42, 0xFF22 }, + { 0xFF43, 0xFF23 }, + { 0xFF44, 0xFF24 }, + { 0xFF45, 0xFF25 }, + { 0xFF46, 0xFF26 }, + { 0xFF47, 0xFF27 }, + { 0xFF48, 0xFF28 }, + { 0xFF49, 0xFF29 }, + { 0xFF4A, 0xFF2A }, + { 0xFF4B, 0xFF2B }, + { 0xFF4C, 0xFF2C }, + { 0xFF4D, 0xFF2D }, + { 0xFF4E, 0xFF2E }, + { 0xFF4F, 0xFF2F }, + { 0xFF50, 0xFF30 }, + { 0xFF51, 0xFF31 }, + { 0xFF52, 0xFF32 }, + { 0xFF53, 0xFF33 }, + { 0xFF54, 0xFF34 }, + { 0xFF55, 0xFF35 }, + { 0xFF56, 0xFF36 }, + { 0xFF57, 0xFF37 }, + { 0xFF58, 0xFF38 }, + { 0xFF59, 0xFF39 }, + { 0xFF5A, 0xFF3A }, +}; + +static const int reverse_caps_table[CAPS_LEN - 1][2] = { + { 0x0041, 0x0061 }, + { 0x0042, 0x0062 }, + { 0x0043, 0x0063 }, + { 0x0044, 0x0064 }, + { 0x0045, 0x0065 }, + { 0x0046, 0x0066 }, + { 0x0047, 0x0067 }, + { 0x0048, 0x0068 }, + { 0x0049, 0x0069 }, + // { 0x0049, 0x0131 }, // dotless I + { 0x004A, 0x006A }, + { 0x004B, 0x006B }, + { 0x004C, 0x006C }, + { 0x004D, 0x006D }, + { 0x004E, 0x006E }, + { 0x004F, 0x006F }, + { 0x0050, 0x0070 }, + { 0x0051, 0x0071 }, + { 0x0052, 0x0072 }, + { 0x0053, 0x0073 }, + { 0x0054, 0x0074 }, + { 0x0055, 0x0075 }, + { 0x0056, 0x0076 }, + { 0x0057, 0x0077 }, + { 0x0058, 0x0078 }, + { 0x0059, 0x0079 }, + { 0x005A, 0x007A }, + { 0x00C0, 0x00E0 }, + { 0x00C1, 0x00E1 }, + { 0x00C2, 0x00E2 }, + { 0x00C3, 0x00E3 }, + { 0x00C4, 0x00E4 }, + { 0x00C5, 0x00E5 }, + { 0x00C6, 0x00E6 }, + { 0x00C7, 0x00E7 }, + { 0x00C8, 0x00E8 }, + { 0x00C9, 0x00E9 }, + { 0x00CA, 0x00EA }, + { 0x00CB, 0x00EB }, + { 0x00CC, 0x00EC }, + { 0x00CD, 0x00ED }, + { 0x00CE, 0x00EE }, + { 0x00CF, 0x00EF }, + { 0x00D0, 0x00F0 }, + { 0x00D1, 0x00F1 }, + { 0x00D2, 0x00F2 }, + { 0x00D3, 0x00F3 }, + { 0x00D4, 0x00F4 }, + { 0x00D5, 0x00F5 }, + { 0x00D6, 0x00F6 }, + { 0x00D8, 0x00F8 }, + { 0x00D9, 0x00F9 }, + { 0x00DA, 0x00FA }, + { 0x00DB, 0x00FB }, + { 0x00DC, 0x00FC }, + { 0x00DD, 0x00FD }, + { 0x00DE, 0x00FE }, + { 0x0100, 0x0101 }, + { 0x0102, 0x0103 }, + { 0x0104, 0x0105 }, + { 0x0106, 0x0107 }, + { 0x0108, 0x0109 }, + { 0x010A, 0x010B }, + { 0x010C, 0x010D }, + { 0x010E, 0x010F }, + { 0x0110, 0x0111 }, + { 0x0112, 0x0113 }, + { 0x0114, 0x0115 }, + { 0x0116, 0x0117 }, + { 0x0118, 0x0119 }, + { 0x011A, 0x011B }, + { 0x011C, 0x011D }, + { 0x011E, 0x011F }, + { 0x0120, 0x0121 }, + { 0x0122, 0x0123 }, + { 0x0124, 0x0125 }, + { 0x0126, 0x0127 }, + { 0x0128, 0x0129 }, + { 0x012A, 0x012B }, + { 0x012C, 0x012D }, + { 0x012E, 0x012F }, + { 0x0132, 0x0133 }, + { 0x0134, 0x0135 }, + { 0x0136, 0x0137 }, + { 0x0139, 0x013A }, + { 0x013B, 0x013C }, + { 0x013D, 0x013E }, + { 0x013F, 0x0140 }, + { 0x0141, 0x0142 }, + { 0x0143, 0x0144 }, + { 0x0145, 0x0146 }, + { 0x0147, 0x0148 }, + { 0x014A, 0x014B }, + { 0x014C, 0x014D }, + { 0x014E, 0x014F }, + { 0x0150, 0x0151 }, + { 0x0152, 0x0153 }, + { 0x0154, 0x0155 }, + { 0x0156, 0x0157 }, + { 0x0158, 0x0159 }, + { 0x015A, 0x015B }, + { 0x015C, 0x015D }, + { 0x015E, 0x015F }, + { 0x0160, 0x0161 }, + { 0x0162, 0x0163 }, + { 0x0164, 0x0165 }, + { 0x0166, 0x0167 }, + { 0x0168, 0x0169 }, + { 0x016A, 0x016B }, + { 0x016C, 0x016D }, + { 0x016E, 0x016F }, + { 0x0170, 0x0171 }, + { 0x0172, 0x0173 }, + { 0x0174, 0x0175 }, + { 0x0176, 0x0177 }, + { 0x0178, 0x00FF }, + { 0x0179, 0x017A }, + { 0x017B, 0x017C }, + { 0x017D, 0x017E }, + { 0x0181, 0x0253 }, + { 0x0182, 0x0183 }, + { 0x0184, 0x0185 }, + { 0x0186, 0x0254 }, + { 0x0187, 0x0188 }, + { 0x018A, 0x0257 }, + { 0x018B, 0x018C }, + { 0x018E, 0x0258 }, + { 0x018F, 0x0259 }, + { 0x0190, 0x025B }, + { 0x0191, 0x0192 }, + { 0x0193, 0x0260 }, + { 0x0194, 0x0263 }, + { 0x0196, 0x0269 }, + { 0x0197, 0x0268 }, + { 0x0198, 0x0199 }, + { 0x019C, 0x026F }, + { 0x019D, 0x0272 }, + { 0x019F, 0x0275 }, + { 0x01A0, 0x01A1 }, + { 0x01A2, 0x01A3 }, + { 0x01A4, 0x01A5 }, + { 0x01A7, 0x01A8 }, + { 0x01A9, 0x0283 }, + { 0x01AC, 0x01AD }, + { 0x01AE, 0x0288 }, + { 0x01AF, 0x01B0 }, + { 0x01B1, 0x028A }, + { 0x01B2, 0x028B }, + { 0x01B3, 0x01B4 }, + { 0x01B5, 0x01B6 }, + { 0x01B7, 0x0292 }, + { 0x01B8, 0x01B9 }, + { 0x01BC, 0x01BD }, + { 0x01C4, 0x01C6 }, + { 0x01C7, 0x01C9 }, + { 0x01CA, 0x01CC }, + { 0x01CD, 0x01CE }, + { 0x01CF, 0x01D0 }, + { 0x01D1, 0x01D2 }, + { 0x01D3, 0x01D4 }, + { 0x01D5, 0x01D6 }, + { 0x01D7, 0x01D8 }, + { 0x01D9, 0x01DA }, + { 0x01DB, 0x01DC }, + { 0x01DE, 0x01DF }, + { 0x01E0, 0x01E1 }, + { 0x01E2, 0x01E3 }, + { 0x01E4, 0x01E5 }, + { 0x01E6, 0x01E7 }, + { 0x01E8, 0x01E9 }, + { 0x01EA, 0x01EB }, + { 0x01EC, 0x01ED }, + { 0x01EE, 0x01EF }, + { 0x01F1, 0x01F3 }, + { 0x01F4, 0x01F5 }, + { 0x01FA, 0x01FB }, + { 0x01FC, 0x01FD }, + { 0x01FE, 0x01FF }, + { 0x0200, 0x0201 }, + { 0x0202, 0x0203 }, + { 0x0204, 0x0205 }, + { 0x0206, 0x0207 }, + { 0x0208, 0x0209 }, + { 0x020A, 0x020B }, + { 0x020C, 0x020D }, + { 0x020E, 0x020F }, + { 0x0210, 0x0211 }, + { 0x0212, 0x0213 }, + { 0x0214, 0x0215 }, + { 0x0216, 0x0217 }, + { 0x0386, 0x03AC }, + { 0x0388, 0x03AD }, + { 0x0389, 0x03AE }, + { 0x038A, 0x03AF }, + { 0x038C, 0x03CC }, + { 0x038E, 0x03CD }, + { 0x038F, 0x03CE }, + { 0x0391, 0x03B1 }, + { 0x0392, 0x03B2 }, + { 0x0393, 0x03B3 }, + { 0x0394, 0x03B4 }, + { 0x0395, 0x03B5 }, + { 0x0396, 0x03B6 }, + { 0x0397, 0x03B7 }, + { 0x0398, 0x03B8 }, + { 0x0399, 0x03B9 }, + { 0x039A, 0x03BA }, + { 0x039B, 0x03BB }, + { 0x039C, 0x03BC }, + { 0x039D, 0x03BD }, + { 0x039E, 0x03BE }, + { 0x039F, 0x03BF }, + { 0x03A0, 0x03C0 }, + { 0x03A1, 0x03C1 }, + { 0x03A3, 0x03C3 }, + { 0x03A4, 0x03C4 }, + { 0x03A5, 0x03C5 }, + { 0x03A6, 0x03C6 }, + { 0x03A7, 0x03C7 }, + { 0x03A8, 0x03C8 }, + { 0x03A9, 0x03C9 }, + { 0x03AA, 0x03CA }, + { 0x03AB, 0x03CB }, + { 0x03E2, 0x03E3 }, + { 0x03E4, 0x03E5 }, + { 0x03E6, 0x03E7 }, + { 0x03E8, 0x03E9 }, + { 0x03EA, 0x03EB }, + { 0x03EC, 0x03ED }, + { 0x03EE, 0x03EF }, + { 0x0401, 0x0451 }, + { 0x0402, 0x0452 }, + { 0x0403, 0x0453 }, + { 0x0404, 0x0454 }, + { 0x0405, 0x0455 }, + { 0x0406, 0x0456 }, + { 0x0407, 0x0457 }, + { 0x0408, 0x0458 }, + { 0x0409, 0x0459 }, + { 0x040A, 0x045A }, + { 0x040B, 0x045B }, + { 0x040C, 0x045C }, + { 0x040E, 0x045E }, + { 0x040F, 0x045F }, + { 0x0410, 0x0430 }, + { 0x0411, 0x0431 }, + { 0x0412, 0x0432 }, + { 0x0413, 0x0433 }, + { 0x0414, 0x0434 }, + { 0x0415, 0x0435 }, + { 0x0416, 0x0436 }, + { 0x0417, 0x0437 }, + { 0x0418, 0x0438 }, + { 0x0419, 0x0439 }, + { 0x041A, 0x043A }, + { 0x041B, 0x043B }, + { 0x041C, 0x043C }, + { 0x041D, 0x043D }, + { 0x041E, 0x043E }, + { 0x041F, 0x043F }, + { 0x0420, 0x0440 }, + { 0x0421, 0x0441 }, + { 0x0422, 0x0442 }, + { 0x0423, 0x0443 }, + { 0x0424, 0x0444 }, + { 0x0425, 0x0445 }, + { 0x0426, 0x0446 }, + { 0x0427, 0x0447 }, + { 0x0428, 0x0448 }, + { 0x0429, 0x0449 }, + { 0x042A, 0x044A }, + { 0x042B, 0x044B }, + { 0x042C, 0x044C }, + { 0x042D, 0x044D }, + { 0x042E, 0x044E }, + { 0x042F, 0x044F }, + { 0x0460, 0x0461 }, + { 0x0462, 0x0463 }, + { 0x0464, 0x0465 }, + { 0x0466, 0x0467 }, + { 0x0468, 0x0469 }, + { 0x046A, 0x046B }, + { 0x046C, 0x046D }, + { 0x046E, 0x046F }, + { 0x0470, 0x0471 }, + { 0x0472, 0x0473 }, + { 0x0474, 0x0475 }, + { 0x0476, 0x0477 }, + { 0x0478, 0x0479 }, + { 0x047A, 0x047B }, + { 0x047C, 0x047D }, + { 0x047E, 0x047F }, + { 0x0480, 0x0481 }, + { 0x0490, 0x0491 }, + { 0x0492, 0x0493 }, + { 0x0494, 0x0495 }, + { 0x0496, 0x0497 }, + { 0x0498, 0x0499 }, + { 0x049A, 0x049B }, + { 0x049C, 0x049D }, + { 0x049E, 0x049F }, + { 0x04A0, 0x04A1 }, + { 0x04A2, 0x04A3 }, + { 0x04A4, 0x04A5 }, + { 0x04A6, 0x04A7 }, + { 0x04A8, 0x04A9 }, + { 0x04AA, 0x04AB }, + { 0x04AC, 0x04AD }, + { 0x04AE, 0x04AF }, + { 0x04B0, 0x04B1 }, + { 0x04B2, 0x04B3 }, + { 0x04B4, 0x04B5 }, + { 0x04B6, 0x04B7 }, + { 0x04B8, 0x04B9 }, + { 0x04BA, 0x04BB }, + { 0x04BC, 0x04BD }, + { 0x04BE, 0x04BF }, + { 0x04C1, 0x04C2 }, + { 0x04C3, 0x04C4 }, + { 0x04C7, 0x04C8 }, + { 0x04CB, 0x04CC }, + { 0x04D0, 0x04D1 }, + { 0x04D2, 0x04D3 }, + { 0x04D4, 0x04D5 }, + { 0x04D6, 0x04D7 }, + { 0x04D8, 0x04D9 }, + { 0x04DA, 0x04DB }, + { 0x04DC, 0x04DD }, + { 0x04DE, 0x04DF }, + { 0x04E0, 0x04E1 }, + { 0x04E2, 0x04E3 }, + { 0x04E4, 0x04E5 }, + { 0x04E6, 0x04E7 }, + { 0x04E8, 0x04E9 }, + { 0x04EA, 0x04EB }, + { 0x04EE, 0x04EF }, + { 0x04F0, 0x04F1 }, + { 0x04F2, 0x04F3 }, + { 0x04F4, 0x04F5 }, + { 0x04F8, 0x04F9 }, + { 0x0531, 0x0561 }, + { 0x0532, 0x0562 }, + { 0x0533, 0x0563 }, + { 0x0534, 0x0564 }, + { 0x0535, 0x0565 }, + { 0x0536, 0x0566 }, + { 0x0537, 0x0567 }, + { 0x0538, 0x0568 }, + { 0x0539, 0x0569 }, + { 0x053A, 0x056A }, + { 0x053B, 0x056B }, + { 0x053C, 0x056C }, + { 0x053D, 0x056D }, + { 0x053E, 0x056E }, + { 0x053F, 0x056F }, + { 0x0540, 0x0570 }, + { 0x0541, 0x0571 }, + { 0x0542, 0x0572 }, + { 0x0543, 0x0573 }, + { 0x0544, 0x0574 }, + { 0x0545, 0x0575 }, + { 0x0546, 0x0576 }, + { 0x0547, 0x0577 }, + { 0x0548, 0x0578 }, + { 0x0549, 0x0579 }, + { 0x054A, 0x057A }, + { 0x054B, 0x057B }, + { 0x054C, 0x057C }, + { 0x054D, 0x057D }, + { 0x054E, 0x057E }, + { 0x054F, 0x057F }, + { 0x0550, 0x0580 }, + { 0x0551, 0x0581 }, + { 0x0552, 0x0582 }, + { 0x0553, 0x0583 }, + { 0x0554, 0x0584 }, + { 0x0555, 0x0585 }, + { 0x0556, 0x0586 }, + { 0x10A0, 0x10D0 }, + { 0x10A1, 0x10D1 }, + { 0x10A2, 0x10D2 }, + { 0x10A3, 0x10D3 }, + { 0x10A4, 0x10D4 }, + { 0x10A5, 0x10D5 }, + { 0x10A6, 0x10D6 }, + { 0x10A7, 0x10D7 }, + { 0x10A8, 0x10D8 }, + { 0x10A9, 0x10D9 }, + { 0x10AA, 0x10DA }, + { 0x10AB, 0x10DB }, + { 0x10AC, 0x10DC }, + { 0x10AD, 0x10DD }, + { 0x10AE, 0x10DE }, + { 0x10AF, 0x10DF }, + { 0x10B0, 0x10E0 }, + { 0x10B1, 0x10E1 }, + { 0x10B2, 0x10E2 }, + { 0x10B3, 0x10E3 }, + { 0x10B4, 0x10E4 }, + { 0x10B5, 0x10E5 }, + { 0x10B6, 0x10E6 }, + { 0x10B7, 0x10E7 }, + { 0x10B8, 0x10E8 }, + { 0x10B9, 0x10E9 }, + { 0x10BA, 0x10EA }, + { 0x10BB, 0x10EB }, + { 0x10BC, 0x10EC }, + { 0x10BD, 0x10ED }, + { 0x10BE, 0x10EE }, + { 0x10BF, 0x10EF }, + { 0x10C0, 0x10F0 }, + { 0x10C1, 0x10F1 }, + { 0x10C2, 0x10F2 }, + { 0x10C3, 0x10F3 }, + { 0x10C4, 0x10F4 }, + { 0x10C5, 0x10F5 }, + { 0x1E00, 0x1E01 }, + { 0x1E02, 0x1E03 }, + { 0x1E04, 0x1E05 }, + { 0x1E06, 0x1E07 }, + { 0x1E08, 0x1E09 }, + { 0x1E0A, 0x1E0B }, + { 0x1E0C, 0x1E0D }, + { 0x1E0E, 0x1E0F }, + { 0x1E10, 0x1E11 }, + { 0x1E12, 0x1E13 }, + { 0x1E14, 0x1E15 }, + { 0x1E16, 0x1E17 }, + { 0x1E18, 0x1E19 }, + { 0x1E1A, 0x1E1B }, + { 0x1E1C, 0x1E1D }, + { 0x1E1E, 0x1E1F }, + { 0x1E20, 0x1E21 }, + { 0x1E22, 0x1E23 }, + { 0x1E24, 0x1E25 }, + { 0x1E26, 0x1E27 }, + { 0x1E28, 0x1E29 }, + { 0x1E2A, 0x1E2B }, + { 0x1E2C, 0x1E2D }, + { 0x1E2E, 0x1E2F }, + { 0x1E30, 0x1E31 }, + { 0x1E32, 0x1E33 }, + { 0x1E34, 0x1E35 }, + { 0x1E36, 0x1E37 }, + { 0x1E38, 0x1E39 }, + { 0x1E3A, 0x1E3B }, + { 0x1E3C, 0x1E3D }, + { 0x1E3E, 0x1E3F }, + { 0x1E40, 0x1E41 }, + { 0x1E42, 0x1E43 }, + { 0x1E44, 0x1E45 }, + { 0x1E46, 0x1E47 }, + { 0x1E48, 0x1E49 }, + { 0x1E4A, 0x1E4B }, + { 0x1E4C, 0x1E4D }, + { 0x1E4E, 0x1E4F }, + { 0x1E50, 0x1E51 }, + { 0x1E52, 0x1E53 }, + { 0x1E54, 0x1E55 }, + { 0x1E56, 0x1E57 }, + { 0x1E58, 0x1E59 }, + { 0x1E5A, 0x1E5B }, + { 0x1E5C, 0x1E5D }, + { 0x1E5E, 0x1E5F }, + { 0x1E60, 0x1E61 }, + { 0x1E62, 0x1E63 }, + { 0x1E64, 0x1E65 }, + { 0x1E66, 0x1E67 }, + { 0x1E68, 0x1E69 }, + { 0x1E6A, 0x1E6B }, + { 0x1E6C, 0x1E6D }, + { 0x1E6E, 0x1E6F }, + { 0x1E70, 0x1E71 }, + { 0x1E72, 0x1E73 }, + { 0x1E74, 0x1E75 }, + { 0x1E76, 0x1E77 }, + { 0x1E78, 0x1E79 }, + { 0x1E7A, 0x1E7B }, + { 0x1E7C, 0x1E7D }, + { 0x1E7E, 0x1E7F }, + { 0x1E80, 0x1E81 }, + { 0x1E82, 0x1E83 }, + { 0x1E84, 0x1E85 }, + { 0x1E86, 0x1E87 }, + { 0x1E88, 0x1E89 }, + { 0x1E8A, 0x1E8B }, + { 0x1E8C, 0x1E8D }, + { 0x1E8E, 0x1E8F }, + { 0x1E90, 0x1E91 }, + { 0x1E92, 0x1E93 }, + { 0x1E94, 0x1E95 }, + { 0x1EA0, 0x1EA1 }, + { 0x1EA2, 0x1EA3 }, + { 0x1EA4, 0x1EA5 }, + { 0x1EA6, 0x1EA7 }, + { 0x1EA8, 0x1EA9 }, + { 0x1EAA, 0x1EAB }, + { 0x1EAC, 0x1EAD }, + { 0x1EAE, 0x1EAF }, + { 0x1EB0, 0x1EB1 }, + { 0x1EB2, 0x1EB3 }, + { 0x1EB4, 0x1EB5 }, + { 0x1EB6, 0x1EB7 }, + { 0x1EB8, 0x1EB9 }, + { 0x1EBA, 0x1EBB }, + { 0x1EBC, 0x1EBD }, + { 0x1EBE, 0x1EBF }, + { 0x1EC0, 0x1EC1 }, + { 0x1EC2, 0x1EC3 }, + { 0x1EC4, 0x1EC5 }, + { 0x1EC6, 0x1EC7 }, + { 0x1EC8, 0x1EC9 }, + { 0x1ECA, 0x1ECB }, + { 0x1ECC, 0x1ECD }, + { 0x1ECE, 0x1ECF }, + { 0x1ED0, 0x1ED1 }, + { 0x1ED2, 0x1ED3 }, + { 0x1ED4, 0x1ED5 }, + { 0x1ED6, 0x1ED7 }, + { 0x1ED8, 0x1ED9 }, + { 0x1EDA, 0x1EDB }, + { 0x1EDC, 0x1EDD }, + { 0x1EDE, 0x1EDF }, + { 0x1EE0, 0x1EE1 }, + { 0x1EE2, 0x1EE3 }, + { 0x1EE4, 0x1EE5 }, + { 0x1EE6, 0x1EE7 }, + { 0x1EE8, 0x1EE9 }, + { 0x1EEA, 0x1EEB }, + { 0x1EEC, 0x1EED }, + { 0x1EEE, 0x1EEF }, + { 0x1EF0, 0x1EF1 }, + { 0x1EF2, 0x1EF3 }, + { 0x1EF4, 0x1EF5 }, + { 0x1EF6, 0x1EF7 }, + { 0x1EF8, 0x1EF9 }, + { 0x1F08, 0x1F00 }, + { 0x1F09, 0x1F01 }, + { 0x1F0A, 0x1F02 }, + { 0x1F0B, 0x1F03 }, + { 0x1F0C, 0x1F04 }, + { 0x1F0D, 0x1F05 }, + { 0x1F0E, 0x1F06 }, + { 0x1F0F, 0x1F07 }, + { 0x1F18, 0x1F10 }, + { 0x1F19, 0x1F11 }, + { 0x1F1A, 0x1F12 }, + { 0x1F1B, 0x1F13 }, + { 0x1F1C, 0x1F14 }, + { 0x1F1D, 0x1F15 }, + { 0x1F28, 0x1F20 }, + { 0x1F29, 0x1F21 }, + { 0x1F2A, 0x1F22 }, + { 0x1F2B, 0x1F23 }, + { 0x1F2C, 0x1F24 }, + { 0x1F2D, 0x1F25 }, + { 0x1F2E, 0x1F26 }, + { 0x1F2F, 0x1F27 }, + { 0x1F38, 0x1F30 }, + { 0x1F39, 0x1F31 }, + { 0x1F3A, 0x1F32 }, + { 0x1F3B, 0x1F33 }, + { 0x1F3C, 0x1F34 }, + { 0x1F3D, 0x1F35 }, + { 0x1F3E, 0x1F36 }, + { 0x1F3F, 0x1F37 }, + { 0x1F48, 0x1F40 }, + { 0x1F49, 0x1F41 }, + { 0x1F4A, 0x1F42 }, + { 0x1F4B, 0x1F43 }, + { 0x1F4C, 0x1F44 }, + { 0x1F4D, 0x1F45 }, + { 0x1F59, 0x1F51 }, + { 0x1F5B, 0x1F53 }, + { 0x1F5D, 0x1F55 }, + { 0x1F5F, 0x1F57 }, + { 0x1F68, 0x1F60 }, + { 0x1F69, 0x1F61 }, + { 0x1F6A, 0x1F62 }, + { 0x1F6B, 0x1F63 }, + { 0x1F6C, 0x1F64 }, + { 0x1F6D, 0x1F65 }, + { 0x1F6E, 0x1F66 }, + { 0x1F6F, 0x1F67 }, + { 0x1F88, 0x1F80 }, + { 0x1F89, 0x1F81 }, + { 0x1F8A, 0x1F82 }, + { 0x1F8B, 0x1F83 }, + { 0x1F8C, 0x1F84 }, + { 0x1F8D, 0x1F85 }, + { 0x1F8E, 0x1F86 }, + { 0x1F8F, 0x1F87 }, + { 0x1F98, 0x1F90 }, + { 0x1F99, 0x1F91 }, + { 0x1F9A, 0x1F92 }, + { 0x1F9B, 0x1F93 }, + { 0x1F9C, 0x1F94 }, + { 0x1F9D, 0x1F95 }, + { 0x1F9E, 0x1F96 }, + { 0x1F9F, 0x1F97 }, + { 0x1FA8, 0x1FA0 }, + { 0x1FA9, 0x1FA1 }, + { 0x1FAA, 0x1FA2 }, + { 0x1FAB, 0x1FA3 }, + { 0x1FAC, 0x1FA4 }, + { 0x1FAD, 0x1FA5 }, + { 0x1FAE, 0x1FA6 }, + { 0x1FAF, 0x1FA7 }, + { 0x1FB8, 0x1FB0 }, + { 0x1FB9, 0x1FB1 }, + { 0x1FD8, 0x1FD0 }, + { 0x1FD9, 0x1FD1 }, + { 0x1FE8, 0x1FE0 }, + { 0x1FE9, 0x1FE1 }, + { 0x24B6, 0x24D0 }, + { 0x24B7, 0x24D1 }, + { 0x24B8, 0x24D2 }, + { 0x24B9, 0x24D3 }, + { 0x24BA, 0x24D4 }, + { 0x24BB, 0x24D5 }, + { 0x24BC, 0x24D6 }, + { 0x24BD, 0x24D7 }, + { 0x24BE, 0x24D8 }, + { 0x24BF, 0x24D9 }, + { 0x24C0, 0x24DA }, + { 0x24C1, 0x24DB }, + { 0x24C2, 0x24DC }, + { 0x24C3, 0x24DD }, + { 0x24C4, 0x24DE }, + { 0x24C5, 0x24DF }, + { 0x24C6, 0x24E0 }, + { 0x24C7, 0x24E1 }, + { 0x24C8, 0x24E2 }, + { 0x24C9, 0x24E3 }, + { 0x24CA, 0x24E4 }, + { 0x24CB, 0x24E5 }, + { 0x24CC, 0x24E6 }, + { 0x24CD, 0x24E7 }, + { 0x24CE, 0x24E8 }, + { 0x24CF, 0x24E9 }, + { 0xFF21, 0xFF41 }, + { 0xFF22, 0xFF42 }, + { 0xFF23, 0xFF43 }, + { 0xFF24, 0xFF44 }, + { 0xFF25, 0xFF45 }, + { 0xFF26, 0xFF46 }, + { 0xFF27, 0xFF47 }, + { 0xFF28, 0xFF48 }, + { 0xFF29, 0xFF49 }, + { 0xFF2A, 0xFF4A }, + { 0xFF2B, 0xFF4B }, + { 0xFF2C, 0xFF4C }, + { 0xFF2D, 0xFF4D }, + { 0xFF2E, 0xFF4E }, + { 0xFF2F, 0xFF4F }, + { 0xFF30, 0xFF50 }, + { 0xFF31, 0xFF51 }, + { 0xFF32, 0xFF52 }, + { 0xFF33, 0xFF53 }, + { 0xFF34, 0xFF54 }, + { 0xFF35, 0xFF55 }, + { 0xFF36, 0xFF56 }, + { 0xFF37, 0xFF57 }, + { 0xFF38, 0xFF58 }, + { 0xFF39, 0xFF59 }, + { 0xFF3A, 0xFF5A }, +}; + +static int _find_upper(int ch) { + int low = 0; + int high = CAPS_LEN - 1; + int middle; + + while (low <= high) { + middle = (low + high) / 2; + + if (ch < caps_table[middle][0]) { + high = middle - 1; //search low end of array + } else if (caps_table[middle][0] < ch) { + low = middle + 1; //search high end of array + } else { + return caps_table[middle][1]; + } + } + + return ch; +} + +static int _find_lower(int ch) { + int low = 0; + int high = CAPS_LEN - 2; + int middle; + + while (low <= high) { + middle = (low + high) / 2; + + if (ch < reverse_caps_table[middle][0]) { + high = middle - 1; //search low end of array + } else if (reverse_caps_table[middle][0] < ch) { + low = middle + 1; //search high end of array + } else { + return reverse_caps_table[middle][1]; + } + } + + return ch; +} +#endif +#line 0 + + +#line 1 "sfwl/core/mutex.cpp" +/*************************************************************************/ +/* mutex.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +static Mutex _global_mutex; + +void _global_lock() { + _global_mutex.lock(); +} + +void _global_unlock() { + _global_mutex.unlock(); +} + +#ifndef NO_THREADS + +template class MutexImpl; +template class MutexImpl; + +#endif +#line 0 + +#line 1 "sfwl/core/safe_refcount.cpp" +/*************************************************************************/ +/* safe_refcount.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#if defined(DEBUG_ENABLED) && !defined(NO_THREADS) + + + +// On C++14 we don't have std::atomic::is_always_lockfree, so this is the best we can do +void check_lockless_atomics() { + // Doing the check for the types we actually care about + if (!std::atomic{}.is_lock_free() || !std::atomic{}.is_lock_free() || !std::atomic_bool{}.is_lock_free()) { + WARN_PRINT("Your compiler doesn't seem to support lockless atomics. Performance will be degraded. Please consider upgrading to a different or newer compiler."); + } +} + +#endif +#line 0 + + +#line 1 "sfwl/core/stime.cpp" +// ---------------------------------------------------------------------------- +// time + + + +#if 0 +uint64_t STime::time_gpu() { + GLint64 t = 123456789; + glGetInteger64v(GL_TIMESTAMP, &t); + return (uint64_t)t; +} +#endif +uint64_t STime::date() { + //time_t epoch = time(0); + //struct tm *ti = localtime(&epoch); + //return atoi64(va("%04d%02d%02d%02d%02d%02d", ti->tm_year + 1900, ti->tm_mon + 1, ti->tm_mday, ti->tm_hour, ti->tm_min, ti->tm_sec)); + return 0; +} +char *STime::date_string() { + //time_t epoch = time(0); + //struct tm *ti = localtime(&epoch); + //return va("%04d-%02d-%02d %02d:%02d:%02d", ti->tm_year + 1900, ti->tm_mon + 1, ti->tm_mday, ti->tm_hour, ti->tm_min, ti->tm_sec); + return 0; +} +uint64_t STime::date_epoch() { + time_t epoch = time(0); + return epoch; +} +#if 0 +double STime::time_ss() { + return glfwGetTime(); +} +double STime::time_ms() { + return glfwGetTime() * 1000.0; +} +uint64_t STime::time_us() { + return (uint64_t)(glfwGetTime() * 1000000.0); // @fixme: use a high resolution timer instead, or time_gpu below +} +uint64_t STime::sleep_us(uint64_t us) { // @fixme: use a high resolution sleeper instead + return sleep_ms( us / 1000.0 ); +} +double STime::sleep_ms(double ms) { + double now = time_ms(); + if( ms <= 0 ) { +#if defined(_WIN64) || defined(_WIN32) + Sleep(0); // yield +#else + usleep(0); +#endif + } else { +#if defined(_WIN64) || defined(_WIN32) + Sleep(ms); +#else + usleep(ms * 1000); +#endif + } + return time_ms() - now; +} +double sleep_ss(double ss) { + return sleep_ms( ss * 1000 ) / 1000.0; +} +#endif + +// high-perf functions + +#define TIMER_E3 1000ULL +#define TIMER_E6 1000000ULL +#define TIMER_E9 1000000000ULL + +#ifdef CLOCK_MONOTONIC_RAW +#define TIME_MONOTONIC CLOCK_MONOTONIC_RAW +#elif defined CLOCK_MONOTONIC +#define TIME_MONOTONIC CLOCK_MONOTONIC +#else +// #define TIME_MONOTONIC CLOCK_REALTIME // untested +#endif + +static uint64_t nanotimer(uint64_t *out_freq) { + if (out_freq) { +#if defined(_WIN64) || defined(_WIN32) + LARGE_INTEGER li; + QueryPerformanceFrequency(&li); + *out_freq = li.QuadPart; +//#elif is(ANDROID) +// *out_freq = CLOCKS_PER_SEC; +#elif defined TIME_MONOTONIC + *out_freq = TIMER_E9; +#else + *out_freq = TIMER_E6; +#endif + } +#if defined(_WIN64) || defined(_WIN32) + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + return (uint64_t)li.QuadPart; +//#elif is(ANDROID) +// return (uint64_t)clock(); +#elif defined TIME_MONOTONIC + struct timespec ts; + clock_gettime(TIME_MONOTONIC, &ts); + return (TIMER_E9 * (uint64_t)ts.tv_sec) + ts.tv_nsec; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return (TIMER_E6 * (uint64_t)tv.tv_sec) + tv.tv_usec; +#endif +} + +uint64_t STime::time_ns() { + static __thread uint64_t epoch = 0; + static __thread uint64_t freq = 0; + if (!freq) { + epoch = nanotimer(&freq); + } + + uint64_t a = nanotimer(NULL) - epoch; + uint64_t b = TIMER_E9; + uint64_t c = freq; + + // Computes (a*b)/c without overflow, as long as both (a*b) and the overall result fit into 64-bits. + // [ref] https://github.com/rust-lang/rust/blob/3809bbf47c8557bd149b3e52ceb47434ca8378d5/src/libstd/sys_common/mod.rs#L124 + uint64_t q = a / c; + uint64_t r = a % c; + return q * b + r * b / c; +} +uint64_t STime::time_us() { + return time_ns() / TIMER_E3; +} +uint64_t STime::time_ms() { + return time_ns() / TIMER_E6; +} +double STime::time_ss() { + return time_ns() / 1e9; // TIMER_E9; +} +double STime::time_mm() { + return time_ss() / 60; +} +double STime::time_hh() { + return time_mm() / 60; +} + +void STime::sleep_ns(double ns) { +#if defined(_WIN64) || defined(_WIN32) + if (ns >= 100) { + LARGE_INTEGER li; // Windows sleep in 100ns units + HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL); + li.QuadPart = (LONGLONG)(__int64)(-ns / 100); // Negative for relative time + SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); +#else + if (ns > 0) { + struct timespec wait = { 0 }; + wait.tv_sec = ns / 1e9; + wait.tv_nsec = ns - wait.tv_sec * 1e9; + nanosleep(&wait, NULL); +#endif + } else { +#if defined(_WIN64) || defined(_WIN32) + Sleep(0); // yield, Sleep(0), SwitchToThread +#else + usleep(0); +#endif + } +} +void STime::sleep_us(double us) { + sleep_ns(us * 1e3); +} +void STime::sleep_ms(double ms) { + sleep_ns(ms * 1e6); +} +void STime::sleep_ss(double ss) { + sleep_ns(ss * 1e9); +} +#line 0 + + +#line 1 "sfwl/core/memory.cpp" +/*************************************************************************/ +/* memory.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +void *operator new(size_t p_size, const char *p_description) { + return Memory::alloc_static(p_size, false); +} + +void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)) { + return p_allocfunc(p_size); +} + +#ifdef _MSC_VER +void operator delete(void *p_mem, const char *p_description) { + CRASH_NOW_MSG("Call to placement delete should not happen."); +} + +void operator delete(void *p_mem, void *(*p_allocfunc)(size_t p_size)) { + CRASH_NOW_MSG("Call to placement delete should not happen."); +} + +void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_description) { + CRASH_NOW_MSG("Call to placement delete should not happen."); +} +#endif + +#ifdef DEBUG_ENABLED +SafeNumeric Memory::mem_usage; +SafeNumeric Memory::max_usage; +#endif + +SafeNumeric Memory::alloc_count; + +void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) { +#ifdef DEBUG_ENABLED + bool prepad = true; +#else + bool prepad = p_pad_align; +#endif + + void *mem = malloc(p_bytes + (prepad ? PAD_ALIGN : 0)); + + ERR_FAIL_COND_V(!mem, nullptr); + + alloc_count.increment(); + + if (prepad) { + uint64_t *s = (uint64_t *)mem; + *s = p_bytes; + + uint8_t *s8 = (uint8_t *)mem; + +#ifdef DEBUG_ENABLED + uint64_t new_mem_usage = mem_usage.add(p_bytes); + max_usage.exchange_if_greater(new_mem_usage); +#endif + return s8 + PAD_ALIGN; + } else { + return mem; + } +} + +void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) { + if (p_memory == nullptr) { + return alloc_static(p_bytes, p_pad_align); + } + + uint8_t *mem = (uint8_t *)p_memory; + +#ifdef DEBUG_ENABLED + bool prepad = true; +#else + bool prepad = p_pad_align; +#endif + + if (prepad) { + mem -= PAD_ALIGN; + uint64_t *s = (uint64_t *)mem; + +#ifdef DEBUG_ENABLED + if (p_bytes > *s) { + uint64_t new_mem_usage = mem_usage.add(p_bytes - *s); + max_usage.exchange_if_greater(new_mem_usage); + } else { + mem_usage.sub(*s - p_bytes); + } +#endif + + if (p_bytes == 0) { + free(mem); + return nullptr; + } else { + *s = p_bytes; + + mem = (uint8_t *)realloc(mem, p_bytes + PAD_ALIGN); + ERR_FAIL_COND_V(!mem, nullptr); + + s = (uint64_t *)mem; + + *s = p_bytes; + + return mem + PAD_ALIGN; + } + } else { + mem = (uint8_t *)realloc(mem, p_bytes); + + ERR_FAIL_COND_V(mem == nullptr && p_bytes > 0, nullptr); + + return mem; + } +} + +void Memory::free_static(void *p_ptr, bool p_pad_align) { + ERR_FAIL_COND(p_ptr == nullptr); + + uint8_t *mem = (uint8_t *)p_ptr; + +#ifdef DEBUG_ENABLED + bool prepad = true; +#else + bool prepad = p_pad_align; +#endif + + alloc_count.decrement(); + + if (prepad) { + mem -= PAD_ALIGN; + +#ifdef DEBUG_ENABLED + uint64_t *s = (uint64_t *)mem; + mem_usage.sub(*s); +#endif + + free(mem); + } else { + free(mem); + } +} + +uint64_t Memory::get_mem_available() { + return -1; // 0xFFFF... +} + +uint64_t Memory::get_mem_usage() { +#ifdef DEBUG_ENABLED + return mem_usage.get(); +#else + return 0; +#endif +} + +uint64_t Memory::get_mem_max_usage() { +#ifdef DEBUG_ENABLED + return max_usage.get(); +#else + return 0; +#endif +} + +_GlobalNil::_GlobalNil() { + color = 1; + left = this; + right = this; + parent = this; +} + +_GlobalNil _GlobalNilClass::_nil; +#line 0 + + + +#line 1 "sfwl/core/pool_vector.cpp" +/*************************************************************************/ +/* pool_vector.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +Mutex pool_vector_lock; + +PoolAllocator *MemoryPool::memory_pool = nullptr; +uint8_t *MemoryPool::pool_memory = nullptr; +size_t *MemoryPool::pool_size = nullptr; + +MemoryPool::Alloc *MemoryPool::allocs = nullptr; +MemoryPool::Alloc *MemoryPool::free_list = nullptr; +uint32_t MemoryPool::alloc_count = 0; +uint32_t MemoryPool::allocs_used = 0; +Mutex MemoryPool::alloc_mutex; + +size_t MemoryPool::total_memory = 0; +size_t MemoryPool::max_memory = 0; + +void MemoryPool::setup(uint32_t p_max_allocs) { + allocs = memnew_arr(Alloc, p_max_allocs); + alloc_count = p_max_allocs; + allocs_used = 0; + + for (uint32_t i = 0; i < alloc_count - 1; i++) { + allocs[i].free_list = &allocs[i + 1]; + } + + free_list = &allocs[0]; +} + +void MemoryPool::cleanup() { + memdelete_arr(allocs); + + ERR_FAIL_COND_MSG(allocs_used > 0, "There are still MemoryPool allocs in use at exit!"); +} +#line 0 + +#line 1 "sfwl/core/pool_allocator.cpp" +/*************************************************************************/ +/* pool_allocator.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +#define COMPACT_CHUNK(m_entry, m_to_pos) \ + do { \ + void *_dst = &((unsigned char *)pool)[m_to_pos]; \ + void *_src = &((unsigned char *)pool)[(m_entry).pos]; \ + memmove(_dst, _src, aligned((m_entry).len)); \ + (m_entry).pos = m_to_pos; \ + } while (0); + +void PoolAllocator::mt_lock() const { +} + +void PoolAllocator::mt_unlock() const { +} + +bool PoolAllocator::get_free_entry(EntryArrayPos *p_pos) { + if (entry_count == entry_max) { + return false; + } + + for (int i = 0; i < entry_max; i++) { + if (entry_array[i].len == 0) { + *p_pos = i; + return true; + } + } + + ERR_PRINT("Out of memory Chunks!"); + + return false; // +} + +/** + * Find a hole + * @param p_pos The hole is behind the block pointed by this variable upon return. if pos==entry_count, then allocate at end + * @param p_for_size hole size + * @return false if hole found, true if no hole found + */ +bool PoolAllocator::find_hole(EntryArrayPos *p_pos, int p_for_size) { + /* position where previous entry ends. Defaults to zero (begin of pool) */ + + int prev_entry_end_pos = 0; + + for (int i = 0; i < entry_count; i++) { + Entry &entry = entry_array[entry_indices[i]]; + + /* determine hole size to previous entry */ + + int hole_size = entry.pos - prev_entry_end_pos; + + /* determine if what we want fits in that hole */ + if (hole_size >= p_for_size) { + *p_pos = i; + return true; + } + + /* prepare for next one */ + prev_entry_end_pos = entry_end(entry); + } + + /* No holes between entries, check at the end..*/ + + if ((pool_size - prev_entry_end_pos) >= p_for_size) { + *p_pos = entry_count; + return true; + } + + return false; +} + +void PoolAllocator::compact(int p_up_to) { + uint32_t prev_entry_end_pos = 0; + + if (p_up_to < 0) { + p_up_to = entry_count; + } + for (int i = 0; i < p_up_to; i++) { + Entry &entry = entry_array[entry_indices[i]]; + + /* determine hole size to previous entry */ + + int hole_size = entry.pos - prev_entry_end_pos; + + /* if we can compact, do it */ + if (hole_size > 0 && !entry.lock) { + COMPACT_CHUNK(entry, prev_entry_end_pos); + } + + /* prepare for next one */ + prev_entry_end_pos = entry_end(entry); + } +} + +void PoolAllocator::compact_up(int p_from) { + uint32_t next_entry_end_pos = pool_size; // - static_area_size; + + for (int i = entry_count - 1; i >= p_from; i--) { + Entry &entry = entry_array[entry_indices[i]]; + + /* determine hole size to nextious entry */ + + int hole_size = next_entry_end_pos - (entry.pos + aligned(entry.len)); + + /* if we can compact, do it */ + if (hole_size > 0 && !entry.lock) { + COMPACT_CHUNK(entry, (next_entry_end_pos - aligned(entry.len))); + } + + /* prepare for next one */ + next_entry_end_pos = entry.pos; + } +} + +bool PoolAllocator::find_entry_index(EntryIndicesPos *p_map_pos, Entry *p_entry) { + EntryArrayPos entry_pos = entry_max; + + for (int i = 0; i < entry_count; i++) { + if (&entry_array[entry_indices[i]] == p_entry) { + entry_pos = i; + break; + } + } + + if (entry_pos == entry_max) { + return false; + } + + *p_map_pos = entry_pos; + return true; +} + +PoolAllocator::ID PoolAllocator::alloc(int p_size) { + ERR_FAIL_COND_V(p_size < 1, POOL_ALLOCATOR_INVALID_ID); + ERR_FAIL_COND_V(p_size > free_mem, POOL_ALLOCATOR_INVALID_ID); + + mt_lock(); + + if (entry_count == entry_max) { + mt_unlock(); + ERR_PRINT("entry_count==entry_max"); + return POOL_ALLOCATOR_INVALID_ID; + } + + int size_to_alloc = aligned(p_size); + + EntryIndicesPos new_entry_indices_pos; + + if (!find_hole(&new_entry_indices_pos, size_to_alloc)) { + /* No hole could be found, try compacting mem */ + compact(); + /* Then search again */ + + if (!find_hole(&new_entry_indices_pos, size_to_alloc)) { + mt_unlock(); + ERR_FAIL_V_MSG(POOL_ALLOCATOR_INVALID_ID, "Memory can't be compacted further."); + } + } + + EntryArrayPos new_entry_array_pos; + + bool found_free_entry = get_free_entry(&new_entry_array_pos); + + if (!found_free_entry) { + mt_unlock(); + ERR_FAIL_V_MSG(POOL_ALLOCATOR_INVALID_ID, "No free entry found in PoolAllocator."); + } + + /* move all entry indices up, make room for this one */ + for (int i = entry_count; i > new_entry_indices_pos; i--) { + entry_indices[i] = entry_indices[i - 1]; + } + + entry_indices[new_entry_indices_pos] = new_entry_array_pos; + + entry_count++; + + Entry &entry = entry_array[entry_indices[new_entry_indices_pos]]; + + entry.len = p_size; + entry.pos = (new_entry_indices_pos == 0) ? 0 : entry_end(entry_array[entry_indices[new_entry_indices_pos - 1]]); //alloc either at beginning or end of previous + entry.lock = 0; + entry.check = (check_count++) & CHECK_MASK; + free_mem -= size_to_alloc; + if (free_mem < free_mem_peak) { + free_mem_peak = free_mem; + } + + ID retval = (entry_indices[new_entry_indices_pos] << CHECK_BITS) | entry.check; + mt_unlock(); + + //ERR_FAIL_COND_V( (uintptr_t)get(retval)%align != 0, retval ); + + return retval; +} + +PoolAllocator::Entry *PoolAllocator::get_entry(ID p_mem) { + unsigned int check = p_mem & CHECK_MASK; + int entry = p_mem >> CHECK_BITS; + ERR_FAIL_INDEX_V(entry, entry_max, nullptr); + ERR_FAIL_COND_V(entry_array[entry].check != check, nullptr); + ERR_FAIL_COND_V(entry_array[entry].len == 0, nullptr); + + return &entry_array[entry]; +} + +const PoolAllocator::Entry *PoolAllocator::get_entry(ID p_mem) const { + unsigned int check = p_mem & CHECK_MASK; + int entry = p_mem >> CHECK_BITS; + ERR_FAIL_INDEX_V(entry, entry_max, nullptr); + ERR_FAIL_COND_V(entry_array[entry].check != check, nullptr); + ERR_FAIL_COND_V(entry_array[entry].len == 0, nullptr); + + return &entry_array[entry]; +} + +void PoolAllocator::free(ID p_mem) { + mt_lock(); + Entry *e = get_entry(p_mem); + if (!e) { + mt_unlock(); + ERR_PRINT("!e"); + return; + } + if (e->lock) { + mt_unlock(); + ERR_PRINT("e->lock"); + return; + } + + EntryIndicesPos entry_indices_pos; + + bool index_found = find_entry_index(&entry_indices_pos, e); + if (!index_found) { + mt_unlock(); + ERR_FAIL_COND(!index_found); + } + + for (int i = entry_indices_pos; i < (entry_count - 1); i++) { + entry_indices[i] = entry_indices[i + 1]; + } + + entry_count--; + free_mem += aligned(e->len); + e->clear(); + mt_unlock(); +} + +int PoolAllocator::get_size(ID p_mem) const { + int size; + mt_lock(); + + const Entry *e = get_entry(p_mem); + if (!e) { + mt_unlock(); + ERR_PRINT("!e"); + return 0; + } + + size = e->len; + + mt_unlock(); + + return size; +} + +Error PoolAllocator::resize(ID p_mem, int p_new_size) { + mt_lock(); + Entry *e = get_entry(p_mem); + + if (!e) { + mt_unlock(); + ERR_FAIL_COND_V(!e, ERR_INVALID_PARAMETER); + } + + if (needs_locking && e->lock) { + mt_unlock(); + ERR_FAIL_COND_V(e->lock, ERR_ALREADY_IN_USE); + } + + uint32_t alloc_size = aligned(p_new_size); + + if ((uint32_t)aligned(e->len) == alloc_size) { + e->len = p_new_size; + mt_unlock(); + return OK; + } else if (e->len > (uint32_t)p_new_size) { + free_mem += aligned(e->len); + free_mem -= alloc_size; + e->len = p_new_size; + mt_unlock(); + return OK; + } + + //p_new_size = align(p_new_size) + int _free = free_mem; // - static_area_size; + + if (uint32_t(_free + aligned(e->len)) < alloc_size) { + mt_unlock(); + ERR_FAIL_V(ERR_OUT_OF_MEMORY); + }; + + EntryIndicesPos entry_indices_pos; + + bool index_found = find_entry_index(&entry_indices_pos, e); + + if (!index_found) { + mt_unlock(); + ERR_FAIL_COND_V(!index_found, ERR_BUG); + } + + //no need to move stuff around, it fits before the next block + uint32_t next_pos; + if (entry_indices_pos + 1 == entry_count) { + next_pos = pool_size; // - static_area_size; + } else { + next_pos = entry_array[entry_indices[entry_indices_pos + 1]].pos; + }; + + if ((next_pos - e->pos) > alloc_size) { + free_mem += aligned(e->len); + e->len = p_new_size; + free_mem -= alloc_size; + mt_unlock(); + return OK; + } + //it doesn't fit, compact around BEFORE current index (make room behind) + + compact(entry_indices_pos + 1); + + if ((next_pos - e->pos) > alloc_size) { + //now fits! hooray! + free_mem += aligned(e->len); + e->len = p_new_size; + free_mem -= alloc_size; + mt_unlock(); + if (free_mem < free_mem_peak) { + free_mem_peak = free_mem; + } + return OK; + } + + //STILL doesn't fit, compact around AFTER current index (make room after) + + compact_up(entry_indices_pos + 1); + + if ((entry_array[entry_indices[entry_indices_pos + 1]].pos - e->pos) > alloc_size) { + //now fits! hooray! + free_mem += aligned(e->len); + e->len = p_new_size; + free_mem -= alloc_size; + mt_unlock(); + if (free_mem < free_mem_peak) { + free_mem_peak = free_mem; + } + return OK; + } + + mt_unlock(); + ERR_FAIL_V(ERR_OUT_OF_MEMORY); +} + +Error PoolAllocator::lock(ID p_mem) { + if (!needs_locking) { + return OK; + } + mt_lock(); + Entry *e = get_entry(p_mem); + if (!e) { + mt_unlock(); + ERR_PRINT("!e"); + return ERR_INVALID_PARAMETER; + } + e->lock++; + mt_unlock(); + return OK; +} + +bool PoolAllocator::is_locked(ID p_mem) const { + if (!needs_locking) { + return false; + } + + mt_lock(); + const Entry *e = ((PoolAllocator *)(this))->get_entry(p_mem); + if (!e) { + mt_unlock(); + ERR_PRINT("!e"); + return false; + } + bool locked = e->lock; + mt_unlock(); + return locked; +} + +const void *PoolAllocator::get(ID p_mem) const { + if (!needs_locking) { + const Entry *e = get_entry(p_mem); + ERR_FAIL_COND_V(!e, nullptr); + return &pool[e->pos]; + } + + mt_lock(); + const Entry *e = get_entry(p_mem); + + if (!e) { + mt_unlock(); + ERR_FAIL_COND_V(!e, nullptr); + } + if (e->lock == 0) { + mt_unlock(); + ERR_PRINT("e->lock == 0"); + return nullptr; + } + + if ((int)e->pos >= pool_size) { + mt_unlock(); + ERR_PRINT("e->pos<0 || e->pos>=pool_size"); + return nullptr; + } + const void *ptr = &pool[e->pos]; + + mt_unlock(); + + return ptr; +} + +void *PoolAllocator::get(ID p_mem) { + if (!needs_locking) { + Entry *e = get_entry(p_mem); + ERR_FAIL_COND_V(!e, nullptr); + return &pool[e->pos]; + } + + mt_lock(); + Entry *e = get_entry(p_mem); + + if (!e) { + mt_unlock(); + ERR_FAIL_COND_V(!e, nullptr); + } + if (e->lock == 0) { + mt_unlock(); + ERR_PRINT("e->lock == 0"); + return nullptr; + } + + if ((int)e->pos >= pool_size) { + mt_unlock(); + ERR_PRINT("e->pos<0 || e->pos>=pool_size"); + return nullptr; + } + void *ptr = &pool[e->pos]; + + mt_unlock(); + + return ptr; +} +void PoolAllocator::unlock(ID p_mem) { + if (!needs_locking) { + return; + } + mt_lock(); + Entry *e = get_entry(p_mem); + if (!e) { + mt_unlock(); + ERR_FAIL_COND(!e); + } + if (e->lock == 0) { + mt_unlock(); + ERR_PRINT("e->lock == 0"); + return; + } + e->lock--; + mt_unlock(); +} + +int PoolAllocator::get_used_mem() const { + return pool_size - free_mem; +} + +int PoolAllocator::get_free_peak() { + return free_mem_peak; +} + +int PoolAllocator::get_free_mem() { + return free_mem; +} + +void PoolAllocator::create_pool(void *p_mem, int p_size, int p_max_entries) { + pool = (uint8_t *)p_mem; + pool_size = p_size; + + entry_array = memnew_arr(Entry, p_max_entries); + entry_indices = memnew_arr(int, p_max_entries); + entry_max = p_max_entries; + entry_count = 0; + + free_mem = p_size; + free_mem_peak = p_size; + + check_count = 0; +} + +PoolAllocator::PoolAllocator(int p_size, bool p_needs_locking, int p_max_entries) { + mem_ptr = memalloc(p_size); + ERR_FAIL_COND(!mem_ptr); + align = 1; + create_pool(mem_ptr, p_size, p_max_entries); + needs_locking = p_needs_locking; +} + +PoolAllocator::PoolAllocator(void *p_mem, int p_size, int p_align, bool p_needs_locking, int p_max_entries) { + if (p_align > 1) { + uint8_t *mem8 = (uint8_t *)p_mem; + uint64_t ofs = (uint64_t)mem8; + if (ofs % p_align) { + int dif = p_align - (ofs % p_align); + mem8 += p_align - (ofs % p_align); + p_size -= dif; + p_mem = (void *)mem8; + }; + }; + + create_pool(p_mem, p_size, p_max_entries); + needs_locking = p_needs_locking; + align = p_align; + mem_ptr = nullptr; +} + +PoolAllocator::PoolAllocator(int p_align, int p_size, bool p_needs_locking, int p_max_entries) { + ERR_FAIL_COND(p_align < 1); + mem_ptr = Memory::alloc_static(p_size + p_align, true); + uint8_t *mem8 = (uint8_t *)mem_ptr; + uint64_t ofs = (uint64_t)mem8; + if (ofs % p_align) { + mem8 += p_align - (ofs % p_align); + } + create_pool(mem8, p_size, p_max_entries); + needs_locking = p_needs_locking; + align = p_align; +} + +PoolAllocator::~PoolAllocator() { + if (mem_ptr) { + memfree(mem_ptr); + } + + memdelete_arr(entry_array); + memdelete_arr(entry_indices); +} + +#undef COMPACT_CHUNK +#line 0 + + +#line 1 "sfwl/core/random_pcg.cpp" +/*************************************************************************/ +/* random_pcg.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +RandomPCG::RandomPCG(uint64_t p_seed, uint64_t p_inc) : + pcg(), + current_inc(p_inc) { + seed(p_seed); +} + +void RandomPCG::randomize() { + seed((STime::date() + STime::time_us()) * pcg.state + PCG_DEFAULT_INC_64); +} + +double RandomPCG::random(double p_from, double p_to) { + return randd() * (p_to - p_from) + p_from; +} + +float RandomPCG::random(float p_from, float p_to) { + return randf() * (p_to - p_from) + p_from; +} + +int RandomPCG::random(int p_from, int p_to) { + if (p_from == p_to) { + return p_from; + } + return rand(abs(p_from - p_to) + 1) + MIN(p_from, p_to); +} +#line 0 + + +#line 1 "sfwl/core/math_funcs.cpp" +/*************************************************************************/ +/* math_funcs.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +RandomPCG Math::default_rand(RandomPCG::DEFAULT_SEED, RandomPCG::DEFAULT_INC); + +#define PHI 0x9e3779b9 + +uint32_t Math::rand_from_seed(uint64_t *seed) { + RandomPCG rng = RandomPCG(*seed, RandomPCG::DEFAULT_INC); + uint32_t r = rng.rand(); + *seed = rng.get_seed(); + return r; +} + +void Math::seed(uint64_t x) { + default_rand.seed(x); +} + +void Math::randomize() { + default_rand.randomize(); +} + +uint32_t Math::rand() { + return default_rand.rand(); +} + +double Math::randfn(double mean, double deviation) { + return default_rand.randfn(mean, deviation); +} + +int Math::step_decimals(double p_step) { + static const int maxn = 10; + static const double sd[maxn] = { + 0.9999, // somehow compensate for floating point error + 0.09999, + 0.009999, + 0.0009999, + 0.00009999, + 0.000009999, + 0.0000009999, + 0.00000009999, + 0.000000009999, + 0.0000000009999 + }; + + double abs = Math::abs(p_step); + double decs = abs - (int)abs; // Strip away integer part + for (int i = 0; i < maxn; i++) { + if (decs >= sd[i]) { + return i; + } + } + + return 0; +} + +// Only meant for editor usage in float ranges, where a step of 0 +// means that decimal digits should not be limited in String::num. +int Math::range_step_decimals(double p_step) { + if (p_step < 0.0000000000001) { + return 16; // Max value hardcoded in String::num + } + return step_decimals(p_step); +} + +double Math::dectime(double p_value, double p_amount, double p_step) { + double sgn = p_value < 0 ? -1.0 : 1.0; + double val = Math::abs(p_value); + val -= p_amount * p_step; + if (val < 0.0) { + val = 0.0; + } + return val * sgn; +} + +double Math::ease(double p_x, double p_c) { + if (p_x < 0) { + p_x = 0; + } else if (p_x > 1.0) { + p_x = 1.0; + } + if (p_c > 0) { + if (p_c < 1.0) { + return 1.0 - Math::pow(1.0 - p_x, 1.0 / p_c); + } else { + return Math::pow(p_x, p_c); + } + } else if (p_c < 0) { + //inout ease + + if (p_x < 0.5) { + return Math::pow(p_x * 2.0, -p_c) * 0.5; + } else { + return (1.0 - Math::pow(1.0 - (p_x - 0.5) * 2.0, -p_c)) * 0.5 + 0.5; + } + } else { + return 0; // no ease (raw) + } +} + +double Math::stepify(double p_value, double p_step) { + if (p_step != 0) { + p_value = Math::floor(p_value / p_step + 0.5) * p_step; + } + return p_value; +} + +uint32_t Math::larger_prime(uint32_t p_val) { + static const uint32_t primes[] = { + 5, + 13, + 23, + 47, + 97, + 193, + 389, + 769, + 1543, + 3079, + 6151, + 12289, + 24593, + 49157, + 98317, + 196613, + 393241, + 786433, + 1572869, + 3145739, + 6291469, + 12582917, + 25165843, + 50331653, + 100663319, + 201326611, + 402653189, + 805306457, + 1610612741, + 0, + }; + + int idx = 0; + while (true) { + ERR_FAIL_COND_V(primes[idx] == 0, 0); + if (primes[idx] > p_val) { + return primes[idx]; + } + idx++; + } +} + +double Math::random(double from, double to) { + return default_rand.random(from, to); +} + +float Math::random(float from, float to) { + return default_rand.random(from, to); +} + +real_t Math::randomr(real_t from, real_t to) { + return default_rand.randomr(from, to); +} + +int Math::random(int from, int to) { + return default_rand.random(from, to); +} +#line 0 + + +#line 1 "sfwl/core/ustring.cpp" +/*************************************************************************/ +/* ustring.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy +#endif + + + +#if defined(MINGW_ENABLED) || defined(_MSC_VER) +#define snprintf _snprintf_s +#endif + +#define MAX_DIGITS 6 +#define UPPERCASE(m_c) (((m_c) >= 'a' && (m_c) <= 'z') ? ((m_c) - ('a' - 'A')) : (m_c)) +#define LOWERCASE(m_c) (((m_c) >= 'A' && (m_c) <= 'Z') ? ((m_c) + ('a' - 'A')) : (m_c)) +#define IS_DIGIT(m_d) ((m_d) >= '0' && (m_d) <= '9') +#define IS_HEX_DIGIT(m_d) (((m_d) >= '0' && (m_d) <= '9') || ((m_d) >= 'a' && (m_d) <= 'f') || ((m_d) >= 'A' && (m_d) <= 'F')) + +#define READING_SIGN 0 +#define READING_INT 1 +#define READING_DEC 2 +#define READING_EXP 3 +#define READING_DONE 4 + +const char CharString::_null = 0; +const char16_t Char16String::_null = 0; +const CharType String::_null = 0; + +static bool _wildcard_match(const CharType *p_pattern, const CharType *p_string, bool p_case_sensitive) { + switch (*p_pattern) { + case '\0': + return !*p_string; + case '*': + return _wildcard_match(p_pattern + 1, p_string, p_case_sensitive) || (*p_string && _wildcard_match(p_pattern, p_string + 1, p_case_sensitive)); + case '?': + return *p_string && (*p_string != '.') && _wildcard_match(p_pattern + 1, p_string + 1, p_case_sensitive); + default: + + return (p_case_sensitive ? (*p_string == *p_pattern) : (_find_upper(*p_string) == _find_upper(*p_pattern))) && _wildcard_match(p_pattern + 1, p_string + 1, p_case_sensitive); + } +} + +static int _humanize_digits(int p_num) { + if (p_num < 100) { + return 2; + } else if (p_num < 1024) { + return 1; + } else { + return 0; + } +} + +bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end) { + const String &s = p_s; + int beg = CLAMP(p_col, 0, s.length()); + int end = beg; + + if (s[beg] > 32 || beg == s.length()) { + bool symbol = beg < s.length() && is_symbol(s[beg]); + + while (beg > 0 && s[beg - 1] > 32 && (symbol == is_symbol(s[beg - 1]))) { + beg--; + } + while (end < s.length() && s[end + 1] > 32 && (symbol == is_symbol(s[end + 1]))) { + end++; + } + + if (end < s.length()) { + end += 1; + } + + r_beg = beg; + r_end = end; + + return true; + } else { + return false; + } +} + +/*************************************************************************/ +/* Char16String */ +/*************************************************************************/ + +bool Char16String::operator<(const Char16String &p_right) const { + if (length() == 0) { + return p_right.length() != 0; + } + + return is_str_less(get_data(), p_right.get_data()); +} + +Char16String &Char16String::operator+=(char16_t p_char) { + const int lhs_len = length(); + resize(lhs_len + 2); + + char16_t *dst = ptrw(); + dst[lhs_len] = p_char; + dst[lhs_len + 1] = 0; + + return *this; +} + +void Char16String::operator=(const char16_t *p_cstr) { + copy_from(p_cstr); +} + +const char16_t *Char16String::get_data() const { + if (size()) { + return &operator[](0); + } else { + return u""; + } +} + +void Char16String::copy_from(const char16_t *p_cstr) { + if (!p_cstr) { + resize(0); + return; + } + + const char16_t *s = p_cstr; + for (; *s; s++) { + } + size_t len = s - p_cstr; + + if (len == 0) { + resize(0); + return; + } + + Error err = resize(++len); // include terminating null char + + ERR_FAIL_COND_MSG(err != OK, "Failed to copy char16_t string."); + + memcpy(ptrw(), p_cstr, len * sizeof(char16_t)); +} + +/*************************************************************************/ +/* CharString */ +/*************************************************************************/ + +bool CharString::operator<(const CharString &p_right) const { + if (length() == 0) { + return p_right.length() != 0; + } + + return is_str_less(get_data(), p_right.get_data()); +} + +CharString &CharString::operator+=(char p_char) { + const int lhs_len = length(); + resize(lhs_len + 2); + + char *dst = ptrw(); + dst[lhs_len] = p_char; + dst[lhs_len + 1] = 0; + + return *this; +} + +void CharString::operator=(const char *p_cstr) { + copy_from(p_cstr); +} + +const char *CharString::get_data() const { + if (size()) { + return &operator[](0); + } else { + return ""; + } +} + +void CharString::copy_from(const char *p_cstr) { + if (!p_cstr) { + resize(0); + return; + } + + size_t len = strlen(p_cstr); + + if (len == 0) { + resize(0); + return; + } + + Error err = resize(++len); // include terminating null char + + ERR_FAIL_COND_MSG(err != OK, "Failed to copy C-string."); + + memcpy(ptrw(), p_cstr, len); +} + +/*************************************************************************/ +/* String */ +/*************************************************************************/ + +void String::copy_from(const char *p_cstr) { + // copy Latin-1 encoded c-string directly + if (!p_cstr) { + resize(0); + return; + } + + const size_t len = strlen(p_cstr); + + if (len == 0) { + resize(0); + return; + } + + resize(len + 1); // include 0 + + CharType *dst = this->ptrw(); + + for (size_t i = 0; i <= len; i++) { + uint8_t c = p_cstr[i] >= 0 ? p_cstr[i] : uint8_t(256 + p_cstr[i]); + if (c == 0 && i < len) { + print_unicode_error("NUL character", true); + dst[i] = 0x20; + } else { + dst[i] = c; + } + } +} + +void String::copy_from(const char *p_cstr, const int p_clip_to) { + // copy Latin-1 encoded c-string directly + if (!p_cstr) { + resize(0); + return; + } + + int len = 0; + const char *ptr = p_cstr; + while ((p_clip_to < 0 || len < p_clip_to) && *(ptr++) != 0) { + len++; + } + + if (len == 0) { + resize(0); + return; + } + + resize(len + 1); // include 0 + + CharType *dst = this->ptrw(); + + for (int i = 0; i < len; i++) { + uint8_t c = p_cstr[i] >= 0 ? p_cstr[i] : uint8_t(256 + p_cstr[i]); + if (c == 0) { + print_unicode_error("NUL character", true); + dst[i] = 0x20; + } else { + dst[i] = c; + } + } + dst[len] = 0; +} + +void String::copy_from(const wchar_t *p_cstr) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit, parse as UTF-16 + parse_utf16((const char16_t *)p_cstr); +#else + // wchar_t is 32-bit, copy directly + copy_from((const CharType *)p_cstr); +#endif +} + +void String::copy_from(const wchar_t *p_cstr, const int p_clip_to) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit, parse as UTF-16 + parse_utf16((const char16_t *)p_cstr, p_clip_to); +#else + // wchar_t is 32-bit, copy directly + copy_from((const CharType *)p_cstr, p_clip_to); +#endif +} + +void String::copy_from(const CharType &p_char) { + if (p_char == 0) { + print_unicode_error("NUL character", true); + return; + } + /* + if ((p_char & 0xfffff800) == 0xd800) { + print_unicode_error(vformat("Unpaired surrogate (%x)", (uint32_t)p_char)); + } + if (p_char > 0x10ffff) { + print_unicode_error(vformat("Invalid unicode codepoint (%x)", (uint32_t)p_char)); + } + */ + + resize(2); + + CharType *dst = ptrw(); + dst[0] = p_char; + dst[1] = 0; +} + +void String::copy_from(const CharType *p_cstr) { + if (!p_cstr) { + resize(0); + return; + } + + int len = 0; + const CharType *ptr = p_cstr; + while (*(ptr++) != 0) { + len++; + } + + if (len == 0) { + resize(0); + return; + } + + copy_from_unchecked(p_cstr, len); +} + +void String::copy_from(const CharType *p_cstr, const int p_clip_to) { + if (!p_cstr) { + resize(0); + return; + } + + int len = 0; + const CharType *ptr = p_cstr; + while ((p_clip_to < 0 || len < p_clip_to) && *(ptr++) != 0) { + len++; + } + + if (len == 0) { + resize(0); + return; + } + + copy_from_unchecked(p_cstr, len); +} + +// assumes the following have already been validated: +// p_char != nullptr +// p_length > 0 +// p_length <= p_char strlen +void String::copy_from_unchecked(const CharType *p_char, const int p_length) { + resize(p_length + 1); + CharType *dst = ptrw(); + dst[p_length] = 0; + + for (int i = 0; i < p_length; i++) { + if (p_char[i] == 0) { + print_unicode_error("NUL character", true); + dst[i] = 0x20; + continue; + } + /* + if ((p_char[i] & 0xfffff800) == 0xd800) { + print_unicode_error(vformat("Unpaired surrogate (%x)", (uint32_t)p_char[i])); + } + if (p_char[i] > 0x10ffff) { + print_unicode_error(vformat("Invalid unicode codepoint (%x)", (uint32_t)p_char[i])); + } + */ + dst[i] = p_char[i]; + } +} + +bool String::operator==(const String &p_str) const { + if (length() != p_str.length()) { + return false; + } + if (empty()) { + return true; + } + + int l = length(); + + const CharType *src = get_data(); + const CharType *dst = p_str.get_data(); + + /* Compare char by char */ + for (int i = 0; i < l; i++) { + if (src[i] != dst[i]) { + return false; + } + } + + return true; +} + +bool String::operator!=(const String &p_str) const { + return !((*this == p_str)); +} + +String String::operator+(const String &p_str) const { + String res = *this; + res += p_str; + return res; +} + +/* +String String::operator+(CharType p_char) const { + String res = *this; + res += p_char; + return res; +} +*/ + +String &String::operator+=(const String &p_str) { + const int lhs_len = length(); + if (lhs_len == 0) { + *this = p_str; + return *this; + } + + const int rhs_len = p_str.length(); + if (rhs_len == 0) { + return *this; + } + + set_length(lhs_len + rhs_len); + + const CharType *src = p_str.get_data(); + CharType *dst = ptrw() + lhs_len; + + memcpy(dst, src, rhs_len * sizeof(CharType)); + + return *this; +} + +String &String::operator+=(CharType p_char) { + if (p_char == 0) { + print_unicode_error("NUL character", true); + return *this; + } + /* + if ((p_char & 0xfffff800) == 0xd800) { + print_unicode_error(vformat("Unpaired surrogate (%x)", (uint32_t)p_char)); + } + if (p_char > 0x10ffff) { + print_unicode_error(vformat("Invalid unicode codepoint (%x)", (uint32_t)p_char)); + } + */ + + const int lhs_len = length(); + set_length(lhs_len + 1); + CharType *dst = ptrw(); + dst[lhs_len] = p_char; + + return *this; +} + +String &String::operator+=(const char *p_str) { + if (!p_str || p_str[0] == 0) { + return *this; + } + + const int lhs_len = length(); + const size_t rhs_len = strlen(p_str); + + set_length(lhs_len + rhs_len); + + CharType *dst = ptrw() + lhs_len; + + for (size_t i = 0; i <= rhs_len; i++) { + uint8_t c = p_str[i] >= 0 ? p_str[i] : uint8_t(256 + p_str[i]); + if (c == 0 && i < rhs_len) { + print_unicode_error("NUL character", true); + dst[i] = 0x20; + } else { + dst[i] = c; + } + } + + return *this; +} + +String &String::operator+=(const wchar_t *p_str) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + *this += String::utf16((const char16_t *)p_str); +#else + // wchar_t is 32-bit + *this += String((const CharType *)p_str); +#endif + return *this; +} + +String &String::operator+=(const CharType *p_str) { + *this += String(p_str); + return *this; +} + +void String::operator=(const char *p_str) { + copy_from(p_str); +} + +void String::operator=(const CharType *p_str) { + copy_from(p_str); +} + +void String::operator=(const wchar_t *p_str) { + copy_from(p_str); +} + +bool String::operator==(const char *p_str) const { + // compare Latin-1 encoded c-string + int len = 0; + const char *aux = p_str; + + while (*(aux++) != 0) { + len++; + } + + if (length() != len) { + return false; + } + if (empty()) { + return true; + } + + int l = length(); + + const CharType *dst = get_data(); + + // Compare char by char + for (int i = 0; i < l; i++) { + if ((CharType)p_str[i] != dst[i]) { + return false; + } + } + + return true; +} + +bool String::operator==(const wchar_t *p_str) const { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit, parse as UTF-16 + return *this == String::utf16((const char16_t *)p_str); +#else + // wchar_t is 32-bit, compare char by char + return *this == (const CharType *)p_str; +#endif +} + +bool String::operator==(const CharType *p_str) const { + int len = 0; + const CharType *aux = p_str; + + while (*(aux++) != 0) { + len++; + } + + if (length() != len) { + return false; + } + if (empty()) { + return true; + } + + int l = length(); + + const CharType *dst = get_data(); + + /* Compare char by char */ + for (int i = 0; i < l; i++) { + if (p_str[i] != dst[i]) { + return false; + } + } + + return true; +} + +bool String::operator==(const StrRange &p_str_range) const { + int len = p_str_range.len; + + if (length() != len) { + return false; + } + if (empty()) { + return true; + } + + const CharType *c_str = p_str_range.c_str; + const CharType *dst = &operator[](0); + + /* Compare char by char */ + for (int i = 0; i < len; i++) { + if (c_str[i] != dst[i]) { + return false; + } + } + + return true; +} + +bool String::operator!=(const char *p_str) const { + return (!(*this == p_str)); +} + +bool String::operator!=(const wchar_t *p_str) const { + return (!(*this == p_str)); +} + +bool String::operator!=(const CharType *p_str) const { + return (!(*this == p_str)); +} + +bool String::operator<(const char *p_str) const { + if (empty() && p_str[0] == 0) { + return false; + } + if (empty()) { + return true; + } + return is_str_less(get_data(), p_str); +} + +bool String::operator<(const wchar_t *p_str) const { + if (empty() && p_str[0] == 0) { + return false; + } + if (empty()) { + return true; + } + +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + return is_str_less(get_data(), String::utf16((const char16_t *)p_str).get_data()); +#else + // wchar_t is 32-bit + return is_str_less(get_data(), (const CharType *)p_str); +#endif +} + +bool String::operator<(const CharType *p_str) const { + if (empty() && p_str[0] == 0) { + return false; + } + if (empty()) { + return true; + } + + return is_str_less(get_data(), p_str); +} + +bool String::operator<(const String &p_str) const { + return operator<(p_str.get_data()); +} + +bool String::operator<=(const String &p_str) const { + return !(p_str < *this); +} + +bool String::operator>(const String &p_str) const { + return p_str < *this; +} + +bool String::operator>=(const String &p_str) const { + return !(*this < p_str); +} + +signed char String::nocasecmp_to(const String &p_str) const { + if (empty() && p_str.empty()) { + return 0; + } + if (empty()) { + return -1; + } + if (p_str.empty()) { + return 1; + } + + const CharType *that_str = p_str.get_data(); + const CharType *this_str = get_data(); + + while (true) { + if (*that_str == 0 && *this_str == 0) { + return 0; //we're equal + } else if (*this_str == 0) { + return -1; //if this is empty, and the other one is not, then we're less.. I think? + } else if (*that_str == 0) { + return 1; //otherwise the other one is smaller.. + } else if (_find_upper(*this_str) < _find_upper(*that_str)) { //more than + return -1; + } else if (_find_upper(*this_str) > _find_upper(*that_str)) { //less than + return 1; + } + + this_str++; + that_str++; + } +} + +signed char String::casecmp_to(const String &p_str) const { + if (empty() && p_str.empty()) { + return 0; + } + if (empty()) { + return -1; + } + if (p_str.empty()) { + return 1; + } + + const CharType *that_str = p_str.get_data(); + const CharType *this_str = get_data(); + + while (true) { + if (*that_str == 0 && *this_str == 0) { + return 0; //we're equal + } else if (*this_str == 0) { + return -1; //if this is empty, and the other one is not, then we're less.. I think? + } else if (*that_str == 0) { + return 1; //otherwise the other one is smaller.. + } else if (*this_str < *that_str) { //more than + return -1; + } else if (*this_str > *that_str) { //less than + return 1; + } + + this_str++; + that_str++; + } +} + +signed char String::naturalnocasecmp_to(const String &p_str) const { + const CharType *this_str = get_data(); + const CharType *that_str = p_str.get_data(); + + if (this_str && that_str) { + while (*this_str == '.' || *that_str == '.') { + if (*this_str++ != '.') { + return 1; + } + if (*that_str++ != '.') { + return -1; + } + if (!*that_str) { + return 1; + } + if (!*this_str) { + return -1; + } + } + + while (*this_str) { + if (!*that_str) { + return 1; + } else if (is_digit(*this_str)) { + if (!is_digit(*that_str)) { + return -1; + } + + // Keep ptrs to start of numerical sequences + const CharType *this_substr = this_str; + const CharType *that_substr = that_str; + + // Compare lengths of both numerical sequences, ignoring leading zeros + while (is_digit(*this_str)) { + this_str++; + } + while (is_digit(*that_str)) { + that_str++; + } + while (*this_substr == '0') { + this_substr++; + } + while (*that_substr == '0') { + that_substr++; + } + int this_len = this_str - this_substr; + int that_len = that_str - that_substr; + + if (this_len < that_len) { + return -1; + } else if (this_len > that_len) { + return 1; + } + + // If lengths equal, compare lexicographically + while (this_substr != this_str && that_substr != that_str) { + if (*this_substr < *that_substr) { + return -1; + } else if (*this_substr > *that_substr) { + return 1; + } + this_substr++; + that_substr++; + } + } else if (is_digit(*that_str)) { + return 1; + } else { + if (_find_upper(*this_str) < _find_upper(*that_str)) { //more than + return -1; + } else if (_find_upper(*this_str) > _find_upper(*that_str)) { //less than + return 1; + } + + this_str++; + that_str++; + } + } + if (*that_str) { + return -1; + } + } + + return 0; +} + +const CharType *String::get_data() const { + static const CharType zero = 0; + return size() ? &operator[](0) : &zero; +} + +bool String::is_valid_string() const { + int l = length(); + const CharType *src = get_data(); + bool valid = true; + for (int i = 0; i < l; i++) { + valid = valid && (src[i] < 0xd800 || (src[i] > 0xdfff && src[i] <= 0x10ffff)); + } + return valid; +} + +void String::print_unicode_error(const String &p_message, bool p_critical) const { + /* + if (p_critical) { + print_error(vformat("Unicode parsing error, some characters were replaced with spaces: %s", p_message)); + } else { + print_error(vformat("Unicode parsing error: %s", p_message)); + } + */ +} + +/* complex helpers */ + +String String::substr(int p_from, int p_chars) const { + if (p_chars == -1) { + p_chars = length() - p_from; + } + + if (empty() || p_from < 0 || p_from >= length() || p_chars <= 0) { + return ""; + } + + if ((p_from + p_chars) > length()) { + p_chars = length() - p_from; + } + + if (p_from == 0 && p_chars >= length()) { + return String(*this); + } + + String s = String(); + s.copy_from_unchecked(&get_data()[p_from], p_chars); + return s; +} + +String String::substr_index(const int start_index, const int end_index) const { + int s = length(); + + if (start_index < 0 || start_index >= s || end_index < 0) { + return ""; + } + + if (start_index > end_index) { + return ""; + } + + if (end_index >= s) { + return substr(start_index, (s - 1) - start_index); + } + + return substr(start_index, end_index - start_index); +} + +int String::find(const String &p_str, int p_from) const { + if (p_from < 0) { + return -1; + } + + const int src_len = p_str.length(); + + const int len = length(); + + if (src_len == 0 || len == 0) { + return -1; // won't find anything! + } + + const CharType *src = get_data(); + const CharType *str = p_str.get_data(); + + for (int i = p_from; i <= (len - src_len); i++) { + bool found = true; + for (int j = 0; j < src_len; j++) { + int read_pos = i + j; + + if (read_pos >= len) { + ERR_PRINT("read_pos>=len"); + return -1; + }; + + if (src[read_pos] != str[j]) { + found = false; + break; + } + } + + if (found) { + return i; + } + } + + return -1; +} + +int String::find(const char *p_str, int p_from) const { + if (p_from < 0) { + return -1; + } + + const int len = length(); + + if (len == 0) { + return -1; // won't find anything! + } + + const CharType *src = get_data(); + + int src_len = 0; + while (p_str[src_len] != '\0') { + src_len++; + } + + if (src_len == 1) { + const CharType needle = p_str[0]; + + for (int i = p_from; i < len; i++) { + if (src[i] == needle) { + return i; + } + } + + } else { + for (int i = p_from; i <= (len - src_len); i++) { + bool found = true; + for (int j = 0; j < src_len; j++) { + int read_pos = i + j; + + if (read_pos >= len) { + ERR_PRINT("read_pos>=len"); + return -1; + }; + + if (src[read_pos] != (CharType)p_str[j]) { + found = false; + break; + } + } + + if (found) { + return i; + } + } + } + + return -1; +} + +int String::find_char(const CharType &p_char, int p_from) const { + return _cowdata.find(p_char, p_from); +} + +int String::find_last(const String &p_str) const { + return rfind(p_str); +} + +int String::findn(const String &p_str, int p_from) const { + if (p_from < 0) { + return -1; + } + + int src_len = p_str.length(); + + if (src_len == 0 || length() == 0) { + return -1; // won't find anything! + } + + const CharType *srcd = get_data(); + + for (int i = p_from; i <= (length() - src_len); i++) { + bool found = true; + for (int j = 0; j < src_len; j++) { + int read_pos = i + j; + + if (read_pos >= length()) { + ERR_PRINT("read_pos>=length()"); + return -1; + }; + + CharType src = _find_lower(srcd[read_pos]); + CharType dst = _find_lower(p_str[j]); + + if (src != dst) { + found = false; + break; + } + } + + if (found) { + return i; + } + } + + return -1; +} + +int String::rfind(const String &p_str, int p_from) const { + // establish a limit + int limit = length() - p_str.length(); + if (limit < 0) { + return -1; + } + + // establish a starting point + if (p_from < 0) { + p_from = limit; + } else if (p_from > limit) { + p_from = limit; + } + + int src_len = p_str.length(); + int len = length(); + + if (src_len == 0 || len == 0) { + return -1; // won't find anything! + } + + const CharType *src = get_data(); + + for (int i = p_from; i >= 0; i--) { + bool found = true; + for (int j = 0; j < src_len; j++) { + int read_pos = i + j; + + if (read_pos >= len) { + ERR_PRINT("read_pos>=len"); + return -1; + }; + + if (src[read_pos] != p_str[j]) { + found = false; + break; + } + } + + if (found) { + return i; + } + } + + return -1; +} + +int String::rfindn(const String &p_str, int p_from) const { + // establish a limit + int limit = length() - p_str.length(); + if (limit < 0) { + return -1; + } + + // establish a starting point + if (p_from < 0) { + p_from = limit; + } else if (p_from > limit) { + p_from = limit; + } + + int src_len = p_str.length(); + int len = length(); + + if (src_len == 0 || len == 0) { + return -1; // won't find anything! + } + + const CharType *src = get_data(); + + for (int i = p_from; i >= 0; i--) { + bool found = true; + for (int j = 0; j < src_len; j++) { + int read_pos = i + j; + + if (read_pos >= len) { + ERR_PRINT("read_pos>=len"); + return -1; + }; + + CharType srcc = _find_lower(src[read_pos]); + CharType dstc = _find_lower(p_str[j]); + + if (srcc != dstc) { + found = false; + break; + } + } + + if (found) { + return i; + } + } + + return -1; +} + +int String::findmk(const Vector &p_keys, int p_from, int *r_key) const { + if (p_from < 0) { + return -1; + } + if (p_keys.size() == 0) { + return -1; + } + + //int src_len=p_str.length(); + const String *keys = &p_keys[0]; + int key_count = p_keys.size(); + int len = length(); + + if (len == 0) { + return -1; // won't find anything! + } + + const CharType *src = get_data(); + + for (int i = p_from; i < len; i++) { + bool found = true; + for (int k = 0; k < key_count; k++) { + found = true; + if (r_key) { + *r_key = k; + } + const CharType *cmp = keys[k].get_data(); + int l = keys[k].length(); + + for (int j = 0; j < l; j++) { + int read_pos = i + j; + + if (read_pos >= len) { + found = false; + break; + }; + + if (src[read_pos] != cmp[j]) { + found = false; + break; + } + } + if (found) { + break; + } + } + + if (found) { + return i; + } + } + + return -1; +} + +int String::find_first_difference_index(const String &p_str) const { + const int olen = p_str.length(); + const int len = length(); + const int c = len < olen ? len : olen; + + const CharType *p = get_data(); + const CharType *op = p_str.get_data(); + + for (int i = 0; i < c; ++i) { + if (p[i] != op[i]) { + return i; + } + } + + return c; +} + +bool String::is_word_at(const int index, const char *p_str) const { + int size = length(); + + ERR_FAIL_INDEX_V(index, size, false); + + int i = 0; + + while (p_str[i] != '\0') { + int iind = index + i; + + if (iind >= size) { + return false; + } + + if (operator[](iind) != (CharType)p_str[i]) { + return false; + } + + ++i; + } + + return true; +} +bool String::is_word_at(const int index, const String &p_str) const { + int size = length(); + + ERR_FAIL_INDEX_V(index, size, false); + + if (index + p_str.length() >= size) { + return false; + } + + for (int i = 0; i < p_str.length(); ++i) { + int iind = index + i; + + if (operator[](iind) != p_str[i]) { + return false; + } + } + + return true; +} + +bool String::match(const String &p_wildcard) const { + if (!p_wildcard.length() || !length()) { + return false; + } + + return _wildcard_match(p_wildcard.get_data(), get_data(), true); +} + +bool String::matchn(const String &p_wildcard) const { + if (!p_wildcard.length() || !length()) { + return false; + } + return _wildcard_match(p_wildcard.get_data(), get_data(), false); +} + +bool String::begins_with(const String &p_string) const { + int l = p_string.length(); + if (l > length()) { + return false; + } + + if (l == 0) { + return true; + } + + const CharType *p = &p_string[0]; + const CharType *s = &operator[](0); + + for (int i = 0; i < l; i++) { + if (p[i] != s[i]) { + return false; + } + } + + return true; +} + +bool String::begins_with(const char *p_string) const { + int l = length(); + if (l == 0 || !p_string) { + return false; + } + + const CharType *str = &operator[](0); + int i = 0; + + while (*p_string && i < l) { + if ((CharType)(*p_string) != str[i]) { + return false; + } + i++; + p_string++; + } + + return *p_string == 0; +} + +bool String::ends_with(const String &p_string) const { + int l = p_string.length(); + if (l > length()) { + return false; + } + + if (l == 0) { + return true; + } + + const CharType *p = &p_string[0]; + const CharType *s = &operator[](length() - l); + + for (int i = 0; i < l; i++) { + if (p[i] != s[i]) { + return false; + } + } + + return true; +} + +bool String::is_enclosed_in(const String &p_string) const { + return begins_with(p_string) && ends_with(p_string); +} + +bool String::is_subsequence_of(const String &p_string) const { + return _base_is_subsequence_of(p_string, false); +} + +bool String::is_subsequence_ofi(const String &p_string) const { + return _base_is_subsequence_of(p_string, true); +} + +bool String::is_quoted() const { + return is_enclosed_in("\"") || is_enclosed_in("'"); +} + +Vector String::bigrams() const { + int n_pairs = length() - 1; + Vector b; + if (n_pairs <= 0) { + return b; + } + b.resize(n_pairs); + for (int i = 0; i < n_pairs; i++) { + b.write[i] = substr(i, 2); + } + return b; +} + +// Similarity according to Sorensen-Dice coefficient +float String::similarity(const String &p_string) const { + if (operator==(p_string)) { + // Equal strings are totally similar + return 1.0f; + } + if (length() < 2 || p_string.length() < 2) { + // No way to calculate similarity without a single bigram + return 0.0f; + } + + Vector src_bigrams = bigrams(); + Vector tgt_bigrams = p_string.bigrams(); + + int src_size = src_bigrams.size(); + int tgt_size = tgt_bigrams.size(); + + float sum = src_size + tgt_size; + float inter = 0; + for (int i = 0; i < src_size; i++) { + for (int j = 0; j < tgt_size; j++) { + if (src_bigrams[i] == tgt_bigrams[j]) { + inter++; + break; + } + } + } + + return (2.0f * inter) / sum; +} + +/* +String String::format(const Variant &values, String placeholder) const { + String new_string = String(this->ptr()); + + if (values.get_type() == Variant::ARRAY) { + Array values_arr = values; + + for (int i = 0; i < values_arr.size(); i++) { + String i_as_str = String::num_int64(i); + + if (values_arr[i].get_type() == Variant::ARRAY) { //Array in Array structure [["name","RobotGuy"],[0,"pandemonium"],["strength",9000.91]] + Array value_arr = values_arr[i]; + + if (value_arr.size() == 2) { + Variant v_key = value_arr[0]; + String key = v_key; + + Variant v_val = value_arr[1]; + String val = v_val; + + new_string = new_string.replace(placeholder.replace("_", key), val); + } else { + ERR_PRINT(String("STRING.format Inner Array size != 2 ").ascii().get_data()); + } + } else { //Array structure ["RobotGuy","Logis","rookie"] + Variant v_val = values_arr[i]; + String val = v_val; + + if (placeholder.find("_") > -1) { + new_string = new_string.replace(placeholder.replace("_", i_as_str), val); + } else { + new_string = new_string.replace_first(placeholder, val); + } + } + } + } else if (values.get_type() == Variant::DICTIONARY) { + Dictionary d = values; + List keys; + d.get_key_list(&keys); + + for (List::Element *E = keys.front(); E; E = E->next()) { + String key = E->get(); + String val = d[E->get()]; + + new_string = new_string.replace(placeholder.replace("_", key), val); + } + } else { + ERR_PRINT(String("Invalid type: use Array or Dictionary.").ascii().get_data()); + } + + return new_string; +} +*/ + +String String::replace_first(const String &p_key, const String &p_with) const { + int pos = find(p_key); + if (pos >= 0) { + return substr(0, pos) + p_with + substr(pos + p_key.length(), length()); + } + + return *this; +} + +String String::replace(const String &p_key, const String &p_with) const { + String new_string; + int search_from = 0; + int result = 0; + + while ((result = find(p_key, search_from)) >= 0) { + new_string += substr(search_from, result - search_from); + new_string += p_with; + search_from = result + p_key.length(); + } + + if (search_from == 0) { + return *this; + } + + new_string += substr(search_from, length() - search_from); + + return new_string; +} + +String String::replace(const char *p_key, const char *p_with) const { + String new_string; + int search_from = 0; + int result = 0; + + while ((result = find(p_key, search_from)) >= 0) { + new_string += substr(search_from, result - search_from); + new_string += p_with; + int k = 0; + while (p_key[k] != '\0') { + k++; + } + search_from = result + k; + } + + if (search_from == 0) { + return *this; + } + + new_string += substr(search_from, length() - search_from); + + return new_string; +} + +String String::replacen(const String &p_key, const String &p_with) const { + String new_string; + int search_from = 0; + int result = 0; + + while ((result = findn(p_key, search_from)) >= 0) { + new_string += substr(search_from, result - search_from); + new_string += p_with; + search_from = result + p_key.length(); + } + + if (search_from == 0) { + return *this; + } + + new_string += substr(search_from, length() - search_from); + return new_string; +} + +String String::newline_to_br() const { + String r = replace("\r\n", "
"); + return r.replace("\n", "
"); +} + +String String::repeat(int p_count) const { + ERR_FAIL_COND_V_MSG(p_count < 0, "", "Parameter count should be a positive number."); + + int len = length(); + String new_string = *this; + new_string.resize(p_count * len + 1); + + CharType *dst = new_string.ptrw(); + int offset = 1; + int stride = 1; + while (offset < p_count) { + memcpy(dst + offset * len, dst, stride * len * sizeof(CharType)); + offset += stride; + stride = MIN(stride * 2, p_count - offset); + } + dst[p_count * len] = _null; + + return new_string; +} + +String String::insert(int p_at_pos, const String &p_string) const { + if (p_at_pos < 0) { + return *this; + } + + if (p_at_pos > length()) { + p_at_pos = length(); + } + + String pre; + if (p_at_pos > 0) { + pre = substr(0, p_at_pos); + } + + String post; + if (p_at_pos < length()) { + post = substr(p_at_pos, length() - p_at_pos); + } + + return pre + p_string + post; +} + +String String::pad_decimals(int p_digits) const { + String s = *this; + int c = s.find("."); + + if (c == -1) { + if (p_digits <= 0) { + return s; + } + s += "."; + c = s.length() - 1; + } else { + if (p_digits <= 0) { + return s.substr(0, c); + } + } + + if (s.length() - (c + 1) > p_digits) { + s = s.substr(0, c + p_digits + 1); + } else { + while (s.length() - (c + 1) < p_digits) { + s += "0"; + } + } + return s; +} + +String String::pad_zeros(int p_digits) const { + String s = *this; + int end = s.find("."); + + if (end == -1) { + end = s.length(); + } + + if (end == 0) { + return s; + } + + int begin = 0; + + while (begin < end && (s[begin] < '0' || s[begin] > '9')) { + begin++; + } + + if (begin >= end) { + return s; + } + + while (end - begin < p_digits) { + s = s.insert(begin, "0"); + end++; + } + + return s; +} + +String String::trim_prefix(const String &p_prefix) const { + String s = *this; + if (s.begins_with(p_prefix)) { + return s.substr(p_prefix.length(), s.length() - p_prefix.length()); + } + return s; +} + +String String::trim_suffix(const String &p_suffix) const { + String s = *this; + if (s.ends_with(p_suffix)) { + return s.substr(0, s.length() - p_suffix.length()); + } + return s; +} + +// Left-pad with a character. +String String::lpad(int min_length, const String &character) const { + String s = *this; + int padding = min_length - s.length(); + if (padding > 0) { + for (int i = 0; i < padding; i++) { + s = character + s; + } + } + + return s; +} + +// Right-pad with a character. +String String::rpad(int min_length, const String &character) const { + String s = *this; + int padding = min_length - s.length(); + if (padding > 0) { + for (int i = 0; i < padding; i++) { + s = s + character; + } + } + + return s; +} + +String String::quote(String quotechar) const { + return quotechar + *this + quotechar; +} + +String String::unquote() const { + if (!is_quoted()) { + return *this; + } + + return substr(1, length() - 2); +} + +String String::num(double p_num, int p_decimals) { + if (Math::is_nan(p_num)) { + return "nan"; + } + + if (Math::is_inf(p_num)) { + if (signbit(p_num)) { + return "-inf"; + } else { + return "inf"; + } + } + +#ifndef NO_USE_STDLIB + + if (p_decimals > 16) { + p_decimals = 16; + } + + char fmt[7]; + fmt[0] = '%'; + fmt[1] = '.'; + + if (p_decimals < 0) { + fmt[1] = 'l'; + fmt[2] = 'f'; + fmt[3] = 0; + + } else if (p_decimals < 10) { + fmt[2] = '0' + p_decimals; + fmt[3] = 'l'; + fmt[4] = 'f'; + fmt[5] = 0; + } else { + fmt[2] = '0' + (p_decimals / 10); + fmt[3] = '0' + (p_decimals % 10); + fmt[4] = 'l'; + fmt[5] = 'f'; + fmt[6] = 0; + } + char buf[256]; + +#if defined(__GNUC__) || defined(_MSC_VER) + snprintf(buf, 256, fmt, p_num); +#else + sprintf(buf, fmt, p_num); +#endif + + buf[255] = 0; + //destroy trailing zeroes + { + bool period = false; + int z = 0; + while (buf[z]) { + if (buf[z] == '.') { + period = true; + } + z++; + } + + if (period) { + z--; + while (z > 0) { + if (buf[z] == '0') { + buf[z] = 0; + } else if (buf[z] == '.') { + buf[z] = 0; + break; + } else { + break; + } + + z--; + } + } + } + + return buf; +#else + + String s; + String sd; + /* integer part */ + + bool neg = p_num < 0; + p_num = ABS(p_num); + int intn = (int)p_num; + + /* decimal part */ + + if (p_decimals > 0 || (p_decimals == -1 && (int)p_num != p_num)) { + double dec = p_num - (double)((int)p_num); + + int digit = 0; + if (p_decimals > MAX_DIGITS) + p_decimals = MAX_DIGITS; + + int dec_int = 0; + int dec_max = 0; + + while (true) { + dec *= 10.0; + dec_int = dec_int * 10 + (int)dec % 10; + dec_max = dec_max * 10 + 9; + digit++; + + if (p_decimals == -1) { + if (digit == MAX_DIGITS) //no point in going to infinite + break; + + if ((dec - (double)((int)dec)) < 1e-6) + break; + } + + if (digit == p_decimals) + break; + } + dec *= 10; + int last = (int)dec % 10; + + if (last > 5) { + if (dec_int == dec_max) { + dec_int = 0; + intn++; + } else { + dec_int++; + } + } + + String decimal; + for (int i = 0; i < digit; i++) { + char num[2] = { 0, 0 }; + num[0] = '0' + dec_int % 10; + decimal = num + decimal; + dec_int /= 10; + } + sd = '.' + decimal; + } + + if (intn == 0) + + s = "0"; + else { + while (intn) { + CharType num = '0' + (intn % 10); + intn /= 10; + s = num + s; + } + } + + s = s + sd; + if (neg) + s = "-" + s; + return s; +#endif +} + +String String::num_scientific(double p_num) { + if (Math::is_nan(p_num)) { + return "nan"; + } + + if (Math::is_inf(p_num)) { + if (signbit(p_num)) { + return "-inf"; + } else { + return "inf"; + } + } + +#ifndef NO_USE_STDLIB + + char buf[256]; + +#if defined(__GNUC__) || defined(_MSC_VER) + +#if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT) + // MinGW requires _set_output_format() to conform to C99 output for printf + unsigned int old_exponent_format = _set_output_format(_TWO_DIGIT_EXPONENT); +#endif + snprintf(buf, 256, "%lg", p_num); + +#if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT) + _set_output_format(old_exponent_format); +#endif + +#else + sprintf(buf, "%.16lg", p_num); +#endif + + buf[255] = 0; + + return buf; +#else + + return String::num(p_num); +#endif +} + +String String::num_real(double p_num) { + if (Math::is_nan(p_num)) { + return "nan"; + } + + if (Math::is_inf(p_num)) { + if (signbit(p_num)) { + return "-inf"; + } else { + return "inf"; + } + } + + String s; + String sd; + /* integer part */ + + bool neg = p_num < 0; + p_num = ABS(p_num); + int intn = (int)p_num; + + /* decimal part */ + + if ((int)p_num != p_num) { + double dec = p_num - (double)((int)p_num); + + int digit = 0; + int decimals = MAX_DIGITS; + + int dec_int = 0; + int dec_max = 0; + + while (true) { + dec *= 10.0; + dec_int = dec_int * 10 + (int)dec % 10; + dec_max = dec_max * 10 + 9; + digit++; + + if ((dec - (double)((int)dec)) < 1e-6) { + break; + } + + if (digit == decimals) { + break; + } + } + + dec *= 10; + int last = (int)dec % 10; + + if (last > 5) { + if (dec_int == dec_max) { + dec_int = 0; + intn++; + } else { + dec_int++; + } + } + + String decimal; + for (int i = 0; i < digit; i++) { + char num[2] = { 0, 0 }; + num[0] = '0' + dec_int % 10; + decimal = num + decimal; + dec_int /= 10; + } + sd = '.' + decimal; + } else { + sd = ".0"; + } + + if (intn == 0) { + s = "0"; + } else { + while (intn) { + CharType num = '0' + (intn % 10); + intn /= 10; + s = num + s; + } + } + + s = s + sd; + if (neg) { + s = "-" + s; + } + return s; +} + +String String::num_int64(int64_t p_num, int base, bool capitalize_hex) { + bool sign = p_num < 0; + + int64_t n = p_num; + + int chars = 0; + do { + n /= base; + chars++; + } while (n); + + if (sign) { + chars++; + } + String s; + s.resize(chars + 1); + CharType *c = s.ptrw(); + c[chars] = 0; + n = p_num; + do { + int mod = ABS(n % base); + if (mod >= 10) { + char a = (capitalize_hex ? 'A' : 'a'); + c[--chars] = a + (mod - 10); + } else { + c[--chars] = '0' + mod; + } + + n /= base; + } while (n); + + if (sign) { + c[0] = '-'; + } + + return s; +} + +String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) { + uint64_t n = p_num; + + int chars = 0; + do { + n /= base; + chars++; + } while (n); + + String s; + s.resize(chars + 1); + CharType *c = s.ptrw(); + c[chars] = 0; + n = p_num; + do { + int mod = n % base; + if (mod >= 10) { + char a = (capitalize_hex ? 'A' : 'a'); + c[--chars] = a + (mod - 10); + } else { + c[--chars] = '0' + mod; + } + + n /= base; + } while (n); + + return s; +} + +String String::chr(CharType p_char) { + CharType c[2] = { p_char, 0 }; + return String(c); +} + +String String::md5(const uint8_t *p_md5) { + return String::hex_encode_buffer(p_md5, 16); +} + +String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) { + static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + String ret; + char v[2] = { 0, 0 }; + + for (int i = 0; i < p_len; i++) { + v[0] = hex[p_buffer[i] >> 4]; + ret += v; + v[0] = hex[p_buffer[i] & 0xF]; + ret += v; + } + + return ret; +} + +String String::bool_num(bool p_val) { + if (p_val) { + return "1"; + } else { + return "0"; + } +} + +String String::bool_str(bool p_val) { + if (p_val) { + return "true"; + } else { + return "false"; + } +} + +bool String::is_numeric() const { + if (length() == 0) { + return false; + }; + + int s = 0; + if (operator[](0) == '-') { + ++s; + } + bool dot = false; + for (int i = s; i < length(); i++) { + CharType c = operator[](i); + if (c == '.') { + if (dot) { + return false; + }; + dot = true; + continue; + } + if (c < '0' || c > '9') { + return false; + }; + }; + + return true; // TODO: Use the parser below for this instead +}; + +bool String::is_zero() const { + int size = length(); + + if (size == 0) { + return false; + } + + int starti = 0; + + if (operator[](0) == '-') { + starti += 1; + } + + bool had_dot = false; + for (int i = starti; i < size; ++i) { + CharType c = operator[](i); + + if (c == '.') { + if (!had_dot) { + had_dot = true; + continue; + } else { + return false; + } + } + + if (c != '0') { + return false; + } + } + + return true; +} + +String String::capitalize() const { + String aux = this->camelcase_to_underscore(true).replace("_", " ").strip_edges(); + String cap; + for (int i = 0; i < aux.get_slice_count(" "); i++) { + String slice = aux.get_slicec(' ', i); + if (slice.length() > 0) { + slice[0] = _find_upper(slice[0]); + if (i > 0) { + cap += " "; + } + cap += slice; + } + } + + return cap; +} + +String String::camelcase_to_underscore(bool lowercase) const { + const CharType *cstr = get_data(); + String new_string; + const char A = 'A', Z = 'Z'; + const char a = 'a', z = 'z'; + int start_index = 0; + + for (int i = 1; i < this->size(); i++) { + bool is_upper = cstr[i] >= A && cstr[i] <= Z; + bool is_number = cstr[i] >= '0' && cstr[i] <= '9'; + bool are_next_2_lower = false; + bool is_next_lower = false; + bool is_next_number = false; + bool was_precedent_upper = cstr[i - 1] >= A && cstr[i - 1] <= Z; + bool was_precedent_number = cstr[i - 1] >= '0' && cstr[i - 1] <= '9'; + + if (i + 2 < this->size()) { + are_next_2_lower = cstr[i + 1] >= a && cstr[i + 1] <= z && cstr[i + 2] >= a && cstr[i + 2] <= z; + } + + if (i + 1 < this->size()) { + is_next_lower = cstr[i + 1] >= a && cstr[i + 1] <= z; + is_next_number = cstr[i + 1] >= '0' && cstr[i + 1] <= '9'; + } + + const bool cond_a = is_upper && !was_precedent_upper && !was_precedent_number; + const bool cond_b = was_precedent_upper && is_upper && are_next_2_lower; + const bool cond_c = is_number && !was_precedent_number; + const bool can_break_number_letter = is_number && !was_precedent_number && is_next_lower; + const bool can_break_letter_number = !is_number && was_precedent_number && (is_next_lower || is_next_number); + + bool should_split = cond_a || cond_b || cond_c || can_break_number_letter || can_break_letter_number; + if (should_split) { + new_string += this->substr(start_index, i - start_index) + "_"; + start_index = i; + } + } + + new_string += this->substr(start_index, this->size() - start_index); + return lowercase ? new_string.to_lower() : new_string; +} + +int String::get_slice_count(String p_splitter) const { + if (empty()) { + return 0; + } + if (p_splitter.empty()) { + return 0; + } + + int pos = 0; + int slices = 1; + + while ((pos = find(p_splitter, pos)) >= 0) { + slices++; + pos += p_splitter.length(); + } + + return slices; +} + +String String::get_slice(String p_splitter, int p_slice) const { + if (empty() || p_splitter.empty()) { + return ""; + } + + int pos = 0; + int prev_pos = 0; + //int slices=1; + if (p_slice < 0) { + return ""; + } + if (find(p_splitter) == -1) { + return *this; + } + + int i = 0; + while (true) { + pos = find(p_splitter, pos); + if (pos == -1) { + pos = length(); //reached end + } + + int from = prev_pos; + //int to=pos; + + if (p_slice == i) { + return substr(from, pos - from); + } + + if (pos == length()) { //reached end and no find + break; + } + pos += p_splitter.length(); + prev_pos = pos; + i++; + } + + return ""; //no find! +} + +String String::get_slicec(CharType p_splitter, int p_slice) const { + if (empty()) { + return String(); + } + + if (p_slice < 0) { + return String(); + } + + const CharType *c = this->ptr(); + int i = 0; + int prev = 0; + int count = 0; + while (true) { + if (c[i] == 0 || c[i] == p_splitter) { + if (p_slice == count) { + return substr(prev, i - prev); + } else if (c[i] == 0) { + return String(); + } else { + count++; + prev = i + 1; + } + } + + i++; + } +} + +Vector String::split(const String &p_splitter, bool p_allow_empty, int p_maxsplit) const { + Vector ret; + int from = 0; + int len = length(); + + while (true) { + int end = find(p_splitter, from); + if (end < 0) { + end = len; + } + if (p_allow_empty || (end > from)) { + if (p_maxsplit <= 0) { + ret.push_back(substr(from, end - from)); + } else { + // Put rest of the string and leave cycle. + if (p_maxsplit == ret.size()) { + ret.push_back(substr(from, len)); + break; + } + + // Otherwise, push items until positive limit is reached. + ret.push_back(substr(from, end - from)); + } + } + + if (end == len) { + break; + } + + from = end + p_splitter.length(); + } + + return ret; +} + +Vector String::rsplit(const String &p_splitter, bool p_allow_empty, int p_maxsplit) const { + Vector ret; + const int len = length(); + int remaining_len = len; + + while (true) { + if (remaining_len < p_splitter.length() || (p_maxsplit > 0 && p_maxsplit == ret.size())) { + // no room for another splitter or hit max splits, push what's left and we're done + if (p_allow_empty || remaining_len > 0) { + ret.push_back(substr(0, remaining_len)); + } + break; + } + + int left_edge = rfind(p_splitter, remaining_len - p_splitter.length()); + + if (left_edge < 0) { + // no more splitters, we're done + ret.push_back(substr(0, remaining_len)); + break; + } + + int substr_start = left_edge + p_splitter.length(); + if (p_allow_empty || substr_start < remaining_len) { + ret.push_back(substr(substr_start, remaining_len - substr_start)); + } + + remaining_len = left_edge; + } + + ret.invert(); + return ret; +} + +Vector String::split_spaces() const { + Vector ret; + int from = 0; + int i = 0; + int len = length(); + if (len == 0) { + return ret; + } + + bool inside = false; + + while (true) { + bool empty = operator[](i) < 33; + + if (i == 0) { + inside = !empty; + } + + if (!empty && !inside) { + inside = true; + from = i; + } + + if (empty && inside) { + ret.push_back(substr(from, i - from)); + inside = false; + } + + if (i == len) { + break; + } + i++; + } + + return ret; +} + +Vector String::split_floats(const String &p_splitter, bool p_allow_empty) const { + Vector ret; + int from = 0; + int len = length(); + + while (true) { + int end = find(p_splitter, from); + if (end < 0) { + end = len; + } + if (p_allow_empty || (end > from)) { + ret.push_back(String::to_double(&get_data()[from])); + } + + if (end == len) { + break; + } + + from = end + p_splitter.length(); + } + + return ret; +} + +Vector String::split_floats_mk(const Vector &p_splitters, bool p_allow_empty) const { + Vector ret; + int from = 0; + int len = length(); + + while (true) { + int idx; + int end = findmk(p_splitters, from, &idx); + int spl_len = 1; + if (end < 0) { + end = len; + } else { + spl_len = p_splitters[idx].length(); + } + + if (p_allow_empty || (end > from)) { + ret.push_back(String::to_double(&get_data()[from])); + } + + if (end == len) { + break; + } + + from = end + spl_len; + } + + return ret; +} + +Vector String::split_ints(const String &p_splitter, bool p_allow_empty) const { + Vector ret; + int from = 0; + int len = length(); + + while (true) { + int end = find(p_splitter, from); + if (end < 0) { + end = len; + } + if (p_allow_empty || (end > from)) { + ret.push_back(String::to_int(&get_data()[from], end - from)); + } + + if (end == len) { + break; + } + + from = end + p_splitter.length(); + } + + return ret; +} + +Vector String::split_ints_mk(const Vector &p_splitters, bool p_allow_empty) const { + Vector ret; + int from = 0; + int len = length(); + + while (true) { + int idx; + int end = findmk(p_splitters, from, &idx); + int spl_len = 1; + if (end < 0) { + end = len; + } else { + spl_len = p_splitters[idx].length(); + } + + if (p_allow_empty || (end > from)) { + ret.push_back(String::to_int(&get_data()[from], end - from)); + } + + if (end == len) { + break; + } + + from = end + spl_len; + } + + return ret; +} + +String String::join(const Vector &parts) const { + String ret; + for (int i = 0; i < parts.size(); ++i) { + if (i > 0) { + ret += *this; + } + ret += parts[i]; + } + return ret; +} + +CharType String::char_uppercase(CharType p_char) { + return _find_upper(p_char); +} + +CharType String::char_lowercase(CharType p_char) { + return _find_lower(p_char); +} + +String String::to_lower() const { + String lower = *this; + + for (int i = 0; i < lower.size(); i++) { + const CharType s = lower[i]; + const CharType t = _find_lower(s); + if (s != t) { // avoid copy on write + lower[i] = t; + } + } + + return lower; +} + +String String::to_upper() const { + String upper = *this; + + for (int i = 0; i < upper.size(); i++) { + const CharType s = upper[i]; + const CharType t = _find_upper(s); + if (s != t) { // avoid copy on write + upper[i] = t; + } + } + + return upper; +} + +int String::_count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const { + if (p_string.empty()) { + return 0; + } + int len = length(); + int slen = p_string.length(); + if (len < slen) { + return 0; + } + String str; + if (p_from >= 0 && p_to >= 0) { + if (p_to == 0) { + p_to = len; + } else if (p_from >= p_to) { + return 0; + } + if (p_from == 0 && p_to == len) { + str = String(); + str.copy_from_unchecked(&get_data()[0], len); + } else { + str = substr(p_from, p_to - p_from); + } + } else { + return 0; + } + int c = 0; + int idx = -1; + do { + idx = p_case_insensitive ? str.findn(p_string) : str.find(p_string); + if (idx != -1) { + str = str.substr(idx + slen, str.length() - slen); + ++c; + } + } while (idx != -1); + return c; +} + +int String::count(const String &p_string, int p_from, int p_to) const { + return _count(p_string, p_from, p_to, false); +} + +int String::countn(const String &p_string, int p_from, int p_to) const { + return _count(p_string, p_from, p_to, true); +} + +String String::left(int p_pos) const { + if (p_pos <= 0) { + return ""; + } + + if (p_pos >= length()) { + return *this; + } + + return substr(0, p_pos); +} + +String String::right(int p_pos) const { + if (p_pos >= length()) { + return ""; + } + + if (p_pos <= 0) { + return *this; + } + + return substr(p_pos, (length() - p_pos)); +} + +String String::indent(const String &p_prefix) const { + String new_string; + int line_start = 0; + + for (int i = 0; i < length(); i++) { + const CharType c = operator[](i); + if (c == '\n') { + if (i == line_start) { + new_string += c; // Leave empty lines empty. + } else { + new_string += p_prefix + substr(line_start, i - line_start + 1); + } + line_start = i + 1; + } + } + if (line_start != length()) { + new_string += p_prefix + substr(line_start); + } + return new_string; +} + +String String::dedent() const { + String new_string; + String indent; + bool has_indent = false; + bool has_text = false; + int line_start = 0; + int indent_stop = -1; + + for (int i = 0; i < length(); i++) { + CharType c = operator[](i); + if (c == '\n') { + if (has_text) { + new_string += substr(indent_stop, i - indent_stop); + } + new_string += "\n"; + has_text = false; + line_start = i + 1; + indent_stop = -1; + } else if (!has_text) { + if (c > 32) { + has_text = true; + if (!has_indent) { + has_indent = true; + indent = substr(line_start, i - line_start); + indent_stop = i; + } + } + if (has_indent && indent_stop < 0) { + int j = i - line_start; + if (j >= indent.length() || c != indent[j]) { + indent_stop = i; + } + } + } + } + + if (has_text) { + new_string += substr(indent_stop, length() - indent_stop); + } + + return new_string; +} + +String String::strip_edges(bool left, bool right) const { + int len = length(); + int beg = 0, end = len; + + if (left) { + for (int i = 0; i < len; i++) { + if (operator[](i) <= 32) { + beg++; + } else { + break; + } + } + } + + if (right) { + for (int i = (int)(len - 1); i >= 0; i--) { + if (operator[](i) <= 32) { + end--; + } else { + break; + } + } + } + + if (beg == 0 && end == len) { + return *this; + } + + return substr(beg, end - beg); +} + +String String::strip_escapes() const { + String new_string; + for (int i = 0; i < length(); i++) { + // Escape characters on first page of the ASCII table, before 32 (Space). + if (operator[](i) < 32) { + continue; + } + new_string += operator[](i); + } + + return new_string; +} + +String String::lstrip(const String &p_chars) const { + int len = length(); + int beg; + + for (beg = 0; beg < len; beg++) { + if (p_chars.find_char(get(beg)) == -1) { + break; + } + } + + if (beg == 0) { + return *this; + } + + return substr(beg, len - beg); +} + +String String::rstrip(const String &p_chars) const { + int len = length(); + int end; + + for (end = len - 1; end >= 0; end--) { + if (p_chars.find_char(get(end)) == -1) { + break; + } + } + + if (end == len - 1) { + return *this; + } + + return substr(0, end + 1); +} + +String String::get_extension() const { + int pos = rfind("."); + if (pos < 0 || pos < MAX(rfind("/"), rfind("\\"))) { + return ""; + } + + return substr(pos + 1, length()); +} + +String String::get_basename() const { + int pos = rfind("."); + if (pos < 0 || pos < MAX(rfind("/"), rfind("\\"))) { + return *this; + } + + return substr(0, pos); +} + +String String::plus_file(const String &p_file) const { + if (empty()) { + return p_file; + } + if (operator[](length() - 1) == '/' || (p_file.size() > 0 && p_file.operator[](0) == '/')) { + return *this + p_file; + } + return *this + "/" + p_file; +} + +CharType String::unicode_at(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, length(), 0); + return operator[](p_idx); +} + +CharType String::ord_at(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, length(), 0); + return operator[](p_idx); +} + +void String::erase(int p_pos, int p_chars) { + *this = left(p_pos) + substr(p_pos + p_chars, length() - ((p_pos + p_chars))); +} +CharString String::ascii(bool p_allow_extended) const { + if (!length()) { + return CharString(); + } + + CharString cs; + cs.resize(size()); + + for (int i = 0; i < size(); i++) { + CharType c = operator[](i); + if ((c <= 0x7f) || (c <= 0xff && p_allow_extended)) { + cs[i] = c; + } else { + //print_unicode_error(vformat("Invalid unicode codepoint (%x), cannot represent as ASCII/Latin-1", (uint32_t)c)); + cs[i] = 0x20; + } + } + + return cs; +} + +String String::utf8(const char *p_utf8, int p_len) { + String ret; + ret.parse_utf8(p_utf8, p_len); + + return ret; +} + +Error String::parse_utf8(const char *p_utf8, int p_len, bool p_skip_cr) { + if (!p_utf8) { + return ERR_INVALID_DATA; + } + + String aux; + + int cstr_size = 0; + int str_size = 0; + + /* HANDLE BOM (Byte Order Mark) */ + if (p_len < 0 || p_len >= 3) { + bool has_bom = uint8_t(p_utf8[0]) == 0xef && uint8_t(p_utf8[1]) == 0xbb && uint8_t(p_utf8[2]) == 0xbf; + if (has_bom) { + //8-bit encoding, byte order has no meaning in UTF-8, just skip it + if (p_len >= 0) { + p_len -= 3; + } + p_utf8 += 3; + } + } + + bool decode_error = false; + bool decode_failed = false; + { + const char *ptrtmp = p_utf8; + const char *ptrtmp_limit = &p_utf8[p_len]; + int skip = 0; + uint8_t c_start = 0; + while (ptrtmp != ptrtmp_limit && *ptrtmp) { + uint8_t c = *ptrtmp >= 0 ? *ptrtmp : uint8_t(256 + *ptrtmp); + + if (skip == 0) { + if (p_skip_cr && c == '\r') { + ptrtmp++; + continue; + } + /* Determine the number of characters in sequence */ + if ((c & 0x80) == 0) { + skip = 0; + } else if ((c & 0xe0) == 0xc0) { + skip = 1; + } else if ((c & 0xf0) == 0xe0) { + skip = 2; + } else if ((c & 0xf8) == 0xf0) { + skip = 3; + } else if ((c & 0xfc) == 0xf8) { + skip = 4; + } else if ((c & 0xfe) == 0xfc) { + skip = 5; + } else { + skip = 0; + //print_unicode_error(vformat("Invalid UTF-8 leading byte (%x)", c), true); + decode_failed = true; + } + c_start = c; + + if (skip == 1 && (c & 0x1e) == 0) { + //print_unicode_error(vformat("Overlong encoding (%x ...)", c)); + decode_error = true; + } + str_size++; + } else { + if ((c_start == 0xe0 && skip == 2 && c < 0xa0) || (c_start == 0xf0 && skip == 3 && c < 0x90) || (c_start == 0xf8 && skip == 4 && c < 0x88) || (c_start == 0xfc && skip == 5 && c < 0x84)) { + //print_unicode_error(vformat("Overlong encoding (%x %x ...)", c_start, c)); + decode_error = true; + } + if (c < 0x80 || c > 0xbf) { + //print_unicode_error(vformat("Invalid UTF-8 continuation byte (%x ... %x ...)", c_start, c), true); + decode_failed = true; + skip = 0; + } else { + --skip; + } + } + + cstr_size++; + ptrtmp++; + } + + if (skip) { + //rrprint_unicode_error(vformat("Missing %d UTF-8 continuation byte(s)", skip), true); + decode_failed = true; + } + } + + if (str_size == 0) { + clear(); + return OK; // empty string + } + + resize(str_size + 1); + CharType *dst = ptrw(); + dst[str_size] = 0; + + int skip = 0; + uint32_t unichar = 0; + while (cstr_size) { + uint8_t c = *p_utf8 >= 0 ? *p_utf8 : uint8_t(256 + *p_utf8); + + if (skip == 0) { + if (p_skip_cr && c == '\r') { + p_utf8++; + continue; + } + /* Determine the number of characters in sequence */ + if ((c & 0x80) == 0) { + *(dst++) = c; + unichar = 0; + skip = 0; + } else if ((c & 0xe0) == 0xc0) { + unichar = (0xff >> 3) & c; + skip = 1; + } else if ((c & 0xf0) == 0xe0) { + unichar = (0xff >> 4) & c; + skip = 2; + } else if ((c & 0xf8) == 0xf0) { + unichar = (0xff >> 5) & c; + skip = 3; + } else if ((c & 0xfc) == 0xf8) { + unichar = (0xff >> 6) & c; + skip = 4; + } else if ((c & 0xfe) == 0xfc) { + unichar = (0xff >> 7) & c; + skip = 5; + } else { + *(dst++) = 0x20; + unichar = 0; + skip = 0; + } + } else { + if (c < 0x80 || c > 0xbf) { + *(dst++) = 0x20; + skip = 0; + } else { + unichar = (unichar << 6) | (c & 0x3f); + --skip; + if (skip == 0) { + if (unichar == 0) { + //print_unicode_error("NUL character", true); + decode_failed = true; + unichar = 0x20; + } + if ((unichar & 0xfffff800) == 0xd800) { + //print_unicode_error(vformat("Unpaired surrogate (%x)", unichar)); + decode_error = true; + } + if (unichar > 0x10ffff) { + //print_unicode_error(vformat("Invalid unicode codepoint (%x)", unichar)); + decode_error = true; + } + *(dst++) = unichar; + } + } + } + + cstr_size--; + p_utf8++; + } + if (skip) { + *(dst++) = 0x20; + } + + if (decode_failed) { + return ERR_INVALID_DATA; + } else if (decode_error) { + return ERR_PARSE_ERROR; + } else { + return OK; + } +} + +CharString String::utf8() const { + int l = length(); + if (!l) { + return CharString(); + } + + const CharType *d = &operator[](0); + int fl = 0; + for (int i = 0; i < l; i++) { + uint32_t c = d[i]; + if (c <= 0x7f) { // 7 bits. + fl += 1; + } else if (c <= 0x7ff) { // 11 bits + fl += 2; + } else if (c <= 0xffff) { // 16 bits + fl += 3; + } else if (c <= 0x001fffff) { // 21 bits + fl += 4; + } else if (c <= 0x03ffffff) { // 26 bits + fl += 5; + //print_unicode_error(vformat("Invalid unicode codepoint (%x)", c)); + } else if (c <= 0x7fffffff) { // 31 bits + fl += 6; + //print_unicode_error(vformat("Invalid unicode codepoint (%x)", c)); + } else { + fl += 1; + //print_unicode_error(vformat("Invalid unicode codepoint (%x), cannot represent as UTF-8", c), true); + } + } + + CharString utf8s; + if (fl == 0) { + return utf8s; + } + + utf8s.resize(fl + 1); + uint8_t *cdst = (uint8_t *)utf8s.get_data(); + +#define APPEND_CHAR(m_c) *(cdst++) = m_c + + for (int i = 0; i < l; i++) { + uint32_t c = d[i]; + + if (c <= 0x7f) { // 7 bits. + APPEND_CHAR(c); + } else if (c <= 0x7ff) { // 11 bits + APPEND_CHAR(uint32_t(0xc0 | ((c >> 6) & 0x1f))); // Top 5 bits. + APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. + } else if (c <= 0xffff) { // 16 bits + APPEND_CHAR(uint32_t(0xe0 | ((c >> 12) & 0x0f))); // Top 4 bits. + APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Middle 6 bits. + APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. + } else if (c <= 0x001fffff) { // 21 bits + APPEND_CHAR(uint32_t(0xf0 | ((c >> 18) & 0x07))); // Top 3 bits. + APPEND_CHAR(uint32_t(0x80 | ((c >> 12) & 0x3f))); // Upper middle 6 bits. + APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Lower middle 6 bits. + APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. + } else if (c <= 0x03ffffff) { // 26 bits + APPEND_CHAR(uint32_t(0xf8 | ((c >> 24) & 0x03))); // Top 2 bits. + APPEND_CHAR(uint32_t(0x80 | ((c >> 18) & 0x3f))); // Upper middle 6 bits. + APPEND_CHAR(uint32_t(0x80 | ((c >> 12) & 0x3f))); // middle 6 bits. + APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Lower middle 6 bits. + APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. + } else if (c <= 0x7fffffff) { // 31 bits + APPEND_CHAR(uint32_t(0xfc | ((c >> 30) & 0x01))); // Top 1 bit. + APPEND_CHAR(uint32_t(0x80 | ((c >> 24) & 0x3f))); // Upper upper middle 6 bits. + APPEND_CHAR(uint32_t(0x80 | ((c >> 18) & 0x3f))); // Lower upper middle 6 bits. + APPEND_CHAR(uint32_t(0x80 | ((c >> 12) & 0x3f))); // Upper lower middle 6 bits. + APPEND_CHAR(uint32_t(0x80 | ((c >> 6) & 0x3f))); // Lower lower middle 6 bits. + APPEND_CHAR(uint32_t(0x80 | (c & 0x3f))); // Bottom 6 bits. + } else { + APPEND_CHAR(0x20); + } + } +#undef APPEND_CHAR + *cdst = 0; //trailing zero + + return utf8s; +} + +int String::utf8_byte_length() const { + int l = length(); + if (!l) { + return 0; + } + + const CharType *d = &operator[](0); + int fl = 0; + for (int i = 0; i < l; i++) { + uint32_t c = d[i]; + if (c <= 0x7f) { // 7 bits. + fl += 1; + } else if (c <= 0x7ff) { // 11 bits + fl += 2; + } else if (c <= 0xffff) { // 16 bits + fl += 3; + } else if (c <= 0x001fffff) { // 21 bits + fl += 4; + } else if (c <= 0x03ffffff) { // 26 bits + fl += 5; + //print_unicode_error(vformat("Invalid unicode codepoint (%x)", c)); + } else if (c <= 0x7fffffff) { // 31 bits + fl += 6; + //print_unicode_error(vformat("Invalid unicode codepoint (%x)", c)); + } else { + fl += 1; + //print_unicode_error(vformat("Invalid unicode codepoint (%x), cannot represent as UTF-8", c), true); + } + } + + return fl; +} + +String String::utf16(const char16_t *p_utf16, int p_len) { + String ret; + ret.parse_utf16(p_utf16, p_len); + + return ret; +} + +Error String::parse_utf16(const char16_t *p_utf16, int p_len) { + if (!p_utf16) { + return ERR_INVALID_DATA; + } + + String aux; + + int cstr_size = 0; + int str_size = 0; + + /* HANDLE BOM (Byte Order Mark) */ + bool byteswap = false; // assume correct endianness if no BOM found + if (p_len < 0 || p_len >= 1) { + bool has_bom = false; + if (uint16_t(p_utf16[0]) == 0xfeff) { // correct BOM, read as is + has_bom = true; + byteswap = false; + } else if (uint16_t(p_utf16[0]) == 0xfffe) { // backwards BOM, swap bytes + has_bom = true; + byteswap = true; + } + if (has_bom) { + if (p_len >= 0) { + p_len -= 1; + } + p_utf16 += 1; + } + } + + bool decode_error = false; + { + const char16_t *ptrtmp = p_utf16; + const char16_t *ptrtmp_limit = &p_utf16[p_len]; + //uint32_t c_prev = 0; + bool skip = false; + while (ptrtmp != ptrtmp_limit && *ptrtmp) { + uint32_t c = (byteswap) ? BSWAP16(*ptrtmp) : *ptrtmp; + + if ((c & 0xfffffc00) == 0xd800) { // lead surrogate + if (skip) { + //print_unicode_error(vformat("Unpaired lead surrogate (%x [trail?] %x)", c_prev, c)); + decode_error = true; + } + skip = true; + } else if ((c & 0xfffffc00) == 0xdc00) { // trail surrogate + if (skip) { + str_size--; + } else { + //print_unicode_error(vformat("Unpaired trail surrogate (%x [lead?] %x)", c_prev, c)); + decode_error = true; + } + skip = false; + } else { + skip = false; + } + + //c_prev = c; + str_size++; + cstr_size++; + ptrtmp++; + } + + if (skip) { + //print_unicode_error(vformat("Unpaired lead surrogate (%x [eol])", c_prev)); + decode_error = true; + } + } + + if (str_size == 0) { + clear(); + return OK; // empty string + } + + resize(str_size + 1); + CharType *dst = ptrw(); + dst[str_size] = 0; + + bool skip = false; + uint32_t c_prev = 0; + while (cstr_size) { + uint32_t c = (byteswap) ? BSWAP16(*p_utf16) : *p_utf16; + + if ((c & 0xfffffc00) == 0xd800) { // lead surrogate + if (skip) { + *(dst++) = c_prev; // unpaired, store as is + } + skip = true; + } else if ((c & 0xfffffc00) == 0xdc00) { // trail surrogate + if (skip) { + *(dst++) = (c_prev << 10UL) + c - ((0xd800 << 10UL) + 0xdc00 - 0x10000); // decode pair + } else { + *(dst++) = c; // unpaired, store as is + } + skip = false; + } else { + *(dst++) = c; + skip = false; + } + + cstr_size--; + p_utf16++; + c_prev = c; + } + + if (skip) { + *(dst++) = c_prev; + } + + if (decode_error) { + return ERR_PARSE_ERROR; + } else { + return OK; + } +} + +Char16String String::utf16() const { + int l = length(); + if (!l) { + return Char16String(); + } + + const CharType *d = &operator[](0); + int fl = 0; + for (int i = 0; i < l; i++) { + uint32_t c = d[i]; + if (c <= 0xffff) { // 16 bits. + fl += 1; + if ((c & 0xfffff800) == 0xd800) { + //print_unicode_error(vformat("Unpaired surrogate (%x)", c)); + } + } else if (c <= 0x10ffff) { // 32 bits. + fl += 2; + } else { + //print_unicode_error(vformat("Invalid unicode codepoint (%x), cannot represent as UTF-16", c), true); + fl += 1; + } + } + + Char16String utf16s; + if (fl == 0) { + return utf16s; + } + + utf16s.resize(fl + 1); + uint16_t *cdst = (uint16_t *)utf16s.get_data(); + +#define APPEND_CHAR(m_c) *(cdst++) = m_c + + for (int i = 0; i < l; i++) { + uint32_t c = d[i]; + + if (c <= 0xffff) { // 16 bits. + APPEND_CHAR(c); + } else if (c <= 0x10ffff) { // 32 bits. + APPEND_CHAR(uint32_t((c >> 10) + 0xd7c0)); // lead surrogate. + APPEND_CHAR(uint32_t((c & 0x3ff) | 0xdc00)); // trail surrogate. + } else { + APPEND_CHAR(0x20); + } + } +#undef APPEND_CHAR + *cdst = 0; //trailing zero + + return utf16s; +} + +int String::utf16_byte_length() const { + int l = length(); + if (!l) { + return 0; + } + + const CharType *d = &operator[](0); + int fl = 0; + for (int i = 0; i < l; i++) { + uint32_t c = d[i]; + if (c <= 0xffff) { // 16 bits. + fl += 1; + if ((c & 0xfffff800) == 0xd800) { + //print_unicode_error(vformat("Unpaired surrogate (%x)", c)); + } + } else if (c <= 0x10ffff) { // 32 bits. + fl += 2; + } else { + //print_unicode_error(vformat("Invalid unicode codepoint (%x), cannot represent as UTF-16", c), true); + fl += 1; + } + } + + return fl; +} + +uint32_t String::hash(const char *p_cstr) { + uint32_t hashv = 5381; + uint32_t c; + + while ((c = *p_cstr++)) { + hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + } + + return hashv; +} + +uint32_t String::hash(const char *p_cstr, int p_len) { + uint32_t hashv = 5381; + for (int i = 0; i < p_len; i++) { + hashv = ((hashv << 5) + hashv) + p_cstr[i]; /* hash * 33 + c */ + } + + return hashv; +} + +uint32_t String::hash(const wchar_t *p_cstr) { + uint32_t hashv = 5381; + uint32_t c; + + while ((c = *p_cstr++)) { + hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + } + + return hashv; +} + +uint32_t String::hash(const wchar_t *p_cstr, int p_len) { + uint32_t hashv = 5381; + for (int i = 0; i < p_len; i++) { + hashv = ((hashv << 5) + hashv) + p_cstr[i]; /* hash * 33 + c */ + } + + return hashv; +} + +uint32_t String::hash(const CharType *p_cstr) { + uint32_t hashv = 5381; + uint32_t c; + + while ((c = *p_cstr++)) { + hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + } + + return hashv; +} + +uint32_t String::hash(const CharType *p_cstr, int p_len) { + uint32_t hashv = 5381; + for (int i = 0; i < p_len; i++) { + hashv = ((hashv << 5) + hashv) + p_cstr[i]; /* hash * 33 + c */ + } + + return hashv; +} + +uint32_t String::hash() const { + /* simple djb2 hashing */ + + const CharType *chr = get_data(); + uint32_t hashv = 5381; + uint32_t c; + + while ((c = *chr++)) { + hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + } + + return hashv; +} + +uint64_t String::hash64() const { + /* simple djb2 hashing */ + + const CharType *chr = get_data(); + uint64_t hashv = 5381; + uint64_t c; + + while ((c = *chr++)) { + hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + } + + return hashv; +} + +bool String::is_abs_path() const { + if (length() > 1) { + return (operator[](0) == '/' || operator[](0) == '\\' || find(":/") != -1 || find(":\\") != -1); + } else if ((length()) == 1) { + return (operator[](0) == '/' || operator[](0) == '\\'); + } else { + return false; + } +} + +bool String::is_rel_path() const { + return !is_abs_path(); +} + +bool String::is_resource_file() const { + return begins_with("res://") && find("::") == -1; +} + +String String::path_to(const String &p_path) const { + String src = this->replace("\\", "/"); + String dst = p_path.replace("\\", "/"); + if (!src.ends_with("/")) { + src += "/"; + } + if (!dst.ends_with("/")) { + dst += "/"; + } + + String base; + + if (src.begins_with("res://") && dst.begins_with("res://")) { + base = "res:/"; + src = src.replace("res://", "/"); + dst = dst.replace("res://", "/"); + + } else if (src.begins_with("user://") && dst.begins_with("user://")) { + base = "user:/"; + src = src.replace("user://", "/"); + dst = dst.replace("user://", "/"); + + } else if (src.begins_with("/") && dst.begins_with("/")) { + //nothing + } else { + //dos style + String src_begin = src.get_slicec('/', 0); + String dst_begin = dst.get_slicec('/', 0); + + if (src_begin != dst_begin) { + return p_path; //impossible to do this + } + + base = src_begin; + src = src.substr(src_begin.length(), src.length()); + dst = dst.substr(dst_begin.length(), dst.length()); + } + + //remove leading and trailing slash and split + Vector src_dirs = src.substr(1, src.length() - 2).split("/"); + Vector dst_dirs = dst.substr(1, dst.length() - 2).split("/"); + + //find common parent + int common_parent = 0; + + while (true) { + if (src_dirs.size() == common_parent) { + break; + } + if (dst_dirs.size() == common_parent) { + break; + } + if (src_dirs[common_parent] != dst_dirs[common_parent]) { + break; + } + common_parent++; + } + + common_parent--; + + String dir; + + for (int i = src_dirs.size() - 1; i > common_parent; i--) { + dir += "../"; + } + + for (int i = common_parent + 1; i < dst_dirs.size(); i++) { + dir += dst_dirs[i] + "/"; + } + + if (dir.length() == 0) { + dir = "./"; + } + return dir; +} + +String String::path_to_file(const String &p_path) const { + // Don't get base dir for src, this is expected to be a dir already. + String src = this->replace("\\", "/"); + String dst = p_path.replace("\\", "/").get_base_dir(); + String rel = src.path_to(dst); + if (rel == dst) { // failed + return p_path; + } else { + return rel + p_path.get_file(); + } +} + +String String::get_base_dir() const { + int end = 0; + + // url scheme style base + int basepos = find("://"); + if (basepos != -1) { + end = basepos + 3; + } + + // windows top level directory base + if (end == 0) { + basepos = find(":/"); + if (basepos == -1) { + basepos = find(":\\"); + } + if (basepos != -1) { + end = basepos + 2; + } + } + + // Windows UNC network share path. + if (end == 0) { + if (is_network_share_path()) { + basepos = find("/", 2); + if (basepos == -1) { + basepos = find("\\", 2); + } + int servpos = find("/", basepos + 1); + if (servpos == -1) { + servpos = find("\\", basepos + 1); + } + if (servpos != -1) { + end = servpos + 1; + } + } + } + + // unix root directory base + if (end == 0) { + if (begins_with("/")) { + end = 1; + } + } + + String rs; + String base; + if (end != 0) { + rs = substr(end, length()); + base = substr(0, end); + } else { + rs = *this; + } + + int sep = MAX(rs.rfind("/"), rs.rfind("\\")); + if (sep == -1) { + return base; + } + + return base + rs.substr(0, sep); +} + +String String::get_file() const { + int sep = MAX(rfind("/"), rfind("\\")); + if (sep == -1) { + return *this; + } + + return substr(sep + 1, length()); +} + +String String::humanize_size(uint64_t p_size) { + uint64_t _div = 1; + Vector prefixes; + prefixes.push_back(RTR("B")); + prefixes.push_back(RTR("KiB")); + prefixes.push_back(RTR("MiB")); + prefixes.push_back(RTR("GiB")); + prefixes.push_back(RTR("TiB")); + prefixes.push_back(RTR("PiB")); + prefixes.push_back(RTR("EiB")); + + int prefix_idx = 0; + + while (prefix_idx < prefixes.size() - 1 && p_size > (_div * 1024)) { + _div *= 1024; + prefix_idx++; + } + + const int digits = prefix_idx > 0 ? _humanize_digits(p_size / _div) : 0; + const double divisor = prefix_idx > 0 ? _div : 1; + + return String::num(p_size / divisor).pad_decimals(digits) + " " + prefixes[prefix_idx]; +} + +String String::simplify_path() const { + String s = *this; + String drive; + if (s.begins_with("local://")) { + drive = "local://"; + s = s.substr(8, s.length()); + } else if (s.begins_with("res://")) { + drive = "res://"; + s = s.substr(6, s.length()); + } else if (s.begins_with("user://")) { + drive = "user://"; + s = s.substr(7, s.length()); + } else if (is_network_share_path()) { + drive = s.substr(0, 2); + s = s.substr(2, s.length() - 2); + } else if (s.begins_with("/") || s.begins_with("\\")) { + drive = s.substr(0, 1); + s = s.substr(1, s.length() - 1); + } else { + int p = s.find(":/"); + if (p == -1) { + p = s.find(":\\"); + } + if (p != -1 && p < s.find("/")) { + drive = s.substr(0, p + 2); + s = s.substr(p + 2, s.length()); + } + } + + s = s.replace("\\", "/"); + while (true) { // in case of using 2 or more slash + String compare = s.replace("//", "/"); + if (s == compare) { + break; + } else { + s = compare; + } + } + Vector dirs = s.split("/", false); + + for (int i = 0; i < dirs.size(); i++) { + String d = dirs[i]; + if (d == ".") { + dirs.remove(i); + i--; + } else if (d == "..") { + if (i == 0) { + dirs.remove(i); + i--; + } else { + dirs.remove(i); + dirs.remove(i - 1); + i -= 2; + } + } + } + + s = ""; + + for (int i = 0; i < dirs.size(); i++) { + if (i > 0) { + s += "/"; + } + s += dirs[i]; + } + + return drive + s; +} + +bool String::is_network_share_path() const { + return begins_with("//") || begins_with("\\\\"); +} + +String String::append_path(const char *path) const { + if (path[0] == '\0') { + return *this; + } + + String ret = *this; + int size = length(); + + if (size == 0) { + ret += path; + return ret; + } + + int sindex = 0; + char ch = path[sindex]; + while (ch == '/' || ch == '\\') { + if (ch == '\0') { + return ret; + } + + ch = path[++sindex]; + } + + // /////folder + // ^ (sindex) + + if (ret.ends_with("/") || ret.ends_with("\\")) { + ret += &path[sindex]; + } else { + if (sindex > 0) { + ret += '/'; + ret += &path[sindex - 1]; + } else { + ret += '/'; + ret += &path[sindex]; + } + } + + return ret; +} + +String String::append_path(const String &path) const { + if (path.length() == 0) { + return *this; + } + + int size = length(); + + if (size == 0) { + return path; + } + + int sindex = 0; + int ts = path.size() - 1; + char ch = path[sindex]; + while (ch == '/' || ch == '\\') { + if (sindex == ts) { + return *this; + } + + ch = path[++sindex]; + } + + String ret = *this; + + // /////folder + // ^ (sindex) + + if (ret.ends_with("/") || ret.ends_with("\\")) { + ret += &path[sindex]; + } else { + if (sindex > 0) { + ret += '/'; + ret += &path[sindex - 1]; + } else { + ret += '/'; + ret += &path[sindex]; + } + } + + return ret; +} + +String String::path_clean_end_slash() const { + // _size > 1, so if root is given ("/"), it will not be removed + + String ret = *this; + + int strip_to = ret.length() - 1; + + CharType c = ret[strip_to]; + + while (strip_to > 1 && (c == '/' || c == '\\')) { + --strip_to; + c = ret[strip_to]; + } + + if (ret.length() != strip_to + 1) { + ret.set_length(strip_to + 1); + } + + return ret; +} +String String::path_ensure_end_slash() const { + // Don't add if empty string, as it would make it root on linux, which can easily become a serious bug + + String ret = *this; + + if (ret.length() == 0) { + return ret; + } + + if (!(ret.ends_with("/") || ret.ends_with("\\"))) { + ret += "/"; + } + + return ret; +} + +String String::path_get_prev_dir() const { + int size = length(); + + if (size == 0) { + return "/"; + } + + int seind = size - 1; + while (seind > 0 && (operator[](seind) == '/' || operator[](seind) == '\\')) { + --seind; + } + + if (seind == 0) { + // ///////// + // or + // a/////// + // no prev dir + + return "/"; + } + + // fol/fol2/fol3// + // ^ (seind) + + while (seind > 0 && (operator[](seind) != '/' && operator[](seind) != '\\')) { + --seind; + } + + // fol/fol2/fol3// + // ^ (seind) + + //--seind; + + if (seind <= 0) { + return "/"; + } + + return substr_index(0, seind); +} + +String String::xml_escape(bool p_escape_quotes) const { + String str = *this; + str = str.replace("&", "&"); + str = str.replace("<", "<"); + str = str.replace(">", ">"); + if (p_escape_quotes) { + str = str.replace("'", "'"); + str = str.replace("\"", """); + } + /* + for (int i=1;i<32;i++) { + + char chr[2]={i,0}; + str=str.replace(chr,"&#"+String::num(i)+";"); + }*/ + return str; +} + +static _FORCE_INLINE_ int _xml_unescape(const CharType *p_src, int p_src_len, CharType *p_dst) { + int len = 0; + while (p_src_len) { + if (*p_src == '&') { + int eat = 0; + + if (p_src_len >= 4 && p_src[1] == '#') { + CharType c = 0; + bool overflow = false; + if (p_src[2] == 'x') { + // Hex entity &#x; + for (int i = 3; i < p_src_len; i++) { + eat = i + 1; + CharType ct = p_src[i]; + if (ct == ';') { + break; + } else if (ct >= '0' && ct <= '9') { + ct = ct - '0'; + } else if (ct >= 'a' && ct <= 'f') { + ct = (ct - 'a') + 10; + } else if (ct >= 'A' && ct <= 'F') { + ct = (ct - 'A') + 10; + } else { + break; + } + if (c > (WCHAR_MAX >> 4)) { + overflow = true; + break; + } + c <<= 4; + c |= ct; + } + } else { + // Decimal entity &#; + for (int i = 2; i < p_src_len; i++) { + eat = i + 1; + CharType ct = p_src[i]; + if (ct == ';' || ct < '0' || ct > '9') { + break; + } + } + if (p_src[eat - 1] == ';') { + int64_t val = String::to_int(p_src + 2, eat - 3); + if (val > 0 && val <= WCHAR_MAX) { + c = (CharType)val; + } else { + overflow = true; + } + } + } + + // Value must be non-zero, in the range of CharType, + // actually end with ';'. If invalid, leave the entity as-is + if (c == '\0' || overflow || p_src[eat - 1] != ';') { + eat = 1; + c = *p_src; + } + if (p_dst) { + *p_dst = c; + } + } else if (p_src_len >= 4 && p_src[1] == 'g' && p_src[2] == 't' && p_src[3] == ';') { + if (p_dst) { + *p_dst = '>'; + } + eat = 4; + } else if (p_src_len >= 4 && p_src[1] == 'l' && p_src[2] == 't' && p_src[3] == ';') { + if (p_dst) { + *p_dst = '<'; + } + eat = 4; + } else if (p_src_len >= 5 && p_src[1] == 'a' && p_src[2] == 'm' && p_src[3] == 'p' && p_src[4] == ';') { + if (p_dst) { + *p_dst = '&'; + } + eat = 5; + } else if (p_src_len >= 6 && p_src[1] == 'q' && p_src[2] == 'u' && p_src[3] == 'o' && p_src[4] == 't' && p_src[5] == ';') { + if (p_dst) { + *p_dst = '"'; + } + eat = 6; + } else if (p_src_len >= 6 && p_src[1] == 'a' && p_src[2] == 'p' && p_src[3] == 'o' && p_src[4] == 's' && p_src[5] == ';') { + if (p_dst) { + *p_dst = '\''; + } + eat = 6; + } else { + if (p_dst) { + *p_dst = *p_src; + } + eat = 1; + } + + if (p_dst) { + p_dst++; + } + + len++; + p_src += eat; + p_src_len -= eat; + } else { + if (p_dst) { + *p_dst = *p_src; + p_dst++; + } + len++; + p_src++; + p_src_len--; + } + } + + return len; +} + +String String::xml_unescape() const { + String str; + int l = length(); + int len = _xml_unescape(get_data(), l, nullptr); + if (len == 0) { + return String(); + } + str.resize(len + 1); + _xml_unescape(get_data(), l, str.ptrw()); + str[len] = 0; + return str; +} + +String String::http_escape() const { + const CharString temp = utf8(); + String res; + for (int i = 0; i < temp.length(); ++i) { + uint8_t ord = temp[i]; + if (ord == '.' || ord == '-' || ord == '_' || ord == '~' || + (ord >= 'a' && ord <= 'z') || + (ord >= 'A' && ord <= 'Z') || + (ord >= '0' && ord <= '9')) { + res += ord; + } else { + char p[4] = { '%', 0, 0, 0 }; + static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + p[1] = hex[ord >> 4]; + p[2] = hex[ord & 0xF]; + res += p; + } + } + return res; +} + +String String::http_unescape() const { + String res; + for (int i = 0; i < length(); ++i) { + if (ord_at(i) == '%' && i + 2 < length()) { + CharType ord1 = ord_at(i + 1); + if ((ord1 >= '0' && ord1 <= '9') || (ord1 >= 'A' && ord1 <= 'Z')) { + CharType ord2 = ord_at(i + 2); + if ((ord2 >= '0' && ord2 <= '9') || (ord2 >= 'A' && ord2 <= 'Z')) { + char bytes[3] = { (char)ord1, (char)ord2, 0 }; + res += (char)strtol(bytes, nullptr, 16); + i += 2; + } + } else { + res += ord_at(i); + } + } else { + res += ord_at(i); + } + } + return String::utf8(res.ascii()); +} + +String String::uri_encode() const { + const CharString temp = utf8(); + String res; + for (int i = 0; i < temp.length(); ++i) { + uint8_t ord = temp[i]; + if (ord == '.' || ord == '-' || ord == '~' || is_ascii_identifier_char(ord)) { + res += ord; + } else { + char p[4] = { '%', 0, 0, 0 }; + static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + p[1] = hex[ord >> 4]; + p[2] = hex[ord & 0xF]; + res += p; + } + } + return res; +} + +String String::uri_decode() const { + CharString src = utf8(); + CharString res; + for (int i = 0; i < src.length(); ++i) { + if (src[i] == '%' && i + 2 < src.length()) { + char ord1 = src[i + 1]; + if (is_digit(ord1) || is_ascii_upper_case(ord1)) { + char ord2 = src[i + 2]; + if (is_digit(ord2) || is_ascii_upper_case(ord2)) { + char bytes[3] = { (char)ord1, (char)ord2, 0 }; + res += (char)strtol(bytes, nullptr, 16); + i += 2; + } + } else { + res += src[i]; + } + } else if (src[i] == '+') { + res += ' '; + } else { + res += src[i]; + } + } + return String::utf8(res); +} + +String String::c_escape() const { + String escaped = *this; + escaped = escaped.replace("\\", "\\\\"); + escaped = escaped.replace("\a", "\\a"); + escaped = escaped.replace("\b", "\\b"); + escaped = escaped.replace("\f", "\\f"); + escaped = escaped.replace("\n", "\\n"); + escaped = escaped.replace("\r", "\\r"); + escaped = escaped.replace("\t", "\\t"); + escaped = escaped.replace("\v", "\\v"); + escaped = escaped.replace("\'", "\\'"); + escaped = escaped.replace("\?", "\\?"); + escaped = escaped.replace("\"", "\\\""); + + return escaped; +} + +String String::c_escape_multiline() const { + String escaped = *this; + escaped = escaped.replace("\\", "\\\\"); + escaped = escaped.replace("\"", "\\\""); + + return escaped; +} + +String String::c_unescape() const { + String escaped = *this; + escaped = escaped.replace("\\a", "\a"); + escaped = escaped.replace("\\b", "\b"); + escaped = escaped.replace("\\f", "\f"); + escaped = escaped.replace("\\n", "\n"); + escaped = escaped.replace("\\r", "\r"); + escaped = escaped.replace("\\t", "\t"); + escaped = escaped.replace("\\v", "\v"); + escaped = escaped.replace("\\'", "\'"); + escaped = escaped.replace("\\\"", "\""); + escaped = escaped.replace("\\?", "\?"); + escaped = escaped.replace("\\\\", "\\"); + + return escaped; +} + +String String::json_escape() const { + String escaped = *this; + escaped = escaped.replace("\\", "\\\\"); + escaped = escaped.replace("\b", "\\b"); + escaped = escaped.replace("\f", "\\f"); + escaped = escaped.replace("\n", "\\n"); + escaped = escaped.replace("\r", "\\r"); + escaped = escaped.replace("\t", "\\t"); + escaped = escaped.replace("\v", "\\v"); + escaped = escaped.replace("\"", "\\\""); + + return escaped; +} + +String String::word_wrap(int p_chars_per_line) const { + String ret; + + int line_start = 0; + int line_end = 0; // End of last word on current line. + int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word. + int word_length = 0; + + for (int i = 0; i < length(); i++) { + const CharType c = operator[](i); + + switch (c) { + case '\n': { + // Force newline. + ret += substr(line_start, i - line_start + 1); + line_start = i + 1; + line_end = line_start; + word_start = line_start; + word_length = 0; + } break; + + case ' ': + case '\t': { + // A whitespace ends current word. + if (word_length > 0) { + line_end = i - 1; + word_start = -1; + word_length = 0; + } + } break; + + default: { + if (word_start == -1) { + word_start = i; + } + word_length += 1; + + if (word_length > p_chars_per_line) { + // Word too long: wrap before current character. + ret += substr(line_start, i - line_start) + "\n"; + line_start = i; + line_end = i; + word_start = i; + word_length = 1; + } else if (i - line_start + 1 > p_chars_per_line) { + // Line too long: wrap after the last word. + ret += substr(line_start, line_end - line_start + 1) + "\n"; + line_start = word_start; + line_end = line_start; + } + } break; + } + } + + const int remaining = length() - line_start; + if (remaining) { + ret += substr(line_start, remaining); + } + + return ret; +} + +Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const { + // Splits the URL into scheme, host, port, path. Strip credentials when present. + String base = *this; + r_scheme = ""; + r_host = ""; + r_port = 0; + r_path = ""; + int pos = base.find("://"); + // Scheme + if (pos != -1) { + r_scheme = base.substr(0, pos + 3).to_lower(); + base = base.substr(pos + 3, base.length() - pos - 3); + } + pos = base.find("/"); + // Path + if (pos != -1) { + r_path = base.substr(pos, base.length() - pos); + base = base.substr(0, pos); + } + // Host + pos = base.find("@"); + if (pos != -1) { + // Strip credentials + base = base.substr(pos + 1, base.length() - pos - 1); + } + if (base.begins_with("[")) { + // Literal IPv6 + pos = base.rfind("]"); + if (pos == -1) { + return ERR_INVALID_PARAMETER; + } + r_host = base.substr(1, pos - 1); + base = base.substr(pos + 1, base.length() - pos - 1); + } else { + // Anything else + if (base.get_slice_count(":") > 2) { + return ERR_INVALID_PARAMETER; + } + pos = base.rfind(":"); + if (pos == -1) { + r_host = base; + base = ""; + } else { + r_host = base.substr(0, pos); + base = base.substr(pos, base.length() - pos); + } + } + if (r_host.empty()) { + return ERR_INVALID_PARAMETER; + } + r_host = r_host.to_lower(); + // Port + if (base.begins_with(":")) { + base = base.substr(1, base.length() - 1); + if (!base.is_valid_integer()) { + return ERR_INVALID_PARAMETER; + } + r_port = base.to_int(); + if (r_port < 1 || r_port > 65535) { + return ERR_INVALID_PARAMETER; + } + } + return OK; +} + +String String::percent_encode() const { + CharString cs = utf8(); + String encoded; + for (int i = 0; i < cs.length(); i++) { + uint8_t c = cs[i]; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '~' || c == '.') { + char p[2] = { (char)c, 0 }; + encoded += p; + } else { + char p[4] = { '%', 0, 0, 0 }; + static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + p[1] = hex[c >> 4]; + p[2] = hex[c & 0xF]; + encoded += p; + } + } + + return encoded; +} +String String::percent_decode() const { + CharString pe; + + CharString cs = utf8(); + for (int i = 0; i < cs.length(); i++) { + uint8_t c = cs[i]; + if (c == '%' && i < length() - 2) { + uint8_t a = LOWERCASE(cs[i + 1]); + uint8_t b = LOWERCASE(cs[i + 2]); + + if (a >= '0' && a <= '9') { + c = (a - '0') << 4; + } else if (a >= 'a' && a <= 'f') { + c = (a - 'a' + 10) << 4; + } else { + continue; + } + + uint8_t d = 0; + + if (b >= '0' && b <= '9') { + d = (b - '0'); + } else if (b >= 'a' && b <= 'f') { + d = (b - 'a' + 10); + } else { + continue; + } + c += d; + i += 2; + } + pe += c; + } + + return String::utf8(pe.ptr()); +} + +String String::property_name_encode() const { + // Escape and quote strings with extended ASCII or further Unicode characters + // as well as '"', '=' or ' ' (32) + const CharType *cstr = get_data(); + for (int i = 0; cstr[i]; i++) { + if (cstr[i] == '=' || cstr[i] == '"' || cstr[i] == ';' || cstr[i] == '[' || cstr[i] == ']' || cstr[i] < 33 || cstr[i] > 126) { + return "\"" + c_escape_multiline() + "\""; + } + } + // Keep as is + return *this; +} + +// Changes made to the set of invalid characters must also be reflected in the String documentation. + +static const char32_t invalid_node_name_characters[] = { '.', ':', '@', '/', '\"', '%', 0 }; + +String String::get_invalid_node_name_characters() { + // Do not use this function for critical validation. + String r; + const char32_t *c = invalid_node_name_characters; + while (*c) { + if (c != invalid_node_name_characters) { + r += " "; + } + r += String::chr(*c); + c++; + } + return r; +} + +String String::validate_node_name() const { + // This is a critical validation in node addition, so it must be optimized. + const char32_t *cn = ptr(); + if (cn == nullptr) { + return String(); + } + + bool valid = true; + uint32_t idx = 0; + while (cn[idx]) { + const char32_t *c = invalid_node_name_characters; + while (*c) { + if (cn[idx] == *c) { + valid = false; + break; + } + c++; + } + if (!valid) { + break; + } + idx++; + } + + if (valid) { + return *this; + } + + String validated = *this; + char32_t *nn = validated.ptrw(); + while (nn[idx]) { + const char32_t *c = invalid_node_name_characters; + while (*c) { + if (nn[idx] == *c) { + nn[idx] = '_'; + break; + } + c++; + } + idx++; + } + + return validated; +} + +static _FORCE_INLINE_ bool _is_valid_identifier_bit(int p_index, char32_t p_char) { + if (p_index == 0 && is_digit(p_char)) { + return false; // No start with number plz. + } + return is_ascii_identifier_char(p_char); +} + +String String::validate_identifier() const { + if (empty()) { + return "_"; // Empty string is not a valid identifier; + } + + String result = *this; + int len = result.length(); + char32_t *buffer = result.ptrw(); + + for (int i = 0; i < len; i++) { + if (!_is_valid_identifier_bit(i, buffer[i])) { + buffer[i] = '_'; + } + } + + return result; +} + +bool String::is_valid_identifier() const { + int len = length(); + + if (len == 0) { + return false; + } + + const CharType *str = &operator[](0); + + for (int i = 0; i < len; i++) { + if (i == 0) { + if (str[0] >= '0' && str[0] <= '9') { + return false; // no start with number plz + } + } + + bool valid_char = (str[i] >= '0' && str[i] <= '9') || (str[i] >= 'a' && str[i] <= 'z') || (str[i] >= 'A' && str[i] <= 'Z') || str[i] == '_'; + + if (!valid_char) { + return false; + } + } + + return true; +} + +bool String::is_valid_integer() const { + int len = length(); + + if (len == 0) { + return false; + } + + int from = 0; + if (len != 1 && (operator[](0) == '+' || operator[](0) == '-')) { + from++; + } + + for (int i = from; i < len; i++) { + if (operator[](i) < '0' || operator[](i) > '9') { + return false; // no start with number plz + } + } + + return true; +} + +bool String::is_valid_hex_number(bool p_with_prefix) const { + int len = length(); + + if (len == 0) { + return false; + } + + int from = 0; + if (len != 1 && (operator[](0) == '+' || operator[](0) == '-')) { + from++; + } + + if (p_with_prefix) { + if (len < 3) { + return false; + } + if (operator[](from) != '0' || operator[](from + 1) != 'x') { + return false; + } + from += 2; + } + + for (int i = from; i < len; i++) { + CharType c = operator[](i); + if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + continue; + } + return false; + } + + return true; +}; + +bool String::is_valid_float() const { + int len = length(); + + if (len == 0) { + return false; + } + + int from = 0; + if (operator[](0) == '+' || operator[](0) == '-') { + from++; + } + + bool exponent_found = false; + bool period_found = false; + bool sign_found = false; + bool exponent_values_found = false; + bool numbers_found = false; + + for (int i = from; i < len; i++) { + if (operator[](i) >= '0' && operator[](i) <= '9') { + if (exponent_found) { + exponent_values_found = true; + } else { + numbers_found = true; + } + } else if (numbers_found && !exponent_found && operator[](i) == 'e') { + exponent_found = true; + } else if (!period_found && !exponent_found && operator[](i) == '.') { + period_found = true; + } else if ((operator[](i) == '-' || operator[](i) == '+') && exponent_found && !exponent_values_found && !sign_found) { + sign_found = true; + } else { + return false; // no start with number plz + } + } + + return numbers_found; +} + +bool String::is_valid_bool() const { + int size = length(); + + if (size == 1) { + CharType c = ptr()[0]; + + if (c == '0') { + return true; + } else if (c == '1') { + return true; + } + + return false; + } else if (size == 4) { + String l = to_lower(); + const CharType *p = l.ptr(); + + if (p[0] == 't' && p[1] == 'r' && p[2] == 'u' && p[3] == 'e') { + return true; + } else { + return false; + } + } else if (size == 5) { + String l = to_lower(); + const CharType *p = l.ptr(); + + if (p[0] == 'f' && p[1] == 'a' && p[2] == 'l' && p[3] == 's' && p[3] == 'e') { + return true; + } else { + return false; + } + } + + return false; +} + +bool String::is_valid_unsigned_integer() const { + int len = length(); + + if (len == 0) { + return false; + } + + int from = 0; + if (len != 1 && (operator[](0) == '+')) { + from++; + } + + for (int i = from; i < len; i++) { + if (operator[](i) < '0' || operator[](i) > '9') { + return false; // no start with number plz + } + } + + return true; +} + +bool String::is_valid_html_color() const { + return Color::html_is_valid(*this); +} + +bool String::is_valid_filename() const { + String stripped = strip_edges(); + if (*this != stripped) { + return false; + } + + if (stripped == String()) { + return false; + } + + return !(find(":") != -1 || find("/") != -1 || find("\\") != -1 || find("?") != -1 || find("*") != -1 || find("\"") != -1 || find("|") != -1 || find("%") != -1 || find("<") != -1 || find(">") != -1); +} + +bool String::is_valid_ip_address() const { + if (find(":") >= 0) { + Vector ip = split(":"); + for (int i = 0; i < ip.size(); i++) { + String n = ip[i]; + if (n.empty()) { + continue; + } + if (n.is_valid_hex_number(false)) { + int nint = n.hex_to_int(false); + if (nint < 0 || nint > 0xffff) { + return false; + } + continue; + }; + if (!n.is_valid_ip_address()) { + return false; + } + }; + + } else { + Vector ip = split("."); + if (ip.size() != 4) { + return false; + } + for (int i = 0; i < ip.size(); i++) { + String n = ip[i]; + if (!n.is_valid_integer()) { + return false; + } + int val = n.to_int(); + if (val < 0 || val > 255) { + return false; + } + } + }; + + return true; +} + +Vector String::to_ascii_buffer() const { + const String *s = this; + if (s->empty()) { + return Vector(); + } + CharString charstr = s->ascii(); + + Vector retval; + size_t len = charstr.length(); + retval.resize(len); + uint8_t *w = retval.ptrw(); + memcpy(w, charstr.ptr(), len); + + return retval; +} + +Vector String::to_utf8_buffer() const { + const String *s = this; + if (s->empty()) { + return Vector(); + } + CharString charstr = s->utf8(); + + Vector retval; + size_t len = charstr.length(); + retval.resize(len); + uint8_t *w = retval.ptrw(); + memcpy(w, charstr.ptr(), len); + + return retval; +} + +Vector String::to_utf16_buffer() const { + const String *s = this; + if (s->empty()) { + return Vector(); + } + Char16String charstr = s->utf16(); + + Vector retval; + size_t len = charstr.length() * sizeof(char16_t); + retval.resize(len); + uint8_t *w = retval.ptrw(); + memcpy(w, (const void *)charstr.ptr(), len); + + return retval; +} + +Vector String::to_utf32_buffer() const { + const String *s = this; + if (s->empty()) { + return Vector(); + } + + Vector retval; + size_t len = s->length() * sizeof(CharType); + retval.resize(len); + uint8_t *w = retval.ptrw(); + memcpy(w, (const void *)s->ptr(), len); + + return retval; +} + +String::String(const char *p_str) { + copy_from(p_str); +} + +String::String(const wchar_t *p_str) { + copy_from(p_str); +} + +String::String(const CharType *p_str) { + copy_from(p_str); +} + +String::String(const char *p_str, int p_clip_to_len) { + copy_from(p_str, p_clip_to_len); +} + +String::String(const wchar_t *p_str, int p_clip_to_len) { + copy_from(p_str, p_clip_to_len); +} + +String::String(const CharType *p_str, int p_clip_to_len) { + copy_from(p_str, p_clip_to_len); +} + +String::String(const StrRange &p_range) { + if (!p_range.c_str) { + return; + } + copy_from(p_range.c_str, p_range.len); +} + +// Other conversions + +int String::hex_to_int(bool p_with_prefix) const { + int len = length(); + ERR_FAIL_COND_V_MSG(p_with_prefix ? len < 3 : len == 0, 0, String("Invalid hexadecimal notation length in string ") + (p_with_prefix ? "with" : "without") + " prefix \"" + *this + "\"."); + + const CharType *s = ptr(); + + int sign = s[0] == '-' ? -1 : 1; + + if (sign < 0) { + s++; + } + + if (p_with_prefix) { + ERR_FAIL_COND_V_MSG(s[0] != '0' || LOWERCASE(s[1]) != 'x', 0, "Invalid hexadecimal notation prefix in string \"" + *this + "\"."); + s += 2; + } + + int hex = 0; + + while (*s) { + CharType c = LOWERCASE(*s); + int n; + if (c >= '0' && c <= '9') { + n = c - '0'; + } else if (c >= 'a' && c <= 'f') { + n = (c - 'a') + 10; + } else { + ERR_FAIL_V_MSG(0, "Invalid hexadecimal notation character \"" + chr(*s) + "\" in string \"" + *this + "\"."); + } + // Check for overflow/underflow, with special case to ensure INT32_MIN does not result in error + bool overflow = ((hex > INT32_MAX / 16) && (sign == 1 || (sign == -1 && hex != (INT32_MAX >> 4) + 1))) || (sign == -1 && hex == (INT32_MAX >> 4) + 1 && c > '0'); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + *this + " as a 32-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); + hex *= 16; + hex += n; + s++; + } + + return hex * sign; +} + +int64_t String::hex_to_int64(bool p_with_prefix) const { + int len = length(); + ERR_FAIL_COND_V_MSG(p_with_prefix ? len < 3 : len == 0, 0, String("Invalid hexadecimal notation length in string ") + (p_with_prefix ? "with" : "without") + " prefix \"" + *this + "\"."); + + const CharType *s = ptr(); + + int64_t sign = s[0] == '-' ? -1 : 1; + + if (sign < 0) { + s++; + } + + if (p_with_prefix) { + ERR_FAIL_COND_V_MSG(s[0] != '0' || LOWERCASE(s[1]) != 'x', 0, "Invalid hexadecimal notation prefix in string \"" + *this + "\"."); + s += 2; + } + + int64_t hex = 0; + + while (*s) { + CharType c = LOWERCASE(*s); + int64_t n; + if (c >= '0' && c <= '9') { + n = c - '0'; + } else if (c >= 'a' && c <= 'f') { + n = (c - 'a') + 10; + } else { + ERR_FAIL_V_MSG(0, "Invalid hexadecimal notation character \"" + chr(*s) + "\" in string \"" + *this + "\"."); + } + bool overflow = ((hex > INT64_MAX / 16) && (sign == 1 || (sign == -1 && hex != (INT64_MAX >> 4) + 1))) || (sign == -1 && hex == (INT64_MAX >> 4) + 1 && c > '0'); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); + hex *= 16; + hex += n; + s++; + } + + return hex * sign; +} + +int64_t String::bin_to_int64(bool p_with_prefix) const { + int len = length(); + ERR_FAIL_COND_V_MSG(p_with_prefix ? len < 3 : len == 0, 0, String("Invalid binary notation length in string ") + (p_with_prefix ? "with" : "without") + " prefix \"" + *this + "\"."); + + const CharType *s = ptr(); + + int64_t sign = s[0] == '-' ? -1 : 1; + + if (sign < 0) { + s++; + } + + if (p_with_prefix) { + ERR_FAIL_COND_V_MSG(s[0] != '0' || LOWERCASE(s[1]) != 'b', 0, "Invalid binary notation prefix in string \"" + *this + "\"."); + s += 2; + } + + int64_t binary = 0; + + while (*s) { + CharType c = LOWERCASE(*s); + int64_t n; + if (c == '0' || c == '1') { + n = c - '0'; + } else { + ERR_FAIL_V_MSG(0, "Invalid binary notation character \"" + chr(*s) + "\" in string \"" + *this + "\"."); + } + // Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error + bool overflow = ((binary > INT64_MAX / 2) && (sign == 1 || (sign == -1 && binary != (INT64_MAX >> 1) + 1))) || (sign == -1 && binary == (INT64_MAX >> 1) + 1 && c > '0'); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); + binary *= 2; + binary += n; + s++; + } + + return binary * sign; +} + +template +static double built_in_strtod( + /* A decimal ASCII floating-point number, + * optionally preceded by white space. Must + * have form "-I.FE-X", where I is the integer + * part of the mantissa, F is the fractional + * part of the mantissa, and X is the + * exponent. Either of the signs may be "+", + * "-", or omitted. Either I or F may be + * omitted, or both. The decimal point isn't + * necessary unless F is present. The "E" may + * actually be an "e". E and X may both be + * omitted (but not just one). */ + const C *string, + /* If non-nullptr, store terminating Cacter's + * address here. */ + C **endPtr = nullptr) { + /* Largest possible base 10 exponent. Any + * exponent larger than this will already + * produce underflow or overflow, so there's + * no need to worry about additional digits. */ + static const int maxExponent = 511; + /* Table giving binary powers of 10. Entry + * is 10^2^i. Used to convert decimal + * exponents into floating-point numbers. */ + static const double powersOf10[] = { + 10., + 100., + 1.0e4, + 1.0e8, + 1.0e16, + 1.0e32, + 1.0e64, + 1.0e128, + 1.0e256 + }; + + bool sign, expSign = false; + double fraction, dblExp; + const double *d; + const C *p; + int c; + /* Exponent read from "EX" field. */ + int exp = 0; + /* Exponent that derives from the fractional + * part. Under normal circumstances, it is + * the negative of the number of digits in F. + * However, if I is very long, the last digits + * of I get dropped (otherwise a long I with a + * large negative exponent could cause an + * unnecessary overflow on I alone). In this + * case, fracExp is incremented one for each + * dropped digit. */ + int fracExp = 0; + /* Number of digits in mantissa. */ + int mantSize; + /* Number of mantissa digits BEFORE decimal point. */ + int decPt; + /* Temporarily holds location of exponent in string. */ + const C *pExp; + + /* + * Strip off leading blanks and check for a sign. + */ + + p = string; + while (*p == ' ' || *p == '\t' || *p == '\n') { + p += 1; + } + if (*p == '-') { + sign = true; + p += 1; + } else { + if (*p == '+') { + p += 1; + } + sign = false; + } + + /* + * Count the number of digits in the mantissa (including the decimal + * point), and also locate the decimal point. + */ + + decPt = -1; + for (mantSize = 0;; mantSize += 1) { + c = *p; + if (!IS_DIGIT(c)) { + if ((c != '.') || (decPt >= 0)) { + break; + } + decPt = mantSize; + } + p += 1; + } + + /* + * Now suck up the digits in the mantissa. Use two integers to collect 9 + * digits each (this is faster than using floating-point). If the mantissa + * has more than 18 digits, ignore the extras, since they can't affect the + * value anyway. + */ + + pExp = p; + p -= mantSize; + if (decPt < 0) { + decPt = mantSize; + } else { + mantSize -= 1; /* One of the digits was the point. */ + } + if (mantSize > 18) { + fracExp = decPt - 18; + mantSize = 18; + } else { + fracExp = decPt - mantSize; + } + if (mantSize == 0) { + fraction = 0.0; + p = string; + goto done; + } else { + int frac1, frac2; + + frac1 = 0; + for (; mantSize > 9; mantSize -= 1) { + c = *p; + p += 1; + if (c == '.') { + c = *p; + p += 1; + } + frac1 = 10 * frac1 + (c - '0'); + } + frac2 = 0; + for (; mantSize > 0; mantSize -= 1) { + c = *p; + p += 1; + if (c == '.') { + c = *p; + p += 1; + } + frac2 = 10 * frac2 + (c - '0'); + } + fraction = (1.0e9 * frac1) + frac2; + } + + /* + * Skim off the exponent. + */ + + p = pExp; + if ((*p == 'E') || (*p == 'e')) { + p += 1; + if (*p == '-') { + expSign = true; + p += 1; + } else { + if (*p == '+') { + p += 1; + } + expSign = false; + } + if (!IS_DIGIT(CharType(*p))) { + p = pExp; + goto done; + } + while (IS_DIGIT(CharType(*p))) { + exp = exp * 10 + (*p - '0'); + p += 1; + } + } + if (expSign) { + exp = fracExp - exp; + } else { + exp = fracExp + exp; + } + + /* + * Generate a floating-point number that represents the exponent. Do this + * by processing the exponent one bit at a time to combine many powers of + * 2 of 10. Then combine the exponent with the fraction. + */ + + if (exp < 0) { + expSign = true; + exp = -exp; + } else { + expSign = false; + } + + if (exp > maxExponent) { + exp = maxExponent; + //WARN_PRINT("Exponent too high"); + } + dblExp = 1.0; + for (d = powersOf10; exp != 0; exp >>= 1, ++d) { + if (exp & 01) { + dblExp *= *d; + } + } + if (expSign) { + fraction /= dblExp; + } else { + fraction *= dblExp; + } + +done: + if (endPtr != nullptr) { + *endPtr = (C *)p; + } + + if (sign) { + return -fraction; + } + return fraction; +} + +double String::to_double() const { + if (empty()) { + return 0; + } + + return built_in_strtod(get_data()); +} + +float String::to_float() const { + if (empty()) { + return 0; + } + + return built_in_strtod(get_data()); +} + +int String::to_int() const { + if (length() == 0) { + return 0; + } + + int to = (find(".") >= 0) ? find(".") : length(); + + int integer = 0; + int sign = 1; + + for (int i = 0; i < to; i++) { + CharType c = operator[](i); + if (c >= '0' && c <= '9') { + bool overflow = (integer > INT32_MAX / 10) || (integer == INT32_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT32_MAX : INT32_MIN, "Cannot represent " + *this + " as a 32-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); + integer *= 10; + integer += c - '0'; + + } else if (integer == 0 && c == '-') { + sign = -sign; + } + } + + return integer * sign; +} + +bool String::to_bool() const { + if (length() == 0) { + return false; + } + + if (is_numeric()) { + return to_int() != 0; + } + + return to_lower() == "true"; +} + +uint32_t String::to_uint() const { + if (is_numeric()) { + return static_cast(to_int()); + } + + return 0; +} + +int64_t String::to_int64() const { + if (length() == 0) { + return 0; + } + + int to = (find(".") >= 0) ? find(".") : length(); + + int64_t integer = 0; + int64_t sign = 1; + + for (int i = 0; i < to; i++) { + CharType c = operator[](i); + if (c >= '0' && c <= '9') { + bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); + integer *= 10; + integer += c - '0'; + + } else if (integer == 0 && c == '-') { + sign = -sign; + } + } + + return integer * sign; +} + +int64_t String::to_int(const char *p_str, int p_len) { + int to = 0; + if (p_len >= 0) { + to = p_len; + } else { + while (p_str[to] != 0 && p_str[to] != '.') { + to++; + } + } + + int64_t integer = 0; + int64_t sign = 1; + + for (int i = 0; i < to; i++) { + char c = p_str[i]; + if (is_digit(c)) { + bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); + integer *= 10; + integer += c - '0'; + + } else if (c == '-' && integer == 0) { + sign = -sign; + } else if (c != ' ') { + break; + } + } + + return integer * sign; +} + +int64_t String::to_int(const wchar_t *p_str, int p_len) { + int to = 0; + if (p_len >= 0) { + to = p_len; + } else { + while (p_str[to] != 0 && p_str[to] != '.') { + to++; + } + } + + int64_t integer = 0; + int64_t sign = 1; + + for (int i = 0; i < to; i++) { + wchar_t c = p_str[i]; + if (is_digit(c)) { + bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); + integer *= 10; + integer += c - '0'; + + } else if (c == '-' && integer == 0) { + sign = -sign; + } else if (c != ' ') { + break; + } + } + + return integer * sign; +} + +int64_t String::to_int(const CharType *p_str, int p_len, bool p_clamp) { + if (p_len == 0 || !p_str[0]) { + return 0; + } + ///@todo make more exact so saving and loading does not lose precision + + int64_t integer = 0; + int64_t sign = 1; + int reading = READING_SIGN; + + const CharType *str = p_str; + const CharType *limit = &p_str[p_len]; + + while (*str && reading != READING_DONE && str != limit) { + CharType c = *(str++); + switch (reading) { + case READING_SIGN: { + if (is_digit(c)) { + reading = READING_INT; + // let it fallthrough + } else if (c == '-') { + sign = -1; + reading = READING_INT; + break; + } else if (c == '+') { + sign = 1; + reading = READING_INT; + break; + } else { + break; + } + FALLTHROUGH; + } + case READING_INT: { + if (is_digit(c)) { + if (integer > INT64_MAX / 10) { + String number(""); + str = p_str; + while (*str && str != limit) { + number += *(str++); + } + if (p_clamp) { + if (sign == 1) { + return INT64_MAX; + } else { + return INT64_MIN; + } + } else { + ERR_FAIL_V_MSG(sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + number + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); + } + } + integer *= 10; + integer += c - '0'; + } else { + reading = READING_DONE; + } + + } break; + } + } + + return sign * integer; +} + +double String::to_float(const char *p_str) { + return built_in_strtod(p_str); +} + +double String::to_float(const wchar_t *p_str, const wchar_t **r_end) { + return built_in_strtod(p_str, (wchar_t **)r_end); +} +double String::to_float(const CharType *p_str, const CharType **r_end) { + return built_in_strtod(p_str, (CharType **)r_end); +} + +double String::to_double(const char *p_str) { + return built_in_strtod(p_str); +} + +double String::to_double(const wchar_t *p_str, const wchar_t **r_end) { + return built_in_strtod(p_str, (wchar_t **)r_end); +} +double String::to_double(const CharType *p_str, const CharType **r_end) { + return built_in_strtod(p_str, (CharType **)r_end); +} + +uint32_t String::num_characters(int64_t p_int) { + int r = 1; + if (p_int < 0) { + r += 1; + if (p_int == INT64_MIN) { + p_int = INT64_MAX; + } else { + p_int = -p_int; + } + } + while (p_int >= 10) { + p_int /= 10; + r++; + } + return r; +} + +bool String::_base_is_subsequence_of(const String &p_string, bool case_insensitive) const { + int len = length(); + if (len == 0) { + // Technically an empty string is subsequence of any string + return true; + } + + if (len > p_string.length()) { + return false; + } + + const CharType *src = &operator[](0); + const CharType *tgt = &p_string[0]; + + for (; *src && *tgt; tgt++) { + bool match = false; + if (case_insensitive) { + CharType srcc = _find_lower(*src); + CharType tgtc = _find_lower(*tgt); + match = srcc == tgtc; + } else { + match = *src == *tgt; + } + if (match) { + src++; + if (!*src) { + return true; + } + } + } + + return false; +} + +bool operator==(const char *p_chr, const String &p_str) { + return p_str == p_chr; +} + +bool operator==(const wchar_t *p_chr, const String &p_str) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + return p_str == String::utf16((const char16_t *)p_chr); +#else + // wchar_t is 32-bi + return p_str == String((const CharType *)p_chr); +#endif +} + +bool operator!=(const char *p_chr, const String &p_str) { + return !(p_str == p_chr); +} + +bool operator!=(const wchar_t *p_chr, const String &p_str) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + return !(p_str == String::utf16((const char16_t *)p_chr)); +#else + // wchar_t is 32-bi + return !(p_str == String((const CharType *)p_chr)); +#endif +} + +String operator+(const char *p_chr, const String &p_str) { + String tmp = p_chr; + tmp += p_str; + return tmp; +} + +String operator+(const wchar_t *p_chr, const String &p_str) { +#ifdef WINDOWS_ENABLED + // wchar_t is 16-bit + String tmp = String::utf16((const char16_t *)p_chr); +#else + // wchar_t is 32-bit + String tmp = (const CharType *)p_chr; +#endif + tmp += p_str; + return tmp; +} + +String operator+(CharType p_chr, const String &p_str) { + return (String::chr(p_chr) + p_str); +} + +String itos(int64_t p_val) { + return String::num_int64(p_val); +} + +String uitos(uint64_t p_val) { + return String::num_uint64(p_val); +} + +String rtos(double p_val) { + return String::num(p_val); +} + +String rtoss(double p_val) { + return String::num_scientific(p_val); +} + +#ifdef TOOLS_ENABLED +String TTR(const String &p_text, const String &p_context) { + return p_text; +} + +/* DTR is used for the documentation, handling descriptions extracted from the XML. + * It also replaces `$DOCS_URL` with the actual URL to the documentation's branch, + * to allow dehardcoding it in the XML and doing proper substitutions everywhere. + */ +String DTR(const String &p_text) { + // Comes straight from the XML, so remove indentation and any trailing whitespace. + const String text = p_text.dedent().strip_edges(); + + return text.replace("$DOCS_URL", VERSION_DOCS_URL); +} +#endif + +String RTR(const String &p_text) { + return p_text; +} +#line 0 + +#line 1 "sfwl/core/logger.cpp" + + + +void RLogger::print_trace(const String &str) { + print_trace(str.utf8().get_data()); +} +void RLogger::print_trace(const char *str) { + printf("T %s\n", str); +} +void RLogger::print_trace(const char *p_function, const char *p_file, int p_line, const char *str) { + printf("T | %s::%s:%d | %s\n", p_file, p_function, p_line, str); +} +void RLogger::print_trace(const char *p_function, const char *p_file, int p_line, const String &str) { + printf("T | %s::%s:%d | %s\n", p_file, p_function, p_line, str.utf8().get_data()); +} + +void RLogger::print_message(const String &str) { + print_message(str.utf8().get_data()); +} +void RLogger::print_message(const char *str) { + printf("M %s\n", str); +} +void RLogger::print_message(const char *p_function, const char *p_file, int p_line, const char *str) { + printf("M | %s::%s:%d | %s\n", p_file, p_function, p_line, str); +} +void RLogger::print_message(const char *p_function, const char *p_file, int p_line, const String &str) { + printf("M | %s::%s:%d | %s\n", p_file, p_function, p_line, str.utf8().get_data()); +} + +void RLogger::print_warning(const String &str) { + print_warning(str.utf8().get_data()); +} +void RLogger::print_warning(const char *str) { + printf("W %s\n", str); +} +void RLogger::print_warning(const char *p_function, const char *p_file, int p_line, const char *str) { + printf("W | %s::%s:%d | %s\n", p_file, p_function, p_line, str); +} +void RLogger::print_warning(const char *p_function, const char *p_file, int p_line, const String &str) { + printf("W | %s::%s:%d | %s\n", p_file, p_function, p_line, str.utf8().get_data()); +} + +void RLogger::print_error(const String &str) { + print_error(str.utf8().get_data()); +} +void RLogger::print_error(const char *str) { + printf("E %s\n", str); +} + +void RLogger::print_error(const char *p_function, const char *p_file, int p_line, const char *str) { + printf("E | %s::%s:%d | %s\n", p_file, p_function, p_line, str); +} +void RLogger::print_error(const char *p_function, const char *p_file, int p_line, const String &str) { + printf("E | %s::%s:%d | %s\n", p_file, p_function, p_line, str.utf8().get_data()); +} +void RLogger::print_msg_error(const char *p_function, const char *p_file, int p_line, const char *p_msg, const char *str) { + printf("E | %s::%s:%d | :: %s. %s\n", p_file, p_function, p_line, str, p_msg); +} +void RLogger::print_index_error(const char *p_function, const char *p_file, int p_line, const int index, const int size, const char *str) { + printf("E (INDEX) | %s::%s:%d | :: index: %d/%d. %s\n", p_file, p_function, p_line, index, size, str); +} + +void RLogger::log_trace(const String &str) { + log_trace(str.utf8().get_data()); +} +void RLogger::log_trace(const char *str) { + printf("T %s\n", str); +} +void RLogger::log_trace(const char *p_function, const char *p_file, int p_line, const char *str) { + printf("T | %s::%s:%d | %s\n", p_file, p_function, p_line, str); +} +void RLogger::log_trace(const char *p_function, const char *p_file, int p_line, const String &str) { + printf("T | %s::%s:%d | %s\n", p_file, p_function, p_line, str.utf8().get_data()); +} + +void RLogger::log_message(const String &str) { + log_message(str.utf8().get_data()); +} +void RLogger::log_message(const char *str) { + printf("M %s\n", str); +} +void RLogger::log_message(const char *p_function, const char *p_file, int p_line, const char *str) { + printf("M | %s::%s:%d | %s\n", p_file, p_function, p_line, str); +} +void RLogger::log_message(const char *p_function, const char *p_file, int p_line, const String &str) { + printf("M | %s::%s:%d | %s\n", p_file, p_function, p_line, str.utf8().get_data()); +} + +void RLogger::log_warning(const String &str) { + log_warning(str.utf8().get_data()); +} +void RLogger::log_warning(const char *str) { + printf("W %s\n", str); +} +void RLogger::log_warning(const char *p_function, const char *p_file, int p_line, const char *str) { + printf("W | %s::%s:%d | %s\n", p_file, p_function, p_line, str); +} +void RLogger::log_warning(const char *p_function, const char *p_file, int p_line, const String &str) { + printf("W | %s::%s:%d | %s\n", p_file, p_function, p_line, str.utf8().get_data()); +} + +void RLogger::log_error(const String &str) { + log_error(str.utf8().get_data()); +} +void RLogger::log_error(const char *str) { + printf("E %s\n", str); +} + +void RLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *str) { + printf("E | %s::%s:%d | %s\n", p_file, p_function, p_line, str); +} +void RLogger::log_error(const char *p_function, const char *p_file, int p_line, const String &str) { + printf("E | %s::%s:%d | %s\n", p_file, p_function, p_line, str.utf8().get_data()); +} +void RLogger::log_msg_error(const char *p_function, const char *p_file, int p_line, const char *p_msg, const char *str) { + printf("E | %s::%s:%d | :: %s. %s\n", p_file, p_function, p_line, str, p_msg); +} +void RLogger::log_index_error(const char *p_function, const char *p_file, int p_line, const int index, const int size, const char *str) { + printf("E (INDEX) | %s::%s:%d | :: index: %d/%d. %s\n", p_file, p_function, p_line, index, size, str); +} +void RLogger::log_index_error(const char *p_function, const char *p_file, int p_line, const int index, const int size, const String &str) { + printf("E (INDEX) | %s::%s:%d | :: index: %d/%d. %s\n", p_file, p_function, p_line, index, size, str.utf8().get_data()); +} + +String *RLogger::get_string_ptr(const int p_default_size) { + return memnew(String()); +} +String *RLogger::get_string_ptr(const char *p_function, const char *p_file, int p_line, const int p_default_size) { + String *s = memnew(String()); + + s->operator+=(p_function); + s->operator+=("::"); + s->operator+=(p_file); + s->operator+=(":"); + s->operator+=(String::num(p_line)); + s->operator+=(" | "); + + return s; +} +String *RLogger::get_string_ptr(const char *p_prefix, const char *p_function, const char *p_file, int p_line, const int p_default_size) { + String *s = memnew(String()); + + s->operator+=(p_prefix); + s->operator+=(" | "); + s->operator+=(p_function); + s->operator+=("::"); + s->operator+=(p_file); + s->operator+=(":"); + s->operator+=(String::num(p_line)); + s->operator+=(" | "); + + return s; +} +void RLogger::return_string_ptr(String *str) { + delete str; +} + +String *RLogger::get_trace_string_ptr(const int p_default_size) { + String *str = get_string_ptr(p_default_size); + str->operator+=("T "); + return str; +} +String *RLogger::get_message_string_ptr(const int p_default_size) { + String *str = get_string_ptr(p_default_size); + str->operator+=("M "); + return str; +} +String *RLogger::get_warning_string_ptr(const int p_default_size) { + String *str = get_string_ptr(p_default_size); + str->operator+=("W "); + return str; +} +String *RLogger::get_error_string_ptr(const int p_default_size) { + String *str = get_string_ptr(p_default_size); + str->operator+=("E "); + return str; +} + +String *RLogger::get_trace_string_ptr(const char *p_function, const char *p_file, int p_line, const int p_default_size) { + return get_string_ptr("T", p_function, p_file, p_line, p_default_size); +} +String *RLogger::get_message_string_ptr(const char *p_function, const char *p_file, int p_line, const int p_default_size) { + return get_string_ptr("M", p_function, p_file, p_line, p_default_size); +} +String *RLogger::get_warning_string_ptr(const char *p_function, const char *p_file, int p_line, const int p_default_size) { + return get_string_ptr("W", p_function, p_file, p_line, p_default_size); +} +String *RLogger::get_error_string_ptr(const char *p_function, const char *p_file, int p_line, const int p_default_size) { + return get_string_ptr("E", p_function, p_file, p_line, p_default_size); +} + +void RLogger::log_ptr(String *str) { + printf("%s\n", str->utf8().get_data()); +} + +void RLogger::log_ret_ptr(String *str) { + log_ptr(str); + + return_string_ptr(str); +} +#line 0 + +#line 1 "sfwl/core/string_name.cpp" +/*************************************************************************/ +/* string_name.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +StaticCString StaticCString::create(const char *p_ptr) { + StaticCString scs; + scs.ptr = p_ptr; + return scs; +} + +StringName::_Data *StringName::_table[STRING_TABLE_LEN]; + +StringName _scs_create(const char *p_chr, bool p_static) { + return (p_chr[0] ? StringName(StaticCString::create(p_chr), p_static) : StringName()); +} + +bool StringName::configured = false; +Mutex StringName::lock; + +#ifdef DEBUG_ENABLED +bool StringName::debug_stringname = false; +#endif + +void StringName::setup() { + ERR_FAIL_COND(configured); + for (int i = 0; i < STRING_TABLE_LEN; i++) { + _table[i] = nullptr; + } + configured = true; +} + +void StringName::cleanup() { + lock.lock(); + +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + Vector<_Data *> data; + for (int i = 0; i < STRING_TABLE_LEN; i++) { + _Data *d = _table[i]; + while (d) { + data.push_back(d); + d = d->next; + } + } + + print_line("\nStringName reference ranking (from most to least referenced):\n"); + + data.sort_custom(); + int unreferenced_stringnames = 0; + int rarely_referenced_stringnames = 0; + for (int i = 0; i < data.size(); i++) { + print_line(itos(i + 1) + ": " + data[i]->get_name() + " - " + itos(data[i]->debug_references)); + if (data[i]->debug_references == 0) { + unreferenced_stringnames += 1; + } else if (data[i]->debug_references < 5) { + rarely_referenced_stringnames += 1; + } + } + + print_line(vformat("\nOut of %d StringNames, %d StringNames were never referenced during this run (0 times) (%.2f%%).", data.size(), unreferenced_stringnames, unreferenced_stringnames / float(data.size()) * 100)); + print_line(vformat("Out of %d StringNames, %d StringNames were rarely referenced during this run (1-4 times) (%.2f%%).", data.size(), rarely_referenced_stringnames, rarely_referenced_stringnames / float(data.size()) * 100)); + } +#endif + + //int lost_strings = 0; + for (int i = 0; i < STRING_TABLE_LEN; i++) { + while (_table[i]) { + _Data *d = _table[i]; + + /* + if (d->static_count.get() != d->refcount.get()) { + lost_strings++; + if (OS::get_singleton()->is_stdout_verbose()) { + if (d->cname) { + print_line("Orphan StringName: " + String(d->cname)); + } else { + print_line("Orphan StringName: " + String(d->name)); + } + } + } + */ + + _table[i] = _table[i]->next; + memdelete(d); + } + } + + /* + if (lost_strings) { + print_verbose("StringName: " + itos(lost_strings) + " unclaimed string names at exit."); + } + */ + + configured = false; + + lock.unlock(); +} + +void StringName::unref() { + ERR_FAIL_COND(!configured); + + if (_data && _data->refcount.unref()) { + lock.lock(); + + if (_data->static_count.get() > 0) { + if (_data->cname) { + ERR_PRINT("BUG: Unreferenced static string to 0: " + String(_data->cname)); + } else { + ERR_PRINT("BUG: Unreferenced static string to 0: " + String(_data->name)); + } + } + + if (_data->prev) { + _data->prev->next = _data->next; + } else { + if (_table[_data->idx] != _data) { + ERR_PRINT("BUG!"); + } + + _table[_data->idx] = _data->next; + } + + if (_data->next) { + _data->next->prev = _data->prev; + } + + memdelete(_data); + lock.unlock(); + } + + _data = nullptr; +} + +bool StringName::operator==(const String &p_name) const { + if (!_data) { + return (p_name.length() == 0); + } + + return (_data->get_name() == p_name); +} + +bool StringName::operator==(const char *p_name) const { + if (!_data) { + return (p_name[0] == 0); + } + + return (_data->get_name() == p_name); +} + +bool StringName::operator!=(const String &p_name) const { + return !(operator==(p_name)); +} + +bool StringName::operator!=(const StringName &p_name) const { + // the real magic of all this mess happens here. + // this is why path comparisons are very fast + return _data != p_name._data; +} + +void StringName::operator=(const StringName &p_name) { + if (this == &p_name) { + return; + } + + unref(); + + if (p_name._data && p_name._data->refcount.ref()) { + _data = p_name._data; + } +} + +StringName::StringName(const StringName &p_name) { + _data = nullptr; + + ERR_FAIL_COND(!configured); + + if (p_name._data && p_name._data->refcount.ref()) { + _data = p_name._data; + } +} + +StringName::StringName(const char *p_name, bool p_static) { + _data = nullptr; + + ERR_FAIL_COND(!configured); + + if (!p_name || p_name[0] == 0) { + return; //empty, ignore + } + + lock.lock(); + + uint32_t hash = String::hash(p_name); + + uint32_t idx = hash & STRING_TABLE_MASK; + + _data = _table[idx]; + + while (_data) { + // compare hash first + if (_data->hash == hash && _data->get_name() == p_name) { + break; + } + _data = _data->next; + } + + if (_data) { + if (_data->refcount.ref()) { + // exists + if (p_static) { + _data->static_count.increment(); + } + +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + _data->debug_references++; + } +#endif + + lock.unlock(); + return; + } + } + + _data = memnew(_Data); + _data->name = p_name; + _data->refcount.init(); + _data->static_count.set(p_static ? 1 : 0); + _data->hash = hash; + _data->idx = idx; + _data->cname = NULL; + _data->next = _table[idx]; + _data->prev = NULL; + +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + // Keep in memory, force static. + _data->refcount.ref(); + _data->static_count.increment(); + } +#endif + + if (_table[idx]) { + _table[idx]->prev = _data; + } + + _table[idx] = _data; + + lock.unlock(); +} + +StringName::StringName(const StaticCString &p_static_string, bool p_static) { + _data = NULL; + + ERR_FAIL_COND(!configured); + + ERR_FAIL_COND(!p_static_string.ptr || !p_static_string.ptr[0]); + + lock.lock(); + + uint32_t hash = String::hash(p_static_string.ptr); + + uint32_t idx = hash & STRING_TABLE_MASK; + + _data = _table[idx]; + + while (_data) { + // compare hash first + if (_data->hash == hash && _data->get_name() == p_static_string.ptr) { + break; + } + _data = _data->next; + } + + if (_data) { + if (_data->refcount.ref()) { + // exists + if (p_static) { + _data->static_count.increment(); + } + +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + _data->debug_references++; + } +#endif + + lock.unlock(); + return; + } + } + + _data = memnew(_Data); + + _data->refcount.init(); + _data->static_count.set(p_static ? 1 : 0); + _data->hash = hash; + _data->idx = idx; + _data->cname = p_static_string.ptr; + _data->next = _table[idx]; + _data->prev = NULL; + +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + // Keep in memory, force static. + _data->refcount.ref(); + _data->static_count.increment(); + } +#endif + + if (_table[idx]) { + _table[idx]->prev = _data; + } + + _table[idx] = _data; + + lock.unlock(); +} + +StringName::StringName(const String &p_name, bool p_static) { + _data = nullptr; + + ERR_FAIL_COND(!configured); + + if (p_name.empty()) { + return; + } + + lock.lock(); + + uint32_t hash = p_name.hash(); + + uint32_t idx = hash & STRING_TABLE_MASK; + + _data = _table[idx]; + + while (_data) { + if (_data->hash == hash && _data->get_name() == p_name) { + break; + } + + _data = _data->next; + } + + if (_data) { + if (_data->refcount.ref()) { + // exists + if (p_static) { + _data->static_count.increment(); + } + +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + _data->debug_references++; + } +#endif + + lock.unlock(); + return; + } + } + + _data = memnew(_Data); + _data->name = p_name; + _data->refcount.init(); + _data->static_count.set(p_static ? 1 : 0); + _data->hash = hash; + _data->idx = idx; + _data->cname = NULL; + _data->next = _table[idx]; + _data->prev = NULL; + +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + // Keep in memory, force static. + _data->refcount.ref(); + _data->static_count.increment(); + } +#endif + + if (_table[idx]) { + _table[idx]->prev = _data; + } + + _table[idx] = _data; + + lock.unlock(); +} + +StringName StringName::search(const char *p_name) { + ERR_FAIL_COND_V(!configured, StringName()); + + ERR_FAIL_COND_V(!p_name, StringName()); + if (!p_name[0]) { + return StringName(); + } + + lock.lock(); + + uint32_t hash = String::hash(p_name); + + uint32_t idx = hash & STRING_TABLE_MASK; + + _Data *_data = _table[idx]; + + while (_data) { + // compare hash first + if (_data->hash == hash && _data->get_name() == p_name) { + break; + } + _data = _data->next; + } + + if (_data && _data->refcount.ref()) { +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + _data->debug_references++; + } +#endif + + lock.unlock(); + + return StringName(_data); + } + + lock.unlock(); + return StringName(); //does not exist +} + +StringName StringName::search(const CharType *p_name) { + ERR_FAIL_COND_V(!configured, StringName()); + + ERR_FAIL_COND_V(!p_name, StringName()); + if (!p_name[0]) { + return StringName(); + } + + lock.lock(); + + uint32_t hash = String::hash(p_name); + + uint32_t idx = hash & STRING_TABLE_MASK; + + _Data *_data = _table[idx]; + + while (_data) { + // compare hash first + if (_data->hash == hash && _data->get_name() == p_name) { + break; + } + + _data = _data->next; + } + + if (_data && _data->refcount.ref()) { + lock.unlock(); + return StringName(_data); + } + + lock.unlock(); + return StringName(); //does not exist +} +StringName StringName::search(const String &p_name) { + ERR_FAIL_COND_V(p_name == "", StringName()); + + lock.lock(); + + uint32_t hash = p_name.hash(); + + uint32_t idx = hash & STRING_TABLE_MASK; + + _Data *_data = _table[idx]; + + while (_data) { + // compare hash first + if (_data->hash == hash && p_name == _data->get_name()) { + break; + } + + _data = _data->next; + } + + if (_data && _data->refcount.ref()) { +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + _data->debug_references++; + } +#endif + lock.unlock(); + return StringName(_data); + } + + lock.unlock(); + return StringName(); //does not exist +} + +StringName::StringName() { + _data = nullptr; +} + +/* +bool operator==(const String &p_name, const StringName &p_string_name) { + return p_name == p_string_name.operator String(); +} +bool operator!=(const String &p_name, const StringName &p_string_name) { + return p_name != p_string_name.operator String(); +} + +bool operator==(const char *p_name, const StringName &p_string_name) { + return p_name == p_string_name.operator String(); +} +bool operator!=(const char *p_name, const StringName &p_string_name) { + return p_name != p_string_name.operator String(); +} +*/ +#line 0 + + +#line 1 "sfwl/core/aabb.cpp" +/*************************************************************************/ +/* aabb.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +real_t AABB::get_volume() const { + return size.x * size.y * size.z; +} + +bool AABB::operator==(const AABB &p_rval) const { + return ((position == p_rval.position) && (size == p_rval.size)); +} +bool AABB::operator!=(const AABB &p_rval) const { + return ((position != p_rval.position) || (size != p_rval.size)); +} + +bool AABB::create_from_points(const Vector &p_points) { + if (!p_points.size()) { + return false; + } + + Vector3 begin = p_points[0]; + Vector3 end = begin; + + for (int n = 1; n < p_points.size(); n++) { + const Vector3 &pt = p_points[n]; + + if (pt.x < begin.x) { + begin.x = pt.x; + } + if (pt.y < begin.y) { + begin.y = pt.y; + } + if (pt.z < begin.z) { + begin.z = pt.z; + } + + if (pt.x > end.x) { + end.x = pt.x; + } + if (pt.y > end.y) { + end.y = pt.y; + } + if (pt.z > end.z) { + end.z = pt.z; + } + } + + position = begin; + size = end - begin; + + return true; +} + +void AABB::merge_with(const AABB &p_aabb) { + Vector3 beg_1, beg_2; + Vector3 end_1, end_2; + Vector3 min, max; + + beg_1 = position; + beg_2 = p_aabb.position; + end_1 = Vector3(size.x, size.y, size.z) + beg_1; + end_2 = Vector3(p_aabb.size.x, p_aabb.size.y, p_aabb.size.z) + beg_2; + + min.x = (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x; + min.y = (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y; + min.z = (beg_1.z < beg_2.z) ? beg_1.z : beg_2.z; + + max.x = (end_1.x > end_2.x) ? end_1.x : end_2.x; + max.y = (end_1.y > end_2.y) ? end_1.y : end_2.y; + max.z = (end_1.z > end_2.z) ? end_1.z : end_2.z; + + position = min; + size = max - min; +} + +bool AABB::is_equal_approx(const AABB &p_aabb) const { + return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size); +} + +AABB AABB::intersection(const AABB &p_aabb) const { + Vector3 src_min = position; + Vector3 src_max = position + size; + Vector3 dst_min = p_aabb.position; + Vector3 dst_max = p_aabb.position + p_aabb.size; + + Vector3 min, max; + + if (src_min.x > dst_max.x || src_max.x < dst_min.x) { + return AABB(); + } else { + min.x = (src_min.x > dst_min.x) ? src_min.x : dst_min.x; + max.x = (src_max.x < dst_max.x) ? src_max.x : dst_max.x; + } + + if (src_min.y > dst_max.y || src_max.y < dst_min.y) { + return AABB(); + } else { + min.y = (src_min.y > dst_min.y) ? src_min.y : dst_min.y; + max.y = (src_max.y < dst_max.y) ? src_max.y : dst_max.y; + } + + if (src_min.z > dst_max.z || src_max.z < dst_min.z) { + return AABB(); + } else { + min.z = (src_min.z > dst_min.z) ? src_min.z : dst_min.z; + max.z = (src_max.z < dst_max.z) ? src_max.z : dst_max.z; + } + + return AABB(min, max - min); +} + +bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *r_clip, Vector3 *r_normal) const { + Vector3 c1, c2; + Vector3 end = position + size; + real_t near = -1e20; + real_t far = 1e20; + int axis = 0; + + for (int i = 0; i < 3; i++) { + if (p_dir[i] == 0) { + if ((p_from[i] < position[i]) || (p_from[i] > end[i])) { + return false; + } + } else { // ray not parallel to planes in this direction + c1[i] = (position[i] - p_from[i]) / p_dir[i]; + c2[i] = (end[i] - p_from[i]) / p_dir[i]; + + if (c1[i] > c2[i]) { + SWAP(c1, c2); + } + if (c1[i] > near) { + near = c1[i]; + axis = i; + } + if (c2[i] < far) { + far = c2[i]; + } + if ((near > far) || (far < 0)) { + return false; + } + } + } + + if (r_clip) { + *r_clip = c1; + } + if (r_normal) { + *r_normal = Vector3(); + (*r_normal)[axis] = p_dir[axis] ? -1 : 1; + } + + return true; +} + +bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector3 *r_clip, Vector3 *r_normal) const { + real_t min = 0, max = 1; + int axis = 0; + real_t sign = 0; + + for (int i = 0; i < 3; i++) { + real_t seg_from = p_from[i]; + real_t seg_to = p_to[i]; + real_t box_begin = position[i]; + real_t box_end = box_begin + size[i]; + real_t cmin, cmax; + real_t csign; + + if (seg_from < seg_to) { + if (seg_from > box_end || seg_to < box_begin) { + return false; + } + real_t length = seg_to - seg_from; + cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; + cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; + csign = -1.0; + + } else { + if (seg_to > box_end || seg_from < box_begin) { + return false; + } + real_t length = seg_to - seg_from; + cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; + cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; + csign = 1.0; + } + + if (cmin > min) { + min = cmin; + axis = i; + sign = csign; + } + if (cmax < max) { + max = cmax; + } + if (max < min) { + return false; + } + } + + Vector3 rel = p_to - p_from; + + if (r_normal) { + Vector3 normal; + normal[axis] = sign; + *r_normal = normal; + } + + if (r_clip) { + *r_clip = p_from + rel * min; + } + + return true; +} + +bool AABB::intersects_plane(const Plane &p_plane) const { + Vector3 points[8] = { + Vector3(position.x, position.y, position.z), + Vector3(position.x, position.y, position.z + size.z), + Vector3(position.x, position.y + size.y, position.z), + Vector3(position.x, position.y + size.y, position.z + size.z), + Vector3(position.x + size.x, position.y, position.z), + Vector3(position.x + size.x, position.y, position.z + size.z), + Vector3(position.x + size.x, position.y + size.y, position.z), + Vector3(position.x + size.x, position.y + size.y, position.z + size.z), + }; + + bool over = false; + bool under = false; + + for (int i = 0; i < 8; i++) { + if (p_plane.distance_to(points[i]) > 0) { + over = true; + } else { + under = true; + } + } + + return under && over; +} + +Vector3 AABB::get_longest_axis() const { + Vector3 axis(1, 0, 0); + real_t max_size = size.x; + + if (size.y > max_size) { + axis = Vector3(0, 1, 0); + max_size = size.y; + } + + if (size.z > max_size) { + axis = Vector3(0, 0, 1); + } + + return axis; +} +int AABB::get_longest_axis_index() const { + int axis = 0; + real_t max_size = size.x; + + if (size.y > max_size) { + axis = 1; + max_size = size.y; + } + + if (size.z > max_size) { + axis = 2; + } + + return axis; +} + +Vector3 AABB::get_shortest_axis() const { + Vector3 axis(1, 0, 0); + real_t max_size = size.x; + + if (size.y < max_size) { + axis = Vector3(0, 1, 0); + max_size = size.y; + } + + if (size.z < max_size) { + axis = Vector3(0, 0, 1); + } + + return axis; +} +int AABB::get_shortest_axis_index() const { + int axis = 0; + real_t max_size = size.x; + + if (size.y < max_size) { + axis = 1; + max_size = size.y; + } + + if (size.z < max_size) { + axis = 2; + } + + return axis; +} + +AABB AABB::merge(const AABB &p_with) const { + AABB aabb = *this; + aabb.merge_with(p_with); + return aabb; +} +AABB AABB::expand(const Vector3 &p_vector) const { + AABB aabb = *this; + aabb.expand_to(p_vector); + return aabb; +} +AABB AABB::grow(real_t p_by) const { + AABB aabb = *this; + aabb.grow_by(p_by); + return aabb; +} + +void AABB::get_edge(int p_edge, Vector3 &r_from, Vector3 &r_to) const { + ERR_FAIL_INDEX(p_edge, 12); + switch (p_edge) { + case 0: { + r_from = Vector3(position.x + size.x, position.y, position.z); + r_to = Vector3(position.x, position.y, position.z); + } break; + case 1: { + r_from = Vector3(position.x + size.x, position.y, position.z + size.z); + r_to = Vector3(position.x + size.x, position.y, position.z); + } break; + case 2: { + r_from = Vector3(position.x, position.y, position.z + size.z); + r_to = Vector3(position.x + size.x, position.y, position.z + size.z); + + } break; + case 3: { + r_from = Vector3(position.x, position.y, position.z); + r_to = Vector3(position.x, position.y, position.z + size.z); + + } break; + case 4: { + r_from = Vector3(position.x, position.y + size.y, position.z); + r_to = Vector3(position.x + size.x, position.y + size.y, position.z); + } break; + case 5: { + r_from = Vector3(position.x + size.x, position.y + size.y, position.z); + r_to = Vector3(position.x + size.x, position.y + size.y, position.z + size.z); + } break; + case 6: { + r_from = Vector3(position.x + size.x, position.y + size.y, position.z + size.z); + r_to = Vector3(position.x, position.y + size.y, position.z + size.z); + + } break; + case 7: { + r_from = Vector3(position.x, position.y + size.y, position.z + size.z); + r_to = Vector3(position.x, position.y + size.y, position.z); + + } break; + case 8: { + r_from = Vector3(position.x, position.y, position.z + size.z); + r_to = Vector3(position.x, position.y + size.y, position.z + size.z); + + } break; + case 9: { + r_from = Vector3(position.x, position.y, position.z); + r_to = Vector3(position.x, position.y + size.y, position.z); + + } break; + case 10: { + r_from = Vector3(position.x + size.x, position.y, position.z); + r_to = Vector3(position.x + size.x, position.y + size.y, position.z); + + } break; + case 11: { + r_from = Vector3(position.x + size.x, position.y, position.z + size.z); + r_to = Vector3(position.x + size.x, position.y + size.y, position.z + size.z); + + } break; + } +} + +/* +Variant AABB::intersects_segmentv(const Vector3 &p_from, const Vector3 &p_to) const { + Vector3 inters; + if (intersects_segment(p_from, p_to, &inters)) { + return inters; + } + return Variant(); +} + +Variant AABB::intersects_rayv(const Vector3 &p_from, const Vector3 &p_dir) const { + Vector3 inters; + if (intersects_ray(p_from, p_dir, &inters)) { + return inters; + } + return Variant(); +} +*/ + +AABB::operator String() const { + return "[P: " + position.operator String() + ", S: " + size + "]"; +} +#line 0 + +#line 1 "sfwl/core/vector3i.cpp" +/*************************************************************************/ +/* vector3i.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +void Vector3i::set_axis(const int p_axis, const int32_t p_value) { + ERR_FAIL_INDEX(p_axis, 3); + coord[p_axis] = p_value; +} + +int32_t Vector3i::get_axis(const int p_axis) const { + ERR_FAIL_INDEX_V(p_axis, 3, 0); + return operator[](p_axis); +} + +Vector3i::Axis Vector3i::min_axis() const { + return x < y ? (x < z ? Vector3i::AXIS_X : Vector3i::AXIS_Z) : (y < z ? Vector3i::AXIS_Y : Vector3i::AXIS_Z); +} + +Vector3i::Axis Vector3i::max_axis() const { + return x < y ? (y < z ? Vector3i::AXIS_Z : Vector3i::AXIS_Y) : (x < z ? Vector3i::AXIS_Z : Vector3i::AXIS_X); +} + +Vector3i Vector3i::clamp(const Vector3i &p_min, const Vector3i &p_max) const { + return Vector3i( + CLAMP(x, p_min.x, p_max.x), + CLAMP(y, p_min.y, p_max.y), + CLAMP(z, p_min.z, p_max.z)); +} + +Vector3 Vector3i::to_vector3() const { + return Vector3(x, y, z); +} + +Vector3i::operator String() const { + return "(" + itos(x) + ", " + itos(y) + ", " + itos(z) + ")"; +} + +Vector3i::operator Vector3() const { + return Vector3(x, y, z); +} +#line 0 + +#line 1 "sfwl/core/transform_2d.cpp" +/*************************************************************************/ +/* transform_2d.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +void Transform2D::invert() { + // FIXME: this function assumes the basis is a rotation matrix, with no scaling. + // Transform2D::affine_inverse can handle matrices with scaling, so GDScript should eventually use that. + SWAP(columns[0][1], columns[1][0]); + columns[2] = basis_xform(-columns[2]); +} + +Transform2D Transform2D::inverse() const { + Transform2D inv = *this; + inv.invert(); + return inv; +} + +void Transform2D::affine_invert() { + real_t det = basis_determinant(); +#ifdef MATH_CHECKS + ERR_FAIL_COND(det == 0); +#endif + real_t idet = 1 / det; + + SWAP(columns[0][0], columns[1][1]); + columns[0] *= Vector2(idet, -idet); + columns[1] *= Vector2(-idet, idet); + + columns[2] = basis_xform(-columns[2]); +} + +Transform2D Transform2D::affine_inverse() const { + Transform2D inv = *this; + inv.affine_invert(); + return inv; +} + +void Transform2D::rotate(real_t p_phi) { + *this = Transform2D(p_phi, Vector2()) * (*this); +} + +real_t Transform2D::get_rotation() const { + return Math::atan2(columns[0].y, columns[0].x); +} + +void Transform2D::set_rotation(real_t p_rot) { + Size2 scale = get_scale(); + real_t cr = Math::cos(p_rot); + real_t sr = Math::sin(p_rot); + columns[0][0] = cr; + columns[0][1] = sr; + columns[1][0] = -sr; + columns[1][1] = cr; + set_scale(scale); +} + +real_t Transform2D::get_skew() const { + real_t det = basis_determinant(); + return Math::acos(columns[0].normalized().dot(SGN(det) * columns[1].normalized())) - (real_t)Math_PI * 0.5f; +} + +void Transform2D::set_skew(const real_t p_angle) { + real_t det = basis_determinant(); + columns[1] = SGN(det) * columns[0].rotated(((real_t)Math_PI * 0.5f + p_angle)).normalized() * columns[1].length(); +} + +Transform2D::Transform2D(real_t p_rot, const Vector2 &p_pos) { + real_t cr = Math::cos(p_rot); + real_t sr = Math::sin(p_rot); + columns[0][0] = cr; + columns[0][1] = sr; + columns[1][0] = -sr; + columns[1][1] = cr; + columns[2] = p_pos; +} + +Transform2D::Transform2D(const real_t p_rot, const Size2 &p_scale, const real_t p_skew, const Vector2 &p_pos) { + columns[0][0] = Math::cos(p_rot) * p_scale.x; + columns[1][1] = Math::cos(p_rot + p_skew) * p_scale.y; + columns[1][0] = -Math::sin(p_rot + p_skew) * p_scale.y; + columns[0][1] = Math::sin(p_rot) * p_scale.x; + columns[2] = p_pos; +} + +Size2 Transform2D::get_scale() const { + real_t det_sign = SGN(basis_determinant()); + return Size2(columns[0].length(), det_sign * columns[1].length()); +} + +void Transform2D::set_scale(const Size2 &p_scale) { + columns[0].normalize(); + columns[1].normalize(); + columns[0] *= p_scale.x; + columns[1] *= p_scale.y; +} + +void Transform2D::scale(const Size2 &p_scale) { + scale_basis(p_scale); + columns[2] *= p_scale; +} +void Transform2D::scale_basis(const Size2 &p_scale) { + columns[0][0] *= p_scale.x; + columns[0][1] *= p_scale.y; + columns[1][0] *= p_scale.x; + columns[1][1] *= p_scale.y; +} + +void Transform2D::translate(real_t p_tx, real_t p_ty) { + translate(Vector2(p_tx, p_ty)); +} +void Transform2D::translate(const Vector2 &p_offset) { + columns[2] += p_offset; +} + +void Transform2D::translate_local(real_t p_tx, real_t p_ty) { + translate_local(Vector2(p_tx, p_ty)); +} +void Transform2D::translate_local(const Vector2 &p_translation) { + columns[2] += basis_xform(p_translation); +} + +void Transform2D::translater(real_t p_tx, real_t p_ty) { + translate(Vector2(p_tx, p_ty)); +} +void Transform2D::translatev(const Vector2 &p_offset) { + columns[2] += p_offset; +} + +void Transform2D::translate_localr(real_t p_tx, real_t p_ty) { + translate_local(Vector2(p_tx, p_ty)); +} +void Transform2D::translate_localv(const Vector2 &p_translation) { + columns[2] += basis_xform(p_translation); +} + +void Transform2D::orthonormalize() { + // Gram-Schmidt Process + + Vector2 x = columns[0]; + Vector2 y = columns[1]; + + x.normalize(); + y = (y - x * (x.dot(y))); + y.normalize(); + + columns[0] = x; + columns[1] = y; +} + +Transform2D Transform2D::orthonormalized() const { + Transform2D on = *this; + on.orthonormalize(); + return on; +} + +bool Transform2D::is_equal_approx(const Transform2D &p_transform) const { + return columns[0].is_equal_approx(p_transform.columns[0]) && columns[1].is_equal_approx(p_transform.columns[1]) && columns[2].is_equal_approx(p_transform.columns[2]); +} + +Transform2D Transform2D::looking_at(const Vector2 &p_target) const { + Transform2D return_trans = Transform2D(get_rotation(), get_origin()); + Vector2 target_position = affine_inverse().xform(p_target); + return_trans.set_rotation(return_trans.get_rotation() + (target_position * get_scale()).angle()); + return return_trans; +} + +bool Transform2D::operator==(const Transform2D &p_transform) const { + for (int i = 0; i < 3; i++) { + if (columns[i] != p_transform.columns[i]) { + return false; + } + } + + return true; +} + +bool Transform2D::operator!=(const Transform2D &p_transform) const { + for (int i = 0; i < 3; i++) { + if (columns[i] != p_transform.columns[i]) { + return true; + } + } + + return false; +} + +void Transform2D::operator*=(const Transform2D &p_transform) { + columns[2] = xform(p_transform.columns[2]); + + real_t x0, x1, y0, y1; + + x0 = tdotx(p_transform.columns[0]); + x1 = tdoty(p_transform.columns[0]); + y0 = tdotx(p_transform.columns[1]); + y1 = tdoty(p_transform.columns[1]); + + columns[0][0] = x0; + columns[0][1] = x1; + columns[1][0] = y0; + columns[1][1] = y1; +} + +Transform2D Transform2D::operator*(const Transform2D &p_transform) const { + Transform2D t = *this; + t *= p_transform; + return t; +} + +void Transform2D::operator*=(const real_t p_val) { + columns[0] *= p_val; + columns[1] *= p_val; + columns[2] *= p_val; +} + +Transform2D Transform2D::operator*(const real_t p_val) const { + Transform2D ret(*this); + ret *= p_val; + return ret; +} + +Transform2D Transform2D::basis_scaled(const Size2 &p_scale) const { + Transform2D copy = *this; + copy.scale_basis(p_scale); + return copy; +} + +Transform2D Transform2D::scaled(const Size2 &p_scale) const { + // Equivalent to left multiplication + Transform2D copy = *this; + copy.scale(p_scale); + return copy; +} + +Transform2D Transform2D::scaled_local(const Size2 &p_scale) const { + // Equivalent to right multiplication + return Transform2D(columns[0] * p_scale.x, columns[1] * p_scale.y, columns[2]); +} + +Transform2D Transform2D::untranslated() const { + Transform2D copy = *this; + copy.columns[2] = Vector2(); + return copy; +} + +Transform2D Transform2D::translated(const Vector2 &p_offset) const { + // Equivalent to left multiplication + return Transform2D(columns[0], columns[1], columns[2] + p_offset); +} + +Transform2D Transform2D::translated_local(const Vector2 &p_offset) const { + // Equivalent to right multiplication + return Transform2D(columns[0], columns[1], columns[2] + basis_xform(p_offset)); +} + +Transform2D Transform2D::rotated(const real_t p_angle) const { + // Equivalent to left multiplication + return Transform2D(p_angle, Vector2()) * (*this); +} + +Transform2D Transform2D::rotated_local(const real_t p_angle) const { + // Equivalent to right multiplication + return (*this) * Transform2D(p_angle, Vector2()); // Could be optimized, because origin transform can be skipped. +} + +real_t Transform2D::basis_determinant() const { + return columns[0].x * columns[1].y - columns[0].y * columns[1].x; +} + +Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t p_c) const { + //extract parameters + Vector2 p1 = get_origin(); + Vector2 p2 = p_transform.get_origin(); + + real_t r1 = get_rotation(); + real_t r2 = p_transform.get_rotation(); + + Size2 s1 = get_scale(); + Size2 s2 = p_transform.get_scale(); + + //slerp rotation + Vector2 v1(Math::cos(r1), Math::sin(r1)); + Vector2 v2(Math::cos(r2), Math::sin(r2)); + + real_t dot = v1.dot(v2); + + dot = CLAMP(dot, -1, 1); + + Vector2 v; + + if (dot > 0.9995f) { + v = Vector2::linear_interpolate(v1, v2, p_c).normalized(); //linearly interpolate to avoid numerical precision issues + } else { + real_t angle = p_c * Math::acos(dot); + Vector2 v3 = (v2 - v1 * dot).normalized(); + v = v1 * Math::cos(angle) + v3 * Math::sin(angle); + } + + //construct matrix + Transform2D res(Math::atan2(v.y, v.x), Vector2::linear_interpolate(p1, p2, p_c)); + res.scale_basis(Vector2::linear_interpolate(s1, s2, p_c)); + return res; +} + +Transform2D::operator String() const { + return "[X: " + columns[0].operator String() + + ", Y: " + columns[1].operator String() + + ", O: " + columns[2].operator String() + "]"; +} +#line 0 + +#line 1 "sfwl/core/projection.cpp" +/*************************************************************************/ +/* projection.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +float Projection::determinant() const { + return matrix[0][3] * matrix[1][2] * matrix[2][1] * matrix[3][0] - matrix[0][2] * matrix[1][3] * matrix[2][1] * matrix[3][0] - + matrix[0][3] * matrix[1][1] * matrix[2][2] * matrix[3][0] + matrix[0][1] * matrix[1][3] * matrix[2][2] * matrix[3][0] + + matrix[0][2] * matrix[1][1] * matrix[2][3] * matrix[3][0] - matrix[0][1] * matrix[1][2] * matrix[2][3] * matrix[3][0] - + matrix[0][3] * matrix[1][2] * matrix[2][0] * matrix[3][1] + matrix[0][2] * matrix[1][3] * matrix[2][0] * matrix[3][1] + + matrix[0][3] * matrix[1][0] * matrix[2][2] * matrix[3][1] - matrix[0][0] * matrix[1][3] * matrix[2][2] * matrix[3][1] - + matrix[0][2] * matrix[1][0] * matrix[2][3] * matrix[3][1] + matrix[0][0] * matrix[1][2] * matrix[2][3] * matrix[3][1] + + matrix[0][3] * matrix[1][1] * matrix[2][0] * matrix[3][2] - matrix[0][1] * matrix[1][3] * matrix[2][0] * matrix[3][2] - + matrix[0][3] * matrix[1][0] * matrix[2][1] * matrix[3][2] + matrix[0][0] * matrix[1][3] * matrix[2][1] * matrix[3][2] + + matrix[0][1] * matrix[1][0] * matrix[2][3] * matrix[3][2] - matrix[0][0] * matrix[1][1] * matrix[2][3] * matrix[3][2] - + matrix[0][2] * matrix[1][1] * matrix[2][0] * matrix[3][3] + matrix[0][1] * matrix[1][2] * matrix[2][0] * matrix[3][3] + + matrix[0][2] * matrix[1][0] * matrix[2][1] * matrix[3][3] - matrix[0][0] * matrix[1][2] * matrix[2][1] * matrix[3][3] - + matrix[0][1] * matrix[1][0] * matrix[2][2] * matrix[3][3] + matrix[0][0] * matrix[1][1] * matrix[2][2] * matrix[3][3]; +} + +void Projection::set_identity() { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + matrix[i][j] = (i == j) ? 1 : 0; + } + } +} + +void Projection::set_zero() { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + matrix[i][j] = 0; + } + } +} + +void Projection::adjust_perspective_znear(real_t p_new_znear) { + real_t zfar = get_z_far(); + real_t znear = p_new_znear; + + real_t deltaZ = zfar - znear; + matrix[2][2] = -(zfar + znear) / deltaZ; + matrix[3][2] = -2 * znear * zfar / deltaZ; +} + +Projection Projection::create_depth_correction(bool p_flip_y) { + Projection proj; + proj.set_depth_correction(p_flip_y); + return proj; +} + +Projection Projection::create_light_atlas_rect(const Rect2 &p_rect) { + Projection proj; + proj.set_light_atlas_rect(p_rect); + return proj; +} + +Projection Projection::create_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov) { + Projection proj; + proj.set_perspective(p_fovy_degrees, p_aspect, p_z_near, p_z_far, p_flip_fov); + return proj; +} + +Projection Projection::create_perspective_hmd(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) { + Projection proj; + proj.set_perspective(p_fovy_degrees, p_aspect, p_z_near, p_z_far, p_flip_fov, p_eye, p_intraocular_dist, p_convergence_dist); + return proj; +} + +Projection Projection::create_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) { + Projection proj; + proj.set_for_hmd(p_eye, p_aspect, p_intraocular_dist, p_display_width, p_display_to_lens, p_oversample, p_z_near, p_z_far); + return proj; +} + +Projection Projection::create_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) { + Projection proj; + proj.set_orthogonal(p_left, p_right, p_bottom, p_top, p_zfar, p_zfar); + return proj; +} + +Projection Projection::create_orthogonal_aspect(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov) { + Projection proj; + proj.set_orthogonal(p_size, p_aspect, p_znear, p_zfar, p_flip_fov); + return proj; +} + +Projection Projection::create_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) { + Projection proj; + proj.set_frustum(p_left, p_right, p_bottom, p_top, p_near, p_far); + return proj; +} + +Projection Projection::create_frustum_aspect(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) { + Projection proj; + proj.set_frustum(p_size, p_aspect, p_offset, p_near, p_far, p_flip_fov); + return proj; +} + +Projection Projection::create_fit_aabb(const AABB &p_aabb) { + Projection proj; + proj.scale_translate_to_fit(p_aabb); + return proj; +} + +Projection Projection::perspective_znear_adjusted(real_t p_new_znear) const { + Projection proj = *this; + proj.adjust_perspective_znear(p_new_znear); + return proj; +} + +Plane Projection::get_projection_plane(Projection::Planes p_plane) const { + const real_t *matrix = (const real_t *)this->matrix; + + switch (p_plane) { + case PLANE_NEAR: { + Plane new_plane = Plane(matrix[3] + matrix[2], + matrix[7] + matrix[6], + matrix[11] + matrix[10], + matrix[15] + matrix[14]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + return new_plane; + } break; + case PLANE_FAR: { + Plane new_plane = Plane(matrix[3] - matrix[2], + matrix[7] - matrix[6], + matrix[11] - matrix[10], + matrix[15] - matrix[14]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + return new_plane; + } break; + case PLANE_LEFT: { + Plane new_plane = Plane(matrix[3] + matrix[0], + matrix[7] + matrix[4], + matrix[11] + matrix[8], + matrix[15] + matrix[12]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + return new_plane; + } break; + case PLANE_TOP: { + Plane new_plane = Plane(matrix[3] - matrix[1], + matrix[7] - matrix[5], + matrix[11] - matrix[9], + matrix[15] - matrix[13]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + return new_plane; + } break; + case PLANE_RIGHT: { + Plane new_plane = Plane(matrix[3] - matrix[0], + matrix[7] - matrix[4], + matrix[11] - matrix[8], + matrix[15] - matrix[12]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + return new_plane; + } break; + case PLANE_BOTTOM: { + Plane new_plane = Plane(matrix[3] + matrix[1], + matrix[7] + matrix[5], + matrix[11] + matrix[9], + matrix[15] + matrix[13]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + return new_plane; + } break; + } + + return Plane(); +} + +Projection Projection::flipped_y() const { + Projection proj = *this; + proj.flip_y(); + return proj; +} + +Projection Projection ::jitter_offseted(const Vector2 &p_offset) const { + Projection proj = *this; + proj.add_jitter_offset(p_offset); + return proj; +} + +void Projection::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov) { + if (p_flip_fov) { + p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect); + } + + real_t sine, cotangent, deltaZ; + real_t radians = Math::deg2rad(p_fovy_degrees / 2.0); + + deltaZ = p_z_far - p_z_near; + sine = Math::sin(radians); + + if ((deltaZ == 0) || (sine == 0) || (p_aspect == 0)) { + return; + } + cotangent = Math::cos(radians) / sine; + + set_identity(); + + matrix[0][0] = cotangent / p_aspect; + matrix[1][1] = cotangent; + matrix[2][2] = -(p_z_far + p_z_near) / deltaZ; + matrix[2][3] = -1; + matrix[3][2] = -2 * p_z_near * p_z_far / deltaZ; + matrix[3][3] = 0; +} + +void Projection::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) { + if (p_flip_fov) { + p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect); + } + + real_t left, right, modeltranslation, ymax, xmax, frustumshift; + + ymax = p_z_near * tan(Math::deg2rad(p_fovy_degrees / 2.0)); + xmax = ymax * p_aspect; + frustumshift = (p_intraocular_dist / 2.0) * p_z_near / p_convergence_dist; + + switch (p_eye) { + case 1: { // left eye + left = -xmax + frustumshift; + right = xmax + frustumshift; + modeltranslation = p_intraocular_dist / 2.0; + } break; + case 2: { // right eye + left = -xmax - frustumshift; + right = xmax - frustumshift; + modeltranslation = -p_intraocular_dist / 2.0; + } break; + default: { // mono, should give the same result as set_perspective(p_fovy_degrees,p_aspect,p_z_near,p_z_far,p_flip_fov) + left = -xmax; + right = xmax; + modeltranslation = 0.0; + } break; + } + + set_frustum(left, right, -ymax, ymax, p_z_near, p_z_far); + + // translate matrix by (modeltranslation, 0.0, 0.0) + Projection cm; + cm.set_identity(); + cm.matrix[3][0] = modeltranslation; + *this = *this * cm; +} + +void Projection::set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) { + // we first calculate our base frustum on our values without taking our lens magnification into account. + real_t f1 = (p_intraocular_dist * 0.5) / p_display_to_lens; + real_t f2 = ((p_display_width - p_intraocular_dist) * 0.5) / p_display_to_lens; + real_t f3 = (p_display_width / 4.0) / p_display_to_lens; + + // now we apply our oversample factor to increase our FOV. how much we oversample is always a balance we strike between performance and how much + // we're willing to sacrifice in FOV. + real_t add = ((f1 + f2) * (p_oversample - 1.0)) / 2.0; + f1 += add; + f2 += add; + f3 *= p_oversample; + + // always apply KEEP_WIDTH aspect ratio + f3 /= p_aspect; + + switch (p_eye) { + case 1: { // left eye + set_frustum(-f2 * p_z_near, f1 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); + } break; + case 2: { // right eye + set_frustum(-f1 * p_z_near, f2 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); + } break; + default: { // mono, does not apply here! + } break; + } +} + +void Projection::set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) { + set_identity(); + + matrix[0][0] = 2.0 / (p_right - p_left); + matrix[3][0] = -((p_right + p_left) / (p_right - p_left)); + matrix[1][1] = 2.0 / (p_top - p_bottom); + matrix[3][1] = -((p_top + p_bottom) / (p_top - p_bottom)); + matrix[2][2] = -2.0 / (p_zfar - p_znear); + matrix[3][2] = -((p_zfar + p_znear) / (p_zfar - p_znear)); + matrix[3][3] = 1.0; +} + +void Projection::set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov) { + if (!p_flip_fov) { + p_size *= p_aspect; + } + + set_orthogonal(-p_size / 2, +p_size / 2, -p_size / p_aspect / 2, +p_size / p_aspect / 2, p_znear, p_zfar); +} + +void Projection::set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) { + ERR_FAIL_COND(p_right <= p_left); + ERR_FAIL_COND(p_top <= p_bottom); + ERR_FAIL_COND(p_far <= p_near); + + real_t *te = &matrix[0][0]; + real_t x = 2 * p_near / (p_right - p_left); + real_t y = 2 * p_near / (p_top - p_bottom); + + real_t a = (p_right + p_left) / (p_right - p_left); + real_t b = (p_top + p_bottom) / (p_top - p_bottom); + real_t c = -(p_far + p_near) / (p_far - p_near); + real_t d = -2 * p_far * p_near / (p_far - p_near); + + te[0] = x; + te[1] = 0; + te[2] = 0; + te[3] = 0; + te[4] = 0; + te[5] = y; + te[6] = 0; + te[7] = 0; + te[8] = a; + te[9] = b; + te[10] = c; + te[11] = -1; + te[12] = 0; + te[13] = 0; + te[14] = d; + te[15] = 0; +} + +void Projection::set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) { + if (!p_flip_fov) { + p_size *= p_aspect; + } + + set_frustum(-p_size / 2 + p_offset.x, +p_size / 2 + p_offset.x, -p_size / p_aspect / 2 + p_offset.y, +p_size / p_aspect / 2 + p_offset.y, p_near, p_far); +} + +real_t Projection::get_z_far() const { + const real_t *matrix = (const real_t *)this->matrix; + Plane new_plane = Plane(matrix[3] - matrix[2], + matrix[7] - matrix[6], + matrix[11] - matrix[10], + matrix[15] - matrix[14]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + return new_plane.d; +} + +real_t Projection::get_z_near() const { + const real_t *matrix = (const real_t *)this->matrix; + Plane new_plane = Plane(matrix[3] + matrix[2], + matrix[7] + matrix[6], + matrix[11] + matrix[10], + -matrix[15] - matrix[14]); + + new_plane.normalize(); + return new_plane.d; +} + +Vector2 Projection::get_viewport_half_extents() const { + const real_t *matrix = (const real_t *)this->matrix; + ///////--- Near Plane ---/////// + Plane near_plane = Plane(matrix[3] + matrix[2], + matrix[7] + matrix[6], + matrix[11] + matrix[10], + -matrix[15] - matrix[14]); + near_plane.normalize(); + + ///////--- Right Plane ---/////// + Plane right_plane = Plane(matrix[3] - matrix[0], + matrix[7] - matrix[4], + matrix[11] - matrix[8], + -matrix[15] + matrix[12]); + right_plane.normalize(); + + Plane top_plane = Plane(matrix[3] - matrix[1], + matrix[7] - matrix[5], + matrix[11] - matrix[9], + -matrix[15] + matrix[13]); + top_plane.normalize(); + + Vector3 res; + near_plane.intersect_3(right_plane, top_plane, &res); + + return Vector2(res.x, res.y); +} + +Vector2 Projection::get_far_plane_half_extents() const { + const real_t *matrix = (const real_t *)this->matrix; + ///////--- Far Plane ---/////// + Plane far_plane = Plane(matrix[3] - matrix[2], + matrix[7] - matrix[6], + matrix[11] - matrix[10], + -matrix[15] + matrix[14]); + far_plane.normalize(); + + ///////--- Right Plane ---/////// + Plane right_plane = Plane(matrix[3] - matrix[0], + matrix[7] - matrix[4], + matrix[11] - matrix[8], + -matrix[15] + matrix[12]); + right_plane.normalize(); + + Plane top_plane = Plane(matrix[3] - matrix[1], + matrix[7] - matrix[5], + matrix[11] - matrix[9], + -matrix[15] + matrix[13]); + top_plane.normalize(); + + Vector3 res; + far_plane.intersect_3(right_plane, top_plane, &res); + + return Vector2(res.x, res.y); +} + +bool Projection::get_endpoints(const Transform &p_transform, Vector3 *p_8points) const { + Vector planes = get_projection_planes(Transform()); + const Planes intersections[8][3] = { + { PLANE_FAR, PLANE_LEFT, PLANE_TOP }, + { PLANE_FAR, PLANE_LEFT, PLANE_BOTTOM }, + { PLANE_FAR, PLANE_RIGHT, PLANE_TOP }, + { PLANE_FAR, PLANE_RIGHT, PLANE_BOTTOM }, + { PLANE_NEAR, PLANE_LEFT, PLANE_TOP }, + { PLANE_NEAR, PLANE_LEFT, PLANE_BOTTOM }, + { PLANE_NEAR, PLANE_RIGHT, PLANE_TOP }, + { PLANE_NEAR, PLANE_RIGHT, PLANE_BOTTOM }, + }; + + for (int i = 0; i < 8; i++) { + Vector3 point; + bool res = planes[intersections[i][0]].intersect_3(planes[intersections[i][1]], planes[intersections[i][2]], &point); + ERR_FAIL_COND_V(!res, false); + p_8points[i] = p_transform.xform(point); + } + + return true; +} + +Vector Projection::get_projection_planes(const Transform &p_transform) const { + /** Fast Plane Extraction from combined modelview/projection matrices. + * References: + * https://web.archive.org/web/20011221205252/https://www.markmorley.com/opengl/frustumculling.html + * https://web.archive.org/web/20061020020112/https://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf + */ + + Vector planes; + planes.resize(6); + + const real_t *matrix = (const real_t *)this->matrix; + + Plane new_plane; + + ///////--- Near Plane ---/////// + new_plane = Plane(matrix[3] + matrix[2], + matrix[7] + matrix[6], + matrix[11] + matrix[10], + matrix[15] + matrix[14]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + planes.write[0] = p_transform.xform(new_plane); + + ///////--- Far Plane ---/////// + new_plane = Plane(matrix[3] - matrix[2], + matrix[7] - matrix[6], + matrix[11] - matrix[10], + matrix[15] - matrix[14]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + planes.write[1] = p_transform.xform(new_plane); + + ///////--- Left Plane ---/////// + new_plane = Plane(matrix[3] + matrix[0], + matrix[7] + matrix[4], + matrix[11] + matrix[8], + matrix[15] + matrix[12]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + planes.write[2] = p_transform.xform(new_plane); + + ///////--- Top Plane ---/////// + new_plane = Plane(matrix[3] - matrix[1], + matrix[7] - matrix[5], + matrix[11] - matrix[9], + matrix[15] - matrix[13]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + planes.write[3] = p_transform.xform(new_plane); + + ///////--- Right Plane ---/////// + new_plane = Plane(matrix[3] - matrix[0], + matrix[7] - matrix[4], + matrix[11] - matrix[8], + matrix[15] - matrix[12]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + planes.write[4] = p_transform.xform(new_plane); + + ///////--- Bottom Plane ---/////// + new_plane = Plane(matrix[3] + matrix[1], + matrix[7] + matrix[5], + matrix[11] + matrix[9], + matrix[15] + matrix[13]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + planes.write[5] = p_transform.xform(new_plane); + + return planes; +} + +Projection Projection::inverse() const { + Projection cm = *this; + cm.invert(); + return cm; +} + +void Projection::invert() { + int i, j, k; + int pvt_i[4], pvt_j[4]; /* Locations of pivot matrix */ + real_t pvt_val; /* Value of current pivot element */ + real_t hold; /* Temporary storage */ + real_t determinant = 1.0f; + for (k = 0; k < 4; k++) { + /** Locate k'th pivot element **/ + pvt_val = matrix[k][k]; /** Initialize for search **/ + pvt_i[k] = k; + pvt_j[k] = k; + for (i = k; i < 4; i++) { + for (j = k; j < 4; j++) { + if (Math::abs(matrix[i][j]) > Math::abs(pvt_val)) { + pvt_i[k] = i; + pvt_j[k] = j; + pvt_val = matrix[i][j]; + } + } + } + + /** Product of pivots, gives determinant when finished **/ + determinant *= pvt_val; + if (Math::is_zero_approx(determinant)) { + return; /** Matrix is singular (zero determinant). **/ + } + + /** "Interchange" rows (with sign change stuff) **/ + i = pvt_i[k]; + if (i != k) { /** If rows are different **/ + for (j = 0; j < 4; j++) { + hold = -matrix[k][j]; + matrix[k][j] = matrix[i][j]; + matrix[i][j] = hold; + } + } + + /** "Interchange" columns **/ + j = pvt_j[k]; + if (j != k) { /** If columns are different **/ + for (i = 0; i < 4; i++) { + hold = -matrix[i][k]; + matrix[i][k] = matrix[i][j]; + matrix[i][j] = hold; + } + } + + /** Divide column by minus pivot value **/ + for (i = 0; i < 4; i++) { + if (i != k) { + matrix[i][k] /= (-pvt_val); + } + } + + /** Reduce the matrix **/ + for (i = 0; i < 4; i++) { + hold = matrix[i][k]; + for (j = 0; j < 4; j++) { + if (i != k && j != k) { + matrix[i][j] += hold * matrix[k][j]; + } + } + } + + /** Divide row by pivot **/ + for (j = 0; j < 4; j++) { + if (j != k) { + matrix[k][j] /= pvt_val; + } + } + + /** Replace pivot by reciprocal (at last we can touch it). **/ + matrix[k][k] = 1.0 / pvt_val; + } + + /* That was most of the work, one final pass of row/column interchange */ + /* to finish */ + for (k = 4 - 2; k >= 0; k--) { /* Don't need to work with 1 by 1 corner*/ + i = pvt_j[k]; /* Rows to swap correspond to pivot COLUMN */ + if (i != k) { /* If rows are different */ + for (j = 0; j < 4; j++) { + hold = matrix[k][j]; + matrix[k][j] = -matrix[i][j]; + matrix[i][j] = hold; + } + } + + j = pvt_i[k]; /* Columns to swap correspond to pivot ROW */ + if (j != k) { /* If columns are different */ + for (i = 0; i < 4; i++) { + hold = matrix[i][k]; + matrix[i][k] = -matrix[i][j]; + matrix[i][j] = hold; + } + } + } +} + +void Projection::flip_y() { + for (int i = 0; i < 4; i++) { + matrix[1][i] = -matrix[1][i]; + } +} + +Projection::Projection() { + set_identity(); +} + +Projection Projection::operator*(const Projection &p_matrix) const { + Projection new_matrix; + + for (int j = 0; j < 4; j++) { + for (int i = 0; i < 4; i++) { + real_t ab = 0; + for (int k = 0; k < 4; k++) { + ab += matrix[k][i] * p_matrix.matrix[j][k]; + } + new_matrix.matrix[j][i] = ab; + } + } + + return new_matrix; +} + +void Projection::set_depth_correction(bool p_flip_y) { + real_t *m = &matrix[0][0]; + + m[0] = 1; + m[1] = 0.0; + m[2] = 0.0; + m[3] = 0.0; + m[4] = 0.0; + m[5] = p_flip_y ? -1 : 1; + m[6] = 0.0; + m[7] = 0.0; + m[8] = 0.0; + m[9] = 0.0; + m[10] = 0.5; + m[11] = 0.0; + m[12] = 0.0; + m[13] = 0.0; + m[14] = 0.5; + m[15] = 1.0; +} + +void Projection::set_light_bias() { + real_t *m = &matrix[0][0]; + + m[0] = 0.5; + m[1] = 0.0; + m[2] = 0.0; + m[3] = 0.0; + m[4] = 0.0; + m[5] = 0.5; + m[6] = 0.0; + m[7] = 0.0; + m[8] = 0.0; + m[9] = 0.0; + m[10] = 0.5; + m[11] = 0.0; + m[12] = 0.5; + m[13] = 0.5; + m[14] = 0.5; + m[15] = 1.0; +} + +void Projection::set_light_atlas_rect(const Rect2 &p_rect) { + real_t *m = &matrix[0][0]; + + m[0] = p_rect.size.width; + m[1] = 0.0; + m[2] = 0.0; + m[3] = 0.0; + m[4] = 0.0; + m[5] = p_rect.size.height; + m[6] = 0.0; + m[7] = 0.0; + m[8] = 0.0; + m[9] = 0.0; + m[10] = 1.0; + m[11] = 0.0; + m[12] = p_rect.position.x; + m[13] = p_rect.position.y; + m[14] = 0.0; + m[15] = 1.0; +} + +Vector4 Projection::xform(const Vector4 &p_vec4) const { + return Vector4( + matrix[0][0] * p_vec4.x + matrix[1][0] * p_vec4.y + matrix[2][0] * p_vec4.z + matrix[3][0] * p_vec4.w, + matrix[0][1] * p_vec4.x + matrix[1][1] * p_vec4.y + matrix[2][1] * p_vec4.z + matrix[3][1] * p_vec4.w, + matrix[0][2] * p_vec4.x + matrix[1][2] * p_vec4.y + matrix[2][2] * p_vec4.z + matrix[3][2] * p_vec4.w, + matrix[0][3] * p_vec4.x + matrix[1][3] * p_vec4.y + matrix[2][3] * p_vec4.z + matrix[3][3] * p_vec4.w); +} + +Vector4 Projection::xform_inv(const Vector4 &p_vec4) const { + return Vector4( + matrix[0][0] * p_vec4.x + matrix[0][1] * p_vec4.y + matrix[0][2] * p_vec4.z + matrix[0][3] * p_vec4.w, + matrix[1][0] * p_vec4.x + matrix[1][1] * p_vec4.y + matrix[1][2] * p_vec4.z + matrix[1][3] * p_vec4.w, + matrix[2][0] * p_vec4.x + matrix[2][1] * p_vec4.y + matrix[2][2] * p_vec4.z + matrix[2][3] * p_vec4.w, + matrix[3][0] * p_vec4.x + matrix[3][1] * p_vec4.y + matrix[3][2] * p_vec4.z + matrix[3][3] * p_vec4.w); +} + +Plane Projection::xform(const Plane &p_vec4) const { + Plane ret; + + ret.normal.x = matrix[0][0] * p_vec4.normal.x + matrix[1][0] * p_vec4.normal.y + matrix[2][0] * p_vec4.normal.z + matrix[3][0] * p_vec4.d; + ret.normal.y = matrix[0][1] * p_vec4.normal.x + matrix[1][1] * p_vec4.normal.y + matrix[2][1] * p_vec4.normal.z + matrix[3][1] * p_vec4.d; + ret.normal.z = matrix[0][2] * p_vec4.normal.x + matrix[1][2] * p_vec4.normal.y + matrix[2][2] * p_vec4.normal.z + matrix[3][2] * p_vec4.d; + ret.d = matrix[0][3] * p_vec4.normal.x + matrix[1][3] * p_vec4.normal.y + matrix[2][3] * p_vec4.normal.z + matrix[3][3] * p_vec4.d; + return ret; +} + +Projection::operator String() const { + return "[ X: " + matrix[0].operator String() + + ", Y: " + matrix[1].operator String() + + ", Z: " + matrix[2].operator String() + + ", W: " + matrix[3].operator String() + " ]"; +} + +real_t Projection::get_aspect() const { + Vector2 vp_he = get_viewport_half_extents(); + return vp_he.x / vp_he.y; +} + +int Projection::get_pixels_per_meter(int p_for_pixel_width) const { + Vector3 result = xform(Vector3(1, 0, -1)); + + return int((result.x * 0.5 + 0.5) * p_for_pixel_width); +} + +bool Projection::is_orthogonal() const { + return matrix[3][3] == 1.0; +} + +real_t Projection::get_fov() const { + const real_t *matrix = (const real_t *)this->matrix; + + Plane right_plane = Plane(matrix[3] - matrix[0], + matrix[7] - matrix[4], + matrix[11] - matrix[8], + -matrix[15] + matrix[12]); + right_plane.normalize(); + + if ((matrix[8] == 0) && (matrix[9] == 0)) { + return Math::rad2deg(Math::acos(Math::abs(right_plane.normal.x))) * 2.0; + } else { + // our frustum is asymmetrical need to calculate the left planes angle separately.. + Plane left_plane = Plane(matrix[3] + matrix[0], + matrix[7] + matrix[4], + matrix[11] + matrix[8], + matrix[15] + matrix[12]); + left_plane.normalize(); + + return Math::rad2deg(Math::acos(Math::abs(left_plane.normal.x))) + Math::rad2deg(Math::acos(Math::abs(right_plane.normal.x))); + } +} + +float Projection::get_lod_multiplier() const { + if (is_orthogonal()) { + return get_viewport_half_extents().x; + } else { + float zn = get_z_near(); + float width = get_viewport_half_extents().x * 2.0; + return 1.0 / (zn / width); + } + + //usage is lod_size / (lod_distance * multiplier) < threshold +} +void Projection::make_scale(const Vector3 &p_scale) { + set_identity(); + matrix[0][0] = p_scale.x; + matrix[1][1] = p_scale.y; + matrix[2][2] = p_scale.z; +} + +void Projection::scale_translate_to_fit(const AABB &p_aabb) { + Vector3 min = p_aabb.position; + Vector3 max = p_aabb.position + p_aabb.size; + + matrix[0][0] = 2 / (max.x - min.x); + matrix[1][0] = 0; + matrix[2][0] = 0; + matrix[3][0] = -(max.x + min.x) / (max.x - min.x); + + matrix[0][1] = 0; + matrix[1][1] = 2 / (max.y - min.y); + matrix[2][1] = 0; + matrix[3][1] = -(max.y + min.y) / (max.y - min.y); + + matrix[0][2] = 0; + matrix[1][2] = 0; + matrix[2][2] = 2 / (max.z - min.z); + matrix[3][2] = -(max.z + min.z) / (max.z - min.z); + + matrix[0][3] = 0; + matrix[1][3] = 0; + matrix[2][3] = 0; + matrix[3][3] = 1; +} + +void Projection::add_jitter_offset(const Vector2 &p_offset) { + matrix[3][0] += p_offset.x; + matrix[3][1] += p_offset.y; +} + +Projection::operator Transform() const { + Transform tr; + const real_t *m = &matrix[0][0]; + + tr.basis.rows[0][0] = m[0]; + tr.basis.rows[1][0] = m[1]; + tr.basis.rows[2][0] = m[2]; + + tr.basis.rows[0][1] = m[4]; + tr.basis.rows[1][1] = m[5]; + tr.basis.rows[2][1] = m[6]; + + tr.basis.rows[0][2] = m[8]; + tr.basis.rows[1][2] = m[9]; + tr.basis.rows[2][2] = m[10]; + + tr.origin.x = m[12]; + tr.origin.y = m[13]; + tr.origin.z = m[14]; + + return tr; +} + +void Projection::set_frustum2(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) { + set_frustum(p_size, p_aspect, p_offset, p_near, p_far, p_flip_fov); +} + +Projection::Projection(const Vector4 &p_x, const Vector4 &p_y, const Vector4 &p_z, const Vector4 &p_w) { + matrix[0] = p_x; + matrix[1] = p_y; + matrix[2] = p_z; + matrix[3] = p_w; +} +Projection::Projection(const Transform &p_transform) { + const Transform &tr = p_transform; + real_t *m = &matrix[0][0]; + + m[0] = tr.basis.rows[0][0]; + m[1] = tr.basis.rows[1][0]; + m[2] = tr.basis.rows[2][0]; + m[3] = 0.0; + m[4] = tr.basis.rows[0][1]; + m[5] = tr.basis.rows[1][1]; + m[6] = tr.basis.rows[2][1]; + m[7] = 0.0; + m[8] = tr.basis.rows[0][2]; + m[9] = tr.basis.rows[1][2]; + m[10] = tr.basis.rows[2][2]; + m[11] = 0.0; + m[12] = tr.origin.x; + m[13] = tr.origin.y; + m[14] = tr.origin.z; + m[15] = 1.0; +} + +Projection::~Projection() { +} +#line 0 + +#line 1 "sfwl/core/vector3.cpp" +/*************************************************************************/ +/* vector3.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +void Vector3::rotate(const Vector3 &p_axis, real_t p_phi) { + *this = Basis(p_axis, p_phi).xform(*this); +} + +Vector3 Vector3::rotated(const Vector3 &p_axis, real_t p_phi) const { + Vector3 r = *this; + r.rotate(p_axis, p_phi); + return r; +} + +void Vector3::set_axis(int p_axis, real_t p_value) { + ERR_FAIL_INDEX(p_axis, 3); + coord[p_axis] = p_value; +} +real_t Vector3::get_axis(int p_axis) const { + ERR_FAIL_INDEX_V(p_axis, 3, 0); + return operator[](p_axis); +} + +void Vector3::snap(const Vector3 &p_val) { + x = Math::stepify(x, p_val.x); + y = Math::stepify(y, p_val.y); + z = Math::stepify(z, p_val.z); +} +Vector3 Vector3::snapped(const Vector3 &p_val) const { + Vector3 v = *this; + v.snap(p_val); + return v; +} + +Vector3 Vector3::limit_length(const real_t p_len) const { + const real_t l = length(); + Vector3 v = *this; + if (l > 0 && p_len < l) { + v /= l; + v *= p_len; + } + + return v; +} + +Vector3 Vector3::move_toward(const Vector3 &p_to, const real_t p_delta) const { + Vector3 v = *this; + Vector3 vd = p_to - v; + real_t len = vd.length(); + return len <= p_delta || len < (real_t)CMP_EPSILON ? p_to : v + vd / len * p_delta; +} + +Basis Vector3::outer(const Vector3 &p_b) const { + Vector3 row0(x * p_b.x, x * p_b.y, x * p_b.z); + Vector3 row1(y * p_b.x, y * p_b.y, y * p_b.z); + Vector3 row2(z * p_b.x, z * p_b.y, z * p_b.z); + + return Basis(row0, row1, row2); +} + +Basis Vector3::to_diagonal_matrix() const { + return Basis(x, 0, 0, + 0, y, 0, + 0, 0, z); +} + +Vector3 Vector3::clamp(const Vector3 &p_min, const Vector3 &p_max) const { + return Vector3( + CLAMP(x, p_min.x, p_max.x), + CLAMP(y, p_min.y, p_max.y), + CLAMP(z, p_min.z, p_max.z)); +} + +bool Vector3::is_equal_approx(const Vector3 &p_v) const { + return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z); +} + +Vector3::operator String() const { + return "(" + String::num_real(x) + ", " + String::num_real(y) + ", " + String::num_real(z) + ")"; +} +#line 0 + +#line 1 "sfwl/core/pcg.cpp" +// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org +// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website) + + + +uint32_t pcg32_random_r(pcg32_random_t* rng) +{ + uint64_t oldstate = rng->state; + // Advance internal state + rng->state = oldstate * 6364136223846793005ULL + (rng->inc|1); + // Calculate output function (XSH RR), uses old state for max ILP + uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u; + uint32_t rot = oldstate >> 59u; + return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); +} + +// Source from http://www.pcg-random.org/downloads/pcg-c-basic-0.9.zip +void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq) +{ + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg32_random_r(rng); + rng->state += initstate; + pcg32_random_r(rng); +} + +// Source from https://github.com/imneme/pcg-c-basic/blob/master/pcg_basic.c +// pcg32_boundedrand_r(rng, bound): +// Generate a uniformly distributed number, r, where 0 <= r < bound +uint32_t pcg32_boundedrand_r(pcg32_random_t *rng, uint32_t bound) { + // To avoid bias, we need to make the range of the RNG a multiple of + // bound, which we do by dropping output less than a threshold. + // A naive scheme to calculate the threshold would be to do + // + // uint32_t threshold = 0x100000000ull % bound; + // + // but 64-bit div/mod is slower than 32-bit div/mod (especially on + // 32-bit platforms). In essence, we do + // + // uint32_t threshold = (0x100000000ull-bound) % bound; + // + // because this version will calculate the same modulus, but the LHS + // value is less than 2^32. + uint32_t threshold = -bound % bound; + + // Uniformity guarantees that this loop will terminate. In practice, it + // should usually terminate quickly; on average (assuming all bounds are + // equally likely), 82.25% of the time, we can expect it to require just + // one iteration. In the worst case, someone passes a bound of 2^31 + 1 + // (i.e., 2147483649), which invalidates almost 50% of the range. In + // practice, bounds are typically small and only a tiny amount of the range + // is eliminated. + for (;;) { + uint32_t r = pcg32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#line 0 + +#line 1 "sfwl/core/vector2.cpp" +/*************************************************************************/ +/* vector2.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +real_t Vector2::angle() const { + return Math::atan2(y, x); +} + +real_t Vector2::length() const { + return Math::sqrt(x * x + y * y); +} + +real_t Vector2::length_squared() const { + return x * x + y * y; +} + +void Vector2::normalize() { + real_t l = x * x + y * y; + if (l != 0) { + l = Math::sqrt(l); + x /= l; + y /= l; + } +} + +Vector2 Vector2::normalized() const { + Vector2 v = *this; + v.normalize(); + return v; +} + +bool Vector2::is_normalized() const { + // use length_squared() instead of length() to avoid sqrt(), makes it more stringent. + return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON); +} + +real_t Vector2::distance_to(const Vector2 &p_vector2) const { + return Math::sqrt((x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y)); +} + +real_t Vector2::distance_squared_to(const Vector2 &p_vector2) const { + return (x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y); +} + +real_t Vector2::angle_to(const Vector2 &p_vector2) const { + return Math::atan2(cross(p_vector2), dot(p_vector2)); +} + +real_t Vector2::angle_to_point(const Vector2 &p_vector2) const { + return Math::atan2(y - p_vector2.y, x - p_vector2.x); +} + +real_t Vector2::dot(const Vector2 &p_other) const { + return x * p_other.x + y * p_other.y; +} + +real_t Vector2::cross(const Vector2 &p_other) const { + return x * p_other.y - y * p_other.x; +} + +Vector2 Vector2::sign() const { + return Vector2(SGN(x), SGN(y)); +} + +Vector2 Vector2::floor() const { + return Vector2(Math::floor(x), Math::floor(y)); +} + +Vector2 Vector2::ceil() const { + return Vector2(Math::ceil(x), Math::ceil(y)); +} + +Vector2 Vector2::round() const { + return Vector2(Math::round(x), Math::round(y)); +} + +Vector2 Vector2::rotated(real_t p_by) const { + Vector2 v; + v.set_rotation(angle() + p_by); + v *= length(); + return v; +} + +Vector2 Vector2::posmod(const real_t p_mod) const { + return Vector2(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod)); +} + +Vector2 Vector2::posmodv(const Vector2 &p_modv) const { + return Vector2(Math::fposmod(x, p_modv.x), Math::fposmod(y, p_modv.y)); +} + +Vector2 Vector2::project(const Vector2 &p_to) const { + return p_to * (dot(p_to) / p_to.length_squared()); +} + +Vector2 Vector2::snapped(const Vector2 &p_by) const { + return Vector2( + Math::stepify(x, p_by.x), + Math::stepify(y, p_by.y)); +} + +Vector2 Vector2::limit_length(const real_t p_len) const { + const real_t l = length(); + Vector2 v = *this; + if (l > 0 && p_len < l) { + v /= l; + v *= p_len; + } + + return v; +} + +Vector2 Vector2::move_toward(const Vector2 &p_to, const real_t p_delta) const { + Vector2 v = *this; + Vector2 vd = p_to - v; + real_t len = vd.length(); + return len <= p_delta || len < (real_t)CMP_EPSILON ? p_to : v + vd / len * p_delta; +} + +// slide returns the component of the vector along the given plane, specified by its normal vector. +Vector2 Vector2::slide(const Vector2 &p_normal) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector2(), "The normal Vector2 must be normalized."); +#endif + return *this - p_normal * this->dot(p_normal); +} + +Vector2 Vector2::bounce(const Vector2 &p_normal) const { + return -reflect(p_normal); +} + +Vector2 Vector2::reflect(const Vector2 &p_normal) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector2(), "The normal Vector2 must be normalized."); +#endif + return 2 * p_normal * this->dot(p_normal) - *this; +} + +bool Vector2::is_equal_approx(const Vector2 &p_v) const { + return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y); +} + +Vector2::operator String() const { + return "(" + String::num_real(x) + ", " + String::num_real(y) + ")"; +} +#line 0 + +#line 1 "sfwl/core/basis.cpp" +/*************************************************************************/ +/* basis.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +#define cofac(row1, col1, row2, col2) \ + (rows[row1][col1] * rows[row2][col2] - rows[row1][col2] * rows[row2][col1]) + +void Basis::from_z(const Vector3 &p_z) { + if (Math::abs(p_z.z) > (real_t)Math_SQRT12) { + // choose p in y-z plane + real_t a = p_z[1] * p_z[1] + p_z[2] * p_z[2]; + real_t k = 1 / Math::sqrt(a); + rows[0] = Vector3(0, -p_z[2] * k, p_z[1] * k); + rows[1] = Vector3(a * k, -p_z[0] * rows[0][2], p_z[0] * rows[0][1]); + } else { + // choose p in x-y plane + real_t a = p_z.x * p_z.x + p_z.y * p_z.y; + real_t k = 1 / Math::sqrt(a); + rows[0] = Vector3(-p_z.y * k, p_z.x * k, 0); + rows[1] = Vector3(-p_z.z * rows[0].y, p_z.z * rows[0].x, a * k); + } + rows[2] = p_z; +} + +void Basis::invert() { + real_t co[3] = { + cofac(1, 1, 2, 2), cofac(1, 2, 2, 0), cofac(1, 0, 2, 1) + }; + real_t det = rows[0][0] * co[0] + + rows[0][1] * co[1] + + rows[0][2] * co[2]; +#ifdef MATH_CHECKS + ERR_FAIL_COND(det == 0); +#endif + real_t s = 1 / det; + + set(co[0] * s, cofac(0, 2, 2, 1) * s, cofac(0, 1, 1, 2) * s, + co[1] * s, cofac(0, 0, 2, 2) * s, cofac(0, 2, 1, 0) * s, + co[2] * s, cofac(0, 1, 2, 0) * s, cofac(0, 0, 1, 1) * s); +} + +void Basis::orthonormalize() { + // Gram-Schmidt Process + + Vector3 x = get_column(0); + Vector3 y = get_column(1); + Vector3 z = get_column(2); + + x.normalize(); + y = (y - x * (x.dot(y))); + y.normalize(); + z = (z - x * (x.dot(z)) - y * (y.dot(z))); + z.normalize(); + + set_column(0, x); + set_column(1, y); + set_column(2, z); +} + +Basis Basis::orthonormalized() const { + Basis c = *this; + c.orthonormalize(); + return c; +} + +bool Basis::is_orthogonal() const { + Basis identity; + Basis m = (*this) * transposed(); + + return m.is_equal_approx(identity); +} + +void Basis::orthogonalize() { + Vector3 scl = get_scale(); + orthonormalize(); + scale_local(scl); +} + +Basis Basis::orthogonalized() const { + Basis c = *this; + c.orthogonalize(); + return c; +} + +bool Basis::is_diagonal() const { + return ( + Math::is_zero_approx(rows[0][1]) && Math::is_zero_approx(rows[0][2]) && + Math::is_zero_approx(rows[1][0]) && Math::is_zero_approx(rows[1][2]) && + Math::is_zero_approx(rows[2][0]) && Math::is_zero_approx(rows[2][1])); +} + +bool Basis::is_rotation() const { + return Math::is_equal_approx(determinant(), 1, (real_t)UNIT_EPSILON) && is_orthogonal(); +} + +bool Basis::is_symmetric() const { + if (!Math::is_equal_approx_ratio(rows[0][1], rows[1][0], (real_t)UNIT_EPSILON)) { + return false; + } + if (!Math::is_equal_approx_ratio(rows[0][2], rows[2][0], (real_t)UNIT_EPSILON)) { + return false; + } + if (!Math::is_equal_approx_ratio(rows[1][2], rows[2][1], (real_t)UNIT_EPSILON)) { + return false; + } + + return true; +} + +Basis Basis::diagonalize() { +//NOTE: only implemented for symmetric matrices +//with the Jacobi iterative method method +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(!is_symmetric(), Basis()); +#endif + const int ite_max = 1024; + + real_t off_matrix_norm_2 = rows[0][1] * rows[0][1] + rows[0][2] * rows[0][2] + rows[1][2] * rows[1][2]; + + int ite = 0; + Basis acc_rot; + while (off_matrix_norm_2 > (real_t)CMP_EPSILON2 && ite++ < ite_max) { + real_t el01_2 = rows[0][1] * rows[0][1]; + real_t el02_2 = rows[0][2] * rows[0][2]; + real_t el12_2 = rows[1][2] * rows[1][2]; + // Find the pivot element + int i, j; + if (el01_2 > el02_2) { + if (el12_2 > el01_2) { + i = 1; + j = 2; + } else { + i = 0; + j = 1; + } + } else { + if (el12_2 > el02_2) { + i = 1; + j = 2; + } else { + i = 0; + j = 2; + } + } + + // Compute the rotation angle + real_t angle; + if (Math::is_equal_approx(rows[j][j], rows[i][i])) { + angle = Math_PI / 4; + } else { + angle = 0.5f * Math::atan(2 * rows[i][j] / (rows[j][j] - rows[i][i])); + } + + // Compute the rotation matrix + Basis rot; + rot.rows[i][i] = rot.rows[j][j] = Math::cos(angle); + rot.rows[i][j] = -(rot.rows[j][i] = Math::sin(angle)); + + // Update the off matrix norm + off_matrix_norm_2 -= rows[i][j] * rows[i][j]; + + // Apply the rotation + *this = rot * *this * rot.transposed(); + acc_rot = rot * acc_rot; + } + + return acc_rot; +} + +Basis Basis::inverse() const { + Basis inv = *this; + inv.invert(); + return inv; +} + +void Basis::transpose() { + SWAP(rows[0][1], rows[1][0]); + SWAP(rows[0][2], rows[2][0]); + SWAP(rows[1][2], rows[2][1]); +} + +Basis Basis::transposed() const { + Basis tr = *this; + tr.transpose(); + return tr; +} + +Basis Basis::from_scale(const Vector3 &p_scale) { + return Basis(p_scale.x, 0, 0, 0, p_scale.y, 0, 0, 0, p_scale.z); +} + +// Multiplies the matrix from left by the scaling matrix: M -> S.M +// See the comment for Basis::rotated for further explanation. +void Basis::scale(const Vector3 &p_scale) { + rows[0][0] *= p_scale.x; + rows[0][1] *= p_scale.x; + rows[0][2] *= p_scale.x; + rows[1][0] *= p_scale.y; + rows[1][1] *= p_scale.y; + rows[1][2] *= p_scale.y; + rows[2][0] *= p_scale.z; + rows[2][1] *= p_scale.z; + rows[2][2] *= p_scale.z; +} + +Basis Basis::scaled(const Vector3 &p_scale) const { + Basis m = *this; + m.scale(p_scale); + return m; +} + +void Basis::scale_local(const Vector3 &p_scale) { + // performs a scaling in object-local coordinate system: + // M -> (M.S.Minv).M = M.S. + *this = scaled_local(p_scale); +} + +Basis Basis::scaled_local(const Vector3 &p_scale) const { + Basis b; + b.set_diagonal(p_scale); + + return (*this) * b; +} + +void Basis::scale_orthogonal(const Vector3 &p_scale) { + *this = scaled_orthogonal(p_scale); +} + +Basis Basis::scaled_orthogonal(const Vector3 &p_scale) const { + Basis m = *this; + Vector3 s = Vector3(-1, -1, -1) + p_scale; + Vector3 dots; + Basis b; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + dots[j] += s[i] * Math::abs(m.get_column(i).normalized().dot(b.get_column(j))); + } + } + m.scale_local(Vector3(1, 1, 1) + dots); + return m; +} + +real_t Basis::get_uniform_scale() const { + return (rows[0].length() + rows[1].length() + rows[2].length()) / 3.0f; +} + +void Basis::make_scale_uniform() { + float l = (rows[0].length() + rows[1].length() + rows[2].length()) / 3.0f; + for (int i = 0; i < 3; i++) { + rows[i].normalize(); + rows[i] *= l; + } +} + +Vector3 Basis::get_scale_abs() const { + return Vector3( + Vector3(rows[0][0], rows[1][0], rows[2][0]).length(), + Vector3(rows[0][1], rows[1][1], rows[2][1]).length(), + Vector3(rows[0][2], rows[1][2], rows[2][2]).length()); +} + +Vector3 Basis::get_scale_local() const { + real_t det_sign = SGN(determinant()); + return det_sign * Vector3(rows[0].length(), rows[1].length(), rows[2].length()); +} + +// get_scale works with get_rotation, use get_scale_abs if you need to enforce positive signature. +Vector3 Basis::get_scale() const { + // FIXME: We are assuming M = R.S (R is rotation and S is scaling), and use polar decomposition to extract R and S. + // A polar decomposition is M = O.P, where O is an orthogonal matrix (meaning rotation and reflection) and + // P is a positive semi-definite matrix (meaning it contains absolute values of scaling along its diagonal). + // + // Despite being different from what we want to achieve, we can nevertheless make use of polar decomposition + // here as follows. We can split O into a rotation and a reflection as O = R.Q, and obtain M = R.S where + // we defined S = Q.P. Now, R is a proper rotation matrix and S is a (signed) scaling matrix, + // which can involve negative scalings. However, there is a catch: unlike the polar decomposition of M = O.P, + // the decomposition of O into a rotation and reflection matrix as O = R.Q is not unique. + // Therefore, we are going to do this decomposition by sticking to a particular convention. + // This may lead to confusion for some users though. + // + // The convention we use here is to absorb the sign flip into the scaling matrix. + // The same convention is also used in other similar functions such as get_rotation_axis_angle, get_rotation, ... + // + // A proper way to get rid of this issue would be to store the scaling values (or at least their signs) + // as a part of Basis. However, if we go that path, we need to disable direct (write) access to the + // matrix rows. + // + // The rotation part of this decomposition is returned by get_rotation* functions. + real_t det_sign = SGN(determinant()); + return det_sign * get_scale_abs(); +} + +// Decomposes a Basis into a rotation-reflection matrix (an element of the group O(3)) and a positive scaling matrix as B = O.S. +// Returns the rotation-reflection matrix via reference argument, and scaling information is returned as a Vector3. +// This (internal) function is too specific and named too ugly to expose to users, and probably there's no need to do so. +Vector3 Basis::rotref_posscale_decomposition(Basis &rotref) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(determinant() == 0, Vector3()); + + Basis m = transposed() * (*this); + ERR_FAIL_COND_V(!m.is_diagonal(), Vector3()); +#endif + Vector3 scale = get_scale(); + Basis inv_scale = Basis().scaled(scale.inverse()); // this will also absorb the sign of scale + rotref = (*this) * inv_scale; + +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(!rotref.is_orthogonal(), Vector3()); +#endif + return scale.abs(); +} + +// Multiplies the matrix from left by the rotation matrix: M -> R.M +// Note that this does *not* rotate the matrix itself. +// +// The main use of Basis is as Transform.basis, which is used a the transformation matrix +// of 3D object. Rotate here refers to rotation of the object (which is R * (*this)), +// not the matrix itself (which is R * (*this) * R.transposed()). +Basis Basis::rotated(const Vector3 &p_axis, real_t p_phi) const { + return Basis(p_axis, p_phi) * (*this); +} + +void Basis::rotate(const Vector3 &p_axis, real_t p_phi) { + *this = rotated(p_axis, p_phi); +} + +void Basis::rotate_local(const Vector3 &p_axis, real_t p_phi) { + // performs a rotation in object-local coordinate system: + // M -> (M.R.Minv).M = M.R. + *this = rotated_local(p_axis, p_phi); +} +Basis Basis::rotated_local(const Vector3 &p_axis, real_t p_phi) const { + return (*this) * Basis(p_axis, p_phi); +} + +Basis Basis::rotated(const Vector3 &p_euler) const { + return Basis(p_euler) * (*this); +} + +void Basis::rotate(const Vector3 &p_euler) { + *this = rotated(p_euler); +} + +Basis Basis::rotated(const Quaternion &p_quat) const { + return Basis(p_quat) * (*this); +} + +void Basis::rotate(const Quaternion &p_quat) { + *this = rotated(p_quat); +} + +Vector3 Basis::get_rotation_euler() const { + // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S, + // and returns the Euler angles corresponding to the rotation part, complementing get_scale(). + // See the comment in get_scale() for further information. + Basis m = orthonormalized(); + real_t det = m.determinant(); + if (det < 0) { + // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles. + m.scale(Vector3(-1, -1, -1)); + } + + return m.get_euler(); +} + +Quaternion Basis::get_rotation_quaternion() const { + // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S, + // and returns the Euler angles corresponding to the rotation part, complementing get_scale(). + // See the comment in get_scale() for further information. + Basis m = orthonormalized(); + real_t det = m.determinant(); + if (det < 0) { + // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles. + m.scale(Vector3(-1, -1, -1)); + } + + return m.get_quaternion(); +} + +void Basis::rotate_to_align(const Vector3 &p_start_direction, const Vector3 &p_end_direction) { + // Takes two vectors and rotates the basis from the first vector to the second vector. + // Adopted from: https://gist.github.com/kevinmoran/b45980723e53edeb8a5a43c49f134724 + const Vector3 axis = p_start_direction.cross(p_end_direction).normalized(); + if (axis.length_squared() != 0) { + real_t dot = p_start_direction.dot(p_end_direction); + dot = CLAMP(dot, -1.0, 1.0); + const real_t angle_rads = Math::acos(dot); + *this = Basis(axis, angle_rads) * (*this); + } +} + +void Basis::get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const { + // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S, + // and returns the Euler angles corresponding to the rotation part, complementing get_scale(). + // See the comment in get_scale() for further information. + Basis m = orthonormalized(); + real_t det = m.determinant(); + if (det < 0) { + // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles. + m.scale(Vector3(-1, -1, -1)); + } + + m.get_axis_angle(p_axis, p_angle); +} + +void Basis::get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const { + // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S, + // and returns the Euler angles corresponding to the rotation part, complementing get_scale(). + // See the comment in get_scale() for further information. + Basis m = transposed(); + m.orthonormalize(); + real_t det = m.determinant(); + if (det < 0) { + // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles. + m.scale(Vector3(-1, -1, -1)); + } + + m.get_axis_angle(p_axis, p_angle); + p_angle = -p_angle; +} + +// get_euler_xyz returns a vector containing the Euler angles in the format +// (a1,a2,a3), where a3 is the angle of the first rotation, and a1 is the last +// (following the convention they are commonly defined in the literature). +// +// The current implementation uses XYZ convention (Z is the first rotation), +// so euler.z is the angle of the (first) rotation around Z axis and so on, +// +// And thus, assuming the matrix is a rotation matrix, this function returns +// the angles in the decomposition R = X(a1).Y(a2).Z(a3) where Z(a) rotates +// around the z-axis by a and so on. +Vector3 Basis::get_euler_xyz() const { + // Euler angles in XYZ convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cy*cz -cy*sz sy + // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx + // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy + + Vector3 euler; + real_t sy = rows[0][2]; + if (sy < (1 - (real_t)CMP_EPSILON)) { + if (sy > -(1 - (real_t)CMP_EPSILON)) { + // is this a pure Y rotation? + if (rows[1][0] == 0 && rows[0][1] == 0 && rows[1][2] == 0 && rows[2][1] == 0 && rows[1][1] == 1) { + // return the simplest form (human friendlier in editor and scripts) + euler.x = 0; + euler.y = atan2(rows[0][2], rows[0][0]); + euler.z = 0; + } else { + euler.x = Math::atan2(-rows[1][2], rows[2][2]); + euler.y = Math::asin(sy); + euler.z = Math::atan2(-rows[0][1], rows[0][0]); + } + } else { + euler.x = Math::atan2(rows[2][1], rows[1][1]); + euler.y = -Math_PI / 2.0; + euler.z = 0.0; + } + } else { + euler.x = Math::atan2(rows[2][1], rows[1][1]); + euler.y = Math_PI / 2.0; + euler.z = 0.0; + } + return euler; +} + +// set_euler_xyz expects a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// The current implementation uses XYZ convention (Z is the first rotation). +void Basis::set_euler_xyz(const Vector3 &p_euler) { + real_t c, s; + + c = Math::cos(p_euler.x); + s = Math::sin(p_euler.x); + Basis xmat(1, 0, 0, 0, c, -s, 0, s, c); + + c = Math::cos(p_euler.y); + s = Math::sin(p_euler.y); + Basis ymat(c, 0, s, 0, 1, 0, -s, 0, c); + + c = Math::cos(p_euler.z); + s = Math::sin(p_euler.z); + Basis zmat(c, -s, 0, s, c, 0, 0, 0, 1); + + //optimizer will optimize away all this anyway + *this = xmat * (ymat * zmat); +} + +Vector3 Basis::get_euler_xzy() const { + // Euler angles in XZY convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cz*cy -sz cz*sy + // sx*sy+cx*cy*sz cx*cz cx*sz*sy-cy*sx + // cy*sx*sz cz*sx cx*cy+sx*sz*sy + + Vector3 euler; + real_t sz = rows[0][1]; + if (sz < (1 - (real_t)CMP_EPSILON)) { + if (sz > -(1 - (real_t)CMP_EPSILON)) { + euler.x = Math::atan2(rows[2][1], rows[1][1]); + euler.y = Math::atan2(rows[0][2], rows[0][0]); + euler.z = Math::asin(-sz); + } else { + // It's -1 + euler.x = -Math::atan2(rows[1][2], rows[2][2]); + euler.y = 0.0; + euler.z = Math_PI / 2.0; + } + } else { + // It's 1 + euler.x = -Math::atan2(rows[1][2], rows[2][2]); + euler.y = 0.0; + euler.z = -Math_PI / 2.0; + } + return euler; +} + +void Basis::set_euler_xzy(const Vector3 &p_euler) { + real_t c, s; + + c = Math::cos(p_euler.x); + s = Math::sin(p_euler.x); + Basis xmat(1, 0, 0, 0, c, -s, 0, s, c); + + c = Math::cos(p_euler.y); + s = Math::sin(p_euler.y); + Basis ymat(c, 0, s, 0, 1, 0, -s, 0, c); + + c = Math::cos(p_euler.z); + s = Math::sin(p_euler.z); + Basis zmat(c, -s, 0, s, c, 0, 0, 0, 1); + + *this = xmat * zmat * ymat; +} + +Vector3 Basis::get_euler_yzx() const { + // Euler angles in YZX convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cy*cz sy*sx-cy*cx*sz cx*sy+cy*sz*sx + // sz cz*cx -cz*sx + // -cz*sy cy*sx+cx*sy*sz cy*cx-sy*sz*sx + + Vector3 euler; + real_t sz = rows[1][0]; + if (sz < (1 - (real_t)CMP_EPSILON)) { + if (sz > -(1 - (real_t)CMP_EPSILON)) { + euler.x = Math::atan2(-rows[1][2], rows[1][1]); + euler.y = Math::atan2(-rows[2][0], rows[0][0]); + euler.z = Math::asin(sz); + } else { + // It's -1 + euler.x = Math::atan2(rows[2][1], rows[2][2]); + euler.y = 0.0; + euler.z = -Math_PI / 2.0; + } + } else { + // It's 1 + euler.x = Math::atan2(rows[2][1], rows[2][2]); + euler.y = 0.0; + euler.z = Math_PI / 2.0; + } + return euler; +} + +void Basis::set_euler_yzx(const Vector3 &p_euler) { + real_t c, s; + + c = Math::cos(p_euler.x); + s = Math::sin(p_euler.x); + Basis xmat(1, 0, 0, 0, c, -s, 0, s, c); + + c = Math::cos(p_euler.y); + s = Math::sin(p_euler.y); + Basis ymat(c, 0, s, 0, 1, 0, -s, 0, c); + + c = Math::cos(p_euler.z); + s = Math::sin(p_euler.z); + Basis zmat(c, -s, 0, s, c, 0, 0, 0, 1); + + *this = ymat * zmat * xmat; +} + +// get_euler_yxz returns a vector containing the Euler angles in the YXZ convention, +// as in first-Z, then-X, last-Y. The angles for X, Y, and Z rotations are returned +// as the x, y, and z components of a Vector3 respectively. +Vector3 Basis::get_euler_yxz() const { + // Euler angles in YXZ convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cy*cz+sy*sx*sz cz*sy*sx-cy*sz cx*sy + // cx*sz cx*cz -sx + // cy*sx*sz-cz*sy cy*cz*sx+sy*sz cy*cx + + Vector3 euler; + + real_t m12 = rows[1][2]; + + if (m12 < (1 - (real_t)CMP_EPSILON)) { + if (m12 > -(1 - (real_t)CMP_EPSILON)) { + // is this a pure X rotation? + if (rows[1][0] == 0 && rows[0][1] == 0 && rows[0][2] == 0 && rows[2][0] == 0 && rows[0][0] == 1) { + // return the simplest form (human friendlier in editor and scripts) + euler.x = atan2(-m12, rows[1][1]); + euler.y = 0; + euler.z = 0; + } else { + euler.x = asin(-m12); + euler.y = atan2(rows[0][2], rows[2][2]); + euler.z = atan2(rows[1][0], rows[1][1]); + } + } else { // m12 == -1 + euler.x = Math_PI * 0.5; + euler.y = atan2(rows[0][1], rows[0][0]); + euler.z = 0; + } + } else { // m12 == 1 + euler.x = -Math_PI * 0.5; + euler.y = -atan2(rows[0][1], rows[0][0]); + euler.z = 0; + } + + return euler; +} + +// set_euler_yxz expects a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// The current implementation uses YXZ convention (Z is the first rotation). +void Basis::set_euler_yxz(const Vector3 &p_euler) { + real_t c, s; + + c = Math::cos(p_euler.x); + s = Math::sin(p_euler.x); + Basis xmat(1, 0, 0, 0, c, -s, 0, s, c); + + c = Math::cos(p_euler.y); + s = Math::sin(p_euler.y); + Basis ymat(c, 0, s, 0, 1, 0, -s, 0, c); + + c = Math::cos(p_euler.z); + s = Math::sin(p_euler.z); + Basis zmat(c, -s, 0, s, c, 0, 0, 0, 1); + + //optimizer will optimize away all this anyway + *this = ymat * xmat * zmat; +} + +Vector3 Basis::get_euler_zxy() const { + // Euler angles in ZXY convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cz*cy-sz*sx*sy -cx*sz cz*sy+cy*sz*sx + // cy*sz+cz*sx*sy cz*cx sz*sy-cz*cy*sx + // -cx*sy sx cx*cy + Vector3 euler; + real_t sx = rows[2][1]; + if (sx < (1 - (real_t)CMP_EPSILON)) { + if (sx > -(1 - (real_t)CMP_EPSILON)) { + euler.x = Math::asin(sx); + euler.y = Math::atan2(-rows[2][0], rows[2][2]); + euler.z = Math::atan2(-rows[0][1], rows[1][1]); + } else { + // It's -1 + euler.x = -Math_PI / 2.0; + euler.y = Math::atan2(rows[0][2], rows[0][0]); + euler.z = 0; + } + } else { + // It's 1 + euler.x = Math_PI / 2.0; + euler.y = Math::atan2(rows[0][2], rows[0][0]); + euler.z = 0; + } + return euler; +} + +void Basis::set_euler_zxy(const Vector3 &p_euler) { + real_t c, s; + + c = Math::cos(p_euler.x); + s = Math::sin(p_euler.x); + Basis xmat(1, 0, 0, 0, c, -s, 0, s, c); + + c = Math::cos(p_euler.y); + s = Math::sin(p_euler.y); + Basis ymat(c, 0, s, 0, 1, 0, -s, 0, c); + + c = Math::cos(p_euler.z); + s = Math::sin(p_euler.z); + Basis zmat(c, -s, 0, s, c, 0, 0, 0, 1); + + *this = zmat * xmat * ymat; +} + +Vector3 Basis::get_euler_zyx() const { + // Euler angles in ZYX convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cz*cy cz*sy*sx-cx*sz sz*sx+cz*cx*cy + // cy*sz cz*cx+sz*sy*sx cx*sz*sy-cz*sx + // -sy cy*sx cy*cx + Vector3 euler; + real_t sy = rows[2][0]; + if (sy < (1 - (real_t)CMP_EPSILON)) { + if (sy > -(1 - (real_t)CMP_EPSILON)) { + euler.x = Math::atan2(rows[2][1], rows[2][2]); + euler.y = Math::asin(-sy); + euler.z = Math::atan2(rows[1][0], rows[0][0]); + } else { + // It's -1 + euler.x = 0; + euler.y = Math_PI / 2.0; + euler.z = -Math::atan2(rows[0][1], rows[1][1]); + } + } else { + // It's 1 + euler.x = 0; + euler.y = -Math_PI / 2.0; + euler.z = -Math::atan2(rows[0][1], rows[1][1]); + } + return euler; +} + +void Basis::set_euler_zyx(const Vector3 &p_euler) { + real_t c, s; + + c = Math::cos(p_euler.x); + s = Math::sin(p_euler.x); + Basis xmat(1, 0, 0, 0, c, -s, 0, s, c); + + c = Math::cos(p_euler.y); + s = Math::sin(p_euler.y); + Basis ymat(c, 0, s, 0, 1, 0, -s, 0, c); + + c = Math::cos(p_euler.z); + s = Math::sin(p_euler.z); + Basis zmat(c, -s, 0, s, c, 0, 0, 0, 1); + + *this = zmat * ymat * xmat; +} + +bool Basis::is_equal_approx(const Basis &p_basis) const { + return rows[0].is_equal_approx(p_basis.rows[0]) && rows[1].is_equal_approx(p_basis.rows[1]) && rows[2].is_equal_approx(p_basis.rows[2]); +} + +bool Basis::is_equal_approx_ratio(const Basis &a, const Basis &b, real_t p_epsilon) const { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (!Math::is_equal_approx_ratio(a.rows[i][j], b.rows[i][j], p_epsilon)) { + return false; + } + } + } + + return true; +} + +bool Basis::operator==(const Basis &p_matrix) const { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (rows[i][j] != p_matrix.rows[i][j]) { + return false; + } + } + } + + return true; +} + +bool Basis::operator!=(const Basis &p_matrix) const { + return (!(*this == p_matrix)); +} + +Basis::operator String() const { + return "[X: " + get_axis(0).operator String() + + ", Y: " + get_axis(1).operator String() + + ", Z: " + get_axis(2).operator String() + "]"; +} + +Quaternion Basis::get_quaternion() const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!is_rotation(), Quaternion(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quaternion() or call orthonormalized() if the Basis contains linearly independent vectors."); +#endif + /* Allow getting a quaternion from an unnormalized transform */ + Basis m = *this; + real_t trace = m.rows[0][0] + m.rows[1][1] + m.rows[2][2]; + real_t temp[4]; + + if (trace > 0) { + real_t s = Math::sqrt(trace + 1); + temp[3] = (s * 0.5f); + s = 0.5f / s; + + temp[0] = ((m.rows[2][1] - m.rows[1][2]) * s); + temp[1] = ((m.rows[0][2] - m.rows[2][0]) * s); + temp[2] = ((m.rows[1][0] - m.rows[0][1]) * s); + } else { + int i = m.rows[0][0] < m.rows[1][1] + ? (m.rows[1][1] < m.rows[2][2] ? 2 : 1) + : (m.rows[0][0] < m.rows[2][2] ? 2 : 0); + int j = (i + 1) % 3; + int k = (i + 2) % 3; + + real_t s = Math::sqrt(m.rows[i][i] - m.rows[j][j] - m.rows[k][k] + 1); + temp[i] = s * 0.5f; + s = 0.5f / s; + + temp[3] = (m.rows[k][j] - m.rows[j][k]) * s; + temp[j] = (m.rows[j][i] + m.rows[i][j]) * s; + temp[k] = (m.rows[k][i] + m.rows[i][k]) * s; + } + + return Quaternion(temp[0], temp[1], temp[2], temp[3]); +} + +static const Basis _ortho_bases[24] = { + Basis(1, 0, 0, 0, 1, 0, 0, 0, 1), + Basis(0, -1, 0, 1, 0, 0, 0, 0, 1), + Basis(-1, 0, 0, 0, -1, 0, 0, 0, 1), + Basis(0, 1, 0, -1, 0, 0, 0, 0, 1), + Basis(1, 0, 0, 0, 0, -1, 0, 1, 0), + Basis(0, 0, 1, 1, 0, 0, 0, 1, 0), + Basis(-1, 0, 0, 0, 0, 1, 0, 1, 0), + Basis(0, 0, -1, -1, 0, 0, 0, 1, 0), + Basis(1, 0, 0, 0, -1, 0, 0, 0, -1), + Basis(0, 1, 0, 1, 0, 0, 0, 0, -1), + Basis(-1, 0, 0, 0, 1, 0, 0, 0, -1), + Basis(0, -1, 0, -1, 0, 0, 0, 0, -1), + Basis(1, 0, 0, 0, 0, 1, 0, -1, 0), + Basis(0, 0, -1, 1, 0, 0, 0, -1, 0), + Basis(-1, 0, 0, 0, 0, -1, 0, -1, 0), + Basis(0, 0, 1, -1, 0, 0, 0, -1, 0), + Basis(0, 0, 1, 0, 1, 0, -1, 0, 0), + Basis(0, -1, 0, 0, 0, 1, -1, 0, 0), + Basis(0, 0, -1, 0, -1, 0, -1, 0, 0), + Basis(0, 1, 0, 0, 0, -1, -1, 0, 0), + Basis(0, 0, 1, 0, -1, 0, 1, 0, 0), + Basis(0, 1, 0, 0, 0, 1, 1, 0, 0), + Basis(0, 0, -1, 0, 1, 0, 1, 0, 0), + Basis(0, -1, 0, 0, 0, -1, 1, 0, 0) +}; + +int Basis::get_orthogonal_index() const { + //could be sped up if i come up with a way + Basis orth = *this; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + real_t v = orth[i][j]; + if (v > 0.5f) { + v = 1; + } else if (v < -0.5f) { + v = -1; + } else { + v = 0; + } + + orth[i][j] = v; + } + } + + for (int i = 0; i < 24; i++) { + if (_ortho_bases[i] == orth) { + return i; + } + } + + return 0; +} + +void Basis::set_orthogonal_index(int p_index) { + //there only exist 24 orthogonal bases in r3 + ERR_FAIL_INDEX(p_index, 24); + + *this = _ortho_bases[p_index]; +} + +void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { + /* checking this is a bad idea, because obtaining from scaled transform is a valid use case +#ifdef MATH_CHECKS + ERR_FAIL_COND(!is_rotation()); +#endif + */ + + // https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm + real_t x, y, z; // Variables for result. + if (Math::is_zero_approx(rows[0][1] - rows[1][0]) && Math::is_zero_approx(rows[0][2] - rows[2][0]) && Math::is_zero_approx(rows[1][2] - rows[2][1])) { + // Singularity found. + // First check for identity matrix which must have +1 for all terms in leading diagonal and zero in other terms. + if (is_diagonal() && (Math::abs(rows[0][0] + rows[1][1] + rows[2][2] - 3) < 3 * CMP_EPSILON)) { + // This singularity is identity matrix so angle = 0. + + r_axis = Vector3(0, 1, 0); + r_angle = 0; + return; + } + + // Otherwise this singularity is angle = 180 + real_t xx = (rows[0][0] + 1) / 2; + real_t yy = (rows[1][1] + 1) / 2; + real_t zz = (rows[2][2] + 1) / 2; + real_t xy = (rows[0][1] + rows[1][0]) / 4; + real_t xz = (rows[0][2] + rows[2][0]) / 4; + real_t yz = (rows[1][2] + rows[2][1]) / 4; + + if ((xx > yy) && (xx > zz)) { // rows[0][0] is the largest diagonal term. + if (xx < CMP_EPSILON) { + x = 0; + y = Math_SQRT12; + z = Math_SQRT12; + } else { + x = Math::sqrt(xx); + y = xy / x; + z = xz / x; + } + } else if (yy > zz) { // rows[1][1] is the largest diagonal term. + if (yy < CMP_EPSILON) { + x = Math_SQRT12; + y = 0; + z = Math_SQRT12; + } else { + y = Math::sqrt(yy); + x = xy / y; + z = yz / y; + } + } else { // rows[2][2] is the largest diagonal term so base result on this. + if (zz < CMP_EPSILON) { + x = Math_SQRT12; + y = Math_SQRT12; + z = 0; + } else { + z = Math::sqrt(zz); + x = xz / z; + y = yz / z; + } + } + r_axis = Vector3(x, y, z); + r_angle = Math_PI; + return; + } + + // As we have reached here there are no singularities so we can handle normally + real_t s = Math::sqrt((rows[2][1] - rows[1][2]) * (rows[2][1] - rows[1][2]) + (rows[0][2] - rows[2][0]) * (rows[0][2] - rows[2][0]) + (rows[1][0] - rows[0][1]) * (rows[1][0] - rows[0][1])); // Used to normalise. + + if (Math::abs(s) < CMP_EPSILON) { + // Prevent divide by zero, should not happen if matrix is orthogonal and should be caught by singularity test above. + s = 1; + } + + x = (rows[2][1] - rows[1][2]) / s; + y = (rows[0][2] - rows[2][0]) / s; + z = (rows[1][0] - rows[0][1]) / s; + + r_axis = Vector3(x, y, z); + // acos does clamping. + r_angle = Math::acos((rows[0][0] + rows[1][1] + rows[2][2] - 1) / 2); +} + +void Basis::set_quaternion(const Quaternion &p_quat) { + real_t d = p_quat.length_squared(); + real_t s = 2 / d; + real_t xs = p_quat.x * s, ys = p_quat.y * s, zs = p_quat.z * s; + real_t wx = p_quat.w * xs, wy = p_quat.w * ys, wz = p_quat.w * zs; + real_t xx = p_quat.x * xs, xy = p_quat.x * ys, xz = p_quat.x * zs; + real_t yy = p_quat.y * ys, yz = p_quat.y * zs, zz = p_quat.z * zs; + set(1 - (yy + zz), xy - wz, xz + wy, + xy + wz, 1 - (xx + zz), yz - wx, + xz - wy, yz + wx, 1 - (xx + yy)); +} + +void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_phi) { +// Rotation matrix from axis and angle, see https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_angle +#ifdef MATH_CHECKS + ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 must be normalized."); +#endif + Vector3 axis_sq(p_axis.x * p_axis.x, p_axis.y * p_axis.y, p_axis.z * p_axis.z); + real_t cosine = Math::cos(p_phi); + rows[0][0] = axis_sq.x + cosine * (1 - axis_sq.x); + rows[1][1] = axis_sq.y + cosine * (1 - axis_sq.y); + rows[2][2] = axis_sq.z + cosine * (1 - axis_sq.z); + + real_t sine = Math::sin(p_phi); + real_t t = 1 - cosine; + + real_t xyzt = p_axis.x * p_axis.y * t; + real_t zyxs = p_axis.z * sine; + rows[0][1] = xyzt - zyxs; + rows[1][0] = xyzt + zyxs; + + xyzt = p_axis.x * p_axis.z * t; + zyxs = p_axis.y * sine; + rows[0][2] = xyzt + zyxs; + rows[2][0] = xyzt - zyxs; + + xyzt = p_axis.y * p_axis.z * t; + zyxs = p_axis.x * sine; + rows[1][2] = xyzt - zyxs; + rows[2][1] = xyzt + zyxs; +} + +void Basis::set_axis_angle_scale(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { + set_diagonal(p_scale); + rotate(p_axis, p_phi); +} + +void Basis::set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale) { + set_diagonal(p_scale); + rotate(p_euler); +} + +void Basis::set_quaternion_scale(const Quaternion &p_quat, const Vector3 &p_scale) { + set_diagonal(p_scale); + rotate(p_quat); +} + +void Basis::set_diagonal(const Vector3 &p_diag) { + rows[0][0] = p_diag.x; + rows[0][1] = 0; + rows[0][2] = 0; + + rows[1][0] = 0; + rows[1][1] = p_diag.y; + rows[1][2] = 0; + + rows[2][0] = 0; + rows[2][1] = 0; + rows[2][2] = p_diag.z; +} + +Basis Basis::slerp(const Basis &p_to, const real_t &p_weight) const { + //consider scale + Quaternion from(*this); + Quaternion to(p_to); + + Basis b(from.slerp(to, p_weight)); + b.rows[0] *= Math::lerp(rows[0].length(), p_to.rows[0].length(), p_weight); + b.rows[1] *= Math::lerp(rows[1].length(), p_to.rows[1].length(), p_weight); + b.rows[2] *= Math::lerp(rows[2].length(), p_to.rows[2].length(), p_weight); + + return b; +} + +void Basis::rotate_sh(real_t *p_values) { + // code by John Hable + // http://filmicworlds.com/blog/simple-and-fast-spherical-harmonic-rotation/ + // this code is Public Domain + + const static real_t s_c3 = 0.94617469575; // (3*sqrt(5))/(4*sqrt(pi)) + const static real_t s_c4 = -0.31539156525; // (-sqrt(5))/(4*sqrt(pi)) + const static real_t s_c5 = 0.54627421529; // (sqrt(15))/(4*sqrt(pi)) + + const static real_t s_c_scale = 1.0 / 0.91529123286551084; + const static real_t s_c_scale_inv = 0.91529123286551084; + + const static real_t s_rc2 = 1.5853309190550713 * s_c_scale; + const static real_t s_c4_div_c3 = s_c4 / s_c3; + const static real_t s_c4_div_c3_x2 = (s_c4 / s_c3) * 2.0; + + const static real_t s_scale_dst2 = s_c3 * s_c_scale_inv; + const static real_t s_scale_dst4 = s_c5 * s_c_scale_inv; + + const real_t src[9] = { p_values[0], p_values[1], p_values[2], p_values[3], p_values[4], p_values[5], p_values[6], p_values[7], p_values[8] }; + + real_t m00 = rows[0][0]; + real_t m01 = rows[0][1]; + real_t m02 = rows[0][2]; + real_t m10 = rows[1][0]; + real_t m11 = rows[1][1]; + real_t m12 = rows[1][2]; + real_t m20 = rows[2][0]; + real_t m21 = rows[2][1]; + real_t m22 = rows[2][2]; + + p_values[0] = src[0]; + p_values[1] = m11 * src[1] - m12 * src[2] + m10 * src[3]; + p_values[2] = -m21 * src[1] + m22 * src[2] - m20 * src[3]; + p_values[3] = m01 * src[1] - m02 * src[2] + m00 * src[3]; + + real_t sh0 = src[7] + src[8] + src[8] - src[5]; + real_t sh1 = src[4] + s_rc2 * src[6] + src[7] + src[8]; + real_t sh2 = src[4]; + real_t sh3 = -src[7]; + real_t sh4 = -src[5]; + + // Rotations. R0 and R1 just use the raw matrix columns + real_t r2x = m00 + m01; + real_t r2y = m10 + m11; + real_t r2z = m20 + m21; + + real_t r3x = m00 + m02; + real_t r3y = m10 + m12; + real_t r3z = m20 + m22; + + real_t r4x = m01 + m02; + real_t r4y = m11 + m12; + real_t r4z = m21 + m22; + + // dense matrix multiplication one column at a time + + // column 0 + real_t sh0_x = sh0 * m00; + real_t sh0_y = sh0 * m10; + real_t d0 = sh0_x * m10; + real_t d1 = sh0_y * m20; + real_t d2 = sh0 * (m20 * m20 + s_c4_div_c3); + real_t d3 = sh0_x * m20; + real_t d4 = sh0_x * m00 - sh0_y * m10; + + // column 1 + real_t sh1_x = sh1 * m02; + real_t sh1_y = sh1 * m12; + d0 += sh1_x * m12; + d1 += sh1_y * m22; + d2 += sh1 * (m22 * m22 + s_c4_div_c3); + d3 += sh1_x * m22; + d4 += sh1_x * m02 - sh1_y * m12; + + // column 2 + real_t sh2_x = sh2 * r2x; + real_t sh2_y = sh2 * r2y; + d0 += sh2_x * r2y; + d1 += sh2_y * r2z; + d2 += sh2 * (r2z * r2z + s_c4_div_c3_x2); + d3 += sh2_x * r2z; + d4 += sh2_x * r2x - sh2_y * r2y; + + // column 3 + real_t sh3_x = sh3 * r3x; + real_t sh3_y = sh3 * r3y; + d0 += sh3_x * r3y; + d1 += sh3_y * r3z; + d2 += sh3 * (r3z * r3z + s_c4_div_c3_x2); + d3 += sh3_x * r3z; + d4 += sh3_x * r3x - sh3_y * r3y; + + // column 4 + real_t sh4_x = sh4 * r4x; + real_t sh4_y = sh4 * r4y; + d0 += sh4_x * r4y; + d1 += sh4_y * r4z; + d2 += sh4 * (r4z * r4z + s_c4_div_c3_x2); + d3 += sh4_x * r4z; + d4 += sh4_x * r4x - sh4_y * r4y; + + // extra multipliers + p_values[4] = d0; + p_values[5] = -d1; + p_values[6] = d2 * s_scale_dst2; + p_values[7] = -d3; + p_values[8] = d4 * s_scale_dst4; +} + +Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up) { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(p_target.is_equal_approx(Vector3()), Basis(), "The target vector can't be zero."); + ERR_FAIL_COND_V_MSG(p_up.is_equal_approx(Vector3()), Basis(), "The up vector can't be zero."); +#endif + Vector3 v_z = -p_target.normalized(); + Vector3 v_x = p_up.cross(v_z); +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(v_x.is_equal_approx(Vector3()), Basis(), "The target vector and up vector can't be parallel to each other."); +#endif + v_x.normalize(); + Vector3 v_y = v_z.cross(v_x); + + Basis basis; + basis.set_columns(v_x, v_y, v_z); + return basis; +} + +#undef cofac +#line 0 + +#line 1 "sfwl/core/face3.cpp" +/*************************************************************************/ +/* face3.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +int Face3::split_by_plane(const Plane &p_plane, Face3 p_res[3], bool p_is_point_over[3]) const { + ERR_FAIL_COND_V(is_degenerate(), 0); + + Vector3 above[4]; + int above_count = 0; + + Vector3 below[4]; + int below_count = 0; + + for (int i = 0; i < 3; i++) { + if (p_plane.has_point(vertex[i], (real_t)CMP_EPSILON)) { // point is in plane + + ERR_FAIL_COND_V(above_count >= 4, 0); + above[above_count++] = vertex[i]; + ERR_FAIL_COND_V(below_count >= 4, 0); + below[below_count++] = vertex[i]; + + } else { + if (p_plane.is_point_over(vertex[i])) { + //Point is over + ERR_FAIL_COND_V(above_count >= 4, 0); + above[above_count++] = vertex[i]; + + } else { + //Point is under + ERR_FAIL_COND_V(below_count >= 4, 0); + below[below_count++] = vertex[i]; + } + + /* Check for Intersection between this and the next vertex*/ + + Vector3 inters; + if (!p_plane.intersects_segment(vertex[i], vertex[(i + 1) % 3], &inters)) { + continue; + } + + /* Intersection goes to both */ + ERR_FAIL_COND_V(above_count >= 4, 0); + above[above_count++] = inters; + ERR_FAIL_COND_V(below_count >= 4, 0); + below[below_count++] = inters; + } + } + + int polygons_created = 0; + + ERR_FAIL_COND_V(above_count >= 4 && below_count >= 4, 0); //bug in the algo + + if (above_count >= 3) { + p_res[polygons_created] = Face3(above[0], above[1], above[2]); + p_is_point_over[polygons_created] = true; + polygons_created++; + + if (above_count == 4) { + p_res[polygons_created] = Face3(above[2], above[3], above[0]); + p_is_point_over[polygons_created] = true; + polygons_created++; + } + } + + if (below_count >= 3) { + p_res[polygons_created] = Face3(below[0], below[1], below[2]); + p_is_point_over[polygons_created] = false; + polygons_created++; + + if (below_count == 4) { + p_res[polygons_created] = Face3(below[2], below[3], below[0]); + p_is_point_over[polygons_created] = false; + polygons_created++; + } + } + + return polygons_created; +} + +bool Face3::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection) const { + //return Geometry::ray_intersects_triangle(p_from, p_dir, vertex[0], vertex[1], vertex[2], p_intersection); + return false; +} + +bool Face3::intersects_segment(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection) const { + //return Geometry::segment_intersects_triangle(p_from, p_dir, vertex[0], vertex[1], vertex[2], p_intersection); + return false; +} + +bool Face3::is_degenerate() const { + Vector3 normal = vec3_cross(vertex[0] - vertex[1], vertex[0] - vertex[2]); + return (normal.length_squared() < (real_t)CMP_EPSILON2); +} + +Face3::Side Face3::get_side_of(const Face3 &p_face, ClockDirection p_clock_dir) const { + int over = 0, under = 0; + + Plane plane = get_plane(p_clock_dir); + + for (int i = 0; i < 3; i++) { + const Vector3 &v = p_face.vertex[i]; + + if (plane.has_point(v)) { //coplanar, don't bother + continue; + } + + if (plane.is_point_over(v)) { + over++; + } else { + under++; + } + } + + if (over > 0 && under == 0) { + return SIDE_OVER; + } else if (under > 0 && over == 0) { + return SIDE_UNDER; + } else if (under == 0 && over == 0) { + return SIDE_COPLANAR; + } else { + return SIDE_SPANNING; + } +} + +Vector3 Face3::get_random_point_inside() const { + real_t a = Math::random(0, 1); + real_t b = Math::random(0, 1); + if (a > b) { + SWAP(a, b); + } + + return vertex[0] * a + vertex[1] * (b - a) + vertex[2] * (1.0 - b); +} + +Plane Face3::get_plane(ClockDirection p_dir) const { + return Plane(vertex[0], vertex[1], vertex[2], p_dir); +} + +Vector3 Face3::get_median_point() const { + return (vertex[0] + vertex[1] + vertex[2]) / 3.0; +} + +real_t Face3::get_area() const { + return vec3_cross(vertex[0] - vertex[1], vertex[0] - vertex[2]).length() * 0.5; +} + +ClockDirection Face3::get_clock_dir() const { + Vector3 normal = vec3_cross(vertex[0] - vertex[1], vertex[0] - vertex[2]); + //printf("normal is %g,%g,%g x %g,%g,%g- wtfu is %g\n",tofloat(normal.x),tofloat(normal.y),tofloat(normal.z),tofloat(vertex[0].x),tofloat(vertex[0].y),tofloat(vertex[0].z),tofloat( normal.dot( vertex[0] ) ) ); + return (normal.dot(vertex[0]) >= 0) ? CLOCKWISE : COUNTERCLOCKWISE; +} + +bool Face3::intersects_aabb(const AABB &p_aabb) const { + /** TEST PLANE **/ + if (!p_aabb.intersects_plane(get_plane())) { + return false; + } + +#define TEST_AXIS(m_ax) \ + /** TEST FACE AXIS */ \ + { \ + real_t aabb_min = p_aabb.position.m_ax; \ + real_t aabb_max = p_aabb.position.m_ax + p_aabb.size.m_ax; \ + real_t tri_min = vertex[0].m_ax; \ + real_t tri_max = vertex[0].m_ax; \ + for (int i = 1; i < 3; i++) { \ + if (vertex[i].m_ax > tri_max) \ + tri_max = vertex[i].m_ax; \ + if (vertex[i].m_ax < tri_min) \ + tri_min = vertex[i].m_ax; \ + } \ + \ + if (tri_max < aabb_min || aabb_max < tri_min) \ + return false; \ + } + + TEST_AXIS(x); + TEST_AXIS(y); + TEST_AXIS(z); + + /** TEST ALL EDGES **/ + + Vector3 edge_norms[3] = { + vertex[0] - vertex[1], + vertex[1] - vertex[2], + vertex[2] - vertex[0], + }; + + for (int i = 0; i < 12; i++) { + Vector3 from, to; + p_aabb.get_edge(i, from, to); + Vector3 e1 = from - to; + for (int j = 0; j < 3; j++) { + Vector3 e2 = edge_norms[j]; + + Vector3 axis = vec3_cross(e1, e2); + + if (axis.length_squared() < 0.0001f) { + continue; // coplanar + } + axis.normalize(); + + real_t minA, maxA, minB, maxB; + p_aabb.project_range_in_plane(Plane(axis, 0), minA, maxA); + project_range(axis, Transform(), minB, maxB); + + if (maxA < minB || maxB < minA) { + return false; + } + } + } + return true; +} + +Face3::operator String() const { + return String() + vertex[0] + ", " + vertex[1] + ", " + vertex[2]; +} + +void Face3::project_range(const Vector3 &p_normal, const Transform &p_transform, real_t &r_min, real_t &r_max) const { + for (int i = 0; i < 3; i++) { + Vector3 v = p_transform.xform(vertex[i]); + real_t d = p_normal.dot(v); + + if (i == 0 || d > r_max) { + r_max = d; + } + + if (i == 0 || d < r_min) { + r_min = d; + } + } +} + +void Face3::get_support(const Vector3 &p_normal, const Transform &p_transform, Vector3 *p_vertices, int *p_count, int p_max) const { +#define _FACE_IS_VALID_SUPPORT_THRESHOLD 0.98 +#define _EDGE_IS_VALID_SUPPORT_THRESHOLD 0.05 + + if (p_max <= 0) { + return; + } + + Vector3 n = p_transform.basis.xform_inv(p_normal); + + /** TEST FACE AS SUPPORT **/ + if (get_plane().normal.dot(n) > (real_t)_FACE_IS_VALID_SUPPORT_THRESHOLD) { + *p_count = MIN(3, p_max); + + for (int i = 0; i < *p_count; i++) { + p_vertices[i] = p_transform.xform(vertex[i]); + } + + return; + } + + /** FIND SUPPORT VERTEX **/ + + int vert_support_idx = -1; + real_t support_max = 0; + + for (int i = 0; i < 3; i++) { + real_t d = n.dot(vertex[i]); + + if (i == 0 || d > support_max) { + support_max = d; + vert_support_idx = i; + } + } + + /** TEST EDGES AS SUPPORT **/ + + for (int i = 0; i < 3; i++) { + if (i != vert_support_idx && i + 1 != vert_support_idx) { + continue; + } + + // check if edge is valid as a support + real_t dot = (vertex[i] - vertex[(i + 1) % 3]).normalized().dot(n); + dot = ABS(dot); + if (dot < (real_t)_EDGE_IS_VALID_SUPPORT_THRESHOLD) { + *p_count = MIN(2, p_max); + + for (int j = 0; j < *p_count; j++) { + p_vertices[j] = p_transform.xform(vertex[(j + i) % 3]); + } + + return; + } + } + + *p_count = 1; + p_vertices[0] = p_transform.xform(vertex[vert_support_idx]); +} + +Vector3 Face3::get_closest_point_to(const Vector3 &p_point) const { + Vector3 edge0 = vertex[1] - vertex[0]; + Vector3 edge1 = vertex[2] - vertex[0]; + Vector3 v0 = vertex[0] - p_point; + + real_t a = edge0.dot(edge0); + real_t b = edge0.dot(edge1); + real_t c = edge1.dot(edge1); + real_t d = edge0.dot(v0); + real_t e = edge1.dot(v0); + + real_t det = a * c - b * b; + real_t s = b * e - c * d; + real_t t = b * d - a * e; + + if (s + t < det) { + if (s < 0.f) { + if (t < 0.f) { + if (d < 0.f) { + s = CLAMP(-d / a, 0.f, 1.f); + t = 0.f; + } else { + s = 0.f; + t = CLAMP(-e / c, 0.f, 1.f); + } + } else { + s = 0.f; + t = CLAMP(-e / c, 0.f, 1.f); + } + } else if (t < 0.f) { + s = CLAMP(-d / a, 0.f, 1.f); + t = 0.f; + } else { + real_t invDet = 1.f / det; + s *= invDet; + t *= invDet; + } + } else { + if (s < 0.f) { + real_t tmp0 = b + d; + real_t tmp1 = c + e; + if (tmp1 > tmp0) { + real_t numer = tmp1 - tmp0; + real_t denom = a - 2 * b + c; + s = CLAMP(numer / denom, 0.f, 1.f); + t = 1 - s; + } else { + t = CLAMP(-e / c, 0.f, 1.f); + s = 0.f; + } + } else if (t < 0.f) { + if (a + d > b + e) { + real_t numer = c + e - b - d; + real_t denom = a - 2 * b + c; + s = CLAMP(numer / denom, 0.f, 1.f); + t = 1 - s; + } else { + s = CLAMP(-d / a, 0.f, 1.f); + t = 0.f; + } + } else { + real_t numer = c + e - b - d; + real_t denom = a - 2 * b + c; + s = CLAMP(numer / denom, 0.f, 1.f); + t = 1.f - s; + } + } + + return vertex[0] + s * edge0 + t * edge1; +} +#line 0 + +#line 1 "sfwl/core/vector4i.cpp" +/*************************************************************************/ +/* vector4i.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +void Vector4i::set_axis(const int p_axis, const int32_t p_value) { + ERR_FAIL_INDEX(p_axis, 4); + coord[p_axis] = p_value; +} + +int32_t Vector4i::get_axis(const int p_axis) const { + ERR_FAIL_INDEX_V(p_axis, 4, 0); + return operator[](p_axis); +} + +Vector4i::Axis Vector4i::min_axis() const { + uint32_t min_index = 0; + int32_t min_value = x; + for (uint32_t i = 1; i < 4; i++) { + if (operator[](i) <= min_value) { + min_index = i; + min_value = operator[](i); + } + } + return Vector4i::Axis(min_index); +} + +Vector4i::Axis Vector4i::max_axis() const { + uint32_t max_index = 0; + int32_t max_value = x; + for (uint32_t i = 1; i < 4; i++) { + if (operator[](i) > max_value) { + max_index = i; + max_value = operator[](i); + } + } + return Vector4i::Axis(max_index); +} + +Vector4i Vector4i::clamp(const Vector4i &p_min, const Vector4i &p_max) const { + return Vector4i( + CLAMP(x, p_min.x, p_max.x), + CLAMP(y, p_min.y, p_max.y), + CLAMP(z, p_min.z, p_max.z), + CLAMP(w, p_min.w, p_max.w)); +} + +Vector4i Vector4i::linear_interpolate(const Vector4i &p_to, const real_t p_weight) const { + return Vector4i( + x + (p_weight * (p_to.x - x)), + y + (p_weight * (p_to.y - y)), + z + (p_weight * (p_to.z - z)), + w + (p_weight * (p_to.w - w))); +} + +Vector4 Vector4i::to_vector4() const { + return Vector4(x, y, z, w); +} + +Vector4i::operator String() const { + return "(" + itos(x) + ", " + itos(y) + ", " + itos(z) + ", " + itos(w) + ")"; +} + +Vector4i::operator Vector4() const { + return Vector4(x, y, z, w); +} + +/* +Vector4i::Vector4i(const Vector4 &p_vec4) { + x = p_vec4.x; + y = p_vec4.y; + z = p_vec4.z; + w = p_vec4.w; +} +*/ +#line 0 + +#line 1 "sfwl/core/transform.cpp" +/*************************************************************************/ +/* transform.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +void Transform::invert() { + basis.transpose(); + origin = basis.xform(-origin); +} + +Transform Transform::inverse() const { + // FIXME: this function assumes the basis is a rotation matrix, with no scaling. + // Transform::affine_inverse can handle matrices with scaling, so GDScript should eventually use that. + Transform ret = *this; + ret.invert(); + return ret; +} + +void Transform::affine_invert() { + basis.invert(); + origin = basis.xform(-origin); +} + +Transform Transform::affine_inverse() const { + Transform ret = *this; + ret.affine_invert(); + return ret; +} + +Transform Transform::rotated(const Vector3 &p_axis, real_t p_angle) const { + // Equivalent to left multiplication + Basis p_basis(p_axis, p_angle); + return Transform(p_basis * basis, p_basis.xform(origin)); +} + +Transform Transform::rotated_local(const Vector3 &p_axis, real_t p_angle) const { + // Equivalent to right multiplication + Basis p_basis(p_axis, p_angle); + return Transform(basis * p_basis, origin); +} + +void Transform::rotate(const Vector3 &p_axis, real_t p_phi) { + *this = rotated(p_axis, p_phi); +} + +void Transform::rotate_local(const Vector3 &p_axis, real_t p_phi) { + *this = rotated_local(p_axis, p_phi); +} + +void Transform::rotate_basis(const Vector3 &p_axis, real_t p_phi) { + basis.rotate(p_axis, p_phi); +} + +void Transform::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up) { +#ifdef MATH_CHECKS + ERR_FAIL_COND(p_eye == p_target); + ERR_FAIL_COND(p_up.length() == 0); +#endif + // Reference: MESA source code + Vector3 v_x, v_y, v_z; + + /* Make rotation matrix */ + + /* Z vector */ + v_z = p_eye - p_target; + + v_z.normalize(); + + v_y = p_up; + + v_x = v_y.cross(v_z); +#ifdef MATH_CHECKS + ERR_FAIL_COND(v_x.length() == 0); +#endif + + /* Recompute Y = Z cross X */ + v_y = v_z.cross(v_x); + + v_x.normalize(); + v_y.normalize(); + + basis.set(v_x, v_y, v_z); + + origin = p_eye; +} + +Transform Transform::looking_at(const Vector3 &p_target, const Vector3 &p_up) const { + Transform t = *this; + t.set_look_at(origin, p_target, p_up); + return t; +} + +void Transform::scale(const Vector3 &p_scale) { + basis.scale(p_scale); + origin *= p_scale; +} + +Transform Transform::scaled(const Vector3 &p_scale) const { + // Equivalent to left multiplication + return Transform(basis.scaled(p_scale), origin * p_scale); +} + +Transform Transform::scaled_local(const Vector3 &p_scale) const { + // Equivalent to right multiplication + return Transform(basis.scaled_local(p_scale), origin); +} + +void Transform::scale_basis(const Vector3 &p_scale) { + basis.scale(p_scale); +} + +void Transform::translate_local(real_t p_tx, real_t p_ty, real_t p_tz) { + translate_local(Vector3(p_tx, p_ty, p_tz)); +} +void Transform::translate_local(const Vector3 &p_translation) { + for (int i = 0; i < 3; i++) { + origin[i] += basis[i].dot(p_translation); + } +} + +void Transform::translate_localr(real_t p_tx, real_t p_ty, real_t p_tz) { + translate_local(Vector3(p_tx, p_ty, p_tz)); +} +void Transform::translate_localv(const Vector3 &p_translation) { + for (int i = 0; i < 3; i++) { + origin[i] += basis[i].dot(p_translation); + } +} + +Transform Transform::translated(const Vector3 &p_translation) const { + // Equivalent to left multiplication + return Transform(basis, origin + p_translation); +} + +Transform Transform::translated_local(const Vector3 &p_translation) const { + // Equivalent to right multiplication + return Transform(basis, origin + basis.xform(p_translation)); +} + +void Transform::orthonormalize() { + basis.orthonormalize(); +} + +Transform Transform::orthonormalized() const { + Transform _copy = *this; + _copy.orthonormalize(); + return _copy; +} + +void Transform::orthogonalize() { + basis.orthogonalize(); +} + +Transform Transform::orthogonalized() const { + Transform _copy = *this; + _copy.orthogonalize(); + return _copy; +} + +bool Transform::is_equal_approx(const Transform &p_transform) const { + return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin); +} + +bool Transform::operator==(const Transform &p_transform) const { + return (basis == p_transform.basis && origin == p_transform.origin); +} +bool Transform::operator!=(const Transform &p_transform) const { + return (basis != p_transform.basis || origin != p_transform.origin); +} + +void Transform::operator*=(const Transform &p_transform) { + origin = xform(p_transform.origin); + basis *= p_transform.basis; +} + +Transform Transform::operator*(const Transform &p_transform) const { + Transform t = *this; + t *= p_transform; + return t; +} + +void Transform::operator*=(const real_t p_val) { + origin *= p_val; + basis *= p_val; +} + +Transform Transform::operator*(const real_t p_val) const { + Transform ret(*this); + ret *= p_val; + return ret; +} + +Transform Transform::spherical_interpolate_with(const Transform &p_transform, real_t p_c) const { + /* not sure if very "efficient" but good enough? */ + + Transform interp; + + Vector3 src_scale = basis.get_scale(); + Quaternion src_rot = basis.get_rotation_quaternion(); + Vector3 src_loc = origin; + + Vector3 dst_scale = p_transform.basis.get_scale(); + Quaternion dst_rot = p_transform.basis.get_rotation_quaternion(); + Vector3 dst_loc = p_transform.origin; + + interp.basis.set_quaternion_scale(src_rot.slerp(dst_rot, p_c).normalized(), src_scale.linear_interpolate(dst_scale, p_c)); + interp.origin = src_loc.linear_interpolate(dst_loc, p_c); + + return interp; +} + +Transform Transform::interpolate_with(const Transform &p_transform, real_t p_c) const { + /* not sure if very "efficient" but good enough? */ + + Vector3 src_scale = basis.get_scale(); + Quaternion src_rot = basis.get_rotation_quaternion(); + Vector3 src_loc = origin; + + Vector3 dst_scale = p_transform.basis.get_scale(); + Quaternion dst_rot = p_transform.basis.get_rotation_quaternion(); + Vector3 dst_loc = p_transform.origin; + + Transform interp; + interp.basis.set_quaternion_scale(src_rot.slerp(dst_rot, p_c).normalized(), src_scale.linear_interpolate(dst_scale, p_c)); + interp.origin = src_loc.linear_interpolate(dst_loc, p_c); + + return interp; +} + +Transform::operator String() const { + return "[X: " + basis.get_axis(0).operator String() + + ", Y: " + basis.get_axis(1).operator String() + + ", Z: " + basis.get_axis(2).operator String() + + ", O: " + origin.operator String() + "]"; +} + +Transform::Transform(const Basis &p_basis, const Vector3 &p_origin) : + basis(p_basis), + origin(p_origin) { +} + +Transform::Transform(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz) { + basis = Basis(xx, xy, xz, yx, yy, yz, zx, zy, zz); + origin = Vector3(ox, oy, oz); +} + +Transform::Transform(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z, const Vector3 &p_origin) : + origin(p_origin) { + basis.set_column(0, p_x); + basis.set_column(1, p_y); + basis.set_column(2, p_z); +} +#line 0 + +#line 1 "sfwl/core/color.cpp" +/*************************************************************************/ +/* color.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +uint32_t Color::to_argb32() const { + uint32_t c = (uint8_t)Math::round(a * 255); + c <<= 8; + c |= (uint8_t)Math::round(r * 255); + c <<= 8; + c |= (uint8_t)Math::round(g * 255); + c <<= 8; + c |= (uint8_t)Math::round(b * 255); + + return c; +} + +uint32_t Color::to_abgr32() const { + uint32_t c = (uint8_t)Math::round(a * 255); + c <<= 8; + c |= (uint8_t)Math::round(b * 255); + c <<= 8; + c |= (uint8_t)Math::round(g * 255); + c <<= 8; + c |= (uint8_t)Math::round(r * 255); + + return c; +} + +uint32_t Color::to_rgba32() const { + uint32_t c = (uint8_t)Math::round(r * 255); + c <<= 8; + c |= (uint8_t)Math::round(g * 255); + c <<= 8; + c |= (uint8_t)Math::round(b * 255); + c <<= 8; + c |= (uint8_t)Math::round(a * 255); + + return c; +} + +uint64_t Color::to_abgr64() const { + uint64_t c = (uint16_t)Math::round(a * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(r * 65535); + + return c; +} + +uint64_t Color::to_argb64() const { + uint64_t c = (uint16_t)Math::round(a * 65535); + c <<= 16; + c |= (uint16_t)Math::round(r * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + + return c; +} + +uint64_t Color::to_rgba64() const { + uint64_t c = (uint16_t)Math::round(r * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + c <<= 16; + c |= (uint16_t)Math::round(a * 65535); + + return c; +} + +float Color::get_h() const { + float min = MIN(r, g); + min = MIN(min, b); + float max = MAX(r, g); + max = MAX(max, b); + + float delta = max - min; + + if (delta == 0) { + return 0; + } + + float h; + if (r == max) { + h = (g - b) / delta; // between yellow & magenta + } else if (g == max) { + h = 2 + (b - r) / delta; // between cyan & yellow + } else { + h = 4 + (r - g) / delta; // between magenta & cyan + } + + h /= 6.0; + if (h < 0) { + h += 1.0; + } + + return h; +} + +float Color::get_s() const { + float min = MIN(r, g); + min = MIN(min, b); + float max = MAX(r, g); + max = MAX(max, b); + + float delta = max - min; + + return (max != 0) ? (delta / max) : 0; +} + +float Color::get_v() const { + float max = MAX(r, g); + max = MAX(max, b); + return max; +} + +void Color::set_hsv(float p_h, float p_s, float p_v, float p_alpha) { + int i; + float f, p, q, t; + a = p_alpha; + + if (p_s == 0) { + // acp_hromatic (grey) + r = g = b = p_v; + return; + } + + p_h *= 6.0; + p_h = Math::fmod(p_h, 6); + i = Math::floor(p_h); + + f = p_h - i; + p = p_v * (1 - p_s); + q = p_v * (1 - p_s * f); + t = p_v * (1 - p_s * (1 - f)); + + switch (i) { + case 0: // Red is the dominant color + r = p_v; + g = t; + b = p; + break; + case 1: // Green is the dominant color + r = q; + g = p_v; + b = p; + break; + case 2: + r = p; + g = p_v; + b = t; + break; + case 3: // Blue is the dominant color + r = p; + g = q; + b = p_v; + break; + case 4: + r = t; + g = p; + b = p_v; + break; + default: // (5) Red is the dominant color + r = p_v; + g = p; + b = q; + break; + } +} + +bool Color::is_equal_approx(const Color &p_color) const { + return Math::is_equal_approx(r, p_color.r) && Math::is_equal_approx(g, p_color.g) && Math::is_equal_approx(b, p_color.b) && Math::is_equal_approx(a, p_color.a); +} + +Color Color::clamp(const Color &p_min, const Color &p_max) const { + return Color( + CLAMP(r, p_min.r, p_max.r), + CLAMP(g, p_min.g, p_max.g), + CLAMP(b, p_min.b, p_max.b), + CLAMP(a, p_min.a, p_max.a)); +} + +void Color::invert() { + r = 1.0 - r; + g = 1.0 - g; + b = 1.0 - b; +} +void Color::contrast() { + r = Math::fmod(r + 0.5, 1.0); + g = Math::fmod(g + 0.5, 1.0); + b = Math::fmod(b + 0.5, 1.0); +} + +Color Color::hex(uint32_t p_hex) { + float a = (p_hex & 0xFF) / 255.0; + p_hex >>= 8; + float b = (p_hex & 0xFF) / 255.0; + p_hex >>= 8; + float g = (p_hex & 0xFF) / 255.0; + p_hex >>= 8; + float r = (p_hex & 0xFF) / 255.0; + + return Color(r, g, b, a); +} + +Color Color::hex64(uint64_t p_hex) { + float a = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float b = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float g = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float r = (p_hex & 0xFFFF) / 65535.0; + + return Color(r, g, b, a); +} + +Color Color::from_rgbe9995(uint32_t p_rgbe) { + float r = p_rgbe & 0x1ff; + float g = (p_rgbe >> 9) & 0x1ff; + float b = (p_rgbe >> 18) & 0x1ff; + float e = (p_rgbe >> 27); + float m = Math::pow(2, e - 15.0 - 9.0); + + float rd = r * m; + float gd = g * m; + float bd = b * m; + + return Color(rd, gd, bd, 1.0f); +} + +static float _parse_col(const String &p_str, int p_ofs) { + int ig = 0; + + for (int i = 0; i < 2; i++) { + int c = p_str[i + p_ofs]; + int v = 0; + + if (c >= '0' && c <= '9') { + v = c - '0'; + } else if (c >= 'a' && c <= 'f') { + v = c - 'a'; + v += 10; + } else if (c >= 'A' && c <= 'F') { + v = c - 'A'; + v += 10; + } else { + return -1; + } + + if (i == 0) { + ig += v * 16; + } else { + ig += v; + } + } + + return ig; +} + +Color Color::inverted() const { + Color c = *this; + c.invert(); + return c; +} + +Color Color::contrasted() const { + Color c = *this; + c.contrast(); + return c; +} + +Color Color::html(const String &p_color) { + String color = p_color; + if (color.length() == 0) { + return Color(); + } + if (color[0] == '#') { + color = color.substr(1, color.length() - 1); + } + if (color.length() == 3 || color.length() == 4) { + String exp_color; + for (int i = 0; i < color.length(); i++) { + exp_color += color[i]; + exp_color += color[i]; + } + color = exp_color; + } + + bool alpha = false; + + if (color.length() == 8) { + alpha = true; + } else if (color.length() == 6) { + alpha = false; + } else { + ERR_FAIL_V_MSG(Color(), "Invalid color code: " + p_color + "."); + } + + int a = 255; + if (alpha) { + a = _parse_col(color, 0); + ERR_FAIL_COND_V_MSG(a < 0, Color(), "Invalid color code: " + p_color + "."); + } + + int from = alpha ? 2 : 0; + + int r = _parse_col(color, from + 0); + ERR_FAIL_COND_V_MSG(r < 0, Color(), "Invalid color code: " + p_color + "."); + int g = _parse_col(color, from + 2); + ERR_FAIL_COND_V_MSG(g < 0, Color(), "Invalid color code: " + p_color + "."); + int b = _parse_col(color, from + 4); + ERR_FAIL_COND_V_MSG(b < 0, Color(), "Invalid color code: " + p_color + "."); + + return Color(r / 255.0, g / 255.0, b / 255.0, a / 255.0); +} + +bool Color::html_is_valid(const String &p_color) { + String color = p_color; + + if (color.length() == 0) { + return false; + } + if (color[0] == '#') { + color = color.substr(1, color.length() - 1); + } + + bool alpha = false; + + if (color.length() == 8) { + alpha = true; + } else if (color.length() == 6) { + alpha = false; + } else { + return false; + } + + if (alpha) { + int a = _parse_col(color, 0); + if (a < 0) { + return false; + } + } + + int from = alpha ? 2 : 0; + + int r = _parse_col(color, from + 0); + if (r < 0) { + return false; + } + int g = _parse_col(color, from + 2); + if (g < 0) { + return false; + } + int b = _parse_col(color, from + 4); + if (b < 0) { + return false; + } + + return true; +} + +String _to_hex(float p_val) { + int v = Math::round(p_val * 255); + v = CLAMP(v, 0, 255); + String ret; + + for (int i = 0; i < 2; i++) { + CharType c[2] = { 0, 0 }; + int lv = v & 0xF; + if (lv < 10) { + c[0] = '0' + lv; + } else { + c[0] = 'a' + lv - 10; + } + + v >>= 4; + String cs = (const CharType *)c; + ret = cs + ret; + } + + return ret; +} + +String Color::to_html(bool p_alpha) const { + String txt; + txt += _to_hex(r); + txt += _to_hex(g); + txt += _to_hex(b); + if (p_alpha) { + txt = _to_hex(a) + txt; + } + return txt; +} + +Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const { + Color c; + c.set_hsv(p_h, p_s, p_v, p_a); + return c; +} + +Color::operator String() const { + return "(" + String::num(r, 4) + ", " + String::num(g, 4) + ", " + String::num(b, 4) + ", " + String::num(a, 4) + ")"; +} + +Color Color::operator+(const Color &p_color) const { + return Color( + r + p_color.r, + g + p_color.g, + b + p_color.b, + a + p_color.a); +} + +void Color::operator+=(const Color &p_color) { + r = r + p_color.r; + g = g + p_color.g; + b = b + p_color.b; + a = a + p_color.a; +} + +Color Color::operator-(const Color &p_color) const { + return Color( + r - p_color.r, + g - p_color.g, + b - p_color.b, + a - p_color.a); +} + +void Color::operator-=(const Color &p_color) { + r = r - p_color.r; + g = g - p_color.g; + b = b - p_color.b; + a = a - p_color.a; +} + +Color Color::operator*(const Color &p_color) const { + return Color( + r * p_color.r, + g * p_color.g, + b * p_color.b, + a * p_color.a); +} + +Color Color::operator*(const real_t &rvalue) const { + return Color( + r * rvalue, + g * rvalue, + b * rvalue, + a * rvalue); +} + +void Color::operator*=(const Color &p_color) { + r = r * p_color.r; + g = g * p_color.g; + b = b * p_color.b; + a = a * p_color.a; +} + +void Color::operator*=(const real_t &rvalue) { + r = r * rvalue; + g = g * rvalue; + b = b * rvalue; + a = a * rvalue; +} + +Color Color::operator/(const Color &p_color) const { + return Color( + r / p_color.r, + g / p_color.g, + b / p_color.b, + a / p_color.a); +} + +Color Color::operator/(const real_t &rvalue) const { + return Color( + r / rvalue, + g / rvalue, + b / rvalue, + a / rvalue); +} + +void Color::operator/=(const Color &p_color) { + r = r / p_color.r; + g = g / p_color.g; + b = b / p_color.b; + a = a / p_color.a; +} + +void Color::operator/=(const real_t &rvalue) { + if (rvalue == 0) { + r = 1.0; + g = 1.0; + b = 1.0; + a = 1.0; + } else { + r = r / rvalue; + g = g / rvalue; + b = b / rvalue; + a = a / rvalue; + } +}; + +Color Color::operator-() const { + return Color( + 1.0 - r, + 1.0 - g, + 1.0 - b, + 1.0 - a); +} +#line 0 + +#line 1 "sfwl/core/quaternion.cpp" +/*************************************************************************/ +/* quaternion.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +real_t Quaternion::angle_to(const Quaternion &p_to) const { + real_t d = dot(p_to); + + // acos does clamping. + return Math::acos(d * d * 2 - 1); +} + +// set_euler_xyz expects a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// This implementation uses XYZ convention (Z is the first rotation). +void Quaternion::set_euler_xyz(const Vector3 &p_euler) { + real_t half_a1 = p_euler.x * 0.5f; + real_t half_a2 = p_euler.y * 0.5f; + real_t half_a3 = p_euler.z * 0.5f; + + // R = X(a1).Y(a2).Z(a3) convention for Euler angles. + // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-2) + // a3 is the angle of the first rotation, following the notation in this reference. + + real_t cos_a1 = Math::cos(half_a1); + real_t sin_a1 = Math::sin(half_a1); + real_t cos_a2 = Math::cos(half_a2); + real_t sin_a2 = Math::sin(half_a2); + real_t cos_a3 = Math::cos(half_a3); + real_t sin_a3 = Math::sin(half_a3); + + set(sin_a1 * cos_a2 * cos_a3 + sin_a2 * sin_a3 * cos_a1, + -sin_a1 * sin_a3 * cos_a2 + sin_a2 * cos_a1 * cos_a3, + sin_a1 * sin_a2 * cos_a3 + sin_a3 * cos_a1 * cos_a2, + -sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3); +} + +// get_euler_xyz returns a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// This implementation uses XYZ convention (Z is the first rotation). +Vector3 Quaternion::get_euler_xyz() const { + Basis m(*this); + return m.get_euler_xyz(); +} + +// set_euler_yxz expects a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// This implementation uses YXZ convention (Z is the first rotation). +void Quaternion::set_euler_yxz(const Vector3 &p_euler) { + real_t half_a1 = p_euler.y * 0.5f; + real_t half_a2 = p_euler.x * 0.5f; + real_t half_a3 = p_euler.z * 0.5f; + + // R = Y(a1).X(a2).Z(a3) convention for Euler angles. + // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6) + // a3 is the angle of the first rotation, following the notation in this reference. + + real_t cos_a1 = Math::cos(half_a1); + real_t sin_a1 = Math::sin(half_a1); + real_t cos_a2 = Math::cos(half_a2); + real_t sin_a2 = Math::sin(half_a2); + real_t cos_a3 = Math::cos(half_a3); + real_t sin_a3 = Math::sin(half_a3); + + set(sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3, + sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3, + -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3, + sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3); +} + +// get_euler_yxz returns a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// This implementation uses YXZ convention (Z is the first rotation). +Vector3 Quaternion::get_euler_yxz() const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion must be normalized."); +#endif + Basis m(*this); + return m.get_euler_yxz(); +} + +void Quaternion::operator*=(const Quaternion &p_q) { + set(w * p_q.x + x * p_q.w + y * p_q.z - z * p_q.y, + w * p_q.y + y * p_q.w + z * p_q.x - x * p_q.z, + w * p_q.z + z * p_q.w + x * p_q.y - y * p_q.x, + w * p_q.w - x * p_q.x - y * p_q.y - z * p_q.z); +} + +Quaternion Quaternion::operator*(const Quaternion &p_q) const { + Quaternion r = *this; + r *= p_q; + return r; +} + +bool Quaternion::is_equal_approx(const Quaternion &p_quat) const { + return Math::is_equal_approx(x, p_quat.x) && Math::is_equal_approx(y, p_quat.y) && Math::is_equal_approx(z, p_quat.z) && Math::is_equal_approx(w, p_quat.w); +} + +real_t Quaternion::length() const { + return Math::sqrt(length_squared()); +} + +void Quaternion::normalize() { + *this /= length(); +} + +Quaternion Quaternion::normalized() const { + return *this / length(); +} + +bool Quaternion::is_normalized() const { + return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON); //use less epsilon +} + +Quaternion Quaternion::inverse() const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The quaternion must be normalized."); +#endif + return Quaternion(-x, -y, -z, w); +} + +Quaternion Quaternion::log() const { + Quaternion src = *this; + Vector3 src_v = src.get_axis() * src.get_angle(); + return Quaternion(src_v.x, src_v.y, src_v.z, 0); +} + +Quaternion Quaternion::exp() const { + Quaternion src = *this; + Vector3 src_v = Vector3(src.x, src.y, src.z); + float theta = src_v.length(); + if (theta < CMP_EPSILON) { + return Quaternion(0, 0, 0, 1); + } + return Quaternion(src_v.normalized(), theta); +} + +Quaternion Quaternion::slerp(const Quaternion &p_to, const real_t &p_weight) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized."); +#endif + Quaternion to1; + real_t omega, cosom, sinom, scale0, scale1; + + // calc cosine + cosom = dot(p_to); + + // adjust signs (if necessary) + if (cosom < 0) { + cosom = -cosom; + to1.x = -p_to.x; + to1.y = -p_to.y; + to1.z = -p_to.z; + to1.w = -p_to.w; + } else { + to1.x = p_to.x; + to1.y = p_to.y; + to1.z = p_to.z; + to1.w = p_to.w; + } + + // calculate coefficients + + if ((1 - cosom) > (real_t)CMP_EPSILON) { + // standard case (slerp) + omega = Math::acos(cosom); + sinom = Math::sin(omega); + scale0 = Math::sin((1 - p_weight) * omega) / sinom; + scale1 = Math::sin(p_weight * omega) / sinom; + } else { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = 1 - p_weight; + scale1 = p_weight; + } + // calculate final values + return Quaternion( + scale0 * x + scale1 * to1.x, + scale0 * y + scale1 * to1.y, + scale0 * z + scale1 * to1.z, + scale0 * w + scale1 * to1.w); +} + +Quaternion Quaternion::slerpni(const Quaternion &p_to, const real_t &p_weight) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized."); +#endif + const Quaternion &from = *this; + + real_t dot = from.dot(p_to); + + if (Math::absf(dot) > 0.9999f) { + return from; + } + + real_t theta = Math::acos(dot), + sinT = 1 / Math::sin(theta), + newFactor = Math::sin(p_weight * theta) * sinT, + invFactor = Math::sin((1 - p_weight) * theta) * sinT; + + return Quaternion(invFactor * from.x + newFactor * p_to.x, + invFactor * from.y + newFactor * p_to.y, + invFactor * from.z + newFactor * p_to.z, + invFactor * from.w + newFactor * p_to.w); +} + +Quaternion Quaternion::cubic_slerp(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion must be normalized."); +#endif + //the only way to do slerp :| + real_t t2 = (1 - p_weight) * p_weight * 2; + Quaternion sp = this->slerp(p_b, p_weight); + Quaternion sq = p_pre_a.slerpni(p_post_b, p_weight); + return sp.slerpni(sq, t2); +} + +Quaternion Quaternion::spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion must be normalized."); +#endif + Quaternion from_q = *this; + Quaternion pre_q = p_pre_a; + Quaternion to_q = p_b; + Quaternion post_q = p_post_b; + + // Align flip phases. + from_q = Basis(from_q).get_rotation_quaternion(); + pre_q = Basis(pre_q).get_rotation_quaternion(); + to_q = Basis(to_q).get_rotation_quaternion(); + post_q = Basis(post_q).get_rotation_quaternion(); + + // Flip quaternions to shortest path if necessary. + bool flip1 = signbit(from_q.dot(pre_q)); + pre_q = flip1 ? -pre_q : pre_q; + bool flip2 = signbit(from_q.dot(to_q)); + to_q = flip2 ? -to_q : to_q; + bool flip3 = flip2 ? to_q.dot(post_q) <= 0 : signbit(to_q.dot(post_q)); + post_q = flip3 ? -post_q : post_q; + + // Calc by Expmap in from_q space. + Quaternion ln_from = Quaternion(0, 0, 0, 0); + Quaternion ln_to = (from_q.inverse() * to_q).log(); + Quaternion ln_pre = (from_q.inverse() * pre_q).log(); + Quaternion ln_post = (from_q.inverse() * post_q).log(); + Quaternion ln = Quaternion(0, 0, 0, 0); + ln.x = Math::cubic_interpolate(ln_from.x, ln_to.x, ln_pre.x, ln_post.x, p_weight); + ln.y = Math::cubic_interpolate(ln_from.y, ln_to.y, ln_pre.y, ln_post.y, p_weight); + ln.z = Math::cubic_interpolate(ln_from.z, ln_to.z, ln_pre.z, ln_post.z, p_weight); + Quaternion q1 = from_q * ln.exp(); + + // Calc by Expmap in to_q space. + ln_from = (to_q.inverse() * from_q).log(); + ln_to = Quaternion(0, 0, 0, 0); + ln_pre = (to_q.inverse() * pre_q).log(); + ln_post = (to_q.inverse() * post_q).log(); + ln = Quaternion(0, 0, 0, 0); + ln.x = Math::cubic_interpolate(ln_from.x, ln_to.x, ln_pre.x, ln_post.x, p_weight); + ln.y = Math::cubic_interpolate(ln_from.y, ln_to.y, ln_pre.y, ln_post.y, p_weight); + ln.z = Math::cubic_interpolate(ln_from.z, ln_to.z, ln_pre.z, ln_post.z, p_weight); + Quaternion q2 = to_q * ln.exp(); + + // To cancel error made by Expmap ambiguity, do blends. + return q1.slerp(q2, p_weight); +} + +Vector3 Quaternion::get_axis() const { + if (Math::abs(w) > 1 - CMP_EPSILON) { + return Vector3(x, y, z); + } + real_t r = ((real_t)1) / Math::sqrt(1 - w * w); + return Vector3(x * r, y * r, z * r); +} + +float Quaternion::get_angle() const { + return 2 * Math::acos(w); +} + +Quaternion::operator String() const { + return "(" + String::num_real(x) + ", " + String::num_real(y) + ", " + String::num_real(z) + ", " + String::num_real(w) + ")"; +} + +void Quaternion::set_axis_angle(const Vector3 &axis, const real_t &angle) { +#ifdef MATH_CHECKS + ERR_FAIL_COND_MSG(!axis.is_normalized(), "The axis Vector3 must be normalized."); +#endif + real_t d = axis.length(); + if (d == 0) { + set(0, 0, 0, 0); + } else { + real_t sin_angle = Math::sin(angle * 0.5f); + real_t cos_angle = Math::cos(angle * 0.5f); + real_t s = sin_angle / d; + set(axis.x * s, axis.y * s, axis.z * s, + cos_angle); + } +} +#line 0 + +#line 1 "sfwl/core/plane.cpp" +/*************************************************************************/ +/* plane.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +void Plane::set_normal(const Vector3 &p_normal) { + normal = p_normal; +} + +void Plane::normalize() { + real_t l = normal.length(); + if (l == 0) { + *this = Plane(0, 0, 0, 0); + return; + } + normal /= l; + d /= l; +} + +Plane Plane::normalized() const { + Plane p = *this; + p.normalize(); + return p; +} + +Vector3 Plane::get_any_point() const { + return get_normal() * d; +} + +Vector3 Plane::get_any_perpendicular_normal() const { + static const Vector3 p1 = Vector3(1, 0, 0); + static const Vector3 p2 = Vector3(0, 1, 0); + Vector3 p; + + if (ABS(normal.dot(p1)) > 0.99f) { // if too similar to p1 + p = p2; // use p2 + } else { + p = p1; // use p1 + } + + p -= normal * normal.dot(p); + p.normalize(); + + return p; +} + +/* intersections */ + +bool Plane::intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r_result) const { + const Plane &p_plane0 = *this; + Vector3 normal0 = p_plane0.normal; + Vector3 normal1 = p_plane1.normal; + Vector3 normal2 = p_plane2.normal; + + real_t denom = vec3_cross(normal0, normal1).dot(normal2); + + if (Math::is_zero_approx(denom)) { + return false; + } + + if (r_result) { + *r_result = ((vec3_cross(normal1, normal2) * p_plane0.d) + + (vec3_cross(normal2, normal0) * p_plane1.d) + + (vec3_cross(normal0, normal1) * p_plane2.d)) / + denom; + } + + return true; +} + +bool Plane::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection) const { + Vector3 segment = p_dir; + real_t den = normal.dot(segment); + + //printf("den is %i\n",den); + if (Math::is_zero_approx(den)) { + return false; + } + + real_t dist = (normal.dot(p_from) - d) / den; + //printf("dist is %i\n",dist); + + if (dist > (real_t)CMP_EPSILON) { //this is a ray, before the emitting pos (p_from) doesn't exist + + return false; + } + + dist = -dist; + *p_intersection = p_from + segment * dist; + + return true; +} + +bool Plane::intersects_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 *p_intersection) const { + Vector3 segment = p_begin - p_end; + real_t den = normal.dot(segment); + + //printf("den is %i\n",den); + if (Math::is_zero_approx(den)) { + return false; + } + + real_t dist = (normal.dot(p_begin) - d) / den; + //printf("dist is %i\n",dist); + + if (dist < (real_t)-CMP_EPSILON || dist > (1 + (real_t)CMP_EPSILON)) { + return false; + } + + dist = -dist; + *p_intersection = p_begin + segment * dist; + + return true; +} + +/* misc */ + +bool Plane::is_equal_approx(const Plane &p_plane) const { + return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d); +} + +bool Plane::is_equal_approx_any_side(const Plane &p_plane) const { + return (normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d)) || (normal.is_equal_approx(-p_plane.normal) && Math::is_equal_approx(d, -p_plane.d)); +} + +Plane::operator String() const { + return "[N: " + normal.operator String() + ", D: " + String::num_real(d) + "]"; +} +#line 0 + +#line 1 "sfwl/core/vector2i.cpp" +/*************************************************************************/ +/* vector2i.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +Vector2i Vector2i::clamp(const Vector2i &p_min, const Vector2i &p_max) const { + return Vector2i( + CLAMP(x, p_min.x, p_max.x), + CLAMP(y, p_min.y, p_max.y)); +} + +int64_t Vector2i::length_squared() const { + return x * (int64_t)x + y * (int64_t)y; +} + +double Vector2i::length() const { + return Math::sqrt((double)length_squared()); +} + +Vector2i Vector2i::operator+(const Vector2i &p_v) const { + return Vector2i(x + p_v.x, y + p_v.y); +} +void Vector2i::operator+=(const Vector2i &p_v) { + x += p_v.x; + y += p_v.y; +} +Vector2i Vector2i::operator-(const Vector2i &p_v) const { + return Vector2i(x - p_v.x, y - p_v.y); +} +void Vector2i::operator-=(const Vector2i &p_v) { + x -= p_v.x; + y -= p_v.y; +} + +Vector2i Vector2i::operator*(const Vector2i &p_v1) const { + return Vector2i(x * p_v1.x, y * p_v1.y); +}; + +Vector2i Vector2i::operator*(const int &rvalue) const { + return Vector2i(x * rvalue, y * rvalue); +}; +void Vector2i::operator*=(const int &rvalue) { + x *= rvalue; + y *= rvalue; +}; + +Vector2i Vector2i::operator/(const Vector2i &p_v1) const { + return Vector2i(x / p_v1.x, y / p_v1.y); +}; + +Vector2i Vector2i::operator/(const int &rvalue) const { + return Vector2i(x / rvalue, y / rvalue); +}; + +void Vector2i::operator/=(const int &rvalue) { + x /= rvalue; + y /= rvalue; +}; + +Vector2i Vector2i::operator-() const { + return Vector2i(-x, -y); +} + +bool Vector2i::operator==(const Vector2i &p_vec2) const { + return x == p_vec2.x && y == p_vec2.y; +} +bool Vector2i::operator!=(const Vector2i &p_vec2) const { + return x != p_vec2.x || y != p_vec2.y; +} + +Vector2i::operator String() const { + return "(" + itos(x) + ", " + itos(y) + ")"; +} +#line 0 + +#line 1 "sfwl/core/rect2.cpp" +/*************************************************************************/ +/* rect2.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +bool Rect2::is_equal_approx(const Rect2 &p_rect) const { + return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size); +} + +bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const { + real_t min = 0, max = 1; + int axis = 0; + real_t sign = 0; + + for (int i = 0; i < 2; i++) { + real_t seg_from = p_from[i]; + real_t seg_to = p_to[i]; + real_t box_begin = position[i]; + real_t box_end = box_begin + size[i]; + real_t cmin, cmax; + real_t csign; + + if (seg_from < seg_to) { + if (seg_from > box_end || seg_to < box_begin) { + return false; + } + real_t length = seg_to - seg_from; + cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; + cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; + csign = -1.0; + + } else { + if (seg_to > box_end || seg_from < box_begin) { + return false; + } + real_t length = seg_to - seg_from; + cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; + cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; + csign = 1.0; + } + + if (cmin > min) { + min = cmin; + axis = i; + sign = csign; + } + if (cmax < max) { + max = cmax; + } + if (max < min) { + return false; + } + } + + Vector2 rel = p_to - p_from; + + if (r_normal) { + Vector2 normal; + normal[axis] = sign; + *r_normal = normal; + } + + if (r_pos) { + *r_pos = p_from + rel * min; + } + + return true; +} + +bool Rect2::intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const { + //SAT intersection between local and transformed rect2 + + Vector2 xf_points[4] = { + p_xform.xform(p_rect.position), + p_xform.xform(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y)), + p_xform.xform(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), + p_xform.xform(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y + p_rect.size.y)), + }; + + real_t low_limit; + + //base rect2 first (faster) + + if (xf_points[0].y > position.y) { + goto next1; + } + if (xf_points[1].y > position.y) { + goto next1; + } + if (xf_points[2].y > position.y) { + goto next1; + } + if (xf_points[3].y > position.y) { + goto next1; + } + + return false; + +next1: + + low_limit = position.y + size.y; + + if (xf_points[0].y < low_limit) { + goto next2; + } + if (xf_points[1].y < low_limit) { + goto next2; + } + if (xf_points[2].y < low_limit) { + goto next2; + } + if (xf_points[3].y < low_limit) { + goto next2; + } + + return false; + +next2: + + if (xf_points[0].x > position.x) { + goto next3; + } + if (xf_points[1].x > position.x) { + goto next3; + } + if (xf_points[2].x > position.x) { + goto next3; + } + if (xf_points[3].x > position.x) { + goto next3; + } + + return false; + +next3: + + low_limit = position.x + size.x; + + if (xf_points[0].x < low_limit) { + goto next4; + } + if (xf_points[1].x < low_limit) { + goto next4; + } + if (xf_points[2].x < low_limit) { + goto next4; + } + if (xf_points[3].x < low_limit) { + goto next4; + } + + return false; + +next4: + + Vector2 xf_points2[4] = { + position, + Vector2(position.x + size.x, position.y), + Vector2(position.x, position.y + size.y), + Vector2(position.x + size.x, position.y + size.y), + }; + + real_t maxa = p_xform.columns[0].dot(xf_points2[0]); + real_t mina = maxa; + + real_t dp = p_xform.columns[0].dot(xf_points2[1]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.columns[0].dot(xf_points2[2]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.columns[0].dot(xf_points2[3]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + real_t maxb = p_xform.columns[0].dot(xf_points[0]); + real_t minb = maxb; + + dp = p_xform.columns[0].dot(xf_points[1]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.columns[0].dot(xf_points[2]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.columns[0].dot(xf_points[3]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + if (mina > maxb) { + return false; + } + if (minb > maxa) { + return false; + } + + maxa = p_xform.columns[1].dot(xf_points2[0]); + mina = maxa; + + dp = p_xform.columns[1].dot(xf_points2[1]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.columns[1].dot(xf_points2[2]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.columns[1].dot(xf_points2[3]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + maxb = p_xform.columns[1].dot(xf_points[0]); + minb = maxb; + + dp = p_xform.columns[1].dot(xf_points[1]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.columns[1].dot(xf_points[2]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.columns[1].dot(xf_points[3]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + if (mina > maxb) { + return false; + } + if (minb > maxa) { + return false; + } + + return true; +} + +Rect2::operator String() const { + return "[P: " + position.operator String() + ", S: " + size + "]"; +} +#line 0 + +#line 1 "sfwl/core/rect2i.cpp" +/*************************************************************************/ +/* rect2i.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +Rect2i::operator String() const { + return "[P: " + position.operator String() + ", S: " + size + "]"; +} +#line 0 + +#line 1 "sfwl/core/vector4.cpp" +/*************************************************************************/ +/* vector4.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +void Vector4::set_axis(const int p_axis, const real_t p_value) { + ERR_FAIL_INDEX(p_axis, 4); + components[p_axis] = p_value; +} + +real_t Vector4::get_axis(const int p_axis) const { + ERR_FAIL_INDEX_V(p_axis, 4, 0); + return operator[](p_axis); +} + +Vector4::Axis Vector4::min_axis() const { + uint32_t min_index = 0; + real_t min_value = x; + for (uint32_t i = 1; i < 4; i++) { + if (operator[](i) <= min_value) { + min_index = i; + min_value = operator[](i); + } + } + return Vector4::Axis(min_index); +} + +Vector4::Axis Vector4::max_axis() const { + uint32_t max_index = 0; + real_t max_value = x; + for (uint32_t i = 1; i < 4; i++) { + if (operator[](i) > max_value) { + max_index = i; + max_value = operator[](i); + } + } + return Vector4::Axis(max_index); +} + +bool Vector4::is_equal_approx(const Vector4 &p_vec4) const { + return Math::is_equal_approx(x, p_vec4.x) && Math::is_equal_approx(y, p_vec4.y) && Math::is_equal_approx(z, p_vec4.z) && Math::is_equal_approx(w, p_vec4.w); +} + +real_t Vector4::length() const { + return Math::sqrt(length_squared()); +} + +void Vector4::normalize() { + *this /= length(); +} + +Vector4 Vector4::normalized() const { + return *this / length(); +} + +bool Vector4::is_normalized() const { + return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON); // Use less epsilon. +} + +Vector4 Vector4::limit_length(const real_t p_len) const { + const real_t l = length(); + Vector4 v = *this; + if (l > 0 && p_len < l) { + v /= l; + v *= p_len; + } + + return v; +} + +real_t Vector4::distance_to(const Vector4 &p_to) const { + return (p_to - *this).length(); +} + +Vector4 Vector4::direction_to(const Vector4 &p_to) const { + Vector4 ret(p_to.x - x, p_to.y - y, p_to.z - z, p_to.w - w); + ret.normalize(); + return ret; +} + +real_t Vector4::distance_squared_to(const Vector4 &p_to) const { + return (p_to - *this).length_squared(); +} + +Vector4 Vector4::abs() const { + return Vector4(Math::abs(x), Math::abs(y), Math::abs(z), Math::abs(w)); +} + +Vector4 Vector4::sign() const { + return Vector4(SGN(x), SGN(y), SGN(z), SGN(w)); +} + +Vector4 Vector4::floor() const { + return Vector4(Math::floor(x), Math::floor(y), Math::floor(z), Math::floor(w)); +} + +Vector4 Vector4::ceil() const { + return Vector4(Math::ceil(x), Math::ceil(y), Math::ceil(z), Math::ceil(w)); +} + +Vector4 Vector4::round() const { + return Vector4(Math::round(x), Math::round(y), Math::round(z), Math::round(w)); +} + +Vector4 Vector4::linear_interpolate(const Vector4 &p_to, const real_t p_weight) const { + return Vector4( + x + (p_weight * (p_to.x - x)), + y + (p_weight * (p_to.y - y)), + z + (p_weight * (p_to.z - z)), + w + (p_weight * (p_to.w - w))); +} + +Vector4 Vector4::cubic_interpolate(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, const real_t p_weight) const { + Vector4 res = *this; + res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight); + res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight); + res.z = Math::cubic_interpolate(res.z, p_b.z, p_pre_a.z, p_post_b.z, p_weight); + res.w = Math::cubic_interpolate(res.w, p_b.w, p_pre_a.w, p_post_b.w, p_weight); + return res; +} + +Vector4 Vector4::posmod(const real_t p_mod) const { + return Vector4(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod), Math::fposmod(z, p_mod), Math::fposmod(w, p_mod)); +} + +Vector4 Vector4::posmodv(const Vector4 &p_modv) const { + return Vector4(Math::fposmod(x, p_modv.x), Math::fposmod(y, p_modv.y), Math::fposmod(z, p_modv.z), Math::fposmod(w, p_modv.w)); +} + +void Vector4::snap(const Vector4 &p_step) { + x = Math::stepify(x, p_step.x); + y = Math::stepify(y, p_step.y); + z = Math::stepify(z, p_step.z); + w = Math::stepify(w, p_step.w); +} + +Vector4 Vector4::snapped(const Vector4 &p_step) const { + Vector4 v = *this; + v.snap(p_step); + return v; +} + +Vector4 Vector4::inverse() const { + return Vector4(1.0f / x, 1.0f / y, 1.0f / z, 1.0f / w); +} + +Vector4 Vector4::clamp(const Vector4 &p_min, const Vector4 &p_max) const { + return Vector4( + CLAMP(x, p_min.x, p_max.x), + CLAMP(y, p_min.y, p_max.y), + CLAMP(z, p_min.z, p_max.z), + CLAMP(w, p_min.w, p_max.w)); +} + +Vector4::operator String() const { + return "(" + String::num_real(x) + ", " + String::num_real(y) + ", " + String::num_real(z) + ", " + String::num_real(w) + ")"; +} +#line 0 + + +#line 1 "sfwl/core/file_access.cpp" + + + + +String FileAccess::read_file(const String &path) { + FILE *f = fopen(path.utf8().get_data(), "r"); + + ERR_FAIL_COND_V_MSG(!f, String(), "Error opening file! " + path); + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); /* same as rewind(f); */ + + CharString cs; + cs.resize(fsize + 1); // +1 for the null terminator + + fread(cs.ptrw(), 1, fsize, f); + fclose(f); + + return String::utf8(cs.ptr()); +} + +Vector FileAccess::read_file_bin(const String &path) { + FILE *f = fopen(path.utf8().get_data(), "rb"); + + Vector fd; + + ERR_FAIL_COND_V_MSG(!f, fd, "Error opening file! " + path); + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); /* same as rewind(f); */ + + fd.resize(fsize); + + fread(fd.ptrw(), 1, fsize, f); + fclose(f); + + return fd; +} + +Error FileAccess::read_file_into_bin(const String &path, Vector *data) { + if (!data) { + return ERR_PARAMETER_RANGE_ERROR; + } + + FILE *f = fopen(path.utf8().get_data(), "rb"); + + if (!f) { + return ERR_FILE_CANT_OPEN; + } + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); /* same as rewind(f); */ + + data->resize(fsize); + + fread(data->ptrw(), 1, fsize, f); + fclose(f); + + return OK; +} + +Error FileAccess::write_file(const String &path, const String &str) { + FILE *f = fopen(path.utf8().get_data(), "w"); + + if (!f) { + return ERR_FILE_CANT_OPEN; + } + + fwrite(str.utf8().ptr(), sizeof(char), str.size(), f); + fclose(f); + + return OK; +} + +Error FileAccess::write_file_bin(const String &path, const Vector &data) { + FILE *f = fopen(path.utf8().get_data(), "wb"); + + if (!f) { + return ERR_FILE_CANT_OPEN; + } + + fwrite(data.ptr(), sizeof(uint8_t), data.size(), f); + fclose(f); + + return OK; +} + +FileAccess::FileAccess() { +} +FileAccess::~FileAccess() { +} +#line 0 + +#line 1 "sfwl/core/3rd_tinydir.h" +/* +Copyright (c) 2013-2019, tinydir authors: +- Cong Xu +- Lautis Sun +- Baudouin Feildel +- Andargor +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef TINYDIR_H +#define TINYDIR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if ((defined _UNICODE) && !(defined UNICODE)) +#define UNICODE +#endif + +#if ((defined UNICODE) && !(defined _UNICODE)) +#define _UNICODE +#endif + +#include +#include +#include +#ifdef _MSC_VER +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# include +# pragma warning(push) +# pragma warning (disable : 4996) +#else +# include +# include +# include +# include +#endif +#ifdef __MINGW32__ +# include +#endif + + +/* types */ + +/* Windows UNICODE wide character support */ +#if defined _MSC_VER || defined __MINGW32__ +# define _tinydir_char_t TCHAR +# define TINYDIR_STRING(s) _TEXT(s) +# define _tinydir_strlen _tcslen +# define _tinydir_strcpy _tcscpy +# define _tinydir_strcat _tcscat +# define _tinydir_strcmp _tcscmp +# define _tinydir_strrchr _tcsrchr +# define _tinydir_strncmp _tcsncmp +#else +# define _tinydir_char_t char +# define TINYDIR_STRING(s) s +# define _tinydir_strlen strlen +# define _tinydir_strcpy strcpy +# define _tinydir_strcat strcat +# define _tinydir_strcmp strcmp +# define _tinydir_strrchr strrchr +# define _tinydir_strncmp strncmp +#endif + +#if (defined _MSC_VER || defined __MINGW32__) +# include +# define _TINYDIR_PATH_MAX MAX_PATH +#elif defined __linux__ +# include +# ifdef PATH_MAX +# define _TINYDIR_PATH_MAX PATH_MAX +# endif +#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +# include +# if defined(BSD) +# include +# ifdef PATH_MAX +# define _TINYDIR_PATH_MAX PATH_MAX +# endif +# endif +#endif + +#ifndef _TINYDIR_PATH_MAX +#define _TINYDIR_PATH_MAX 4096 +#endif + +#ifdef _MSC_VER +/* extra chars for the "\\*" mask */ +# define _TINYDIR_PATH_EXTRA 2 +#else +# define _TINYDIR_PATH_EXTRA 0 +#endif + +#define _TINYDIR_FILENAME_MAX 256 + +#if (defined _MSC_VER || defined __MINGW32__) +#define _TINYDIR_DRIVE_MAX 3 +#endif + +#ifdef _MSC_VER +# define _TINYDIR_FUNC static __inline +#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L +# define _TINYDIR_FUNC static __inline__ +#else +# define _TINYDIR_FUNC static inline +#endif + +/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */ +#ifdef TINYDIR_USE_READDIR_R + +/* readdir_r is a POSIX-only function, and may not be available under various + * environments/settings, e.g. MinGW. Use readdir fallback */ +#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\ + _POSIX_SOURCE +# define _TINYDIR_HAS_READDIR_R +#endif +#if _POSIX_C_SOURCE >= 200112L +# define _TINYDIR_HAS_FPATHCONF +# include +#endif +#if _BSD_SOURCE || _SVID_SOURCE || \ + (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700) +# define _TINYDIR_HAS_DIRFD +# include +#endif +#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\ + defined _PC_NAME_MAX +# define _TINYDIR_USE_FPATHCONF +#endif +#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\ + !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX) +# define _TINYDIR_USE_READDIR +#endif + +/* Use readdir by default */ +#else +# define _TINYDIR_USE_READDIR +#endif + +/* MINGW32 has two versions of dirent, ASCII and UNICODE*/ +#ifndef _MSC_VER +#if (defined __MINGW32__) && (defined _UNICODE) +#define _TINYDIR_DIR _WDIR +#define _tinydir_dirent _wdirent +#define _tinydir_opendir _wopendir +#define _tinydir_readdir _wreaddir +#define _tinydir_closedir _wclosedir +#else +#define _TINYDIR_DIR DIR +#define _tinydir_dirent dirent +#define _tinydir_opendir opendir +#define _tinydir_readdir readdir +#define _tinydir_closedir closedir +#endif +#endif + +/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */ +#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE) +#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE) +#else +#error "Either define both alloc and free or none of them!" +#endif + +#if !defined(_TINYDIR_MALLOC) + #define _TINYDIR_MALLOC(_size) malloc(_size) + #define _TINYDIR_FREE(_ptr) free(_ptr) +#endif /* !defined(_TINYDIR_MALLOC) */ + +typedef struct tinydir_file +{ + _tinydir_char_t path[_TINYDIR_PATH_MAX]; + _tinydir_char_t name[_TINYDIR_FILENAME_MAX]; + _tinydir_char_t *extension; + int is_dir; + int is_reg; + +#ifndef _MSC_VER +#ifdef __MINGW32__ + struct _stat _s; +#else + struct stat _s; +#endif +#endif +} tinydir_file; + +typedef struct tinydir_dir +{ + _tinydir_char_t path[_TINYDIR_PATH_MAX]; + int has_next; + size_t n_files; + + tinydir_file *_files; +#ifdef _MSC_VER + HANDLE _h; + WIN32_FIND_DATA _f; +#else + _TINYDIR_DIR *_d; + struct _tinydir_dirent *_e; +#ifndef _TINYDIR_USE_READDIR + struct _tinydir_dirent *_ep; +#endif +#endif +} tinydir_dir; + + +/* declarations */ + +_TINYDIR_FUNC +int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path); +_TINYDIR_FUNC +int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path); +_TINYDIR_FUNC +void tinydir_close(tinydir_dir *dir); + +_TINYDIR_FUNC +int tinydir_next(tinydir_dir *dir); +_TINYDIR_FUNC +int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file); +_TINYDIR_FUNC +int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i); +_TINYDIR_FUNC +int tinydir_open_subdir_n(tinydir_dir *dir, size_t i); + +_TINYDIR_FUNC +int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path); +_TINYDIR_FUNC +void _tinydir_get_ext(tinydir_file *file); +_TINYDIR_FUNC +int _tinydir_file_cmp(const void *a, const void *b); +#ifndef _MSC_VER +#ifndef _TINYDIR_USE_READDIR +_TINYDIR_FUNC +size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp); +#endif +#endif + + +/* definitions*/ + +_TINYDIR_FUNC +int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path) +{ +#ifndef _MSC_VER +#ifndef _TINYDIR_USE_READDIR + int error; + int size; /* using int size */ +#endif +#else + _tinydir_char_t path_buf[_TINYDIR_PATH_MAX]; +#endif + _tinydir_char_t *pathp; + + if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0) + { + errno = EINVAL; + return -1; + } + if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + /* initialise dir */ + dir->_files = NULL; +#ifdef _MSC_VER + dir->_h = INVALID_HANDLE_VALUE; +#else + dir->_d = NULL; +#ifndef _TINYDIR_USE_READDIR + dir->_ep = NULL; +#endif +#endif + tinydir_close(dir); + + _tinydir_strcpy(dir->path, path); + /* Remove trailing slashes */ + pathp = &dir->path[_tinydir_strlen(dir->path) - 1]; + while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/'))) + { + *pathp = TINYDIR_STRING('\0'); + pathp++; + } +#ifdef _MSC_VER + _tinydir_strcpy(path_buf, dir->path); + _tinydir_strcat(path_buf, TINYDIR_STRING("\\*")); +#if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP) + dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0); +#else + dir->_h = FindFirstFile(path_buf, &dir->_f); +#endif + if (dir->_h == INVALID_HANDLE_VALUE) + { + errno = ENOENT; +#else + dir->_d = _tinydir_opendir(path); + if (dir->_d == NULL) + { +#endif + goto bail; + } + + /* read first file */ + dir->has_next = 1; +#ifndef _MSC_VER +#ifdef _TINYDIR_USE_READDIR + dir->_e = _tinydir_readdir(dir->_d); +#else + /* allocate dirent buffer for readdir_r */ + size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */ + if (size == -1) return -1; + dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size); + if (dir->_ep == NULL) return -1; + + error = readdir_r(dir->_d, dir->_ep, &dir->_e); + if (error != 0) return -1; +#endif + if (dir->_e == NULL) + { + dir->has_next = 0; + } +#endif + + return 0; + +bail: + tinydir_close(dir); + return -1; +} + +_TINYDIR_FUNC +int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path) +{ + /* Count the number of files first, to pre-allocate the files array */ + size_t n_files = 0; + if (tinydir_open(dir, path) == -1) + { + return -1; + } + while (dir->has_next) + { + n_files++; + if (tinydir_next(dir) == -1) + { + goto bail; + } + } + tinydir_close(dir); + + if (n_files == 0 || tinydir_open(dir, path) == -1) + { + return -1; + } + + dir->n_files = 0; + dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files); + if (dir->_files == NULL) + { + goto bail; + } + while (dir->has_next) + { + tinydir_file *p_file; + dir->n_files++; + + p_file = &dir->_files[dir->n_files - 1]; + if (tinydir_readfile(dir, p_file) == -1) + { + goto bail; + } + + if (tinydir_next(dir) == -1) + { + goto bail; + } + + /* Just in case the number of files has changed between the first and + second reads, terminate without writing into unallocated memory */ + if (dir->n_files == n_files) + { + break; + } + } + + qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp); + + return 0; + +bail: + tinydir_close(dir); + return -1; +} + +_TINYDIR_FUNC +void tinydir_close(tinydir_dir *dir) +{ + if (dir == NULL) + { + return; + } + + memset(dir->path, 0, sizeof(dir->path)); + dir->has_next = 0; + dir->n_files = 0; + _TINYDIR_FREE(dir->_files); + dir->_files = NULL; +#ifdef _MSC_VER + if (dir->_h != INVALID_HANDLE_VALUE) + { + FindClose(dir->_h); + } + dir->_h = INVALID_HANDLE_VALUE; +#else + if (dir->_d) + { + _tinydir_closedir(dir->_d); + } + dir->_d = NULL; + dir->_e = NULL; +#ifndef _TINYDIR_USE_READDIR + _TINYDIR_FREE(dir->_ep); + dir->_ep = NULL; +#endif +#endif +} + +_TINYDIR_FUNC +int tinydir_next(tinydir_dir *dir) +{ + if (dir == NULL) + { + errno = EINVAL; + return -1; + } + if (!dir->has_next) + { + errno = ENOENT; + return -1; + } + +#ifdef _MSC_VER + if (FindNextFile(dir->_h, &dir->_f) == 0) +#else +#ifdef _TINYDIR_USE_READDIR + dir->_e = _tinydir_readdir(dir->_d); +#else + if (dir->_ep == NULL) + { + return -1; + } + if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0) + { + return -1; + } +#endif + if (dir->_e == NULL) +#endif + { + dir->has_next = 0; +#ifdef _MSC_VER + if (GetLastError() != ERROR_SUCCESS && + GetLastError() != ERROR_NO_MORE_FILES) + { + tinydir_close(dir); + errno = EIO; + return -1; + } +#endif + } + + return 0; +} + +_TINYDIR_FUNC +int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file) +{ + const _tinydir_char_t *filename; + if (dir == NULL || file == NULL) + { + errno = EINVAL; + return -1; + } +#ifdef _MSC_VER + if (dir->_h == INVALID_HANDLE_VALUE) +#else + if (dir->_e == NULL) +#endif + { + errno = ENOENT; + return -1; + } + filename = +#ifdef _MSC_VER + dir->_f.cFileName; +#else + dir->_e->d_name; +#endif + if (_tinydir_strlen(dir->path) + + _tinydir_strlen(filename) + 1 + _TINYDIR_PATH_EXTRA >= + _TINYDIR_PATH_MAX) + { + /* the path for the file will be too long */ + errno = ENAMETOOLONG; + return -1; + } + if (_tinydir_strlen(filename) >= _TINYDIR_FILENAME_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + _tinydir_strcpy(file->path, dir->path); + if (_tinydir_strcmp(dir->path, TINYDIR_STRING("/")) != 0) + _tinydir_strcat(file->path, TINYDIR_STRING("/")); + _tinydir_strcpy(file->name, filename); + _tinydir_strcat(file->path, filename); +#ifndef _MSC_VER +#ifdef __MINGW32__ + if (_tstat( +#elif (defined _BSD_SOURCE) || (defined _DEFAULT_SOURCE) \ + || ((defined _XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) \ + || ((defined _POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) + if (lstat( +#else + if (stat( +#endif + file->path, &file->_s) == -1) + { + return -1; + } +#endif + _tinydir_get_ext(file); + + file->is_dir = +#ifdef _MSC_VER + !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); +#else + S_ISDIR(file->_s.st_mode); +#endif + file->is_reg = +#ifdef _MSC_VER + !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || + ( + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) && + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && +#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) && +#endif +#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) && +#endif + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) && + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY)); +#else + S_ISREG(file->_s.st_mode); +#endif + + return 0; +} + +_TINYDIR_FUNC +int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i) +{ + if (dir == NULL || file == NULL) + { + errno = EINVAL; + return -1; + } + if (i >= dir->n_files) + { + errno = ENOENT; + return -1; + } + + memcpy(file, &dir->_files[i], sizeof(tinydir_file)); + _tinydir_get_ext(file); + + return 0; +} + +_TINYDIR_FUNC +int tinydir_open_subdir_n(tinydir_dir *dir, size_t i) +{ + _tinydir_char_t path[_TINYDIR_PATH_MAX]; + if (dir == NULL) + { + errno = EINVAL; + return -1; + } + if (i >= dir->n_files || !dir->_files[i].is_dir) + { + errno = ENOENT; + return -1; + } + + _tinydir_strcpy(path, dir->_files[i].path); + tinydir_close(dir); + if (tinydir_open_sorted(dir, path) == -1) + { + return -1; + } + + return 0; +} + +/* Open a single file given its path */ +_TINYDIR_FUNC +int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path) +{ + tinydir_dir dir; + int result = 0; + int found = 0; + _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX]; + _tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX]; + _tinydir_char_t *dir_name; + _tinydir_char_t *base_name; +#if (defined _MSC_VER || defined __MINGW32__) + _tinydir_char_t drive_buf[_TINYDIR_PATH_MAX]; + _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX]; +#endif + + if (file == NULL || path == NULL || _tinydir_strlen(path) == 0) + { + errno = EINVAL; + return -1; + } + if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + /* Get the parent path */ +#if (defined _MSC_VER || defined __MINGW32__) +#if ((defined _MSC_VER) && (_MSC_VER >= 1400)) + errno = _tsplitpath_s( + path, + drive_buf, _TINYDIR_DRIVE_MAX, + dir_name_buf, _TINYDIR_FILENAME_MAX, + file_name_buf, _TINYDIR_FILENAME_MAX, + ext_buf, _TINYDIR_FILENAME_MAX); +#else + _tsplitpath( + path, + drive_buf, + dir_name_buf, + file_name_buf, + ext_buf); +#endif + + if (errno) + { + return -1; + } + +/* _splitpath_s not work fine with only filename and widechar support */ +#ifdef _UNICODE + if (drive_buf[0] == L'\xFEFE') + drive_buf[0] = '\0'; + if (dir_name_buf[0] == L'\xFEFE') + dir_name_buf[0] = '\0'; +#endif + + /* Emulate the behavior of dirname by returning "." for dir name if it's + empty */ + if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0') + { + _tinydir_strcpy(dir_name_buf, TINYDIR_STRING(".")); + } + /* Concatenate the drive letter and dir name to form full dir name */ + _tinydir_strcat(drive_buf, dir_name_buf); + dir_name = drive_buf; + /* Concatenate the file name and extension to form base name */ + _tinydir_strcat(file_name_buf, ext_buf); + base_name = file_name_buf; +#else + _tinydir_strcpy(dir_name_buf, path); + dir_name = dirname(dir_name_buf); + _tinydir_strcpy(file_name_buf, path); + base_name = basename(file_name_buf); +#endif + + /* Special case: if the path is a root dir, open the parent dir as the file */ +#if (defined _MSC_VER || defined __MINGW32__) + if (_tinydir_strlen(base_name) == 0) +#else + if ((_tinydir_strcmp(base_name, TINYDIR_STRING("/"))) == 0) +#endif + { + memset(file, 0, sizeof * file); + file->is_dir = 1; + file->is_reg = 0; + _tinydir_strcpy(file->path, dir_name); + file->extension = file->path + _tinydir_strlen(file->path); + return 0; + } + + /* Open the parent directory */ + if (tinydir_open(&dir, dir_name) == -1) + { + return -1; + } + + /* Read through the parent directory and look for the file */ + while (dir.has_next) + { + if (tinydir_readfile(&dir, file) == -1) + { + result = -1; + goto bail; + } + if (_tinydir_strcmp(file->name, base_name) == 0) + { + /* File found */ + found = 1; + break; + } + tinydir_next(&dir); + } + if (!found) + { + result = -1; + errno = ENOENT; + } + +bail: + tinydir_close(&dir); + return result; +} + +_TINYDIR_FUNC +void _tinydir_get_ext(tinydir_file *file) +{ + _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.')); + if (period == NULL) + { + file->extension = &(file->name[_tinydir_strlen(file->name)]); + } + else + { + file->extension = period + 1; + } +} + +_TINYDIR_FUNC +int _tinydir_file_cmp(const void *a, const void *b) +{ + const tinydir_file *fa = (const tinydir_file *)a; + const tinydir_file *fb = (const tinydir_file *)b; + if (fa->is_dir != fb->is_dir) + { + return -(fa->is_dir - fb->is_dir); + } + return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX); +} + +#ifndef _MSC_VER +#ifndef _TINYDIR_USE_READDIR +/* +The following authored by Ben Hutchings +from https://womble.decadent.org.uk/readdir_r-advisory.html +*/ +/* Calculate the required buffer size (in bytes) for directory * +* entries read from the given directory handle. Return -1 if this * +* this cannot be done. * +* * +* This code does not trust values of NAME_MAX that are less than * +* 255, since some systems (including at least HP-UX) incorrectly * +* define it to be a smaller value. */ +_TINYDIR_FUNC +size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp) +{ + long name_max; + size_t name_end; + /* parameter may be unused */ + (void)dirp; + +#if defined _TINYDIR_USE_FPATHCONF + name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX); + if (name_max == -1) +#if defined(NAME_MAX) + name_max = (NAME_MAX > 255) ? NAME_MAX : 255; +#else + return (size_t)(-1); +#endif +#elif defined(NAME_MAX) + name_max = (NAME_MAX > 255) ? NAME_MAX : 255; +#else +#error "buffer size for readdir_r cannot be determined" +#endif + name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1; + return (name_end > sizeof(struct _tinydir_dirent) ? + name_end : sizeof(struct _tinydir_dirent)); +} +#endif +#endif + +#ifdef __cplusplus +} +#endif + +# if defined (_MSC_VER) +# pragma warning(pop) +# endif + +#endif +#line 0 + + +#line 1 "sfwl/core/dir_access.cpp" + + + +Error DirAccess::open_dir(const String &path, bool skip_specials) { + if (_dir_open) { + return ERR_CANT_ACQUIRE_RESOURCE; + } + + _skip_specials = skip_specials; + + if (tinydir_open(_dir, path.utf8().get_data()) == -1) { + return FAILED; + } + + _dir_open = true; + + return OK; +} + +Error DirAccess::open_dir(const char *path, bool skip_specials) { + if (_dir_open) { + return ERR_CANT_ACQUIRE_RESOURCE; + } + + _skip_specials = skip_specials; + + if (tinydir_open(_dir, path) == -1) { + return FAILED; + } + + _dir_open = true; + + return OK; +} + +void DirAccess::close_dir() { + if (!_dir_open) { + return; + } + + tinydir_close(_dir); + + _dir_open = false; +} + +bool DirAccess::has_next() { + if (!_dir) { + return false; + } + + return _dir->has_next; +} +bool DirAccess::read() { + _read_file_result = tinydir_readfile(_dir, _file); + + return _read_file_result != -1; +} +bool DirAccess::next() { + if (!_dir->has_next) { + return false; + } + + bool rres = read(); + while (!rres && _dir->has_next) { + tinydir_next(_dir); + rres = read(); + } + + if (!rres) { + return false; + } + + if (_dir->has_next) { + tinydir_next(_dir); + } + + if (_skip_specials && current_is_dir() && current_is_special_dir()) { + return next(); + } + + return true; +} + +bool DirAccess::current_is_ok() { + return _read_file_result == 01; +} +String DirAccess::current_get_name() { + return String(_file->name); +} +String DirAccess::current_get_path() { + return String(_file->path); +} +String DirAccess::current_get_extension() { + return String(_file->extension); +} +const char *DirAccess::current_get_name_cstr() { + return _file->name; +} +const char *DirAccess::current_get_path_cstr() { + return _file->path; +} +const char *DirAccess::current_get_extension_cstr() { + return _file->extension; +} +bool DirAccess::current_is_file() { + return !_file->is_dir; +} +bool DirAccess::current_is_dir() { + return _file->is_dir; +} +bool DirAccess::current_is_special_dir() { + if ((_file->name[0] == '.' && _file->name[1] == '\0') || (_file->name[0] == '.' && _file->name[1] == '.')) { + return true; + } + + return false; +} + +bool DirAccess::is_dir_open() { + return _dir_open; +} +bool DirAccess::is_dir_closed() { + return !_dir_open; +} + +DirAccess::DirAccess() { + _skip_specials = true; + _read_file_result = 0; + _dir_open = false; + _dir = memnew(tinydir_dir); + _file = memnew(tinydir_file); +} +DirAccess::~DirAccess() { + if (is_dir_open()) { + close_dir(); + } + + memdelete(_dir); + memdelete(_file); +} +#line 0 + + +#line 1 "sfwl/core/sfw_core.cpp" + + + +void SFWCore::setup() { + if (_initialized) { + return; + } + + _initialized = true; + + StringName::setup(); + MemoryPool::setup(); +} + +void SFWCore::cleanup() { + if (!_initialized) { + return; + } + + _initialized = false; + + StringName::cleanup(); + MemoryPool::cleanup(); +} + +bool SFWCore::_initialized = false; +#line 0 + + +//=================== OBJECT SECTION =================== + + +#line 1 "sfwl/object/resource.cpp" + + +void Resource::emit_changed() { + changed.emit(this); +} + +Error Resource::load(const String &path) { + return ERR_UNAVAILABLE; +} +Error Resource::save(const String &path) { + return ERR_UNAVAILABLE; +} + +Resource::Resource() : + Reference() { +} + +Resource::~Resource() { +} +#line 0 + +#line 1 "sfwl/object/reference.cpp" + + +/*************************************************************************/ +/* reference.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +bool Reference::init_ref() { + if (reference()) { + if (!is_referenced() && refcount_init.unref()) { + unreference(); // first referencing is already 1, so compensate for the ref above + } + + return true; + } else { + return false; + } +} + +int Reference::reference_get_count() const { + return refcount.get(); +} + +bool Reference::reference() { + uint32_t rc_val = refcount.refval(); + bool success = rc_val != 0; + + return success; +} + +bool Reference::unreference() { + uint32_t rc_val = refcount.unrefval(); + bool die = rc_val == 0; + + return die; +} + +Reference::Reference() : + Object() { + refcount.init(); + refcount_init.init(); +} + +Reference::~Reference() { +} + +/* +void WeakRef::set_obj(Object *p_object) { + //ref = p_object ? p_object->get_instance_id() : 0; +} + +void WeakRef::set_ref(const REF &p_ref) { + //ref = p_ref.is_valid() ? p_ref->get_instance_id() : 0; +} + +WeakRef::WeakRef() : + ref(0) { +} +*/#line 0 + +#line 1 "sfwl/object/object.cpp" + + +void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid) { + if (p_name == CoreStringNames::get_singleton()->_meta) { + //set_meta(p_name,p_value); + metadata = p_value.duplicate(); + if (r_valid) { + *r_valid = true; + } + return; + } + + //something inside the object... :| + bool success = _setv(p_name, p_value); + if (success) { + if (r_valid) { + *r_valid = true; + } + return; + } + + if (r_valid) { + *r_valid = false; + } +} + +Variant Object::get(const StringName &p_name, bool *r_valid) const { + Variant ret; + + if (p_name == CoreStringNames::get_singleton()->_meta) { + ret = metadata; + if (r_valid) { + *r_valid = true; + } + return ret; + + } else { + //something inside the object... :| + bool success = _getv(p_name, ret); + if (success) { + if (r_valid) { + *r_valid = true; + } + return ret; + } + + if (r_valid) { + *r_valid = false; + } + + return Variant(); + } +} + +bool Object::lt(const Variant &p_value_l, const Variant &p_value_r) { + return p_value_l < p_value_r; +} + +void Object::notification(int p_notification, bool p_reversed) { + _notificationv(p_notification, p_reversed); +} + +String Object::to_string() { + return "[" + get_class() + ":" + itos(get_instance_id()) + "]"; +} + +bool Object::_predelete() { + _predelete_ok = 1; + notification(NOTIFICATION_PREDELETE, true); + if (_predelete_ok) { + _class_ptr = nullptr; //must restore so destructors can access class ptr correctly + } + return _predelete_ok; +} + +void Object::_postinitialize() { + _class_ptr = _get_class_namev(); + notification(NOTIFICATION_POSTINITIALIZE); +} + +bool Object::has_meta(const String &p_name) const { + return metadata.has(p_name); +} + +void Object::set_meta(const String &p_name, const Variant &p_value) { + if (p_value.get_type() == Variant::NIL) { + metadata.erase(p_name); + return; + }; + + metadata[p_name] = p_value; +} + +Variant Object::get_meta(const String &p_name, const Variant &p_default) const { + if (!metadata.has(p_name)) { + return p_default; + } + return metadata[p_name]; +} + +void Object::remove_meta(const String &p_name) { + metadata.erase(p_name); +} + +void Object::cancel_free() { + _predelete_ok = 0; +} + +Object::Object() { + _is_queued_for_deletion = false; + _predelete_ok = 0; + _instance_id = 0; + _instance_id = ObjectDB::add_instance(this); +} + +Object::~Object() { +} + +ObjectRC *Object::_use_rc() { + // The RC object is lazily created the first time it's requested; + // that way, there's no need to allocate and release it at all if this Object + // is not being referred by any Variant at all. + + // Although when dealing with Objects from multiple threads some locking + // mechanism should be used, this at least makes safe the case of first + // assignment. + + ObjectRC *rc = nullptr; + ObjectRC *const creating = reinterpret_cast(1); + if (unlikely(_rc.compare_exchange_strong(rc, creating, std::memory_order_acq_rel))) { + // Not created yet + rc = memnew(ObjectRC(this)); + _rc.store(rc, std::memory_order_release); + return rc; + } + + // Spin-wait until we know it's created (or just return if it's already created) + for (;;) { + if (likely(rc != creating)) { + rc->increment(); + return rc; + } + rc = _rc.load(std::memory_order_acquire); + } +} + +bool predelete_handler(Object *p_object) { + return p_object->_predelete(); +} + +void postinitialize_handler(Object *p_object) { + p_object->_postinitialize(); +} + +HashMap ObjectDB::instances; +ObjectID ObjectDB::instance_counter = 1; +HashMap ObjectDB::instance_checks; +ObjectID ObjectDB::add_instance(Object *p_object) { + ERR_FAIL_COND_V(p_object->get_instance_id() != 0, 0); + + rw_lock.write_lock(); + ObjectID instance_id = ++instance_counter; + instances[instance_id] = p_object; + instance_checks[p_object] = instance_id; + + rw_lock.write_unlock(); + + return instance_id; +} + +void ObjectDB::remove_instance(Object *p_object) { + rw_lock.write_lock(); + + instances.erase(p_object->get_instance_id()); + instance_checks.erase(p_object); + + rw_lock.write_unlock(); +} +Object *ObjectDB::get_instance(ObjectID p_instance_id) { + rw_lock.read_lock(); + Object **obj = instances.getptr(p_instance_id); + rw_lock.read_unlock(); + + if (!obj) { + return nullptr; + } + return *obj; +} + +void ObjectDB::debug_objects(DebugFunc p_func) { + rw_lock.read_lock(); + + const ObjectID *K = nullptr; + while ((K = instances.next(K))) { + p_func(instances[*K]); + } + + rw_lock.read_unlock(); +} + +int ObjectDB::get_object_count() { + rw_lock.read_lock(); + int count = instances.size(); + rw_lock.read_unlock(); + + return count; +} + +RWLock ObjectDB::rw_lock; + +void ObjectDB::cleanup() { + rw_lock.write_lock(); + if (instances.size()) { + LOG_WARN("ObjectDB instances leaked at exit!"); + } + instances.clear(); + instance_checks.clear(); + rw_lock.write_unlock(); +} +#line 0 + +#line 1 "sfwl/object/ref_ptr.cpp" +/*************************************************************************/ +/* ref_ptr.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +void RefPtr::operator=(const RefPtr &p_other) { + Ref *ref = reinterpret_cast *>(&data[0]); + Ref *ref_other = reinterpret_cast *>(const_cast(&p_other.data[0])); + + *ref = *ref_other; +} + +bool RefPtr::operator==(const RefPtr &p_other) const { + Ref *ref = reinterpret_cast *>(&data[0]); + Ref *ref_other = reinterpret_cast *>(const_cast(&p_other.data[0])); + + return *ref == *ref_other; +} + +bool RefPtr::operator!=(const RefPtr &p_other) const { + Ref *ref = reinterpret_cast *>(&data[0]); + Ref *ref_other = reinterpret_cast *>(const_cast(&p_other.data[0])); + + return *ref != *ref_other; +} + +RefPtr::RefPtr(const RefPtr &p_other) { + memnew_placement(&data[0], Ref); + + Ref *ref = reinterpret_cast *>(&data[0]); + Ref *ref_other = reinterpret_cast *>(const_cast(&p_other.data[0])); + + *ref = *ref_other; +} + +bool RefPtr::is_null() const { + Ref *ref = reinterpret_cast *>(&data[0]); + return ref->is_null(); +} + +void RefPtr::unref() { + Ref *ref = reinterpret_cast *>(&data[0]); + ref->unref(); +} + +RefPtr::RefPtr() { + ERR_FAIL_COND(sizeof(Ref) > DATASIZE); + memnew_placement(&data[0], Ref); +} + +RefPtr::~RefPtr() { + Ref *ref = reinterpret_cast *>(&data[0]); + ref->~Ref(); +} +#line 0 + +#line 1 "sfwl/object/core_string_names.cpp" +/*************************************************************************/ +/* core_string_names.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +CoreStringNames *CoreStringNames::singleton = nullptr; + +CoreStringNames::CoreStringNames() : + _free(StaticCString::create("free")), + changed(StaticCString::create("changed")), + _meta(StaticCString::create("__meta__")), + _script(StaticCString::create("script")), + script_changed(StaticCString::create("script_changed")), + ___pdcdata(StaticCString::create("___pdcdata")), + __getvar(StaticCString::create("__getvar")), + _iter_init(StaticCString::create("_iter_init")), + _iter_next(StaticCString::create("_iter_next")), + _iter_get(StaticCString::create("_iter_get")), + get_rid(StaticCString::create("get_rid")), + _to_string(StaticCString::create("_to_string")), +#ifdef TOOLS_ENABLED + _sections_unfolded(StaticCString::create("_sections_unfolded")), +#endif + _custom_features(StaticCString::create("_custom_features")), + x(StaticCString::create("x")), + y(StaticCString::create("y")), + z(StaticCString::create("z")), + w(StaticCString::create("w")), + r(StaticCString::create("r")), + g(StaticCString::create("g")), + b(StaticCString::create("b")), + a(StaticCString::create("a")), + position(StaticCString::create("position")), + size(StaticCString::create("size")), + end(StaticCString::create("end")), + basis(StaticCString::create("basis")), + origin(StaticCString::create("origin")), + normal(StaticCString::create("normal")), + d(StaticCString::create("d")), + h(StaticCString::create("h")), + s(StaticCString::create("s")), + v(StaticCString::create("v")), + r8(StaticCString::create("r8")), + g8(StaticCString::create("g8")), + b8(StaticCString::create("b8")), + a8(StaticCString::create("a8")) { +} +#line 0 + +#line 1 "sfwl/object/dictionary.cpp" +/*************************************************************************/ +/* dictionary.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct DictionaryPrivate { + SafeRefCount refcount; + OrderedHashMap variant_map; +}; + +void Dictionary::get_key_list(List *p_keys) const { + if (_p->variant_map.empty()) { + return; + } + + for (OrderedHashMap::Element E = _p->variant_map.front(); E; E = E.next()) { + p_keys->push_back(E.key()); + } +} + +Variant Dictionary::get_key_at_index(int p_index) const { + int index = 0; + for (OrderedHashMap::Element E = _p->variant_map.front(); E; E = E.next()) { + if (index == p_index) { + return E.key(); + } + index++; + } + + return Variant(); +} + +Variant Dictionary::get_value_at_index(int p_index) const { + int index = 0; + for (OrderedHashMap::Element E = _p->variant_map.front(); E; E = E.next()) { + if (index == p_index) { + return E.value(); + } + index++; + } + + return Variant(); +} + +Variant &Dictionary::operator[](const Variant &p_key) { + return _p->variant_map[p_key]; +} + +const Variant &Dictionary::operator[](const Variant &p_key) const { + return _p->variant_map[p_key]; +} +const Variant *Dictionary::getptr(const Variant &p_key) const { + OrderedHashMap::ConstElement E = ((const OrderedHashMap *)&_p->variant_map)->find(p_key); + + if (!E) { + return nullptr; + } + return &E.get(); +} + +Variant *Dictionary::getptr(const Variant &p_key) { + OrderedHashMap::Element E = _p->variant_map.find(p_key); + + if (!E) { + return nullptr; + } + return &E.get(); +} + +Variant Dictionary::get_valid(const Variant &p_key) const { + OrderedHashMap::ConstElement E = ((const OrderedHashMap *)&_p->variant_map)->find(p_key); + + if (!E) { + return Variant(); + } + return E.get(); +} + +Variant Dictionary::get(const Variant &p_key, const Variant &p_default) const { + const Variant *result = getptr(p_key); + if (!result) { + return p_default; + } + + return *result; +} + +int Dictionary::size() const { + return _p->variant_map.size(); +} +bool Dictionary::empty() const { + return !_p->variant_map.size(); +} + +bool Dictionary::has(const Variant &p_key) const { + return _p->variant_map.has(p_key); +} + +bool Dictionary::has_all(const Array &p_keys) const { + for (int i = 0; i < p_keys.size(); i++) { + if (!has(p_keys[i])) { + return false; + } + } + return true; +} + +Variant Dictionary::find_key(const Variant &p_value) const { + for (OrderedHashMap::Element E = _p->variant_map.front(); E; E = E.next()) { + if (E.value() == p_value) { + return E.key(); + } + } + return Variant(); +} + +bool Dictionary::erase(const Variant &p_key) { + return _p->variant_map.erase(p_key); +} + +bool Dictionary::deep_equal(const Dictionary &p_dictionary, int p_recursion_count) const { + // Cheap checks + ERR_FAIL_COND_V_MSG(p_recursion_count > MAX_RECURSION, 0, "Max recursion reached"); + if (_p == p_dictionary._p) { + return true; + } + if (_p->variant_map.size() != p_dictionary._p->variant_map.size()) { + return false; + } + + // Heavy O(n) check + OrderedHashMap::Element this_E = _p->variant_map.front(); + OrderedHashMap::Element other_E = p_dictionary._p->variant_map.front(); + p_recursion_count++; + while (this_E && other_E) { + if ( + !this_E.key().deep_equal(other_E.key(), p_recursion_count) || + !this_E.value().deep_equal(other_E.value(), p_recursion_count)) { + return false; + } + + this_E = this_E.next(); + other_E = other_E.next(); + } + + return !this_E && !other_E; +} + +bool Dictionary::operator==(const Dictionary &p_dictionary) const { + return _p == p_dictionary._p; +} + +bool Dictionary::operator!=(const Dictionary &p_dictionary) const { + return _p != p_dictionary._p; +} + +void Dictionary::_ref(const Dictionary &p_from) const { + //make a copy first (thread safe) + if (!p_from._p->refcount.ref()) { + return; // couldn't copy + } + + //if this is the same, unreference the other one + if (p_from._p == _p) { + _p->refcount.unref(); + return; + } + if (_p) { + _unref(); + } + _p = p_from._p; +} + +void Dictionary::clear() { + _p->variant_map.clear(); +} + +void Dictionary::merge(const Dictionary &p_dictionary, bool p_overwrite) { + for (OrderedHashMap::Element E = p_dictionary._p->variant_map.front(); E; E = E.next()) { + if (p_overwrite || !has(E.key())) { + this->operator[](E.key()) = E.value(); + } + } +} + +void Dictionary::_unref() const { + ERR_FAIL_COND(!_p); + if (_p->refcount.unref()) { + memdelete(_p); + } + _p = nullptr; +} + +uint32_t Dictionary::hash() const { + return recursive_hash(0); +} + +uint32_t Dictionary::recursive_hash(int p_recursion_count) const { + ERR_FAIL_COND_V_MSG(p_recursion_count > MAX_RECURSION, 0, "Max recursion reached"); + p_recursion_count++; + + uint32_t h = hash_murmur3_one_32(Variant::DICTIONARY); + + for (OrderedHashMap::Element E = _p->variant_map.front(); E; E = E.next()) { + h = hash_murmur3_one_32(E.key().recursive_hash(p_recursion_count), h); + h = hash_murmur3_one_32(E.value().recursive_hash(p_recursion_count), h); + } + + return hash_fmix32(h); +} + +Array Dictionary::keys() const { + Array varr; + if (_p->variant_map.empty()) { + return varr; + } + + varr.resize(size()); + + int i = 0; + for (OrderedHashMap::Element E = _p->variant_map.front(); E; E = E.next()) { + varr[i] = E.key(); + i++; + } + + return varr; +} + +Array Dictionary::values() const { + Array varr; + if (_p->variant_map.empty()) { + return varr; + } + + varr.resize(size()); + + int i = 0; + for (OrderedHashMap::Element E = _p->variant_map.front(); E; E = E.next()) { + varr[i] = E.get(); + i++; + } + + return varr; +} + +const Variant *Dictionary::next(const Variant *p_key) const { + if (p_key == nullptr) { + // caller wants to get the first element + if (_p->variant_map.front()) { + return &_p->variant_map.front().key(); + } + return nullptr; + } + OrderedHashMap::Element E = _p->variant_map.find(*p_key); + + if (E && E.next()) { + return &E.next().key(); + } + return nullptr; +} + +Dictionary Dictionary::duplicate(bool p_deep) const { + Dictionary n; + + for (OrderedHashMap::Element E = _p->variant_map.front(); E; E = E.next()) { + n[E.key()] = p_deep ? E.value().duplicate(true) : E.value(); + } + + return n; +} + +void Dictionary::operator=(const Dictionary &p_dictionary) { + _ref(p_dictionary); +} + +const void *Dictionary::id() const { + return _p; +} + +Dictionary::Dictionary(const Dictionary &p_from) { + _p = nullptr; + _ref(p_from); +} + +Dictionary::Dictionary() { + _p = memnew(DictionaryPrivate); + _p->refcount.init(); +} +Dictionary::~Dictionary() { + _unref(); +} +#line 0 + +#line 1 "sfwl/object/array.cpp" +/*************************************************************************/ +/* array.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +class ArrayPrivate { +public: + SafeRefCount refcount; + Vector array; +}; + +void Array::_ref(const Array &p_from) const { + ArrayPrivate *_fp = p_from._p; + + ERR_FAIL_COND(!_fp); // should NOT happen. + + if (_fp == _p) { + return; // whatever it is, nothing to do here move along + } + + bool success = _fp->refcount.ref(); + + ERR_FAIL_COND(!success); // should really not happen either + + _unref(); + + _p = p_from._p; +} + +void Array::_unref() const { + if (!_p) { + return; + } + + if (_p->refcount.unref()) { + memdelete(_p); + } + _p = nullptr; +} + +Variant &Array::operator[](int p_idx) { + return _p->array.write[p_idx]; +} + +const Variant &Array::operator[](int p_idx) const { + return _p->array[p_idx]; +} + +int Array::size() const { + return _p->array.size(); +} +bool Array::empty() const { + return _p->array.empty(); +} +void Array::clear() { + _p->array.clear(); +} + +bool Array::deep_equal(const Array &p_array, int p_recursion_count) const { + // Cheap checks + ERR_FAIL_COND_V_MSG(p_recursion_count > MAX_RECURSION, true, "Max recursion reached"); + if (_p == p_array._p) { + return true; + } + const Vector &a1 = _p->array; + const Vector &a2 = p_array._p->array; + const int size = a1.size(); + if (size != a2.size()) { + return false; + } + + // Heavy O(n) check + p_recursion_count++; + for (int i = 0; i < size; i++) { + if (!a1[i].deep_equal(a2[i], p_recursion_count)) { + return false; + } + } + + return true; +} + +bool Array::operator==(const Array &p_array) const { + return _p == p_array._p; +} + +uint32_t Array::hash() const { + return recursive_hash(0); +} + +uint32_t Array::recursive_hash(int p_recursion_count) const { + ERR_FAIL_COND_V_MSG(p_recursion_count > MAX_RECURSION, 0, "Max recursion reached"); + p_recursion_count++; + + uint32_t h = hash_murmur3_one_32(0); + + for (int i = 0; i < _p->array.size(); i++) { + h = hash_murmur3_one_32(_p->array[i].recursive_hash(p_recursion_count), h); + } + return hash_fmix32(h); +} + +void Array::operator=(const Array &p_array) { + _ref(p_array); +} + +void Array::push_back(const Variant &p_value) { + _p->array.push_back(p_value); +} + +void Array::append_array(const Array &p_array) { + _p->array.append_array(p_array._p->array); +} + +Error Array::resize(int p_new_size) { + return _p->array.resize(p_new_size); +} + +void Array::insert(int p_pos, const Variant &p_value) { + _p->array.insert(p_pos, p_value); +} + +void Array::fill(const Variant &p_value) { + _p->array.fill(p_value); +} + +void Array::erase(const Variant &p_value) { + _p->array.erase(p_value); +} + +Variant Array::front() const { + ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array."); + return operator[](0); +} + +Variant Array::back() const { + ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array."); + return operator[](_p->array.size() - 1); +} + +int Array::find(const Variant &p_value, int p_from) const { + return _p->array.find(p_value, p_from); +} + +int Array::rfind(const Variant &p_value, int p_from) const { + if (_p->array.size() == 0) { + return -1; + } + + if (p_from < 0) { + // Relative offset from the end + p_from = _p->array.size() + p_from; + } + if (p_from < 0 || p_from >= _p->array.size()) { + // Limit to array boundaries + p_from = _p->array.size() - 1; + } + + for (int i = p_from; i >= 0; i--) { + if (_p->array[i] == p_value) { + return i; + } + } + + return -1; +} + +int Array::find_last(const Variant &p_value) const { + return rfind(p_value); +} + +int Array::count(const Variant &p_value) const { + if (_p->array.size() == 0) { + return 0; + } + + int amount = 0; + for (int i = 0; i < _p->array.size(); i++) { + if (_p->array[i] == p_value) { + amount++; + } + } + + return amount; +} + +bool Array::has(const Variant &p_value) const { + return _p->array.find(p_value, 0) != -1; +} + +void Array::remove(int p_pos) { + _p->array.remove(p_pos); +} + +void Array::set(int p_idx, const Variant &p_value) { + operator[](p_idx) = p_value; +} + +const Variant &Array::get(int p_idx) const { + return operator[](p_idx); +} + +Array Array::duplicate(bool p_deep) const { + Array new_arr; + int element_count = size(); + new_arr.resize(element_count); + for (int i = 0; i < element_count; i++) { + new_arr[i] = p_deep ? get(i).duplicate(p_deep) : get(i); + } + + return new_arr; +} + +int Array::_clamp_slice_index(int p_index) const { + int arr_size = size(); + int fixed_index = CLAMP(p_index, -arr_size, arr_size - 1); + if (fixed_index < 0) { + fixed_index = arr_size + fixed_index; + } + return fixed_index; +} + +Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const { // like python, but inclusive on upper bound + + Array new_arr; + + ERR_FAIL_COND_V_MSG(p_step == 0, new_arr, "Array slice step size cannot be zero."); + + if (empty()) { // Don't try to slice empty arrays. + return new_arr; + } + if (p_step > 0) { + if (p_begin >= size() || p_end < -size()) { + return new_arr; + } + } else { // p_step < 0 + if (p_begin < -size() || p_end >= size()) { + return new_arr; + } + } + + int begin = _clamp_slice_index(p_begin); + int end = _clamp_slice_index(p_end); + + int new_arr_size = MAX(((end - begin + p_step) / p_step), 0); + new_arr.resize(new_arr_size); + + if (p_step > 0) { + int dest_idx = 0; + for (int idx = begin; idx <= end; idx += p_step) { + ERR_FAIL_COND_V_MSG(dest_idx < 0 || dest_idx >= new_arr_size, Array(), "Bug in Array slice()"); + new_arr[dest_idx++] = p_deep ? get(idx).duplicate(p_deep) : get(idx); + } + } else { // p_step < 0 + int dest_idx = 0; + for (int idx = begin; idx >= end; idx += p_step) { + ERR_FAIL_COND_V_MSG(dest_idx < 0 || dest_idx >= new_arr_size, Array(), "Bug in Array slice()"); + new_arr[dest_idx++] = p_deep ? get(idx).duplicate(p_deep) : get(idx); + } + } + + return new_arr; +} + +struct _ArrayVariantSort { + _FORCE_INLINE_ bool operator()(const Variant &p_l, const Variant &p_r) const { + bool valid = false; + Variant res; + Variant::evaluate(Variant::OP_LESS, p_l, p_r, res, valid); + if (!valid) { + res = false; + } + return res; + } +}; + +Array &Array::sort() { + _p->array.sort_custom<_ArrayVariantSort>(); + return *this; +} + +struct _ArrayVariantSortCustom { + Object *obj; + + _FORCE_INLINE_ bool operator()(const Variant &p_l, const Variant &p_r) const { + return obj->lt(p_l, p_r); + } +}; +Array &Array::sort_custom(Object *p_obj) { + ERR_FAIL_COND_V(!p_obj, *this); + + SortArray avs; + avs.compare.obj = p_obj; + avs.sort(_p->array.ptrw(), _p->array.size()); + return *this; +} + +void Array::shuffle() { + const int n = _p->array.size(); + if (n < 2) { + return; + } + Variant *data = _p->array.ptrw(); + for (int i = n - 1; i >= 1; i--) { + const int j = Math::rand() % (i + 1); + const Variant tmp = data[j]; + data[j] = data[i]; + data[i] = tmp; + } +} + +template +_FORCE_INLINE_ int bisect(const Vector &p_array, const Variant &p_value, bool p_before, const Less &p_less) { + int lo = 0; + int hi = p_array.size(); + if (p_before) { + while (lo < hi) { + const int mid = (lo + hi) / 2; + if (p_less(p_array.get(mid), p_value)) { + lo = mid + 1; + } else { + hi = mid; + } + } + } else { + while (lo < hi) { + const int mid = (lo + hi) / 2; + if (p_less(p_value, p_array.get(mid))) { + hi = mid; + } else { + lo = mid + 1; + } + } + } + return lo; +} + +int Array::bsearch(const Variant &p_value, bool p_before) { + return bisect(_p->array, p_value, p_before, _ArrayVariantSort()); +} + +int Array::bsearch_custom(const Variant &p_value, Object *p_obj, const StringName &p_function, bool p_before) { + ERR_FAIL_COND_V(!p_obj, 0); + + _ArrayVariantSortCustom less; + less.obj = p_obj; + + return bisect(_p->array, p_value, p_before, less); +} + +Array &Array::invert() { + _p->array.invert(); + return *this; +} + +void Array::push_front(const Variant &p_value) { + _p->array.insert(0, p_value); +} + +Variant Array::pop_back() { + if (!_p->array.empty()) { + const int n = _p->array.size() - 1; + const Variant ret = _p->array.get(n); + _p->array.resize(n); + return ret; + } + return Variant(); +} + +Variant Array::pop_front() { + if (!_p->array.empty()) { + const Variant ret = _p->array.get(0); + _p->array.remove(0); + return ret; + } + return Variant(); +} + +Variant Array::pop_at(int p_pos) { + if (_p->array.empty()) { + // Return `null` without printing an error to mimic `pop_back()` and `pop_front()` behavior. + return Variant(); + } + + if (p_pos < 0) { + // Relative offset from the end + p_pos = _p->array.size() + p_pos; + } + + ERR_FAIL_INDEX_V_MSG( + p_pos, + _p->array.size(), + Variant(), + vformat( + "The calculated index %s is out of bounds (the array has %s elements). Leaving the array untouched and returning `null`.", + p_pos, + _p->array.size())); + + const Variant ret = _p->array.get(p_pos); + _p->array.remove(p_pos); + return ret; +} + +Variant Array::min() const { + Variant minval; + for (int i = 0; i < size(); i++) { + if (i == 0) { + minval = get(i); + } else { + bool valid; + Variant ret; + Variant test = get(i); + Variant::evaluate(Variant::OP_LESS, test, minval, ret, valid); + if (!valid) { + return Variant(); //not a valid comparison + } + if (bool(ret)) { + //is less + minval = test; + } + } + } + return minval; +} + +Variant Array::max() const { + Variant maxval; + for (int i = 0; i < size(); i++) { + if (i == 0) { + maxval = get(i); + } else { + bool valid; + Variant ret; + Variant test = get(i); + Variant::evaluate(Variant::OP_GREATER, test, maxval, ret, valid); + if (!valid) { + return Variant(); //not a valid comparison + } + if (bool(ret)) { + //is less + maxval = test; + } + } + } + return maxval; +} + +const void *Array::id() const { + return _p; +} + +String Array::sprintf(const String &p_format, bool *error) const { + String formatted; + CharType *self = (CharType *)p_format.get_data(); + bool in_format = false; + int value_index = 0; + int min_chars = 0; + int min_decimals = 0; + bool in_decimals = false; + bool pad_with_zeros = false; + bool left_justified = false; + bool show_sign = false; + + const Array &values = *this; + + if (error) { + *error = true; + } + + for (; *self; self++) { + const CharType c = *self; + + if (in_format) { // We have % - let's see what else we get. + switch (c) { + case '%': { // Replace %% with % + formatted += p_format.chr(c); + in_format = false; + break; + } + case 'd': // Integer (signed) + case 'o': // Octal + case 'x': // Hexadecimal (lowercase) + case 'X': { // Hexadecimal (uppercase) + if (value_index >= values.size()) { + return "not enough arguments for format string"; + } + + if (!values[value_index].is_num()) { + return "a number is required"; + } + + int64_t value = values[value_index]; + int base = 16; + bool capitalize = false; + switch (c) { + case 'd': + base = 10; + break; + case 'o': + base = 8; + break; + case 'x': + break; + case 'X': + base = 16; + capitalize = true; + break; + } + // Get basic number. + String str = String::num_int64(ABS(value), base, capitalize); + int number_len = str.length(); + + // Padding. + int pad_chars_count = (value < 0 || show_sign) ? min_chars - 1 : min_chars; + String pad_char = pad_with_zeros ? String("0") : String(" "); + if (left_justified) { + str = str.rpad(pad_chars_count, pad_char); + } else { + str = str.lpad(pad_chars_count, pad_char); + } + + // Sign. + if (show_sign || value < 0) { + String sign_char = value < 0 ? "-" : "+"; + if (left_justified) { + str = str.insert(0, sign_char); + } else { + str = str.insert(pad_with_zeros ? 0 : str.length() - number_len, sign_char); + } + } + + formatted += str; + ++value_index; + in_format = false; + + break; + } + case 'f': { // Float + if (value_index >= values.size()) { + return "not enough arguments for format string"; + } + + if (!values[value_index].is_num()) { + return "a number is required"; + } + + double value = values[value_index]; + bool is_negative = (value < 0); + String str = String::num(ABS(value), min_decimals); + bool not_numeric = isinf(value) || isnan(value); + + // Pad decimals out. + if (!not_numeric) { + str = str.pad_decimals(min_decimals); + } + + int initial_len = str.length(); + + // Padding. Leave room for sign later if required. + int pad_chars_count = (is_negative || show_sign) ? min_chars - 1 : min_chars; + String pad_char = (pad_with_zeros && !not_numeric) ? String("0") : String(" "); // Never pad NaN or inf with zeros + if (left_justified) { + str = str.rpad(pad_chars_count, pad_char); + } else { + str = str.lpad(pad_chars_count, pad_char); + } + + // Add sign if needed. + if (show_sign || is_negative) { + String sign_char = is_negative ? "-" : "+"; + if (left_justified) { + str = str.insert(0, sign_char); + } else { + str = str.insert(pad_with_zeros ? 0 : str.length() - initial_len, sign_char); + } + } + + formatted += str; + ++value_index; + in_format = false; + break; + } + case 's': { // String + if (value_index >= values.size()) { + return "not enough arguments for format string"; + } + + String str = values[value_index]; + // Padding. + if (left_justified) { + str = str.rpad(min_chars); + } else { + str = str.lpad(min_chars); + } + + formatted += str; + ++value_index; + in_format = false; + break; + } + case 'c': { + if (value_index >= values.size()) { + return "not enough arguments for format string"; + } + + // Convert to character. + String str; + if (values[value_index].is_num()) { + int value = values[value_index]; + if (value < 0) { + return "unsigned integer is lower than minimum"; + } else if (value >= 0xd800 && value <= 0xdfff) { + return "unsigned integer is invalid Unicode character"; + } else if (value > 0x10ffff) { + return "unsigned integer is greater than maximum"; + } + str = p_format.chr(values[value_index]); + } else if (values[value_index].get_type() == Variant::STRING) { + str = values[value_index]; + if (str.length() != 1) { + return "%c requires number or single-character string"; + } + } else { + return "%c requires number or single-character string"; + } + + // Padding. + if (left_justified) { + str = str.rpad(min_chars); + } else { + str = str.lpad(min_chars); + } + + formatted += str; + ++value_index; + in_format = false; + break; + } + case '-': { // Left justify + left_justified = true; + break; + } + case '+': { // Show + if positive. + show_sign = true; + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + int n = c - '0'; + if (in_decimals) { + min_decimals *= 10; + min_decimals += n; + } else { + if (c == '0' && min_chars == 0) { + if (left_justified) { + LOG_WARN("'0' flag ignored with '-' flag in string format"); + } else { + pad_with_zeros = true; + } + } else { + min_chars *= 10; + min_chars += n; + } + } + break; + } + case '.': { // Float separator. + if (in_decimals) { + return "too many decimal points in format"; + } + in_decimals = true; + min_decimals = 0; // We want to add the value manually. + break; + } + + case '*': { // Dynamic width, based on value. + if (value_index >= values.size()) { + return "not enough arguments for format string"; + } + + if (!values[value_index].is_num()) { + return "* wants number"; + } + + int size = values[value_index]; + + if (in_decimals) { + min_decimals = size; + } else { + min_chars = size; + } + + ++value_index; + break; + } + + default: { + return "unsupported format character"; + } + } + } else { // Not in format string. + switch (c) { + case '%': + in_format = true; + // Back to defaults: + min_chars = 0; + min_decimals = 6; + pad_with_zeros = false; + left_justified = false; + show_sign = false; + in_decimals = false; + break; + default: + formatted += p_format.chr(c); + } + } + } + + if (in_format) { + return "incomplete format"; + } + + if (value_index != values.size()) { + return "not all arguments converted during string formatting"; + } + + if (error) { + *error = false; + } + return formatted; +} + +Array::Array(const Array &p_from) { + _p = nullptr; + _ref(p_from); +} + +Array::Array() { + _p = memnew(ArrayPrivate); + _p->refcount.init(); +} +Array::~Array() { + _unref(); +} +#line 0 + +#line 1 "sfwl/object/psignal.cpp" + + + +void Signal::connect_static(void (*func)(Signal *)) { + StaticSignalEntry *se = new StaticSignalEntry(); + se->func = func; + + entries.push_back(se); +} +void Signal::disconnect_static(void (*func)(Signal *)) { + for (int i = 0; i < entries.size(); ++i) { + SignalEntry *e = entries[i]; + + if (e->type == SIGNAL_ENTRY_TYPE_STATIC) { + StaticSignalEntry *se = static_cast(e); + + if (se->func == func) { + entries.remove(i); + return; + } + } + } +} +bool Signal::is_connected_static(void (*func)(Signal *)) { + for (int i = 0; i < entries.size(); ++i) { + SignalEntry *e = entries[i]; + + if (e->type == SIGNAL_ENTRY_TYPE_STATIC) { + StaticSignalEntry *se = static_cast(e); + + if (se->func == func) { + return true; + } + } + } + + return false; +} + +void Signal::emit(Object *p_emitter) { + emitter = p_emitter; + + for (int i = 0; i < entries.size(); ++i) { + entries[i]->call(this); + } +} + +void Signal::emit(Object *p_emitter, const Variant &p1) { + emitter = p_emitter; + + params.push_back(p1); + + for (int i = 0; i < entries.size(); ++i) { + entries[i]->call(this); + } + + params.clear(); +} +void Signal::emit(Object *p_emitter, const Variant &p1, const Variant &p2) { + emitter = p_emitter; + + params.push_back(p1); + params.push_back(p2); + + for (int i = 0; i < entries.size(); ++i) { + entries[i]->call(this); + } + + params.clear(); +} +void Signal::emit(Object *p_emitter, const Variant &p1, const Variant &p2, const Variant &p3) { + emitter = p_emitter; + + params.push_back(p1); + params.push_back(p2); + params.push_back(p3); + + for (int i = 0; i < entries.size(); ++i) { + entries[i]->call(this); + } + + params.clear(); +} + +void Signal::emit(Object *p_emitter, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4) { + emitter = p_emitter; + + params.push_back(p1); + params.push_back(p2); + params.push_back(p3); + params.push_back(p4); + + for (int i = 0; i < entries.size(); ++i) { + entries[i]->call(this); + } + + params.clear(); +} + +void Signal::emit(Object *p_emitter, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4, const Variant &p5) { + emitter = p_emitter; + + params.push_back(p1); + params.push_back(p2); + params.push_back(p3); + params.push_back(p4); + params.push_back(p5); + + for (int i = 0; i < entries.size(); ++i) { + entries[i]->call(this); + } + + params.clear(); +} + +Signal::Signal() { +} +Signal::~Signal() { +} +#line 0 + +#line 1 "sfwl/object/variant.cpp" +/*************************************************************************/ +/* variant.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +String Variant::get_type_name(Variant::Type p_type) { + switch (p_type) { + case NIL: { + return "Nil"; + } break; + + // atomic types + case BOOL: { + return "bool"; + } break; + case INT: { + return "int"; + + } break; + case REAL: { + return "float"; + + } break; + case STRING: { + return "String"; + } break; + + // math types + case RECT2: { + return "Rect2"; + } break; + case RECT2I: { + return "Rect2i"; + } break; + case VECTOR2: { + return "Vector2"; + } break; + case VECTOR2I: { + return "Vector2i"; + } break; + case VECTOR3: { + return "Vector3"; + } break; + case VECTOR3I: { + return "Vector3i"; + } break; + case VECTOR4: { + return "Vector4"; + } break; + case VECTOR4I: { + return "Vector4i"; + } break; + + case PLANE: { + return "Plane"; + } break; + case QUATERNION: { + return "Quaternion"; + } break; + case AABB: { + return "AABB"; + } break; + case BASIS: { + return "Basis"; + } break; + case TRANSFORM: { + return "Transform"; + } break; + case TRANSFORM2D: { + return "Transform2D"; + } break; + case PROJECTION: { + return "Projection"; + } break; + + // misc types + case COLOR: { + return "Color"; + } break; + case OBJECT: { + return "Object"; + } break; + case STRING_NAME: { + return "StringName"; + } break; + case DICTIONARY: { + return "Dictionary"; + } break; + case ARRAY: { + return "Array"; + } break; + + // arrays + case POOL_BYTE_ARRAY: { + return "PoolByteArray"; + } break; + case POOL_INT_ARRAY: { + return "PoolIntArray"; + } break; + case POOL_REAL_ARRAY: { + return "PoolRealArray"; + } break; + case POOL_STRING_ARRAY: { + return "PoolStringArray"; + } break; + case POOL_VECTOR2_ARRAY: { + return "PoolVector2Array"; + } break; + case POOL_VECTOR2I_ARRAY: { + return "PoolVector2iArray"; + } break; + case POOL_VECTOR3_ARRAY: { + return "PoolVector3Array"; + } break; + case POOL_VECTOR3I_ARRAY: { + return "PoolVector3iArray"; + } break; + case POOL_VECTOR4_ARRAY: { + return "PoolVector4Array"; + } break; + case POOL_VECTOR4I_ARRAY: { + return "PoolVector4iArray"; + } break; + case POOL_COLOR_ARRAY: { + return "PoolColorArray"; + } break; + default: { + } + } + + return ""; +} + +bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) { + if (p_type_from == p_type_to) { + return true; + } + if (p_type_to == NIL && p_type_from != NIL) { //nil can convert to anything + return true; + } + + if (p_type_from == NIL) { + return (p_type_to == OBJECT); + }; + + const Type *valid_types = nullptr; + const Type *invalid_types = nullptr; + + switch (p_type_to) { + case NIL: { + //can't + } break; + case BOOL: { + static const Type valid[] = { + INT, + REAL, + STRING, + NIL, + }; + + valid_types = valid; + } break; + case INT: { + static const Type valid[] = { + BOOL, + REAL, + STRING, + NIL, + }; + + valid_types = valid; + } break; + case REAL: { + static const Type valid[] = { + BOOL, + INT, + STRING, + NIL, + }; + + valid_types = valid; + } break; + case STRING: { + static const Type invalid[] = { + OBJECT, + NIL + }; + + invalid_types = invalid; + } break; + case RECT2: { + static const Type valid[] = { + RECT2I, + NIL + }; + + valid_types = valid; + } break; + case RECT2I: { + static const Type valid[] = { + RECT2, + NIL + }; + + valid_types = valid; + } break; + case VECTOR2: { + static const Type valid[] = { + VECTOR2I, + NIL + }; + + valid_types = valid; + } break; + case VECTOR2I: { + static const Type valid[] = { + VECTOR2, + NIL + }; + + valid_types = valid; + } break; + case VECTOR3: { + static const Type valid[] = { + VECTOR3I, + NIL + }; + + valid_types = valid; + } break; + case VECTOR3I: { + static const Type valid[] = { + VECTOR3, + NIL + }; + + valid_types = valid; + } break; + case VECTOR4: { + static const Type valid[] = { + VECTOR4I, + NIL + }; + + valid_types = valid; + } break; + case VECTOR4I: { + static const Type valid[] = { + VECTOR4, + NIL + }; + + valid_types = valid; + } break; + case PLANE: { + //can't + } break; + case QUATERNION: { + static const Type valid[] = { + BASIS, + NIL + }; + + valid_types = valid; + } break; + case AABB: { + //can't + } break; + case BASIS: { + static const Type valid[] = { + QUATERNION, + VECTOR3, + NIL + }; + + valid_types = valid; + } break; + case TRANSFORM: { + static const Type valid[] = { + TRANSFORM2D, + QUATERNION, + BASIS, + NIL + }; + + valid_types = valid; + } break; + case TRANSFORM2D: { + static const Type valid[] = { + TRANSFORM, + NIL + }; + + valid_types = valid; + } break; + case PROJECTION: { + static const Type valid[] = { + TRANSFORM, + NIL + }; + + valid_types = valid; + } break; + // misc types + case COLOR: { + static const Type valid[] = { + STRING, + INT, + NIL, + }; + + valid_types = valid; + } break; + case OBJECT: { + static const Type valid[] = { + NIL + }; + + valid_types = valid; + } break; + case STRING_NAME: { + static const Type valid[] = { + STRING, + NIL + }; + + valid_types = valid; + } break; + case DICTIONARY: { + //can't + } break; + case ARRAY: { + static const Type valid[] = { + POOL_BYTE_ARRAY, + POOL_INT_ARRAY, + POOL_STRING_ARRAY, + POOL_REAL_ARRAY, + POOL_COLOR_ARRAY, + POOL_VECTOR2_ARRAY, + POOL_VECTOR2I_ARRAY, + POOL_VECTOR3_ARRAY, + POOL_VECTOR3I_ARRAY, + NIL + }; + + valid_types = valid; + } break; + // arrays + case POOL_BYTE_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + case POOL_INT_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + valid_types = valid; + } break; + case POOL_REAL_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + case POOL_STRING_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + valid_types = valid; + } break; + case POOL_VECTOR2_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + valid_types = valid; + } break; + case POOL_VECTOR2I_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + valid_types = valid; + } break; + case POOL_VECTOR3_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + valid_types = valid; + } break; + case POOL_VECTOR3I_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + valid_types = valid; + } break; + case POOL_VECTOR4_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + valid_types = valid; + } break; + case POOL_VECTOR4I_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + valid_types = valid; + } break; + case POOL_COLOR_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + default: { + } + } + + if (valid_types) { + int i = 0; + while (valid_types[i] != NIL) { + if (p_type_from == valid_types[i]) { + return true; + } + i++; + } + + } else if (invalid_types) { + int i = 0; + while (invalid_types[i] != NIL) { + if (p_type_from == invalid_types[i]) { + return false; + } + i++; + } + + return true; + } + + return false; +} + +bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type_to) { + if (p_type_from == p_type_to) { + return true; + } + if (p_type_to == NIL && p_type_from != NIL) { //nil can convert to anything + return true; + } + + if (p_type_from == NIL) { + return (p_type_to == OBJECT); + }; + + const Type *valid_types = nullptr; + + switch (p_type_to) { + case NIL: { + //can't, also already handled + } break; + case BOOL: { + static const Type valid[] = { + INT, + REAL, + //STRING, + NIL, + }; + + valid_types = valid; + } break; + case INT: { + static const Type valid[] = { + BOOL, + REAL, + //STRING, + NIL, + }; + + valid_types = valid; + + } break; + case REAL: { + static const Type valid[] = { + BOOL, + INT, + //STRING, + NIL, + }; + + valid_types = valid; + + } break; + case STRING: { + static const Type valid[] = { + STRING_NAME, + NIL + }; + + valid_types = valid; + } break; + case RECT2: { + static const Type valid[] = { + RECT2I, + NIL + }; + + valid_types = valid; + } break; + case RECT2I: { + static const Type valid[] = { + RECT2, + NIL + }; + + valid_types = valid; + } break; + case VECTOR2: { + static const Type valid[] = { + VECTOR2I, + NIL + }; + + valid_types = valid; + } break; + case VECTOR2I: { + static const Type valid[] = { + VECTOR2, + NIL + }; + + valid_types = valid; + } break; + case VECTOR3: { + static const Type valid[] = { + VECTOR3I, + NIL + }; + + valid_types = valid; + } break; + case VECTOR3I: { + static const Type valid[] = { + VECTOR3, + NIL + }; + + valid_types = valid; + } break; + case VECTOR4: { + static const Type valid[] = { + VECTOR4I, + NIL + }; + + valid_types = valid; + } break; + case VECTOR4I: { + static const Type valid[] = { + VECTOR4, + NIL + }; + + valid_types = valid; + } break; + case PLANE: { + //Can't + } break; + case QUATERNION: { + static const Type valid[] = { + BASIS, + NIL + }; + + valid_types = valid; + } break; + case AABB: { + //Can't + } break; + case BASIS: { + static const Type valid[] = { + QUATERNION, + VECTOR3, + NIL + }; + + valid_types = valid; + } break; + case TRANSFORM: { + static const Type valid[] = { + TRANSFORM2D, + QUATERNION, + BASIS, + NIL + }; + + valid_types = valid; + } break; + case TRANSFORM2D: { + static const Type valid[] = { + TRANSFORM, + NIL + }; + + valid_types = valid; + } break; + case PROJECTION: { + static const Type valid[] = { + TRANSFORM, + NIL + }; + + valid_types = valid; + } break; + case COLOR: { + static const Type valid[] = { + STRING, + INT, + NIL, + }; + + valid_types = valid; + } break; + case OBJECT: { + static const Type valid[] = { + NIL + }; + + valid_types = valid; + } break; + case STRING_NAME: { + static const Type valid[] = { + STRING, + NIL + }; + + valid_types = valid; + } break; + case DICTIONARY: { + //Can't + } break; + case ARRAY: { + static const Type valid[] = { + POOL_BYTE_ARRAY, + POOL_INT_ARRAY, + POOL_STRING_ARRAY, + POOL_REAL_ARRAY, + POOL_COLOR_ARRAY, + POOL_VECTOR2_ARRAY, + POOL_VECTOR2I_ARRAY, + POOL_VECTOR3_ARRAY, + POOL_VECTOR3I_ARRAY, + POOL_VECTOR4_ARRAY, + POOL_VECTOR4I_ARRAY, + NIL + }; + + valid_types = valid; + } break; + // arrays + case POOL_BYTE_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + case POOL_INT_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + valid_types = valid; + } break; + case POOL_REAL_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + case POOL_STRING_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + case POOL_VECTOR2_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + case POOL_VECTOR2I_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + case POOL_VECTOR3_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + case POOL_VECTOR3I_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + case POOL_VECTOR4_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + case POOL_VECTOR4I_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + case POOL_COLOR_ARRAY: { + static const Type valid[] = { + ARRAY, + NIL + }; + + valid_types = valid; + } break; + default: { + } + } + + if (valid_types) { + int i = 0; + while (valid_types[i] != NIL) { + if (p_type_from == valid_types[i]) { + return true; + } + i++; + } + } + + return false; +} + +bool Variant::deep_equal(const Variant &p_variant, int p_recursion_count) const { + ERR_FAIL_COND_V_MSG(p_recursion_count > MAX_RECURSION, true, "Max recursion reached"); + + // Containers must be handled with recursivity checks + switch (type) { + case Variant::Type::DICTIONARY: { + if (p_variant.type != Variant::Type::DICTIONARY) { + return false; + } + + const Dictionary v1_as_d = Dictionary(*this); + const Dictionary v2_as_d = Dictionary(p_variant); + + return v1_as_d.deep_equal(v2_as_d, p_recursion_count + 1); + } break; + case Variant::Type::ARRAY: { + if (p_variant.type != Variant::Type::ARRAY) { + return false; + } + + const Array v1_as_a = Array(*this); + const Array v2_as_a = Array(p_variant); + + return v1_as_a.deep_equal(v2_as_a, p_recursion_count + 1); + } break; + default: { + return *this == p_variant; + } break; + } +} + +bool Variant::operator==(const Variant &p_variant) const { + if (type != p_variant.type) { //evaluation of operator== needs to be more strict + return false; + } + bool v; + Variant r; + evaluate(OP_EQUAL, *this, p_variant, r, v); + return r; +} + +bool Variant::operator!=(const Variant &p_variant) const { + if (type != p_variant.type) { //evaluation of operator== needs to be more strict + return true; + } + bool v; + Variant r; + evaluate(OP_NOT_EQUAL, *this, p_variant, r, v); + return r; +} + +bool Variant::operator<(const Variant &p_variant) const { + if (type != p_variant.type) { //if types differ, then order by type first + return type < p_variant.type; + } + bool v; + Variant r; + evaluate(OP_LESS, *this, p_variant, r, v); + return r; +} + +bool Variant::is_zero() const { + switch (type) { + case NIL: { + return true; + } break; + + // atomic types + case BOOL: { + return !(_data._bool); + } break; + case INT: { + return _data._int == 0; + } break; + case REAL: { + return _data._real == 0; + } break; + case STRING: { + return *reinterpret_cast(_data._mem) == String(); + } break; + + // math types + case RECT2: { + return *reinterpret_cast(_data._mem) == Rect2(); + } break; + case RECT2I: { + return *reinterpret_cast(_data._mem) == Rect2i(); + } break; + case VECTOR2: { + return *reinterpret_cast(_data._mem) == Vector2(); + } break; + case VECTOR2I: { + return *reinterpret_cast(_data._mem) == Vector2i(); + } break; + case VECTOR3: { + return *reinterpret_cast(_data._mem) == Vector3(); + } break; + case VECTOR3I: { + return *reinterpret_cast(_data._mem) == Vector3i(); + } break; + case VECTOR4: { + return *reinterpret_cast(_data._mem) == Vector4(); + } break; + case VECTOR4I: { + return *reinterpret_cast(_data._mem) == Vector4i(); + } break; + + case PLANE: { + return *reinterpret_cast(_data._mem) == Plane(); + } break; + case QUATERNION: { + return *reinterpret_cast(_data._mem) == Quaternion(); + } break; + case AABB: { + return *_data._aabb == ::AABB(); + } break; + case BASIS: { + return *_data._basis == Basis(); + } break; + case TRANSFORM: { + return *_data._transform == Transform(); + } break; + case TRANSFORM2D: { + return *_data._transform2d == Transform2D(); + } break; + case PROJECTION: { + return *_data._projection == Projection(); + } break; + + // misc types + case COLOR: { + return *reinterpret_cast(_data._mem) == Color(); + } break; + case OBJECT: { + return _UNSAFE_OBJ_PROXY_PTR(*this) == nullptr; + } break; + case STRING_NAME: { + return *reinterpret_cast(_data._mem) != StringName(); + } break; + case DICTIONARY: { + return reinterpret_cast(_data._mem)->empty(); + } break; + case ARRAY: { + return reinterpret_cast(_data._mem)->empty(); + } break; + + // arrays + case POOL_BYTE_ARRAY: { + return reinterpret_cast *>(_data._mem)->size() == 0; + } break; + case POOL_INT_ARRAY: { + return reinterpret_cast *>(_data._mem)->size() == 0; + } break; + case POOL_REAL_ARRAY: { + return reinterpret_cast *>(_data._mem)->size() == 0; + } break; + case POOL_STRING_ARRAY: { + return reinterpret_cast *>(_data._mem)->size() == 0; + } break; + case POOL_VECTOR2_ARRAY: { + return reinterpret_cast *>(_data._mem)->size() == 0; + } break; + case POOL_VECTOR2I_ARRAY: { + return reinterpret_cast *>(_data._mem)->size() == 0; + } break; + case POOL_VECTOR3_ARRAY: { + return reinterpret_cast *>(_data._mem)->size() == 0; + } break; + case POOL_VECTOR3I_ARRAY: { + return reinterpret_cast *>(_data._mem)->size() == 0; + } break; + case POOL_VECTOR4_ARRAY: { + return reinterpret_cast *>(_data._mem)->size() == 0; + } break; + case POOL_VECTOR4I_ARRAY: { + return reinterpret_cast *>(_data._mem)->size() == 0; + } break; + case POOL_COLOR_ARRAY: { + return reinterpret_cast *>(_data._mem)->size() == 0; + } break; + default: { + } + } + + return false; +} + +bool Variant::is_one() const { + switch (type) { + case NIL: { + return true; + } break; + + // atomic types + case BOOL: { + return _data._bool; + } break; + case INT: { + return _data._int == 1; + } break; + case REAL: { + return _data._real == 1; + } break; + case RECT2: { + return *reinterpret_cast(_data._mem) == Rect2(1, 1, 1, 1); + } break; + case RECT2I: { + return *reinterpret_cast(_data._mem) == Rect2i(1, 1, 1, 1); + } break; + case VECTOR2: { + return *reinterpret_cast(_data._mem) == Vector2(1, 1); + } break; + case VECTOR2I: { + return *reinterpret_cast(_data._mem) == Vector2i(1, 1); + } break; + case VECTOR3: { + return *reinterpret_cast(_data._mem) == Vector3(1, 1, 1); + } break; + case VECTOR3I: { + return *reinterpret_cast(_data._mem) == Vector3i(1, 1, 1); + } break; + case VECTOR4: { + return *reinterpret_cast(_data._mem) == Vector4(1, 1, 1, 1); + } break; + case VECTOR4I: { + return *reinterpret_cast(_data._mem) == Vector4i(1, 1, 1, 1); + } break; + case PLANE: { + return *reinterpret_cast(_data._mem) == Plane(1, 1, 1, 1); + } break; + case COLOR: { + return *reinterpret_cast(_data._mem) == Color(1, 1, 1, 1); + } break; + + default: { + return !is_zero(); + } + } + + return false; +} + +ObjectID Variant::get_object_instance_id() const { + if (unlikely(type != OBJECT)) { + return 0; + } else if (likely(_get_obj().rc)) { + return _get_obj().rc->instance_id; + } else if (likely(!_get_obj().ref.is_null())) { + return _REF_OBJ_PTR(*this)->get_instance_id(); + } else { + return 0; + } +} + +bool Variant::is_invalid_object() const { + return type == OBJECT && _get_obj().rc && !_get_obj().rc->get_ptr(); +} + +void Variant::reference(const Variant &p_variant) { + switch (type) { + case NIL: + case BOOL: + case INT: + case REAL: + break; + default: + clear(); + } + + type = p_variant.type; + + switch (p_variant.type) { + case NIL: { + // none + } break; + + // atomic types + case BOOL: { + _data._bool = p_variant._data._bool; + } break; + case INT: { + _data._int = p_variant._data._int; + } break; + case REAL: { + _data._real = p_variant._data._real; + } break; + case STRING: { + memnew_placement(_data._mem, String(*reinterpret_cast(p_variant._data._mem))); + } break; + + // math types + case RECT2: { + memnew_placement(_data._mem, Rect2(*reinterpret_cast(p_variant._data._mem))); + } break; + case RECT2I: { + memnew_placement(_data._mem, Rect2i(*reinterpret_cast(p_variant._data._mem))); + } break; + case VECTOR2: { + memnew_placement(_data._mem, Vector2(*reinterpret_cast(p_variant._data._mem))); + } break; + case VECTOR2I: { + memnew_placement(_data._mem, Vector2i(*reinterpret_cast(p_variant._data._mem))); + } break; + case VECTOR3: { + memnew_placement(_data._mem, Vector3(*reinterpret_cast(p_variant._data._mem))); + } break; + case VECTOR3I: { + memnew_placement(_data._mem, Vector3i(*reinterpret_cast(p_variant._data._mem))); + } break; + case VECTOR4: { + memnew_placement(_data._mem, Vector4(*reinterpret_cast(p_variant._data._mem))); + } break; + case VECTOR4I: { + memnew_placement(_data._mem, Vector4i(*reinterpret_cast(p_variant._data._mem))); + } break; + + case PLANE: { + memnew_placement(_data._mem, Plane(*reinterpret_cast(p_variant._data._mem))); + } break; + case QUATERNION: { + memnew_placement(_data._mem, Quaternion(*reinterpret_cast(p_variant._data._mem))); + } break; + case AABB: { + _data._aabb = memnew(::AABB(*p_variant._data._aabb)); + } break; + case BASIS: { + _data._basis = memnew(Basis(*p_variant._data._basis)); + } break; + case TRANSFORM: { + _data._transform = memnew(Transform(*p_variant._data._transform)); + } break; + case TRANSFORM2D: { + _data._transform2d = memnew(Transform2D(*p_variant._data._transform2d)); + } break; + case PROJECTION: { + _data._projection = memnew(Projection(*p_variant._data._projection)); + } break; + + // misc types + case COLOR: { + memnew_placement(_data._mem, Color(*reinterpret_cast(p_variant._data._mem))); + } break; + case OBJECT: { + memnew_placement(_data._mem, ObjData(p_variant._get_obj())); + if (likely(_get_obj().rc)) { + _get_obj().rc->increment(); + } + } break; + case STRING_NAME: { + memnew_placement(_data._mem, StringName(*reinterpret_cast(p_variant._data._mem))); + } break; + case DICTIONARY: { + memnew_placement(_data._mem, Dictionary(*reinterpret_cast(p_variant._data._mem))); + } break; + case ARRAY: { + memnew_placement(_data._mem, Array(*reinterpret_cast(p_variant._data._mem))); + } break; + + // arrays + case POOL_BYTE_ARRAY: { + memnew_placement(_data._mem, PoolVector(*reinterpret_cast *>(p_variant._data._mem))); + } break; + case POOL_INT_ARRAY: { + memnew_placement(_data._mem, PoolVector(*reinterpret_cast *>(p_variant._data._mem))); + } break; + case POOL_REAL_ARRAY: { + memnew_placement(_data._mem, PoolVector(*reinterpret_cast *>(p_variant._data._mem))); + } break; + case POOL_STRING_ARRAY: { + memnew_placement(_data._mem, PoolVector(*reinterpret_cast *>(p_variant._data._mem))); + } break; + case POOL_VECTOR2_ARRAY: { + memnew_placement(_data._mem, PoolVector(*reinterpret_cast *>(p_variant._data._mem))); + } break; + case POOL_VECTOR2I_ARRAY: { + memnew_placement(_data._mem, PoolVector(*reinterpret_cast *>(p_variant._data._mem))); + } break; + case POOL_VECTOR3_ARRAY: { + memnew_placement(_data._mem, PoolVector(*reinterpret_cast *>(p_variant._data._mem))); + } break; + case POOL_VECTOR3I_ARRAY: { + memnew_placement(_data._mem, PoolVector(*reinterpret_cast *>(p_variant._data._mem))); + } break; + case POOL_VECTOR4_ARRAY: { + memnew_placement(_data._mem, PoolVector(*reinterpret_cast *>(p_variant._data._mem))); + } break; + case POOL_VECTOR4I_ARRAY: { + memnew_placement(_data._mem, PoolVector(*reinterpret_cast *>(p_variant._data._mem))); + } break; + case POOL_COLOR_ARRAY: { + memnew_placement(_data._mem, PoolVector(*reinterpret_cast *>(p_variant._data._mem))); + } break; + default: { + } + } +} + +void Variant::zero() { + switch (type) { + case NIL: + break; + case BOOL: + this->_data._bool = false; + break; + case INT: + this->_data._int = 0; + break; + case REAL: + this->_data._real = 0; + break; + case RECT2: + *reinterpret_cast(this->_data._mem) = Rect2(); + break; + case RECT2I: + *reinterpret_cast(this->_data._mem) = Rect2i(); + break; + case VECTOR2: + *reinterpret_cast(this->_data._mem) = Vector2(); + break; + case VECTOR2I: + *reinterpret_cast(this->_data._mem) = Vector2i(); + break; + case VECTOR3: + *reinterpret_cast(this->_data._mem) = Vector3(); + break; + case VECTOR3I: + *reinterpret_cast(this->_data._mem) = Vector3i(); + break; + case VECTOR4: + *reinterpret_cast(this->_data._mem) = Vector4(); + break; + case VECTOR4I: + *reinterpret_cast(this->_data._mem) = Vector4i(); + break; + case PLANE: + *reinterpret_cast(this->_data._mem) = Plane(); + break; + case QUATERNION: + *reinterpret_cast(this->_data._mem) = Quaternion(); + break; + case AABB: + *reinterpret_cast<::AABB *>(this->_data._mem) = ::AABB(); + break; + case COLOR: + *reinterpret_cast(this->_data._mem) = Color(); + break; + case PROJECTION: + *reinterpret_cast(this->_data._mem) = Projection(); + break; + default: + this->clear(); + break; + } +} + +void Variant::clear() { + switch (type) { + /* + // no point, they don't allocate memory + BOOL, + INT, + REAL, + */ + case STRING: { + reinterpret_cast(_data._mem)->~String(); + } break; + /* + RECT2, + RECT2I + VECTOR2, + VECTOR2I, + VECTOR3, + VECTOR3i, + VECTOR4, + VECTOR4i, + + PLANE, + QUATERNION, + */ + case AABB: { + memdelete(_data._aabb); + } break; + case BASIS: { + memdelete(_data._basis); + } break; + case TRANSFORM: { + memdelete(_data._transform); + } break; + case TRANSFORM2D: { + memdelete(_data._transform2d); + } break; + case PROJECTION: { + memdelete(_data._projection); + } break; + //COLOR + + // misc types + case OBJECT: { + if (likely(_get_obj().rc)) { + if (unlikely(_get_obj().rc->decrement())) { + memdelete(_get_obj().rc); + } + } else { + _get_obj().ref.unref(); + } + } break; + case STRING_NAME: { + reinterpret_cast(_data._mem)->~StringName(); + } break; + case DICTIONARY: { + reinterpret_cast(_data._mem)->~Dictionary(); + } break; + case ARRAY: { + reinterpret_cast(_data._mem)->~Array(); + } break; + + // arrays + case POOL_BYTE_ARRAY: { + reinterpret_cast *>(_data._mem)->~PoolVector(); + } break; + case POOL_INT_ARRAY: { + reinterpret_cast *>(_data._mem)->~PoolVector(); + } break; + case POOL_REAL_ARRAY: { + reinterpret_cast *>(_data._mem)->~PoolVector(); + } break; + case POOL_STRING_ARRAY: { + reinterpret_cast *>(_data._mem)->~PoolVector(); + } break; + case POOL_VECTOR2_ARRAY: { + reinterpret_cast *>(_data._mem)->~PoolVector(); + } break; + case POOL_VECTOR2I_ARRAY: { + reinterpret_cast *>(_data._mem)->~PoolVector(); + } break; + case POOL_VECTOR3_ARRAY: { + reinterpret_cast *>(_data._mem)->~PoolVector(); + } break; + case POOL_VECTOR3I_ARRAY: { + reinterpret_cast *>(_data._mem)->~PoolVector(); + } break; + case POOL_VECTOR4_ARRAY: { + reinterpret_cast *>(_data._mem)->~PoolVector(); + } break; + case POOL_VECTOR4I_ARRAY: { + reinterpret_cast *>(_data._mem)->~PoolVector(); + } break; + case POOL_COLOR_ARRAY: { + reinterpret_cast *>(_data._mem)->~PoolVector(); + } break; + default: { + } /* not needed */ + } + + type = NIL; +} + +Variant::operator signed int() const { + switch (type) { + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case REAL: + return _data._real; + case STRING: + return operator String().to_int(); + default: { + return 0; + } + } +} +Variant::operator unsigned int() const { + switch (type) { + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case REAL: + return _data._real; + case STRING: + return operator String().to_int(); + default: { + return 0; + } + } +} + +Variant::operator int64_t() const { + switch (type) { + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case REAL: + return _data._real; + case STRING: + return operator String().to_int64(); + default: { + return 0; + } + } +} + +/* +Variant::operator long unsigned int() const { + + switch( type ) { + + case NIL: return 0; + case BOOL: return _data._bool ? 1 : 0; + case INT: return _data._int; + case REAL: return _data._real; + case STRING: return operator String().to_int(); + default: { + + return 0; + } + } + + return 0; +}; +*/ + +Variant::operator uint64_t() const { + switch (type) { + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case REAL: + return _data._real; + case STRING: + return operator String().to_int(); + default: { + return 0; + } + } +} + +#ifdef NEED_LONG_INT +Variant::operator signed long() const { + switch (type) { + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case REAL: + return _data._real; + case STRING: + return operator String().to_int(); + default: { + return 0; + } + } + + return 0; +}; + +Variant::operator unsigned long() const { + switch (type) { + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case REAL: + return _data._real; + case STRING: + return operator String().to_int(); + default: { + return 0; + } + } + + return 0; +}; +#endif + +Variant::operator signed short() const { + switch (type) { + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case REAL: + return _data._real; + case STRING: + return operator String().to_int(); + default: { + return 0; + } + } +} +Variant::operator unsigned short() const { + switch (type) { + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case REAL: + return _data._real; + case STRING: + return operator String().to_int(); + default: { + return 0; + } + } +} +Variant::operator signed char() const { + switch (type) { + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case REAL: + return _data._real; + case STRING: + return operator String().to_int(); + default: { + return 0; + } + } +} +Variant::operator unsigned char() const { + switch (type) { + case NIL: + return 0; + case BOOL: + return _data._bool ? 1 : 0; + case INT: + return _data._int; + case REAL: + return _data._real; + case STRING: + return operator String().to_int(); + default: { + return 0; + } + } +} + +Variant::operator CharType() const { + return operator unsigned int(); +} + +Variant::operator float() const { + switch (type) { + case NIL: + return 0; + case BOOL: + return _data._bool ? 1.0 : 0.0; + case INT: + return (float)_data._int; + case REAL: + return _data._real; + case STRING: + return operator String().to_double(); + default: { + return 0; + } + } +} +Variant::operator double() const { + switch (type) { + case NIL: + return 0; + case BOOL: + return _data._bool ? 1.0 : 0.0; + case INT: + return (double)_data._int; + case REAL: + return _data._real; + case STRING: + return operator String().to_double(); + default: { + return 0; + } + } +} + +Variant::operator StringName() const { + if (type == STRING_NAME) { + return *reinterpret_cast(_data._mem); + } + + return StringName(operator String()); +} + +struct _VariantStrPair { + String key; + String value; + + bool operator<(const _VariantStrPair &p) const { + return key < p.key; + } +}; + +Variant::operator String() const { + List stack; + + return stringify(stack); +} + +template +String stringify_vector(const T &vec, List &stack) { + String str("["); + for (int i = 0; i < vec.size(); i++) { + if (i > 0) { + str += ", "; + } + str = str + Variant(vec[i]).stringify(stack); + } + str += "]"; + return str; +} + +String Variant::stringify(List &stack) const { + switch (type) { + case NIL: + return "Null"; + case BOOL: + return _data._bool ? "True" : "False"; + case INT: + return itos(_data._int); + case REAL: + return rtos(_data._real); + case STRING: + return *reinterpret_cast(_data._mem); + case RECT2: + return operator Rect2(); + case RECT2I: + return operator Rect2i(); + case VECTOR2: + return operator Vector2(); + case VECTOR2I: + return operator Vector2i(); + case VECTOR3: + return operator Vector3(); + case VECTOR3I: + return operator Vector3i(); + case VECTOR4: + return operator Vector4(); + case VECTOR4I: + return operator Vector4i(); + case PLANE: + return operator Plane(); + case QUATERNION: + return operator Quaternion(); + case AABB: + return operator ::AABB(); + case BASIS: { + return operator Basis(); + } break; + case TRANSFORM: + return operator Transform(); + case TRANSFORM2D: { + return operator Transform2D(); + } break; + case PROJECTION: + return operator Projection(); + case COLOR: + return operator Color(); + case OBJECT: { + Object *obj = _OBJ_PTR(*this); + if (likely(obj)) { + return obj->to_string(); + } else { + if (_get_obj().rc) { + return "[Deleted Object]"; + } + return "[Object:null]"; + } + } break; + case STRING_NAME: + return operator StringName(); + case DICTIONARY: { + const Dictionary &d = *reinterpret_cast(_data._mem); + if (stack.find(d.id())) { + return "{...}"; + } + + stack.push_back(d.id()); + + //const String *K=NULL; + String str("{"); + List keys; + d.get_key_list(&keys); + + Vector<_VariantStrPair> pairs; + + for (List::Element *E = keys.front(); E; E = E->next()) { + _VariantStrPair sp; + sp.key = E->get().stringify(stack); + sp.value = d[E->get()].stringify(stack); + + pairs.push_back(sp); + } + + pairs.sort(); + + for (int i = 0; i < pairs.size(); i++) { + if (i > 0) { + str += ", "; + } + str += pairs[i].key + ":" + pairs[i].value; + } + str += "}"; + + stack.erase(d.id()); + return str; + } break; + case ARRAY: { + Array arr = operator Array(); + if (stack.find(arr.id())) { + return "[...]"; + } + stack.push_back(arr.id()); + String str = stringify_vector(arr, stack); + stack.erase(arr.id()); + return str; + + } break; + + case POOL_BYTE_ARRAY: { + return stringify_vector(operator PoolVector(), stack); + } break; + case POOL_INT_ARRAY: { + return stringify_vector(operator PoolVector(), stack); + } break; + case POOL_REAL_ARRAY: { + return stringify_vector(operator PoolVector(), stack); + } break; + case POOL_STRING_ARRAY: { + return stringify_vector(operator PoolVector(), stack); + } break; + case POOL_VECTOR2_ARRAY: { + return stringify_vector(operator PoolVector(), stack); + } break; + case POOL_VECTOR2I_ARRAY: { + return stringify_vector(operator PoolVector(), stack); + } break; + case POOL_VECTOR3_ARRAY: { + return stringify_vector(operator PoolVector(), stack); + } break; + case POOL_VECTOR3I_ARRAY: { + return stringify_vector(operator PoolVector(), stack); + } break; + case POOL_VECTOR4_ARRAY: { + return stringify_vector(operator PoolVector(), stack); + } break; + case POOL_VECTOR4I_ARRAY: { + return stringify_vector(operator PoolVector(), stack); + } break; + case POOL_COLOR_ARRAY: { + return stringify_vector(operator PoolVector(), stack); + } break; + + default: { + return "[" + get_type_name(type) + "]"; + } + } + + return ""; +} + +Variant::operator Rect2() const { + if (type == RECT2) { + return *reinterpret_cast(_data._mem); + } else if (type == RECT2I) { + return Rect2(*reinterpret_cast(_data._mem)); + } else { + return Rect2(); + } +} +Variant::operator Rect2i() const { + if (type == RECT2I) { + return *reinterpret_cast(_data._mem); + } else if (type == RECT2) { + return Rect2i(*reinterpret_cast(_data._mem)); + } else { + return Rect2i(); + } +} + +Variant::operator Vector2() const { + if (type == VECTOR2) { + return *reinterpret_cast(_data._mem); + } else if (type == VECTOR2I) { + return Vector2(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y); + } else if (type == VECTOR3) { + return Vector2(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y); + } else if (type == VECTOR3I) { + return Vector2(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y); + } else if (type == VECTOR4) { + return Vector2(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y); + } else if (type == VECTOR4I) { + return Vector2(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y); + } else { + return Vector2(); + } +} +Variant::operator Vector2i() const { + if (type == VECTOR2I) { + return *reinterpret_cast(_data._mem); + } else if (type == VECTOR2) { + return Vector2i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y); + } else if (type == VECTOR3) { + return Vector2i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y); + } else if (type == VECTOR3I) { + return Vector2i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y); + } else if (type == VECTOR4) { + return Vector2i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y); + } else if (type == VECTOR4I) { + return Vector2i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y); + } else { + return Vector2i(); + } +} + +Variant::operator Vector3() const { + if (type == VECTOR3) { + return *reinterpret_cast(_data._mem); + } else if (type == VECTOR3I) { + return Vector3(*reinterpret_cast(_data._mem)); + } else if (type == VECTOR2) { + return Vector3(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, 0.0); + } else if (type == VECTOR2I) { + return Vector3(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, 0.0); + } else if (type == VECTOR4) { + return Vector3(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, reinterpret_cast(_data._mem)->z); + } else if (type == VECTOR4I) { + return Vector3(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, reinterpret_cast(_data._mem)->z); + } else { + return Vector3(); + } +} +Variant::operator Vector3i() const { + if (type == VECTOR3I) { + return *reinterpret_cast(_data._mem); + } else if (type == VECTOR3) { + return Vector3i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, reinterpret_cast(_data._mem)->z); + } else if (type == VECTOR2) { + return Vector3i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, 0.0); + } else if (type == VECTOR2I) { + return Vector3i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, 0.0); + } else if (type == VECTOR4) { + return Vector3i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, reinterpret_cast(_data._mem)->z); + } else if (type == VECTOR4I) { + return Vector3i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, reinterpret_cast(_data._mem)->z); + } else { + return Vector3i(); + } +} + +Variant::operator Vector4() const { + if (type == VECTOR4) { + return *reinterpret_cast(_data._mem); + } else if (type == VECTOR4I) { + return *reinterpret_cast(_data._mem); + } else if (type == VECTOR2) { + return Vector4(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, 0.0, 0.0); + } else if (type == VECTOR2I) { + return Vector4(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, 0.0, 0.0); + } else if (type == VECTOR3) { + return Vector4(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, reinterpret_cast(_data._mem)->z, 0.0); + } else if (type == VECTOR3I) { + return Vector4(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, reinterpret_cast(_data._mem)->z, 0.0); + } else { + return Vector4(); + } +} + +Variant::operator Vector4i() const { + if (type == VECTOR4I) { + return *reinterpret_cast(_data._mem); + } else if (type == VECTOR4) { + const Vector4 &v4 = *reinterpret_cast(_data._mem); + return Vector4i(v4.x, v4.y, v4.z, v4.w); + } else if (type == VECTOR2) { + return Vector4i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, 0.0, 0.0); + } else if (type == VECTOR2I) { + return Vector4i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, 0.0, 0.0); + } else if (type == VECTOR3) { + return Vector4i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, reinterpret_cast(_data._mem)->z, 0.0); + } else if (type == VECTOR3I) { + return Vector4i(reinterpret_cast(_data._mem)->x, reinterpret_cast(_data._mem)->y, reinterpret_cast(_data._mem)->z, 0.0); + } else { + return Vector4i(); + } +} + +Variant::operator Plane() const { + if (type == PLANE) { + return *reinterpret_cast(_data._mem); + } else { + return Plane(); + } +} +Variant::operator ::AABB() const { + if (type == AABB) { + return *_data._aabb; + } else { + return ::AABB(); + } +} + +Variant::operator Basis() const { + if (type == BASIS) { + return *_data._basis; + } else if (type == QUATERNION) { + return *reinterpret_cast(_data._mem); + } else if (type == VECTOR3) { + return Basis(*reinterpret_cast(_data._mem)); + } else if (type == TRANSFORM) { // unexposed in Variant::can_convert? + return _data._transform->basis; + } else { + return Basis(); + } +} + +Variant::operator Quaternion() const { + if (type == QUATERNION) { + return *reinterpret_cast(_data._mem); + } else if (type == BASIS) { + return *_data._basis; + } else if (type == TRANSFORM) { + return _data._transform->basis; + } else { + return Quaternion(); + } +} + +Variant::operator Transform2D() const { + if (type == TRANSFORM2D) { + return *_data._transform2d; + } else if (type == TRANSFORM) { + const Transform &t = *_data._transform; + Transform2D m; + m.columns[0][0] = t.basis.rows[0][0]; + m.columns[0][1] = t.basis.rows[1][0]; + m.columns[1][0] = t.basis.rows[0][1]; + m.columns[1][1] = t.basis.rows[1][1]; + m.columns[2][0] = t.origin[0]; + m.columns[2][1] = t.origin[1]; + return m; + } else { + return Transform2D(); + } +} + +Variant::operator Transform() const { + if (type == TRANSFORM) { + return *_data._transform; + } else if (type == BASIS) { + return Transform(*_data._basis, Vector3()); + } else if (type == QUATERNION) { + return Transform(Basis(*reinterpret_cast(_data._mem)), Vector3()); + } else if (type == TRANSFORM2D) { + const Transform2D &t = *_data._transform2d; + Transform m; + m.basis.rows[0][0] = t.columns[0][0]; + m.basis.rows[1][0] = t.columns[0][1]; + m.basis.rows[0][1] = t.columns[1][0]; + m.basis.rows[1][1] = t.columns[1][1]; + m.origin[0] = t.columns[2][0]; + m.origin[1] = t.columns[2][1]; + return m; + } else if (type == PROJECTION) { + return *_data._projection; + } else { + return Transform(); + } +} + +Variant::operator Projection() const { + if (type == TRANSFORM) { + return *_data._transform; + } else if (type == BASIS) { + return Transform(*_data._basis, Vector3()); + } else if (type == QUATERNION) { + return Transform(Basis(*reinterpret_cast(_data._mem)), Vector3()); + } else if (type == TRANSFORM2D) { + const Transform2D &t = *_data._transform2d; + Transform m; + m.basis.rows[0][0] = t.columns[0][0]; + m.basis.rows[1][0] = t.columns[0][1]; + m.basis.rows[0][1] = t.columns[1][0]; + m.basis.rows[1][1] = t.columns[1][1]; + m.origin[0] = t.columns[2][0]; + m.origin[1] = t.columns[2][1]; + return m; + } else if (type == PROJECTION) { + return *_data._projection; + } else { + return Projection(); + } +} + +Variant::operator Color() const { + if (type == COLOR) { + return *reinterpret_cast(_data._mem); + } else if (type == STRING) { + return Color::html(operator String()); + } else if (type == INT) { + return Color::hex(operator int()); + } else { + return Color(); + } +} + +Variant::operator RefPtr() const { + if (type == OBJECT) { + return _get_obj().ref; + } else { + return RefPtr(); + } +} + +Variant::operator Object *() const { + if (type == OBJECT) { + return _OBJ_PTR(*this); + } else { + return nullptr; + } +} + +template +inline DA _convert_array(const SA &p_array) { + DA da; + da.resize(p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { + da.set(i, Variant(p_array.get(i))); + } + + return da; +} + +template +inline DA _convert_array_from_variant(const Variant &p_variant) { + switch (p_variant.get_type()) { + case Variant::ARRAY: { + return _convert_array(p_variant.operator Array()); + } + case Variant::POOL_BYTE_ARRAY: { + return _convert_array>(p_variant.operator PoolVector()); + } + case Variant::POOL_INT_ARRAY: { + return _convert_array>(p_variant.operator PoolVector()); + } + case Variant::POOL_REAL_ARRAY: { + return _convert_array>(p_variant.operator PoolVector()); + } + case Variant::POOL_STRING_ARRAY: { + return _convert_array>(p_variant.operator PoolVector()); + } + case Variant::POOL_VECTOR2_ARRAY: { + return _convert_array>(p_variant.operator PoolVector()); + } + case Variant::POOL_VECTOR2I_ARRAY: { + return _convert_array>(p_variant.operator PoolVector()); + } + case Variant::POOL_VECTOR3_ARRAY: { + return _convert_array>(p_variant.operator PoolVector()); + } + case Variant::POOL_VECTOR3I_ARRAY: { + return _convert_array>(p_variant.operator PoolVector()); + } + case Variant::POOL_VECTOR4_ARRAY: { + return _convert_array>(p_variant.operator PoolVector()); + } + case Variant::POOL_VECTOR4I_ARRAY: { + return _convert_array>(p_variant.operator PoolVector()); + } + case Variant::POOL_COLOR_ARRAY: { + return _convert_array>(p_variant.operator PoolVector()); + } + default: { + return DA(); + } + } +} + +Variant::operator Dictionary() const { + if (type == DICTIONARY) { + return *reinterpret_cast(_data._mem); + } else { + return Dictionary(); + } +} + + +Variant::operator Array() const { + if (type == ARRAY) { + return *reinterpret_cast(_data._mem); + } else { + return _convert_array_from_variant(*this); + } +} + +Variant::operator PoolVector() const { + if (type == POOL_BYTE_ARRAY) { + return *reinterpret_cast *>(_data._mem); + } else { + return _convert_array_from_variant>(*this); + } +} +Variant::operator PoolVector() const { + if (type == POOL_INT_ARRAY) { + return *reinterpret_cast *>(_data._mem); + } else { + return _convert_array_from_variant>(*this); + } +} +Variant::operator PoolVector() const { + if (type == POOL_REAL_ARRAY) { + return *reinterpret_cast *>(_data._mem); + } else { + return _convert_array_from_variant>(*this); + } +} + +Variant::operator PoolVector() const { + if (type == POOL_STRING_ARRAY) { + return *reinterpret_cast *>(_data._mem); + } else { + return _convert_array_from_variant>(*this); + } +} +Variant::operator PoolVector() const { + if (type == POOL_VECTOR2_ARRAY) { + return *reinterpret_cast *>(_data._mem); + } else { + return _convert_array_from_variant>(*this); + } +} +Variant::operator PoolVector() const { + if (type == POOL_VECTOR2I_ARRAY) { + return *reinterpret_cast *>(_data._mem); + } else { + return _convert_array_from_variant>(*this); + } +} +Variant::operator PoolVector() const { + if (type == POOL_VECTOR3_ARRAY) { + return *reinterpret_cast *>(_data._mem); + } else { + return _convert_array_from_variant>(*this); + } +} +Variant::operator PoolVector() const { + if (type == POOL_VECTOR3I_ARRAY) { + return *reinterpret_cast *>(_data._mem); + } else { + return _convert_array_from_variant>(*this); + } +} +Variant::operator PoolVector() const { + if (type == POOL_VECTOR4_ARRAY) { + return *reinterpret_cast *>(_data._mem); + } else { + return _convert_array_from_variant>(*this); + } +} +Variant::operator PoolVector() const { + if (type == POOL_VECTOR4I_ARRAY) { + return *reinterpret_cast *>(_data._mem); + } else { + return _convert_array_from_variant>(*this); + } +} +Variant::operator PoolVector() const { + if (type == POOL_COLOR_ARRAY) { + return *reinterpret_cast *>(_data._mem); + } else { + return _convert_array_from_variant>(*this); + } +} + +/* helpers */ + +Variant::operator PoolVector() const { + Array va = operator Array(); + PoolVector planes; + int va_size = va.size(); + if (va_size == 0) { + return planes; + } + + planes.resize(va_size); + PoolVector::Write w = planes.write(); + + for (int i = 0; i < va_size; i++) { + w[i] = va[i]; + } + + return planes; +} + +Variant::operator PoolVector() const { + PoolVector va = operator PoolVector(); + PoolVector faces; + int va_size = va.size(); + if (va_size == 0) { + return faces; + } + + faces.resize(va_size / 3); + PoolVector::Write w = faces.write(); + PoolVector::Read r = va.read(); + + for (int i = 0; i < va_size; i++) { + w[i / 3].vertex[i % 3] = r[i]; + } + + return faces; +} + +Variant::operator Vector() const { + Array va = operator Array(); + Vector planes; + int va_size = va.size(); + if (va_size == 0) { + return planes; + } + + planes.resize(va_size); + + for (int i = 0; i < va_size; i++) { + planes.write[i] = va[i]; + } + + return planes; +} + +Variant::operator Vector() const { + Array from = operator Array(); + Vector to; + int len = from.size(); + to.resize(len); + for (int i = 0; i < len; i++) { + to.write[i] = from[i]; + } + return to; +} + +Variant::operator Vector() const { + PoolVector from = operator PoolVector(); + Vector to; + int len = from.size(); + to.resize(len); + for (int i = 0; i < len; i++) { + to.write[i] = from[i]; + } + return to; +} +Variant::operator Vector() const { + PoolVector from = operator PoolVector(); + Vector to; + int len = from.size(); + to.resize(len); + for (int i = 0; i < len; i++) { + to.write[i] = from[i]; + } + return to; +} +Variant::operator Vector() const { + PoolVector from = operator PoolVector(); + Vector to; + int len = from.size(); + to.resize(len); + for (int i = 0; i < len; i++) { + to.write[i] = from[i]; + } + return to; +} + +Variant::operator Vector() const { + PoolVector from = operator PoolVector(); + Vector to; + int len = from.size(); + to.resize(len); + for (int i = 0; i < len; i++) { + to.write[i] = from[i]; + } + return to; +} +Variant::operator Vector() const { + PoolVector from = operator PoolVector(); + Vector to; + int len = from.size(); + to.resize(len); + for (int i = 0; i < len; i++) { + to.write[i] = from[i]; + } + return to; +} + +Variant::operator Vector() const { + PoolVector from = operator PoolVector(); + Vector to; + int len = from.size(); + if (len == 0) { + return Vector(); + } + to.resize(len); + PoolVector::Read r = from.read(); + Vector2 *w = to.ptrw(); + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + return to; +} +Variant::operator Vector() const { + PoolVector from = operator PoolVector(); + Vector to; + int len = from.size(); + if (len == 0) { + return Vector(); + } + to.resize(len); + PoolVector::Read r = from.read(); + Vector2i *w = to.ptrw(); + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + return to; +} + +Variant::operator Vector() const { + PoolVector from = operator PoolVector(); + Vector to; + int len = from.size(); + if (len == 0) { + return Vector(); + } + to.resize(len); + PoolVector::Read r = from.read(); + Vector3 *w = to.ptrw(); + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + return to; +} +Variant::operator Vector() const { + PoolVector from = operator PoolVector(); + Vector to; + int len = from.size(); + if (len == 0) { + return Vector(); + } + to.resize(len); + PoolVector::Read r = from.read(); + Vector3i *w = to.ptrw(); + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + return to; +} + +Variant::operator Vector() const { + PoolVector from = operator PoolVector(); + Vector to; + int len = from.size(); + if (len == 0) { + return Vector(); + } + to.resize(len); + PoolVector::Read r = from.read(); + Vector4 *w = to.ptrw(); + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + return to; +} +Variant::operator Vector() const { + PoolVector from = operator PoolVector(); + Vector to; + int len = from.size(); + if (len == 0) { + return Vector(); + } + to.resize(len); + PoolVector::Read r = from.read(); + Vector4i *w = to.ptrw(); + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + return to; +} + +Variant::operator Vector() const { + PoolVector from = operator PoolVector(); + Vector to; + int len = from.size(); + if (len == 0) { + return Vector(); + } + to.resize(len); + PoolVector::Read r = from.read(); + Color *w = to.ptrw(); + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + return to; +} + +Variant::operator Margin() const { + return (Margin) operator int(); +} +Variant::operator Side() const { + return (Side) operator int(); +} +Variant::operator Orientation() const { + return (Orientation) operator int(); +} + +Variant::Variant(bool p_bool) { + type = BOOL; + _data._bool = p_bool; +} + +/* +Variant::Variant(long unsigned int p_long) { + + type=INT; + _data._int=p_long; +}; +*/ + +Variant::Variant(signed int p_int) { + type = INT; + _data._int = p_int; +} +Variant::Variant(unsigned int p_int) { + type = INT; + _data._int = p_int; +} + +#ifdef NEED_LONG_INT + +Variant::Variant(signed long p_int) { + type = INT; + _data._int = p_int; +} +Variant::Variant(unsigned long p_int) { + type = INT; + _data._int = p_int; +} +#endif + +Variant::Variant(int64_t p_int) { + type = INT; + _data._int = p_int; +} + +Variant::Variant(uint64_t p_int) { + type = INT; + _data._int = p_int; +} + +Variant::Variant(signed short p_short) { + type = INT; + _data._int = p_short; +} +Variant::Variant(unsigned short p_short) { + type = INT; + _data._int = p_short; +} +Variant::Variant(signed char p_char) { + type = INT; + _data._int = p_char; +} +Variant::Variant(unsigned char p_char) { + type = INT; + _data._int = p_char; +} +Variant::Variant(float p_float) { + type = REAL; + _data._real = p_float; +} +Variant::Variant(double p_double) { + type = REAL; + _data._real = p_double; +} + +Variant::Variant(const StringName &p_string) { + type = STRING_NAME; + memnew_placement(_data._mem, StringName(p_string)); +} +Variant::Variant(const String &p_string) { + type = STRING; + memnew_placement(_data._mem, String(p_string)); +} + +Variant::Variant(const char *const p_cstring) { + type = STRING; + memnew_placement(_data._mem, String((const char *)p_cstring)); +} + +Variant::Variant(const CharType *p_wstring) { + type = STRING; + memnew_placement(_data._mem, String(p_wstring)); +} + +Variant::Variant(const Rect2 &p_rect2) { + type = RECT2; + memnew_placement(_data._mem, Rect2(p_rect2)); +} +Variant::Variant(const Rect2i &p_rect2) { + type = RECT2I; + memnew_placement(_data._mem, Rect2i(p_rect2)); +} + +Variant::Variant(const Vector2 &p_vector2) { + type = VECTOR2; + memnew_placement(_data._mem, Vector2(p_vector2)); +} +Variant::Variant(const Vector2i &p_vector2) { + type = VECTOR2I; + memnew_placement(_data._mem, Vector2i(p_vector2)); +} + +Variant::Variant(const Vector3 &p_vector3) { + type = VECTOR3; + memnew_placement(_data._mem, Vector3(p_vector3)); +} +Variant::Variant(const Vector3i &p_vector3) { + type = VECTOR3I; + memnew_placement(_data._mem, Vector3i(p_vector3)); +} + +Variant::Variant(const Vector4 &p_vector4) { + type = VECTOR4; + memnew_placement(_data._mem, Vector4(p_vector4)); +} +Variant::Variant(const Vector4i &p_vector4) { + type = VECTOR4I; + memnew_placement(_data._mem, Vector4i(p_vector4)); +} + +Variant::Variant(const Plane &p_plane) { + type = PLANE; + memnew_placement(_data._mem, Plane(p_plane)); +} +Variant::Variant(const ::AABB &p_aabb) { + type = AABB; + _data._aabb = memnew(::AABB(p_aabb)); +} + +Variant::Variant(const Basis &p_matrix) { + type = BASIS; + _data._basis = memnew(Basis(p_matrix)); +} + +Variant::Variant(const Quaternion &p_quat) { + type = QUATERNION; + memnew_placement(_data._mem, Quaternion(p_quat)); +} +Variant::Variant(const Transform &p_transform) { + type = TRANSFORM; + _data._transform = memnew(Transform(p_transform)); +} + +Variant::Variant(const Transform2D &p_transform) { + type = TRANSFORM2D; + _data._transform2d = memnew(Transform2D(p_transform)); +} + +Variant::Variant(const Projection &p_projection) { + type = PROJECTION; + _data._projection = memnew(Projection(p_projection)); +} + +Variant::Variant(const Color &p_color) { + type = COLOR; + memnew_placement(_data._mem, Color(p_color)); +} + +Variant::Variant(const RefPtr &p_resource) { + type = OBJECT; + memnew_placement(_data._mem, ObjData); + _get_obj().rc = nullptr; + _get_obj().ref = p_resource; +} + +Variant::Variant(const Object *p_object) { + type = OBJECT; + Object *obj = const_cast(p_object); + + memnew_placement(_data._mem, ObjData); + Reference *ref = Object::cast_to(obj); + if (unlikely(ref)) { + *reinterpret_cast *>(_get_obj().ref.get_data()) = Ref(ref); + _get_obj().rc = nullptr; + } else { + _get_obj().rc = likely(obj) ? obj->_use_rc() : nullptr; + } +} + +Variant::Variant(const Dictionary &p_dictionary) { + type = DICTIONARY; + memnew_placement(_data._mem, Dictionary(p_dictionary)); +} + +Variant::Variant(const Array &p_array) { + type = ARRAY; + memnew_placement(_data._mem, Array(p_array)); +} + +Variant::Variant(const PoolVector &p_array) { + type = ARRAY; + + Array *plane_array = memnew_placement(_data._mem, Array); + + plane_array->resize(p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { + plane_array->operator[](i) = Variant(p_array[i]); + } +} + +Variant::Variant(const Vector &p_array) { + type = ARRAY; + + Array *plane_array = memnew_placement(_data._mem, Array); + + plane_array->resize(p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { + plane_array->operator[](i) = Variant(p_array[i]); + } +} + +Variant::Variant(const PoolVector &p_raw_array) { + type = POOL_BYTE_ARRAY; + memnew_placement(_data._mem, PoolVector(p_raw_array)); +} +Variant::Variant(const PoolVector &p_int_array) { + type = POOL_INT_ARRAY; + memnew_placement(_data._mem, PoolVector(p_int_array)); +} +Variant::Variant(const PoolVector &p_real_array) { + type = POOL_REAL_ARRAY; + memnew_placement(_data._mem, PoolVector(p_real_array)); +} +Variant::Variant(const PoolVector &p_string_array) { + type = POOL_STRING_ARRAY; + memnew_placement(_data._mem, PoolVector(p_string_array)); +} +Variant::Variant(const PoolVector &p_vector2_array) { + type = POOL_VECTOR2_ARRAY; + memnew_placement(_data._mem, PoolVector(p_vector2_array)); +} +Variant::Variant(const PoolVector &p_vector2_array) { + type = POOL_VECTOR2I_ARRAY; + memnew_placement(_data._mem, PoolVector(p_vector2_array)); +} +Variant::Variant(const PoolVector &p_vector3_array) { + type = POOL_VECTOR3_ARRAY; + memnew_placement(_data._mem, PoolVector(p_vector3_array)); +} +Variant::Variant(const PoolVector &p_vector3_array) { + type = POOL_VECTOR3I_ARRAY; + memnew_placement(_data._mem, PoolVector(p_vector3_array)); +} + +Variant::Variant(const PoolVector &p_vector4_array) { + type = POOL_VECTOR4_ARRAY; + memnew_placement(_data._mem, PoolVector(p_vector4_array)); +} +Variant::Variant(const PoolVector &p_vector4_array) { + type = POOL_VECTOR4I_ARRAY; + memnew_placement(_data._mem, PoolVector(p_vector4_array)); +} + +Variant::Variant(const PoolVector &p_color_array) { + type = POOL_COLOR_ARRAY; + memnew_placement(_data._mem, PoolVector(p_color_array)); +} + +Variant::Variant(const PoolVector &p_face_array) { + PoolVector vertices; + int face_count = p_face_array.size(); + vertices.resize(face_count * 3); + + if (face_count) { + PoolVector::Read r = p_face_array.read(); + PoolVector::Write w = vertices.write(); + + for (int i = 0; i < face_count; i++) { + for (int j = 0; j < 3; j++) { + w[i * 3 + j] = r[i].vertex[j]; + } + } + } + + type = NIL; + + *this = vertices; +} + +/* helpers */ + +Variant::Variant(const Vector &p_array) { + type = NIL; + Array v; + int len = p_array.size(); + v.resize(len); + for (int i = 0; i < len; i++) { + v.set(i, p_array[i]); + } + *this = v; +} + +Variant::Variant(const Vector &p_array) { + type = NIL; + PoolVector v; + int len = p_array.size(); + v.resize(len); + for (int i = 0; i < len; i++) { + v.set(i, p_array[i]); + } + *this = v; +} + +Variant::Variant(const Vector &p_array) { + type = NIL; + PoolVector v; + int len = p_array.size(); + v.resize(len); + for (int i = 0; i < len; i++) { + v.set(i, p_array[i]); + } + *this = v; +} + +Variant::Variant(const Vector &p_array) { + type = NIL; + PoolVector v; + int len = p_array.size(); + v.resize(len); + for (int i = 0; i < len; i++) { + v.set(i, p_array[i]); + } + *this = v; +} + +Variant::Variant(const Vector &p_array) { + type = NIL; + PoolVector v; + int len = p_array.size(); + v.resize(len); + for (int i = 0; i < len; i++) { + v.set(i, p_array[i]); + } + *this = v; +} + +Variant::Variant(const Vector &p_array) { + type = NIL; + PoolVector v; + int len = p_array.size(); + v.resize(len); + for (int i = 0; i < len; i++) { + v.set(i, p_array[i]); + } + *this = v; +} + +Variant::Variant(const Vector &p_array) { + type = NIL; + PoolVector v; + int len = p_array.size(); + if (len > 0) { + v.resize(len); + PoolVector::Write w = v.write(); + const Vector2 *r = p_array.ptr(); + + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + } + *this = v; +} +Variant::Variant(const Vector &p_array) { + type = NIL; + PoolVector v; + int len = p_array.size(); + if (len > 0) { + v.resize(len); + PoolVector::Write w = v.write(); + const Vector2i *r = p_array.ptr(); + + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + } + *this = v; +} + +Variant::Variant(const Vector &p_array) { + type = NIL; + PoolVector v; + int len = p_array.size(); + if (len > 0) { + v.resize(len); + PoolVector::Write w = v.write(); + const Vector3 *r = p_array.ptr(); + + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + } + *this = v; +} +Variant::Variant(const Vector &p_array) { + type = NIL; + PoolVector v; + int len = p_array.size(); + if (len > 0) { + v.resize(len); + PoolVector::Write w = v.write(); + const Vector3i *r = p_array.ptr(); + + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + } + *this = v; +} + +Variant::Variant(const Vector &p_array) { + type = NIL; + PoolVector v; + int len = p_array.size(); + if (len > 0) { + v.resize(len); + PoolVector::Write w = v.write(); + const Vector4 *r = p_array.ptr(); + + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + } + *this = v; +} +Variant::Variant(const Vector &p_array) { + type = NIL; + PoolVector v; + int len = p_array.size(); + if (len > 0) { + v.resize(len); + PoolVector::Write w = v.write(); + const Vector4i *r = p_array.ptr(); + + for (int i = 0; i < len; i++) { + w[i] = r[i]; + } + } + *this = v; +} + +Variant::Variant(const Vector &p_array) { + type = NIL; + PoolVector v; + int len = p_array.size(); + v.resize(len); + for (int i = 0; i < len; i++) { + v.set(i, p_array[i]); + } + *this = v; +} + +void Variant::operator=(const Variant &p_variant) { + if (unlikely(this == &p_variant)) { + return; + } + + if (unlikely(type != p_variant.type)) { + reference(p_variant); + return; + } + + switch (p_variant.type) { + case NIL: { + // none + } break; + + // atomic types + case BOOL: { + _data._bool = p_variant._data._bool; + } break; + case INT: { + _data._int = p_variant._data._int; + } break; + case REAL: { + _data._real = p_variant._data._real; + } break; + case STRING: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + + // math types + case RECT2: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + case RECT2I: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + case VECTOR2: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + case VECTOR2I: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + case VECTOR3: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + case VECTOR3I: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + case VECTOR4: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + case VECTOR4I: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + + case PLANE: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + case QUATERNION: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + case AABB: { + *_data._aabb = *(p_variant._data._aabb); + } break; + case BASIS: { + *_data._basis = *(p_variant._data._basis); + } break; + case TRANSFORM: { + *_data._transform = *(p_variant._data._transform); + } break; + case TRANSFORM2D: { + *_data._transform2d = *(p_variant._data._transform2d); + } break; + case PROJECTION: { + *_data._projection = *(p_variant._data._projection); + } break; + + // misc types + case COLOR: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + case OBJECT: { + if (likely(_get_obj().rc)) { + if (unlikely(_get_obj().rc->decrement())) { + memdelete(_get_obj().rc); + } + } + *reinterpret_cast(_data._mem) = p_variant._get_obj(); + if (likely(_get_obj().rc)) { + _get_obj().rc->increment(); + } + } break; + case STRING_NAME: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + case DICTIONARY: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + case ARRAY: { + *reinterpret_cast(_data._mem) = *reinterpret_cast(p_variant._data._mem); + } break; + + // arrays + case POOL_BYTE_ARRAY: { + *reinterpret_cast *>(_data._mem) = *reinterpret_cast *>(p_variant._data._mem); + } break; + case POOL_INT_ARRAY: { + *reinterpret_cast *>(_data._mem) = *reinterpret_cast *>(p_variant._data._mem); + } break; + case POOL_REAL_ARRAY: { + *reinterpret_cast *>(_data._mem) = *reinterpret_cast *>(p_variant._data._mem); + } break; + case POOL_STRING_ARRAY: { + *reinterpret_cast *>(_data._mem) = *reinterpret_cast *>(p_variant._data._mem); + } break; + case POOL_VECTOR2_ARRAY: { + *reinterpret_cast *>(_data._mem) = *reinterpret_cast *>(p_variant._data._mem); + } break; + case POOL_VECTOR2I_ARRAY: { + *reinterpret_cast *>(_data._mem) = *reinterpret_cast *>(p_variant._data._mem); + } break; + case POOL_VECTOR3_ARRAY: { + *reinterpret_cast *>(_data._mem) = *reinterpret_cast *>(p_variant._data._mem); + } break; + case POOL_VECTOR3I_ARRAY: { + *reinterpret_cast *>(_data._mem) = *reinterpret_cast *>(p_variant._data._mem); + } break; + case POOL_VECTOR4_ARRAY: { + *reinterpret_cast *>(_data._mem) = *reinterpret_cast *>(p_variant._data._mem); + } break; + case POOL_VECTOR4I_ARRAY: { + *reinterpret_cast *>(_data._mem) = *reinterpret_cast *>(p_variant._data._mem); + } break; + case POOL_COLOR_ARRAY: { + *reinterpret_cast *>(_data._mem) = *reinterpret_cast *>(p_variant._data._mem); + } break; + default: { + } + } +} + +Variant::Variant(const Variant &p_variant) { + type = NIL; + reference(p_variant); +} + +/* +Variant::~Variant() { + + clear(); +}*/ + +uint32_t Variant::hash() const { + return recursive_hash(0); +} + +uint32_t Variant::recursive_hash(int p_recursion_count) const { + switch (type) { + case NIL: { + return 0; + } break; + + case BOOL: { + return _data._bool ? 1 : 0; + } break; + case INT: { + return hash_one_uint64((uint64_t)_data._int); + } break; + case REAL: { + return hash_murmur3_one_float(_data._real); + } break; + case STRING: { + return reinterpret_cast(_data._mem)->hash(); + } break; + + // math types + case RECT2: { + return HashMapHasherDefault::hash(*reinterpret_cast(_data._mem)); + } break; + case RECT2I: { + return HashMapHasherDefault::hash(*reinterpret_cast(_data._mem)); + } break; + case VECTOR2: { + return HashMapHasherDefault::hash(*reinterpret_cast(_data._mem)); + } break; + case VECTOR2I: { + return HashMapHasherDefault::hash(*reinterpret_cast(_data._mem)); + } break; + case VECTOR3: { + return HashMapHasherDefault::hash(*reinterpret_cast(_data._mem)); + } break; + case VECTOR3I: { + return HashMapHasherDefault::hash(*reinterpret_cast(_data._mem)); + } break; + case VECTOR4: { + return HashMapHasherDefault::hash(*reinterpret_cast(_data._mem)); + } break; + case VECTOR4I: { + return HashMapHasherDefault::hash(*reinterpret_cast(_data._mem)); + } break; + case PLANE: { + uint32_t h = HASH_MURMUR3_SEED; + const Plane &p = *reinterpret_cast(_data._mem); + h = hash_murmur3_one_real(p.normal.x, h); + h = hash_murmur3_one_real(p.normal.y, h); + h = hash_murmur3_one_real(p.normal.z, h); + h = hash_murmur3_one_real(p.d, h); + return hash_fmix32(h); + } break; + case QUATERNION: { + uint32_t h = HASH_MURMUR3_SEED; + const Quaternion &q = *reinterpret_cast(_data._mem); + h = hash_murmur3_one_real(q.x, h); + h = hash_murmur3_one_real(q.y, h); + h = hash_murmur3_one_real(q.z, h); + h = hash_murmur3_one_real(q.w, h); + return hash_fmix32(h); + } break; + case AABB: { + return HashMapHasherDefault::hash(*_data._aabb); + } break; + case BASIS: { + uint32_t h = HASH_MURMUR3_SEED; + const Basis &b = *_data._basis; + h = hash_murmur3_one_real(b[0].x, h); + h = hash_murmur3_one_real(b[0].y, h); + h = hash_murmur3_one_real(b[0].z, h); + h = hash_murmur3_one_real(b[1].x, h); + h = hash_murmur3_one_real(b[1].y, h); + h = hash_murmur3_one_real(b[1].z, h); + h = hash_murmur3_one_real(b[2].x, h); + h = hash_murmur3_one_real(b[2].y, h); + h = hash_murmur3_one_real(b[2].z, h); + return hash_fmix32(h); + } break; + case TRANSFORM: { + uint32_t h = HASH_MURMUR3_SEED; + const Transform &t = *_data._transform; + h = hash_murmur3_one_real(t.basis[0].x, h); + h = hash_murmur3_one_real(t.basis[0].y, h); + h = hash_murmur3_one_real(t.basis[0].z, h); + h = hash_murmur3_one_real(t.basis[1].x, h); + h = hash_murmur3_one_real(t.basis[1].y, h); + h = hash_murmur3_one_real(t.basis[1].z, h); + h = hash_murmur3_one_real(t.basis[2].x, h); + h = hash_murmur3_one_real(t.basis[2].y, h); + h = hash_murmur3_one_real(t.basis[2].z, h); + h = hash_murmur3_one_real(t.origin.x, h); + h = hash_murmur3_one_real(t.origin.y, h); + h = hash_murmur3_one_real(t.origin.z, h); + return hash_fmix32(h); + } break; + case TRANSFORM2D: { + uint32_t h = HASH_MURMUR3_SEED; + const Transform2D &t = *_data._transform2d; + h = hash_murmur3_one_real(t[0].x, h); + h = hash_murmur3_one_real(t[0].y, h); + h = hash_murmur3_one_real(t[1].x, h); + h = hash_murmur3_one_real(t[1].y, h); + h = hash_murmur3_one_real(t[2].x, h); + h = hash_murmur3_one_real(t[2].y, h); + + return hash_fmix32(h); + } break; + case PROJECTION: { + uint32_t h = HASH_MURMUR3_SEED; + const Projection &t = *_data._projection; + h = hash_murmur3_one_real(t.matrix[0].x, h); + h = hash_murmur3_one_real(t.matrix[0].y, h); + h = hash_murmur3_one_real(t.matrix[0].z, h); + h = hash_murmur3_one_real(t.matrix[0].w, h); + h = hash_murmur3_one_real(t.matrix[1].x, h); + h = hash_murmur3_one_real(t.matrix[1].y, h); + h = hash_murmur3_one_real(t.matrix[1].z, h); + h = hash_murmur3_one_real(t.matrix[1].w, h); + h = hash_murmur3_one_real(t.matrix[2].x, h); + h = hash_murmur3_one_real(t.matrix[2].y, h); + h = hash_murmur3_one_real(t.matrix[2].z, h); + h = hash_murmur3_one_real(t.matrix[2].w, h); + h = hash_murmur3_one_real(t.matrix[3].x, h); + h = hash_murmur3_one_real(t.matrix[3].y, h); + h = hash_murmur3_one_real(t.matrix[3].z, h); + h = hash_murmur3_one_real(t.matrix[3].w, h); + return hash_fmix32(h); + } break; + + // misc types + case COLOR: { + uint32_t h = HASH_MURMUR3_SEED; + const Color &c = *reinterpret_cast(_data._mem); + h = hash_murmur3_one_float(c.r, h); + h = hash_murmur3_one_float(c.g, h); + h = hash_murmur3_one_float(c.b, h); + h = hash_murmur3_one_float(c.a, h); + return hash_fmix32(h); + } break; + case OBJECT: { + return hash_one_uint64(hash_make_uint64_t(_UNSAFE_OBJ_PROXY_PTR(*this))); + } break; + case STRING_NAME: { + return reinterpret_cast(_data._mem)->hash(); + } break; + case DICTIONARY: { + return reinterpret_cast(_data._mem)->recursive_hash(p_recursion_count); + } break; + case ARRAY: { + const Array &arr = *reinterpret_cast(_data._mem); + return arr.recursive_hash(p_recursion_count); + } break; + + case POOL_BYTE_ARRAY: { + const PoolVector &arr = *reinterpret_cast *>(_data._mem); + int len = arr.size(); + if (likely(len)) { + PoolVector::Read r = arr.read(); + return hash_murmur3_buffer((uint8_t *)&r[0], len); + } else { + return hash_murmur3_one_64(0); + } + + } break; + case POOL_INT_ARRAY: { + const PoolVector &arr = *reinterpret_cast *>(_data._mem); + int len = arr.size(); + if (likely(len)) { + PoolVector::Read r = arr.read(); + return hash_murmur3_buffer((uint8_t *)&r[0], len * sizeof(int)); + } else { + return hash_murmur3_one_64(0); + } + + } break; + case POOL_REAL_ARRAY: { + const PoolVector &arr = *reinterpret_cast *>(_data._mem); + int len = arr.size(); + + if (likely(len)) { + PoolVector::Read r = arr.read(); + uint32_t h = HASH_MURMUR3_SEED; + + for (int i = 0; i < len; i++) { + h = hash_murmur3_one_real(r[i], h); + } + + return hash_fmix32(h); + } else { + return hash_murmur3_one_real(0.0); + } + + } break; + case POOL_STRING_ARRAY: { + uint32_t hash = HASH_MURMUR3_SEED; + const PoolVector &arr = *reinterpret_cast *>(_data._mem); + int len = arr.size(); + + if (likely(len)) { + PoolVector::Read r = arr.read(); + + for (int i = 0; i < len; i++) { + hash = hash_murmur3_one_32(r[i].hash(), hash); + } + + hash = hash_fmix32(hash); + } + + return hash; + } break; + case POOL_VECTOR2_ARRAY: { + uint32_t hash = HASH_MURMUR3_SEED; + const PoolVector &arr = *reinterpret_cast *>(_data._mem); + int len = arr.size(); + + if (likely(len)) { + PoolVector::Read r = arr.read(); + + for (int i = 0; i < len; i++) { + hash = hash_murmur3_one_real(r[i].x, hash); + hash = hash_murmur3_one_real(r[i].y, hash); + } + + hash = hash_fmix32(hash); + } + + return hash; + } break; + case POOL_VECTOR2I_ARRAY: { + uint32_t hash = HASH_MURMUR3_SEED; + const PoolVector &arr = *reinterpret_cast *>(_data._mem); + int len = arr.size(); + + if (likely(len)) { + PoolVector::Read r = arr.read(); + + for (int i = 0; i < len; i++) { + hash = hash_murmur3_one_32(r[i].x, hash); + hash = hash_murmur3_one_32(r[i].y, hash); + } + + hash = hash_fmix32(hash); + } + + return hash; + } break; + case POOL_VECTOR3_ARRAY: { + uint32_t hash = HASH_MURMUR3_SEED; + const PoolVector &arr = *reinterpret_cast *>(_data._mem); + int len = arr.size(); + + if (likely(len)) { + PoolVector::Read r = arr.read(); + + for (int i = 0; i < len; i++) { + hash = hash_murmur3_one_real(r[i].x, hash); + hash = hash_murmur3_one_real(r[i].y, hash); + hash = hash_murmur3_one_real(r[i].z, hash); + } + + hash = hash_fmix32(hash); + } + + return hash; + } break; + case POOL_VECTOR3I_ARRAY: { + uint32_t hash = HASH_MURMUR3_SEED; + const PoolVector &arr = *reinterpret_cast *>(_data._mem); + int len = arr.size(); + + if (likely(len)) { + PoolVector::Read r = arr.read(); + + for (int i = 0; i < len; i++) { + hash = hash_murmur3_one_32(r[i].x, hash); + hash = hash_murmur3_one_32(r[i].y, hash); + hash = hash_murmur3_one_32(r[i].z, hash); + } + + hash = hash_fmix32(hash); + } + + return hash; + } break; + case POOL_VECTOR4_ARRAY: { + uint32_t hash = HASH_MURMUR3_SEED; + const PoolVector &arr = *reinterpret_cast *>(_data._mem); + int len = arr.size(); + + if (likely(len)) { + PoolVector::Read r = arr.read(); + + for (int i = 0; i < len; i++) { + hash = hash_murmur3_one_real(r[i].x, hash); + hash = hash_murmur3_one_real(r[i].y, hash); + hash = hash_murmur3_one_real(r[i].z, hash); + hash = hash_murmur3_one_real(r[i].w, hash); + } + + hash = hash_fmix32(hash); + } + + return hash; + } break; + case POOL_VECTOR4I_ARRAY: { + uint32_t hash = HASH_MURMUR3_SEED; + const PoolVector &arr = *reinterpret_cast *>(_data._mem); + int len = arr.size(); + + if (likely(len)) { + PoolVector::Read r = arr.read(); + + for (int i = 0; i < len; i++) { + hash = hash_murmur3_one_32(r[i].x, hash); + hash = hash_murmur3_one_32(r[i].y, hash); + hash = hash_murmur3_one_32(r[i].z, hash); + hash = hash_murmur3_one_32(r[i].w, hash); + } + + hash = hash_fmix32(hash); + } + + return hash; + } break; + case POOL_COLOR_ARRAY: { + uint32_t hash = HASH_MURMUR3_SEED; + const PoolVector &arr = *reinterpret_cast *>(_data._mem); + int len = arr.size(); + + if (likely(len)) { + PoolVector::Read r = arr.read(); + + for (int i = 0; i < len; i++) { + hash = hash_murmur3_one_real(r[i].r, hash); + hash = hash_murmur3_one_real(r[i].g, hash); + hash = hash_murmur3_one_real(r[i].b, hash); + hash = hash_murmur3_one_real(r[i].a, hash); + } + + hash = hash_fmix32(hash); + } + + return hash; + } break; + default: { + } + } + + return 0; +} + +#define hash_compare_scalar(p_lhs, p_rhs) \ + (((p_lhs) == (p_rhs)) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs))) + +#define hash_compare_vector2(p_lhs, p_rhs) \ + (hash_compare_scalar((p_lhs).x, (p_rhs).x) && \ + hash_compare_scalar((p_lhs).y, (p_rhs).y)) + +#define hash_compare_vector2i(p_lhs, p_rhs) \ + (((p_lhs).x == (p_rhs).x) && \ + ((p_lhs).y == (p_rhs).y)) + +#define hash_compare_vector3(p_lhs, p_rhs) \ + (hash_compare_scalar((p_lhs).x, (p_rhs).x) && \ + hash_compare_scalar((p_lhs).y, (p_rhs).y) && \ + hash_compare_scalar((p_lhs).z, (p_rhs).z)) + +#define hash_compare_vector3i(p_lhs, p_rhs) \ + (((p_lhs).x == (p_rhs).x) && \ + ((p_lhs).y == (p_rhs).y) && \ + ((p_lhs).z == (p_rhs).z)) + +#define hash_compare_vector4(p_lhs, p_rhs) \ + (hash_compare_scalar((p_lhs).x, (p_rhs).x) && \ + hash_compare_scalar((p_lhs).y, (p_rhs).y) && \ + hash_compare_scalar((p_lhs).z, (p_rhs).z) && \ + hash_compare_scalar((p_lhs).w, (p_rhs).w)) + +#define hash_compare_vector4i(p_lhs, p_rhs) \ + (((p_lhs).x == (p_rhs).x) && \ + ((p_lhs).y == (p_rhs).y) && \ + ((p_lhs).z == (p_rhs).z) && \ + ((p_lhs).w == (p_rhs).w)) + +#define hash_compare_quat(p_lhs, p_rhs) \ + (hash_compare_scalar((p_lhs).x, (p_rhs).x) && \ + hash_compare_scalar((p_lhs).y, (p_rhs).y) && \ + hash_compare_scalar((p_lhs).z, (p_rhs).z) && \ + hash_compare_scalar((p_lhs).w, (p_rhs).w)) + +#define hash_compare_color(p_lhs, p_rhs) \ + (hash_compare_scalar((p_lhs).r, (p_rhs).r) && \ + hash_compare_scalar((p_lhs).g, (p_rhs).g) && \ + hash_compare_scalar((p_lhs).b, (p_rhs).b) && \ + hash_compare_scalar((p_lhs).a, (p_rhs).a)) + +#define hash_compare_pool_array(p_lhs, p_rhs, p_type, p_compare_func) \ + const PoolVector &l = *reinterpret_cast *>(p_lhs); \ + const PoolVector &r = *reinterpret_cast *>(p_rhs); \ + \ + if (l.size() != r.size()) \ + return false; \ + \ + PoolVector::Read lr = l.read(); \ + PoolVector::Read rr = r.read(); \ + \ + for (int i = 0; i < l.size(); ++i) { \ + if (!p_compare_func((lr[i]), (rr[i]))) \ + return false; \ + } \ + \ + return true + +bool Variant::hash_compare(const Variant &p_variant) const { + if (type != p_variant.type) { + return false; + } + + switch (type) { + //BOOL + case INT: { + return _data._int == p_variant._data._int; + } break; + case REAL: { + return hash_compare_scalar(_data._real, p_variant._data._real); + } break; + case STRING: { + return *reinterpret_cast(_data._mem) == *reinterpret_cast(p_variant._data._mem); + } break; + + case RECT2: { + const Rect2 *l = reinterpret_cast(_data._mem); + const Rect2 *r = reinterpret_cast(p_variant._data._mem); + + return hash_compare_vector2(l->position, r->position) && + hash_compare_vector2(l->size, r->size); + } break; + case RECT2I: { + const Rect2i *l = reinterpret_cast(_data._mem); + const Rect2i *r = reinterpret_cast(p_variant._data._mem); + + return hash_compare_vector2i(l->position, r->position) && + hash_compare_vector2i(l->size, r->size); + } break; + case VECTOR2: { + const Vector2 *l = reinterpret_cast(_data._mem); + const Vector2 *r = reinterpret_cast(p_variant._data._mem); + + return hash_compare_vector2(*l, *r); + } break; + case VECTOR2I: { + const Vector2i *l = reinterpret_cast(_data._mem); + const Vector2i *r = reinterpret_cast(p_variant._data._mem); + + return hash_compare_vector2i(*l, *r); + } break; + case VECTOR3: { + const Vector3 *l = reinterpret_cast(_data._mem); + const Vector3 *r = reinterpret_cast(p_variant._data._mem); + + return hash_compare_vector3(*l, *r); + } break; + case VECTOR3I: { + const Vector3i *l = reinterpret_cast(_data._mem); + const Vector3i *r = reinterpret_cast(p_variant._data._mem); + + return hash_compare_vector3i(*l, *r); + } break; + case VECTOR4: { + const Vector4 *l = reinterpret_cast(_data._mem); + const Vector4 *r = reinterpret_cast(p_variant._data._mem); + + return hash_compare_vector4(*l, *r); + } break; + case VECTOR4I: { + const Vector4i *l = reinterpret_cast(_data._mem); + const Vector4i *r = reinterpret_cast(p_variant._data._mem); + + return hash_compare_vector4i(*l, *r); + } break; + + case PLANE: { + const Plane *l = reinterpret_cast(_data._mem); + const Plane *r = reinterpret_cast(p_variant._data._mem); + + return hash_compare_vector3(l->normal, r->normal) && + hash_compare_scalar(l->d, r->d); + } break; + case QUATERNION: { + const Quaternion *l = reinterpret_cast(_data._mem); + const Quaternion *r = reinterpret_cast(p_variant._data._mem); + + return hash_compare_quat(*l, *r); + } break; + case AABB: { + const ::AABB *l = _data._aabb; + const ::AABB *r = p_variant._data._aabb; + + return hash_compare_vector3(l->position, r->position) && + hash_compare_vector3(l->size, r->size); + + } break; + case BASIS: { + const Basis *l = _data._basis; + const Basis *r = p_variant._data._basis; + + for (int i = 0; i < 3; i++) { + if (!hash_compare_vector3(l->rows[i], r->rows[i])) { + return false; + } + } + + return true; + } break; + case TRANSFORM: { + const Transform *l = _data._transform; + const Transform *r = p_variant._data._transform; + + for (int i = 0; i < 3; i++) { + if (!hash_compare_vector3(l->basis.rows[i], r->basis.rows[i])) { + return false; + } + } + + return hash_compare_vector3(l->origin, r->origin); + } break; + case TRANSFORM2D: { + Transform2D *l = _data._transform2d; + Transform2D *r = p_variant._data._transform2d; + + for (int i = 0; i < 3; i++) { + if (!hash_compare_vector2(l->columns[i], r->columns[i])) { + return false; + } + } + + return true; + } break; + case PROJECTION: { + const Projection *l = _data._projection; + const Projection *r = p_variant._data._projection; + + for (int i = 0; i < 4; i++) { + if (!hash_compare_vector4(l->matrix[i], r->matrix[i])) { + return false; + } + } + + return true; + } break; + + case COLOR: { + const Color *l = reinterpret_cast(_data._mem); + const Color *r = reinterpret_cast(p_variant._data._mem); + + return hash_compare_color(*l, *r); + } break; + case ARRAY: { + const Array &l = *(reinterpret_cast(_data._mem)); + const Array &r = *(reinterpret_cast(p_variant._data._mem)); + + if (l.size() != r.size()) { + return false; + } + + for (int i = 0; i < l.size(); ++i) { + if (!l[i].hash_compare(r[i])) { + return false; + } + } + + return true; + } break; + + case POOL_REAL_ARRAY: { + hash_compare_pool_array(_data._mem, p_variant._data._mem, real_t, hash_compare_scalar); + } break; + case POOL_VECTOR2_ARRAY: { + hash_compare_pool_array(_data._mem, p_variant._data._mem, Vector2, hash_compare_vector2); + } break; + case POOL_VECTOR2I_ARRAY: { + hash_compare_pool_array(_data._mem, p_variant._data._mem, Vector2i, hash_compare_vector2i); + } break; + case POOL_VECTOR3_ARRAY: { + hash_compare_pool_array(_data._mem, p_variant._data._mem, Vector3, hash_compare_vector3); + } break; + case POOL_VECTOR3I_ARRAY: { + hash_compare_pool_array(_data._mem, p_variant._data._mem, Vector3i, hash_compare_vector3i); + } break; + case POOL_VECTOR4_ARRAY: { + hash_compare_pool_array(_data._mem, p_variant._data._mem, Vector4, hash_compare_vector4); + } break; + case POOL_VECTOR4I_ARRAY: { + hash_compare_pool_array(_data._mem, p_variant._data._mem, Vector4i, hash_compare_vector4i); + } break; + case POOL_COLOR_ARRAY: { + hash_compare_pool_array(_data._mem, p_variant._data._mem, Color, hash_compare_color); + } break; + + default: + bool v; + Variant r; + evaluate(OP_EQUAL, *this, p_variant, r, v); + return r; + } + + return false; +} + +bool Variant::is_ref() const { + return type == OBJECT && !_get_obj().ref.is_null(); +} + +Vector varray() { + return Vector(); +} + +Vector varray(const Variant &p_arg1) { + Vector v; + v.push_back(p_arg1); + return v; +} +Vector varray(const Variant &p_arg1, const Variant &p_arg2) { + Vector v; + v.push_back(p_arg1); + v.push_back(p_arg2); + return v; +} +Vector varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3) { + Vector v; + v.push_back(p_arg1); + v.push_back(p_arg2); + v.push_back(p_arg3); + return v; +} +Vector varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4) { + Vector v; + v.push_back(p_arg1); + v.push_back(p_arg2); + v.push_back(p_arg3); + v.push_back(p_arg4); + return v; +} + +Vector varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5) { + Vector v; + v.push_back(p_arg1); + v.push_back(p_arg2); + v.push_back(p_arg3); + v.push_back(p_arg4); + v.push_back(p_arg5); + return v; +} + +bool Variant::is_shared() const { + switch (type) { + case OBJECT: + return true; + case ARRAY: + return true; + case DICTIONARY: + return true; + default: { + } + } + + return false; +} + +String vformat(const String &p_text, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4, const Variant &p5) { + Array args; + if (p1.get_type() != Variant::NIL) { + args.push_back(p1); + + if (p2.get_type() != Variant::NIL) { + args.push_back(p2); + + if (p3.get_type() != Variant::NIL) { + args.push_back(p3); + + if (p4.get_type() != Variant::NIL) { + args.push_back(p4); + + if (p5.get_type() != Variant::NIL) { + args.push_back(p5); + } + } + } + } + } + + bool error = false; + String fmt = args.sprintf(p_text, &error); + + ERR_FAIL_COND_V_MSG(error, String(), fmt); + + return fmt; +} +#line 0 + +#line 1 "sfwl/object/variant_op.cpp" +/*************************************************************************/ +/* variant_op.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +#define CASE_TYPE_ALL(PREFIX, OP) \ + CASE_TYPE(PREFIX, OP, INT) \ + CASE_TYPE_ALL_BUT_INT(PREFIX, OP) + +#define CASE_TYPE_ALL_BUT_INT(PREFIX, OP) \ + CASE_TYPE(PREFIX, OP, NIL) \ + CASE_TYPE(PREFIX, OP, BOOL) \ + CASE_TYPE(PREFIX, OP, REAL) \ + CASE_TYPE(PREFIX, OP, STRING) \ + CASE_TYPE(PREFIX, OP, RECT2) \ + CASE_TYPE(PREFIX, OP, RECT2I) \ + CASE_TYPE(PREFIX, OP, VECTOR2) \ + CASE_TYPE(PREFIX, OP, VECTOR2I) \ + CASE_TYPE(PREFIX, OP, VECTOR3) \ + CASE_TYPE(PREFIX, OP, VECTOR3I) \ + CASE_TYPE(PREFIX, OP, VECTOR4) \ + CASE_TYPE(PREFIX, OP, VECTOR4I) \ + CASE_TYPE(PREFIX, OP, PLANE) \ + CASE_TYPE(PREFIX, OP, QUATERNION) \ + CASE_TYPE(PREFIX, OP, AABB) \ + CASE_TYPE(PREFIX, OP, BASIS) \ + CASE_TYPE(PREFIX, OP, TRANSFORM) \ + CASE_TYPE(PREFIX, OP, TRANSFORM2D) \ + CASE_TYPE(PREFIX, OP, PROJECTION) \ + CASE_TYPE(PREFIX, OP, COLOR) \ + CASE_TYPE(PREFIX, OP, OBJECT) \ + CASE_TYPE(PREFIX, OP, STRING_NAME) \ + CASE_TYPE(PREFIX, OP, DICTIONARY) \ + CASE_TYPE(PREFIX, OP, ARRAY) \ + CASE_TYPE(PREFIX, OP, POOL_BYTE_ARRAY) \ + CASE_TYPE(PREFIX, OP, POOL_INT_ARRAY) \ + CASE_TYPE(PREFIX, OP, POOL_REAL_ARRAY) \ + CASE_TYPE(PREFIX, OP, POOL_STRING_ARRAY) \ + CASE_TYPE(PREFIX, OP, POOL_VECTOR2_ARRAY) \ + CASE_TYPE(PREFIX, OP, POOL_VECTOR2I_ARRAY) \ + CASE_TYPE(PREFIX, OP, POOL_VECTOR3_ARRAY) \ + CASE_TYPE(PREFIX, OP, POOL_VECTOR3I_ARRAY) \ + CASE_TYPE(PREFIX, OP, POOL_VECTOR4_ARRAY) \ + CASE_TYPE(PREFIX, OP, POOL_VECTOR4I_ARRAY) \ + CASE_TYPE(PREFIX, OP, POOL_COLOR_ARRAY) + +#ifdef __GNUC__ +#define TYPE(PREFIX, OP, TYPE) &&PREFIX##_##OP##_##TYPE + +/* clang-format off */ + +#define TYPES(PREFIX, OP) { \ + TYPE(PREFIX, OP, NIL), \ + TYPE(PREFIX, OP, BOOL), \ + TYPE(PREFIX, OP, INT), \ + TYPE(PREFIX, OP, REAL), \ + TYPE(PREFIX, OP, STRING), \ + TYPE(PREFIX, OP, RECT2), \ + TYPE(PREFIX, OP, RECT2I), \ + TYPE(PREFIX, OP, VECTOR2), \ + TYPE(PREFIX, OP, VECTOR2I), \ + TYPE(PREFIX, OP, VECTOR3), \ + TYPE(PREFIX, OP, VECTOR3I), \ + TYPE(PREFIX, OP, VECTOR4), \ + TYPE(PREFIX, OP, VECTOR4I), \ + TYPE(PREFIX, OP, PLANE), \ + TYPE(PREFIX, OP, QUATERNION), \ + TYPE(PREFIX, OP, AABB), \ + TYPE(PREFIX, OP, BASIS), \ + TYPE(PREFIX, OP, TRANSFORM), \ + TYPE(PREFIX, OP, TRANSFORM2D), \ + TYPE(PREFIX, OP, PROJECTION), \ + TYPE(PREFIX, OP, COLOR), \ + TYPE(PREFIX, OP, OBJECT), \ + TYPE(PREFIX, OP, STRING_NAME), \ + TYPE(PREFIX, OP, DICTIONARY), \ + TYPE(PREFIX, OP, ARRAY), \ + TYPE(PREFIX, OP, POOL_BYTE_ARRAY), \ + TYPE(PREFIX, OP, POOL_INT_ARRAY), \ + TYPE(PREFIX, OP, POOL_REAL_ARRAY), \ + TYPE(PREFIX, OP, POOL_STRING_ARRAY), \ + TYPE(PREFIX, OP, POOL_VECTOR2_ARRAY), \ + TYPE(PREFIX, OP, POOL_VECTOR2I_ARRAY), \ + TYPE(PREFIX, OP, POOL_VECTOR3_ARRAY), \ + TYPE(PREFIX, OP, POOL_VECTOR3I_ARRAY), \ + TYPE(PREFIX, OP, POOL_VECTOR4_ARRAY), \ + TYPE(PREFIX, OP, POOL_VECTOR4I_ARRAY), \ + TYPE(PREFIX, OP, POOL_COLOR_ARRAY), \ +} + +/* clang-format on */ + +#define CASES(PREFIX) static const void *switch_table_##PREFIX[25][38] = { \ + TYPES(PREFIX, OP_EQUAL), \ + TYPES(PREFIX, OP_NOT_EQUAL), \ + TYPES(PREFIX, OP_LESS), \ + TYPES(PREFIX, OP_LESS_EQUAL), \ + TYPES(PREFIX, OP_GREATER), \ + TYPES(PREFIX, OP_GREATER_EQUAL), \ + TYPES(PREFIX, OP_ADD), \ + TYPES(PREFIX, OP_SUBTRACT), \ + TYPES(PREFIX, OP_MULTIPLY), \ + TYPES(PREFIX, OP_DIVIDE), \ + TYPES(PREFIX, OP_NEGATE), \ + TYPES(PREFIX, OP_POSITIVE), \ + TYPES(PREFIX, OP_MODULE), \ + TYPES(PREFIX, OP_STRING_CONCAT), \ + TYPES(PREFIX, OP_SHIFT_LEFT), \ + TYPES(PREFIX, OP_SHIFT_RIGHT), \ + TYPES(PREFIX, OP_BIT_AND), \ + TYPES(PREFIX, OP_BIT_OR), \ + TYPES(PREFIX, OP_BIT_XOR), \ + TYPES(PREFIX, OP_BIT_NEGATE), \ + TYPES(PREFIX, OP_AND), \ + TYPES(PREFIX, OP_OR), \ + TYPES(PREFIX, OP_XOR), \ + TYPES(PREFIX, OP_NOT), \ + TYPES(PREFIX, OP_IN), \ +} + +#define SWITCH(PREFIX, op, val) goto *switch_table_##PREFIX[op][val]; +#define SWITCH_OP(PREFIX, OP, val) +#define CASE_TYPE(PREFIX, OP, TYPE) PREFIX##_##OP##_##TYPE: + +#else +#define CASES(PREFIX) +#define SWITCH(PREFIX, op, val) switch (op) +#define SWITCH_OP(PREFIX, OP, val) \ + case OP: \ + switch (val) +#define CASE_TYPE(PREFIX, OP, TYPE) case TYPE: +#endif + +Variant::operator bool() const { + return booleanize(); +} + +// We consider all uninitialized or empty types to be false based on the type's +// zeroiness. +bool Variant::booleanize() const { + return !is_zero(); +} + +#define _RETURN(m_what) \ + { \ + r_ret = m_what; \ + return; \ + } + +#define _RETURN_FAIL \ + { \ + r_valid = false; \ + return; \ + } + +#define DEFAULT_OP_NUM(m_prefix, m_op_name, m_name, m_op, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == INT) \ + _RETURN(p_a._data.m_type m_op p_b._data._int); \ + if (p_b.type == REAL) \ + _RETURN(p_a._data.m_type m_op p_b._data._real); \ + \ + _RETURN_FAIL \ + }; + +#define DEFAULT_OP_NUM_NULL(m_prefix, m_op_name, m_name, m_op, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == INT) \ + _RETURN(p_a._data.m_type m_op p_b._data._int); \ + if (p_b.type == REAL) \ + _RETURN(p_a._data.m_type m_op p_b._data._real); \ + if (p_b.type == NIL) \ + _RETURN(!(p_b.type m_op NIL)); \ + \ + _RETURN_FAIL \ + }; + +#ifdef DEBUG_ENABLED +#define DEFAULT_OP_NUM_DIV(m_prefix, m_op_name, m_name, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == INT) { \ + if (p_b._data._int == 0) { \ + r_valid = false; \ + _RETURN("Division By Zero"); \ + } \ + _RETURN(p_a._data.m_type / p_b._data._int); \ + } \ + if (p_b.type == REAL) { \ + if (p_b._data._real == 0) { \ + r_valid = false; \ + _RETURN("Division By Zero"); \ + } \ + _RETURN(p_a._data.m_type / p_b._data._real); \ + } \ + \ + _RETURN_FAIL \ + }; +#else +#define DEFAULT_OP_NUM_DIV(m_prefix, m_op_name, m_name, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == INT) \ + _RETURN(p_a._data.m_type / p_b._data._int); \ + if (p_b.type == REAL) \ + _RETURN(p_a._data.m_type / p_b._data._real); \ + \ + _RETURN_FAIL \ + }; +#endif + +#define DEFAULT_OP_NUM_NEG(m_prefix, m_op_name, m_name, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + _RETURN(-p_a._data.m_type); \ + }; + +#define DEFAULT_OP_NUM_POS(m_prefix, m_op_name, m_name, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + _RETURN(p_a._data.m_type); \ + }; + +#define DEFAULT_OP_NUM_VEC(m_prefix, m_op_name, m_name, m_op, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == INT) \ + _RETURN(p_a._data.m_type m_op p_b._data._int); \ + if (p_b.type == REAL) \ + _RETURN(p_a._data.m_type m_op p_b._data._real); \ + if (p_b.type == VECTOR2) \ + _RETURN(p_a._data.m_type m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == VECTOR2I) \ + _RETURN(p_a._data.m_type m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == VECTOR3) \ + _RETURN(p_a._data.m_type m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == VECTOR3I) \ + _RETURN(p_a._data.m_type m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == VECTOR4) \ + _RETURN(p_a._data.m_type m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == VECTOR4I) \ + _RETURN(p_a._data.m_type m_op *reinterpret_cast(p_b._data._mem)); \ + \ + _RETURN_FAIL \ + }; + +#define DEFAULT_OP_STR_REV(m_prefix, m_op_name, m_name, m_op, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == STRING) \ + _RETURN(*reinterpret_cast(p_b._data._mem) m_op *reinterpret_cast(p_a._data._mem)); \ + if (p_b.type == STRING_NAME) \ + _RETURN(*reinterpret_cast(p_b._data._mem) m_op *reinterpret_cast(p_a._data._mem)); \ + \ + _RETURN_FAIL \ + }; + +#define DEFAULT_OP_STR(m_prefix, m_op_name, m_name, m_op, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == STRING) \ + _RETURN(*reinterpret_cast(p_a._data._mem) m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == STRING_NAME) \ + _RETURN(*reinterpret_cast(p_a._data._mem) m_op *reinterpret_cast(p_b._data._mem)); \ + \ + _RETURN_FAIL \ + }; + +#define DEFAULT_OP_STR_NULL(m_prefix, m_op_name, m_name, m_op, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == STRING) \ + _RETURN(*reinterpret_cast(p_a._data._mem) m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == STRING_NAME) \ + _RETURN(*reinterpret_cast(p_a._data._mem) m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == NIL) \ + _RETURN(!(p_b.type m_op NIL)); \ + \ + _RETURN_FAIL \ + }; + +#define DEFAULT_OP_STR_NULL_NP(m_prefix, m_op_name, m_name, m_op, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == STRING) \ + _RETURN(*reinterpret_cast(p_a._data._mem) m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == NIL) \ + _RETURN(!(p_b.type m_op NIL)); \ + \ + _RETURN_FAIL \ + }; + +#define DEFAULT_OP_STR_NULL_SN(m_prefix, m_op_name, m_name, m_op, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == STRING) \ + _RETURN(*reinterpret_cast(p_a._data._mem) m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == STRING_NAME) \ + _RETURN(*reinterpret_cast(p_a._data._mem) m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == NIL) \ + _RETURN(!(p_b.type m_op NIL)); \ + \ + _RETURN_FAIL \ + }; + +#define DEFAULT_OP_LOCALMEM_REV(m_prefix, m_op_name, m_name, m_op, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == m_name) \ + _RETURN(*reinterpret_cast(p_b._data._mem) m_op *reinterpret_cast(p_a._data._mem)); \ + \ + _RETURN_FAIL \ + }; + +#define DEFAULT_OP_LOCALMEM(m_prefix, m_op_name, m_name, m_op, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == m_name) \ + _RETURN(*reinterpret_cast(p_a._data._mem) m_op *reinterpret_cast(p_b._data._mem)); \ + \ + _RETURN_FAIL \ + }; + +#define DEFAULT_OP_LOCALMEM_NULL(m_prefix, m_op_name, m_name, m_op, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == m_name) \ + _RETURN(*reinterpret_cast(p_a._data._mem) m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == NIL) \ + _RETURN(!(p_b.type m_op NIL)); \ + \ + _RETURN_FAIL \ + }; + +#define DEFAULT_OP_LOCALMEM_NEG(m_prefix, m_op_name, m_name, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + _RETURN(-*reinterpret_cast(p_a._data._mem)); \ + } + +#define DEFAULT_OP_LOCALMEM_POS(m_prefix, m_op_name, m_name, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + _RETURN(*reinterpret_cast(p_a._data._mem)); \ + } + +#define DEFAULT_OP_LOCALMEM_NUM(m_prefix, m_op_name, m_name, m_op, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == m_name) \ + _RETURN(*reinterpret_cast(p_a._data._mem) m_op *reinterpret_cast(p_b._data._mem)); \ + if (p_b.type == INT) \ + _RETURN(*reinterpret_cast(p_a._data._mem) m_op p_b._data._int); \ + if (p_b.type == REAL) \ + _RETURN(*reinterpret_cast(p_a._data._mem) m_op p_b._data._real); \ + \ + _RETURN_FAIL \ + } + +#define DEFAULT_OP_PTR(m_op, m_name, m_sub) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == m_name) \ + _RETURN(p_a._data.m_sub m_op p_b._data.m_sub); \ + \ + _RETURN_FAIL \ + } + +#define DEFAULT_OP_PTRREF(m_prefix, m_op_name, m_name, m_op, m_sub) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == m_name) \ + _RETURN(*p_a._data.m_sub m_op *p_b._data.m_sub); \ + \ + _RETURN_FAIL \ + } + +#define DEFAULT_OP_PTRREF_NULL(m_prefix, m_op_name, m_name, m_op, m_sub) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == m_name) \ + _RETURN(*p_a._data.m_sub m_op *p_b._data.m_sub); \ + if (p_b.type == NIL) \ + _RETURN(!(p_b.type m_op NIL)); \ + \ + _RETURN_FAIL \ + } + +#define DEFAULT_OP_ARRAY_EQ(m_prefix, m_op_name, m_name, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == NIL) \ + _RETURN(false) \ + DEFAULT_OP_ARRAY_OP_BODY(m_prefix, m_op_name, m_name, m_type, !=, !=, true, false, false) \ + } + +#define DEFAULT_OP_ARRAY_NEQ(m_prefix, m_op_name, m_name, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_b.type == NIL) \ + _RETURN(true) \ + DEFAULT_OP_ARRAY_OP_BODY(m_prefix, m_op_name, m_name, m_type, !=, !=, false, true, true) \ + } + +#define DEFAULT_OP_ARRAY_LT(m_prefix, m_op_name, m_name, m_type) \ + DEFAULT_OP_ARRAY_OP(m_prefix, m_op_name, m_name, m_type, <, !=, false, a_len < array_b.size(), true) + +#define DEFAULT_OP_ARRAY_GT(m_prefix, m_op_name, m_name, m_type) \ + DEFAULT_OP_ARRAY_OP(m_prefix, m_op_name, m_name, m_type, >, !=, false, a_len < array_b.size(), true) + +#define DEFAULT_OP_ARRAY_OP(m_prefix, m_op_name, m_name, m_type, m_opa, m_opb, m_ret_def, m_ret_s, m_ret_f) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + DEFAULT_OP_ARRAY_OP_BODY(m_prefix, m_op_name, m_name, m_type, m_opa, m_opb, m_ret_def, m_ret_s, m_ret_f) \ + } + +#define DEFAULT_OP_ARRAY_OP_BODY(m_prefix, m_op_name, m_name, m_type, m_opa, m_opb, m_ret_def, m_ret_s, m_ret_f) \ + if (p_a.type != p_b.type) \ + _RETURN_FAIL \ + \ + const PoolVector &array_a = *reinterpret_cast *>(p_a._data._mem); \ + const PoolVector &array_b = *reinterpret_cast *>(p_b._data._mem); \ + \ + int a_len = array_a.size(); \ + if (a_len m_opa array_b.size()) { \ + _RETURN(m_ret_s); \ + } else { \ + PoolVector::Read ra = array_a.read(); \ + PoolVector::Read rb = array_b.read(); \ + \ + for (int i = 0; i < a_len; i++) { \ + if (ra[i] m_opb rb[i]) \ + _RETURN(m_ret_f); \ + } \ + \ + _RETURN(m_ret_def); \ + } + +#define DEFAULT_OP_ARRAY_ADD(m_prefix, m_op_name, m_name, m_type) \ + CASE_TYPE(m_prefix, m_op_name, m_name) { \ + if (p_a.type != p_b.type) \ + _RETURN_FAIL; \ + \ + const PoolVector &array_a = *reinterpret_cast *>(p_a._data._mem); \ + const PoolVector &array_b = *reinterpret_cast *>(p_b._data._mem); \ + PoolVector sum = array_a; \ + sum.append_array(array_b); \ + _RETURN(sum); \ + } + +void Variant::evaluate(const Operator &p_op, const Variant &p_a, + const Variant &p_b, Variant &r_ret, bool &r_valid) { + CASES(math); + r_valid = true; + + SWITCH(math, p_op, p_a.type) { + SWITCH_OP(math, OP_EQUAL, p_a.type) { + CASE_TYPE(math, OP_EQUAL, NIL) { + if (p_b.type == NIL) + _RETURN(true); + if (p_b.type == OBJECT) + _RETURN(_UNSAFE_OBJ_PROXY_PTR(p_b) == nullptr); + + _RETURN(false); + } + + CASE_TYPE(math, OP_EQUAL, BOOL) { + if (p_b.type != BOOL) { + if (p_b.type == NIL) + _RETURN(false); + _RETURN_FAIL; + } + + _RETURN(p_a._data._bool == p_b._data._bool); + } + + CASE_TYPE(math, OP_EQUAL, OBJECT) { + if (p_b.type == OBJECT) + _RETURN(_UNSAFE_OBJ_PROXY_PTR(p_a) == _UNSAFE_OBJ_PROXY_PTR(p_b)); + if (p_b.type == NIL) + _RETURN(_UNSAFE_OBJ_PROXY_PTR(p_a) == nullptr); + + _RETURN_FAIL; + } + + CASE_TYPE(math, OP_EQUAL, DICTIONARY) { + if (p_b.type != DICTIONARY) { + if (p_b.type == NIL) + _RETURN(false); + _RETURN_FAIL; + } + + const Dictionary *arr_a = reinterpret_cast(p_a._data._mem); + const Dictionary *arr_b = reinterpret_cast(p_b._data._mem); + + _RETURN(*arr_a == *arr_b); + } + + CASE_TYPE(math, OP_EQUAL, ARRAY) { + if (p_b.type != ARRAY) { + if (p_b.type == NIL) + _RETURN(false); + _RETURN_FAIL; + } + const Array *arr_a = reinterpret_cast(p_a._data._mem); + const Array *arr_b = reinterpret_cast(p_b._data._mem); + + int l = arr_a->size(); + if (arr_b->size() != l) + _RETURN(false); + for (int i = 0; i < l; i++) { + if (!((*arr_a)[i] == (*arr_b)[i])) { + _RETURN(false); + } + } + + _RETURN(true); + } + + DEFAULT_OP_NUM_NULL(math, OP_EQUAL, INT, ==, _int); + DEFAULT_OP_NUM_NULL(math, OP_EQUAL, REAL, ==, _real); + DEFAULT_OP_STR_NULL(math, OP_EQUAL, STRING, ==, String); + DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, RECT2, ==, Rect2); + DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, RECT2I, ==, Rect2i); + DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, VECTOR2, ==, Vector2); + DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, VECTOR2I, ==, Vector2i); + DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, VECTOR3, ==, Vector3); + DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, VECTOR3I, ==, Vector3i); + DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, VECTOR4, ==, Vector4); + DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, VECTOR4I, ==, Vector4i); + DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, PLANE, ==, Plane); + DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, QUATERNION, ==, Quaternion); + DEFAULT_OP_PTRREF_NULL(math, OP_EQUAL, AABB, ==, _aabb); + DEFAULT_OP_PTRREF_NULL(math, OP_EQUAL, BASIS, ==, _basis); + DEFAULT_OP_PTRREF_NULL(math, OP_EQUAL, TRANSFORM, ==, _transform); + DEFAULT_OP_PTRREF_NULL(math, OP_EQUAL, TRANSFORM2D, ==, _transform2d); + DEFAULT_OP_PTRREF_NULL(math, OP_EQUAL, PROJECTION, ==, _projection); + DEFAULT_OP_LOCALMEM_NULL(math, OP_EQUAL, COLOR, ==, Color); + DEFAULT_OP_STR_NULL_SN(math, OP_EQUAL, STRING_NAME, ==, StringName); + + DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, POOL_BYTE_ARRAY, uint8_t); + DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, POOL_INT_ARRAY, int); + DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, POOL_REAL_ARRAY, real_t); + DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, POOL_STRING_ARRAY, String); + DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, POOL_VECTOR2_ARRAY, Vector2); + DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, POOL_VECTOR2I_ARRAY, Vector2i); + DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, POOL_VECTOR3_ARRAY, Vector3); + DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, POOL_VECTOR3I_ARRAY, Vector3i); + DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, POOL_VECTOR4_ARRAY, Vector4); + DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, POOL_VECTOR4I_ARRAY, Vector4i); + DEFAULT_OP_ARRAY_EQ(math, OP_EQUAL, POOL_COLOR_ARRAY, Color); + } + + SWITCH_OP(math, OP_NOT_EQUAL, p_a.type) { + CASE_TYPE(math, OP_NOT_EQUAL, NIL) { + if (p_b.type == NIL) + _RETURN(false); + if (p_b.type == OBJECT) + _RETURN(_UNSAFE_OBJ_PROXY_PTR(p_b) != nullptr); + + _RETURN(true); + } + + CASE_TYPE(math, OP_NOT_EQUAL, BOOL) { + if (p_b.type != BOOL) { + if (p_b.type == NIL) + _RETURN(true); + + _RETURN_FAIL; + } + + _RETURN(p_a._data._bool != p_b._data._bool); + } + + CASE_TYPE(math, OP_NOT_EQUAL, OBJECT) { + if (p_b.type == OBJECT) + _RETURN((_UNSAFE_OBJ_PROXY_PTR(p_a) != _UNSAFE_OBJ_PROXY_PTR(p_b))); + if (p_b.type == NIL) + _RETURN(_UNSAFE_OBJ_PROXY_PTR(p_a) != nullptr); + + _RETURN_FAIL; + } + + CASE_TYPE(math, OP_NOT_EQUAL, DICTIONARY) { + if (p_b.type != DICTIONARY) { + if (p_b.type == NIL) + _RETURN(true); + _RETURN_FAIL; + } + + const Dictionary *arr_a = reinterpret_cast(p_a._data._mem); + const Dictionary *arr_b = reinterpret_cast(p_b._data._mem); + + _RETURN(*arr_a != *arr_b); + } + + CASE_TYPE(math, OP_NOT_EQUAL, ARRAY) { + if (p_b.type != ARRAY) { + if (p_b.type == NIL) + _RETURN(true); + + _RETURN_FAIL; + } + + const Array *arr_a = reinterpret_cast(p_a._data._mem); + const Array *arr_b = reinterpret_cast(p_b._data._mem); + + int l = arr_a->size(); + if (arr_b->size() != l) + _RETURN(true); + for (int i = 0; i < l; i++) { + if (((*arr_a)[i] != (*arr_b)[i])) { + _RETURN(true); + } + } + + _RETURN(false); + } + + DEFAULT_OP_NUM_NULL(math, OP_NOT_EQUAL, INT, !=, _int); + DEFAULT_OP_NUM_NULL(math, OP_NOT_EQUAL, REAL, !=, _real); + DEFAULT_OP_STR_NULL(math, OP_NOT_EQUAL, STRING, !=, String); + DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, RECT2, !=, Rect2); + DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, RECT2I, !=, Rect2i); + DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, VECTOR2, !=, Vector2); + DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, VECTOR2I, !=, Vector2i); + DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, VECTOR3, !=, Vector3); + DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, VECTOR3I, !=, Vector3i); + DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, VECTOR4, !=, Vector4); + DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, VECTOR4I, !=, Vector4i); + DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, PLANE, !=, Plane); + DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, QUATERNION, !=, Quaternion); + DEFAULT_OP_PTRREF_NULL(math, OP_NOT_EQUAL, AABB, !=, _aabb); + DEFAULT_OP_PTRREF_NULL(math, OP_NOT_EQUAL, BASIS, !=, _basis); + DEFAULT_OP_PTRREF_NULL(math, OP_NOT_EQUAL, TRANSFORM, !=, _transform); + DEFAULT_OP_PTRREF_NULL(math, OP_NOT_EQUAL, TRANSFORM2D, !=, _transform2d); + DEFAULT_OP_PTRREF_NULL(math, OP_NOT_EQUAL, PROJECTION, !=, _projection); + DEFAULT_OP_LOCALMEM_NULL(math, OP_NOT_EQUAL, COLOR, !=, Color); + DEFAULT_OP_STR_NULL_SN(math, OP_NOT_EQUAL, STRING_NAME, !=, StringName); + + DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, POOL_BYTE_ARRAY, uint8_t); + DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, POOL_INT_ARRAY, int); + DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, POOL_REAL_ARRAY, real_t); + DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, POOL_STRING_ARRAY, String); + DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, POOL_VECTOR2_ARRAY, Vector2); + DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, POOL_VECTOR2I_ARRAY, Vector2i); + DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, POOL_VECTOR3_ARRAY, Vector3); + DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, POOL_VECTOR3I_ARRAY, Vector3i); + DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, POOL_VECTOR4_ARRAY, Vector4); + DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, POOL_VECTOR4I_ARRAY, Vector4i); + DEFAULT_OP_ARRAY_NEQ(math, OP_NOT_EQUAL, POOL_COLOR_ARRAY, Color); + } + + SWITCH_OP(math, OP_LESS, p_a.type) { + CASE_TYPE(math, OP_LESS, BOOL) { + if (p_b.type != BOOL) + _RETURN_FAIL; + + if (p_a._data._bool == p_b._data._bool) + _RETURN(false); + + if (p_a._data._bool && !p_b._data._bool) + _RETURN(false); + + _RETURN(true); + } + + CASE_TYPE(math, OP_LESS, OBJECT) { + if (p_b.type != OBJECT) + _RETURN_FAIL; + _RETURN(_UNSAFE_OBJ_PROXY_PTR(p_a) < _UNSAFE_OBJ_PROXY_PTR(p_b)); + } + + CASE_TYPE(math, OP_LESS, ARRAY) { + if (p_b.type != ARRAY) + _RETURN_FAIL; + + const Array *arr_a = reinterpret_cast(p_a._data._mem); + const Array *arr_b = reinterpret_cast(p_b._data._mem); + + int l = arr_a->size(); + if (arr_b->size() < l) + _RETURN(false); + for (int i = 0; i < l; i++) { + if (!((*arr_a)[i] < (*arr_b)[i])) { + _RETURN(true); + } + } + + _RETURN(false); + } + + DEFAULT_OP_NUM(math, OP_LESS, INT, <, _int); + DEFAULT_OP_NUM(math, OP_LESS, REAL, <, _real); + DEFAULT_OP_STR(math, OP_LESS, STRING, <, String); + DEFAULT_OP_LOCALMEM(math, OP_LESS, VECTOR2, <, Vector2); + DEFAULT_OP_LOCALMEM(math, OP_LESS, VECTOR2I, <, Vector2i); + DEFAULT_OP_LOCALMEM(math, OP_LESS, VECTOR3, <, Vector3); + DEFAULT_OP_LOCALMEM(math, OP_LESS, VECTOR3I, <, Vector3i); + DEFAULT_OP_LOCALMEM(math, OP_LESS, VECTOR4, <, Vector4); + DEFAULT_OP_LOCALMEM(math, OP_LESS, VECTOR4I, <, Vector4i); + + DEFAULT_OP_ARRAY_LT(math, OP_LESS, POOL_BYTE_ARRAY, uint8_t); + DEFAULT_OP_ARRAY_LT(math, OP_LESS, POOL_INT_ARRAY, int); + DEFAULT_OP_ARRAY_LT(math, OP_LESS, POOL_REAL_ARRAY, real_t); + DEFAULT_OP_ARRAY_LT(math, OP_LESS, POOL_STRING_ARRAY, String); + DEFAULT_OP_ARRAY_LT(math, OP_LESS, POOL_VECTOR2_ARRAY, Vector2); + DEFAULT_OP_ARRAY_LT(math, OP_LESS, POOL_VECTOR2I_ARRAY, Vector2i); + DEFAULT_OP_ARRAY_LT(math, OP_LESS, POOL_VECTOR3_ARRAY, Vector3); + DEFAULT_OP_ARRAY_LT(math, OP_LESS, POOL_VECTOR3I_ARRAY, Vector3i); + DEFAULT_OP_ARRAY_LT(math, OP_LESS, POOL_VECTOR4_ARRAY, Vector4); + DEFAULT_OP_ARRAY_LT(math, OP_LESS, POOL_VECTOR4I_ARRAY, Vector4i); + DEFAULT_OP_ARRAY_LT(math, OP_LESS, POOL_COLOR_ARRAY, Color); + + CASE_TYPE(math, OP_LESS, NIL) + CASE_TYPE(math, OP_LESS, RECT2) + CASE_TYPE(math, OP_LESS, RECT2I) + CASE_TYPE(math, OP_LESS, PLANE) + CASE_TYPE(math, OP_LESS, QUATERNION) + CASE_TYPE(math, OP_LESS, AABB) + CASE_TYPE(math, OP_LESS, BASIS) + CASE_TYPE(math, OP_LESS, TRANSFORM) + CASE_TYPE(math, OP_LESS, TRANSFORM2D) + CASE_TYPE(math, OP_LESS, PROJECTION) + CASE_TYPE(math, OP_LESS, STRING_NAME) + CASE_TYPE(math, OP_LESS, COLOR) + CASE_TYPE(math, OP_LESS, DICTIONARY) + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_LESS_EQUAL, p_a.type) { + CASE_TYPE(math, OP_LESS_EQUAL, OBJECT) { + if (p_b.type != OBJECT) + _RETURN_FAIL; + _RETURN(_UNSAFE_OBJ_PROXY_PTR(p_a) <= _UNSAFE_OBJ_PROXY_PTR(p_b)); + } + + DEFAULT_OP_NUM(math, OP_LESS_EQUAL, INT, <=, _int); + DEFAULT_OP_NUM(math, OP_LESS_EQUAL, REAL, <=, _real); + DEFAULT_OP_STR(math, OP_LESS_EQUAL, STRING, <=, String); + DEFAULT_OP_LOCALMEM(math, OP_LESS_EQUAL, VECTOR2, <=, Vector2); + DEFAULT_OP_LOCALMEM(math, OP_LESS_EQUAL, VECTOR2I, <=, Vector2i); + DEFAULT_OP_LOCALMEM(math, OP_LESS_EQUAL, VECTOR3, <=, Vector3); + DEFAULT_OP_LOCALMEM(math, OP_LESS_EQUAL, VECTOR3I, <=, Vector3i); + DEFAULT_OP_LOCALMEM(math, OP_LESS_EQUAL, VECTOR4, <=, Vector4); + DEFAULT_OP_LOCALMEM(math, OP_LESS_EQUAL, VECTOR4I, <=, Vector4i); + + CASE_TYPE(math, OP_LESS_EQUAL, NIL) + CASE_TYPE(math, OP_LESS_EQUAL, BOOL) + CASE_TYPE(math, OP_LESS_EQUAL, RECT2) + CASE_TYPE(math, OP_LESS_EQUAL, RECT2I) + CASE_TYPE(math, OP_LESS_EQUAL, PLANE) + CASE_TYPE(math, OP_LESS_EQUAL, QUATERNION) + CASE_TYPE(math, OP_LESS_EQUAL, AABB) + CASE_TYPE(math, OP_LESS_EQUAL, BASIS) + CASE_TYPE(math, OP_LESS_EQUAL, TRANSFORM) + CASE_TYPE(math, OP_LESS_EQUAL, TRANSFORM2D) + CASE_TYPE(math, OP_LESS_EQUAL, PROJECTION) + CASE_TYPE(math, OP_LESS_EQUAL, COLOR) + CASE_TYPE(math, OP_LESS_EQUAL, STRING_NAME) + CASE_TYPE(math, OP_LESS_EQUAL, DICTIONARY) + CASE_TYPE(math, OP_LESS_EQUAL, ARRAY) + CASE_TYPE(math, OP_LESS_EQUAL, POOL_BYTE_ARRAY); + CASE_TYPE(math, OP_LESS_EQUAL, POOL_INT_ARRAY); + CASE_TYPE(math, OP_LESS_EQUAL, POOL_REAL_ARRAY); + CASE_TYPE(math, OP_LESS_EQUAL, POOL_STRING_ARRAY); + CASE_TYPE(math, OP_LESS_EQUAL, POOL_VECTOR2_ARRAY); + CASE_TYPE(math, OP_LESS_EQUAL, POOL_VECTOR2I_ARRAY); + CASE_TYPE(math, OP_LESS_EQUAL, POOL_VECTOR3_ARRAY); + CASE_TYPE(math, OP_LESS_EQUAL, POOL_VECTOR3I_ARRAY); + CASE_TYPE(math, OP_LESS_EQUAL, POOL_VECTOR4_ARRAY); + CASE_TYPE(math, OP_LESS_EQUAL, POOL_VECTOR4I_ARRAY); + CASE_TYPE(math, OP_LESS_EQUAL, POOL_COLOR_ARRAY); + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_GREATER, p_a.type) { + CASE_TYPE(math, OP_GREATER, BOOL) { + if (p_b.type != BOOL) + _RETURN_FAIL; + + if (p_a._data._bool == p_b._data._bool) + _RETURN(false); + + if (!p_a._data._bool && p_b._data._bool) + _RETURN(false); + + _RETURN(true); + } + + CASE_TYPE(math, OP_GREATER, OBJECT) { + if (p_b.type != OBJECT) + _RETURN_FAIL; + _RETURN(_UNSAFE_OBJ_PROXY_PTR(p_a) > _UNSAFE_OBJ_PROXY_PTR(p_b)); + } + + CASE_TYPE(math, OP_GREATER, ARRAY) { + if (p_b.type != ARRAY) + _RETURN_FAIL; + + const Array *arr_a = reinterpret_cast(p_a._data._mem); + const Array *arr_b = reinterpret_cast(p_b._data._mem); + + int l = arr_a->size(); + if (arr_b->size() > l) + _RETURN(false); + for (int i = 0; i < l; i++) { + if (((*arr_a)[i] < (*arr_b)[i])) { + _RETURN(false); + } + } + + _RETURN(true); + } + + DEFAULT_OP_NUM(math, OP_GREATER, INT, >, _int); + DEFAULT_OP_NUM(math, OP_GREATER, REAL, >, _real); + DEFAULT_OP_STR_REV(math, OP_GREATER, STRING, <, String); + DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER, VECTOR2, <, Vector2); + DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER, VECTOR2I, <, Vector2i); + DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER, VECTOR3, <, Vector3); + DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER, VECTOR3I, <, Vector3i); + DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER, VECTOR4, <, Vector4); + DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER, VECTOR4I, <, Vector4i); + DEFAULT_OP_ARRAY_GT(math, OP_GREATER, POOL_BYTE_ARRAY, uint8_t); + DEFAULT_OP_ARRAY_GT(math, OP_GREATER, POOL_INT_ARRAY, int); + DEFAULT_OP_ARRAY_GT(math, OP_GREATER, POOL_REAL_ARRAY, real_t); + DEFAULT_OP_ARRAY_GT(math, OP_GREATER, POOL_STRING_ARRAY, String); + DEFAULT_OP_ARRAY_GT(math, OP_GREATER, POOL_VECTOR2_ARRAY, Vector2); + DEFAULT_OP_ARRAY_GT(math, OP_GREATER, POOL_VECTOR2I_ARRAY, Vector2i); + DEFAULT_OP_ARRAY_GT(math, OP_GREATER, POOL_VECTOR3_ARRAY, Vector3); + DEFAULT_OP_ARRAY_GT(math, OP_GREATER, POOL_VECTOR3I_ARRAY, Vector3i); + DEFAULT_OP_ARRAY_GT(math, OP_GREATER, POOL_VECTOR4_ARRAY, Vector4); + DEFAULT_OP_ARRAY_GT(math, OP_GREATER, POOL_VECTOR4I_ARRAY, Vector4i); + DEFAULT_OP_ARRAY_GT(math, OP_GREATER, POOL_COLOR_ARRAY, Color); + + CASE_TYPE(math, OP_GREATER, NIL) + CASE_TYPE(math, OP_GREATER, RECT2) + CASE_TYPE(math, OP_GREATER, RECT2I) + CASE_TYPE(math, OP_GREATER, PLANE) + CASE_TYPE(math, OP_GREATER, QUATERNION) + CASE_TYPE(math, OP_GREATER, AABB) + CASE_TYPE(math, OP_GREATER, BASIS) + CASE_TYPE(math, OP_GREATER, STRING_NAME) + CASE_TYPE(math, OP_GREATER, TRANSFORM) + CASE_TYPE(math, OP_GREATER, TRANSFORM2D) + CASE_TYPE(math, OP_GREATER, PROJECTION) + CASE_TYPE(math, OP_GREATER, COLOR) + CASE_TYPE(math, OP_GREATER, DICTIONARY) + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_GREATER_EQUAL, p_a.type) { + CASE_TYPE(math, OP_GREATER_EQUAL, OBJECT) { + if (p_b.type != OBJECT) + _RETURN_FAIL; + _RETURN(_UNSAFE_OBJ_PROXY_PTR(p_a) >= _UNSAFE_OBJ_PROXY_PTR(p_b)); + } + + DEFAULT_OP_NUM(math, OP_GREATER_EQUAL, INT, >=, _int); + DEFAULT_OP_NUM(math, OP_GREATER_EQUAL, REAL, >=, _real); + DEFAULT_OP_STR_REV(math, OP_GREATER_EQUAL, STRING, <=, String); + DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER_EQUAL, VECTOR2, <=, Vector2); + DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER_EQUAL, VECTOR2I, <=, Vector2i); + DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER_EQUAL, VECTOR3, <=, Vector3); + DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER_EQUAL, VECTOR3I, <=, Vector3i); + DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER_EQUAL, VECTOR4, <=, Vector4); + DEFAULT_OP_LOCALMEM_REV(math, OP_GREATER_EQUAL, VECTOR4I, <=, Vector4i); + + CASE_TYPE(math, OP_GREATER_EQUAL, NIL) + CASE_TYPE(math, OP_GREATER_EQUAL, BOOL) + CASE_TYPE(math, OP_GREATER_EQUAL, RECT2) + CASE_TYPE(math, OP_GREATER_EQUAL, RECT2I) + CASE_TYPE(math, OP_GREATER_EQUAL, PLANE) + CASE_TYPE(math, OP_GREATER_EQUAL, QUATERNION) + CASE_TYPE(math, OP_GREATER_EQUAL, AABB) + CASE_TYPE(math, OP_GREATER_EQUAL, BASIS) + CASE_TYPE(math, OP_GREATER_EQUAL, TRANSFORM) + CASE_TYPE(math, OP_GREATER_EQUAL, TRANSFORM2D) + CASE_TYPE(math, OP_GREATER_EQUAL, PROJECTION) + CASE_TYPE(math, OP_GREATER_EQUAL, COLOR) + CASE_TYPE(math, OP_GREATER_EQUAL, DICTIONARY) + CASE_TYPE(math, OP_GREATER_EQUAL, STRING_NAME) + CASE_TYPE(math, OP_GREATER_EQUAL, ARRAY) + CASE_TYPE(math, OP_GREATER_EQUAL, POOL_BYTE_ARRAY); + CASE_TYPE(math, OP_GREATER_EQUAL, POOL_INT_ARRAY); + CASE_TYPE(math, OP_GREATER_EQUAL, POOL_REAL_ARRAY); + CASE_TYPE(math, OP_GREATER_EQUAL, POOL_STRING_ARRAY); + CASE_TYPE(math, OP_GREATER_EQUAL, POOL_VECTOR2_ARRAY); + CASE_TYPE(math, OP_GREATER_EQUAL, POOL_VECTOR2I_ARRAY); + CASE_TYPE(math, OP_GREATER_EQUAL, POOL_VECTOR3_ARRAY); + CASE_TYPE(math, OP_GREATER_EQUAL, POOL_VECTOR3I_ARRAY); + CASE_TYPE(math, OP_GREATER_EQUAL, POOL_VECTOR4_ARRAY); + CASE_TYPE(math, OP_GREATER_EQUAL, POOL_VECTOR4I_ARRAY); + CASE_TYPE(math, OP_GREATER_EQUAL, POOL_COLOR_ARRAY); + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_ADD, p_a.type) { + CASE_TYPE(math, OP_ADD, ARRAY) { + if (p_a.type != p_b.type) + _RETURN_FAIL; + + const Array &array_a = *reinterpret_cast(p_a._data._mem); + const Array &array_b = *reinterpret_cast(p_b._data._mem); + Array sum; + int asize = array_a.size(); + int bsize = array_b.size(); + sum.resize(asize + bsize); + for (int i = 0; i < asize; i++) { + sum[i] = array_a[i]; + } + for (int i = 0; i < bsize; i++) { + sum[i + asize] = array_b[i]; + } + _RETURN(sum); + } + + DEFAULT_OP_NUM(math, OP_ADD, INT, +, _int); + DEFAULT_OP_NUM(math, OP_ADD, REAL, +, _real); + DEFAULT_OP_STR(math, OP_ADD, STRING, +, String); + DEFAULT_OP_LOCALMEM(math, OP_ADD, VECTOR2, +, Vector2); + DEFAULT_OP_LOCALMEM(math, OP_ADD, VECTOR2I, +, Vector2i); + DEFAULT_OP_LOCALMEM(math, OP_ADD, VECTOR3, +, Vector3); + DEFAULT_OP_LOCALMEM(math, OP_ADD, VECTOR3I, +, Vector3i); + DEFAULT_OP_LOCALMEM(math, OP_ADD, VECTOR4, +, Vector4); + DEFAULT_OP_LOCALMEM(math, OP_ADD, VECTOR4I, +, Vector4i); + DEFAULT_OP_LOCALMEM(math, OP_ADD, QUATERNION, +, Quaternion); + DEFAULT_OP_LOCALMEM(math, OP_ADD, COLOR, +, Color); + + DEFAULT_OP_ARRAY_ADD(math, OP_ADD, POOL_BYTE_ARRAY, uint8_t); + DEFAULT_OP_ARRAY_ADD(math, OP_ADD, POOL_INT_ARRAY, int); + DEFAULT_OP_ARRAY_ADD(math, OP_ADD, POOL_REAL_ARRAY, real_t); + DEFAULT_OP_ARRAY_ADD(math, OP_ADD, POOL_STRING_ARRAY, String); + DEFAULT_OP_ARRAY_ADD(math, OP_ADD, POOL_VECTOR2_ARRAY, Vector2); + DEFAULT_OP_ARRAY_ADD(math, OP_ADD, POOL_VECTOR2I_ARRAY, Vector2i); + DEFAULT_OP_ARRAY_ADD(math, OP_ADD, POOL_VECTOR3_ARRAY, Vector3); + DEFAULT_OP_ARRAY_ADD(math, OP_ADD, POOL_VECTOR3I_ARRAY, Vector3i); + DEFAULT_OP_ARRAY_ADD(math, OP_ADD, POOL_VECTOR4_ARRAY, Vector4); + DEFAULT_OP_ARRAY_ADD(math, OP_ADD, POOL_VECTOR4I_ARRAY, Vector4i); + DEFAULT_OP_ARRAY_ADD(math, OP_ADD, POOL_COLOR_ARRAY, Color); + + CASE_TYPE(math, OP_ADD, NIL) + CASE_TYPE(math, OP_ADD, BOOL) + CASE_TYPE(math, OP_ADD, RECT2) + CASE_TYPE(math, OP_ADD, RECT2I) + CASE_TYPE(math, OP_ADD, PLANE) + CASE_TYPE(math, OP_ADD, AABB) + CASE_TYPE(math, OP_ADD, BASIS) + CASE_TYPE(math, OP_ADD, TRANSFORM) + CASE_TYPE(math, OP_ADD, TRANSFORM2D) + CASE_TYPE(math, OP_ADD, PROJECTION) + CASE_TYPE(math, OP_ADD, OBJECT) + CASE_TYPE(math, OP_ADD, DICTIONARY) + CASE_TYPE(math, OP_ADD, STRING_NAME) + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_SUBTRACT, p_a.type) { + DEFAULT_OP_NUM(math, OP_SUBTRACT, INT, -, _int); + DEFAULT_OP_NUM(math, OP_SUBTRACT, REAL, -, _real); + DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, VECTOR2, -, Vector2); + DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, VECTOR2I, -, Vector2i); + DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, VECTOR3, -, Vector3); + DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, VECTOR3I, -, Vector3i); + DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, VECTOR4, -, Vector4); + DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, VECTOR4I, -, Vector4i); + DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, QUATERNION, -, Quaternion); + DEFAULT_OP_LOCALMEM(math, OP_SUBTRACT, COLOR, -, Color); + + CASE_TYPE(math, OP_SUBTRACT, NIL) + CASE_TYPE(math, OP_SUBTRACT, BOOL) + CASE_TYPE(math, OP_SUBTRACT, STRING) + CASE_TYPE(math, OP_SUBTRACT, RECT2) + CASE_TYPE(math, OP_SUBTRACT, RECT2I) + CASE_TYPE(math, OP_SUBTRACT, PLANE) + CASE_TYPE(math, OP_SUBTRACT, AABB) + CASE_TYPE(math, OP_SUBTRACT, BASIS) + CASE_TYPE(math, OP_SUBTRACT, TRANSFORM) + CASE_TYPE(math, OP_SUBTRACT, TRANSFORM2D) + CASE_TYPE(math, OP_SUBTRACT, PROJECTION) + CASE_TYPE(math, OP_SUBTRACT, OBJECT) + CASE_TYPE(math, OP_SUBTRACT, STRING_NAME) + CASE_TYPE(math, OP_SUBTRACT, DICTIONARY) + CASE_TYPE(math, OP_SUBTRACT, ARRAY) + CASE_TYPE(math, OP_SUBTRACT, POOL_BYTE_ARRAY); + CASE_TYPE(math, OP_SUBTRACT, POOL_INT_ARRAY); + CASE_TYPE(math, OP_SUBTRACT, POOL_REAL_ARRAY); + CASE_TYPE(math, OP_SUBTRACT, POOL_STRING_ARRAY); + CASE_TYPE(math, OP_SUBTRACT, POOL_VECTOR2_ARRAY); + CASE_TYPE(math, OP_SUBTRACT, POOL_VECTOR2I_ARRAY); + CASE_TYPE(math, OP_SUBTRACT, POOL_VECTOR3_ARRAY); + CASE_TYPE(math, OP_SUBTRACT, POOL_VECTOR3I_ARRAY); + CASE_TYPE(math, OP_SUBTRACT, POOL_VECTOR4_ARRAY); + CASE_TYPE(math, OP_SUBTRACT, POOL_VECTOR4I_ARRAY); + CASE_TYPE(math, OP_SUBTRACT, POOL_COLOR_ARRAY); + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_MULTIPLY, p_a.type) { + CASE_TYPE(math, OP_MULTIPLY, QUATERNION) { + switch (p_b.type) { + case VECTOR3: { + _RETURN(reinterpret_cast(p_a._data._mem)->xform(*(const Vector3 *)p_b._data._mem)); + } + case VECTOR3I: { + _RETURN(reinterpret_cast(p_a._data._mem)->xform(*(const Vector3i *)p_b._data._mem)); + } + case QUATERNION: { + _RETURN(*reinterpret_cast(p_a._data._mem) * *reinterpret_cast(p_b._data._mem)); + } + case REAL: { + _RETURN(*reinterpret_cast(p_a._data._mem) * p_b._data._real); + } + default: + _RETURN_FAIL; + } + } + + CASE_TYPE(math, OP_MULTIPLY, BASIS) { + switch (p_b.type) { + case VECTOR3: { + _RETURN(p_a._data._basis->xform(*(const Vector3 *)p_b._data._mem)); + } + case VECTOR3I: { + _RETURN(p_a._data._basis->xform(*(const Vector3i *)p_b._data._mem)); + } + case BASIS: { + _RETURN(*p_a._data._basis * *p_b._data._basis); + } + default: + _RETURN_FAIL; + } + } + + CASE_TYPE(math, OP_MULTIPLY, TRANSFORM) { + switch (p_b.type) { + case VECTOR3: { + _RETURN(p_a._data._transform->xform(*(const Vector3 *)p_b._data._mem)); + } + case VECTOR3I: { + _RETURN(p_a._data._transform->xform(*(const Vector3i *)p_b._data._mem)); + } + case TRANSFORM: { + _RETURN(*p_a._data._transform * *p_b._data._transform); + } + default: + _RETURN_FAIL; + } + } + + CASE_TYPE(math, OP_MULTIPLY, TRANSFORM2D) { + switch (p_b.type) { + case TRANSFORM2D: { + _RETURN(*p_a._data._transform2d * *p_b._data._transform2d); + } + case VECTOR2: { + _RETURN(p_a._data._transform2d->xform(*(const Vector2 *)p_b._data._mem)); + } + case VECTOR2I: { + _RETURN(p_a._data._transform2d->xform(*(const Vector2i *)p_b._data._mem)); + } + default: + _RETURN_FAIL; + } + } + + CASE_TYPE(math, OP_MULTIPLY, PROJECTION) { + switch (p_b.type) { + case VECTOR4: { + _RETURN(p_a._data._projection->xform(*(const Vector4 *)p_b._data._mem)); + } + case VECTOR3: { + _RETURN(p_a._data._projection->xform(*(const Vector3 *)p_b._data._mem)); + } + case PLANE: { + _RETURN(p_a._data._projection->xform(*(const Plane *)p_b._data._mem)); + } + case PROJECTION: { + _RETURN(p_a._data._projection->operator*(*(const Projection *)p_b._data._mem)); + } + default: + _RETURN_FAIL; + } + } + + DEFAULT_OP_NUM_VEC(math, OP_MULTIPLY, INT, *, _int); + DEFAULT_OP_NUM_VEC(math, OP_MULTIPLY, REAL, *, _real); + DEFAULT_OP_LOCALMEM_NUM(math, OP_MULTIPLY, VECTOR2, *, Vector2); + DEFAULT_OP_LOCALMEM_NUM(math, OP_MULTIPLY, VECTOR2I, *, Vector2i); + DEFAULT_OP_LOCALMEM_NUM(math, OP_MULTIPLY, VECTOR3, *, Vector3); + DEFAULT_OP_LOCALMEM_NUM(math, OP_MULTIPLY, VECTOR3I, *, Vector3i); + DEFAULT_OP_LOCALMEM_NUM(math, OP_MULTIPLY, VECTOR4, *, Vector4); + DEFAULT_OP_LOCALMEM_NUM(math, OP_MULTIPLY, VECTOR4I, *, Vector4i); + DEFAULT_OP_LOCALMEM_NUM(math, OP_MULTIPLY, COLOR, *, Color); + + CASE_TYPE(math, OP_MULTIPLY, NIL) + CASE_TYPE(math, OP_MULTIPLY, BOOL) + CASE_TYPE(math, OP_MULTIPLY, STRING) + CASE_TYPE(math, OP_MULTIPLY, RECT2) + CASE_TYPE(math, OP_MULTIPLY, RECT2I) + CASE_TYPE(math, OP_MULTIPLY, PLANE) + CASE_TYPE(math, OP_MULTIPLY, AABB) + CASE_TYPE(math, OP_MULTIPLY, OBJECT) + CASE_TYPE(math, OP_MULTIPLY, STRING_NAME) + CASE_TYPE(math, OP_MULTIPLY, DICTIONARY) + CASE_TYPE(math, OP_MULTIPLY, ARRAY) + CASE_TYPE(math, OP_MULTIPLY, POOL_BYTE_ARRAY); + CASE_TYPE(math, OP_MULTIPLY, POOL_INT_ARRAY); + CASE_TYPE(math, OP_MULTIPLY, POOL_REAL_ARRAY); + CASE_TYPE(math, OP_MULTIPLY, POOL_STRING_ARRAY); + CASE_TYPE(math, OP_MULTIPLY, POOL_VECTOR2_ARRAY); + CASE_TYPE(math, OP_MULTIPLY, POOL_VECTOR2I_ARRAY); + CASE_TYPE(math, OP_MULTIPLY, POOL_VECTOR3_ARRAY); + CASE_TYPE(math, OP_MULTIPLY, POOL_VECTOR3I_ARRAY); + CASE_TYPE(math, OP_MULTIPLY, POOL_VECTOR4_ARRAY); + CASE_TYPE(math, OP_MULTIPLY, POOL_VECTOR4I_ARRAY); + CASE_TYPE(math, OP_MULTIPLY, POOL_COLOR_ARRAY); + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_DIVIDE, p_a.type) { + CASE_TYPE(math, OP_DIVIDE, QUATERNION) { + if (p_b.type != REAL) + _RETURN_FAIL; +#ifdef DEBUG_ENABLED + if (p_b._data._real == 0) { + r_valid = false; + _RETURN("Division By Zero"); + } +#endif + _RETURN(*reinterpret_cast(p_a._data._mem) / p_b._data._real); + } + + DEFAULT_OP_NUM_DIV(math, OP_DIVIDE, INT, _int); + DEFAULT_OP_NUM_DIV(math, OP_DIVIDE, REAL, _real); + DEFAULT_OP_LOCALMEM_NUM(math, OP_DIVIDE, VECTOR2, /, Vector2); + DEFAULT_OP_LOCALMEM_NUM(math, OP_DIVIDE, VECTOR2I, /, Vector2i); + DEFAULT_OP_LOCALMEM_NUM(math, OP_DIVIDE, VECTOR3, /, Vector3); + DEFAULT_OP_LOCALMEM_NUM(math, OP_DIVIDE, VECTOR3I, /, Vector3i); + DEFAULT_OP_LOCALMEM_NUM(math, OP_DIVIDE, VECTOR4, /, Vector4); + DEFAULT_OP_LOCALMEM_NUM(math, OP_DIVIDE, VECTOR4I, /, Vector4i); + DEFAULT_OP_LOCALMEM_NUM(math, OP_DIVIDE, COLOR, /, Color); + + CASE_TYPE(math, OP_DIVIDE, NIL) + CASE_TYPE(math, OP_DIVIDE, BOOL) + CASE_TYPE(math, OP_DIVIDE, STRING) + CASE_TYPE(math, OP_DIVIDE, RECT2) + CASE_TYPE(math, OP_DIVIDE, RECT2I) + CASE_TYPE(math, OP_DIVIDE, PLANE) + CASE_TYPE(math, OP_DIVIDE, AABB) + CASE_TYPE(math, OP_DIVIDE, BASIS) + CASE_TYPE(math, OP_DIVIDE, TRANSFORM) + CASE_TYPE(math, OP_DIVIDE, TRANSFORM2D) + CASE_TYPE(math, OP_DIVIDE, PROJECTION) + CASE_TYPE(math, OP_DIVIDE, OBJECT) + CASE_TYPE(math, OP_DIVIDE, STRING_NAME) + CASE_TYPE(math, OP_DIVIDE, DICTIONARY) + CASE_TYPE(math, OP_DIVIDE, ARRAY) + CASE_TYPE(math, OP_DIVIDE, POOL_BYTE_ARRAY); + CASE_TYPE(math, OP_DIVIDE, POOL_INT_ARRAY); + CASE_TYPE(math, OP_DIVIDE, POOL_REAL_ARRAY); + CASE_TYPE(math, OP_DIVIDE, POOL_STRING_ARRAY); + CASE_TYPE(math, OP_DIVIDE, POOL_VECTOR2_ARRAY); + CASE_TYPE(math, OP_DIVIDE, POOL_VECTOR2I_ARRAY); + CASE_TYPE(math, OP_DIVIDE, POOL_VECTOR3_ARRAY); + CASE_TYPE(math, OP_DIVIDE, POOL_VECTOR3I_ARRAY); + CASE_TYPE(math, OP_DIVIDE, POOL_VECTOR4_ARRAY); + CASE_TYPE(math, OP_DIVIDE, POOL_VECTOR4I_ARRAY); + CASE_TYPE(math, OP_DIVIDE, POOL_COLOR_ARRAY); + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_POSITIVE, p_a.type) { + DEFAULT_OP_NUM_POS(math, OP_POSITIVE, INT, _int); + DEFAULT_OP_NUM_POS(math, OP_POSITIVE, REAL, _real); + DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, VECTOR2, Vector2); + DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, VECTOR2I, Vector2i); + DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, VECTOR3, Vector3); + DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, VECTOR3I, Vector3i); + DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, VECTOR4, Vector4); + DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, VECTOR4I, Vector4i); + DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, PLANE, Plane); + DEFAULT_OP_LOCALMEM_POS(math, OP_POSITIVE, QUATERNION, Quaternion); + + CASE_TYPE(math, OP_POSITIVE, NIL) + CASE_TYPE(math, OP_POSITIVE, BOOL) + CASE_TYPE(math, OP_POSITIVE, STRING) + CASE_TYPE(math, OP_POSITIVE, RECT2) + CASE_TYPE(math, OP_POSITIVE, RECT2I) + CASE_TYPE(math, OP_POSITIVE, AABB) + CASE_TYPE(math, OP_POSITIVE, BASIS) + CASE_TYPE(math, OP_POSITIVE, TRANSFORM) + CASE_TYPE(math, OP_POSITIVE, TRANSFORM2D) + CASE_TYPE(math, OP_POSITIVE, PROJECTION) + CASE_TYPE(math, OP_POSITIVE, COLOR) + CASE_TYPE(math, OP_POSITIVE, OBJECT) + CASE_TYPE(math, OP_POSITIVE, STRING_NAME) + CASE_TYPE(math, OP_POSITIVE, DICTIONARY) + CASE_TYPE(math, OP_POSITIVE, ARRAY) + CASE_TYPE(math, OP_POSITIVE, POOL_BYTE_ARRAY) + CASE_TYPE(math, OP_POSITIVE, POOL_INT_ARRAY) + CASE_TYPE(math, OP_POSITIVE, POOL_REAL_ARRAY) + CASE_TYPE(math, OP_POSITIVE, POOL_STRING_ARRAY) + CASE_TYPE(math, OP_POSITIVE, POOL_VECTOR2_ARRAY) + CASE_TYPE(math, OP_POSITIVE, POOL_VECTOR2I_ARRAY) + CASE_TYPE(math, OP_POSITIVE, POOL_VECTOR3_ARRAY) + CASE_TYPE(math, OP_POSITIVE, POOL_VECTOR3I_ARRAY) + CASE_TYPE(math, OP_POSITIVE, POOL_VECTOR4_ARRAY) + CASE_TYPE(math, OP_POSITIVE, POOL_VECTOR4I_ARRAY) + CASE_TYPE(math, OP_POSITIVE, POOL_COLOR_ARRAY) + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_NEGATE, p_a.type) { + DEFAULT_OP_NUM_NEG(math, OP_NEGATE, INT, _int); + DEFAULT_OP_NUM_NEG(math, OP_NEGATE, REAL, _real); + + DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, VECTOR2, Vector2); + DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, VECTOR2I, Vector2i); + DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, VECTOR3, Vector3); + DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, VECTOR3I, Vector3i); + DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, VECTOR4, Vector4); + DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, VECTOR4I, Vector4i); + DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, PLANE, Plane); + DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, QUATERNION, Quaternion); + DEFAULT_OP_LOCALMEM_NEG(math, OP_NEGATE, COLOR, Color); + + CASE_TYPE(math, OP_NEGATE, NIL) + CASE_TYPE(math, OP_NEGATE, BOOL) + CASE_TYPE(math, OP_NEGATE, STRING) + CASE_TYPE(math, OP_NEGATE, RECT2) + CASE_TYPE(math, OP_NEGATE, RECT2I) + CASE_TYPE(math, OP_NEGATE, AABB) + CASE_TYPE(math, OP_NEGATE, BASIS) + CASE_TYPE(math, OP_NEGATE, TRANSFORM) + CASE_TYPE(math, OP_NEGATE, TRANSFORM2D) + CASE_TYPE(math, OP_NEGATE, PROJECTION) + CASE_TYPE(math, OP_NEGATE, OBJECT) + CASE_TYPE(math, OP_NEGATE, STRING_NAME) + CASE_TYPE(math, OP_NEGATE, DICTIONARY) + CASE_TYPE(math, OP_NEGATE, ARRAY) + CASE_TYPE(math, OP_NEGATE, POOL_BYTE_ARRAY) + CASE_TYPE(math, OP_NEGATE, POOL_INT_ARRAY) + CASE_TYPE(math, OP_NEGATE, POOL_REAL_ARRAY) + CASE_TYPE(math, OP_NEGATE, POOL_STRING_ARRAY) + CASE_TYPE(math, OP_NEGATE, POOL_VECTOR2_ARRAY) + CASE_TYPE(math, OP_NEGATE, POOL_VECTOR2I_ARRAY) + CASE_TYPE(math, OP_NEGATE, POOL_VECTOR3_ARRAY) + CASE_TYPE(math, OP_NEGATE, POOL_VECTOR3I_ARRAY) + CASE_TYPE(math, OP_NEGATE, POOL_VECTOR4_ARRAY) + CASE_TYPE(math, OP_NEGATE, POOL_VECTOR4I_ARRAY) + CASE_TYPE(math, OP_NEGATE, POOL_COLOR_ARRAY) + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_MODULE, p_a.type) { + CASE_TYPE(math, OP_MODULE, INT) { + if (p_b.type != INT) + _RETURN_FAIL; +#ifdef DEBUG_ENABLED + if (p_b._data._int == 0) { + r_valid = false; + _RETURN("Division By Zero"); + } +#endif + _RETURN(p_a._data._int % p_b._data._int); + } + + CASE_TYPE(math, OP_MODULE, STRING) { + const String *format = reinterpret_cast(p_a._data._mem); + + String result; + bool error; + if (p_b.type == ARRAY) { + // e.g. "frog %s %d" % ["fish", 12] + const Array *args = reinterpret_cast(p_b._data._mem); + result = args->sprintf(*format, &error); + } else { + // e.g. "frog %d" % 12 + Array args; + args.push_back(p_b); + result = args.sprintf(*format, &error); + } + r_valid = !error; + _RETURN(result); + } + + CASE_TYPE(math, OP_MODULE, NIL) + CASE_TYPE(math, OP_MODULE, BOOL) + CASE_TYPE(math, OP_MODULE, REAL) + CASE_TYPE(math, OP_MODULE, RECT2) + CASE_TYPE(math, OP_MODULE, RECT2I) + CASE_TYPE(math, OP_MODULE, VECTOR2) + CASE_TYPE(math, OP_MODULE, VECTOR2I) + CASE_TYPE(math, OP_MODULE, VECTOR3) + CASE_TYPE(math, OP_MODULE, VECTOR3I) + CASE_TYPE(math, OP_MODULE, VECTOR4) + CASE_TYPE(math, OP_MODULE, VECTOR4I) + CASE_TYPE(math, OP_MODULE, PLANE) + CASE_TYPE(math, OP_MODULE, QUATERNION) + CASE_TYPE(math, OP_MODULE, AABB) + CASE_TYPE(math, OP_MODULE, BASIS) + CASE_TYPE(math, OP_MODULE, TRANSFORM) + CASE_TYPE(math, OP_MODULE, TRANSFORM2D) + CASE_TYPE(math, OP_MODULE, PROJECTION) + CASE_TYPE(math, OP_MODULE, COLOR) + CASE_TYPE(math, OP_MODULE, OBJECT) + CASE_TYPE(math, OP_MODULE, STRING_NAME) + CASE_TYPE(math, OP_MODULE, DICTIONARY) + CASE_TYPE(math, OP_MODULE, ARRAY) + CASE_TYPE(math, OP_MODULE, POOL_BYTE_ARRAY) + CASE_TYPE(math, OP_MODULE, POOL_INT_ARRAY) + CASE_TYPE(math, OP_MODULE, POOL_REAL_ARRAY) + CASE_TYPE(math, OP_MODULE, POOL_STRING_ARRAY) + CASE_TYPE(math, OP_MODULE, POOL_VECTOR2_ARRAY) + CASE_TYPE(math, OP_MODULE, POOL_VECTOR2I_ARRAY) + CASE_TYPE(math, OP_MODULE, POOL_VECTOR3_ARRAY) + CASE_TYPE(math, OP_MODULE, POOL_VECTOR3I_ARRAY) + CASE_TYPE(math, OP_MODULE, POOL_VECTOR4_ARRAY) + CASE_TYPE(math, OP_MODULE, POOL_VECTOR4I_ARRAY) + CASE_TYPE(math, OP_MODULE, POOL_COLOR_ARRAY) + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_STRING_CONCAT, p_a.type) { + CASE_TYPE_ALL(math, OP_STRING_CONCAT) + + _RETURN(p_a.operator String() + p_b.operator String()); + } + + SWITCH_OP(math, OP_SHIFT_LEFT, p_a.type) { + CASE_TYPE(math, OP_SHIFT_LEFT, INT) { + if (p_b.type != INT) + _RETURN_FAIL; + if (p_b._data._int < 0 || p_b._data._int >= 64) + _RETURN_FAIL; + _RETURN(p_a._data._int << p_b._data._int); + } + + CASE_TYPE_ALL_BUT_INT(math, OP_SHIFT_LEFT) + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_SHIFT_RIGHT, p_a.type) { + CASE_TYPE(math, OP_SHIFT_RIGHT, INT) { + if (p_b.type != INT) + _RETURN_FAIL; + if (p_b._data._int < 0 || p_b._data._int >= 64) + _RETURN_FAIL; + _RETURN(p_a._data._int >> p_b._data._int); + } + + CASE_TYPE_ALL_BUT_INT(math, OP_SHIFT_RIGHT) + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_BIT_AND, p_a.type) { + CASE_TYPE(math, OP_BIT_AND, INT) { + if (p_b.type != INT) + _RETURN_FAIL; + _RETURN(p_a._data._int & p_b._data._int); + } + + CASE_TYPE_ALL_BUT_INT(math, OP_BIT_AND) + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_BIT_OR, p_a.type) { + CASE_TYPE(math, OP_BIT_OR, INT) { + if (p_b.type != INT) + _RETURN_FAIL; + _RETURN(p_a._data._int | p_b._data._int); + } + + CASE_TYPE_ALL_BUT_INT(math, OP_BIT_OR) + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_BIT_XOR, p_a.type) { + CASE_TYPE(math, OP_BIT_XOR, INT) { + if (p_b.type != INT) + _RETURN_FAIL; + _RETURN(p_a._data._int ^ p_b._data._int); + } + + CASE_TYPE_ALL_BUT_INT(math, OP_BIT_XOR) + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_BIT_NEGATE, p_a.type) { + CASE_TYPE(math, OP_BIT_NEGATE, INT) { + _RETURN(~p_a._data._int); + } + + CASE_TYPE_ALL_BUT_INT(math, OP_BIT_NEGATE) + _RETURN_FAIL; + } + + SWITCH_OP(math, OP_AND, p_a.type) { + CASE_TYPE_ALL(math, OP_AND) { + bool l = p_a.booleanize(); + bool r = p_b.booleanize(); + + _RETURN(l && r); + } + } + + SWITCH_OP(math, OP_OR, p_a.type) { + CASE_TYPE_ALL(math, OP_OR) { + bool l = p_a.booleanize(); + bool r = p_b.booleanize(); + + _RETURN(l || r); + } + } + + SWITCH_OP(math, OP_XOR, p_a.type) { + CASE_TYPE_ALL(math, OP_XOR) { + bool l = p_a.booleanize(); + bool r = p_b.booleanize(); + + _RETURN((l || r) && !(l && r)); + } + } + + SWITCH_OP(math, OP_NOT, p_a.type) { + CASE_TYPE_ALL(math, OP_NOT) { + bool l = p_a.booleanize(); + _RETURN(!l); + } + } + + SWITCH_OP(math, OP_IN, p_a.type) { + CASE_TYPE_ALL(math, OP_IN) + _RETURN(p_b.in(p_a, &r_valid)); + } + } +} + +void Variant::set_named(const StringName &p_index, const Variant &p_value, bool *r_valid) { + bool valid = false; + switch (type) { + case RECT2: { + if (p_value.type == Variant::VECTOR2) { + Rect2 *v = reinterpret_cast(_data._mem); + //scalar name + if (p_index == CoreStringNames::singleton->position) { + v->position = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->size) { + v->size = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->end) { + v->size = *reinterpret_cast(p_value._data._mem) - v->position; + valid = true; + } + } else if (p_value.type == Variant::VECTOR2I) { + Rect2 *v = reinterpret_cast(_data._mem); + //scalar name + if (p_index == CoreStringNames::singleton->position) { + v->position = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->size) { + v->size = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->end) { + v->size = *reinterpret_cast(p_value._data._mem) - v->position; + valid = true; + } + } + } break; + case RECT2I: { + if (p_value.type == Variant::VECTOR2) { + Rect2i *v = reinterpret_cast(_data._mem); + //scalar name + if (p_index == CoreStringNames::singleton->position) { + v->position = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->size) { + v->size = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->end) { + v->size = *reinterpret_cast(p_value._data._mem) - v->position; + valid = true; + } + } else if (p_value.type == Variant::VECTOR2I) { + Rect2i *v = reinterpret_cast(_data._mem); + //scalar name + if (p_index == CoreStringNames::singleton->position) { + v->position = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->size) { + v->size = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->end) { + v->size = *reinterpret_cast(p_value._data._mem) - v->position; + valid = true; + } + } + } break; + case VECTOR2: { + if (p_value.type == Variant::INT) { + Vector2 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._int; + valid = true; + } + } else if (p_value.type == Variant::REAL) { + Vector2 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._real; + valid = true; + } + } + + } break; + case VECTOR2I: { + if (p_value.type == Variant::INT) { + Vector2i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._int; + valid = true; + } + } else if (p_value.type == Variant::REAL) { + Vector2i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = static_cast(p_value._data._real); + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = static_cast(p_value._data._real); + valid = true; + } + } + + } break; + case VECTOR3: { + if (p_value.type == Variant::INT) { + Vector3 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->z = p_value._data._int; + valid = true; + } + } else if (p_value.type == Variant::REAL) { + Vector3 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->z = p_value._data._real; + valid = true; + } + } + + } break; + case VECTOR3I: { + if (p_value.type == Variant::INT) { + Vector3i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->z = p_value._data._int; + valid = true; + } + } else if (p_value.type == Variant::REAL) { + Vector3i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->z = p_value._data._real; + valid = true; + } + } + + } break; + case VECTOR4: { + if (p_value.type == Variant::INT) { + Vector4 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->z = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->w) { + v->w = p_value._data._int; + valid = true; + } + } else if (p_value.type == Variant::REAL) { + Vector4 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->z = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->w) { + v->w = p_value._data._real; + valid = true; + } + } + + } break; + case VECTOR4I: { + if (p_value.type == Variant::INT) { + Vector4i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->z = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->w) { + v->w = p_value._data._int; + valid = true; + } + } else if (p_value.type == Variant::REAL) { + Vector4i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->z = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->w) { + v->w = p_value._data._real; + valid = true; + } + } + + } break; + case PLANE: { + if (p_value.type == Variant::INT) { + Plane *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->normal.x = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->normal.y = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->normal.z = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->d) { + v->d = p_value._data._int; + valid = true; + } + } else if (p_value.type == Variant::REAL) { + Plane *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->normal.x = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->normal.y = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->normal.z = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->d) { + v->d = p_value._data._real; + valid = true; + } + + } else if (p_value.type == Variant::VECTOR3) { + Plane *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->normal) { + v->normal = *reinterpret_cast(p_value._data._mem); + valid = true; + } + } + + } break; + case QUATERNION: { + if (p_value.type == Variant::INT) { + Quaternion *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->z = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->w) { + v->w = p_value._data._int; + valid = true; + } + } else if (p_value.type == Variant::REAL) { + Quaternion *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + v->x = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->y = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->z = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->w) { + v->w = p_value._data._real; + valid = true; + } + } + + } break; // 10 + case AABB: { + if (p_value.type == Variant::VECTOR3) { + ::AABB *v = _data._aabb; + //scalar name + if (p_index == CoreStringNames::singleton->position) { + v->position = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->size) { + v->size = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->end) { + v->size = *reinterpret_cast(p_value._data._mem) - v->position; + valid = true; + } + } else if (p_value.type == Variant::VECTOR3I) { + ::AABB *v = _data._aabb; + //scalar name + if (p_index == CoreStringNames::singleton->position) { + v->position = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->size) { + v->size = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->end) { + v->size = Vector3(*reinterpret_cast(p_value._data._mem)) - v->position; + valid = true; + } + } + } break; + case BASIS: { + if (p_value.type == Variant::VECTOR3) { + Basis *v = _data._basis; + //scalar name + if (p_index == CoreStringNames::singleton->x) { + v->set_axis(0, *reinterpret_cast(p_value._data._mem)); + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->set_axis(1, *reinterpret_cast(p_value._data._mem)); + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->set_axis(2, *reinterpret_cast(p_value._data._mem)); + valid = true; + } + } else if (p_value.type == Variant::VECTOR3I) { + Basis *v = _data._basis; + //scalar name + if (p_index == CoreStringNames::singleton->x) { + v->set_axis(0, *reinterpret_cast(p_value._data._mem)); + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->set_axis(1, *reinterpret_cast(p_value._data._mem)); + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->set_axis(2, *reinterpret_cast(p_value._data._mem)); + valid = true; + } + } + } break; + case TRANSFORM: { + if (p_value.type == Variant::BASIS && p_index == CoreStringNames::singleton->basis) { + _data._transform->basis = *p_value._data._basis; + valid = true; + } else if (p_value.type == Variant::VECTOR3 && p_index == CoreStringNames::singleton->origin) { + _data._transform->origin = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_value.type == Variant::VECTOR3I && p_index == CoreStringNames::singleton->origin) { + _data._transform->origin = *reinterpret_cast(p_value._data._mem); + valid = true; + } + + } break; + case TRANSFORM2D: { + if (p_value.type == Variant::VECTOR2) { + Transform2D *v = _data._transform2d; + if (p_index == CoreStringNames::singleton->x) { + v->columns[0] = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->columns[1] = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->origin) { + v->columns[2] = *reinterpret_cast(p_value._data._mem); + valid = true; + } + } else if (p_value.type == Variant::VECTOR2I) { + Transform2D *v = _data._transform2d; + if (p_index == CoreStringNames::singleton->x) { + v->columns[0] = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->columns[1] = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->origin) { + v->columns[2] = *reinterpret_cast(p_value._data._mem); + valid = true; + } + } + + } break; + case PROJECTION: { + if (p_value.type == Variant::VECTOR4) { + Projection *v = _data._projection; + if (p_index == CoreStringNames::singleton->x) { + v->matrix[0] = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->y) { + v->matrix[1] = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->z) { + v->matrix[2] = *reinterpret_cast(p_value._data._mem); + valid = true; + } else if (p_index == CoreStringNames::singleton->w) { + v->matrix[3] = *reinterpret_cast(p_value._data._mem); + valid = true; + } + } + + } break; + case COLOR: { + if (p_value.type == Variant::INT) { + Color *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->r) { + v->r = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->g) { + v->g = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->b) { + v->b = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->a) { + v->a = p_value._data._int; + valid = true; + } else if (p_index == CoreStringNames::singleton->r8) { + v->r = p_value._data._int / 255.0; + valid = true; + } else if (p_index == CoreStringNames::singleton->g8) { + v->g = p_value._data._int / 255.0; + valid = true; + } else if (p_index == CoreStringNames::singleton->b8) { + v->b = p_value._data._int / 255.0; + valid = true; + } else if (p_index == CoreStringNames::singleton->a8) { + v->a = p_value._data._int / 255.0; + valid = true; + } else if (p_index == CoreStringNames::singleton->h) { + v->set_hsv(p_value._data._int, v->get_s(), v->get_v(), v->a); + valid = true; + } else if (p_index == CoreStringNames::singleton->s) { + v->set_hsv(v->get_h(), p_value._data._int, v->get_v(), v->a); + valid = true; + } else if (p_index == CoreStringNames::singleton->v) { + v->set_hsv(v->get_h(), v->get_s(), p_value._data._int, v->a); + valid = true; + } + } else if (p_value.type == Variant::REAL) { + Color *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->r) { + v->r = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->g) { + v->g = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->b) { + v->b = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->a) { + v->a = p_value._data._real; + valid = true; + } else if (p_index == CoreStringNames::singleton->r8) { + v->r = p_value._data._real / 255.0; + valid = true; + } else if (p_index == CoreStringNames::singleton->g8) { + v->g = p_value._data._real / 255.0; + valid = true; + } else if (p_index == CoreStringNames::singleton->b8) { + v->b = p_value._data._real / 255.0; + valid = true; + } else if (p_index == CoreStringNames::singleton->a8) { + v->a = p_value._data._real / 255.0; + valid = true; + } else if (p_index == CoreStringNames::singleton->h) { + v->set_hsv(p_value._data._real, v->get_s(), v->get_v(), v->a); + valid = true; + } else if (p_index == CoreStringNames::singleton->s) { + v->set_hsv(v->get_h(), p_value._data._real, v->get_v(), v->a); + valid = true; + } else if (p_index == CoreStringNames::singleton->v) { + v->set_hsv(v->get_h(), v->get_s(), p_value._data._real, v->a); + valid = true; + } + } + } break; + case OBJECT: { + Object *obj = _OBJ_PTR(*this); + if (unlikely(!obj)) { +#ifdef DEBUG_ENABLED + if (_get_obj().rc) { + ERR_PRINT("Attempted set on a deleted object."); + } +#endif + break; + } + obj->set(p_index, p_value, &valid); + + } break; + default: { + set(p_index.operator String(), p_value, &valid); + } break; + } + + if (r_valid) { + *r_valid = valid; + } +} + +Variant Variant::get_named(const StringName &p_index, bool *r_valid) const { + if (r_valid) { + *r_valid = true; + } + switch (type) { + case RECT2: { + const Rect2 *v = reinterpret_cast(_data._mem); + //scalar name + if (p_index == CoreStringNames::singleton->position) { + return v->position; + } else if (p_index == CoreStringNames::singleton->size) { + return v->size; + } else if (p_index == CoreStringNames::singleton->end) { + return v->size + v->position; + } + } break; + case RECT2I: { + const Rect2i *v = reinterpret_cast(_data._mem); + //scalar name + if (p_index == CoreStringNames::singleton->position) { + return v->position; + } else if (p_index == CoreStringNames::singleton->size) { + return v->size; + } else if (p_index == CoreStringNames::singleton->end) { + return v->size + v->position; + } + } break; + case VECTOR2: { + const Vector2 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + return v->y; + } + + } break; + case VECTOR2I: { + const Vector2i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + return v->y; + } + + } break; + case VECTOR3: { + const Vector3 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + return v->y; + } else if (p_index == CoreStringNames::singleton->z) { + return v->z; + } + + } break; + case VECTOR3I: { + const Vector3i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + return v->y; + } else if (p_index == CoreStringNames::singleton->z) { + return v->z; + } + + } break; + case VECTOR4: { + const Vector4 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + return v->y; + } else if (p_index == CoreStringNames::singleton->z) { + return v->z; + } else if (p_index == CoreStringNames::singleton->w) { + return v->w; + } + + } break; + case VECTOR4I: { + const Vector4i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + return v->y; + } else if (p_index == CoreStringNames::singleton->z) { + return v->z; + } else if (p_index == CoreStringNames::singleton->w) { + return v->w; + } + + } break; + case PLANE: { + const Plane *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + return v->normal.x; + } else if (p_index == CoreStringNames::singleton->y) { + return v->normal.y; + } else if (p_index == CoreStringNames::singleton->z) { + return v->normal.z; + } else if (p_index == CoreStringNames::singleton->d) { + return v->d; + } else if (p_index == CoreStringNames::singleton->normal) { + return v->normal; + } + + } break; + case QUATERNION: { + const Quaternion *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + return v->y; + } else if (p_index == CoreStringNames::singleton->z) { + return v->z; + } else if (p_index == CoreStringNames::singleton->w) { + return v->w; + } + + } break; // 10 + case AABB: { + const ::AABB *v = _data._aabb; + //scalar name + if (p_index == CoreStringNames::singleton->position) { + return v->position; + } else if (p_index == CoreStringNames::singleton->size) { + return v->size; + } else if (p_index == CoreStringNames::singleton->end) { + return v->size + v->position; + } + } break; + case BASIS: { + const Basis *v = _data._basis; + //scalar name + if (p_index == CoreStringNames::singleton->x) { + return v->get_axis(0); + } else if (p_index == CoreStringNames::singleton->y) { + return v->get_axis(1); + } else if (p_index == CoreStringNames::singleton->z) { + return v->get_axis(2); + } + + } break; + case TRANSFORM: { + if (p_index == CoreStringNames::singleton->basis) { + return _data._transform->basis; + } else if (p_index == CoreStringNames::singleton->origin) { + return _data._transform->origin; + } + + } break; + case TRANSFORM2D: { + const Transform2D *v = _data._transform2d; + if (p_index == CoreStringNames::singleton->x) { + return v->columns[0]; + } else if (p_index == CoreStringNames::singleton->y) { + return v->columns[1]; + } else if (p_index == CoreStringNames::singleton->origin) { + return v->columns[2]; + } + + } break; + case PROJECTION: { + const Projection *v = _data._projection; + if (p_index == CoreStringNames::singleton->x) { + return v->matrix[0]; + } else if (p_index == CoreStringNames::singleton->y) { + return v->matrix[1]; + } else if (p_index == CoreStringNames::singleton->z) { + return v->matrix[2]; + } else if (p_index == CoreStringNames::singleton->w) { + return v->matrix[3]; + } + + } break; + case COLOR: { + const Color *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->r) { + return v->r; + } else if (p_index == CoreStringNames::singleton->g) { + return v->g; + } else if (p_index == CoreStringNames::singleton->b) { + return v->b; + } else if (p_index == CoreStringNames::singleton->a) { + return v->a; + } else if (p_index == CoreStringNames::singleton->r8) { + return int(Math::round(v->r * 255.0)); + } else if (p_index == CoreStringNames::singleton->g8) { + return int(Math::round(v->g * 255.0)); + } else if (p_index == CoreStringNames::singleton->b8) { + return int(Math::round(v->b * 255.0)); + } else if (p_index == CoreStringNames::singleton->a8) { + return int(Math::round(v->a * 255.0)); + } else if (p_index == CoreStringNames::singleton->h) { + return v->get_h(); + } else if (p_index == CoreStringNames::singleton->s) { + return v->get_s(); + } else if (p_index == CoreStringNames::singleton->v) { + return v->get_v(); + } + } break; + case OBJECT: { + Object *obj = _OBJ_PTR(*this); + if (unlikely(!obj)) { + if (r_valid) { + *r_valid = false; + } +#ifdef DEBUG_ENABLED + if (_get_obj().rc) { + ERR_PRINT("Attempted get on a deleted object."); + } +#endif + return Variant(); + } + + return obj->get(p_index, r_valid); + + } break; + case DICTIONARY: { + const Dictionary *dic = reinterpret_cast(_data._mem); + const Variant *res = dic->getptr(p_index); + if (!res) { + // Backwards compatibility for before variants supported stringnames. + const Variant *res2 = dic->getptr(p_index.operator String()); + + if (res2) { + if (r_valid) { + *r_valid = true; + } + return *res2; + } + } else { + if (r_valid) { + *r_valid = true; + } + return *res; + } + } break; + default: { + return get(p_index.operator String(), r_valid); + } + } + + if (r_valid) { + *r_valid = false; + } + return Variant(); +} + +#define DEFAULT_OP_ARRAY_CMD(m_name, m_type, skip_test, cmd) \ + case m_name: { \ + skip_test; \ + \ + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { \ + int index = p_index; \ + m_type *arr = reinterpret_cast(_data._mem); \ + \ + if (index < 0) \ + index += arr->size(); \ + if (index >= 0 && index < arr->size()) { \ + valid = true; \ + cmd; \ + } \ + } \ + } break; + +// clang-format 14 wants to add a space after the last return, +// and clang-format 15 removes it... +/* clang-format off */ +#define DEFAULT_OP_DVECTOR_SET(m_name, dv_type, skip_cond) \ + DEFAULT_OP_ARRAY_CMD(m_name, PoolVector, if (skip_cond) return;, arr->set(index, p_value); return) +/* clang-format on */ + +#define DEFAULT_OP_DVECTOR_GET(m_name, dv_type) \ + DEFAULT_OP_ARRAY_CMD(m_name, const PoolVector, ;, return arr->get(index)) + +void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) { + static bool _dummy = false; + + bool &valid = r_valid ? *r_valid : _dummy; + valid = false; + + switch (type) { + case NIL: { + return; + } break; + case BOOL: { + return; + } break; + case INT: { + return; + } break; + case REAL: { + return; + } break; + case STRING: { + if (p_index.type != Variant::INT && p_index.type != Variant::REAL) { + return; + } + + int idx = p_index; + String *str = reinterpret_cast(_data._mem); + int len = str->length(); + if (idx < 0) { + idx += len; + } + if (idx < 0 || idx >= len) { + return; + } + + String chr; + if (p_value.type == Variant::INT || p_value.type == Variant::REAL) { + chr = String::chr(p_value); + } else if (p_value.type == Variant::STRING) { + chr = p_value; + } else { + return; + } + + *str = str->substr(0, idx) + chr + str->substr(idx + 1, len); + valid = true; + return; + + } break; + case RECT2: { + if (p_value.type == Variant::VECTOR2 || p_value.type == Variant::VECTOR2I) { + if (p_index.get_type() == Variant::STRING) { + //scalar name + + const String *str = reinterpret_cast(p_index._data._mem); + Rect2 *v = reinterpret_cast(_data._mem); + if (*str == "position") { + valid = true; + v->position = p_value; + return; + } else if (*str == "size") { + valid = true; + v->size = p_value; + return; + } else if (*str == "end") { + valid = true; + v->size = Vector2(p_value) - v->position; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + Rect2 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->position) { + valid = true; + v->position = p_value; + return; + } else if (p_index == CoreStringNames::singleton->size) { + valid = true; + v->size = p_value; + return; + } else if (p_index == CoreStringNames::singleton->end) { + valid = true; + v->size = Vector2(p_value) - v->position; + return; + } + } + } else { + return; + } + } break; //7 + case RECT2I: { + if (p_value.type == Variant::VECTOR2 || p_value.type == Variant::VECTOR2I) { + if (p_index.get_type() == Variant::STRING) { + //scalar name + + const String *str = reinterpret_cast(p_index._data._mem); + Rect2i *v = reinterpret_cast(_data._mem); + if (*str == "position") { + valid = true; + v->position = p_value; + return; + } else if (*str == "size") { + valid = true; + v->size = p_value; + return; + } else if (*str == "end") { + valid = true; + //TODO fix + v->size = Vector2i(Vector2(p_value)) - v->position; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + Rect2i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->position) { + valid = true; + v->position = p_value; + return; + } else if (p_index == CoreStringNames::singleton->size) { + valid = true; + v->size = p_value; + return; + } else if (p_index == CoreStringNames::singleton->end) { + valid = true; + v->size = Vector2(p_value) - v->position; + return; + } + } + } else { + return; + } + } break; + case VECTOR2: { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + // scalar index + int idx = p_index; + + if (idx < 0) { + idx += 2; + } + if (idx >= 0 && idx < 2) { + Vector2 *v = reinterpret_cast(_data._mem); + valid = true; + (*v)[idx] = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + + const String *str = reinterpret_cast(p_index._data._mem); + Vector2 *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + v->x = p_value; + return; + } else if (*str == "y") { + valid = true; + v->y = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + Vector2 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + v->x = p_value; + return; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + v->y = p_value; + return; + } + } + } break; // 5 + case VECTOR2I: { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + // scalar index + int idx = p_index; + + if (idx < 0) { + idx += 2; + } + if (idx >= 0 && idx < 2) { + Vector2i *v = reinterpret_cast(_data._mem); + valid = true; + (*v)[idx] = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + + const String *str = reinterpret_cast(p_index._data._mem); + Vector2i *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + v->x = p_value; + return; + } else if (*str == "y") { + valid = true; + v->y = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + Vector2i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + v->x = p_value; + return; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + v->y = p_value; + return; + } + } + + } break; //6 + + case VECTOR3: { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + //scalar index + int idx = p_index; + if (idx < 0) { + idx += 3; + } + if (idx >= 0 && idx < 3) { + Vector3 *v = reinterpret_cast(_data._mem); + valid = true; + (*v)[idx] = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + const String *str = reinterpret_cast(p_index._data._mem); + Vector3 *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + v->x = p_value; + return; + } else if (*str == "y") { + valid = true; + v->y = p_value; + return; + } else if (*str == "z") { + valid = true; + v->z = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + Vector3 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + v->x = p_value; + return; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + v->y = p_value; + return; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + v->z = p_value; + return; + } + } + + } break; + case VECTOR3I: { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + //scalar index + int idx = p_index; + if (idx < 0) { + idx += 3; + } + if (idx >= 0 && idx < 3) { + Vector3i *v = reinterpret_cast(_data._mem); + valid = true; + (*v)[idx] = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + const String *str = reinterpret_cast(p_index._data._mem); + Vector3 *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + v->x = p_value; + return; + } else if (*str == "y") { + valid = true; + v->y = p_value; + return; + } else if (*str == "z") { + valid = true; + v->z = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + Vector3i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + v->x = p_value; + return; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + v->y = p_value; + return; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + v->z = p_value; + return; + } + } + + } break; + case VECTOR4: { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + //scalar index + int idx = p_index; + if (idx < 0) { + idx += 4; + } + if (idx >= 0 && idx < 4) { + Vector4 *v = reinterpret_cast(_data._mem); + valid = true; + (*v)[idx] = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + const String *str = reinterpret_cast(p_index._data._mem); + Vector4 *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + v->x = p_value; + return; + } else if (*str == "y") { + valid = true; + v->y = p_value; + return; + } else if (*str == "z") { + valid = true; + v->z = p_value; + return; + } else if (*str == "w") { + valid = true; + v->w = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + Vector4 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + v->x = p_value; + return; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + v->y = p_value; + return; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + v->z = p_value; + return; + } else if (p_index == CoreStringNames::singleton->w) { + valid = true; + v->w = p_value; + return; + } + } + } break; + case VECTOR4I: { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + //scalar index + int idx = p_index; + if (idx < 0) { + idx += 4; + } + if (idx >= 0 && idx < 4) { + Vector4i *v = reinterpret_cast(_data._mem); + valid = true; + (*v)[idx] = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + const String *str = reinterpret_cast(p_index._data._mem); + Vector4 *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + v->x = p_value; + return; + } else if (*str == "y") { + valid = true; + v->y = p_value; + return; + } else if (*str == "z") { + valid = true; + v->z = p_value; + return; + } else if (*str == "w") { + valid = true; + v->w = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + Vector4i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + v->x = p_value; + return; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + v->y = p_value; + return; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + v->z = p_value; + return; + } else if (p_index == CoreStringNames::singleton->w) { + valid = true; + v->w = p_value; + return; + } + } + + } break; + case PLANE: { + if (p_index.get_type() == Variant::STRING) { + //scalar name + const String *str = reinterpret_cast(p_index._data._mem); + Plane *v = reinterpret_cast(_data._mem); + if (*str == "x") { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + valid = true; + v->normal.x = p_value; + return; + } else if (*str == "y") { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + valid = true; + v->normal.y = p_value; + return; + } else if (*str == "z") { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + valid = true; + v->normal.z = p_value; + return; + } else if (*str == "normal") { + if (p_value.type != Variant::VECTOR3) { + return; + } + + valid = true; + v->normal = p_value; + return; + } else if (*str == "d") { + valid = true; + v->d = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + Plane *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + valid = true; + v->normal.x = p_value; + return; + } else if (p_index == CoreStringNames::singleton->y) { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + valid = true; + v->normal.y = p_value; + return; + } else if (p_index == CoreStringNames::singleton->z) { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + valid = true; + v->normal.z = p_value; + return; + } else if (p_index == CoreStringNames::singleton->normal) { + if (p_value.type != Variant::VECTOR3) { + return; + } + + valid = true; + v->normal = p_value; + return; + } else if (p_index == CoreStringNames::singleton->d) { + valid = true; + v->d = p_value; + return; + } + } + + } break; + case QUATERNION: { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + if (p_index.get_type() == Variant::STRING) { + const String *str = reinterpret_cast(p_index._data._mem); + Quaternion *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + v->x = p_value; + return; + } else if (*str == "y") { + valid = true; + v->y = p_value; + return; + } else if (*str == "z") { + valid = true; + v->z = p_value; + return; + } else if (*str == "w") { + valid = true; + v->w = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + Quaternion *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + v->x = p_value; + return; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + v->y = p_value; + return; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + v->z = p_value; + return; + } else if (p_index == CoreStringNames::singleton->w) { + valid = true; + v->w = p_value; + return; + } + } + + } break; // 10 + case AABB: { + if (p_value.type != Variant::VECTOR3) { + return; + } + + if (p_index.get_type() == Variant::STRING) { + //scalar name + + const String *str = reinterpret_cast(p_index._data._mem); + ::AABB *v = _data._aabb; + if (*str == "position") { + valid = true; + v->position = p_value; + return; + } else if (*str == "size") { + valid = true; + v->size = p_value; + return; + } else if (*str == "end") { + valid = true; + v->size = Vector3(p_value) - v->position; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + ::AABB *v = _data._aabb; + if (p_index == CoreStringNames::singleton->position) { + valid = true; + v->position = p_value; + return; + } else if (p_index == CoreStringNames::singleton->size) { + valid = true; + v->size = p_value; + return; + } else if (p_index == CoreStringNames::singleton->end) { + valid = true; + v->size = Vector3(p_value) - v->position; + return; + } + } + } break; + case BASIS: { + if (p_value.type != Variant::VECTOR3) { + return; + } + + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + int index = p_index; + + if (index < 0) { + index += 3; + } + if (index >= 0 && index < 3) { + Basis *v = _data._basis; + + valid = true; + v->set_axis(index, p_value); + return; + } + } else if (p_index.get_type() == Variant::STRING) { + const String *str = reinterpret_cast(p_index._data._mem); + Basis *v = _data._basis; + + if (*str == "x") { + valid = true; + v->set_axis(0, p_value); + return; + } else if (*str == "y") { + valid = true; + v->set_axis(1, p_value); + return; + } else if (*str == "z") { + valid = true; + v->set_axis(2, p_value); + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + Basis *v = _data._basis; + + if (p_index == CoreStringNames::singleton->x) { + valid = true; + v->set_axis(0, p_value); + return; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + v->set_axis(1, p_value); + return; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + v->set_axis(2, p_value); + return; + } + } + + } break; + case TRANSFORM: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + if (p_value.type != Variant::VECTOR3) { + return; + } + + int index = p_index; + + if (index < 0) { + index += 4; + } + if (index >= 0 && index < 4) { + Transform *v = _data._transform; + valid = true; + if (index == 3) { + v->origin = p_value; + } else { + v->basis.set_axis(index, p_value); + } + return; + } + } else if (p_index.get_type() == Variant::STRING) { + Transform *v = _data._transform; + const String *str = reinterpret_cast(p_index._data._mem); + + if (*str == "basis") { + if (p_value.type != Variant::BASIS) { + return; + } + valid = true; + v->basis = p_value; + return; + } + if (*str == "origin") { + if (p_value.type != Variant::VECTOR3) { + return; + } + valid = true; + v->origin = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + Transform *v = _data._transform; + + if (p_index == CoreStringNames::singleton->basis) { + if (p_value.type != Variant::BASIS) { + return; + } + valid = true; + v->basis = p_value; + return; + } + if (p_index == CoreStringNames::singleton->origin) { + if (p_value.type != Variant::VECTOR3) { + return; + } + valid = true; + v->origin = p_value; + return; + } + } + + } break; + case TRANSFORM2D: { + if (p_value.type != Variant::VECTOR2 || p_value.get_type() != Variant::VECTOR2I) { + return; + } + + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + int index = p_index; + + if (index < 0) { + index += 3; + } + if (index >= 0 && index < 3) { + Transform2D *v = _data._transform2d; + + valid = true; + v->columns[index] = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + const String *str = reinterpret_cast(p_index._data._mem); + Transform2D *v = _data._transform2d; + if (*str == "x") { + valid = true; + v->columns[0] = p_value; + return; + } else if (*str == "y") { + valid = true; + v->columns[1] = p_value; + return; + } else if (*str == "origin") { + valid = true; + v->columns[2] = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + Transform2D *v = _data._transform2d; + if (p_index == CoreStringNames::singleton->x) { + valid = true; + v->columns[0] = p_value; + return; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + v->columns[1] = p_value; + return; + } else if (p_index == CoreStringNames::singleton->origin) { + valid = true; + v->columns[2] = p_value; + return; + } + } + + } break; + case PROJECTION: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + if (p_value.type != Variant::VECTOR4) { + return; + } + + int index = p_index; + + if (index < 0) { + index += 4; + } + if (index >= 0 && index < 4) { + Projection *v = _data._projection; + valid = true; + v->matrix[index] = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING) { + Projection *v = _data._projection; + const String *str = reinterpret_cast(p_index._data._mem); + + if (p_value.type != Variant::VECTOR4) { + return; + } + + if (*str == "x") { + valid = true; + v->matrix[0] = p_value; + return; + } else if (*str == "y") { + valid = true; + v->matrix[1] = p_value; + return; + } else if (*str == "z") { + valid = true; + v->matrix[2] = p_value; + return; + } else if (*str == "w") { + valid = true; + v->matrix[3] = p_value; + return; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + Projection *v = _data._projection; + + if (p_value.type != Variant::VECTOR4) { + return; + } + + if (p_index == CoreStringNames::singleton->x) { + valid = true; + v->matrix[0] = p_value; + return; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + v->matrix[1] = p_value; + return; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + v->matrix[2] = p_value; + return; + } else if (p_index == CoreStringNames::singleton->w) { + valid = true; + v->matrix[3] = p_value; + return; + } + } + + } break; + case COLOR: { + if (p_value.type != Variant::INT && p_value.type != Variant::REAL) { + return; + } + + if (p_index.get_type() == Variant::STRING) { + const String *str = reinterpret_cast(p_index._data._mem); + Color *v = reinterpret_cast(_data._mem); + if (*str == "r") { + valid = true; + v->r = p_value; + return; + } else if (*str == "g") { + valid = true; + v->g = p_value; + return; + } else if (*str == "b") { + valid = true; + v->b = p_value; + return; + } else if (*str == "a") { + valid = true; + v->a = p_value; + return; + } else if (*str == "h") { + valid = true; + v->set_hsv(p_value, v->get_s(), v->get_v(), v->a); + return; + } else if (*str == "s") { + valid = true; + v->set_hsv(v->get_h(), p_value, v->get_v(), v->a); + return; + } else if (*str == "v") { + valid = true; + v->set_hsv(v->get_h(), v->get_s(), p_value, v->a); + return; + } else if (*str == "r8") { + valid = true; + v->r = float(p_value) / 255.0; + return; + } else if (*str == "g8") { + valid = true; + v->g = float(p_value) / 255.0; + return; + } else if (*str == "b8") { + valid = true; + v->b = float(p_value) / 255.0; + return; + } else if (*str == "a8") { + valid = true; + v->a = float(p_value) / 255.0; + return; + } + } else if (p_index.get_type() == Variant::INT) { + int idx = p_index; + if (idx < 0) { + idx += 4; + } + if (idx >= 0 && idx < 4) { + Color *v = reinterpret_cast(_data._mem); + (*v)[idx] = p_value; + valid = true; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + Color *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->r) { + valid = true; + v->r = p_value; + return; + } else if (p_index == CoreStringNames::singleton->g) { + valid = true; + v->g = p_value; + return; + } else if (p_index == CoreStringNames::singleton->b) { + valid = true; + v->b = p_value; + return; + } else if (p_index == CoreStringNames::singleton->a) { + valid = true; + v->a = p_value; + return; + } else if (p_index == CoreStringNames::singleton->h) { + valid = true; + v->set_hsv(p_value, v->get_s(), v->get_v(), v->a); + return; + } else if (p_index == CoreStringNames::singleton->s) { + valid = true; + v->set_hsv(v->get_h(), p_value, v->get_v(), v->a); + return; + } else if (p_index == CoreStringNames::singleton->v) { + valid = true; + v->set_hsv(v->get_h(), v->get_s(), p_value, v->a); + return; + } else if (p_index == CoreStringNames::singleton->r8) { + valid = true; + v->r = float(p_value) / 255.0; + return; + } else if (p_index == CoreStringNames::singleton->g8) { + valid = true; + v->g = float(p_value) / 255.0; + return; + } else if (p_index == CoreStringNames::singleton->b8) { + valid = true; + v->b = float(p_value) / 255.0; + return; + } else if (p_index == CoreStringNames::singleton->a8) { + valid = true; + v->a = float(p_value) / 255.0; + return; + } + } + + } break; + case OBJECT: { + Object *obj = _OBJ_PTR(*this); + if (unlikely(!obj)) { + valid = false; +#ifdef DEBUG_ENABLED + if (_get_obj().rc) { + ERR_PRINT("Attempted set on a deleted object."); + } +#endif + return; + } + + obj->set(p_index, p_value, r_valid); + return; + } break; + case STRING_NAME: { + } break; + case DICTIONARY: { + Dictionary *dic = reinterpret_cast(_data._mem); + dic->operator[](p_index) = p_value; + valid = true; //always valid, i guess? should this really be ok? + return; + } break; + // clang-format 14 wants to add a space after the last return, + // and clang-format 15 removes it... + /* clang-format off */ + DEFAULT_OP_ARRAY_CMD(ARRAY, Array, ;, (*arr)[index] = p_value; return) // 20 + /* clang-format on */ + DEFAULT_OP_DVECTOR_SET(POOL_BYTE_ARRAY, uint8_t, p_value.type != Variant::REAL && p_value.type != Variant::INT) + DEFAULT_OP_DVECTOR_SET(POOL_INT_ARRAY, int, p_value.type != Variant::REAL && p_value.type != Variant::INT) + DEFAULT_OP_DVECTOR_SET(POOL_REAL_ARRAY, real_t, p_value.type != Variant::REAL && p_value.type != Variant::INT) + DEFAULT_OP_DVECTOR_SET(POOL_STRING_ARRAY, String, p_value.type != Variant::STRING) + DEFAULT_OP_DVECTOR_SET(POOL_VECTOR2_ARRAY, Vector2, p_value.type != Variant::VECTOR2) // 25 + DEFAULT_OP_DVECTOR_SET(POOL_VECTOR3_ARRAY, Vector3, p_value.type != Variant::VECTOR3) + DEFAULT_OP_DVECTOR_SET(POOL_COLOR_ARRAY, Color, p_value.type != Variant::COLOR) + default: + return; + } +} + +Variant Variant::get(const Variant &p_index, bool *r_valid) const { + static bool _dummy = false; + + bool &valid = r_valid ? *r_valid : _dummy; + + valid = false; + + switch (type) { + case NIL: { + return Variant(); + } break; + case BOOL: { + return Variant(); + } break; + case INT: { + return Variant(); + } break; + case REAL: { + return Variant(); + } break; + case STRING: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + //string index + + int idx = p_index; + const String *str = reinterpret_cast(_data._mem); + if (idx < 0) { + idx += str->length(); + } + if (idx >= 0 && idx < str->length()) { + valid = true; + return str->substr(idx, 1); + } + } + + } break; + case RECT2: { + if (p_index.get_type() == Variant::STRING) { + //scalar name + + const String *str = reinterpret_cast(p_index._data._mem); + const Rect2 *v = reinterpret_cast(_data._mem); + if (*str == "position") { + valid = true; + return v->position; + } else if (*str == "size") { + valid = true; + return v->size; + } else if (*str == "end") { + valid = true; + return v->size + v->position; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + const Rect2 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->position) { + valid = true; + return v->position; + } else if (p_index == CoreStringNames::singleton->size) { + valid = true; + return v->size; + } else if (p_index == CoreStringNames::singleton->end) { + valid = true; + return v->size + v->position; + } + } + } break; + case RECT2I: { + if (p_index.get_type() == Variant::STRING) { + //scalar name + + const String *str = reinterpret_cast(p_index._data._mem); + const Rect2i *v = reinterpret_cast(_data._mem); + if (*str == "position") { + valid = true; + return v->position; + } else if (*str == "size") { + valid = true; + return v->size; + } else if (*str == "end") { + valid = true; + return v->size + v->position; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + const Rect2i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->position) { + valid = true; + return v->position; + } else if (p_index == CoreStringNames::singleton->size) { + valid = true; + return v->size; + } else if (p_index == CoreStringNames::singleton->end) { + valid = true; + return v->size + v->position; + } + } + } break; + case VECTOR2: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + // scalar index + int idx = p_index; + if (idx < 0) { + idx += 2; + } + if (idx >= 0 && idx < 2) { + const Vector2 *v = reinterpret_cast(_data._mem); + valid = true; + return (*v)[idx]; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + + const String *str = reinterpret_cast(p_index._data._mem); + const Vector2 *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + return v->x; + } else if (*str == "y") { + valid = true; + return v->y; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + const Vector2 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + return v->y; + } + } + + } break; // 5 + case VECTOR2I: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + // scalar index + int idx = p_index; + if (idx < 0) { + idx += 2; + } + if (idx >= 0 && idx < 2) { + const Vector2i *v = reinterpret_cast(_data._mem); + valid = true; + return (*v)[idx]; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + + const String *str = reinterpret_cast(p_index._data._mem); + const Vector2i *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + return v->x; + } else if (*str == "y") { + valid = true; + return v->y; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + const Vector2i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + return v->y; + } + } + + } break; // 6 + case VECTOR3: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + //scalar index + int idx = p_index; + if (idx < 0) { + idx += 3; + } + if (idx >= 0 && idx < 3) { + const Vector3 *v = reinterpret_cast(_data._mem); + valid = true; + return (*v)[idx]; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + const String *str = reinterpret_cast(p_index._data._mem); + const Vector3 *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + return v->x; + } else if (*str == "y") { + valid = true; + return v->y; + } else if (*str == "z") { + valid = true; + return v->z; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + const Vector3 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + return v->y; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + return v->z; + } + } + + } break; + case VECTOR3I: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + //scalar index + int idx = p_index; + if (idx < 0) { + idx += 3; + } + if (idx >= 0 && idx < 3) { + const Vector3i *v = reinterpret_cast(_data._mem); + valid = true; + return (*v)[idx]; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + const String *str = reinterpret_cast(p_index._data._mem); + const Vector3i *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + return v->x; + } else if (*str == "y") { + valid = true; + return v->y; + } else if (*str == "z") { + valid = true; + return v->z; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + const Vector3i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + return v->y; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + return v->z; + } + } + + } break; + case VECTOR4: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + //scalar index + int idx = p_index; + if (idx < 0) { + idx += 4; + } + if (idx >= 0 && idx < 4) { + const Vector4 *v = reinterpret_cast(_data._mem); + valid = true; + return (*v)[idx]; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + const String *str = reinterpret_cast(p_index._data._mem); + const Vector4 *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + return v->x; + } else if (*str == "y") { + valid = true; + return v->y; + } else if (*str == "z") { + valid = true; + return v->z; + } else if (*str == "w") { + valid = true; + return v->w; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + const Vector4 *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + return v->y; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + return v->z; + } else if (p_index == CoreStringNames::singleton->w) { + valid = true; + return v->w; + } + } + + } break; + case VECTOR4I: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + //scalar index + int idx = p_index; + if (idx < 0) { + idx += 4; + } + if (idx >= 0 && idx < 4) { + const Vector4i *v = reinterpret_cast(_data._mem); + valid = true; + return (*v)[idx]; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + const String *str = reinterpret_cast(p_index._data._mem); + const Vector4i *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + return v->x; + } else if (*str == "y") { + valid = true; + return v->y; + } else if (*str == "z") { + valid = true; + return v->z; + } else if (*str == "w") { + valid = true; + return v->w; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + const Vector4i *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + return v->y; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + return v->z; + } else if (p_index == CoreStringNames::singleton->w) { + valid = true; + return v->w; + } + } + + } break; + case PLANE: { + if (p_index.get_type() == Variant::STRING) { + //scalar name + const String *str = reinterpret_cast(p_index._data._mem); + const Plane *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + return v->normal.x; + } else if (*str == "y") { + valid = true; + return v->normal.y; + } else if (*str == "z") { + valid = true; + return v->normal.z; + } else if (*str == "normal") { + valid = true; + return v->normal; + } else if (*str == "d") { + valid = true; + return v->d; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + const Plane *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + return v->normal.x; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + return v->normal.y; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + return v->normal.z; + } else if (p_index == CoreStringNames::singleton->normal) { + valid = true; + return v->normal; + } else if (p_index == CoreStringNames::singleton->d) { + valid = true; + return v->d; + } + } + + } break; + case QUATERNION: { + if (p_index.get_type() == Variant::STRING) { + const String *str = reinterpret_cast(p_index._data._mem); + const Quaternion *v = reinterpret_cast(_data._mem); + if (*str == "x") { + valid = true; + return v->x; + } else if (*str == "y") { + valid = true; + return v->y; + } else if (*str == "z") { + valid = true; + return v->z; + } else if (*str == "w") { + valid = true; + return v->w; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + const Quaternion *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->x) { + valid = true; + return v->x; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + return v->y; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + return v->z; + } else if (p_index == CoreStringNames::singleton->w) { + valid = true; + return v->w; + } + } + + } break; // 10 + case AABB: { + if (p_index.get_type() == Variant::STRING) { + //scalar name + + const String *str = reinterpret_cast(p_index._data._mem); + const ::AABB *v = _data._aabb; + if (*str == "position") { + valid = true; + return v->position; + } else if (*str == "size") { + valid = true; + return v->size; + } else if (*str == "end") { + valid = true; + return v->size + v->position; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + const ::AABB *v = _data._aabb; + if (p_index == CoreStringNames::singleton->position) { + valid = true; + return v->position; + } else if (p_index == CoreStringNames::singleton->size) { + valid = true; + return v->size; + } else if (p_index == CoreStringNames::singleton->end) { + valid = true; + return v->size + v->position; + } + } + } break; + case BASIS: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + int index = p_index; + if (index < 0) { + index += 3; + } + if (index >= 0 && index < 3) { + const Basis *v = _data._basis; + + valid = true; + return v->get_axis(index); + } + } else if (p_index.get_type() == Variant::STRING) { + const String *str = reinterpret_cast(p_index._data._mem); + const Basis *v = _data._basis; + + if (*str == "x") { + valid = true; + return v->get_axis(0); + } else if (*str == "y") { + valid = true; + return v->get_axis(1); + } else if (*str == "z") { + valid = true; + return v->get_axis(2); + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + const Basis *v = _data._basis; + + if (p_index == CoreStringNames::singleton->x) { + valid = true; + return v->get_axis(0); + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + return v->get_axis(1); + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + return v->get_axis(2); + } + } + + } break; + case TRANSFORM: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + int index = p_index; + if (index < 0) { + index += 4; + } + if (index >= 0 && index < 4) { + const Transform *v = _data._transform; + valid = true; + return index == 3 ? v->origin : v->basis.get_axis(index); + } + } else if (p_index.get_type() == Variant::STRING) { + const Transform *v = _data._transform; + const String *str = reinterpret_cast(p_index._data._mem); + + if (*str == "basis") { + valid = true; + return v->basis; + } + if (*str == "origin") { + valid = true; + return v->origin; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + const Transform *v = _data._transform; + + if (p_index == CoreStringNames::singleton->basis) { + valid = true; + return v->basis; + } + if (p_index == CoreStringNames::singleton->origin) { + valid = true; + return v->origin; + } + } + + } break; + case TRANSFORM2D: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + int index = p_index; + + if (index < 0) { + index += 3; + } + if (index >= 0 && index < 3) { + const Transform2D *v = _data._transform2d; + + valid = true; + return v->columns[index]; + } + } else if (p_index.get_type() == Variant::STRING) { + //scalar name + const String *str = reinterpret_cast(p_index._data._mem); + const Transform2D *v = _data._transform2d; + if (*str == "x") { + valid = true; + return v->columns[0]; + } else if (*str == "y") { + valid = true; + return v->columns[1]; + } else if (*str == "origin") { + valid = true; + return v->columns[2]; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + //scalar name + + const Transform2D *v = _data._transform2d; + if (p_index == CoreStringNames::singleton->x) { + valid = true; + return v->columns[0]; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + return v->columns[1]; + } else if (p_index == CoreStringNames::singleton->origin) { + valid = true; + return v->columns[2]; + } + } + + } break; + case PROJECTION: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + int index = p_index; + if (index < 0) { + index += 4; + } + if (index >= 0 && index < 4) { + const Projection *v = _data._projection; + valid = true; + return v->matrix[index]; + } + } else if (p_index.get_type() == Variant::STRING) { + const Projection *v = _data._projection; + const String *str = reinterpret_cast(p_index._data._mem); + + if (*str == "x") { + valid = true; + return v->matrix[0]; + } else if (*str == "y") { + valid = true; + return v->matrix[1]; + } else if (*str == "z") { + valid = true; + return v->matrix[2]; + } else if (*str == "w") { + valid = true; + return v->matrix[3]; + } + } else if (p_index.get_type() == Variant::STRING_NAME) { + const Projection *v = _data._projection; + + if (p_index == CoreStringNames::singleton->x) { + valid = true; + return v->matrix[0]; + } else if (p_index == CoreStringNames::singleton->y) { + valid = true; + return v->matrix[1]; + } else if (p_index == CoreStringNames::singleton->z) { + valid = true; + return v->matrix[2]; + } else if (p_index == CoreStringNames::singleton->w) { + valid = true; + return v->matrix[3]; + } + } + + } break; + case COLOR: { + if (p_index.get_type() == Variant::STRING) { + const String *str = reinterpret_cast(p_index._data._mem); + const Color *v = reinterpret_cast(_data._mem); + if (*str == "r") { + valid = true; + return v->r; + } else if (*str == "g") { + valid = true; + return v->g; + } else if (*str == "b") { + valid = true; + return v->b; + } else if (*str == "a") { + valid = true; + return v->a; + } else if (*str == "h") { + valid = true; + return v->get_h(); + } else if (*str == "s") { + valid = true; + return v->get_s(); + } else if (*str == "v") { + valid = true; + return v->get_v(); + } else if (*str == "r8") { + valid = true; + return (int)Math::round(v->r * 255.0); + } else if (*str == "g8") { + valid = true; + return (int)Math::round(v->g * 255.0); + } else if (*str == "b8") { + valid = true; + return (int)Math::round(v->b * 255.0); + } else if (*str == "a8") { + valid = true; + return (int)Math::round(v->a * 255.0); + } + } else if (p_index.get_type() == Variant::INT) { + int idx = p_index; + if (idx < 0) { + idx += 4; + } + if (idx >= 0 && idx < 4) { + const Color *v = reinterpret_cast(_data._mem); + valid = true; + return (*v)[idx]; + } + } else if (p_index.get_type() == Variant::STRING) { + const Color *v = reinterpret_cast(_data._mem); + if (p_index == CoreStringNames::singleton->r) { + valid = true; + return v->r; + } else if (p_index == CoreStringNames::singleton->g) { + valid = true; + return v->g; + } else if (p_index == CoreStringNames::singleton->b) { + valid = true; + return v->b; + } else if (p_index == CoreStringNames::singleton->a) { + valid = true; + return v->a; + } else if (p_index == CoreStringNames::singleton->h) { + valid = true; + return v->get_h(); + } else if (p_index == CoreStringNames::singleton->s) { + valid = true; + return v->get_s(); + } else if (p_index == CoreStringNames::singleton->v) { + valid = true; + return v->get_v(); + } else if (p_index == CoreStringNames::singleton->r8) { + valid = true; + return (int)Math::round(v->r * 255.0); + } else if (p_index == CoreStringNames::singleton->g8) { + valid = true; + return (int)Math::round(v->g * 255.0); + } else if (p_index == CoreStringNames::singleton->b8) { + valid = true; + return (int)Math::round(v->b * 255.0); + } else if (p_index == CoreStringNames::singleton->a8) { + valid = true; + return (int)Math::round(v->a * 255.0); + } + } + + } break; + case OBJECT: { + Object *obj = _OBJ_PTR(*this); + if (unlikely(!obj)) { + valid = false; +#ifdef DEBUG_ENABLED + if (_get_obj().rc) { + ERR_PRINT("Attempted get on a deleted object."); + } +#endif + return Variant(); + } + + return obj->get(p_index, r_valid); + + } break; + case STRING_NAME: { + } break; + case DICTIONARY: { + const Dictionary *dic = reinterpret_cast(_data._mem); + const Variant *res = dic->getptr(p_index); + if (res) { + valid = true; + return *res; + } + } break; + DEFAULT_OP_ARRAY_CMD(ARRAY, const Array, ;, return (*arr)[index]) // 20 + DEFAULT_OP_DVECTOR_GET(POOL_BYTE_ARRAY, uint8_t) + DEFAULT_OP_DVECTOR_GET(POOL_INT_ARRAY, int) + DEFAULT_OP_DVECTOR_GET(POOL_REAL_ARRAY, real_t) + DEFAULT_OP_DVECTOR_GET(POOL_STRING_ARRAY, String) + DEFAULT_OP_DVECTOR_GET(POOL_VECTOR2_ARRAY, Vector2) + DEFAULT_OP_DVECTOR_GET(POOL_VECTOR2I_ARRAY, Vector2i) + DEFAULT_OP_DVECTOR_GET(POOL_VECTOR3_ARRAY, Vector3) + DEFAULT_OP_DVECTOR_GET(POOL_VECTOR3I_ARRAY, Vector3i) + DEFAULT_OP_DVECTOR_GET(POOL_VECTOR4_ARRAY, Vector4) + DEFAULT_OP_DVECTOR_GET(POOL_VECTOR4I_ARRAY, Vector4i) + DEFAULT_OP_DVECTOR_GET(POOL_COLOR_ARRAY, Color) + default: + return Variant(); + } + + return Variant(); +} + +bool Variant::in(const Variant &p_index, bool *r_valid) const { + if (r_valid) { + *r_valid = true; + } + + switch (type) { + case STRING: { + if (p_index.get_type() == Variant::STRING) { + //string index + String idx = p_index; + const String *str = reinterpret_cast(_data._mem); + + return str->find(idx) != -1; + } + + } break; + case OBJECT: { + Object *obj = _OBJ_PTR(*this); + if (unlikely(!obj)) { + if (r_valid) { + *r_valid = false; + } +#ifdef DEBUG_ENABLED + if (_get_obj().rc) { + ERR_PRINT("Attempted 'in' on a deleted object."); + } +#endif + return false; + } + + bool result; + obj->get(p_index, &result); + return result; + } break; + case DICTIONARY: { + const Dictionary *dic = reinterpret_cast(_data._mem); + return dic->has(p_index); + + } break; // 20 + case ARRAY: { + const Array *arr = reinterpret_cast(_data._mem); + int l = arr->size(); + if (l) { + for (int i = 0; i < l; i++) { + if (evaluate(OP_EQUAL, (*arr)[i], p_index)) { + return true; + } + } + } + + return false; + + } break; + case POOL_BYTE_ARRAY: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + int index = p_index; + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int l = arr->size(); + if (l) { + PoolVector::Read r = arr->read(); + for (int i = 0; i < l; i++) { + if (r[i] == index) { + return true; + } + } + } + + return false; + } + + } break; + case POOL_INT_ARRAY: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + int index = p_index; + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int l = arr->size(); + if (l) { + PoolVector::Read r = arr->read(); + for (int i = 0; i < l; i++) { + if (r[i] == index) { + return true; + } + } + } + + return false; + } + } break; + case POOL_REAL_ARRAY: { + if (p_index.get_type() == Variant::INT || p_index.get_type() == Variant::REAL) { + real_t index = p_index; + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int l = arr->size(); + if (l) { + PoolVector::Read r = arr->read(); + for (int i = 0; i < l; i++) { + if (r[i] == index) { + return true; + } + } + } + + return false; + } + + } break; + case POOL_STRING_ARRAY: { + if (p_index.get_type() == Variant::STRING) { + String index = p_index; + const PoolVector *arr = reinterpret_cast *>(_data._mem); + + int l = arr->size(); + if (l) { + PoolVector::Read r = arr->read(); + for (int i = 0; i < l; i++) { + if (r[i] == index) { + return true; + } + } + } + + return false; + } + + } break; //25 + case POOL_VECTOR2_ARRAY: { + if (p_index.get_type() == Variant::VECTOR2) { + Vector2 index = p_index; + const PoolVector *arr = reinterpret_cast *>(_data._mem); + + int l = arr->size(); + if (l) { + PoolVector::Read r = arr->read(); + for (int i = 0; i < l; i++) { + if (r[i] == index) { + return true; + } + } + } + + return false; + } + + } break; + case POOL_VECTOR2I_ARRAY: { + if (p_index.get_type() == Variant::VECTOR2I) { + Vector2i index = p_index; + const PoolVector *arr = reinterpret_cast *>(_data._mem); + + int l = arr->size(); + if (l) { + PoolVector::Read r = arr->read(); + for (int i = 0; i < l; i++) { + if (r[i] == index) { + return true; + } + } + } + + return false; + } + + } break; + case POOL_VECTOR3_ARRAY: { + if (p_index.get_type() == Variant::VECTOR3) { + Vector3 index = p_index; + const PoolVector *arr = reinterpret_cast *>(_data._mem); + + int l = arr->size(); + if (l) { + PoolVector::Read r = arr->read(); + for (int i = 0; i < l; i++) { + if (r[i] == index) { + return true; + } + } + } + + return false; + } + + } break; + case POOL_VECTOR3I_ARRAY: { + if (p_index.get_type() == Variant::VECTOR3I) { + Vector3i index = p_index; + const PoolVector *arr = reinterpret_cast *>(_data._mem); + + int l = arr->size(); + if (l) { + PoolVector::Read r = arr->read(); + for (int i = 0; i < l; i++) { + if (r[i] == index) { + return true; + } + } + } + + return false; + } + + } break; + case POOL_VECTOR4_ARRAY: { + if (p_index.get_type() == Variant::VECTOR4) { + Vector4 index = p_index; + const PoolVector *arr = reinterpret_cast *>(_data._mem); + + int l = arr->size(); + if (l) { + PoolVector::Read r = arr->read(); + for (int i = 0; i < l; i++) { + if (r[i] == index) { + return true; + } + } + } + + return false; + } + + } break; + case POOL_VECTOR4I_ARRAY: { + if (p_index.get_type() == Variant::VECTOR4I) { + Vector4i index = p_index; + const PoolVector *arr = reinterpret_cast *>(_data._mem); + + int l = arr->size(); + if (l) { + PoolVector::Read r = arr->read(); + for (int i = 0; i < l; i++) { + if (r[i] == index) { + return true; + } + } + } + + return false; + } + + } break; + case POOL_COLOR_ARRAY: { + if (p_index.get_type() == Variant::COLOR) { + Color index = p_index; + const PoolVector *arr = reinterpret_cast *>(_data._mem); + + int l = arr->size(); + if (l) { + PoolVector::Read r = arr->read(); + for (int i = 0; i < l; i++) { + if (r[i] == index) { + return true; + } + } + } + + return false; + } + } break; + default: { + } + } + + if (r_valid) { + *r_valid = false; + } + return false; +} + +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 REAL: { + r_iter = 0; + return _data._real > 0.0; + } break; + case VECTOR2: { + int64_t from = reinterpret_cast(_data._mem)->x; + int64_t 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: { + 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; + } else { + return step < 0; + } + //return true; + } 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; + } else { + return step < 0; + } + //return true; + } break; + case STRING: { + const String *str = reinterpret_cast(_data._mem); + if (str->empty()) { + return false; + } + r_iter = 0; + return true; + } break; + case DICTIONARY: { + const Dictionary *dic = reinterpret_cast(_data._mem); + if (dic->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->empty()) { + return false; + } + r_iter = 0; + return true; + } break; + case POOL_BYTE_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + + } break; + case POOL_INT_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + + } break; + case POOL_REAL_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + + } break; + case POOL_STRING_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + } break; + case POOL_VECTOR2_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + } break; + case POOL_VECTOR2I_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + } break; + case POOL_VECTOR3_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + } break; + case POOL_VECTOR3I_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + } break; + case POOL_VECTOR4_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + } break; + case POOL_VECTOR4I_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + if (arr->size() == 0) { + return false; + } + r_iter = 0; + return true; + } break; + case POOL_COLOR_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + 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 REAL: { + int64_t idx = r_iter; + idx++; + if (idx >= _data._real) { + return false; + } + r_iter = idx; + return true; + } break; + case VECTOR2: { + 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 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: { + 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 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 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 POOL_BYTE_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + + } break; + case POOL_INT_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + + } break; + case POOL_REAL_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + + } break; + case POOL_STRING_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + } break; + case POOL_VECTOR2_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + } break; + case POOL_VECTOR2I_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + } break; + case POOL_VECTOR3_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + } break; + case POOL_VECTOR3I_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + } break; + case POOL_VECTOR4_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + } break; + case POOL_VECTOR4I_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= arr->size()) { + return false; + } + r_iter = idx; + return true; + } break; + case POOL_COLOR_ARRAY: { + const PoolVector *arr = reinterpret_cast *>(_data._mem); + 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 REAL: { + 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 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 POOL_BYTE_ARRAY: { + const PoolVector *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 POOL_INT_ARRAY: { + const PoolVector *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 POOL_REAL_ARRAY: { + const PoolVector *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 POOL_STRING_ARRAY: { + const PoolVector *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 POOL_VECTOR2_ARRAY: { + const PoolVector *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 POOL_VECTOR2I_ARRAY: { + const PoolVector *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 POOL_VECTOR3_ARRAY: { + const PoolVector *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 POOL_VECTOR3I_ARRAY: { + const PoolVector *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 POOL_VECTOR4_ARRAY: { + const PoolVector *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 POOL_VECTOR4I_ARRAY: { + const PoolVector *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 POOL_COLOR_ARRAY: { + const PoolVector *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; + default: { + } + } + + r_valid = false; + return Variant(); +} + +Variant Variant::duplicate(bool deep) const { + switch (type) { + case OBJECT: { + /* breaks stuff :( + if (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().duplicate(deep); + case ARRAY: + return operator Array().duplicate(deep); + 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 REAL: { + real_t ra = a._data._real; + real_t rb = b._data._real; + r_dst = ra - rb; + } + 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 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 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 VECTOR4: { + r_dst = *reinterpret_cast(a._data._mem) - *reinterpret_cast(b._data._mem); + } + return; + case VECTOR4I: { + 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 vaw = reinterpret_cast(a._data._mem)->w; + 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; + int32_t vbw = reinterpret_cast(b._data._mem)->w; + r_dst = Vector4i(int32_t(vax - vbx), int32_t(vay - vby), int32_t(vaz - vbz), int32_t(vaw - vbw)); + } + 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 REAL: { + double ra = a._data._real; + double rb = b._data._real; + r_dst = ra + rb * c; + } + 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); + r_dst = Rect2(ra->position + rb->position * c, ra->size + rb->size * c); + } + return; + case VECTOR2: { + r_dst = *reinterpret_cast(a._data._mem) + *reinterpret_cast(b._data._mem) * c; + } + return; + case VECTOR2I: { + r_dst = *reinterpret_cast(a._data._mem) + *reinterpret_cast(b._data._mem) * c; + } + return; + case VECTOR3: { + r_dst = *reinterpret_cast(a._data._mem) + *reinterpret_cast(b._data._mem) * c; + } + return; + case VECTOR3I: { + r_dst = *reinterpret_cast(a._data._mem) + *reinterpret_cast(b._data._mem) * c; + } + return; + case VECTOR4: { + r_dst = *reinterpret_cast(a._data._mem) + *reinterpret_cast(b._data._mem) * c; + } + return; + case VECTOR4I: { + r_dst = *reinterpret_cast(a._data._mem) + *reinterpret_cast(b._data._mem) * c; + } + 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 REAL: { + real_t va = a._data._real; + real_t vb = b._data._real; + r_dst = va + (vb - va) * c; + } + return; + case STRING: { + //this is pretty funny and bizarre, but artists like to use it for typewritter 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++) { + CharType 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 RECT2: { + r_dst = Rect2(reinterpret_cast(a._data._mem)->position.linear_interpolate(reinterpret_cast(b._data._mem)->position, c), reinterpret_cast(a._data._mem)->size.linear_interpolate(reinterpret_cast(b._data._mem)->size, c)); + } + return; + case RECT2I: { + r_dst = Rect2(reinterpret_cast(a._data._mem)->position.linear_interpolate(reinterpret_cast(b._data._mem)->position, c), reinterpret_cast(a._data._mem)->size.linear_interpolate(reinterpret_cast(b._data._mem)->size, c)); + } + return; + case VECTOR2: { + r_dst = reinterpret_cast(a._data._mem)->linear_interpolate(*reinterpret_cast(b._data._mem), c); + } + return; + case VECTOR2I: { + r_dst = reinterpret_cast(a._data._mem)->linear_interpolate(*reinterpret_cast(b._data._mem), c); + } + return; + case VECTOR3: { + r_dst = reinterpret_cast(a._data._mem)->linear_interpolate(*reinterpret_cast(b._data._mem), c); + } + return; + case VECTOR3I: { + r_dst = reinterpret_cast(a._data._mem)->linear_interpolate(*reinterpret_cast(b._data._mem), c); + } + return; + case VECTOR4: { + r_dst = reinterpret_cast(a._data._mem)->linear_interpolate(*reinterpret_cast(b._data._mem), c); + } + return; + case VECTOR4I: { + r_dst = reinterpret_cast(a._data._mem)->linear_interpolate(*reinterpret_cast(b._data._mem), 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.linear_interpolate(b._data._aabb->position, c), a._data._aabb->size.linear_interpolate(b._data._aabb->size, c)); + } + return; + case BASIS: { + r_dst = Transform(*a._data._basis).interpolate_with(Transform(*b._data._basis), c).basis; + } + return; + case TRANSFORM: { + r_dst = a._data._transform->interpolate_with(*b._data._transform, c); + } + return; + case TRANSFORM2D: { + r_dst = a._data._transform2d->interpolate_with(*b._data._transform2d, c); + } + return; + case COLOR: { + r_dst = reinterpret_cast(a._data._mem)->linear_interpolate(*reinterpret_cast(b._data._mem), c); + } + return; + case OBJECT: { + r_dst = a; + } + return; + case STRING_NAME: { + r_dst = a; + } + return; + case DICTIONARY: { + } + return; + case ARRAY: { + r_dst = a; + } + return; + case POOL_BYTE_ARRAY: { + r_dst = a; + } + return; + case POOL_INT_ARRAY: { + const PoolVector *arr_a = reinterpret_cast *>(a._data._mem); + const PoolVector *arr_b = reinterpret_cast *>(b._data._mem); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + PoolVector v; + v.resize(sz); + { + PoolVector::Write vw = v.write(); + PoolVector::Read ar = arr_a->read(); + PoolVector::Read br = arr_b->read(); + + 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 POOL_REAL_ARRAY: { + const PoolVector *arr_a = reinterpret_cast *>(a._data._mem); + const PoolVector *arr_b = reinterpret_cast *>(b._data._mem); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + PoolVector v; + v.resize(sz); + { + PoolVector::Write vw = v.write(); + PoolVector::Read ar = arr_a->read(); + PoolVector::Read br = arr_b->read(); + + 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 POOL_STRING_ARRAY: { + r_dst = a; + } + return; + case POOL_VECTOR2_ARRAY: { + const PoolVector *arr_a = reinterpret_cast *>(a._data._mem); + const PoolVector *arr_b = reinterpret_cast *>(b._data._mem); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + PoolVector v; + v.resize(sz); + { + PoolVector::Write vw = v.write(); + PoolVector::Read ar = arr_a->read(); + PoolVector::Read br = arr_b->read(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].linear_interpolate(br[i], c); + } + } + r_dst = v; + } + } + return; + case POOL_VECTOR2I_ARRAY: { + const PoolVector *arr_a = reinterpret_cast *>(a._data._mem); + const PoolVector *arr_b = reinterpret_cast *>(b._data._mem); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + PoolVector v; + v.resize(sz); + { + PoolVector::Write vw = v.write(); + PoolVector::Read ar = arr_a->read(); + PoolVector::Read br = arr_b->read(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].linear_interpolate(br[i], c); + } + } + r_dst = v; + } + } + return; + case POOL_VECTOR3_ARRAY: { + const PoolVector *arr_a = reinterpret_cast *>(a._data._mem); + const PoolVector *arr_b = reinterpret_cast *>(b._data._mem); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + PoolVector v; + v.resize(sz); + { + PoolVector::Write vw = v.write(); + PoolVector::Read ar = arr_a->read(); + PoolVector::Read br = arr_b->read(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].linear_interpolate(br[i], c); + } + } + r_dst = v; + } + } + return; + case POOL_VECTOR3I_ARRAY: { + const PoolVector *arr_a = reinterpret_cast *>(a._data._mem); + const PoolVector *arr_b = reinterpret_cast *>(b._data._mem); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + PoolVector v; + v.resize(sz); + { + PoolVector::Write vw = v.write(); + PoolVector::Read ar = arr_a->read(); + PoolVector::Read br = arr_b->read(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].linear_interpolate(br[i], c); + } + } + r_dst = v; + } + } + return; + case POOL_VECTOR4_ARRAY: { + const PoolVector *arr_a = reinterpret_cast *>(a._data._mem); + const PoolVector *arr_b = reinterpret_cast *>(b._data._mem); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + PoolVector v; + v.resize(sz); + { + PoolVector::Write vw = v.write(); + PoolVector::Read ar = arr_a->read(); + PoolVector::Read br = arr_b->read(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].linear_interpolate(br[i], c); + } + } + r_dst = v; + } + } + return; + case POOL_VECTOR4I_ARRAY: { + /* + const PoolVector *arr_a = reinterpret_cast *>(a._data._mem); + const PoolVector *arr_b = reinterpret_cast *>(b._data._mem); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + PoolVector v; + v.resize(sz); + { + PoolVector::Write vw = v.write(); + PoolVector::Read ar = arr_a->read(); + PoolVector::Read br = arr_b->read(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].lerp(br[i], c); + } + } + r_dst = v; + } + */ + r_dst = a; + } + return; + case POOL_COLOR_ARRAY: { + const PoolVector *arr_a = reinterpret_cast *>(a._data._mem); + const PoolVector *arr_b = reinterpret_cast *>(b._data._mem); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + r_dst = a; + } else { + PoolVector v; + v.resize(sz); + { + PoolVector::Write vw = v.write(); + PoolVector::Read ar = arr_a->read(); + PoolVector::Read br = arr_b->read(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].linear_interpolate(br[i], c); + } + } + r_dst = v; + } + } + return; + default: { + r_dst = a; + } + } +} + +static const char *_op_names[Variant::OP_MAX] = { + "==", + "!=", + "<", + "<=", + ">", + ">=", + "+", + "-", + "*", + "/", + "- (negation)", + "+ (positive)", + "%", + "+ (concatenation)", + "<<", + ">>", + "&", + "|", + "^", + "~", + "and", + "or", + "xor", + "not", + "in" + +}; + +String Variant::get_operator_name(Operator p_op) { + ERR_FAIL_INDEX_V(p_op, OP_MAX, ""); + return _op_names[p_op]; +} + +#undef CASE_TYPE_ALL +#undef CASE_TYPE_ALL_BUT_INT +#undef TYPE +#undef TYPES +#undef CASES +#undef SWITCH +#undef SWITCH_OP +#undef CASE_TYPE +#undef CASES +#undef SWITCH +#undef SWITCH_OP +#undef CASE_TYPE +#undef _RETURN +#undef _RETURN_FAIL +#undef DEFAULT_OP_NUM +#undef DEFAULT_OP_NUM_NULL +#undef DEFAULT_OP_NUM_DIV +#undef DEFAULT_OP_NUM_DIV +#undef DEFAULT_OP_NUM_NEG +#undef DEFAULT_OP_NUM_POS +#undef DEFAULT_OP_NUM_VEC +#undef DEFAULT_OP_STR_REV +#undef DEFAULT_OP_STR +#undef DEFAULT_OP_STR_NULL +#undef DEFAULT_OP_STR_NULL_NP +#undef DEFAULT_OP_STR_NULL_SN +#undef DEFAULT_OP_LOCALMEM_REV +#undef DEFAULT_OP_LOCALMEM +#undef DEFAULT_OP_LOCALMEM_NULL +#undef DEFAULT_OP_LOCALMEM_NEG +#undef DEFAULT_OP_LOCALMEM_POS +#undef DEFAULT_OP_LOCALMEM_NUM +#undef DEFAULT_OP_PTR +#undef DEFAULT_OP_PTRREF +#undef DEFAULT_OP_PTRREF_NULL +#undef DEFAULT_OP_ARRAY_EQ +#undef DEFAULT_OP_ARRAY_NEQ +#undef DEFAULT_OP_ARRAY_LT +#undef DEFAULT_OP_ARRAY_GT +#undef DEFAULT_OP_ARRAY_OP +#undef DEFAULT_OP_ARRAY_OP_BODY +#undef DEFAULT_OP_ARRAY_ADD +#undef DEFAULT_OP_ARRAY_CMD +#undef DEFAULT_OP_DVECTOR_SET +#undef DEFAULT_OP_DVECTOR_GET +#line 0 diff --git a/tools/doc/sfwl.h b/tools/doc/sfwl.h new file mode 100644 index 0000000..30e71ed --- /dev/null +++ b/tools/doc/sfwl.h @@ -0,0 +1,18718 @@ +#ifndef SFWL_H +#define SFWL_H + + +// https://github.com/Relintai/sfw + + +/* +Copyright (c) 2023-present Péter Magyar. + +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. +*/ + +// TODO gather all licenses to here + + +#define _REENTRANT + +#include +#include +#include +#include + +//=================== CORE SECTION =================== + + +#line 1 "sfwl/core/int_types.h" +/*************************************************************************/ +/* int_types.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#ifdef _MSC_VER + +typedef signed __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef signed __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef signed __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#else + +#ifdef NO_STDINT_H +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef unsigned short uint16_t; +typedef signed short int16_t; +typedef unsigned int uint32_t; +typedef signed int int32_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; +#else +#include +#endif + +#endif +#line 0 + +#line 1 "sfwl/core/math_defs.h" +#ifndef MATH_DEFS_H +#define MATH_DEFS_H + +/*************************************************************************/ +/* math_defs.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#define CMP_EPSILON 0.00001 +#define CMP_EPSILON2 (CMP_EPSILON * CMP_EPSILON) + +#define CMP_NORMALIZE_TOLERANCE 0.000001 +#define CMP_POINT_IN_PLANE_EPSILON 0.00001 + +#define Math_SQRT12 0.7071067811865475244008443621048490 +#define Math_SQRT2 1.4142135623730950488016887242 +#define Math_LN2 0.6931471805599453094172321215 +#define Math_TAU 6.2831853071795864769252867666 +#define Math_PI 3.1415926535897932384626433833 +#define Math_E 2.7182818284590452353602874714 +#define Math_INF INFINITY +#define Math_NAN NAN + +#ifdef DEBUG_ENABLED +#define MATH_CHECKS +#endif + +//this epsilon is for values related to a unit size (scalar or vector len) +#ifdef PRECISE_MATH_CHECKS +#define UNIT_EPSILON 0.00001 +#else +//tolerate some more floating point error normally +#define UNIT_EPSILON 0.001 +#endif + +#define USEC_TO_SEC(m_usec) ((m_usec) / 1000000.0) +#define SEC_TO_USEC(m_usec) ((m_usec) * 1000000.0) + +enum ClockDirection { + CLOCKWISE, + COUNTERCLOCKWISE +}; + +enum Orientation { + + HORIZONTAL, + VERTICAL +}; + +enum HAlign { + + HALIGN_LEFT, + HALIGN_CENTER, + HALIGN_RIGHT +}; + +enum VAlign { + + VALIGN_TOP, + VALIGN_CENTER, + VALIGN_BOTTOM +}; + +enum Margin { + + MARGIN_LEFT, + MARGIN_TOP, + MARGIN_RIGHT, + MARGIN_BOTTOM +}; + +enum Side { + SIDE_LEFT, + SIDE_TOP, + SIDE_RIGHT, + SIDE_BOTTOM +}; + +enum Corner { + + CORNER_TOP_LEFT, + CORNER_TOP_RIGHT, + CORNER_BOTTOM_RIGHT, + CORNER_BOTTOM_LEFT +}; + +/** + * The "Real" type is an abstract type used for real numbers, such as 1.5, + * in contrast to integer numbers. Precision can be controlled with the + * presence or absence of the REAL_T_IS_DOUBLE define. + */ +#ifdef REAL_T_IS_DOUBLE +typedef double real_t; +#else +typedef float real_t; +#endif + +#endif // MATH_DEFS_H +#line 0 + +#line 1 "sfwl/core/error_list.h" +/*************************************************************************/ +/* error_list.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#ifndef ERROR_LIST_H +#define ERROR_LIST_H + +/** Error List. Please never compare an error against FAILED + * Either do result != OK , or !result. This way, Error fail + * values can be more detailed in the future. + * + * This is a generic error list, mainly for organizing a language of returning errors. + */ + +enum Error { + OK, // (0) + FAILED, ///< Generic fail error + ERR_UNAVAILABLE, ///< What is requested is unsupported/unavailable + ERR_UNCONFIGURED, ///< The object being used hasn't been properly set up yet + ERR_UNAUTHORIZED, ///< Missing credentials for requested resource + ERR_PARAMETER_RANGE_ERROR, ///< Parameter given out of range (5) + ERR_OUT_OF_MEMORY, ///< Out of memory + ERR_FILE_NOT_FOUND, + ERR_FILE_BAD_DRIVE, + ERR_FILE_BAD_PATH, + ERR_FILE_NO_PERMISSION, // (10) + ERR_FILE_ALREADY_IN_USE, + ERR_FILE_CANT_OPEN, + ERR_FILE_CANT_WRITE, + ERR_FILE_CANT_READ, + ERR_FILE_UNRECOGNIZED, // (15) + ERR_FILE_CORRUPT, + ERR_FILE_MISSING_DEPENDENCIES, + ERR_FILE_EOF, + ERR_CANT_OPEN, ///< Can't open a resource/socket/file + ERR_CANT_CREATE, // (20) + ERR_QUERY_FAILED, + ERR_ALREADY_IN_USE, + ERR_LOCKED, ///< resource is locked + ERR_TIMEOUT, + ERR_CANT_CONNECT, // (25) + ERR_CANT_RESOLVE, + ERR_CONNECTION_ERROR, + ERR_CANT_ACQUIRE_RESOURCE, + ERR_CANT_FORK, + ERR_INVALID_DATA, ///< Data passed is invalid (30) + ERR_INVALID_PARAMETER, ///< Parameter passed is invalid + ERR_ALREADY_EXISTS, ///< When adding, item already exists + ERR_DOES_NOT_EXIST, ///< When retrieving/erasing, if item does not exist + ERR_DATABASE_CANT_READ, ///< database is full + ERR_DATABASE_CANT_WRITE, ///< database is full (35) + ERR_COMPILATION_FAILED, + ERR_METHOD_NOT_FOUND, + ERR_LINK_FAILED, + ERR_SCRIPT_FAILED, + ERR_CYCLIC_LINK, // (40) + ERR_INVALID_DECLARATION, + ERR_DUPLICATE_SYMBOL, + ERR_PARSE_ERROR, + ERR_BUSY, + ERR_SKIP, // (45) + ERR_HELP, ///< user requested help!! + ERR_BUG, ///< a bug in the software certainly happened, due to a double check failing or unexpected behavior. + ERR_PRINTER_ON_FIRE, /// the parallel port printer is engulfed in flames +}; + +#endif +#line 0 + +#line 1 "sfwl/core/logger.h" + +#ifndef LOGGER_H +#define LOGGER_H + + +class String; + +class RLogger { +public: + static void print_trace(const String &str); + static void print_trace(const char *str); + static void print_trace(const char *p_function, const char *p_file, int p_line, const String &str); + static void print_trace(const char *p_function, const char *p_file, int p_line, const char *str); + + static void print_message(const String &str); + static void print_message(const char *str); + static void print_message(const char *p_function, const char *p_file, int p_line, const String &str); + static void print_message(const char *p_function, const char *p_file, int p_line, const char *str); + + static void print_warning(const String &str); + static void print_warning(const char *str); + static void print_warning(const char *p_function, const char *p_file, int p_line, const String &str); + static void print_warning(const char *p_function, const char *p_file, int p_line, const char *str); + + static void print_error(const String &str); + static void print_error(const char *str); + static void print_error(const char *p_function, const char *p_file, int p_line, const char *str); + static void print_error(const char *p_function, const char *p_file, int p_line, const String &str); + static void print_msg_error(const char *p_function, const char *p_file, int p_line, const char *p_msg, const char *str); + static void print_index_error(const char *p_function, const char *p_file, int p_line, const int index, const int size, const char *str); + + static void log_trace(const String &str); + static void log_trace(const char *str); + static void log_trace(const char *p_function, const char *p_file, int p_line, const String &str); + static void log_trace(const char *p_function, const char *p_file, int p_line, const char *str); + + static void log_message(const String &str); + static void log_message(const char *str); + static void log_message(const char *p_function, const char *p_file, int p_line, const String &str); + static void log_message(const char *p_function, const char *p_file, int p_line, const char *str); + + static void log_warning(const String &str); + static void log_warning(const char *str); + static void log_warning(const char *p_function, const char *p_file, int p_line, const String &str); + static void log_warning(const char *p_function, const char *p_file, int p_line, const char *str); + + static void log_error(const String &str); + static void log_error(const char *str); + static void log_error(const char *p_function, const char *p_file, int p_line, const char *str); + static void log_error(const char *p_function, const char *p_file, int p_line, const String &str); + static void log_msg_error(const char *p_function, const char *p_file, int p_line, const char *p_msg, const char *str); + static void log_index_error(const char *p_function, const char *p_file, int p_line, const int index, const int size, const char *str); + static void log_index_error(const char *p_function, const char *p_file, int p_line, const int index, const int size, const String &str); + + static String *get_string_ptr(const int p_default_size = 100); + static String *get_string_ptr(const char *p_function, const char *p_file, int p_line, const int p_default_size = 300); + static String *get_string_ptr(const char *p_prefix, const char *p_function, const char *p_file, int p_line, const int p_default_size = 300); + static void return_string_ptr(String *str); + + static String *get_trace_string_ptr(const int p_default_size = 100); + static String *get_message_string_ptr(const int p_default_size = 100); + static String *get_warning_string_ptr(const int p_default_size = 100); + static String *get_error_string_ptr(const int p_default_size = 100); + + static String *get_trace_string_ptr(const char *p_function, const char *p_file, int p_line, const int p_default_size = 300); + static String *get_message_string_ptr(const char *p_function, const char *p_file, int p_line, const int p_default_size = 300); + static String *get_warning_string_ptr(const char *p_function, const char *p_file, int p_line, const int p_default_size = 300); + static String *get_error_string_ptr(const char *p_function, const char *p_file, int p_line, const int p_default_size = 300); + + static void log_ptr(String *str); + static void log_ret_ptr(String *str); +}; + +#endif +#line 0 + + +#line 1 "sfwl/core/typedefs.h" +#ifndef TYPEDEFS_H +#define TYPEDEFS_H + +/*************************************************************************/ +/* typedefs.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +/** + * Basic definitions and simple functions to be used everywhere. + */ + +//#include "platform_config.h" + +#ifndef _STR +#define _STR(m_x) #m_x +#define _MKSTR(m_x) _STR(m_x) +#endif + +//should always inline no matter what +#ifndef _ALWAYS_INLINE_ + +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define _ALWAYS_INLINE_ __attribute__((always_inline)) inline +#elif defined(__llvm__) +#define _ALWAYS_INLINE_ __attribute__((always_inline)) inline +#elif defined(_MSC_VER) +#define _ALWAYS_INLINE_ __forceinline +#else +#define _ALWAYS_INLINE_ inline +#endif + +#endif + +// Should always inline, except in dev builds because it makes debugging harder. +#ifndef _FORCE_INLINE_ +#ifdef DEV_ENABLED +#define _FORCE_INLINE_ inline +#else +#define _FORCE_INLINE_ _ALWAYS_INLINE_ +#endif + +#endif + +// No discard allows the compiler to flag warnings if we don't use the return value of functions / classes +#ifndef _NO_DISCARD_ +// c++ 17 onwards +#if __cplusplus >= 201703L +#define _NO_DISCARD_ [[nodiscard]] +#else +// __warn_unused_result__ supported on clang and GCC +#if (defined(__clang__) || defined(__GNUC__)) && defined(__has_attribute) +#if __has_attribute(__warn_unused_result__) +#define _NO_DISCARD_ __attribute__((__warn_unused_result__)) +#endif +#endif + +// Visual Studio 2012 onwards +#if _MSC_VER >= 1700 +#define _NO_DISCARD_ _Check_return_ +#endif + +// If nothing supported, just noop the macro +#ifndef _NO_DISCARD_ +#define _NO_DISCARD_ +#endif +#endif // not c++ 17 +#endif // not defined _NO_DISCARD_ + +// In some cases _NO_DISCARD_ will get false positives, +// we can prevent the warning in specific cases by preceding the call with a cast. +#ifndef _ALLOW_DISCARD_ +#define _ALLOW_DISCARD_ (void) +#endif + +// GCC (prior to c++ 17) Does not seem to support no discard with classes, only functions. +// So we will use a specific macro for classes. +#ifndef _NO_DISCARD_CLASS_ +#if (defined(__clang__) || defined(_MSC_VER)) +#define _NO_DISCARD_CLASS_ _NO_DISCARD_ +#else +#define _NO_DISCARD_CLASS_ +#endif +#endif + +//custom, gcc-safe offsetof, because gcc complains a lot. +template +T *_nullptr() { + T *t = NULL; + return t; +} + +#define OFFSET_OF(st, m) \ + ((size_t)((char *)&(_nullptr()->m) - (char *)0)) +/** + * Some platforms (devices) don't define NULL + */ + +#ifndef NULL +#define NULL 0 +#endif + +/** + * Windows badly defines a lot of stuff we'll never use. Undefine it. + */ + +#ifdef _WIN32 +#undef min // override standard definition +#undef max // override standard definition +#undef ERROR // override (really stupid) wingdi.h standard definition +#undef DELETE // override (another really stupid) winnt.h standard definition +#undef MessageBox // override winuser.h standard definition +#undef MIN // override standard definition +#undef MAX // override standard definition +#undef CLAMP // override standard definition +#undef Error +#undef OK +#undef CONNECT_DEFERRED // override from Windows SDK, clashes with Object enum +#endif + + + +/** Generic ABS function, for math uses please use Math::abs */ + +#ifndef ABS +#define ABS(m_v) (((m_v) < 0) ? (-(m_v)) : (m_v)) +#endif + +#define ABSDIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) + +#ifndef SGN +#define SGN(m_v) (((m_v) < 0) ? (-1.0f) : (+1.0f)) +#endif + +#ifndef MIN +#define MIN(m_a, m_b) (((m_a) < (m_b)) ? (m_a) : (m_b)) +#endif + +#ifndef MAX +#define MAX(m_a, m_b) (((m_a) > (m_b)) ? (m_a) : (m_b)) +#endif + +#ifndef CLAMP +#define CLAMP(m_a, m_min, m_max) (((m_a) < (m_min)) ? (m_min) : (((m_a) > (m_max)) ? m_max : m_a)) +#endif + +/** Generic swap template */ +#ifndef SWAP + +#define SWAP(m_x, m_y) __swap_tmpl((m_x), (m_y)) +template +inline void __swap_tmpl(T &x, T &y) { + T aux = x; + x = y; + y = aux; +} + +#endif //swap + +/* clang-format off */ +#define HEX2CHR(m_hex) \ + ((m_hex >= '0' && m_hex <= '9') ? (m_hex - '0') : \ + ((m_hex >= 'A' && m_hex <= 'F') ? (10 + m_hex - 'A') : \ + ((m_hex >= 'a' && m_hex <= 'f') ? (10 + m_hex - 'a') : 0))) +/* clang-format on */ + +// Macro to check whether we are compiled by clang +// and we have a specific builtin +#if defined(__llvm__) && defined(__has_builtin) +#define _llvm_has_builtin(x) __has_builtin(x) +#else +#define _llvm_has_builtin(x) 0 +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 5)) || _llvm_has_builtin(__builtin_mul_overflow) +#define _mul_overflow __builtin_mul_overflow +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 5)) || _llvm_has_builtin(__builtin_add_overflow) +#define _add_overflow __builtin_add_overflow +#endif + +/** Function to find the next power of 2 to an integer */ + +static _FORCE_INLINE_ unsigned int next_power_of_2(unsigned int x) { + if (x == 0) { + return 0; + } + + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + + return ++x; +} + +static _FORCE_INLINE_ unsigned int previous_power_of_2(unsigned int x) { + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x - (x >> 1); +} + +static _FORCE_INLINE_ unsigned int closest_power_of_2(unsigned int x) { + unsigned int nx = next_power_of_2(x); + unsigned int px = previous_power_of_2(x); + return (nx - x) > (x - px) ? px : nx; +} + +// We need this definition inside the function below. +static inline int get_shift_from_power_of_2(unsigned int p_pixel); + +template +static _FORCE_INLINE_ T nearest_power_of_2_templated(T x) { + --x; + + // The number of operations on x is the base two logarithm + // of the p_number of bits in the type. Add three to account + // for sizeof(T) being in bytes. + size_t num = get_shift_from_power_of_2(sizeof(T)) + 3; + + // If the compiler is smart, it unrolls this loop + // If its dumb, this is a bit slow. + for (size_t i = 0; i < num; i++) { + x |= x >> (1 << i); + } + + return ++x; +} + +/** Function to find the nearest (bigger) power of 2 to an integer */ + +static inline unsigned int nearest_shift(unsigned int p_number) { + for (int i = 30; i >= 0; i--) { + if (p_number & (1 << i)) { + return i + 1; + } + } + + return 0; +} + +/** get a shift value from a power of 2 */ +static inline int get_shift_from_power_of_2(unsigned int p_pixel) { + // return a GL_TEXTURE_SIZE_ENUM + + for (unsigned int i = 0; i < 32; i++) { + if (p_pixel == (unsigned int)(1 << i)) { + return i; + } + } + + return -1; +} + +/** Swap 16 bits value for endianness */ +#if defined(__GNUC__) || _llvm_has_builtin(__builtin_bswap16) +#define BSWAP16(x) __builtin_bswap16(x) +#else +static inline uint16_t BSWAP16(uint16_t x) { + return (x >> 8) | (x << 8); +} +#endif + +/** Swap 32 bits value for endianness */ +#if defined(__GNUC__) || _llvm_has_builtin(__builtin_bswap32) +#define BSWAP32(x) __builtin_bswap32(x) +#else +static inline uint32_t BSWAP32(uint32_t x) { + return ((x << 24) | ((x << 8) & 0x00FF0000) | ((x >> 8) & 0x0000FF00) | (x >> 24)); +} +#endif + +/** Swap 64 bits value for endianness */ +#if defined(__GNUC__) || _llvm_has_builtin(__builtin_bswap64) +#define BSWAP64(x) __builtin_bswap64(x) +#else +static inline uint64_t BSWAP64(uint64_t x) { + x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32; + x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16; + x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8; + return x; +} +#endif + +/** When compiling with RTTI, we can add an "extra" + * layer of safeness in many operations, so dynamic_cast + * is used besides casting by enum. + */ + +template +struct Comparator { + _ALWAYS_INLINE_ bool operator()(const T &p_a, const T &p_b) const { return (p_a < p_b); } +}; + +void _global_lock(); +void _global_unlock(); + +struct _GlobalLock { + _GlobalLock() { _global_lock(); } + ~_GlobalLock() { _global_unlock(); } +}; + +#define GLOBAL_LOCK_FUNCTION _GlobalLock _global_lock_; + +#ifdef NO_SAFE_CAST +#define SAFE_CAST static_cast +#else +#define SAFE_CAST dynamic_cast +#endif + +#define MT_SAFE + +#define __STRX(m_index) #m_index +#define __STR(m_index) __STRX(m_index) + +#ifdef __GNUC__ +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) x +#define unlikely(x) x +#endif + +#if defined(__GNUC__) +#define _PRINTF_FORMAT_ATTRIBUTE_2_0 __attribute__((format(printf, 2, 0))) +#define _PRINTF_FORMAT_ATTRIBUTE_2_3 __attribute__((format(printf, 2, 3))) +#else +#define _PRINTF_FORMAT_ATTRIBUTE_2_0 +#define _PRINTF_FORMAT_ATTRIBUTE_2_3 +#endif + +/** This is needed due to a strange OpenGL API that expects a pointer + * type for an argument that is actually an offset. + */ +#define CAST_INT_TO_UCHAR_PTR(ptr) ((uint8_t *)(uintptr_t)(ptr)) + +/** Hint for compilers that this fallthrough in a switch is intentional. + * Can be replaced by [[fallthrough]] annotation if we move to C++17. + * Including conditional support for it for people who set -std=c++17 + * themselves. + * Requires a trailing semicolon when used. + */ +#if __cplusplus >= 201703L +#define FALLTHROUGH [[fallthrough]] +#elif defined(__GNUC__) && __GNUC__ >= 7 +#define FALLTHROUGH __attribute__((fallthrough)) +#elif defined(__llvm__) && __cplusplus >= 201103L && defined(__has_feature) +#if __has_feature(cxx_attributes) && defined(__has_warning) +#if __has_warning("-Wimplicit-fallthrough") +#define FALLTHROUGH [[clang::fallthrough]] +#endif +#endif +#endif + +#ifndef FALLTHROUGH +#define FALLTHROUGH +#endif + +// Limit the depth of recursive algorithms when dealing with Array/Dictionary +#define MAX_RECURSION 100 + +//HAS_TRIVIAL_CONSTRUCTOR + +#if defined(__llvm__) && _llvm_has_builtin(__is_trivially_constructible) +#define HAS_TRIVIAL_CONSTRUCTOR(T) __is_trivially_constructible(T) +#endif + +#ifndef HAS_TRIVIAL_CONSTRUCTOR +#define HAS_TRIVIAL_CONSTRUCTOR(T) __has_trivial_constructor(T) +#endif + +//HAS_TRIVIAL_DESTRUCTOR + +#if defined(__llvm__) && _llvm_has_builtin(__is_trivially_destructible) +#define HAS_TRIVIAL_DESTRUCTOR(T) __is_trivially_destructible(T) +#endif + +#ifndef HAS_TRIVIAL_DESTRUCTOR +#define HAS_TRIVIAL_DESTRUCTOR(T) __has_trivial_destructor(T) +#endif + +//HAS_TRIVIAL_COPY + +#if defined(__llvm__) && _llvm_has_builtin(__is_trivially_copyable) +#define HAS_TRIVIAL_COPY(T) __is_trivially_copyable(T) +#endif + +#ifndef HAS_TRIVIAL_COPY +#define HAS_TRIVIAL_COPY(T) __has_trivial_copy(T) +#endif + +#endif // TYPEDEFS_H +#line 0 + + + +#line 1 "sfwl/core/stime.h" +#ifndef STIME_H +#define STIME_H + +// ----------------------------------------------------------------------------- +// time framework utils +// - originally by rlyeh, public domain. + + + +class STime { +public: + static uint64_t date(); // YYYYMMDDhhmmss + static uint64_t date_epoch(); // linux epoch + static char *date_string(); // "YYYY-MM-DD hh:mm:ss" + static double time_hh(); + static double time_mm(); + static double time_ss(); + static uint64_t time_ms(); + static uint64_t time_us(); + static uint64_t time_ns(); + static void sleep_ss(double ss); + static void sleep_ms(double ms); + static void sleep_us(double us); + static void sleep_ns(double us); +}; + +#endif +#line 0 + +#line 1 "sfwl/core/safe_refcount.h" +#ifndef SAFE_REFCOUNT_H +#define SAFE_REFCOUNT_H + +/*************************************************************************/ +/* safe_refcount.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +#if !defined(NO_THREADS) + +#include +#include + +// Design goals for these classes: +// - No automatic conversions or arithmetic operators, +// to keep explicit the use of atomics everywhere. +// - Using acquire-release semantics, even to set the first value. +// The first value may be set relaxedly in many cases, but adding the distinction +// between relaxed and unrelaxed operation to the interface would make it needlessly +// flexible. There's negligible waste in having release semantics for the initial +// value and, as an important benefit, you can be sure the value is properly synchronized +// even with threads that are already running. + +// This is used in very specific areas of the engine where it's critical that these guarantees are held +#define SAFE_NUMERIC_TYPE_PUN_GUARANTEES(m_type) \ + static_assert(sizeof(SafeNumeric) == sizeof(m_type), ""); \ + static_assert(alignof(SafeNumeric) == alignof(m_type), ""); \ + static_assert(std::is_trivially_destructible>::value, ""); + +#if defined(DEBUG_ENABLED) +void check_lockless_atomics(); +#endif + +template +class SafeNumeric { + std::atomic value; + +public: + _ALWAYS_INLINE_ void set(T p_value) { + value.store(p_value, std::memory_order_release); + } + + _ALWAYS_INLINE_ T get() const { + return value.load(std::memory_order_acquire); + } + + _ALWAYS_INLINE_ T increment() { + return value.fetch_add(1, std::memory_order_acq_rel) + 1; + } + + // Returns the original value instead of the new one + _ALWAYS_INLINE_ T postincrement() { + return value.fetch_add(1, std::memory_order_acq_rel); + } + + _ALWAYS_INLINE_ T decrement() { + return value.fetch_sub(1, std::memory_order_acq_rel) - 1; + } + + // Returns the original value instead of the new one + _ALWAYS_INLINE_ T postdecrement() { + return value.fetch_sub(1, std::memory_order_acq_rel); + } + + _ALWAYS_INLINE_ T add(T p_value) { + return value.fetch_add(p_value, std::memory_order_acq_rel) + p_value; + } + + // Returns the original value instead of the new one + _ALWAYS_INLINE_ T postadd(T p_value) { + return value.fetch_add(p_value, std::memory_order_acq_rel); + } + + _ALWAYS_INLINE_ T sub(T p_value) { + return value.fetch_sub(p_value, std::memory_order_acq_rel) - p_value; + } + + // Returns the original value instead of the new one + _ALWAYS_INLINE_ T postsub(T p_value) { + return value.fetch_sub(p_value, std::memory_order_acq_rel); + } + + _ALWAYS_INLINE_ T exchange_if_greater(T p_value) { + while (true) { + T tmp = value.load(std::memory_order_acquire); + if (tmp >= p_value) { + return tmp; // already greater, or equal + } + if (value.compare_exchange_weak(tmp, p_value, std::memory_order_acq_rel)) { + return p_value; + } + } + } + + _ALWAYS_INLINE_ T conditional_increment() { + while (true) { + T c = value.load(std::memory_order_acquire); + if (c == 0) { + return 0; + } + if (value.compare_exchange_weak(c, c + 1, std::memory_order_acq_rel)) { + return c + 1; + } + } + } + + _ALWAYS_INLINE_ explicit SafeNumeric(T p_value = static_cast(0)) { + set(p_value); + } +}; + +class SafeFlag { + std::atomic_bool flag; + +public: + _ALWAYS_INLINE_ bool is_set() const { + return flag.load(std::memory_order_acquire); + } + + _ALWAYS_INLINE_ void set() { + flag.store(true, std::memory_order_release); + } + + _ALWAYS_INLINE_ void clear() { + flag.store(false, std::memory_order_release); + } + + _ALWAYS_INLINE_ void set_to(bool p_value) { + flag.store(p_value, std::memory_order_release); + } + + _ALWAYS_INLINE_ explicit SafeFlag(bool p_value = false) { + set_to(p_value); + } +}; + +class SafeRefCount { + SafeNumeric count; + +public: + _ALWAYS_INLINE_ bool ref() { // true on success + return count.conditional_increment() != 0; + } + + _ALWAYS_INLINE_ uint32_t refval() { // none-zero on success + return count.conditional_increment(); + } + + _ALWAYS_INLINE_ bool unref() { // true if must be disposed of + return count.decrement() == 0; + } + + _ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of + return count.decrement(); + } + + _ALWAYS_INLINE_ uint32_t get() const { + return count.get(); + } + + _ALWAYS_INLINE_ void init(uint32_t p_value = 1) { + count.set(p_value); + } +}; + +#else + +template +class SafeNumeric { +protected: + T value; + +public: + _ALWAYS_INLINE_ void set(T p_value) { + value = p_value; + } + + _ALWAYS_INLINE_ T get() const { + return value; + } + + _ALWAYS_INLINE_ T increment() { + return ++value; + } + + _ALWAYS_INLINE_ T postincrement() { + return value++; + } + + _ALWAYS_INLINE_ T decrement() { + return --value; + } + + _ALWAYS_INLINE_ T postdecrement() { + return value--; + } + + _ALWAYS_INLINE_ T add(T p_value) { + return value += p_value; + } + + _ALWAYS_INLINE_ T postadd(T p_value) { + T old = value; + value += p_value; + return old; + } + + _ALWAYS_INLINE_ T sub(T p_value) { + return value -= p_value; + } + + _ALWAYS_INLINE_ T postsub(T p_value) { + T old = value; + value -= p_value; + return old; + } + + _ALWAYS_INLINE_ T exchange_if_greater(T p_value) { + if (value < p_value) { + value = p_value; + } + return value; + } + + _ALWAYS_INLINE_ T conditional_increment() { + if (value == 0) { + return 0; + } else { + return ++value; + } + } + + _ALWAYS_INLINE_ explicit SafeNumeric(T p_value = static_cast(0)) : + value(p_value) { + } +}; + +class SafeFlag { +protected: + bool flag; + +public: + _ALWAYS_INLINE_ bool is_set() const { + return flag; + } + + _ALWAYS_INLINE_ void set() { + flag = true; + } + + _ALWAYS_INLINE_ void clear() { + flag = false; + } + + _ALWAYS_INLINE_ void set_to(bool p_value) { + flag = p_value; + } + + _ALWAYS_INLINE_ explicit SafeFlag(bool p_value = false) : + flag(p_value) {} +}; + +class SafeRefCount { + uint32_t count; + +public: + _ALWAYS_INLINE_ bool ref() { // true on success + if (count != 0) { + ++count; + return true; + } else { + return false; + } + } + + _ALWAYS_INLINE_ uint32_t refval() { // none-zero on success + if (count != 0) { + return ++count; + } else { + return 0; + } + } + + _ALWAYS_INLINE_ bool unref() { // true if must be disposed of + return --count == 0; + } + + _ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of + return --count; + } + + _ALWAYS_INLINE_ uint32_t get() const { + return count; + } + + _ALWAYS_INLINE_ void init(uint32_t p_value = 1) { + count = p_value; + } + + SafeRefCount() : + count(0) {} +}; + +#endif + +#endif // SAFE_REFCOUNT_H +#line 0 + + +#line 1 "sfwl/core/error_macros.h" +#ifndef ERROR_MACROS_H +#define ERROR_MACROS_H + + + +// Based on Godot Engine's error_macros.h +// MIT License +// Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. +// Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). + +#ifdef _MSC_VER +#define GENERATE_TRAP \ + __debugbreak(); \ + /* Avoid warning about control paths */ \ + for (;;) { \ + } +#else +#define GENERATE_TRAP __builtin_trap(); +#endif + +// template methods for the variadic log macros. Add more as needed. +template +_FORCE_INLINE_ void _RLOG_MACRO_TEMPLATE_FUNC(STR str, A p0) { + str->operator+=(p0); +} + +template +_FORCE_INLINE_ void _RLOG_MACRO_TEMPLATE_FUNC(STR str, A p0, B p1) { + str->operator+=(p0); + str->operator+=(' '); + str->operator+=(p1); +} + +template +_FORCE_INLINE_ void _RLOG_MACRO_TEMPLATE_FUNC(STR str, A p0, B p1, C p2) { + str->operator+=(p0); + str->operator+=(' '); + str->operator+=(p1); + str->operator+=(' '); + str->operator+=(p2); +} + +template +_FORCE_INLINE_ void _RLOG_MACRO_TEMPLATE_FUNC(STR str, A p0, B p1, C p2, D p3) { + str->operator+=(p0); + str->operator+=(' '); + str->operator+=(p1); + str->operator+=(' '); + str->operator+=(p2); + str->operator+=(' '); + str->operator+=(p3); +} + +template +_FORCE_INLINE_ void _RLOG_MACRO_TEMPLATE_FUNC(STR str, A p0, B p1, C p2, D p3, E p4) { + str->operator+=(p0); + str->operator+=(' '); + str->operator+=(p1); + str->operator+=(' '); + str->operator+=(p2); + str->operator+=(' '); + str->operator+=(p3); + str->operator+=(' '); + str->operator+=(p4); +} + +#define PRINT_TRACE(str) \ + RLogger::print_trace(__FUNCTION__, __FILE__, __LINE__, str); + +#define LOG_TRACE(...) \ + { \ + String *_rlogger_string_ptr = RLogger::get_trace_string_ptr(__FUNCTION__, __FILE__, __LINE__); \ + _RLOG_MACRO_TEMPLATE_FUNC(_rlogger_string_ptr, __VA_ARGS__); \ + RLogger::log_ret_ptr(_rlogger_string_ptr); \ + } + +#define PRINT_MSG(str) \ + RLogger::print_message(__FUNCTION__, __FILE__, __LINE__, str); + +#define LOG_MSG(...) \ + { \ + String *_rlogger_string_ptr = RLogger::get_message_string_ptr(__FUNCTION__, __FILE__, __LINE__); \ + _RLOG_MACRO_TEMPLATE_FUNC(_rlogger_string_ptr, __VA_ARGS__); \ + RLogger::log_ret_ptr(_rlogger_string_ptr); \ + } + +#define PRINT_WARN(str) \ + RLogger::print_warning(__FUNCTION__, __FILE__, __LINE__, str); + +#define LOG_WARN(...) \ + { \ + String *_rlogger_string_ptr = RLogger::get_warning_string_ptr(__FUNCTION__, __FILE__, __LINE__); \ + _RLOG_MACRO_TEMPLATE_FUNC(_rlogger_string_ptr, __VA_ARGS__); \ + RLogger::log_ret_ptr(_rlogger_string_ptr); \ + } + +#define PRINT_ERR(str) \ + RLogger::print_error(__FUNCTION__, __FILE__, __LINE__, str); + +#define ERR_PRINT(str) \ + RLogger::print_error(__FUNCTION__, __FILE__, __LINE__, str); + +#define LOG_ERR(...) \ + { \ + String *_rlogger_string_ptr = RLogger::get_error_string_ptr(__FUNCTION__, __FILE__, __LINE__); \ + _RLOG_MACRO_TEMPLATE_FUNC(_rlogger_string_ptr, __VA_ARGS__); \ + RLogger::log_ret_ptr(_rlogger_string_ptr); \ + } + +#define ERR_FAIL_MSG(msg) \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, msg); \ + return; + +#define ERR_FAIL_V(val) \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, ""); \ + return val; + +#define ERR_FAIL_V_MSG(val, msg) \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, msg); \ + return val; + +#define ERR_FAIL_INDEX(index, size) \ + if ((index < 0) || (index >= size)) { \ + RLogger::log_index_error(__FUNCTION__, __FILE__, __LINE__, index, size, ""); \ + return; \ + } else \ + ((void)0) + +#define ERR_FAIL_INDEX_MSG(index, size, msg) \ + if ((index < 0) || (index >= size)) { \ + RLogger::log_index_error(__FUNCTION__, __FILE__, __LINE__, index, size, msg); \ + return; \ + } else \ + ((void)0) + +#define ERR_FAIL_INDEX_V(index, size, val) \ + if ((index < 0) || (index >= size)) { \ + RLogger::log_index_error(__FUNCTION__, __FILE__, __LINE__, index, size, ""); \ + return val; \ + } else \ + ((void)0) + +#define ERR_FAIL_INDEX_V_MSG(index, size, val, msg) \ + if ((index < 0) || (index >= size)) { \ + RLogger::log_index_error(__FUNCTION__, __FILE__, __LINE__, index, size, msg); \ + return val; \ + } else \ + ((void)0) + +#define ERR_FAIL_COND(cond) \ + if (cond) { \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, "ERR_FAIL_COND: \"" #cond "\" is true!"); \ + return; \ + } else \ + ((void)0) + +#define ERR_FAIL_COND_MSG(cond, msg) \ + if (cond) { \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, msg); \ + return; \ + } else \ + ((void)0) + +#define ERR_FAIL_COND_V(cond, val) \ + if (cond) { \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, "ERR_FAIL_COND: \"" #cond "\" is true!"); \ + return val; \ + } else \ + ((void)0) + +#define ERR_FAIL_COND_V_MSG(cond, val, msg) \ + if (cond) { \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, msg); \ + return val; \ + } else \ + ((void)0) + +#define ERR_CONTINUE(cond) \ + if (cond) { \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, "ERR_CONTINUE: \"" #cond "\" is true!"); \ + continue; \ + } else \ + ((void)0) + +#define ERR_CONTINUE_MSG(cond, msg) \ + if (cond) { \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, msg); \ + continue; \ + } else \ + ((void)0) + +#define ERR_CONTINUE_ACTION(cond, action) \ + if (cond) { \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, "ERR_CONTINUE: \"" #cond "\" is true!"); \ + action; \ + continue; \ + } else \ + ((void)0) + +#define ERR_CONTINUE_ACTION_MSG(cond, action, msg) \ + if (cond) { \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, msg); \ + action; \ + continue; \ + } else \ + ((void)0) + +#define CRASH_INDEX(index, size) \ + if ((index < 0) || (index >= size)) { \ + RLogger::log_index_error(__FUNCTION__, __FILE__, __LINE__, index, size, "CRASH!"); \ + GENERATE_TRAP \ + } else \ + ((void)0) + +#define CRASH_BAD_INDEX(index, size) \ + if ((index < 0) || (index >= size)) { \ + RLogger::log_index_error(__FUNCTION__, __FILE__, __LINE__, index, size, "CRASH!"); \ + GENERATE_TRAP \ + } else \ + ((void)0) + +#define CRASH_BAD_UNSIGNED_INDEX(index, size) \ + if ((index < 0) || (index >= size)) { \ + RLogger::log_index_error(__FUNCTION__, __FILE__, __LINE__, index, size, "CRASH!"); \ + GENERATE_TRAP \ + } else \ + ((void)0) + +#define CRASH_COND(cond) \ + if (cond) { \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, "CRASH_COND: \"" #cond "\" is true!"); \ + GENERATE_TRAP \ + } else \ + ((void)0) + +#define CRASH_COND_MSG(cond, msg) \ + if (cond) { \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, msg); \ + GENERATE_TRAP \ + } else \ + ((void)0) + +#define CRASH_MSG(msg) \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, msg); \ + GENERATE_TRAP + +#define CRASH_NOW(msg) \ + RLogger::log_error(__FUNCTION__, __FILE__, __LINE__, "CRASH!"); \ + GENERATE_TRAP + +/** + * This should be a 'free' assert for program flow and should not be needed in any releases, + * only used in dev builds. + */ +#ifdef DEV_ENABLED +#define DEV_ASSERT(m_cond) \ + if (unlikely(!(m_cond))) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: DEV_ASSERT failed \"" _STR(m_cond) "\" is false."); \ + _err_flush_stdout(); \ + GENERATE_TRAP \ + } else \ + ((void)0) +#else +#define DEV_ASSERT(m_cond) +#endif + +/** + * These should be 'free' checks for program flow and should not be needed in any releases, + * only used in dev builds. + */ +#ifdef DEV_ENABLED +#define DEV_CHECK(m_cond) \ + if (unlikely(!(m_cond))) { \ + ERR_PRINT("DEV_CHECK failed \"" _STR(m_cond) "\" is false."); \ + } else \ + ((void)0) +#else +#define DEV_CHECK(m_cond) +#endif + +#ifdef DEV_ENABLED +#define DEV_CHECK_ONCE(m_cond) \ + if (unlikely(!(m_cond))) { \ + ERR_PRINT_ONCE("DEV_CHECK_ONCE failed \"" _STR(m_cond) "\" is false."); \ + } else \ + ((void)0) +#else +#define DEV_CHECK_ONCE(m_cond) +#endif + +#endif +#line 0 + + + +#line 1 "sfwl/core/memory.h" +#ifndef MEMORY_H +#define MEMORY_H + +/*************************************************************************/ +/* memory.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +#ifndef PAD_ALIGN +#define PAD_ALIGN 16 //must always be greater than this at much +#endif + +class Memory { +#ifdef DEBUG_ENABLED + static SafeNumeric mem_usage; + static SafeNumeric max_usage; +#endif + + static SafeNumeric alloc_count; + +public: + static void *alloc_static(size_t p_bytes, bool p_pad_align = false); + static void *realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align = false); + static void free_static(void *p_ptr, bool p_pad_align = false); + + static uint64_t get_mem_available(); + static uint64_t get_mem_usage(); + static uint64_t get_mem_max_usage(); +}; + +class DefaultAllocator { +public: + _FORCE_INLINE_ static void *alloc(size_t p_memory) { return Memory::alloc_static(p_memory, false); } + _FORCE_INLINE_ static void free(void *p_ptr) { Memory::free_static(p_ptr, false); } +}; + +void *operator new(size_t p_size, const char *p_description); ///< operator new that takes a description and uses MemoryStaticPool +void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)); ///< operator new that takes a description and uses MemoryStaticPool + +void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description); ///< operator new that takes a description and uses a pointer to the preallocated memory + +#ifdef _MSC_VER +// When compiling with VC++ 2017, the above declarations of placement new generate many irrelevant warnings (C4291). +// The purpose of the following definitions is to muffle these warnings, not to provide a usable implementation of placement delete. +void operator delete(void *p_mem, const char *p_description); +void operator delete(void *p_mem, void *(*p_allocfunc)(size_t p_size)); +void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_description); +#endif + +#define memalloc(m_size) Memory::alloc_static(m_size) +#define memrealloc(m_mem, m_size) Memory::realloc_static(m_mem, m_size) +#define memfree(m_mem) Memory::free_static(m_mem) + +_ALWAYS_INLINE_ void postinitialize_handler(void *) {} + +template +_ALWAYS_INLINE_ T *_post_initialize(T *p_obj) { + postinitialize_handler(p_obj); + return p_obj; +} + +#define memnew(m_class) _post_initialize(new ("") m_class) + +_ALWAYS_INLINE_ void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description) { + //void *failptr=0; + //ERR_FAIL_COND_V( check < p_size , failptr); /** bug, or strange compiler, most likely */ + + return p_pointer; +} + +#define memnew_allocator(m_class, m_allocator) _post_initialize(new (m_allocator::alloc) m_class) +#define memnew_placement(m_placement, m_class) _post_initialize(new (m_placement, sizeof(m_class), "") m_class) + +_ALWAYS_INLINE_ bool predelete_handler(void *) { + return true; +} + +template +void memdelete(T *p_class) { + if (!predelete_handler(p_class)) { + return; // doesn't want to be deleted + } + if (!HAS_TRIVIAL_DESTRUCTOR(T)) { + p_class->~T(); + } + + Memory::free_static(p_class, false); +} + +template +void memdelete_allocator(T *p_class) { + if (!predelete_handler(p_class)) { + return; // doesn't want to be deleted + } + if (!HAS_TRIVIAL_DESTRUCTOR(T)) { + p_class->~T(); + } + + A::free(p_class); +} + +#define memdelete_notnull(m_v) \ + { \ + if (m_v) \ + memdelete(m_v); \ + } + +#define memnew_arr(m_class, m_count) memnew_arr_template(m_count) + +template +T *memnew_arr_template(size_t p_elements, const char *p_descr = "") { + if (p_elements == 0) { + return nullptr; + } + /** overloading operator new[] cannot be done , because it may not return the real allocated address (it may pad the 'element count' before the actual array). Because of that, it must be done by hand. This is the + same strategy used by std::vector, and the PoolVector class, so it should be safe.*/ + + size_t len = sizeof(T) * p_elements; + uint64_t *mem = (uint64_t *)Memory::alloc_static(len, true); + T *failptr = nullptr; //get rid of a warning + ERR_FAIL_COND_V(!mem, failptr); + *(mem - 1) = p_elements; + + if (!HAS_TRIVIAL_CONSTRUCTOR(T)) { + T *elems = (T *)mem; + + /* call operator new */ + for (size_t i = 0; i < p_elements; i++) { + new (&elems[i], sizeof(T), p_descr) T; + } + } + + return (T *)mem; +} + +/** + * Wonders of having own array functions, you can actually check the length of + * an allocated-with memnew_arr() array + */ + +template +size_t memarr_len(const T *p_class) { + uint64_t *ptr = (uint64_t *)p_class; + return *(ptr - 1); +} + +template +void memdelete_arr(T *p_class) { + uint64_t *ptr = (uint64_t *)p_class; + + if (!HAS_TRIVIAL_DESTRUCTOR(T)) { + uint64_t elem_count = *(ptr - 1); + + for (uint64_t i = 0; i < elem_count; i++) { + p_class[i].~T(); + } + } + + Memory::free_static(ptr, true); +} + +struct _GlobalNil { + int color; + _GlobalNil *right; + _GlobalNil *left; + _GlobalNil *parent; + + _GlobalNil(); +}; + +struct _GlobalNilClass { + static _GlobalNil _nil; +}; + +#endif +#line 0 + + + +#line 1 "sfwl/core/mutex.h" +#ifndef MUTEX_H +#define MUTEX_H + +/*************************************************************************/ +/* mutex.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +#if !defined(NO_THREADS) + +#include + +template +class MutexImpl { + mutable StdMutexT mutex; + friend class MutexLock; + +public: + _ALWAYS_INLINE_ void lock() const { + mutex.lock(); + } + + _ALWAYS_INLINE_ void unlock() const { + mutex.unlock(); + } + + _ALWAYS_INLINE_ Error try_lock() const { + return mutex.try_lock() ? OK : ERR_BUSY; + } +}; + +// This is written this way instead of being a template to overcome a limitation of C++ pre-17 +// that would require MutexLock to be used like this: MutexLock lock; +class MutexLock { + union { + std::recursive_mutex *recursive_mutex; + std::mutex *mutex; + }; + bool recursive; + +public: + _ALWAYS_INLINE_ explicit MutexLock(const MutexImpl &p_mutex) : + recursive_mutex(&p_mutex.mutex), + recursive(true) { + recursive_mutex->lock(); + } + _ALWAYS_INLINE_ explicit MutexLock(const MutexImpl &p_mutex) : + mutex(&p_mutex.mutex), + recursive(false) { + mutex->lock(); + } + + _ALWAYS_INLINE_ ~MutexLock() { + if (recursive) { + recursive_mutex->unlock(); + } else { + mutex->unlock(); + } + } +}; + +using Mutex = MutexImpl; // Recursive, for general use +using BinaryMutex = MutexImpl; // Non-recursive, handle with care + +extern template class MutexImpl; +extern template class MutexImpl; + +#else + +class FakeMutex { + FakeMutex() {} +}; + +template +class MutexImpl { +public: + _ALWAYS_INLINE_ void lock() const {} + _ALWAYS_INLINE_ void unlock() const {} + _ALWAYS_INLINE_ Error try_lock() const { return OK; } +}; + +class MutexLock { +public: + explicit MutexLock(const MutexImpl &p_mutex) {} +}; + +using Mutex = MutexImpl; +using BinaryMutex = MutexImpl; // Non-recursive, handle with care + +#endif // !NO_THREADS + +#endif // MUTEX_H +#line 0 + +#line 1 "sfwl/core/rw_lock.h" +#ifndef RWLOCK_H +#define RWLOCK_H + +/*************************************************************************/ +/* rw_lock.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +#if !defined(NO_THREADS) + +#include + +class RWLock { + mutable std::shared_timed_mutex mutex; + +public: + // Lock the rwlock, block if locked by someone else + void read_lock() const { + mutex.lock_shared(); + } + + // Unlock the rwlock, let other threads continue + void read_unlock() const { + mutex.unlock_shared(); + } + + // Attempt to lock the rwlock, OK on success, ERR_BUSY means it can't lock. + Error read_try_lock() const { + return mutex.try_lock_shared() ? OK : ERR_BUSY; + } + + // Lock the rwlock, block if locked by someone else + void write_lock() { + mutex.lock(); + } + + // Unlock the rwlock, let other thwrites continue + void write_unlock() { + mutex.unlock(); + } + + // Attempt to lock the rwlock, OK on success, ERR_BUSY means it can't lock. + Error write_try_lock() { + return mutex.try_lock() ? OK : ERR_BUSY; + } +}; + +#else + +class RWLock { +public: + void read_lock() const {} + void read_unlock() const {} + Error read_try_lock() const { return OK; } + + void write_lock() {} + void write_unlock() {} + Error write_try_lock() { return OK; } +}; + +#endif + +class RWLockRead { + const RWLock &lock; + +public: + RWLockRead(const RWLock &p_lock) : + lock(p_lock) { + lock.read_lock(); + } + ~RWLockRead() { + lock.read_unlock(); + } +}; + +class RWLockWrite { + RWLock &lock; + +public: + RWLockWrite(RWLock &p_lock) : + lock(p_lock) { + lock.write_lock(); + } + ~RWLockWrite() { + lock.write_unlock(); + } +}; + +#endif // RWLOCK_H +#line 0 + +#line 1 "sfwl/core/spin_lock.h" +#ifndef SPIN_LOCK_H +#define SPIN_LOCK_H + +/*************************************************************************/ +/* spin_lock.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +class SpinLock { + std::atomic_flag locked = ATOMIC_FLAG_INIT; + +public: + _ALWAYS_INLINE_ void lock() { + while (locked.test_and_set(std::memory_order_acquire)) { + ; + } + } + _ALWAYS_INLINE_ void unlock() { + locked.clear(std::memory_order_release); + } +}; +#endif // SPIN_LOCK_H +#line 0 + +#line 1 "sfwl/core/thread_safe.h" +#ifndef THREAD_SAFE_H +#define THREAD_SAFE_H + +/*************************************************************************/ +/* thread_safe.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +#define _THREAD_SAFE_CLASS_ mutable Mutex _thread_safe_; +#define _THREAD_SAFE_METHOD_ MutexLock _thread_safe_method_(_thread_safe_); +#define _THREAD_SAFE_LOCK_ _thread_safe_.lock(); +#define _THREAD_SAFE_UNLOCK_ _thread_safe_.unlock(); + +#endif +#line 0 + + +#line 1 "sfwl/core/pcg.h" +// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org +// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website) + +#ifndef RANDOM_H +#define RANDOM_H + + + +#define PCG_DEFAULT_INC_64 1442695040888963407ULL + +typedef struct { uint64_t state; uint64_t inc; } pcg32_random_t; +uint32_t pcg32_random_r(pcg32_random_t* rng); +void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq); +uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound); + +#endif // RANDOM_H +#line 0 + +#line 1 "sfwl/core/random_pcg.h" +#ifndef RANDOM_PCG_H +#define RANDOM_PCG_H + +/*************************************************************************/ +/* random_pcg.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +#if defined(__GNUC__) || (_llvm_has_builtin(__builtin_clz)) +#define CLZ32(x) __builtin_clz(x) +#elif defined(_MSC_VER) +#include "intrin.h" +static int __bsr_clz32(uint32_t x) { + unsigned long index; + _BitScanReverse(&index, x); + return 31 - index; +} +#define CLZ32(x) __bsr_clz32(x) +#else +#endif + +#if defined(__GNUC__) || (_llvm_has_builtin(__builtin_ldexp) && _llvm_has_builtin(__builtin_ldexpf)) +#define LDEXP(s, e) __builtin_ldexp(s, e) +#define LDEXPF(s, e) __builtin_ldexpf(s, e) +#else +#include "math.h" +#define LDEXP(s, e) ldexp(s, e) +#define LDEXPF(s, e) ldexp(s, e) +#endif + +class RandomPCG { + pcg32_random_t pcg; + uint64_t current_seed; // The seed the current generator state started from. + uint64_t current_inc; + +public: + static const uint64_t DEFAULT_SEED = 12047754176567800795U; + static const uint64_t DEFAULT_INC = PCG_DEFAULT_INC_64; + + RandomPCG(uint64_t p_seed = DEFAULT_SEED, uint64_t p_inc = DEFAULT_INC); + + _FORCE_INLINE_ void seed(uint64_t p_seed) { + current_seed = p_seed; + pcg32_srandom_r(&pcg, current_seed, current_inc); + } + _FORCE_INLINE_ uint64_t get_seed() { return current_seed; } + + _FORCE_INLINE_ void set_state(uint64_t p_state) { pcg.state = p_state; } + _FORCE_INLINE_ uint64_t get_state() const { return pcg.state; } + + void randomize(); + _FORCE_INLINE_ uint32_t rand() { + return pcg32_random_r(&pcg); + } + _FORCE_INLINE_ uint32_t rand(uint32_t bounds) { + return pcg32_boundedrand_r(&pcg, bounds); + } + + // Obtaining floating point numbers in [0, 1] range with "good enough" uniformity. + // These functions sample the output of rand() as the fraction part of an infinite binary number, + // with some tricks applied to reduce ops and branching: + // 1. Instead of shifting to the first 1 and connecting random bits, we simply set the MSB and LSB to 1. + // Provided that the RNG is actually uniform bit by bit, this should have the exact same effect. + // 2. In order to compensate for exponent info loss, we count zeros from another random number, + // and just add that to the initial offset. + // This has the same probability as counting and shifting an actual bit stream: 2^-n for n zeroes. + // For all numbers above 2^-96 (2^-64 for floats), the functions should be uniform. + // However, all numbers below that threshold are floored to 0. + // The thresholds are chosen to minimize rand() calls while keeping the numbers within a totally subjective quality standard. + // If clz or ldexp isn't available, fall back to bit truncation for performance, sacrificing uniformity. + _FORCE_INLINE_ double randd() { +#if defined(CLZ32) + uint32_t proto_exp_offset = rand(); + if (unlikely(proto_exp_offset == 0)) { + return 0; + } + uint64_t significand = (((uint64_t)rand()) << 32) | rand() | 0x8000000000000001U; + return LDEXP((double)significand, -64 - CLZ32(proto_exp_offset)); +#else +#pragma message("RandomPCG::randd - intrinsic clz is not available, falling back to bit truncation") + return (double)(((((uint64_t)rand()) << 32) | rand()) & 0x1FFFFFFFFFFFFFU) / (double)0x1FFFFFFFFFFFFFU; +#endif + } + _FORCE_INLINE_ float randf() { +#if defined(CLZ32) + uint32_t proto_exp_offset = rand(); + if (unlikely(proto_exp_offset == 0)) { + return 0; + } + return LDEXPF((float)(rand() | 0x80000001), -32 - CLZ32(proto_exp_offset)); +#else +#pragma message("RandomPCG::randf - intrinsic clz is not available, falling back to bit truncation") + return (float)(rand() & 0xFFFFFF) / (float)0xFFFFFF; +#endif + } + + _FORCE_INLINE_ double randfn(double p_mean, double p_deviation) { + return p_mean + p_deviation * (cos(Math_TAU * randd()) * sqrt(-2.0 * log(randd()))); // Box-Muller transform + } + _FORCE_INLINE_ float randfn(float p_mean, float p_deviation) { + return p_mean + p_deviation * (cos(Math_TAU * randf()) * sqrt(-2.0 * log(randf()))); // Box-Muller transform + } + + double random(double p_from, double p_to); + float random(float p_from, float p_to); + real_t randomr(real_t p_from, real_t p_to) { return random(p_from, p_to); } + int random(int p_from, int p_to); +}; + +#endif // RANDOM_PCG_H +#line 0 + + +#line 1 "sfwl/core/math_funcs.h" +#ifndef MATH_FUNCS_H +#define MATH_FUNCS_H + +/*************************************************************************/ +/* math_funcs.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +class Math { + static RandomPCG default_rand; + +public: + Math() {} // useless to instance + + // Not using 'RANDOM_MAX' to avoid conflict with system headers on some OSes (at least NetBSD). + static const uint64_t RANDOM_32BIT_MAX = 0xFFFFFFFF; + + static _ALWAYS_INLINE_ double sin(double p_x) { return ::sin(p_x); } + static _ALWAYS_INLINE_ float sin(float p_x) { return ::sinf(p_x); } + + static _ALWAYS_INLINE_ double cos(double p_x) { return ::cos(p_x); } + static _ALWAYS_INLINE_ float cos(float p_x) { return ::cosf(p_x); } + + static _ALWAYS_INLINE_ double tan(double p_x) { return ::tan(p_x); } + static _ALWAYS_INLINE_ float tan(float p_x) { return ::tanf(p_x); } + + static _ALWAYS_INLINE_ double sinh(double p_x) { return ::sinh(p_x); } + static _ALWAYS_INLINE_ float sinh(float p_x) { return ::sinhf(p_x); } + + static _ALWAYS_INLINE_ float sinc(float p_x) { return p_x == 0 ? 1 : ::sin(p_x) / p_x; } + static _ALWAYS_INLINE_ double sinc(double p_x) { return p_x == 0 ? 1 : ::sin(p_x) / p_x; } + + static _ALWAYS_INLINE_ float sincn(float p_x) { return sinc((float)Math_PI * p_x); } + static _ALWAYS_INLINE_ double sincn(double p_x) { return sinc(Math_PI * p_x); } + + static _ALWAYS_INLINE_ double cosh(double p_x) { return ::cosh(p_x); } + static _ALWAYS_INLINE_ float cosh(float p_x) { return ::coshf(p_x); } + + static _ALWAYS_INLINE_ double tanh(double p_x) { return ::tanh(p_x); } + static _ALWAYS_INLINE_ float tanh(float p_x) { return ::tanhf(p_x); } + + // Always does clamping so always safe to use. + static _ALWAYS_INLINE_ double asin(double p_x) { return p_x < -1 ? (-Math_PI / 2) : (p_x > 1 ? (Math_PI / 2) : ::asin(p_x)); } + static _ALWAYS_INLINE_ float asin(float p_x) { return p_x < -1 ? (-Math_PI / 2) : (p_x > 1 ? (Math_PI / 2) : ::asinf(p_x)); } + + // Always does clamping so always safe to use. + static _ALWAYS_INLINE_ double acos(double p_x) { return p_x < -1 ? Math_PI : (p_x > 1 ? 0 : ::acos(p_x)); } + static _ALWAYS_INLINE_ float acos(float p_x) { return p_x < -1 ? Math_PI : (p_x > 1 ? 0 : ::acosf(p_x)); } + + static _ALWAYS_INLINE_ double asin_unsafe(double p_x) { return ::asin(p_x); } + static _ALWAYS_INLINE_ float asin_unsafe(float p_x) { return ::asinf(p_x); } + + static _ALWAYS_INLINE_ double acos_unsafe(double p_x) { return ::acos(p_x); } + static _ALWAYS_INLINE_ float acos_unsafe(float p_x) { return ::acosf(p_x); } + + static _ALWAYS_INLINE_ double atan(double p_x) { return ::atan(p_x); } + static _ALWAYS_INLINE_ float atan(float p_x) { return ::atanf(p_x); } + + static _ALWAYS_INLINE_ double atan2(double p_y, double p_x) { return ::atan2(p_y, p_x); } + static _ALWAYS_INLINE_ float atan2(float p_y, float p_x) { return ::atan2f(p_y, p_x); } + + static _ALWAYS_INLINE_ double sqrt(double p_x) { return ::sqrt(p_x); } + static _ALWAYS_INLINE_ float sqrt(float p_x) { return ::sqrtf(p_x); } + + static _ALWAYS_INLINE_ double fmod(double p_x, double p_y) { return ::fmod(p_x, p_y); } + static _ALWAYS_INLINE_ float fmod(float p_x, float p_y) { return ::fmodf(p_x, p_y); } + + static _ALWAYS_INLINE_ double floor(double p_x) { return ::floor(p_x); } + static _ALWAYS_INLINE_ float floor(float p_x) { return ::floorf(p_x); } + // x + 0.5 -> so f.e. 0.9999999 will become 1 + static _ALWAYS_INLINE_ int floorf_int(const float x) { return static_cast(x + 0.5); } + + static _ALWAYS_INLINE_ double ceil(double p_x) { return ::ceil(p_x); } + static _ALWAYS_INLINE_ float ceil(float p_x) { return ::ceilf(p_x); } + + static _ALWAYS_INLINE_ double pow(double p_x, double p_y) { return ::pow(p_x, p_y); } + static _ALWAYS_INLINE_ float pow(float p_x, float p_y) { return ::powf(p_x, p_y); } + + static _ALWAYS_INLINE_ double log(double p_x) { return ::log(p_x); } + static _ALWAYS_INLINE_ float log(float p_x) { return ::logf(p_x); } + + static _ALWAYS_INLINE_ double log1p(double p_x) { return ::log1p(p_x); } + static _ALWAYS_INLINE_ float log1p(float p_x) { return ::log1pf(p_x); } + + static _ALWAYS_INLINE_ double log10(double p_x) { return ::log10f(p_x); } + static _ALWAYS_INLINE_ float log10(float p_x) { return ::log10(p_x); } + + static _ALWAYS_INLINE_ double log2(double p_x) { return ::log2(p_x); } + static _ALWAYS_INLINE_ float log2(float p_x) { return ::log2f(p_x); } + + static _ALWAYS_INLINE_ double exp(double p_x) { return ::exp(p_x); } + static _ALWAYS_INLINE_ float exp(float p_x) { return ::expf(p_x); } + + static _ALWAYS_INLINE_ double erf(double p_x) { return ::erf(p_x); } + static _ALWAYS_INLINE_ float erf(float p_x) { return ::erff(p_x); } + + // can save typing static_cast + inline static float divf(const float a, const float b) { return a / b; } + + static _ALWAYS_INLINE_ bool is_nan(double p_val) { +#ifdef _MSC_VER + return _isnan(p_val); +#elif defined(__GNUC__) && __GNUC__ < 6 + union { + uint64_t u; + double f; + } ieee754; + ieee754.f = p_val; + // (unsigned)(0x7ff0000000000001 >> 32) : 0x7ff00000 + return ((((unsigned)(ieee754.u >> 32) & 0x7fffffff) + ((unsigned)ieee754.u != 0)) > 0x7ff00000); +#else + return isnan(p_val); +#endif + } + + static _ALWAYS_INLINE_ bool is_nan(float p_val) { +#ifdef _MSC_VER + return _isnan(p_val); +#elif defined(__GNUC__) && __GNUC__ < 6 + union { + uint32_t u; + float f; + } ieee754; + ieee754.f = p_val; + // ----------------------------------- + // (single-precision floating-point) + // NaN : s111 1111 1xxx xxxx xxxx xxxx xxxx xxxx + // : (> 0x7f800000) + // where, + // s : sign + // x : non-zero number + // ----------------------------------- + return ((ieee754.u & 0x7fffffff) > 0x7f800000); +#else + return isnan(p_val); +#endif + } + + static _ALWAYS_INLINE_ bool is_inf(double p_val) { +#ifdef _MSC_VER + return !_finite(p_val); +// use an inline implementation of isinf as a workaround for problematic libstdc++ versions from gcc 5.x era +#elif defined(__GNUC__) && __GNUC__ < 6 + union { + uint64_t u; + double f; + } ieee754; + ieee754.f = p_val; + return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && + ((unsigned)ieee754.u == 0); +#else + return isinf(p_val); +#endif + } + + static _ALWAYS_INLINE_ bool is_inf(float p_val) { +#ifdef _MSC_VER + return !_finite(p_val); +// use an inline implementation of isinf as a workaround for problematic libstdc++ versions from gcc 5.x era +#elif defined(__GNUC__) && __GNUC__ < 6 + union { + uint32_t u; + float f; + } ieee754; + ieee754.f = p_val; + return (ieee754.u & 0x7fffffff) == 0x7f800000; +#else + return isinf(p_val); +#endif + } + + static _ALWAYS_INLINE_ double abs(double g) { + return absd(g); + } + static _ALWAYS_INLINE_ float abs(float g) { + return absf(g); + } + static _ALWAYS_INLINE_ int abs(int g) { + return g > 0 ? g : -g; + } + static _ALWAYS_INLINE_ int64_t abs(int64_t g) { + return g > 0 ? g : -g; + } + static _ALWAYS_INLINE_ int absi(int g) { + return g > 0 ? g : -g; + } + static _ALWAYS_INLINE_ int64_t absi(int64_t g) { + return g > 0 ? g : -g; + } + + static _ALWAYS_INLINE_ double fposmod(double p_x, double p_y) { + double value = Math::fmod(p_x, p_y); + if (((value < 0) && (p_y > 0)) || ((value > 0) && (p_y < 0))) { + value += p_y; + } + value += 0.0; + return value; + } + static _ALWAYS_INLINE_ float fposmod(float p_x, float p_y) { + float value = Math::fmod(p_x, p_y); + if (((value < 0) && (p_y > 0)) || ((value > 0) && (p_y < 0))) { + value += p_y; + } + value += 0.0f; + return value; + } + + static _ALWAYS_INLINE_ float fposmodp(float p_x, float p_y) { + float value = Math::fmod(p_x, p_y); + if (value < 0) { + value += p_y; + } + value += 0.0f; + return value; + } + static _ALWAYS_INLINE_ double fposmodp(double p_x, double p_y) { + double value = Math::fmod(p_x, p_y); + if (value < 0) { + value += p_y; + } + value += 0.0; + return value; + } + + static _ALWAYS_INLINE_ int64_t posmod(int64_t p_x, int64_t p_y) { + ERR_FAIL_COND_V_MSG(p_y == 0, 0, "Division by zero in posmod is undefined. Returning 0 as fallback."); + int64_t value = p_x % p_y; + if (((value < 0) && (p_y > 0)) || ((value > 0) && (p_y < 0))) { + value += p_y; + } + return value; + } + + static _ALWAYS_INLINE_ double deg2rad(double p_y) { + return p_y * Math_PI / 180.0; + } + static _ALWAYS_INLINE_ float deg2rad(float p_y) { + return p_y * (float)(Math_PI / 180.0); + } + + static _ALWAYS_INLINE_ double rad2deg(double p_y) { + return p_y * 180.0 / Math_PI; + } + static _ALWAYS_INLINE_ float rad2deg(float p_y) { + return p_y * (float)(180.0 / Math_PI); + } + + static _ALWAYS_INLINE_ double lerp(double p_from, double p_to, double p_weight) { + return p_from + (p_to - p_from) * p_weight; + } + static _ALWAYS_INLINE_ float lerp(float p_from, float p_to, float p_weight) { + return p_from + (p_to - p_from) * p_weight; + } + + static _ALWAYS_INLINE_ double lerp_angle(double p_from, double p_to, double p_weight) { + double difference = fmod(p_to - p_from, Math_TAU); + double distance = fmod(2.0 * difference, Math_TAU) - difference; + return p_from + distance * p_weight; + } + static _ALWAYS_INLINE_ float lerp_angle(float p_from, float p_to, float p_weight) { + float difference = fmod(p_to - p_from, (float)Math_TAU); + float distance = fmod(2.0f * difference, (float)Math_TAU) - difference; + return p_from + distance * p_weight; + } + + static _ALWAYS_INLINE_ double inverse_lerp(double p_from, double p_to, double p_value) { + return (p_value - p_from) / (p_to - p_from); + } + static _ALWAYS_INLINE_ float inverse_lerp(float p_from, float p_to, float p_value) { + return (p_value - p_from) / (p_to - p_from); + } + + static _ALWAYS_INLINE_ double range_lerp(double p_value, double p_istart, double p_istop, double p_ostart, double p_ostop) { + return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); + } + static _ALWAYS_INLINE_ float range_lerp(float p_value, float p_istart, float p_istop, float p_ostart, float p_ostop) { + return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); + } + + static _ALWAYS_INLINE_ double cubic_interpolate(double p_from, double p_to, double p_pre, double p_post, double p_weight) { + return 0.5 * + ((p_from * 2.0) + + (-p_pre + p_to) * p_weight + + (2.0 * p_pre - 5.0 * p_from + 4.0 * p_to - p_post) * (p_weight * p_weight) + + (-p_pre + 3.0 * p_from - 3.0 * p_to + p_post) * (p_weight * p_weight * p_weight)); + } + static _ALWAYS_INLINE_ float cubic_interpolate(float p_from, float p_to, float p_pre, float p_post, float p_weight) { + return 0.5f * + ((p_from * 2.0f) + + (-p_pre + p_to) * p_weight + + (2.0f * p_pre - 5.0f * p_from + 4.0f * p_to - p_post) * (p_weight * p_weight) + + (-p_pre + 3.0f * p_from - 3.0f * p_to + p_post) * (p_weight * p_weight * p_weight)); + } + + static _ALWAYS_INLINE_ double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { + /* Formula from Wikipedia article on Bezier curves. */ + double omt = (1.0 - p_t); + double omt2 = omt * omt; + double omt3 = omt2 * omt; + double t2 = p_t * p_t; + double t3 = t2 * p_t; + + return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3; + } + static _ALWAYS_INLINE_ float bezier_interpolate(float p_start, float p_control_1, float p_control_2, float p_end, float p_t) { + /* Formula from Wikipedia article on Bezier curves. */ + float omt = (1.0f - p_t); + float omt2 = omt * omt; + float omt3 = omt2 * omt; + float t2 = p_t * p_t; + float t3 = t2 * p_t; + + return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0f + p_control_2 * omt * t2 * 3.0f + p_end * t3; + } + + static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_s) { + if (is_equal_approx(p_from, p_to)) { + return p_from; + } + double s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0, 1.0); + return s * s * (3.0 - 2.0 * s); + } + static _ALWAYS_INLINE_ float smoothstep(float p_from, float p_to, float p_s) { + if (is_equal_approx(p_from, p_to)) { + return p_from; + } + float s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0f, 1.0f); + return s * s * (3.0f - 2.0f * s); + } + static _ALWAYS_INLINE_ double move_toward(double p_from, double p_to, double p_delta) { + return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; + } + static _ALWAYS_INLINE_ float move_toward(float p_from, float p_to, float p_delta) { + return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; + } + + static _ALWAYS_INLINE_ double linear2db(double p_linear) { + return Math::log(p_linear) * 8.6858896380650365530225783783321; + } + static _ALWAYS_INLINE_ float linear2db(float p_linear) { + return Math::log(p_linear) * (float)8.6858896380650365530225783783321; + } + + static _ALWAYS_INLINE_ double db2linear(double p_db) { + return Math::exp(p_db * 0.11512925464970228420089957273422); + } + static _ALWAYS_INLINE_ float db2linear(float p_db) { + return Math::exp(p_db * (float)0.11512925464970228420089957273422); + } + + static _ALWAYS_INLINE_ double round(double p_val) { + return ::round(p_val); + } + static _ALWAYS_INLINE_ float round(float p_val) { + return ::roundf(p_val); + } + + static _ALWAYS_INLINE_ int64_t wrapi(int64_t value, int64_t min, int64_t max) { + int64_t range = max - min; + return range == 0 ? min : min + ((((value - min) % range) + range) % range); + } + static _ALWAYS_INLINE_ double wrapf(double value, double min, double max) { + double range = max - min; + double result = is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range)); + if (is_equal_approx(result, max)) { + return min; + } + return result; + } + static _ALWAYS_INLINE_ float wrapf(float value, float min, float max) { + float range = max - min; + float result = is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range)); + if (is_equal_approx(result, max)) { + return min; + } + return result; + } + + static _ALWAYS_INLINE_ float fract(float value) { + return value - floor(value); + } + static _ALWAYS_INLINE_ double fract(double value) { + return value - floor(value); + } + static _ALWAYS_INLINE_ float pingpong(float value, float length) { + return (length != 0.0f) ? abs(fract((value - length) / (length * 2.0f)) * length * 2.0f - length) : 0.0f; + } + static _ALWAYS_INLINE_ double pingpong(double value, double length) { + return (length != 0.0) ? abs(fract((value - length) / (length * 2.0)) * length * 2.0 - length) : 0.0; + } + + // double only, as these functions are mainly used by the editor and not performance-critical, + static double ease(double p_x, double p_c); + static int step_decimals(double p_step); + static int range_step_decimals(double p_step); + static double stepify(double p_value, double p_step); + static double dectime(double p_value, double p_amount, double p_step); + + static uint32_t larger_prime(uint32_t p_val); + + static void seed(uint64_t x); + static void randomize(); + static uint32_t rand_from_seed(uint64_t *seed); + static uint32_t rand(); + static _ALWAYS_INLINE_ double randd() { + return (double)rand() / (double)Math::RANDOM_32BIT_MAX; + } + static _ALWAYS_INLINE_ float randf() { + return (float)rand() / (float)Math::RANDOM_32BIT_MAX; + } + static double randfn(double mean, double deviation); + + static double random(double from, double to); + static float random(float from, float to); + static real_t randomr(real_t from, real_t to); + static int random(int from, int to); + static _ALWAYS_INLINE_ int randomi(int from, int to) { + return random(from, to); + } + + static _ALWAYS_INLINE_ bool is_equal_approx_ratio(real_t a, real_t b, real_t epsilon = CMP_EPSILON, real_t min_epsilon = CMP_EPSILON) { + // this is an approximate way to check that numbers are close, as a ratio of their average size + // helps compare approximate numbers that may be very big or very small + real_t diff = abs(a - b); + if (diff == 0 || diff < min_epsilon) { + return true; + } + real_t avg_size = (abs(a) + abs(b)) / 2; + diff /= avg_size; + return diff < epsilon; + } + + static _ALWAYS_INLINE_ bool is_equal_approx(float a, float b) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. + float tolerance = (float)CMP_EPSILON * abs(a); + if (tolerance < (float)CMP_EPSILON) { + tolerance = (float)CMP_EPSILON; + } + return abs(a - b) < tolerance; + } + + static _ALWAYS_INLINE_ bool is_equal_approx(float a, float b, float tolerance) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. + return abs(a - b) < tolerance; + } + + static _ALWAYS_INLINE_ bool is_equal_approxt(float a, float b, float tolerance) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. + return abs(a - b) < tolerance; + } + + static _ALWAYS_INLINE_ bool is_zero_approx(float s) { + return abs(s) < (float)CMP_EPSILON; + } + + static _ALWAYS_INLINE_ bool is_equal_approx(double a, double b) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. + double tolerance = CMP_EPSILON * abs(a); + if (tolerance < CMP_EPSILON) { + tolerance = CMP_EPSILON; + } + return abs(a - b) < tolerance; + } + + static _ALWAYS_INLINE_ bool is_equal_approx(double a, double b, double tolerance) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. + return abs(a - b) < tolerance; + } + + static _ALWAYS_INLINE_ bool is_zero_approx(double s) { + return abs(s) < CMP_EPSILON; + } + + static _ALWAYS_INLINE_ float absf(float g) { + union { + float f; + uint32_t i; + } u; + + u.f = g; + u.i &= 2147483647u; + return u.f; + } + + static _ALWAYS_INLINE_ double absd(double g) { + union { + double d; + uint64_t i; + } u; + u.d = g; + u.i &= (uint64_t)9223372036854775807ll; + return u.d; + } + + // This function should be as fast as possible and rounding mode should not matter. + static _ALWAYS_INLINE_ int fast_ftoi(float a) { + // Assuming every supported compiler has `lrint()`. + return lrintf(a); + } + + static _ALWAYS_INLINE_ uint32_t halfbits_to_floatbits(uint16_t h) { + uint16_t h_exp, h_sig; + uint32_t f_sgn, f_exp, f_sig; + + h_exp = (h & 0x7c00u); + f_sgn = ((uint32_t)h & 0x8000u) << 16; + switch (h_exp) { + case 0x0000u: /* 0 or subnormal */ + h_sig = (h & 0x03ffu); + /* Signed zero */ + if (h_sig == 0) { + return f_sgn; + } + /* Subnormal */ + h_sig <<= 1; + while ((h_sig & 0x0400u) == 0) { + h_sig <<= 1; + h_exp++; + } + f_exp = ((uint32_t)(127 - 15 - h_exp)) << 23; + f_sig = ((uint32_t)(h_sig & 0x03ffu)) << 13; + return f_sgn + f_exp + f_sig; + case 0x7c00u: /* inf or NaN */ + /* All-ones exponent and a copy of the significand */ + return f_sgn + 0x7f800000u + (((uint32_t)(h & 0x03ffu)) << 13); + default: /* normalized */ + /* Just need to adjust the exponent and shift */ + return f_sgn + (((uint32_t)(h & 0x7fffu) + 0x1c000u) << 13); + } + } + + static _ALWAYS_INLINE_ float halfptr_to_float(const uint16_t *h) { + union { + uint32_t u32; + float f32; + } u; + + u.u32 = halfbits_to_floatbits(*h); + return u.f32; + } + + static _ALWAYS_INLINE_ float half_to_float(const uint16_t h) { + return halfptr_to_float(&h); + } + + static _ALWAYS_INLINE_ uint16_t make_half_float(float f) { + union { + float fv; + uint32_t ui; + } ci; + ci.fv = f; + + uint32_t x = ci.ui; + uint32_t sign = (unsigned short)(x >> 31); + uint32_t mantissa; + uint32_t exp; + uint16_t hf; + + // get mantissa + mantissa = x & ((1 << 23) - 1); + // get exponent bits + exp = x & (0xFF << 23); + if (exp >= 0x47800000) { + // check if the original single precision float number is a NaN + if (mantissa && (exp == (0xFF << 23))) { + // we have a single precision NaN + mantissa = (1 << 23) - 1; + } else { + // 16-bit half-float representation stores number as Inf + mantissa = 0; + } + hf = (((uint16_t)sign) << 15) | (uint16_t)((0x1F << 10)) | + (uint16_t)(mantissa >> 13); + } + // check if exponent is <= -15 + else if (exp <= 0x38000000) { + /*// store a denorm half-float value or zero + exp = (0x38000000 - exp) >> 23; + mantissa >>= (14 + exp); + + hf = (((uint16_t)sign) << 15) | (uint16_t)(mantissa); + */ + hf = 0; //denormals do not work for 3D, convert to zero + } else { + hf = (((uint16_t)sign) << 15) | + (uint16_t)((exp - 0x38000000) >> 13) | + (uint16_t)(mantissa >> 13); + } + + return hf; + } + + static _ALWAYS_INLINE_ float snap_scalar(float p_offset, float p_step, float p_target) { + return p_step != 0 ? Math::stepify(p_target - p_offset, p_step) + p_offset : p_target; + } + + static _ALWAYS_INLINE_ float snap_scalar_separation(float p_offset, float p_step, float p_target, float p_separation) { + if (p_step != 0) { + float a = Math::stepify(p_target - p_offset, p_step + p_separation) + p_offset; + float b = a; + if (p_target >= 0) { + b -= p_separation; + } else { + b += p_step; + } + return (Math::abs(p_target - a) < Math::abs(p_target - b)) ? a : b; + } + return p_target; + } +}; + +#endif // MATH_FUNCS_H +#line 0 + + + + +#line 1 "sfwl/core/cowdata.h" +#ifndef COWDATA_H_ +#define COWDATA_H_ + +/*************************************************************************/ +/* cowdata.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +template +class Vector; +class String; +class Char16String; +class CharString; +template +class VMap; + +#if !defined(NO_THREADS) +SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t) +#endif + +template +class CowData { + template + friend class Vector; + friend class String; + friend class Char16String; + friend class CharString; + template + friend class VMap; + +private: + mutable T *_ptr; + + // internal helpers + + _FORCE_INLINE_ SafeNumeric *_get_refcount() const { + if (!_ptr) { + return nullptr; + } + + return reinterpret_cast *>(_ptr) - 2; + } + + _FORCE_INLINE_ uint32_t *_get_size() const { + if (!_ptr) { + return nullptr; + } + + return reinterpret_cast(_ptr) - 1; + } + + _FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const { + //return nearest_power_of_2_templated(p_elements*sizeof(T)+sizeof(SafeRefCount)+sizeof(int)); + return next_power_of_2(p_elements * sizeof(T)); + } + + _FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const { +#if defined(_add_overflow) && defined(_mul_overflow) + size_t o; + size_t p; + if (_mul_overflow(p_elements, sizeof(T), &o)) { + *out = 0; + return false; + } + *out = next_power_of_2(o); + if (_add_overflow(o, static_cast(32), &p)) { + return false; //no longer allocated here + } + return true; +#else + // Speed is more important than correctness here, do the operations unchecked + // and hope the best + *out = _get_alloc_size(p_elements); + return true; +#endif + } + + void _unref(void *p_data); + void _ref(const CowData *p_from); + void _ref(const CowData &p_from); + uint32_t _copy_on_write(); + +public: + void operator=(const CowData &p_from) { _ref(p_from); } + + _FORCE_INLINE_ T *ptrw() { + _copy_on_write(); + return _ptr; + } + + _FORCE_INLINE_ const T *ptr() const { + return _ptr; + } + + _FORCE_INLINE_ int size() const { + uint32_t *size = (uint32_t *)_get_size(); + if (size) { + return *size; + } else { + return 0; + } + } + + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ bool empty() const { return _ptr == nullptr; } + + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { + CRASH_BAD_INDEX(p_index, size()); + _copy_on_write(); + _ptr[p_index] = p_elem; + } + + _FORCE_INLINE_ T &get_m(int p_index) { + CRASH_BAD_INDEX(p_index, size()); + _copy_on_write(); + return _ptr[p_index]; + } + + _FORCE_INLINE_ const T &get(int p_index) const { + CRASH_BAD_INDEX(p_index, size()); + + return _ptr[p_index]; + } + + Error resize(int p_size); + + _FORCE_INLINE_ void remove(int p_index) { + ERR_FAIL_INDEX(p_index, size()); + T *p = ptrw(); + int len = size(); + for (int i = p_index; i < len - 1; i++) { + p[i] = p[i + 1]; + }; + + resize(len - 1); + } + + Error insert(int p_pos, const T &p_val) { + ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); + resize(size() + 1); + for (int i = (size() - 1); i > p_pos; i--) { + set(i, get(i - 1)); + } + set(p_pos, p_val); + + return OK; + } + + void fill(const T &p_val) { + int len = size(); + + if (len == 0) { + return; + } + + T *p = ptrw(); + for (int i = 0; i < len; ++i) { + p[i] = p_val; + } + } + + int find(const T &p_val, int p_from = 0) const; + + _FORCE_INLINE_ CowData(); + _FORCE_INLINE_ ~CowData(); + _FORCE_INLINE_ CowData(CowData &p_from) { _ref(p_from); }; +}; + +template +void CowData::_unref(void *p_data) { + if (!p_data) { + return; + } + + SafeNumeric *refc = _get_refcount(); + + if (refc->decrement() > 0) { + return; // still in use + } + // clean up + + if (!HAS_TRIVIAL_DESTRUCTOR(T)) { + uint32_t *count = _get_size(); + T *data = (T *)(count + 1); + + for (uint32_t i = 0; i < *count; ++i) { + // call destructors + data[i].~T(); + } + } + + // free mem + Memory::free_static((uint8_t *)p_data, true); +} + +template +uint32_t CowData::_copy_on_write() { + if (!_ptr) { + return 0; + } + + SafeNumeric *refc = _get_refcount(); + + uint32_t rc = refc->get(); + if (likely(rc > 1)) { + /* in use by more than me */ + uint32_t current_size = *_get_size(); + + uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true); + + new (mem_new - 2, sizeof(uint32_t), "") SafeNumeric(1); //refcount + *(mem_new - 1) = current_size; //size + + T *_data = (T *)(mem_new); + + // initialize new elements + if (HAS_TRIVIAL_COPY(T)) { + memcpy(mem_new, _ptr, current_size * sizeof(T)); + + } else { + for (uint32_t i = 0; i < current_size; i++) { + memnew_placement(&_data[i], T(_ptr[i])); + } + } + + _unref(_ptr); + _ptr = _data; + + rc = 1; + } + return rc; +} + +template +Error CowData::resize(int p_size) { + ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER); + + int current_size = size(); + + if (p_size == current_size) { + return OK; + } + + if (p_size == 0) { + // wants to clean up + _unref(_ptr); + _ptr = nullptr; + return OK; + } + + // possibly changing size, copy on write + uint32_t rc = _copy_on_write(); + + size_t current_alloc_size = _get_alloc_size(current_size); + size_t alloc_size; + ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY); + + if (p_size > current_size) { + if (alloc_size != current_alloc_size) { + if (current_size == 0) { + // alloc from scratch + uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true); + ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY); + *(ptr - 1) = 0; //size, currently none + new (ptr - 2, sizeof(uint32_t), "") SafeNumeric(1); //refcount + + _ptr = (T *)ptr; + + } else { + uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true); + ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + new (_ptrnew - 2, sizeof(uint32_t), "") SafeNumeric(rc); //refcount + + _ptr = (T *)(_ptrnew); + } + } + + // construct the newly created elements + + if (!HAS_TRIVIAL_CONSTRUCTOR(T)) { + for (int i = *_get_size(); i < p_size; i++) { + memnew_placement(&_ptr[i], T); + } + } + + *_get_size() = p_size; + + } else if (p_size < current_size) { + if (!HAS_TRIVIAL_DESTRUCTOR(T)) { + // deinitialize no longer needed elements + for (uint32_t i = p_size; i < *_get_size(); i++) { + T *t = &_ptr[i]; + t->~T(); + } + } + + if (alloc_size != current_alloc_size) { + uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true); + ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + new (_ptrnew - 2, sizeof(uint32_t), "") SafeNumeric(rc); //refcount + + _ptr = (T *)(_ptrnew); + } + + *_get_size() = p_size; + } + + return OK; +} + +template +int CowData::find(const T &p_val, int p_from) const { + int ret = -1; + + if (p_from < 0 || size() == 0) { + return ret; + } + + for (int i = p_from; i < size(); i++) { + if (get(i) == p_val) { + ret = i; + break; + } + } + + return ret; +} + +template +void CowData::_ref(const CowData *p_from) { + _ref(*p_from); +} + +template +void CowData::_ref(const CowData &p_from) { + if (_ptr == p_from._ptr) { + return; // self assign, do nothing. + } + + _unref(_ptr); + _ptr = nullptr; + + if (!p_from._ptr) { + return; //nothing to do + } + + if (p_from._get_refcount()->conditional_increment() > 0) { // could reference + _ptr = p_from._ptr; + } +} + +template +CowData::CowData() { + _ptr = nullptr; +} + +template +CowData::~CowData() { + _unref(_ptr); +} + +#endif /* COW_H_ */ +#line 0 + +#line 1 "sfwl/core/sort_array.h" +#ifndef SORT_ARRAY_H +#define SORT_ARRAY_H + +/*************************************************************************/ +/* sort_array.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +#define ERR_BAD_COMPARE(cond) \ + if (unlikely(cond)) { \ + ERR_PRINT("bad comparison function; sorting will be broken"); \ + break; \ + } + +template +struct _DefaultComparator { + _FORCE_INLINE_ bool operator()(const T &a, const T &b) const { return (a < b); } +}; + +#ifdef DEBUG_ENABLED +#define SORT_ARRAY_VALIDATE_ENABLED true +#else +#define SORT_ARRAY_VALIDATE_ENABLED false +#endif + +template , bool Validate = SORT_ARRAY_VALIDATE_ENABLED> +class SortArray { + enum { + + INTROSORT_THRESHOLD = 16 + }; + +public: + Comparator compare; + + inline const T &median_of_3(const T &a, const T &b, const T &c) const { + if (compare(a, b)) { + if (compare(b, c)) { + return b; + } else if (compare(a, c)) { + return c; + } else { + return a; + } + } else if (compare(a, c)) { + return a; + } else if (compare(b, c)) { + return c; + } else { + return b; + } + } + + inline int bitlog(int n) const { + int k; + for (k = 0; n != 1; n >>= 1) { + ++k; + } + return k; + } + + /* Heap / Heapsort functions */ + + inline void push_heap(int p_first, int p_hole_idx, int p_top_index, T p_value, T *p_array) const { + int parent = (p_hole_idx - 1) / 2; + while (p_hole_idx > p_top_index && compare(p_array[p_first + parent], p_value)) { + p_array[p_first + p_hole_idx] = p_array[p_first + parent]; + p_hole_idx = parent; + parent = (p_hole_idx - 1) / 2; + } + p_array[p_first + p_hole_idx] = p_value; + } + + inline void pop_heap(int p_first, int p_last, int p_result, T p_value, T *p_array) const { + p_array[p_result] = p_array[p_first]; + adjust_heap(p_first, 0, p_last - p_first, p_value, p_array); + } + inline void pop_heap(int p_first, int p_last, T *p_array) const { + pop_heap(p_first, p_last - 1, p_last - 1, p_array[p_last - 1], p_array); + } + + inline void adjust_heap(int p_first, int p_hole_idx, int p_len, T p_value, T *p_array) const { + int top_index = p_hole_idx; + int second_child = 2 * p_hole_idx + 2; + + while (second_child < p_len) { + if (compare(p_array[p_first + second_child], p_array[p_first + (second_child - 1)])) { + second_child--; + } + + p_array[p_first + p_hole_idx] = p_array[p_first + second_child]; + p_hole_idx = second_child; + second_child = 2 * (second_child + 1); + } + + if (second_child == p_len) { + p_array[p_first + p_hole_idx] = p_array[p_first + (second_child - 1)]; + p_hole_idx = second_child - 1; + } + push_heap(p_first, p_hole_idx, top_index, p_value, p_array); + } + + inline void sort_heap(int p_first, int p_last, T *p_array) const { + while (p_last - p_first > 1) { + pop_heap(p_first, p_last--, p_array); + } + } + + inline void make_heap(int p_first, int p_last, T *p_array) const { + if (p_last - p_first < 2) { + return; + } + int len = p_last - p_first; + int parent = (len - 2) / 2; + + while (true) { + adjust_heap(p_first, parent, len, p_array[p_first + parent], p_array); + if (parent == 0) { + return; + } + parent--; + } + } + + inline void partial_sort(int p_first, int p_last, int p_middle, T *p_array) const { + make_heap(p_first, p_middle, p_array); + for (int i = p_middle; i < p_last; i++) { + if (compare(p_array[i], p_array[p_first])) { + pop_heap(p_first, p_middle, i, p_array[i], p_array); + } + } + sort_heap(p_first, p_middle, p_array); + } + + inline void partial_select(int p_first, int p_last, int p_middle, T *p_array) const { + make_heap(p_first, p_middle, p_array); + for (int i = p_middle; i < p_last; i++) { + if (compare(p_array[i], p_array[p_first])) { + pop_heap(p_first, p_middle, i, p_array[i], p_array); + } + } + } + + inline int partitioner(int p_first, int p_last, T p_pivot, T *p_array) const { + const int unmodified_first = p_first; + const int unmodified_last = p_last; + + while (true) { + while (compare(p_array[p_first], p_pivot)) { + if (Validate) { + ERR_BAD_COMPARE(p_first == unmodified_last - 1); + } + p_first++; + } + p_last--; + while (compare(p_pivot, p_array[p_last])) { + if (Validate) { + ERR_BAD_COMPARE(p_last == unmodified_first); + } + p_last--; + } + + if (!(p_first < p_last)) { + return p_first; + } + + SWAP(p_array[p_first], p_array[p_last]); + p_first++; + } + } + + inline void introsort(int p_first, int p_last, T *p_array, int p_max_depth) const { + while (p_last - p_first > INTROSORT_THRESHOLD) { + if (p_max_depth == 0) { + partial_sort(p_first, p_last, p_last, p_array); + return; + } + + p_max_depth--; + + int cut = partitioner( + p_first, + p_last, + median_of_3( + p_array[p_first], + p_array[p_first + (p_last - p_first) / 2], + p_array[p_last - 1]), + p_array); + + introsort(cut, p_last, p_array, p_max_depth); + p_last = cut; + } + } + + inline void introselect(int p_first, int p_nth, int p_last, T *p_array, int p_max_depth) const { + while (p_last - p_first > 3) { + if (p_max_depth == 0) { + partial_select(p_first, p_nth + 1, p_last, p_array); + SWAP(p_first, p_nth); + return; + } + + p_max_depth--; + + int cut = partitioner( + p_first, + p_last, + median_of_3( + p_array[p_first], + p_array[p_first + (p_last - p_first) / 2], + p_array[p_last - 1]), + p_array); + + if (cut <= p_nth) { + p_first = cut; + } else { + p_last = cut; + } + } + + insertion_sort(p_first, p_last, p_array); + } + + inline void unguarded_linear_insert(int p_last, T p_value, T *p_array) const { + int next = p_last - 1; + while (compare(p_value, p_array[next])) { + if (Validate) { + ERR_BAD_COMPARE(next == 0); + } + p_array[p_last] = p_array[next]; + p_last = next; + next--; + } + p_array[p_last] = p_value; + } + + inline void linear_insert(int p_first, int p_last, T *p_array) const { + T val = p_array[p_last]; + if (compare(val, p_array[p_first])) { + for (int i = p_last; i > p_first; i--) { + p_array[i] = p_array[i - 1]; + } + + p_array[p_first] = val; + } else { + unguarded_linear_insert(p_last, val, p_array); + } + } + + inline void insertion_sort(int p_first, int p_last, T *p_array) const { + if (p_first == p_last) { + return; + } + for (int i = p_first + 1; i != p_last; i++) { + linear_insert(p_first, i, p_array); + } + } + + inline void unguarded_insertion_sort(int p_first, int p_last, T *p_array) const { + for (int i = p_first; i != p_last; i++) { + unguarded_linear_insert(i, p_array[i], p_array); + } + } + + inline void final_insertion_sort(int p_first, int p_last, T *p_array) const { + if (p_last - p_first > INTROSORT_THRESHOLD) { + insertion_sort(p_first, p_first + INTROSORT_THRESHOLD, p_array); + unguarded_insertion_sort(p_first + INTROSORT_THRESHOLD, p_last, p_array); + } else { + insertion_sort(p_first, p_last, p_array); + } + } + + inline void sort_range(int p_first, int p_last, T *p_array) const { + if (p_first != p_last) { + introsort(p_first, p_last, p_array, bitlog(p_last - p_first) * 2); + final_insertion_sort(p_first, p_last, p_array); + } + } + + inline void sort(T *p_array, int p_len) const { + sort_range(0, p_len, p_array); + } + + inline void nth_element(int p_first, int p_last, int p_nth, T *p_array) const { + if (p_first == p_last || p_nth == p_last) { + return; + } + introselect(p_first, p_nth, p_last, p_array, bitlog(p_last - p_first) * 2); + } +}; + +#undef ERR_BAD_COMPARE + +#endif // SORT_ARRAY_H +#line 0 + +#line 1 "sfwl/core/rb_map.h" +#ifndef RB_MAP_H +#define RB_MAP_H + +/*************************************************************************/ +/* rb_map.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +// based on the very nice implementation of rb-trees by: +// https://web.archive.org/web/20120507164830/http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html + +template , class A = DefaultAllocator> +class RBMap { + enum Color { + RED, + BLACK + }; + struct _Data; + +public: + class Element { + private: + friend class RBMap; + int color; + Element *right; + Element *left; + Element *parent; + Element *_next; + Element *_prev; + K _key; + V _value; + //_Data *data; + + public: + const Element *next() const { + return _next; + } + Element *next() { + return _next; + } + const Element *prev() const { + return _prev; + } + Element *prev() { + return _prev; + } + const K &key() const { + return _key; + }; + V &value() { + return _value; + }; + const V &value() const { + return _value; + }; + V &get() { + return _value; + }; + const V &get() const { + return _value; + }; + Element() { + color = RED; + right = nullptr; + left = nullptr; + parent = nullptr; + _next = nullptr; + _prev = nullptr; + }; + }; + +private: + struct _Data { + Element *_root; + Element *_nil; + int size_cache; + + _FORCE_INLINE_ _Data() { +#ifdef GLOBALNIL_DISABLED + _nil = memnew_allocator(Element, A); + _nil->parent = _nil->left = _nil->right = _nil; + _nil->color = BLACK; +#else + _nil = (Element *)&_GlobalNilClass::_nil; +#endif + _root = nullptr; + size_cache = 0; + } + + void _create_root() { + _root = memnew_allocator(Element, A); + _root->parent = _root->left = _root->right = _nil; + _root->color = BLACK; + } + + void _free_root() { + if (_root) { + memdelete_allocator(_root); + _root = nullptr; + } + } + + ~_Data() { + _free_root(); + +#ifdef GLOBALNIL_DISABLED + memdelete_allocator(_nil); +#endif + } + }; + + _Data _data; + + inline void _set_color(Element *p_node, int p_color) { + ERR_FAIL_COND(p_node == _data._nil && p_color == RED); + p_node->color = p_color; + } + + inline void _rotate_left(Element *p_node) { + Element *r = p_node->right; + p_node->right = r->left; + if (r->left != _data._nil) { + r->left->parent = p_node; + } + r->parent = p_node->parent; + if (p_node == p_node->parent->left) { + p_node->parent->left = r; + } else { + p_node->parent->right = r; + } + + r->left = p_node; + p_node->parent = r; + } + + inline void _rotate_right(Element *p_node) { + Element *l = p_node->left; + p_node->left = l->right; + if (l->right != _data._nil) { + l->right->parent = p_node; + } + l->parent = p_node->parent; + if (p_node == p_node->parent->right) { + p_node->parent->right = l; + } else { + p_node->parent->left = l; + } + + l->right = p_node; + p_node->parent = l; + } + + inline Element *_successor(Element *p_node) const { + Element *node = p_node; + + if (node->right != _data._nil) { + node = node->right; + while (node->left != _data._nil) { /* returns the minimum of the right subtree of node */ + node = node->left; + } + return node; + } else { + while (node == node->parent->right) { + node = node->parent; + } + + if (node->parent == _data._root) { + return nullptr; // No successor, as p_node = last node + } + return node->parent; + } + } + + inline Element *_predecessor(Element *p_node) const { + Element *node = p_node; + + if (node->left != _data._nil) { + node = node->left; + while (node->right != _data._nil) { /* returns the minimum of the left subtree of node */ + node = node->right; + } + return node; + } else { + while (node == node->parent->left) { + node = node->parent; + } + + if (node == _data._root) { + return nullptr; // No predecessor, as p_node = first node + } + return node->parent; + } + } + + Element *_find(const K &p_key) const { + Element *node = _data._root->left; + C less; + + while (node != _data._nil) { + if (less(p_key, node->_key)) { + node = node->left; + } else if (less(node->_key, p_key)) { + node = node->right; + } else { + return node; // found + } + } + + return nullptr; + } + + Element *_find_closest(const K &p_key) const { + Element *node = _data._root->left; + Element *prev = nullptr; + C less; + + while (node != _data._nil) { + prev = node; + + if (less(p_key, node->_key)) { + node = node->left; + } else if (less(node->_key, p_key)) { + node = node->right; + } else { + return node; // found + } + } + + if (prev == nullptr) { + return nullptr; // tree empty + } + + if (less(p_key, prev->_key)) { + prev = prev->_prev; + } + + return prev; + } + + void _insert_rb_fix(Element *p_new_node) { + Element *node = p_new_node; + Element *nparent = node->parent; + Element *ngrand_parent; + + while (nparent->color == RED) { + ngrand_parent = nparent->parent; + + if (nparent == ngrand_parent->left) { + if (ngrand_parent->right->color == RED) { + _set_color(nparent, BLACK); + _set_color(ngrand_parent->right, BLACK); + _set_color(ngrand_parent, RED); + node = ngrand_parent; + nparent = node->parent; + } else { + if (node == nparent->right) { + _rotate_left(nparent); + node = nparent; + nparent = node->parent; + } + _set_color(nparent, BLACK); + _set_color(ngrand_parent, RED); + _rotate_right(ngrand_parent); + } + } else { + if (ngrand_parent->left->color == RED) { + _set_color(nparent, BLACK); + _set_color(ngrand_parent->left, BLACK); + _set_color(ngrand_parent, RED); + node = ngrand_parent; + nparent = node->parent; + } else { + if (node == nparent->left) { + _rotate_right(nparent); + node = nparent; + nparent = node->parent; + } + _set_color(nparent, BLACK); + _set_color(ngrand_parent, RED); + _rotate_left(ngrand_parent); + } + } + } + + _set_color(_data._root->left, BLACK); + } + + Element *_insert(const K &p_key, const V &p_value) { + Element *new_parent = _data._root; + Element *node = _data._root->left; + C less; + + while (node != _data._nil) { + new_parent = node; + + if (less(p_key, node->_key)) { + node = node->left; + } else if (less(node->_key, p_key)) { + node = node->right; + } else { + node->_value = p_value; + return node; // Return existing node with new value + } + } + + Element *new_node = memnew_allocator(Element, A); + new_node->parent = new_parent; + new_node->right = _data._nil; + new_node->left = _data._nil; + new_node->_key = p_key; + new_node->_value = p_value; + //new_node->data=_data; + + if (new_parent == _data._root || less(p_key, new_parent->_key)) { + new_parent->left = new_node; + } else { + new_parent->right = new_node; + } + + new_node->_next = _successor(new_node); + new_node->_prev = _predecessor(new_node); + if (new_node->_next) { + new_node->_next->_prev = new_node; + } + if (new_node->_prev) { + new_node->_prev->_next = new_node; + } + + _data.size_cache++; + _insert_rb_fix(new_node); + return new_node; + } + + void _erase_fix_rb(Element *p_node) { + Element *root = _data._root->left; + Element *node = _data._nil; + Element *sibling = p_node; + Element *parent = sibling->parent; + + while (node != root) { // If red node found, will exit at a break + if (sibling->color == RED) { + _set_color(sibling, BLACK); + _set_color(parent, RED); + if (sibling == parent->right) { + sibling = sibling->left; + _rotate_left(parent); + } else { + sibling = sibling->right; + _rotate_right(parent); + } + } + if ((sibling->left->color == BLACK) && (sibling->right->color == BLACK)) { + _set_color(sibling, RED); + if (parent->color == RED) { + _set_color(parent, BLACK); + break; + } else { // loop: haven't found any red nodes yet + node = parent; + parent = node->parent; + sibling = (node == parent->left) ? parent->right : parent->left; + } + } else { + if (sibling == parent->right) { + if (sibling->right->color == BLACK) { + _set_color(sibling->left, BLACK); + _set_color(sibling, RED); + _rotate_right(sibling); + sibling = sibling->parent; + } + _set_color(sibling, parent->color); + _set_color(parent, BLACK); + _set_color(sibling->right, BLACK); + _rotate_left(parent); + break; + } else { + if (sibling->left->color == BLACK) { + _set_color(sibling->right, BLACK); + _set_color(sibling, RED); + _rotate_left(sibling); + sibling = sibling->parent; + } + + _set_color(sibling, parent->color); + _set_color(parent, BLACK); + _set_color(sibling->left, BLACK); + _rotate_right(parent); + break; + } + } + } + + ERR_FAIL_COND(_data._nil->color != BLACK); + } + + void _erase(Element *p_node) { + Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next; + Element *node = (rp->left == _data._nil) ? rp->right : rp->left; + + Element *sibling; + if (rp == rp->parent->left) { + rp->parent->left = node; + sibling = rp->parent->right; + } else { + rp->parent->right = node; + sibling = rp->parent->left; + } + + if (node->color == RED) { + node->parent = rp->parent; + _set_color(node, BLACK); + } else if (rp->color == BLACK && rp->parent != _data._root) { + _erase_fix_rb(sibling); + } + + if (rp != p_node) { + ERR_FAIL_COND(rp == _data._nil); + + rp->left = p_node->left; + rp->right = p_node->right; + rp->parent = p_node->parent; + rp->color = p_node->color; + if (p_node->left != _data._nil) { + p_node->left->parent = rp; + } + if (p_node->right != _data._nil) { + p_node->right->parent = rp; + } + + if (p_node == p_node->parent->left) { + p_node->parent->left = rp; + } else { + p_node->parent->right = rp; + } + } + + if (p_node->_next) { + p_node->_next->_prev = p_node->_prev; + } + if (p_node->_prev) { + p_node->_prev->_next = p_node->_next; + } + + memdelete_allocator(p_node); + _data.size_cache--; + ERR_FAIL_COND(_data._nil->color == RED); + } + + void _calculate_depth(Element *p_element, int &max_d, int d) const { + if (p_element == _data._nil) { + return; + } + + _calculate_depth(p_element->left, max_d, d + 1); + _calculate_depth(p_element->right, max_d, d + 1); + + if (d > max_d) { + max_d = d; + } + } + + void _cleanup_tree(Element *p_element) { + if (p_element == _data._nil) { + return; + } + + _cleanup_tree(p_element->left); + _cleanup_tree(p_element->right); + memdelete_allocator(p_element); + } + + void _copy_from(const RBMap &p_map) { + clear(); + // not the fastest way, but safeset to write. + for (Element *I = p_map.front(); I; I = I->next()) { + insert(I->key(), I->value()); + } + } + +public: + const Element *find(const K &p_key) const { + if (!_data._root) { + return nullptr; + } + + const Element *res = _find(p_key); + return res; + } + + Element *find(const K &p_key) { + if (!_data._root) { + return nullptr; + } + + Element *res = _find(p_key); + return res; + } + + const Element *find_closest(const K &p_key) const { + if (!_data._root) { + return NULL; + } + + const Element *res = _find_closest(p_key); + return res; + } + + Element *find_closest(const K &p_key) { + if (!_data._root) { + return nullptr; + } + + Element *res = _find_closest(p_key); + return res; + } + + bool has(const K &p_key) const { + return find(p_key) != nullptr; + } + + Element *insert(const K &p_key, const V &p_value) { + if (!_data._root) { + _data._create_root(); + } + return _insert(p_key, p_value); + } + + void erase(Element *p_element) { + if (!_data._root || !p_element) { + return; + } + + _erase(p_element); + if (_data.size_cache == 0 && _data._root) { + _data._free_root(); + } + } + + bool erase(const K &p_key) { + if (!_data._root) { + return false; + } + + Element *e = find(p_key); + if (!e) { + return false; + } + + _erase(e); + if (_data.size_cache == 0 && _data._root) { + _data._free_root(); + } + return true; + } + + const V &operator[](const K &p_key) const { + CRASH_COND(!_data._root); + const Element *e = find(p_key); + CRASH_COND(!e); + return e->_value; + } + + V &operator[](const K &p_key) { + if (!_data._root) { + _data._create_root(); + } + + Element *e = find(p_key); + if (!e) { + e = insert(p_key, V()); + } + + return e->_value; + } + + Element *front() const { + if (!_data._root) { + return nullptr; + } + + Element *e = _data._root->left; + if (e == _data._nil) { + return nullptr; + } + + while (e->left != _data._nil) { + e = e->left; + } + + return e; + } + + Element *back() const { + if (!_data._root) { + return nullptr; + } + + Element *e = _data._root->left; + if (e == _data._nil) { + return nullptr; + } + + while (e->right != _data._nil) { + e = e->right; + } + + return e; + } + + inline bool empty() const { return _data.size_cache == 0; } + inline int size() const { return _data.size_cache; } + + int calculate_depth() const { + // used for debug mostly + if (!_data._root) { + return 0; + } + + int max_d = 0; + _calculate_depth(_data._root->left, max_d, 0); + return max_d; + } + + void clear() { + if (!_data._root) { + return; + } + + _cleanup_tree(_data._root->left); + _data._root->left = _data._nil; + _data.size_cache = 0; + _data._free_root(); + } + + void operator=(const RBMap &p_map) { + _copy_from(p_map); + } + + RBMap(const RBMap &p_map) { + _copy_from(p_map); + } + + _FORCE_INLINE_ RBMap() { + } + + ~RBMap() { + clear(); + } +}; + +#endif +#line 0 + +#line 1 "sfwl/core/rb_set.h" +#ifndef RB_SET_H +#define RB_SET_H + +/*************************************************************************/ +/* rb_set.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +// based on the very nice implementation of rb-trees by: +// https://web.archive.org/web/20120507164830/http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html + +template , class A = DefaultAllocator> +class RBSet { + enum Color { + RED, + BLACK + }; + struct _Data; + +public: + class Element { + private: + friend class RBSet; + int color; + Element *right; + Element *left; + Element *parent; + Element *_next; + Element *_prev; + T value; + //_Data *data; + + public: + const Element *next() const { + return _next; + } + Element *next() { + return _next; + } + const Element *prev() const { + return _prev; + } + Element *prev() { + return _prev; + } + const T &get() const { + return value; + }; + Element() { + color = RED; + right = nullptr; + left = nullptr; + parent = nullptr; + _next = nullptr; + _prev = nullptr; + }; + }; + +private: + struct _Data { + Element *_root; + Element *_nil; + int size_cache; + + _FORCE_INLINE_ _Data() { +#ifdef GLOBALNIL_DISABLED + _nil = memnew_allocator(Element, A); + _nil->parent = _nil->left = _nil->right = _nil; + _nil->color = BLACK; +#else + _nil = (Element *)&_GlobalNilClass::_nil; +#endif + _root = nullptr; + size_cache = 0; + } + + void _create_root() { + _root = memnew_allocator(Element, A); + _root->parent = _root->left = _root->right = _nil; + _root->color = BLACK; + } + + void _free_root() { + if (_root) { + memdelete_allocator(_root); + _root = nullptr; + } + } + + ~_Data() { + _free_root(); + +#ifdef GLOBALNIL_DISABLED + memdelete_allocator(_nil); +#endif + } + }; + + _Data _data; + + inline void _set_color(Element *p_node, int p_color) { + ERR_FAIL_COND(p_node == _data._nil && p_color == RED); + p_node->color = p_color; + } + + inline void _rotate_left(Element *p_node) { + Element *r = p_node->right; + p_node->right = r->left; + if (r->left != _data._nil) { + r->left->parent = p_node; + } + r->parent = p_node->parent; + if (p_node == p_node->parent->left) { + p_node->parent->left = r; + } else { + p_node->parent->right = r; + } + + r->left = p_node; + p_node->parent = r; + } + + inline void _rotate_right(Element *p_node) { + Element *l = p_node->left; + p_node->left = l->right; + if (l->right != _data._nil) { + l->right->parent = p_node; + } + l->parent = p_node->parent; + if (p_node == p_node->parent->right) { + p_node->parent->right = l; + } else { + p_node->parent->left = l; + } + + l->right = p_node; + p_node->parent = l; + } + + inline Element *_successor(Element *p_node) const { + Element *node = p_node; + + if (node->right != _data._nil) { + node = node->right; + while (node->left != _data._nil) { /* returns the minimum of the right subtree of node */ + node = node->left; + } + return node; + } else { + while (node == node->parent->right) { + node = node->parent; + } + + if (node->parent == _data._root) { + return nullptr; // No successor, as p_node = last node + } + return node->parent; + } + } + + inline Element *_predecessor(Element *p_node) const { + Element *node = p_node; + + if (node->left != _data._nil) { + node = node->left; + while (node->right != _data._nil) { /* returns the minimum of the left subtree of node */ + node = node->right; + } + return node; + } else { + while (node == node->parent->left) { + node = node->parent; + } + + if (node == _data._root) { + return nullptr; // No predecessor, as p_node = first node. + } + return node->parent; + } + } + + Element *_find(const T &p_value) const { + Element *node = _data._root->left; + C less; + + while (node != _data._nil) { + if (less(p_value, node->value)) { + node = node->left; + } else if (less(node->value, p_value)) { + node = node->right; + } else { + return node; // found + } + } + + return nullptr; + } + + Element *_lower_bound(const T &p_value) const { + Element *node = _data._root->left; + Element *prev = nullptr; + C less; + + while (node != _data._nil) { + prev = node; + + if (less(p_value, node->value)) { + node = node->left; + } else if (less(node->value, p_value)) { + node = node->right; + } else { + return node; // found + } + } + + if (prev == nullptr) { + return nullptr; // tree empty + } + + if (less(prev->value, p_value)) { + prev = prev->_next; + } + + return prev; + } + + void _insert_rb_fix(Element *p_new_node) { + Element *node = p_new_node; + Element *nparent = node->parent; + Element *ngrand_parent; + + while (nparent->color == RED) { + ngrand_parent = nparent->parent; + + if (nparent == ngrand_parent->left) { + if (ngrand_parent->right->color == RED) { + _set_color(nparent, BLACK); + _set_color(ngrand_parent->right, BLACK); + _set_color(ngrand_parent, RED); + node = ngrand_parent; + nparent = node->parent; + } else { + if (node == nparent->right) { + _rotate_left(nparent); + node = nparent; + nparent = node->parent; + } + _set_color(nparent, BLACK); + _set_color(ngrand_parent, RED); + _rotate_right(ngrand_parent); + } + } else { + if (ngrand_parent->left->color == RED) { + _set_color(nparent, BLACK); + _set_color(ngrand_parent->left, BLACK); + _set_color(ngrand_parent, RED); + node = ngrand_parent; + nparent = node->parent; + } else { + if (node == nparent->left) { + _rotate_right(nparent); + node = nparent; + nparent = node->parent; + } + _set_color(nparent, BLACK); + _set_color(ngrand_parent, RED); + _rotate_left(ngrand_parent); + } + } + } + + _set_color(_data._root->left, BLACK); + } + + Element *_insert(const T &p_value) { + Element *new_parent = _data._root; + Element *node = _data._root->left; + C less; + + while (node != _data._nil) { + new_parent = node; + + if (less(p_value, node->value)) { + node = node->left; + } else if (less(node->value, p_value)) { + node = node->right; + } else { + return node; // Return existing node + } + } + + Element *new_node = memnew_allocator(Element, A); + new_node->parent = new_parent; + new_node->right = _data._nil; + new_node->left = _data._nil; + new_node->value = p_value; + //new_node->data=_data; + + if (new_parent == _data._root || less(p_value, new_parent->value)) { + new_parent->left = new_node; + } else { + new_parent->right = new_node; + } + + new_node->_next = _successor(new_node); + new_node->_prev = _predecessor(new_node); + if (new_node->_next) { + new_node->_next->_prev = new_node; + } + if (new_node->_prev) { + new_node->_prev->_next = new_node; + } + + _data.size_cache++; + _insert_rb_fix(new_node); + return new_node; + } + + void _erase_fix_rb(Element *p_node) { + Element *root = _data._root->left; + Element *node = _data._nil; + Element *sibling = p_node; + Element *parent = sibling->parent; + + while (node != root) { // If red node found, will exit at a break + if (sibling->color == RED) { + _set_color(sibling, BLACK); + _set_color(parent, RED); + if (sibling == parent->right) { + sibling = sibling->left; + _rotate_left(parent); + } else { + sibling = sibling->right; + _rotate_right(parent); + } + } + if ((sibling->left->color == BLACK) && (sibling->right->color == BLACK)) { + _set_color(sibling, RED); + if (parent->color == RED) { + _set_color(parent, BLACK); + break; + } else { // loop: haven't found any red nodes yet + node = parent; + parent = node->parent; + sibling = (node == parent->left) ? parent->right : parent->left; + } + } else { + if (sibling == parent->right) { + if (sibling->right->color == BLACK) { + _set_color(sibling->left, BLACK); + _set_color(sibling, RED); + _rotate_right(sibling); + sibling = sibling->parent; + } + _set_color(sibling, parent->color); + _set_color(parent, BLACK); + _set_color(sibling->right, BLACK); + _rotate_left(parent); + break; + } else { + if (sibling->left->color == BLACK) { + _set_color(sibling->right, BLACK); + _set_color(sibling, RED); + _rotate_left(sibling); + sibling = sibling->parent; + } + + _set_color(sibling, parent->color); + _set_color(parent, BLACK); + _set_color(sibling->left, BLACK); + _rotate_right(parent); + break; + } + } + } + + ERR_FAIL_COND(_data._nil->color != BLACK); + } + + void _erase(Element *p_node) { + Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next; + Element *node = (rp->left == _data._nil) ? rp->right : rp->left; + + Element *sibling; + if (rp == rp->parent->left) { + rp->parent->left = node; + sibling = rp->parent->right; + } else { + rp->parent->right = node; + sibling = rp->parent->left; + } + + if (node->color == RED) { + node->parent = rp->parent; + _set_color(node, BLACK); + } else if (rp->color == BLACK && rp->parent != _data._root) { + _erase_fix_rb(sibling); + } + + if (rp != p_node) { + ERR_FAIL_COND(rp == _data._nil); + + rp->left = p_node->left; + rp->right = p_node->right; + rp->parent = p_node->parent; + rp->color = p_node->color; + if (p_node->left != _data._nil) { + p_node->left->parent = rp; + } + if (p_node->right != _data._nil) { + p_node->right->parent = rp; + } + + if (p_node == p_node->parent->left) { + p_node->parent->left = rp; + } else { + p_node->parent->right = rp; + } + } + + if (p_node->_next) { + p_node->_next->_prev = p_node->_prev; + } + if (p_node->_prev) { + p_node->_prev->_next = p_node->_next; + } + + memdelete_allocator(p_node); + _data.size_cache--; + ERR_FAIL_COND(_data._nil->color == RED); + } + + void _calculate_depth(Element *p_element, int &max_d, int d) const { + if (p_element == _data._nil) { + return; + } + + _calculate_depth(p_element->left, max_d, d + 1); + _calculate_depth(p_element->right, max_d, d + 1); + + if (d > max_d) { + max_d = d; + } + } + + void _cleanup_tree(Element *p_element) { + if (p_element == _data._nil) { + return; + } + + _cleanup_tree(p_element->left); + _cleanup_tree(p_element->right); + memdelete_allocator(p_element); + } + + void _copy_from(const RBSet &p_set) { + clear(); + // not the fastest way, but safeset to write. + for (Element *I = p_set.front(); I; I = I->next()) { + insert(I->get()); + } + } + +public: + const Element *find(const T &p_value) const { + if (!_data._root) { + return nullptr; + } + + const Element *res = _find(p_value); + return res; + } + + Element *find(const T &p_value) { + if (!_data._root) { + return nullptr; + } + + Element *res = _find(p_value); + return res; + } + + Element *lower_bound(const T &p_value) const { + if (!_data._root) { + return nullptr; + } + return _lower_bound(p_value); + } + + bool has(const T &p_value) const { + return find(p_value) != nullptr; + } + + Element *insert(const T &p_value) { + if (!_data._root) { + _data._create_root(); + } + return _insert(p_value); + } + + void erase(Element *p_element) { + if (!_data._root || !p_element) { + return; + } + + _erase(p_element); + if (_data.size_cache == 0 && _data._root) { + _data._free_root(); + } + } + + bool erase(const T &p_value) { + if (!_data._root) { + return false; + } + + Element *e = find(p_value); + if (!e) { + return false; + } + + _erase(e); + if (_data.size_cache == 0 && _data._root) { + _data._free_root(); + } + return true; + } + + Element *front() const { + if (!_data._root) { + return nullptr; + } + + Element *e = _data._root->left; + if (e == _data._nil) { + return nullptr; + } + + while (e->left != _data._nil) { + e = e->left; + } + + return e; + } + + Element *back() const { + if (!_data._root) { + return nullptr; + } + + Element *e = _data._root->left; + if (e == _data._nil) { + return nullptr; + } + + while (e->right != _data._nil) { + e = e->right; + } + + return e; + } + + inline bool empty() const { return _data.size_cache == 0; } + inline int size() const { return _data.size_cache; } + + int calculate_depth() const { + // used for debug mostly + if (!_data._root) { + return 0; + } + + int max_d = 0; + _calculate_depth(_data._root->left, max_d, 0); + return max_d; + } + + void clear() { + if (!_data._root) { + return; + } + + _cleanup_tree(_data._root->left); + _data._root->left = _data._nil; + _data.size_cache = 0; + _data._free_root(); + } + + void operator=(const RBSet &p_set) { + _copy_from(p_set); + } + + RBSet(const RBSet &p_set) { + _copy_from(p_set); + } + + _FORCE_INLINE_ RBSet() { + } + + ~RBSet() { + clear(); + } +}; + +#endif +#line 0 + +#line 1 "sfwl/core/vmap.h" +#ifndef VMAP_H +#define VMAP_H + +/*************************************************************************/ +/* vmap.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +template +class VMap { +public: + struct Pair { + T key; + V value; + + _FORCE_INLINE_ Pair() {} + + _FORCE_INLINE_ Pair(const T &p_key, const V &p_value) { + key = p_key; + value = p_value; + } + }; + +private: + CowData _cowdata; + + _FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const { + r_exact = false; + if (_cowdata.empty()) { + return 0; + } + + int low = 0; + int high = _cowdata.size() - 1; + const Pair *a = _cowdata.ptr(); + int middle = 0; + +#ifdef DEBUG_ENABLED + if (low > high) + ERR_PRINT("low > high, this may be a bug"); +#endif + while (low <= high) { + middle = (low + high) / 2; + + if (p_val < a[middle].key) { + high = middle - 1; //search low end of array + } else if (a[middle].key < p_val) { + low = middle + 1; //search high end of array + } else { + r_exact = true; + return middle; + } + } + + //return the position where this would be inserted + if (a[middle].key < p_val) { + middle++; + } + return middle; + } + + _FORCE_INLINE_ int _find_exact(const T &p_val) const { + if (_cowdata.empty()) { + return -1; + } + + int low = 0; + int high = _cowdata.size() - 1; + int middle; + const Pair *a = _cowdata.ptr(); + + while (low <= high) { + middle = (low + high) / 2; + + if (p_val < a[middle].key) { + high = middle - 1; //search low end of array + } else if (a[middle].key < p_val) { + low = middle + 1; //search high end of array + } else { + return middle; + } + } + + return -1; + } + +public: + int insert(const T &p_key, const V &p_val) { + bool exact; + int pos = _find(p_key, exact); + if (exact) { + _cowdata.get_m(pos).value = p_val; + return pos; + } + _cowdata.insert(pos, Pair(p_key, p_val)); + return pos; + } + + bool has(const T &p_val) const { + return _find_exact(p_val) != -1; + } + + void erase(const T &p_val) { + int pos = _find_exact(p_val); + if (pos < 0) { + return; + } + _cowdata.remove(pos); + } + + int find(const T &p_val) const { + return _find_exact(p_val); + } + + int find_nearest(const T &p_val) const { + if (_cowdata.empty()) { + return -1; + } + bool exact; + return _find(p_val, exact); + } + + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); } + + const Pair *get_array() const { + return _cowdata.ptr(); + } + + Pair *get_array() { + return _cowdata.ptrw(); + } + + const V &getv(int p_index) const { + return _cowdata.get(p_index).value; + } + + V &getv(int p_index) { + return _cowdata.get_m(p_index).value; + } + + const T &getk(int p_index) const { + return _cowdata.get(p_index).key; + } + + T &getk(int p_index) { + return _cowdata.get_m(p_index).key; + } + + inline const V &operator[](const T &p_key) const { + int pos = _find_exact(p_key); + + CRASH_COND(pos < 0); + + return _cowdata.get(pos).value; + } + + inline V &operator[](const T &p_key) { + int pos = _find_exact(p_key); + if (pos < 0) { + pos = insert(p_key, V()); + } + + return _cowdata.get_m(pos).value; + } + + _FORCE_INLINE_ VMap(){}; + _FORCE_INLINE_ VMap(const VMap &p_from) { _cowdata._ref(p_from._cowdata); } + inline VMap &operator=(const VMap &p_from) { + _cowdata._ref(p_from._cowdata); + return *this; + } +}; +#endif // VMAP_H +#line 0 + +#line 1 "sfwl/core/vector.h" +#ifndef VECTOR_H +#define VECTOR_H + +/*************************************************************************/ +/* vector.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +/** + * @class Vector + * @author Juan Linietsky + * Vector container. Regular Vector Container. Use with care and for smaller arrays when possible. Use PoolVector for large arrays. + */ + + + +template +class VectorWriteProxy { +public: + _FORCE_INLINE_ T &operator[](int p_index) { + CRASH_BAD_INDEX(p_index, ((Vector *)(this))->_cowdata.size()); + + return ((Vector *)(this))->_cowdata.ptrw()[p_index]; + } +}; + +template +class Vector { + friend class VectorWriteProxy; + +public: + VectorWriteProxy write; + +private: + CowData _cowdata; + +public: + bool push_back(T p_elem); + + void remove(int p_index) { _cowdata.remove(p_index); } + _FORCE_INLINE_ bool erase(const T &p_val) { + int idx = find(p_val); + if (idx >= 0) { + remove(idx); + return true; + } + return false; + }; + void invert(); + + _FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const T *ptr() const { return _cowdata.ptr(); } + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); } + + _FORCE_INLINE_ T get(int p_index) { return _cowdata.get(p_index); } + _FORCE_INLINE_ const T &get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + Error resize(int p_size) { return _cowdata.resize(p_size); } + _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); } + Error insert(int p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); } + int find(const T &p_val, int p_from = 0) const { return _cowdata.find(p_val, p_from); } + _FORCE_INLINE_ void fill(const T &p_val) { _cowdata.fill(p_val); } + + void append_array(Vector p_other); + + template + void sort_custom() { + int len = _cowdata.size(); + if (len == 0) { + return; + } + + T *data = ptrw(); + SortArray sorter; + sorter.sort(data, len); + } + + void sort() { + sort_custom<_DefaultComparator>(); + } + + void ordered_insert(const T &p_val) { + int i; + for (i = 0; i < _cowdata.size(); i++) { + if (p_val < operator[](i)) { + break; + }; + }; + insert(i, p_val); + } + + _FORCE_INLINE_ Vector() {} + _FORCE_INLINE_ Vector(const Vector &p_from) { _cowdata._ref(p_from._cowdata); } + inline Vector &operator=(const Vector &p_from) { + _cowdata._ref(p_from._cowdata); + return *this; + } + + Vector to_byte_array() const { + Vector ret; + ret.resize(size() * sizeof(T)); + memcpy(ret.ptrw(), ptr(), sizeof(T) * size()); + return ret; + } + + Vector slice(int p_begin, int p_end = INT32_MAX) const { + Vector result; + + const int s = size(); + + int begin = CLAMP(p_begin, -s, s); + if (begin < 0) { + begin += s; + } + int end = CLAMP(p_end, -s, s); + if (end < 0) { + end += s; + } + + ERR_FAIL_COND_V(begin > end, result); + + int result_size = end - begin; + result.resize(result_size); + + const T *const r = ptr(); + T *const w = result.ptrw(); + for (int i = 0; i < result_size; ++i) { + w[i] = r[begin + i]; + } + + return result; + } + + _FORCE_INLINE_ ~Vector() {} +}; + +template +void Vector::invert() { + for (int i = 0; i < size() / 2; i++) { + T *p = ptrw(); + SWAP(p[i], p[size() - i - 1]); + } +} + +template +void Vector::append_array(Vector p_other) { + const int ds = p_other.size(); + if (ds == 0) { + return; + } + const int bs = size(); + resize(bs + ds); + for (int i = 0; i < ds; ++i) { + ptrw()[bs + i] = p_other[i]; + } +} + +template +bool Vector::push_back(T p_elem) { + Error err = resize(size() + 1); + ERR_FAIL_COND_V(err, true); + set(size() - 1, p_elem); + + return false; +} + +#endif +#line 0 + +#line 1 "sfwl/core/vset.h" +#ifndef VSET_H +#define VSET_H + +/*************************************************************************/ +/* vset.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +template +class VSet { + Vector _data; + + _FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const { + r_exact = false; + if (_data.empty()) { + return 0; + } + + int low = 0; + int high = _data.size() - 1; + const T *a = &_data[0]; + int middle = 0; + +#ifdef DEBUG_ENABLED + if (low > high) + ERR_PRINT("low > high, this may be a bug"); +#endif + + while (low <= high) { + middle = (low + high) / 2; + + if (p_val < a[middle]) { + high = middle - 1; //search low end of array + } else if (a[middle] < p_val) { + low = middle + 1; //search high end of array + } else { + r_exact = true; + return middle; + } + } + + //return the position where this would be inserted + if (a[middle] < p_val) { + middle++; + } + return middle; + } + + _FORCE_INLINE_ int _find_exact(const T &p_val) const { + if (_data.empty()) { + return -1; + } + + int low = 0; + int high = _data.size() - 1; + int middle; + const T *a = &_data[0]; + + while (low <= high) { + middle = (low + high) / 2; + + if (p_val < a[middle]) { + high = middle - 1; //search low end of array + } else if (a[middle] < p_val) { + low = middle + 1; //search high end of array + } else { + return middle; + } + } + + return -1; + } + +public: + void insert(const T &p_val) { + bool exact; + int pos = _find(p_val, exact); + if (exact) { + return; + } + _data.insert(pos, p_val); + } + + bool has(const T &p_val) const { + return _find_exact(p_val) != -1; + } + + void erase(const T &p_val) { + int pos = _find_exact(p_val); + if (pos < 0) { + return; + } + _data.remove(pos); + } + + int find(const T &p_val) const { + return _find_exact(p_val); + } + + _FORCE_INLINE_ bool empty() const { return _data.empty(); } + + _FORCE_INLINE_ int size() const { return _data.size(); } + + inline T &operator[](int p_index) { + return _data.write[p_index]; + } + + inline const T &operator[](int p_index) const { + return _data[p_index]; + } +}; + +#endif // VSET_H +#line 0 + +#line 1 "sfwl/core/list.h" +#ifndef GLOBALS_LIST_H +#define GLOBALS_LIST_H + +/*************************************************************************/ +/* list.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +/** + * Generic Templatized Linked List Implementation. + * The implementation differs from the STL one because + * a compatible preallocated linked list can be written + * using the same API, or features such as erasing an element + * from the iterator. + */ + +template +class List { + struct _Data; + +public: + class Element { + private: + friend class List; + + T value; + Element *next_ptr; + Element *prev_ptr; + _Data *data; + + public: + /** + * Get NEXT Element iterator, for constant lists. + */ + _FORCE_INLINE_ const Element *next() const { + return next_ptr; + }; + /** + * Get NEXT Element iterator, + */ + _FORCE_INLINE_ Element *next() { + return next_ptr; + }; + + /** + * Get PREV Element iterator, for constant lists. + */ + _FORCE_INLINE_ const Element *prev() const { + return prev_ptr; + }; + /** + * Get PREV Element iterator, + */ + _FORCE_INLINE_ Element *prev() { + return prev_ptr; + }; + + /** + * * operator, for using as *iterator, when iterators are defined on stack. + */ + _FORCE_INLINE_ const T &operator*() const { + return value; + }; + /** + * operator->, for using as iterator->, when iterators are defined on stack, for constant lists. + */ + _FORCE_INLINE_ const T *operator->() const { + return &value; + }; + /** + * * operator, for using as *iterator, when iterators are defined on stack, + */ + _FORCE_INLINE_ T &operator*() { + return value; + }; + /** + * operator->, for using as iterator->, when iterators are defined on stack, for constant lists. + */ + _FORCE_INLINE_ T *operator->() { + return &value; + }; + + /** + * get the value stored in this element. + */ + _FORCE_INLINE_ T &get() { + return value; + }; + /** + * get the value stored in this element, for constant lists + */ + _FORCE_INLINE_ const T &get() const { + return value; + }; + /** + * set the value stored in this element. + */ + _FORCE_INLINE_ void set(const T &p_value) { + value = (T &)p_value; + }; + + void erase() { + data->erase(this); + } + + _FORCE_INLINE_ Element() { + next_ptr = nullptr; + prev_ptr = nullptr; + data = nullptr; + }; + }; + +private: + struct _Data { + Element *first; + Element *last; + int size_cache; + + bool erase(const Element *p_I) { + ERR_FAIL_COND_V(!p_I, false); + ERR_FAIL_COND_V(p_I->data != this, false); + + if (first == p_I) { + first = p_I->next_ptr; + }; + + if (last == p_I) { + last = p_I->prev_ptr; + } + + if (p_I->prev_ptr) { + p_I->prev_ptr->next_ptr = p_I->next_ptr; + } + + if (p_I->next_ptr) { + p_I->next_ptr->prev_ptr = p_I->prev_ptr; + } + + memdelete_allocator(const_cast(p_I)); + size_cache--; + + return true; + } + }; + + _Data *_data; + +public: + /** + * return a const iterator to the beginning of the list. + */ + _FORCE_INLINE_ const Element *front() const { + return _data ? _data->first : nullptr; + }; + + /** + * return an iterator to the beginning of the list. + */ + _FORCE_INLINE_ Element *front() { + return _data ? _data->first : nullptr; + }; + + /** + * return a const iterator to the last member of the list. + */ + _FORCE_INLINE_ const Element *back() const { + return _data ? _data->last : nullptr; + }; + + /** + * return an iterator to the last member of the list. + */ + _FORCE_INLINE_ Element *back() { + return _data ? _data->last : nullptr; + }; + + /** + * store a new element at the end of the list + */ + Element *push_back(const T &value) { + if (!_data) { + _data = memnew_allocator(_Data, A); + _data->first = nullptr; + _data->last = nullptr; + _data->size_cache = 0; + } + + Element *n = memnew_allocator(Element, A); + n->value = (T &)value; + + n->prev_ptr = _data->last; + n->next_ptr = nullptr; + n->data = _data; + + if (_data->last) { + _data->last->next_ptr = n; + } + + _data->last = n; + + if (!_data->first) { + _data->first = n; + } + + _data->size_cache++; + + return n; + }; + + void pop_back() { + if (_data && _data->last) { + erase(_data->last); + } + } + + /** + * store a new element at the beginning of the list + */ + Element *push_front(const T &value) { + if (!_data) { + _data = memnew_allocator(_Data, A); + _data->first = nullptr; + _data->last = nullptr; + _data->size_cache = 0; + } + + Element *n = memnew_allocator(Element, A); + n->value = (T &)value; + n->prev_ptr = nullptr; + n->next_ptr = _data->first; + n->data = _data; + + if (_data->first) { + _data->first->prev_ptr = n; + } + + _data->first = n; + + if (!_data->last) { + _data->last = n; + } + + _data->size_cache++; + + return n; + }; + + void pop_front() { + if (_data && _data->first) { + erase(_data->first); + } + } + + Element *insert_after(Element *p_element, const T &p_value) { + CRASH_COND(p_element && (!_data || p_element->data != _data)); + + if (!p_element) { + return push_back(p_value); + } + + Element *n = memnew_allocator(Element, A); + n->value = (T &)p_value; + n->prev_ptr = p_element; + n->next_ptr = p_element->next_ptr; + n->data = _data; + + if (!p_element->next_ptr) { + _data->last = n; + } else { + p_element->next_ptr->prev_ptr = n; + } + + p_element->next_ptr = n; + + _data->size_cache++; + + return n; + } + + Element *insert_before(Element *p_element, const T &p_value) { + CRASH_COND(p_element && (!_data || p_element->data != _data)); + + if (!p_element) { + return push_back(p_value); + } + + Element *n = memnew_allocator(Element, A); + n->value = (T &)p_value; + n->prev_ptr = p_element->prev_ptr; + n->next_ptr = p_element; + n->data = _data; + + if (!p_element->prev_ptr) { + _data->first = n; + } else { + p_element->prev_ptr->next_ptr = n; + } + + p_element->prev_ptr = n; + + _data->size_cache++; + + return n; + } + + /** + * find an element in the list, + */ + template + Element *find(const T_v &p_val) { + Element *it = front(); + while (it) { + if (it->value == p_val) { + return it; + } + it = it->next(); + }; + + return nullptr; + }; + + /** + * erase an element in the list, by iterator pointing to it. Return true if it was found/erased. + */ + bool erase(const Element *p_I) { + if (_data) { + bool ret = _data->erase(p_I); + + if (_data->size_cache == 0) { + memdelete_allocator<_Data, A>(_data); + _data = nullptr; + } + + return ret; + } + + return false; + }; + + /** + * erase the first element in the list, that contains value + */ + bool erase(const T &value) { + Element *I = find(value); + return erase(I); + }; + + /** + * return whether the list is empty + */ + _FORCE_INLINE_ bool empty() const { + return (!_data || !_data->size_cache); + } + + /** + * clear the list + */ + void clear() { + while (front()) { + erase(front()); + }; + }; + + _FORCE_INLINE_ int size() const { + return _data ? _data->size_cache : 0; + } + + void swap(Element *p_A, Element *p_B) { + ERR_FAIL_COND(!p_A || !p_B); + ERR_FAIL_COND(p_A->data != _data); + ERR_FAIL_COND(p_B->data != _data); + + if (p_A == p_B) { + return; + } + Element *A_prev = p_A->prev_ptr; + Element *A_next = p_A->next_ptr; + Element *B_prev = p_B->prev_ptr; + Element *B_next = p_B->next_ptr; + + if (A_prev) { + A_prev->next_ptr = p_B; + } else { + _data->first = p_B; + } + if (B_prev) { + B_prev->next_ptr = p_A; + } else { + _data->first = p_A; + } + if (A_next) { + A_next->prev_ptr = p_B; + } else { + _data->last = p_B; + } + if (B_next) { + B_next->prev_ptr = p_A; + } else { + _data->last = p_A; + } + p_A->prev_ptr = A_next == p_B ? p_B : B_prev; + p_A->next_ptr = B_next == p_A ? p_B : B_next; + p_B->prev_ptr = B_next == p_A ? p_A : A_prev; + p_B->next_ptr = A_next == p_B ? p_A : A_next; + } + /** + * copy the list + */ + void operator=(const List &p_list) { + clear(); + const Element *it = p_list.front(); + while (it) { + push_back(it->get()); + it = it->next(); + } + } + + T &operator[](int p_index) { + CRASH_BAD_INDEX(p_index, size()); + + Element *I = front(); + int c = 0; + while (I) { + if (c == p_index) { + return I->get(); + } + I = I->next(); + c++; + } + + CRASH_NOW(); // bug!! + } + + const T &operator[](int p_index) const { + CRASH_BAD_INDEX(p_index, size()); + + const Element *I = front(); + int c = 0; + while (I) { + if (c == p_index) { + return I->get(); + } + I = I->next(); + c++; + } + + CRASH_NOW(); // bug!! + } + + void move_to_back(Element *p_I) { + ERR_FAIL_COND(p_I->data != _data); + if (!p_I->next_ptr) { + return; + } + + if (_data->first == p_I) { + _data->first = p_I->next_ptr; + }; + + if (_data->last == p_I) { + _data->last = p_I->prev_ptr; + } + + if (p_I->prev_ptr) { + p_I->prev_ptr->next_ptr = p_I->next_ptr; + } + + p_I->next_ptr->prev_ptr = p_I->prev_ptr; + + _data->last->next_ptr = p_I; + p_I->prev_ptr = _data->last; + p_I->next_ptr = nullptr; + _data->last = p_I; + } + + void invert() { + int s = size() / 2; + Element *F = front(); + Element *B = back(); + for (int i = 0; i < s; i++) { + SWAP(F->value, B->value); + F = F->next(); + B = B->prev(); + } + } + + void move_to_front(Element *p_I) { + ERR_FAIL_COND(p_I->data != _data); + if (!p_I->prev_ptr) { + return; + } + + if (_data->first == p_I) { + _data->first = p_I->next_ptr; + }; + + if (_data->last == p_I) { + _data->last = p_I->prev_ptr; + } + + p_I->prev_ptr->next_ptr = p_I->next_ptr; + + if (p_I->next_ptr) { + p_I->next_ptr->prev_ptr = p_I->prev_ptr; + } + + _data->first->prev_ptr = p_I; + p_I->next_ptr = _data->first; + p_I->prev_ptr = nullptr; + _data->first = p_I; + } + + void move_before(Element *value, Element *where) { + if (value->prev_ptr) { + value->prev_ptr->next_ptr = value->next_ptr; + } else { + _data->first = value->next_ptr; + } + if (value->next_ptr) { + value->next_ptr->prev_ptr = value->prev_ptr; + } else { + _data->last = value->prev_ptr; + } + + value->next_ptr = where; + if (!where) { + value->prev_ptr = _data->last; + _data->last = value; + return; + }; + + value->prev_ptr = where->prev_ptr; + + if (where->prev_ptr) { + where->prev_ptr->next_ptr = value; + } else { + _data->first = value; + }; + + where->prev_ptr = value; + }; + + /** + * simple insertion sort + */ + + void sort() { + sort_custom>(); + } + + template + void sort_custom_inplace() { + if (size() < 2) { + return; + } + + Element *from = front(); + Element *current = from; + Element *to = from; + + while (current) { + Element *next = current->next_ptr; + + if (from != current) { + current->prev_ptr = NULL; + current->next_ptr = from; + + Element *find = from; + C less; + while (find && less(find->value, current->value)) { + current->prev_ptr = find; + current->next_ptr = find->next_ptr; + find = find->next_ptr; + } + + if (current->prev_ptr) { + current->prev_ptr->next_ptr = current; + } else { + from = current; + } + + if (current->next_ptr) { + current->next_ptr->prev_ptr = current; + } else { + to = current; + } + } else { + current->prev_ptr = NULL; + current->next_ptr = NULL; + } + + current = next; + } + _data->first = from; + _data->last = to; + } + + template + struct AuxiliaryComparator { + C compare; + _FORCE_INLINE_ bool operator()(const Element *a, const Element *b) const { + return compare(a->value, b->value); + } + }; + + template + void sort_custom() { + //this version uses auxiliary memory for speed. + //if you don't want to use auxiliary memory, use the in_place version + + int s = size(); + if (s < 2) { + return; + } + + Element **aux_buffer = memnew_arr(Element *, s); + + int idx = 0; + for (Element *E = front(); E; E = E->next_ptr) { + aux_buffer[idx] = E; + idx++; + } + + SortArray> sort; + sort.sort(aux_buffer, s); + + _data->first = aux_buffer[0]; + aux_buffer[0]->prev_ptr = nullptr; + aux_buffer[0]->next_ptr = aux_buffer[1]; + + _data->last = aux_buffer[s - 1]; + aux_buffer[s - 1]->prev_ptr = aux_buffer[s - 2]; + aux_buffer[s - 1]->next_ptr = nullptr; + + for (int i = 1; i < s - 1; i++) { + aux_buffer[i]->prev_ptr = aux_buffer[i - 1]; + aux_buffer[i]->next_ptr = aux_buffer[i + 1]; + } + + memdelete_arr(aux_buffer); + } + + const void *id() const { + return (void *)_data; + } + + /** + * copy constructor for the list + */ + List(const List &p_list) { + _data = nullptr; + const Element *it = p_list.front(); + while (it) { + push_back(it->get()); + it = it->next(); + } + } + + List() { + _data = nullptr; + }; + ~List() { + clear(); + if (_data) { + ERR_FAIL_COND(_data->size_cache); + memdelete_allocator<_Data, A>(_data); + } + }; +}; + +#endif +#line 0 + +#line 1 "sfwl/core/ring_buffer.h" +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +/*************************************************************************/ +/* ring_buffer.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +template +class RingBuffer { + Vector data; + int read_pos; + int write_pos; + int size_mask; + + inline int inc(int &p_var, int p_size) const { + int ret = p_var; + p_var += p_size; + p_var = p_var & size_mask; + return ret; + }; + +public: + T read() { + ERR_FAIL_COND_V(space_left() < 1, T()); + return data.ptr()[inc(read_pos, 1)]; + }; + + int read(T *p_buf, int p_size, bool p_advance = true) { + int left = data_left(); + p_size = MIN(left, p_size); + int pos = read_pos; + int to_read = p_size; + int dst = 0; + while (to_read) { + int end = pos + to_read; + end = MIN(end, size()); + int total = end - pos; + const T *read = data.ptr(); + for (int i = 0; i < total; i++) { + p_buf[dst++] = read[pos + i]; + }; + to_read -= total; + pos = 0; + }; + if (p_advance) { + inc(read_pos, p_size); + }; + return p_size; + }; + + int copy(T *p_buf, int p_offset, int p_size) const { + int left = data_left(); + if ((p_offset + p_size) > left) { + p_size -= left - p_offset; + if (p_size <= 0) { + return 0; + } + } + p_size = MIN(left, p_size); + int pos = read_pos; + inc(pos, p_offset); + int to_read = p_size; + int dst = 0; + while (to_read) { + int end = pos + to_read; + end = MIN(end, size()); + int total = end - pos; + for (int i = 0; i < total; i++) { + p_buf[dst++] = data[pos + i]; + }; + to_read -= total; + pos = 0; + }; + return p_size; + }; + + int find(const T &t, int p_offset, int p_max_size) const { + int left = data_left(); + if ((p_offset + p_max_size) > left) { + p_max_size -= left - p_offset; + if (p_max_size <= 0) { + return 0; + } + } + p_max_size = MIN(left, p_max_size); + int pos = read_pos; + inc(pos, p_offset); + int to_read = p_max_size; + while (to_read) { + int end = pos + to_read; + end = MIN(end, size()); + int total = end - pos; + for (int i = 0; i < total; i++) { + if (data[pos + i] == t) { + return i + (p_max_size - to_read); + } + }; + to_read -= total; + pos = 0; + } + return -1; + } + + inline int advance_read(int p_n) { + p_n = MIN(p_n, data_left()); + inc(read_pos, p_n); + return p_n; + }; + + inline int decrease_write(int p_n) { + p_n = MIN(p_n, data_left()); + inc(write_pos, size_mask + 1 - p_n); + return p_n; + } + + Error write(const T &p_v) { + ERR_FAIL_COND_V(space_left() < 1, FAILED); + data.write[inc(write_pos, 1)] = p_v; + return OK; + }; + + int write(const T *p_buf, int p_size) { + int left = space_left(); + p_size = MIN(left, p_size); + + int pos = write_pos; + int to_write = p_size; + int src = 0; + while (to_write) { + int end = pos + to_write; + end = MIN(end, size()); + int total = end - pos; + + for (int i = 0; i < total; i++) { + data.write[pos + i] = p_buf[src++]; + }; + to_write -= total; + pos = 0; + }; + + inc(write_pos, p_size); + return p_size; + }; + + inline int space_left() const { + int left = read_pos - write_pos; + if (left < 0) { + return size() + left - 1; + }; + if (left == 0) { + return size() - 1; + }; + return left - 1; + }; + inline int data_left() const { + return size() - space_left() - 1; + }; + + inline int size() const { + return data.size(); + }; + + inline void clear() { + read_pos = 0; + write_pos = 0; + } + + void resize(int p_power) { + int old_size = size(); + int new_size = 1 << p_power; + int mask = new_size - 1; + data.resize(1 << p_power); + if (old_size < new_size && read_pos > write_pos) { + for (int i = 0; i < write_pos; i++) { + data.write[(old_size + i) & mask] = data[i]; + }; + write_pos = (old_size + write_pos) & mask; + } else { + read_pos = read_pos & mask; + write_pos = write_pos & mask; + }; + + size_mask = mask; + }; + + RingBuffer(int p_power = 0) { + read_pos = 0; + write_pos = 0; + resize(p_power); + }; + ~RingBuffer(){}; +}; + +#endif +#line 0 + +#line 1 "sfwl/core/paged_allocator.h" +#ifndef PAGED_ALLOCATOR_H +#define PAGED_ALLOCATOR_H + +/*************************************************************************/ +/* paged_allocator.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +template +class PagedAllocator { + T **page_pool = nullptr; + T ***available_pool = nullptr; + uint32_t pages_allocated = 0; + uint32_t allocs_available = 0; + + uint32_t page_shift = 0; + uint32_t page_mask = 0; + uint32_t page_size = 0; + SpinLock spin_lock; + +public: + T *alloc() { + if (thread_safe) { + spin_lock.lock(); + } + if (unlikely(allocs_available == 0)) { + uint32_t pages_used = pages_allocated; + + pages_allocated++; + page_pool = (T **)memrealloc(page_pool, sizeof(T *) * pages_allocated); + available_pool = (T ***)memrealloc(available_pool, sizeof(T **) * pages_allocated); + + page_pool[pages_used] = (T *)memalloc(sizeof(T) * page_size); + available_pool[pages_used] = (T **)memalloc(sizeof(T *) * page_size); + + for (uint32_t i = 0; i < page_size; i++) { + available_pool[0][i] = &page_pool[pages_used][i]; + } + allocs_available += page_size; + } + + allocs_available--; + T *alloc = available_pool[allocs_available >> page_shift][allocs_available & page_mask]; + if (thread_safe) { + spin_lock.unlock(); + } + memnew_placement(alloc, T); + return alloc; + } + + void free(T *p_mem) { + if (thread_safe) { + spin_lock.lock(); + } + p_mem->~T(); + available_pool[allocs_available >> page_shift][allocs_available & page_mask] = p_mem; + if (thread_safe) { + spin_lock.unlock(); + } + allocs_available++; + } + + void reset(bool p_allow_unfreed = false) { + if (!p_allow_unfreed || !HAS_TRIVIAL_DESTRUCTOR(T)) { + ERR_FAIL_COND(allocs_available < pages_allocated * page_size); + } + if (pages_allocated) { + for (uint32_t i = 0; i < pages_allocated; i++) { + memfree(page_pool[i]); + memfree(available_pool[i]); + } + memfree(page_pool); + memfree(available_pool); + page_pool = nullptr; + available_pool = nullptr; + pages_allocated = 0; + allocs_available = 0; + } + } + bool is_configured() const { + return page_size > 0; + } + + void configure(uint32_t p_page_size) { + ERR_FAIL_COND(page_pool != nullptr); //sanity check + ERR_FAIL_COND(p_page_size == 0); + page_size = nearest_power_of_2_templated(p_page_size); + page_mask = page_size - 1; + page_shift = get_shift_from_power_of_2(page_size); + } + + // Power of 2 recommended because of alignment with OS page sizes. + // Even if element is bigger, its still a multiple and get rounded amount of pages + PagedAllocator(uint32_t p_page_size = 4096) { + configure(p_page_size); + } + + ~PagedAllocator() { + ERR_FAIL_COND_MSG(allocs_available < pages_allocated * page_size, "Pages in use exist at exit in PagedAllocator"); + reset(); + } +}; + +#endif // PAGED_ALLOCATOR_H +#line 0 + +#line 1 "sfwl/core/pool_allocator.h" +#ifndef POOL_ALLOCATOR_H +#define POOL_ALLOCATOR_H + +/*************************************************************************/ +/* pool_allocator.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +/** + @author Juan Linietsky + * Generic Pool Allocator. + * This is a generic memory pool allocator, with locking, compacting and alignment. (@TODO alignment) + * It used as a standard way to manage allocation in a specific region of memory, such as texture memory, + * audio sample memory, or just any kind of memory overall. + * (@TODO) abstraction should be greater, because in many platforms, you need to manage a nonreachable memory. +*/ + +enum { + + POOL_ALLOCATOR_INVALID_ID = -1 ///< default invalid value. use INVALID_ID( id ) to test +}; + +class PoolAllocator { +public: + typedef int ID; + +private: + enum { + CHECK_BITS = 8, + CHECK_LEN = (1 << CHECK_BITS), + CHECK_MASK = CHECK_LEN - 1 + + }; + + struct Entry { + unsigned int pos; + unsigned int len; + unsigned int lock; + unsigned int check; + + inline void clear() { + pos = 0; + len = 0; + lock = 0; + check = 0; + } + Entry() { clear(); } + }; + + typedef int EntryArrayPos; + typedef int EntryIndicesPos; + + Entry *entry_array; + int *entry_indices; + int entry_max; + int entry_count; + + uint8_t *pool; + void *mem_ptr; + int pool_size; + + int free_mem; + int free_mem_peak; + + unsigned int check_count; + int align; + + bool needs_locking; + + inline int entry_end(const Entry &p_entry) const { + return p_entry.pos + aligned(p_entry.len); + } + inline int aligned(int p_size) const { + int rem = p_size % align; + if (rem) { + p_size += align - rem; + } + + return p_size; + } + + void compact(int p_up_to = -1); + void compact_up(int p_from = 0); + bool get_free_entry(EntryArrayPos *p_pos); + bool find_hole(EntryArrayPos *p_pos, int p_for_size); + bool find_entry_index(EntryIndicesPos *p_map_pos, Entry *p_entry); + Entry *get_entry(ID p_mem); + const Entry *get_entry(ID p_mem) const; + + void create_pool(void *p_mem, int p_size, int p_max_entries); + +protected: + virtual void mt_lock() const; ///< Reimplement for custom mt locking + virtual void mt_unlock() const; ///< Reimplement for custom mt locking + +public: + enum { + DEFAULT_MAX_ALLOCS = 4096, + }; + + ID alloc(int p_size); ///< Alloc memory, get an ID on success, POOL_ALOCATOR_INVALID_ID on failure + void free(ID p_mem); ///< Free allocated memory + Error resize(ID p_mem, int p_new_size); ///< resize a memory chunk + int get_size(ID p_mem) const; + + int get_free_mem(); ///< get free memory + int get_used_mem() const; + int get_free_peak(); ///< get free memory + + Error lock(ID p_mem); //@todo move this out + void *get(ID p_mem); + const void *get(ID p_mem) const; + void unlock(ID p_mem); + bool is_locked(ID p_mem) const; + + PoolAllocator(int p_size, bool p_needs_locking = false, int p_max_entries = DEFAULT_MAX_ALLOCS); + PoolAllocator(void *p_mem, int p_size, int p_align = 1, bool p_needs_locking = false, int p_max_entries = DEFAULT_MAX_ALLOCS); + PoolAllocator(int p_align, int p_size, bool p_needs_locking = false, int p_max_entries = DEFAULT_MAX_ALLOCS); + + virtual ~PoolAllocator(); +}; + +#endif +#line 0 + + + + +#line 1 "sfwl/core/char_range.inc" +/*************************************************************************/ +/* char_range.inc */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#ifndef CHAR_RANGE_INC +#define CHAR_RANGE_INC + + + +struct CharRange { + char32_t start; + char32_t end; +}; + +static CharRange xid_start[] = { + { 0x41, 0x5a }, + { 0x5f, 0x5f }, + { 0x61, 0x7a }, + { 0xaa, 0xaa }, + { 0xb5, 0xb5 }, + { 0xba, 0xba }, + { 0xc0, 0xd6 }, + { 0xd8, 0xf6 }, + { 0xf8, 0x2c1 }, + { 0x2c6, 0x2d1 }, + { 0x2e0, 0x2e4 }, + { 0x2ec, 0x2ec }, + { 0x2ee, 0x2ee }, + { 0x370, 0x374 }, + { 0x376, 0x377 }, + { 0x37a, 0x37d }, + { 0x37f, 0x37f }, + { 0x386, 0x386 }, + { 0x388, 0x38a }, + { 0x38c, 0x38c }, + { 0x38e, 0x3a1 }, + { 0x3a3, 0x3f5 }, + { 0x3f7, 0x481 }, + { 0x48a, 0x52f }, + { 0x531, 0x556 }, + { 0x559, 0x559 }, + { 0x560, 0x588 }, + { 0x5d0, 0x5ea }, + { 0x5ef, 0x5f2 }, + { 0x620, 0x64a }, + { 0x66e, 0x66f }, + { 0x671, 0x6d3 }, + { 0x6d5, 0x6d5 }, + { 0x6e5, 0x6e6 }, + { 0x6ee, 0x6ef }, + { 0x6fa, 0x6fc }, + { 0x6ff, 0x6ff }, + { 0x710, 0x710 }, + { 0x712, 0x72f }, + { 0x74d, 0x7a5 }, + { 0x7b1, 0x7b1 }, + { 0x7ca, 0x7ea }, + { 0x7f4, 0x7f5 }, + { 0x7fa, 0x7fa }, + { 0x800, 0x815 }, + { 0x81a, 0x81a }, + { 0x824, 0x824 }, + { 0x828, 0x828 }, + { 0x840, 0x858 }, + { 0x860, 0x86a }, + { 0x870, 0x887 }, + { 0x889, 0x88e }, + { 0x8a0, 0x8c9 }, + { 0x904, 0x939 }, + { 0x93d, 0x93d }, + { 0x950, 0x950 }, + { 0x958, 0x961 }, + { 0x971, 0x980 }, + { 0x985, 0x98c }, + { 0x98f, 0x990 }, + { 0x993, 0x9a8 }, + { 0x9aa, 0x9b0 }, + { 0x9b2, 0x9b2 }, + { 0x9b6, 0x9b9 }, + { 0x9bd, 0x9bd }, + { 0x9ce, 0x9ce }, + { 0x9dc, 0x9dd }, + { 0x9df, 0x9e1 }, + { 0x9f0, 0x9f1 }, + { 0x9fc, 0x9fc }, + { 0xa05, 0xa0a }, + { 0xa0f, 0xa10 }, + { 0xa13, 0xa28 }, + { 0xa2a, 0xa30 }, + { 0xa32, 0xa33 }, + { 0xa35, 0xa36 }, + { 0xa38, 0xa39 }, + { 0xa59, 0xa5c }, + { 0xa5e, 0xa5e }, + { 0xa72, 0xa74 }, + { 0xa85, 0xa8d }, + { 0xa8f, 0xa91 }, + { 0xa93, 0xaa8 }, + { 0xaaa, 0xab0 }, + { 0xab2, 0xab3 }, + { 0xab5, 0xab9 }, + { 0xabd, 0xabd }, + { 0xad0, 0xad0 }, + { 0xae0, 0xae1 }, + { 0xaf9, 0xaf9 }, + { 0xb05, 0xb0c }, + { 0xb0f, 0xb10 }, + { 0xb13, 0xb28 }, + { 0xb2a, 0xb30 }, + { 0xb32, 0xb33 }, + { 0xb35, 0xb39 }, + { 0xb3d, 0xb3d }, + { 0xb5c, 0xb5d }, + { 0xb5f, 0xb61 }, + { 0xb71, 0xb71 }, + { 0xb83, 0xb83 }, + { 0xb85, 0xb8a }, + { 0xb8e, 0xb90 }, + { 0xb92, 0xb95 }, + { 0xb99, 0xb9a }, + { 0xb9c, 0xb9c }, + { 0xb9e, 0xb9f }, + { 0xba3, 0xba4 }, + { 0xba8, 0xbaa }, + { 0xbae, 0xbb9 }, + { 0xbd0, 0xbd0 }, + { 0xc05, 0xc0c }, + { 0xc0e, 0xc10 }, + { 0xc12, 0xc28 }, + { 0xc2a, 0xc39 }, + { 0xc3d, 0xc3d }, + { 0xc58, 0xc5a }, + { 0xc5d, 0xc5d }, + { 0xc60, 0xc61 }, + { 0xc80, 0xc80 }, + { 0xc85, 0xc8c }, + { 0xc8e, 0xc90 }, + { 0xc92, 0xca8 }, + { 0xcaa, 0xcb3 }, + { 0xcb5, 0xcb9 }, + { 0xcbd, 0xcbd }, + { 0xcdd, 0xcde }, + { 0xce0, 0xce1 }, + { 0xcf1, 0xcf2 }, + { 0xd04, 0xd0c }, + { 0xd0e, 0xd10 }, + { 0xd12, 0xd3a }, + { 0xd3d, 0xd3d }, + { 0xd4e, 0xd4e }, + { 0xd54, 0xd56 }, + { 0xd5f, 0xd61 }, + { 0xd7a, 0xd7f }, + { 0xd85, 0xd96 }, + { 0xd9a, 0xdb1 }, + { 0xdb3, 0xdbb }, + { 0xdbd, 0xdbd }, + { 0xdc0, 0xdc6 }, + { 0xe01, 0xe30 }, + { 0xe32, 0xe33 }, + { 0xe40, 0xe46 }, + { 0xe81, 0xe82 }, + { 0xe84, 0xe84 }, + { 0xe86, 0xe8a }, + { 0xe8c, 0xea3 }, + { 0xea5, 0xea5 }, + { 0xea7, 0xeb0 }, + { 0xeb2, 0xeb3 }, + { 0xebd, 0xebd }, + { 0xec0, 0xec4 }, + { 0xec6, 0xec6 }, + { 0xedc, 0xedf }, + { 0xf00, 0xf00 }, + { 0xf40, 0xf47 }, + { 0xf49, 0xf6c }, + { 0xf88, 0xf8c }, + { 0x1000, 0x102a }, + { 0x103f, 0x103f }, + { 0x1050, 0x1055 }, + { 0x105a, 0x105d }, + { 0x1061, 0x1061 }, + { 0x1065, 0x1066 }, + { 0x106e, 0x1070 }, + { 0x1075, 0x1081 }, + { 0x108e, 0x108e }, + { 0x10a0, 0x10c5 }, + { 0x10c7, 0x10c7 }, + { 0x10cd, 0x10cd }, + { 0x10d0, 0x10fa }, + { 0x10fc, 0x1248 }, + { 0x124a, 0x124d }, + { 0x1250, 0x1256 }, + { 0x1258, 0x1258 }, + { 0x125a, 0x125d }, + { 0x1260, 0x1288 }, + { 0x128a, 0x128d }, + { 0x1290, 0x12b0 }, + { 0x12b2, 0x12b5 }, + { 0x12b8, 0x12be }, + { 0x12c0, 0x12c0 }, + { 0x12c2, 0x12c5 }, + { 0x12c8, 0x12d6 }, + { 0x12d8, 0x1310 }, + { 0x1312, 0x1315 }, + { 0x1318, 0x135a }, + { 0x1380, 0x138f }, + { 0x13a0, 0x13f5 }, + { 0x13f8, 0x13fd }, + { 0x1401, 0x166c }, + { 0x166f, 0x167f }, + { 0x1681, 0x169a }, + { 0x16a0, 0x16ea }, + { 0x16ee, 0x16f8 }, + { 0x1700, 0x1711 }, + { 0x171f, 0x1731 }, + { 0x1740, 0x1751 }, + { 0x1760, 0x176c }, + { 0x176e, 0x1770 }, + { 0x1780, 0x17b3 }, + { 0x17d7, 0x17d7 }, + { 0x17dc, 0x17dc }, + { 0x1820, 0x1878 }, + { 0x1880, 0x1884 }, + { 0x1887, 0x18a8 }, + { 0x18aa, 0x18aa }, + { 0x18b0, 0x18f5 }, + { 0x1900, 0x191e }, + { 0x1950, 0x196d }, + { 0x1970, 0x1974 }, + { 0x1980, 0x19ab }, + { 0x19b0, 0x19c9 }, + { 0x1a00, 0x1a16 }, + { 0x1a20, 0x1a54 }, + { 0x1aa7, 0x1aa7 }, + { 0x1b05, 0x1b33 }, + { 0x1b45, 0x1b4c }, + { 0x1b83, 0x1ba0 }, + { 0x1bae, 0x1baf }, + { 0x1bba, 0x1be5 }, + { 0x1c00, 0x1c23 }, + { 0x1c4d, 0x1c4f }, + { 0x1c5a, 0x1c7d }, + { 0x1c80, 0x1c88 }, + { 0x1c90, 0x1cba }, + { 0x1cbd, 0x1cbf }, + { 0x1ce9, 0x1cec }, + { 0x1cee, 0x1cf3 }, + { 0x1cf5, 0x1cf6 }, + { 0x1cfa, 0x1cfa }, + { 0x1d00, 0x1dbf }, + { 0x1e00, 0x1f15 }, + { 0x1f18, 0x1f1d }, + { 0x1f20, 0x1f45 }, + { 0x1f48, 0x1f4d }, + { 0x1f50, 0x1f57 }, + { 0x1f59, 0x1f59 }, + { 0x1f5b, 0x1f5b }, + { 0x1f5d, 0x1f5d }, + { 0x1f5f, 0x1f7d }, + { 0x1f80, 0x1fb4 }, + { 0x1fb6, 0x1fbc }, + { 0x1fbe, 0x1fbe }, + { 0x1fc2, 0x1fc4 }, + { 0x1fc6, 0x1fcc }, + { 0x1fd0, 0x1fd3 }, + { 0x1fd6, 0x1fdb }, + { 0x1fe0, 0x1fec }, + { 0x1ff2, 0x1ff4 }, + { 0x1ff6, 0x1ffc }, + { 0x2071, 0x2071 }, + { 0x207f, 0x207f }, + { 0x2090, 0x209c }, + { 0x2102, 0x2102 }, + { 0x2107, 0x2107 }, + { 0x210a, 0x2113 }, + { 0x2115, 0x2115 }, + { 0x2118, 0x211d }, + { 0x2124, 0x2124 }, + { 0x2126, 0x2126 }, + { 0x2128, 0x2128 }, + { 0x212a, 0x2139 }, + { 0x213c, 0x213f }, + { 0x2145, 0x2149 }, + { 0x214e, 0x214e }, + { 0x2160, 0x2188 }, + { 0x2c00, 0x2ce4 }, + { 0x2ceb, 0x2cee }, + { 0x2cf2, 0x2cf3 }, + { 0x2d00, 0x2d25 }, + { 0x2d27, 0x2d27 }, + { 0x2d2d, 0x2d2d }, + { 0x2d30, 0x2d67 }, + { 0x2d6f, 0x2d6f }, + { 0x2d80, 0x2d96 }, + { 0x2da0, 0x2da6 }, + { 0x2da8, 0x2dae }, + { 0x2db0, 0x2db6 }, + { 0x2db8, 0x2dbe }, + { 0x2dc0, 0x2dc6 }, + { 0x2dc8, 0x2dce }, + { 0x2dd0, 0x2dd6 }, + { 0x2dd8, 0x2dde }, + { 0x3005, 0x3007 }, + { 0x3021, 0x3029 }, + { 0x3031, 0x3035 }, + { 0x3038, 0x303c }, + { 0x3041, 0x3096 }, + { 0x309b, 0x309f }, + { 0x30a1, 0x30fa }, + { 0x30fc, 0x30ff }, + { 0x3105, 0x312f }, + { 0x3131, 0x318e }, + { 0x31a0, 0x31bf }, + { 0x31f0, 0x31ff }, + { 0x3400, 0x4dbf }, + { 0x4e00, 0xa48c }, + { 0xa4d0, 0xa4fd }, + { 0xa500, 0xa60c }, + { 0xa610, 0xa61f }, + { 0xa62a, 0xa62b }, + { 0xa640, 0xa66e }, + { 0xa67f, 0xa69d }, + { 0xa6a0, 0xa6ef }, + { 0xa717, 0xa71f }, + { 0xa722, 0xa788 }, + { 0xa78b, 0xa7ca }, + { 0xa7d0, 0xa7d1 }, + { 0xa7d3, 0xa7d3 }, + { 0xa7d5, 0xa7d9 }, + { 0xa7f2, 0xa801 }, + { 0xa803, 0xa805 }, + { 0xa807, 0xa80a }, + { 0xa80c, 0xa822 }, + { 0xa840, 0xa873 }, + { 0xa882, 0xa8b3 }, + { 0xa8f2, 0xa8f7 }, + { 0xa8fb, 0xa8fb }, + { 0xa8fd, 0xa8fe }, + { 0xa90a, 0xa925 }, + { 0xa930, 0xa946 }, + { 0xa960, 0xa97c }, + { 0xa984, 0xa9b2 }, + { 0xa9cf, 0xa9cf }, + { 0xa9e0, 0xa9e4 }, + { 0xa9e6, 0xa9ef }, + { 0xa9fa, 0xa9fe }, + { 0xaa00, 0xaa28 }, + { 0xaa40, 0xaa42 }, + { 0xaa44, 0xaa4b }, + { 0xaa60, 0xaa76 }, + { 0xaa7a, 0xaa7a }, + { 0xaa7e, 0xaaaf }, + { 0xaab1, 0xaab1 }, + { 0xaab5, 0xaab6 }, + { 0xaab9, 0xaabd }, + { 0xaac0, 0xaac0 }, + { 0xaac2, 0xaac2 }, + { 0xaadb, 0xaadd }, + { 0xaae0, 0xaaea }, + { 0xaaf2, 0xaaf4 }, + { 0xab01, 0xab06 }, + { 0xab09, 0xab0e }, + { 0xab11, 0xab16 }, + { 0xab20, 0xab26 }, + { 0xab28, 0xab2e }, + { 0xab30, 0xab5a }, + { 0xab5c, 0xab69 }, + { 0xab70, 0xabe2 }, + { 0xac00, 0xd7a3 }, + { 0xd7b0, 0xd7c6 }, + { 0xd7cb, 0xd7fb }, + { 0xf900, 0xfa6d }, + { 0xfa70, 0xfad9 }, + { 0xfb00, 0xfb06 }, + { 0xfb13, 0xfb17 }, + { 0xfb1d, 0xfb1d }, + { 0xfb1f, 0xfb28 }, + { 0xfb2a, 0xfb36 }, + { 0xfb38, 0xfb3c }, + { 0xfb3e, 0xfb3e }, + { 0xfb40, 0xfb41 }, + { 0xfb43, 0xfb44 }, + { 0xfb46, 0xfbb1 }, + { 0xfbd3, 0xfd3d }, + { 0xfd50, 0xfd8f }, + { 0xfd92, 0xfdc7 }, + { 0xfdf0, 0xfdfb }, + { 0xfe70, 0xfe74 }, + { 0xfe76, 0xfefc }, + { 0xff21, 0xff3a }, + { 0xff41, 0xff5a }, + { 0xff66, 0xffbe }, + { 0xffc2, 0xffc7 }, + { 0xffca, 0xffcf }, + { 0xffd2, 0xffd7 }, + { 0xffda, 0xffdc }, + { 0x10000, 0x1000b }, + { 0x1000d, 0x10026 }, + { 0x10028, 0x1003a }, + { 0x1003c, 0x1003d }, + { 0x1003f, 0x1004d }, + { 0x10050, 0x1005d }, + { 0x10080, 0x100fa }, + { 0x10140, 0x10174 }, + { 0x10280, 0x1029c }, + { 0x102a0, 0x102d0 }, + { 0x10300, 0x1031f }, + { 0x1032d, 0x1034a }, + { 0x10350, 0x10375 }, + { 0x10380, 0x1039d }, + { 0x103a0, 0x103c3 }, + { 0x103c8, 0x103cf }, + { 0x103d1, 0x103d5 }, + { 0x10400, 0x1049d }, + { 0x104b0, 0x104d3 }, + { 0x104d8, 0x104fb }, + { 0x10500, 0x10527 }, + { 0x10530, 0x10563 }, + { 0x10570, 0x1057a }, + { 0x1057c, 0x1058a }, + { 0x1058c, 0x10592 }, + { 0x10594, 0x10595 }, + { 0x10597, 0x105a1 }, + { 0x105a3, 0x105b1 }, + { 0x105b3, 0x105b9 }, + { 0x105bb, 0x105bc }, + { 0x10600, 0x10736 }, + { 0x10740, 0x10755 }, + { 0x10760, 0x10767 }, + { 0x10780, 0x10785 }, + { 0x10787, 0x107b0 }, + { 0x107b2, 0x107ba }, + { 0x10800, 0x10805 }, + { 0x10808, 0x10808 }, + { 0x1080a, 0x10835 }, + { 0x10837, 0x10838 }, + { 0x1083c, 0x1083c }, + { 0x1083f, 0x10855 }, + { 0x10860, 0x10876 }, + { 0x10880, 0x1089e }, + { 0x108e0, 0x108f2 }, + { 0x108f4, 0x108f5 }, + { 0x10900, 0x10915 }, + { 0x10920, 0x10939 }, + { 0x10980, 0x109b7 }, + { 0x109be, 0x109bf }, + { 0x10a00, 0x10a00 }, + { 0x10a10, 0x10a13 }, + { 0x10a15, 0x10a17 }, + { 0x10a19, 0x10a35 }, + { 0x10a60, 0x10a7c }, + { 0x10a80, 0x10a9c }, + { 0x10ac0, 0x10ac7 }, + { 0x10ac9, 0x10ae4 }, + { 0x10b00, 0x10b35 }, + { 0x10b40, 0x10b55 }, + { 0x10b60, 0x10b72 }, + { 0x10b80, 0x10b91 }, + { 0x10c00, 0x10c48 }, + { 0x10c80, 0x10cb2 }, + { 0x10cc0, 0x10cf2 }, + { 0x10d00, 0x10d23 }, + { 0x10e80, 0x10ea9 }, + { 0x10eb0, 0x10eb1 }, + { 0x10f00, 0x10f1c }, + { 0x10f27, 0x10f27 }, + { 0x10f30, 0x10f45 }, + { 0x10f70, 0x10f81 }, + { 0x10fb0, 0x10fc4 }, + { 0x10fe0, 0x10ff6 }, + { 0x11003, 0x11037 }, + { 0x11071, 0x11072 }, + { 0x11075, 0x11075 }, + { 0x11083, 0x110af }, + { 0x110d0, 0x110e8 }, + { 0x11103, 0x11126 }, + { 0x11144, 0x11144 }, + { 0x11147, 0x11147 }, + { 0x11150, 0x11172 }, + { 0x11176, 0x11176 }, + { 0x11183, 0x111b2 }, + { 0x111c1, 0x111c4 }, + { 0x111da, 0x111da }, + { 0x111dc, 0x111dc }, + { 0x11200, 0x11211 }, + { 0x11213, 0x1122b }, + { 0x11280, 0x11286 }, + { 0x11288, 0x11288 }, + { 0x1128a, 0x1128d }, + { 0x1128f, 0x1129d }, + { 0x1129f, 0x112a8 }, + { 0x112b0, 0x112de }, + { 0x11305, 0x1130c }, + { 0x1130f, 0x11310 }, + { 0x11313, 0x11328 }, + { 0x1132a, 0x11330 }, + { 0x11332, 0x11333 }, + { 0x11335, 0x11339 }, + { 0x1133d, 0x1133d }, + { 0x11350, 0x11350 }, + { 0x1135d, 0x11361 }, + { 0x11400, 0x11434 }, + { 0x11447, 0x1144a }, + { 0x1145f, 0x11461 }, + { 0x11480, 0x114af }, + { 0x114c4, 0x114c5 }, + { 0x114c7, 0x114c7 }, + { 0x11580, 0x115ae }, + { 0x115d8, 0x115db }, + { 0x11600, 0x1162f }, + { 0x11644, 0x11644 }, + { 0x11680, 0x116aa }, + { 0x116b8, 0x116b8 }, + { 0x11700, 0x1171a }, + { 0x11740, 0x11746 }, + { 0x11800, 0x1182b }, + { 0x118a0, 0x118df }, + { 0x118ff, 0x11906 }, + { 0x11909, 0x11909 }, + { 0x1190c, 0x11913 }, + { 0x11915, 0x11916 }, + { 0x11918, 0x1192f }, + { 0x1193f, 0x1193f }, + { 0x11941, 0x11941 }, + { 0x119a0, 0x119a7 }, + { 0x119aa, 0x119d0 }, + { 0x119e1, 0x119e1 }, + { 0x119e3, 0x119e3 }, + { 0x11a00, 0x11a00 }, + { 0x11a0b, 0x11a32 }, + { 0x11a3a, 0x11a3a }, + { 0x11a50, 0x11a50 }, + { 0x11a5c, 0x11a89 }, + { 0x11a9d, 0x11a9d }, + { 0x11ab0, 0x11af8 }, + { 0x11c00, 0x11c08 }, + { 0x11c0a, 0x11c2e }, + { 0x11c40, 0x11c40 }, + { 0x11c72, 0x11c8f }, + { 0x11d00, 0x11d06 }, + { 0x11d08, 0x11d09 }, + { 0x11d0b, 0x11d30 }, + { 0x11d46, 0x11d46 }, + { 0x11d60, 0x11d65 }, + { 0x11d67, 0x11d68 }, + { 0x11d6a, 0x11d89 }, + { 0x11d98, 0x11d98 }, + { 0x11ee0, 0x11ef2 }, + { 0x11fb0, 0x11fb0 }, + { 0x12000, 0x12399 }, + { 0x12400, 0x1246e }, + { 0x12480, 0x12543 }, + { 0x12f90, 0x12ff0 }, + { 0x13000, 0x1342e }, + { 0x14400, 0x14646 }, + { 0x16800, 0x16a38 }, + { 0x16a40, 0x16a5e }, + { 0x16a70, 0x16abe }, + { 0x16ad0, 0x16aed }, + { 0x16b00, 0x16b2f }, + { 0x16b40, 0x16b43 }, + { 0x16b63, 0x16b77 }, + { 0x16b7d, 0x16b8f }, + { 0x16e40, 0x16e7f }, + { 0x16f00, 0x16f4a }, + { 0x16f50, 0x16f50 }, + { 0x16f93, 0x16f9f }, + { 0x16fe0, 0x16fe1 }, + { 0x16fe3, 0x16fe3 }, + { 0x17000, 0x187f7 }, + { 0x18800, 0x18cd5 }, + { 0x18d00, 0x18d08 }, + { 0x1aff0, 0x1aff3 }, + { 0x1aff5, 0x1affb }, + { 0x1affd, 0x1affe }, + { 0x1b000, 0x1b122 }, + { 0x1b150, 0x1b152 }, + { 0x1b164, 0x1b167 }, + { 0x1b170, 0x1b2fb }, + { 0x1bc00, 0x1bc6a }, + { 0x1bc70, 0x1bc7c }, + { 0x1bc80, 0x1bc88 }, + { 0x1bc90, 0x1bc99 }, + { 0x1d400, 0x1d454 }, + { 0x1d456, 0x1d49c }, + { 0x1d49e, 0x1d49f }, + { 0x1d4a2, 0x1d4a2 }, + { 0x1d4a5, 0x1d4a6 }, + { 0x1d4a9, 0x1d4ac }, + { 0x1d4ae, 0x1d4b9 }, + { 0x1d4bb, 0x1d4bb }, + { 0x1d4bd, 0x1d4c3 }, + { 0x1d4c5, 0x1d505 }, + { 0x1d507, 0x1d50a }, + { 0x1d50d, 0x1d514 }, + { 0x1d516, 0x1d51c }, + { 0x1d51e, 0x1d539 }, + { 0x1d53b, 0x1d53e }, + { 0x1d540, 0x1d544 }, + { 0x1d546, 0x1d546 }, + { 0x1d54a, 0x1d550 }, + { 0x1d552, 0x1d6a5 }, + { 0x1d6a8, 0x1d6c0 }, + { 0x1d6c2, 0x1d6da }, + { 0x1d6dc, 0x1d6fa }, + { 0x1d6fc, 0x1d714 }, + { 0x1d716, 0x1d734 }, + { 0x1d736, 0x1d74e }, + { 0x1d750, 0x1d76e }, + { 0x1d770, 0x1d788 }, + { 0x1d78a, 0x1d7a8 }, + { 0x1d7aa, 0x1d7c2 }, + { 0x1d7c4, 0x1d7cb }, + { 0x1df00, 0x1df1e }, + { 0x1e100, 0x1e12c }, + { 0x1e137, 0x1e13d }, + { 0x1e14e, 0x1e14e }, + { 0x1e290, 0x1e2ad }, + { 0x1e2c0, 0x1e2eb }, + { 0x1e7e0, 0x1e7e6 }, + { 0x1e7e8, 0x1e7eb }, + { 0x1e7ed, 0x1e7ee }, + { 0x1e7f0, 0x1e7fe }, + { 0x1e800, 0x1e8c4 }, + { 0x1e900, 0x1e943 }, + { 0x1e94b, 0x1e94b }, + { 0x1ee00, 0x1ee03 }, + { 0x1ee05, 0x1ee1f }, + { 0x1ee21, 0x1ee22 }, + { 0x1ee24, 0x1ee24 }, + { 0x1ee27, 0x1ee27 }, + { 0x1ee29, 0x1ee32 }, + { 0x1ee34, 0x1ee37 }, + { 0x1ee39, 0x1ee39 }, + { 0x1ee3b, 0x1ee3b }, + { 0x1ee42, 0x1ee42 }, + { 0x1ee47, 0x1ee47 }, + { 0x1ee49, 0x1ee49 }, + { 0x1ee4b, 0x1ee4b }, + { 0x1ee4d, 0x1ee4f }, + { 0x1ee51, 0x1ee52 }, + { 0x1ee54, 0x1ee54 }, + { 0x1ee57, 0x1ee57 }, + { 0x1ee59, 0x1ee59 }, + { 0x1ee5b, 0x1ee5b }, + { 0x1ee5d, 0x1ee5d }, + { 0x1ee5f, 0x1ee5f }, + { 0x1ee61, 0x1ee62 }, + { 0x1ee64, 0x1ee64 }, + { 0x1ee67, 0x1ee6a }, + { 0x1ee6c, 0x1ee72 }, + { 0x1ee74, 0x1ee77 }, + { 0x1ee79, 0x1ee7c }, + { 0x1ee7e, 0x1ee7e }, + { 0x1ee80, 0x1ee89 }, + { 0x1ee8b, 0x1ee9b }, + { 0x1eea1, 0x1eea3 }, + { 0x1eea5, 0x1eea9 }, + { 0x1eeab, 0x1eebb }, + { 0x20000, 0x2a6df }, + { 0x2a700, 0x2b738 }, + { 0x2b740, 0x2b81d }, + { 0x2b820, 0x2cea1 }, + { 0x2ceb0, 0x2ebe0 }, + { 0x2f800, 0x2fa1d }, + { 0x30000, 0x3134a }, + { 0x0, 0x0 }, +}; + +static CharRange xid_continue[] = { + { 0x30, 0x39 }, + { 0x41, 0x5a }, + { 0x5f, 0x5f }, + { 0x61, 0x7a }, + { 0xaa, 0xaa }, + { 0xb5, 0xb5 }, + { 0xb7, 0xb7 }, + { 0xba, 0xba }, + { 0xc0, 0xd6 }, + { 0xd8, 0xf6 }, + { 0xf8, 0x2c1 }, + { 0x2c6, 0x2d1 }, + { 0x2e0, 0x2e4 }, + { 0x2ec, 0x2ec }, + { 0x2ee, 0x2ee }, + { 0x300, 0x374 }, + { 0x376, 0x377 }, + { 0x37a, 0x37d }, + { 0x37f, 0x37f }, + { 0x386, 0x38a }, + { 0x38c, 0x38c }, + { 0x38e, 0x3a1 }, + { 0x3a3, 0x3f5 }, + { 0x3f7, 0x481 }, + { 0x483, 0x487 }, + { 0x48a, 0x52f }, + { 0x531, 0x556 }, + { 0x559, 0x559 }, + { 0x560, 0x588 }, + { 0x591, 0x5bd }, + { 0x5bf, 0x5bf }, + { 0x5c1, 0x5c2 }, + { 0x5c4, 0x5c5 }, + { 0x5c7, 0x5c7 }, + { 0x5d0, 0x5ea }, + { 0x5ef, 0x5f2 }, + { 0x610, 0x61a }, + { 0x620, 0x669 }, + { 0x66e, 0x6d3 }, + { 0x6d5, 0x6dc }, + { 0x6df, 0x6e8 }, + { 0x6ea, 0x6fc }, + { 0x6ff, 0x6ff }, + { 0x710, 0x74a }, + { 0x74d, 0x7b1 }, + { 0x7c0, 0x7f5 }, + { 0x7fa, 0x7fa }, + { 0x7fd, 0x7fd }, + { 0x800, 0x82d }, + { 0x840, 0x85b }, + { 0x860, 0x86a }, + { 0x870, 0x887 }, + { 0x889, 0x88e }, + { 0x898, 0x8e1 }, + { 0x8e3, 0x963 }, + { 0x966, 0x96f }, + { 0x971, 0x983 }, + { 0x985, 0x98c }, + { 0x98f, 0x990 }, + { 0x993, 0x9a8 }, + { 0x9aa, 0x9b0 }, + { 0x9b2, 0x9b2 }, + { 0x9b6, 0x9b9 }, + { 0x9bc, 0x9c4 }, + { 0x9c7, 0x9c8 }, + { 0x9cb, 0x9ce }, + { 0x9d7, 0x9d7 }, + { 0x9dc, 0x9dd }, + { 0x9df, 0x9e3 }, + { 0x9e6, 0x9f1 }, + { 0x9fc, 0x9fc }, + { 0x9fe, 0x9fe }, + { 0xa01, 0xa03 }, + { 0xa05, 0xa0a }, + { 0xa0f, 0xa10 }, + { 0xa13, 0xa28 }, + { 0xa2a, 0xa30 }, + { 0xa32, 0xa33 }, + { 0xa35, 0xa36 }, + { 0xa38, 0xa39 }, + { 0xa3c, 0xa3c }, + { 0xa3e, 0xa42 }, + { 0xa47, 0xa48 }, + { 0xa4b, 0xa4d }, + { 0xa51, 0xa51 }, + { 0xa59, 0xa5c }, + { 0xa5e, 0xa5e }, + { 0xa66, 0xa75 }, + { 0xa81, 0xa83 }, + { 0xa85, 0xa8d }, + { 0xa8f, 0xa91 }, + { 0xa93, 0xaa8 }, + { 0xaaa, 0xab0 }, + { 0xab2, 0xab3 }, + { 0xab5, 0xab9 }, + { 0xabc, 0xac5 }, + { 0xac7, 0xac9 }, + { 0xacb, 0xacd }, + { 0xad0, 0xad0 }, + { 0xae0, 0xae3 }, + { 0xae6, 0xaef }, + { 0xaf9, 0xaff }, + { 0xb01, 0xb03 }, + { 0xb05, 0xb0c }, + { 0xb0f, 0xb10 }, + { 0xb13, 0xb28 }, + { 0xb2a, 0xb30 }, + { 0xb32, 0xb33 }, + { 0xb35, 0xb39 }, + { 0xb3c, 0xb44 }, + { 0xb47, 0xb48 }, + { 0xb4b, 0xb4d }, + { 0xb55, 0xb57 }, + { 0xb5c, 0xb5d }, + { 0xb5f, 0xb63 }, + { 0xb66, 0xb6f }, + { 0xb71, 0xb71 }, + { 0xb82, 0xb83 }, + { 0xb85, 0xb8a }, + { 0xb8e, 0xb90 }, + { 0xb92, 0xb95 }, + { 0xb99, 0xb9a }, + { 0xb9c, 0xb9c }, + { 0xb9e, 0xb9f }, + { 0xba3, 0xba4 }, + { 0xba8, 0xbaa }, + { 0xbae, 0xbb9 }, + { 0xbbe, 0xbc2 }, + { 0xbc6, 0xbc8 }, + { 0xbca, 0xbcd }, + { 0xbd0, 0xbd0 }, + { 0xbd7, 0xbd7 }, + { 0xbe6, 0xbef }, + { 0xc00, 0xc0c }, + { 0xc0e, 0xc10 }, + { 0xc12, 0xc28 }, + { 0xc2a, 0xc39 }, + { 0xc3c, 0xc44 }, + { 0xc46, 0xc48 }, + { 0xc4a, 0xc4d }, + { 0xc55, 0xc56 }, + { 0xc58, 0xc5a }, + { 0xc5d, 0xc5d }, + { 0xc60, 0xc63 }, + { 0xc66, 0xc6f }, + { 0xc80, 0xc83 }, + { 0xc85, 0xc8c }, + { 0xc8e, 0xc90 }, + { 0xc92, 0xca8 }, + { 0xcaa, 0xcb3 }, + { 0xcb5, 0xcb9 }, + { 0xcbc, 0xcc4 }, + { 0xcc6, 0xcc8 }, + { 0xcca, 0xccd }, + { 0xcd5, 0xcd6 }, + { 0xcdd, 0xcde }, + { 0xce0, 0xce3 }, + { 0xce6, 0xcef }, + { 0xcf1, 0xcf2 }, + { 0xd00, 0xd0c }, + { 0xd0e, 0xd10 }, + { 0xd12, 0xd44 }, + { 0xd46, 0xd48 }, + { 0xd4a, 0xd4e }, + { 0xd54, 0xd57 }, + { 0xd5f, 0xd63 }, + { 0xd66, 0xd6f }, + { 0xd7a, 0xd7f }, + { 0xd81, 0xd83 }, + { 0xd85, 0xd96 }, + { 0xd9a, 0xdb1 }, + { 0xdb3, 0xdbb }, + { 0xdbd, 0xdbd }, + { 0xdc0, 0xdc6 }, + { 0xdca, 0xdca }, + { 0xdcf, 0xdd4 }, + { 0xdd6, 0xdd6 }, + { 0xdd8, 0xddf }, + { 0xde6, 0xdef }, + { 0xdf2, 0xdf3 }, + { 0xe01, 0xe3a }, + { 0xe40, 0xe4e }, + { 0xe50, 0xe59 }, + { 0xe81, 0xe82 }, + { 0xe84, 0xe84 }, + { 0xe86, 0xe8a }, + { 0xe8c, 0xea3 }, + { 0xea5, 0xea5 }, + { 0xea7, 0xebd }, + { 0xec0, 0xec4 }, + { 0xec6, 0xec6 }, + { 0xec8, 0xecd }, + { 0xed0, 0xed9 }, + { 0xedc, 0xedf }, + { 0xf00, 0xf00 }, + { 0xf18, 0xf19 }, + { 0xf20, 0xf29 }, + { 0xf35, 0xf35 }, + { 0xf37, 0xf37 }, + { 0xf39, 0xf39 }, + { 0xf3e, 0xf47 }, + { 0xf49, 0xf6c }, + { 0xf71, 0xf84 }, + { 0xf86, 0xf97 }, + { 0xf99, 0xfbc }, + { 0xfc6, 0xfc6 }, + { 0x1000, 0x1049 }, + { 0x1050, 0x109d }, + { 0x10a0, 0x10c5 }, + { 0x10c7, 0x10c7 }, + { 0x10cd, 0x10cd }, + { 0x10d0, 0x10fa }, + { 0x10fc, 0x1248 }, + { 0x124a, 0x124d }, + { 0x1250, 0x1256 }, + { 0x1258, 0x1258 }, + { 0x125a, 0x125d }, + { 0x1260, 0x1288 }, + { 0x128a, 0x128d }, + { 0x1290, 0x12b0 }, + { 0x12b2, 0x12b5 }, + { 0x12b8, 0x12be }, + { 0x12c0, 0x12c0 }, + { 0x12c2, 0x12c5 }, + { 0x12c8, 0x12d6 }, + { 0x12d8, 0x1310 }, + { 0x1312, 0x1315 }, + { 0x1318, 0x135a }, + { 0x135d, 0x135f }, + { 0x1369, 0x1369 }, + { 0x1371, 0x1371 }, + { 0x1380, 0x138f }, + { 0x13a0, 0x13f5 }, + { 0x13f8, 0x13fd }, + { 0x1401, 0x166c }, + { 0x166f, 0x167f }, + { 0x1681, 0x169a }, + { 0x16a0, 0x16ea }, + { 0x16ee, 0x16f8 }, + { 0x1700, 0x1715 }, + { 0x171f, 0x1734 }, + { 0x1740, 0x1753 }, + { 0x1760, 0x176c }, + { 0x176e, 0x1770 }, + { 0x1772, 0x1773 }, + { 0x1780, 0x17d3 }, + { 0x17d7, 0x17d7 }, + { 0x17dc, 0x17dd }, + { 0x17e0, 0x17e9 }, + { 0x180b, 0x180d }, + { 0x180f, 0x1819 }, + { 0x1820, 0x1878 }, + { 0x1880, 0x18aa }, + { 0x18b0, 0x18f5 }, + { 0x1900, 0x191e }, + { 0x1920, 0x192b }, + { 0x1930, 0x193b }, + { 0x1946, 0x196d }, + { 0x1970, 0x1974 }, + { 0x1980, 0x19ab }, + { 0x19b0, 0x19c9 }, + { 0x19d0, 0x19da }, + { 0x1a00, 0x1a1b }, + { 0x1a20, 0x1a5e }, + { 0x1a60, 0x1a7c }, + { 0x1a7f, 0x1a89 }, + { 0x1a90, 0x1a99 }, + { 0x1aa7, 0x1aa7 }, + { 0x1ab0, 0x1abd }, + { 0x1abf, 0x1ace }, + { 0x1b00, 0x1b4c }, + { 0x1b50, 0x1b59 }, + { 0x1b6b, 0x1b73 }, + { 0x1b80, 0x1bf3 }, + { 0x1c00, 0x1c37 }, + { 0x1c40, 0x1c49 }, + { 0x1c4d, 0x1c7d }, + { 0x1c80, 0x1c88 }, + { 0x1c90, 0x1cba }, + { 0x1cbd, 0x1cbf }, + { 0x1cd0, 0x1cd2 }, + { 0x1cd4, 0x1cfa }, + { 0x1d00, 0x1f15 }, + { 0x1f18, 0x1f1d }, + { 0x1f20, 0x1f45 }, + { 0x1f48, 0x1f4d }, + { 0x1f50, 0x1f57 }, + { 0x1f59, 0x1f59 }, + { 0x1f5b, 0x1f5b }, + { 0x1f5d, 0x1f5d }, + { 0x1f5f, 0x1f7d }, + { 0x1f80, 0x1fb4 }, + { 0x1fb6, 0x1fbc }, + { 0x1fbe, 0x1fbe }, + { 0x1fc2, 0x1fc4 }, + { 0x1fc6, 0x1fcc }, + { 0x1fd0, 0x1fd3 }, + { 0x1fd6, 0x1fdb }, + { 0x1fe0, 0x1fec }, + { 0x1ff2, 0x1ff4 }, + { 0x1ff6, 0x1ffc }, + { 0x203f, 0x2040 }, + { 0x2054, 0x2054 }, + { 0x2071, 0x2071 }, + { 0x207f, 0x207f }, + { 0x2090, 0x209c }, + { 0x20d0, 0x20dc }, + { 0x20e1, 0x20e1 }, + { 0x20e5, 0x20f0 }, + { 0x2102, 0x2102 }, + { 0x2107, 0x2107 }, + { 0x210a, 0x2113 }, + { 0x2115, 0x2115 }, + { 0x2118, 0x211d }, + { 0x2124, 0x2124 }, + { 0x2126, 0x2126 }, + { 0x2128, 0x2128 }, + { 0x212a, 0x2139 }, + { 0x213c, 0x213f }, + { 0x2145, 0x2149 }, + { 0x214e, 0x214e }, + { 0x2160, 0x2188 }, + { 0x2c00, 0x2ce4 }, + { 0x2ceb, 0x2cf3 }, + { 0x2d00, 0x2d25 }, + { 0x2d27, 0x2d27 }, + { 0x2d2d, 0x2d2d }, + { 0x2d30, 0x2d67 }, + { 0x2d6f, 0x2d6f }, + { 0x2d7f, 0x2d96 }, + { 0x2da0, 0x2da6 }, + { 0x2da8, 0x2dae }, + { 0x2db0, 0x2db6 }, + { 0x2db8, 0x2dbe }, + { 0x2dc0, 0x2dc6 }, + { 0x2dc8, 0x2dce }, + { 0x2dd0, 0x2dd6 }, + { 0x2dd8, 0x2dde }, + { 0x2de0, 0x2dff }, + { 0x3005, 0x3007 }, + { 0x3021, 0x302f }, + { 0x3031, 0x3035 }, + { 0x3038, 0x303c }, + { 0x3041, 0x3096 }, + { 0x3099, 0x309f }, + { 0x30a1, 0x30fa }, + { 0x30fc, 0x30ff }, + { 0x3105, 0x312f }, + { 0x3131, 0x318e }, + { 0x31a0, 0x31bf }, + { 0x31f0, 0x31ff }, + { 0x3400, 0x4dbf }, + { 0x4e00, 0xa48c }, + { 0xa4d0, 0xa4fd }, + { 0xa500, 0xa60c }, + { 0xa610, 0xa62b }, + { 0xa640, 0xa66f }, + { 0xa674, 0xa67d }, + { 0xa67f, 0xa6f1 }, + { 0xa717, 0xa71f }, + { 0xa722, 0xa788 }, + { 0xa78b, 0xa7ca }, + { 0xa7d0, 0xa7d1 }, + { 0xa7d3, 0xa7d3 }, + { 0xa7d5, 0xa7d9 }, + { 0xa7f2, 0xa827 }, + { 0xa82c, 0xa82c }, + { 0xa840, 0xa873 }, + { 0xa880, 0xa8c5 }, + { 0xa8d0, 0xa8d9 }, + { 0xa8e0, 0xa8f7 }, + { 0xa8fb, 0xa8fb }, + { 0xa8fd, 0xa92d }, + { 0xa930, 0xa953 }, + { 0xa960, 0xa97c }, + { 0xa980, 0xa9c0 }, + { 0xa9cf, 0xa9d9 }, + { 0xa9e0, 0xa9fe }, + { 0xaa00, 0xaa36 }, + { 0xaa40, 0xaa4d }, + { 0xaa50, 0xaa59 }, + { 0xaa60, 0xaa76 }, + { 0xaa7a, 0xaac2 }, + { 0xaadb, 0xaadd }, + { 0xaae0, 0xaaef }, + { 0xaaf2, 0xaaf6 }, + { 0xab01, 0xab06 }, + { 0xab09, 0xab0e }, + { 0xab11, 0xab16 }, + { 0xab20, 0xab26 }, + { 0xab28, 0xab2e }, + { 0xab30, 0xab5a }, + { 0xab5c, 0xab69 }, + { 0xab70, 0xabea }, + { 0xabec, 0xabed }, + { 0xabf0, 0xabf9 }, + { 0xac00, 0xd7a3 }, + { 0xd7b0, 0xd7c6 }, + { 0xd7cb, 0xd7fb }, + { 0xf900, 0xfa6d }, + { 0xfa70, 0xfad9 }, + { 0xfb00, 0xfb06 }, + { 0xfb13, 0xfb17 }, + { 0xfb1d, 0xfb28 }, + { 0xfb2a, 0xfb36 }, + { 0xfb38, 0xfb3c }, + { 0xfb3e, 0xfb3e }, + { 0xfb40, 0xfb41 }, + { 0xfb43, 0xfb44 }, + { 0xfb46, 0xfbb1 }, + { 0xfbd3, 0xfd3d }, + { 0xfd50, 0xfd8f }, + { 0xfd92, 0xfdc7 }, + { 0xfdf0, 0xfdfb }, + { 0xfe00, 0xfe0f }, + { 0xfe20, 0xfe2f }, + { 0xfe33, 0xfe34 }, + { 0xfe4d, 0xfe4f }, + { 0xfe70, 0xfe74 }, + { 0xfe76, 0xfefc }, + { 0xff10, 0xff19 }, + { 0xff21, 0xff3a }, + { 0xff3f, 0xff3f }, + { 0xff41, 0xff5a }, + { 0xff66, 0xffbe }, + { 0xffc2, 0xffc7 }, + { 0xffca, 0xffcf }, + { 0xffd2, 0xffd7 }, + { 0xffda, 0xffdc }, + { 0x10000, 0x1000b }, + { 0x1000d, 0x10026 }, + { 0x10028, 0x1003a }, + { 0x1003c, 0x1003d }, + { 0x1003f, 0x1004d }, + { 0x10050, 0x1005d }, + { 0x10080, 0x100fa }, + { 0x10140, 0x10174 }, + { 0x101fd, 0x101fd }, + { 0x10280, 0x1029c }, + { 0x102a0, 0x102d0 }, + { 0x102e0, 0x102e0 }, + { 0x10300, 0x1031f }, + { 0x1032d, 0x1034a }, + { 0x10350, 0x1037a }, + { 0x10380, 0x1039d }, + { 0x103a0, 0x103c3 }, + { 0x103c8, 0x103cf }, + { 0x103d1, 0x103d5 }, + { 0x10400, 0x1049d }, + { 0x104a0, 0x104a9 }, + { 0x104b0, 0x104d3 }, + { 0x104d8, 0x104fb }, + { 0x10500, 0x10527 }, + { 0x10530, 0x10563 }, + { 0x10570, 0x1057a }, + { 0x1057c, 0x1058a }, + { 0x1058c, 0x10592 }, + { 0x10594, 0x10595 }, + { 0x10597, 0x105a1 }, + { 0x105a3, 0x105b1 }, + { 0x105b3, 0x105b9 }, + { 0x105bb, 0x105bc }, + { 0x10600, 0x10736 }, + { 0x10740, 0x10755 }, + { 0x10760, 0x10767 }, + { 0x10780, 0x10785 }, + { 0x10787, 0x107b0 }, + { 0x107b2, 0x107ba }, + { 0x10800, 0x10805 }, + { 0x10808, 0x10808 }, + { 0x1080a, 0x10835 }, + { 0x10837, 0x10838 }, + { 0x1083c, 0x1083c }, + { 0x1083f, 0x10855 }, + { 0x10860, 0x10876 }, + { 0x10880, 0x1089e }, + { 0x108e0, 0x108f2 }, + { 0x108f4, 0x108f5 }, + { 0x10900, 0x10915 }, + { 0x10920, 0x10939 }, + { 0x10980, 0x109b7 }, + { 0x109be, 0x109bf }, + { 0x10a00, 0x10a03 }, + { 0x10a05, 0x10a06 }, + { 0x10a0c, 0x10a13 }, + { 0x10a15, 0x10a17 }, + { 0x10a19, 0x10a35 }, + { 0x10a38, 0x10a3a }, + { 0x10a3f, 0x10a3f }, + { 0x10a60, 0x10a7c }, + { 0x10a80, 0x10a9c }, + { 0x10ac0, 0x10ac7 }, + { 0x10ac9, 0x10ae6 }, + { 0x10b00, 0x10b35 }, + { 0x10b40, 0x10b55 }, + { 0x10b60, 0x10b72 }, + { 0x10b80, 0x10b91 }, + { 0x10c00, 0x10c48 }, + { 0x10c80, 0x10cb2 }, + { 0x10cc0, 0x10cf2 }, + { 0x10d00, 0x10d27 }, + { 0x10d30, 0x10d39 }, + { 0x10e80, 0x10ea9 }, + { 0x10eab, 0x10eac }, + { 0x10eb0, 0x10eb1 }, + { 0x10f00, 0x10f1c }, + { 0x10f27, 0x10f27 }, + { 0x10f30, 0x10f50 }, + { 0x10f70, 0x10f85 }, + { 0x10fb0, 0x10fc4 }, + { 0x10fe0, 0x10ff6 }, + { 0x11000, 0x11046 }, + { 0x11066, 0x11075 }, + { 0x1107f, 0x110ba }, + { 0x110c2, 0x110c2 }, + { 0x110d0, 0x110e8 }, + { 0x110f0, 0x110f9 }, + { 0x11100, 0x11134 }, + { 0x11136, 0x1113f }, + { 0x11144, 0x11147 }, + { 0x11150, 0x11173 }, + { 0x11176, 0x11176 }, + { 0x11180, 0x111c4 }, + { 0x111c9, 0x111cc }, + { 0x111ce, 0x111da }, + { 0x111dc, 0x111dc }, + { 0x11200, 0x11211 }, + { 0x11213, 0x11237 }, + { 0x1123e, 0x1123e }, + { 0x11280, 0x11286 }, + { 0x11288, 0x11288 }, + { 0x1128a, 0x1128d }, + { 0x1128f, 0x1129d }, + { 0x1129f, 0x112a8 }, + { 0x112b0, 0x112ea }, + { 0x112f0, 0x112f9 }, + { 0x11300, 0x11303 }, + { 0x11305, 0x1130c }, + { 0x1130f, 0x11310 }, + { 0x11313, 0x11328 }, + { 0x1132a, 0x11330 }, + { 0x11332, 0x11333 }, + { 0x11335, 0x11339 }, + { 0x1133b, 0x11344 }, + { 0x11347, 0x11348 }, + { 0x1134b, 0x1134d }, + { 0x11350, 0x11350 }, + { 0x11357, 0x11357 }, + { 0x1135d, 0x11363 }, + { 0x11366, 0x1136c }, + { 0x11370, 0x11374 }, + { 0x11400, 0x1144a }, + { 0x11450, 0x11459 }, + { 0x1145e, 0x11461 }, + { 0x11480, 0x114c5 }, + { 0x114c7, 0x114c7 }, + { 0x114d0, 0x114d9 }, + { 0x11580, 0x115b5 }, + { 0x115b8, 0x115c0 }, + { 0x115d8, 0x115dd }, + { 0x11600, 0x11640 }, + { 0x11644, 0x11644 }, + { 0x11650, 0x11659 }, + { 0x11680, 0x116b8 }, + { 0x116c0, 0x116c9 }, + { 0x11700, 0x1171a }, + { 0x1171d, 0x1172b }, + { 0x11730, 0x11739 }, + { 0x11740, 0x11746 }, + { 0x11800, 0x1183a }, + { 0x118a0, 0x118e9 }, + { 0x118ff, 0x11906 }, + { 0x11909, 0x11909 }, + { 0x1190c, 0x11913 }, + { 0x11915, 0x11916 }, + { 0x11918, 0x11935 }, + { 0x11937, 0x11938 }, + { 0x1193b, 0x11943 }, + { 0x11950, 0x11959 }, + { 0x119a0, 0x119a7 }, + { 0x119aa, 0x119d7 }, + { 0x119da, 0x119e1 }, + { 0x119e3, 0x119e4 }, + { 0x11a00, 0x11a3e }, + { 0x11a47, 0x11a47 }, + { 0x11a50, 0x11a99 }, + { 0x11a9d, 0x11a9d }, + { 0x11ab0, 0x11af8 }, + { 0x11c00, 0x11c08 }, + { 0x11c0a, 0x11c36 }, + { 0x11c38, 0x11c40 }, + { 0x11c50, 0x11c59 }, + { 0x11c72, 0x11c8f }, + { 0x11c92, 0x11ca7 }, + { 0x11ca9, 0x11cb6 }, + { 0x11d00, 0x11d06 }, + { 0x11d08, 0x11d09 }, + { 0x11d0b, 0x11d36 }, + { 0x11d3a, 0x11d3a }, + { 0x11d3c, 0x11d3d }, + { 0x11d3f, 0x11d47 }, + { 0x11d50, 0x11d59 }, + { 0x11d60, 0x11d65 }, + { 0x11d67, 0x11d68 }, + { 0x11d6a, 0x11d8e }, + { 0x11d90, 0x11d91 }, + { 0x11d93, 0x11d98 }, + { 0x11da0, 0x11da9 }, + { 0x11ee0, 0x11ef6 }, + { 0x11fb0, 0x11fb0 }, + { 0x12000, 0x12399 }, + { 0x12400, 0x1246e }, + { 0x12480, 0x12543 }, + { 0x12f90, 0x12ff0 }, + { 0x13000, 0x1342e }, + { 0x14400, 0x14646 }, + { 0x16800, 0x16a38 }, + { 0x16a40, 0x16a5e }, + { 0x16a60, 0x16a69 }, + { 0x16a70, 0x16abe }, + { 0x16ac0, 0x16ac9 }, + { 0x16ad0, 0x16aed }, + { 0x16af0, 0x16af4 }, + { 0x16b00, 0x16b36 }, + { 0x16b40, 0x16b43 }, + { 0x16b50, 0x16b59 }, + { 0x16b63, 0x16b77 }, + { 0x16b7d, 0x16b8f }, + { 0x16e40, 0x16e7f }, + { 0x16f00, 0x16f4a }, + { 0x16f4f, 0x16f87 }, + { 0x16f8f, 0x16f9f }, + { 0x16fe0, 0x16fe1 }, + { 0x16fe3, 0x16fe4 }, + { 0x16ff0, 0x16ff1 }, + { 0x17000, 0x187f7 }, + { 0x18800, 0x18cd5 }, + { 0x18d00, 0x18d08 }, + { 0x1aff0, 0x1aff3 }, + { 0x1aff5, 0x1affb }, + { 0x1affd, 0x1affe }, + { 0x1b000, 0x1b122 }, + { 0x1b150, 0x1b152 }, + { 0x1b164, 0x1b167 }, + { 0x1b170, 0x1b2fb }, + { 0x1bc00, 0x1bc6a }, + { 0x1bc70, 0x1bc7c }, + { 0x1bc80, 0x1bc88 }, + { 0x1bc90, 0x1bc99 }, + { 0x1bc9d, 0x1bc9e }, + { 0x1cf00, 0x1cf2d }, + { 0x1cf30, 0x1cf46 }, + { 0x1d165, 0x1d169 }, + { 0x1d16d, 0x1d172 }, + { 0x1d17b, 0x1d182 }, + { 0x1d185, 0x1d18b }, + { 0x1d1aa, 0x1d1ad }, + { 0x1d242, 0x1d244 }, + { 0x1d400, 0x1d454 }, + { 0x1d456, 0x1d49c }, + { 0x1d49e, 0x1d49f }, + { 0x1d4a2, 0x1d4a2 }, + { 0x1d4a5, 0x1d4a6 }, + { 0x1d4a9, 0x1d4ac }, + { 0x1d4ae, 0x1d4b9 }, + { 0x1d4bb, 0x1d4bb }, + { 0x1d4bd, 0x1d4c3 }, + { 0x1d4c5, 0x1d505 }, + { 0x1d507, 0x1d50a }, + { 0x1d50d, 0x1d514 }, + { 0x1d516, 0x1d51c }, + { 0x1d51e, 0x1d539 }, + { 0x1d53b, 0x1d53e }, + { 0x1d540, 0x1d544 }, + { 0x1d546, 0x1d546 }, + { 0x1d54a, 0x1d550 }, + { 0x1d552, 0x1d6a5 }, + { 0x1d6a8, 0x1d6c0 }, + { 0x1d6c2, 0x1d6da }, + { 0x1d6dc, 0x1d6fa }, + { 0x1d6fc, 0x1d714 }, + { 0x1d716, 0x1d734 }, + { 0x1d736, 0x1d74e }, + { 0x1d750, 0x1d76e }, + { 0x1d770, 0x1d788 }, + { 0x1d78a, 0x1d7a8 }, + { 0x1d7aa, 0x1d7c2 }, + { 0x1d7c4, 0x1d7cb }, + { 0x1d7ce, 0x1d7ff }, + { 0x1da00, 0x1da36 }, + { 0x1da3b, 0x1da6c }, + { 0x1da75, 0x1da75 }, + { 0x1da84, 0x1da84 }, + { 0x1da9b, 0x1da9f }, + { 0x1daa1, 0x1daaf }, + { 0x1df00, 0x1df1e }, + { 0x1e000, 0x1e006 }, + { 0x1e008, 0x1e018 }, + { 0x1e01b, 0x1e021 }, + { 0x1e023, 0x1e024 }, + { 0x1e026, 0x1e02a }, + { 0x1e100, 0x1e12c }, + { 0x1e130, 0x1e13d }, + { 0x1e140, 0x1e149 }, + { 0x1e14e, 0x1e14e }, + { 0x1e290, 0x1e2ae }, + { 0x1e2c0, 0x1e2f9 }, + { 0x1e7e0, 0x1e7e6 }, + { 0x1e7e8, 0x1e7eb }, + { 0x1e7ed, 0x1e7ee }, + { 0x1e7f0, 0x1e7fe }, + { 0x1e800, 0x1e8c4 }, + { 0x1e8d0, 0x1e8d6 }, + { 0x1e900, 0x1e94b }, + { 0x1e950, 0x1e959 }, + { 0x1ee00, 0x1ee03 }, + { 0x1ee05, 0x1ee1f }, + { 0x1ee21, 0x1ee22 }, + { 0x1ee24, 0x1ee24 }, + { 0x1ee27, 0x1ee27 }, + { 0x1ee29, 0x1ee32 }, + { 0x1ee34, 0x1ee37 }, + { 0x1ee39, 0x1ee39 }, + { 0x1ee3b, 0x1ee3b }, + { 0x1ee42, 0x1ee42 }, + { 0x1ee47, 0x1ee47 }, + { 0x1ee49, 0x1ee49 }, + { 0x1ee4b, 0x1ee4b }, + { 0x1ee4d, 0x1ee4f }, + { 0x1ee51, 0x1ee52 }, + { 0x1ee54, 0x1ee54 }, + { 0x1ee57, 0x1ee57 }, + { 0x1ee59, 0x1ee59 }, + { 0x1ee5b, 0x1ee5b }, + { 0x1ee5d, 0x1ee5d }, + { 0x1ee5f, 0x1ee5f }, + { 0x1ee61, 0x1ee62 }, + { 0x1ee64, 0x1ee64 }, + { 0x1ee67, 0x1ee6a }, + { 0x1ee6c, 0x1ee72 }, + { 0x1ee74, 0x1ee77 }, + { 0x1ee79, 0x1ee7c }, + { 0x1ee7e, 0x1ee7e }, + { 0x1ee80, 0x1ee89 }, + { 0x1ee8b, 0x1ee9b }, + { 0x1eea1, 0x1eea3 }, + { 0x1eea5, 0x1eea9 }, + { 0x1eeab, 0x1eebb }, + { 0x1fbf0, 0x1fbf9 }, + { 0x20000, 0x2a6df }, + { 0x2a700, 0x2b738 }, + { 0x2b740, 0x2b81d }, + { 0x2b820, 0x2cea1 }, + { 0x2ceb0, 0x2ebe0 }, + { 0x2f800, 0x2fa1d }, + { 0x30000, 0x3134a }, + { 0xe0100, 0xe01ef }, + { 0x0, 0x0 }, +}; + +#endif // CHAR_RANGE_INC +#line 0 + +#line 1 "sfwl/core/char_utils.h" +/*************************************************************************/ +/* char_utils.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#ifndef CHAR_UTILS_H +#define CHAR_UTILS_H + + + +static _FORCE_INLINE_ bool is_unicode_identifier_start(char32_t c) { + for (int i = 0; xid_start[i].start != 0; i++) { + if (c >= xid_start[i].start && c <= xid_start[i].end) { + return true; + } + } + return false; +} + +static _FORCE_INLINE_ bool is_unicode_identifier_continue(char32_t c) { + for (int i = 0; xid_continue[i].start != 0; i++) { + if (c >= xid_continue[i].start && c <= xid_continue[i].end) { + return true; + } + } + return false; +} + +static _FORCE_INLINE_ bool is_ascii_upper_case(char32_t c) { + return (c >= 'A' && c <= 'Z'); +} + +static _FORCE_INLINE_ bool is_ascii_lower_case(char32_t c) { + return (c >= 'a' && c <= 'z'); +} + +static _FORCE_INLINE_ bool is_digit(char32_t c) { + return (c >= '0' && c <= '9'); +} + +static _FORCE_INLINE_ bool is_hex_digit(char32_t c) { + return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + +static _FORCE_INLINE_ bool is_binary_digit(char32_t c) { + return (c == '0' || c == '1'); +} + +static _FORCE_INLINE_ bool is_ascii_char(char32_t c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static _FORCE_INLINE_ bool is_ascii_alphanumeric_char(char32_t c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); +} + +static _FORCE_INLINE_ bool is_ascii_identifier_char(char32_t c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; +} + +static _FORCE_INLINE_ bool is_symbol(char32_t c) { + return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' '); +} + +static _FORCE_INLINE_ bool is_control(char32_t p_char) { + return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009f); +} + +static _FORCE_INLINE_ bool is_whitespace(char32_t p_char) { + return (p_char == ' ') || (p_char == 0x00a0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085); +} + +static _FORCE_INLINE_ bool is_linebreak(char32_t p_char) { + return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029); +} + +static _FORCE_INLINE_ bool is_punct(char32_t p_char) { + return (p_char >= ' ' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '^') || (p_char == '`') || (p_char >= '{' && p_char <= '~') || (p_char >= 0x2000 && p_char <= 0x206f) || (p_char >= 0x3000 && p_char <= 0x303f); +} + +static _FORCE_INLINE_ bool is_underscore(char32_t p_char) { + return (p_char == '_'); +} + +#endif // CHAR_UTILS_H +#line 0 + +#line 1 "sfwl/core/ustring.h" +#ifndef USTRING_H +#define USTRING_H + +/*************************************************************************/ +/* ustring.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +/*************************************************************************/ +/* CharProxy */ +/*************************************************************************/ + +template +class CharProxy { + friend class Char16String; + friend class CharString; + friend class String; + + const int _index; + CowData &_cowdata; + static const T _null = 0; + + _FORCE_INLINE_ CharProxy(const int &p_index, CowData &p_cowdata) : + _index(p_index), + _cowdata(p_cowdata) {} + +public: + _FORCE_INLINE_ CharProxy(const CharProxy &p_other) : + _index(p_other._index), + _cowdata(p_other._cowdata) {} + + _FORCE_INLINE_ operator T() const { + if (unlikely(_cowdata.size() == 0)) { + return _null; + } + + return _cowdata.get(_index); + } + + _FORCE_INLINE_ const T *operator&() const { + return _cowdata.ptr() + _index; + } + + _FORCE_INLINE_ void operator=(const T &p_other) const { + _cowdata.set(_index, p_other); + } + + _FORCE_INLINE_ void operator=(const CharProxy &p_other) const { + _cowdata.set(_index, p_other.operator T()); + } +}; + +/*************************************************************************/ +/* Char16String */ +/*************************************************************************/ + +class Char16String { + CowData _cowdata; + static const char16_t _null; + +public: + _FORCE_INLINE_ char16_t *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const char16_t *ptr() const { return _cowdata.ptr(); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + Error resize(int p_size) { + Error err = _cowdata.resize(p_size); + + //Ensure null terminator + if (err == OK && _cowdata.size() > 0) { + _cowdata.set(_cowdata.size() - 1, '\0'); + } + + return err; + } + + _FORCE_INLINE_ char16_t get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const char16_t &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ const char16_t &operator[](int p_index) const { + if (unlikely(_cowdata.size() == 0)) { + return _null; + } + + return _cowdata.get(p_index); + } + _FORCE_INLINE_ CharProxy operator[](int p_index) { return CharProxy(p_index, _cowdata); } + + _FORCE_INLINE_ Char16String() {} + _FORCE_INLINE_ Char16String(const Char16String &p_str) { _cowdata._ref(p_str._cowdata); } + _FORCE_INLINE_ void operator=(const Char16String &p_str) { _cowdata._ref(p_str._cowdata); } + _FORCE_INLINE_ Char16String(const char16_t *p_cstr) { copy_from(p_cstr); } + + void operator=(const char16_t *p_cstr); + bool operator<(const Char16String &p_right) const; + Char16String &operator+=(char16_t p_char); + int length() const { return size() ? size() - 1 : 0; } + const char16_t *get_data() const; + operator const char16_t *() const { return get_data(); }; + +protected: + void copy_from(const char16_t *p_cstr); +}; + +/*************************************************************************/ +/* CharString */ +/*************************************************************************/ + +class CharString { + CowData _cowdata; + static const char _null; + +public: + _FORCE_INLINE_ char *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const char *ptr() const { return _cowdata.ptr(); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + Error resize(int p_size) { + Error err = _cowdata.resize(p_size); + + //Ensure null terminator + if (err == OK && _cowdata.size() > 0) { + _cowdata.set(_cowdata.size() - 1, '\0'); + } + + return err; + } + + _FORCE_INLINE_ char get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const char &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ const char &operator[](int p_index) const { + if (unlikely(_cowdata.size() == 0)) { + return _null; + } + + return _cowdata.get(p_index); + } + _FORCE_INLINE_ CharProxy operator[](int p_index) { return CharProxy(p_index, _cowdata); } + + _FORCE_INLINE_ CharString() {} + _FORCE_INLINE_ CharString(const CharString &p_str) { _cowdata._ref(p_str._cowdata); } + _FORCE_INLINE_ void operator=(const CharString &p_str) { _cowdata._ref(p_str._cowdata); } + _FORCE_INLINE_ CharString(const char *p_cstr) { copy_from(p_cstr); } + + void operator=(const char *p_cstr); + bool operator<(const CharString &p_right) const; + CharString &operator+=(char p_char); + int length() const { return size() ? size() - 1 : 0; } + const char *get_data() const; + operator const char *() const { return get_data(); }; + +protected: + void copy_from(const char *p_cstr); +}; + +/*************************************************************************/ +/* String */ +/*************************************************************************/ + +typedef char32_t CharType; + +struct StrRange { + const CharType *c_str; + int len; + + StrRange(const CharType *p_c_str = nullptr, int p_len = 0) { + c_str = p_c_str; + len = p_len; + } +}; + +class String { +public: + enum { + npos = -1 /// 0) { + _cowdata.set(_cowdata.size() - 1, '\0'); + } + } + + _FORCE_INLINE_ void clear() { resize(0); } + + _FORCE_INLINE_ CharType get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const CharType &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + _FORCE_INLINE_ Error resize(int p_size) { + Error err = _cowdata.resize(p_size); + + //Ensure null terminator + if (err == OK && _cowdata.size() > 0) { + _cowdata.set(_cowdata.size() - 1, '\0'); + } + + return err; + } + _FORCE_INLINE_ Error set_length(int p_length) { return resize(p_length + 1); } + + _FORCE_INLINE_ const CharType &operator[](int p_index) const { + return _cowdata.get(p_index); + } + _FORCE_INLINE_ CharProxy operator[](int p_index) { return CharProxy(p_index, _cowdata); } + + bool operator==(const String &p_str) const; + bool operator!=(const String &p_str) const; + String operator+(const String &p_str) const; + //String operator+(CharType p_char) const; + + String &operator+=(const String &); + String &operator+=(CharType p_char); + String &operator+=(const char *p_str); + String &operator+=(const wchar_t *p_str); + String &operator+=(const CharType *p_str); + + /* Compatibility Operators */ + + void operator=(const char *p_str); + void operator=(const wchar_t *p_str); + void operator=(const CharType *p_str); + + bool operator==(const char *p_str) const; + bool operator==(const wchar_t *p_str) const; + bool operator==(const CharType *p_str) const; + bool operator==(const StrRange &p_str_range) const; + + bool operator!=(const char *p_str) const; + bool operator!=(const wchar_t *p_str) const; + bool operator!=(const CharType *p_str) const; + + bool operator<(const CharType *p_str) const; + bool operator<(const char *p_str) const; + bool operator<(const wchar_t *p_str) const; + + bool operator<(const String &p_str) const; + bool operator<=(const String &p_str) const; + bool operator>(const String &p_str) const; + bool operator>=(const String &p_str) const; + + signed char casecmp_to(const String &p_str) const; + signed char nocasecmp_to(const String &p_str) const; + signed char naturalnocasecmp_to(const String &p_str) const; + + const CharType *get_data() const; + + /* standard size stuff */ + + _FORCE_INLINE_ int length() const { + int s = size(); + return s ? (s - 1) : 0; // length does not include zero + } + + bool is_valid_string() const; + + /* debug, error messages */ + void print_unicode_error(const String &p_message, bool p_critical = false) const; + + /* complex helpers */ + String substr(int p_from, int p_chars = -1) const; + String substr_index(const int p_start_index, const int p_end_index) const; //end_index is not included + + int find(const String &p_str, int p_from = 0) const; ///< return <0 if failed + int find(const char *p_str, int p_from = 0) const; ///< return <0 if failed + int find_char(const CharType &p_char, int p_from = 0) const; ///< return <0 if failed + int find_last(const String &p_str) const; ///< return <0 if failed + int findn(const String &p_str, int p_from = 0) const; ///< return <0 if failed, case insensitive + int rfind(const String &p_str, int p_from = -1) const; ///< return <0 if failed + int rfindn(const String &p_str, int p_from = -1) const; ///< return <0 if failed, case insensitive + int findmk(const Vector &p_keys, int p_from = 0, int *r_key = nullptr) const; ///< return <0 if failed + int find_first_difference_index(const String &p_str) const; + + bool is_word_at(const int p_index, const char *p_str) const; + bool is_word_at(const int p_index, const String &p_str) const; + + bool match(const String &p_wildcard) const; + bool matchn(const String &p_wildcard) const; + + bool begins_with(const String &p_string) const; + bool begins_with(const char *p_string) const; + bool ends_with(const String &p_string) const; + + bool is_enclosed_in(const String &p_string) const; + bool is_subsequence_of(const String &p_string) const; + bool is_subsequence_ofi(const String &p_string) const; + bool is_quoted() const; + + Vector bigrams() const; + float similarity(const String &p_string) const; + //String format(const Variant &values, String placeholder = "{_}") const; + + String replace_first(const String &p_key, const String &p_with) const; + String replace(const String &p_key, const String &p_with) const; + String replace(const char *p_key, const char *p_with) const; + String replacen(const String &p_key, const String &p_with) const; + + String newline_to_br() const; + + String repeat(int p_count) const; + String insert(int p_at_pos, const String &p_string) const; + + String pad_decimals(int p_digits) const; + String pad_zeros(int p_digits) const; + + String trim_prefix(const String &p_prefix) const; + String trim_suffix(const String &p_suffix) const; + + String lpad(int min_length, const String &character = " ") const; + String rpad(int min_length, const String &character = " ") const; + + //Moved this to Array to simplify the library's design + //String sprintf(const Array &values, bool *error) const; + + String quote(String quotechar = "\"") const; + String unquote() const; + + static String num(double p_num, int p_decimals = -1); + static String num_scientific(double p_num); + static String num_real(double p_num); + static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false); + static String num_uint64(uint64_t p_num, int base = 10, bool capitalize_hex = false); + + static String chr(CharType p_char); + + static String md5(const uint8_t *p_md5); + + static String hex_encode_buffer(const uint8_t *p_buffer, int p_len); + + static String bool_num(bool p_val); + static String bool_str(bool p_val); + + bool is_numeric() const; + bool is_zero() const; + + double to_double() const; + float to_float() const; + int to_int() const; + bool to_bool() const; + uint32_t to_uint() const; + + int hex_to_int(bool p_with_prefix = true) const; + int64_t hex_to_int64(bool p_with_prefix = true) const; + int64_t bin_to_int64(bool p_with_prefix = true) const; + int64_t to_int64() const; + + static int64_t to_int(const char *p_str, int p_len = -1); + static int64_t to_int(const wchar_t *p_str, int p_len = -1); + static int64_t to_int(const CharType *p_str, int p_len = -1, bool p_clamp = false); + + static double to_float(const char *p_str); + static double to_float(const wchar_t *p_str, const wchar_t **r_end = nullptr); + static double to_float(const CharType *p_str, const CharType **r_end = nullptr); + + static double to_double(const char *p_str); + static double to_double(const wchar_t *p_str, const wchar_t **r_end = nullptr); + static double to_double(const CharType *p_str, const CharType **r_end = nullptr); + + static uint32_t num_characters(int64_t p_int); + + String capitalize() const; + String camelcase_to_underscore(bool lowercase = true) const; + + int get_slice_count(String p_splitter) const; + String get_slice(String p_splitter, int p_slice) const; + String get_slicec(CharType p_splitter, int p_slice) const; + + Vector split(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const; + Vector rsplit(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const; + Vector split_spaces() const; + Vector split_floats(const String &p_splitter, bool p_allow_empty = true) const; + Vector split_floats_mk(const Vector &p_splitters, bool p_allow_empty = true) const; + Vector split_ints(const String &p_splitter, bool p_allow_empty = true) const; + Vector split_ints_mk(const Vector &p_splitters, bool p_allow_empty = true) const; + + String join(const Vector &parts) const; + + static CharType char_uppercase(CharType p_char); + static CharType char_lowercase(CharType p_char); + String to_upper() const; + String to_lower() const; + + int count(const String &p_string, int p_from = 0, int p_to = 0) const; + int countn(const String &p_string, int p_from = 0, int p_to = 0) const; + + String left(int p_pos) const; + String right(int p_pos) const; + String indent(const String &p_prefix) const; + String dedent() const; + + String strip_edges(bool left = true, bool right = true) const; + String strip_escapes() const; + String lstrip(const String &p_chars) const; + String rstrip(const String &p_chars) const; + + String get_extension() const; + String get_basename() const; + String plus_file(const String &p_file) const; + + CharType unicode_at(int p_idx) const; + CharType ord_at(int p_idx) const; + + void erase(int p_pos, int p_chars); + + CharString ascii(bool p_allow_extended = false) const; + CharString utf8() const; + Error parse_utf8(const char *p_utf8, int p_len = -1, bool p_skip_cr = false); //return true on error + static String utf8(const char *p_utf8, int p_len = -1); + int utf8_byte_length() const; + + Char16String utf16() const; + Error parse_utf16(const char16_t *p_utf16, int p_len = -1); + static String utf16(const char16_t *p_utf16, int p_len = -1); + int utf16_byte_length() const; + + static uint32_t hash(const char *p_cstr); /* hash the string */ + static uint32_t hash(const char *p_cstr, int p_len); /* hash the string */ + static uint32_t hash(const wchar_t *p_cstr); /* hash the string */ + static uint32_t hash(const wchar_t *p_cstr, int p_len); /* hash the string */ + static uint32_t hash(const CharType *p_cstr); /* hash the string */ + static uint32_t hash(const CharType *p_cstr, int p_len); /* hash the string */ + + uint32_t hash() const; /* hash the string */ + uint64_t hash64() const; /* hash the string */ + + _FORCE_INLINE_ bool empty() const { return length() == 0; } + _FORCE_INLINE_ bool contains(const char *p_str) const { return find(p_str) != -1; } + _FORCE_INLINE_ bool contains(const String &p_str) const { return find(p_str) != -1; } + + // path functions + bool is_abs_path() const; + bool is_rel_path() const; + bool is_resource_file() const; + String path_to(const String &p_path) const; + String path_to_file(const String &p_path) const; + String get_base_dir() const; + String get_file() const; + static String humanize_size(uint64_t p_size); + String simplify_path() const; + bool is_network_share_path() const; + String append_path(const char *p_path) const; + String append_path(const String &p_path) const; + String path_clean_end_slash() const; + String path_ensure_end_slash() const; + String path_get_prev_dir() const; + + String xml_escape(bool p_escape_quotes = false) const; + String xml_unescape() const; + String http_escape() const; + String http_unescape() const; + String uri_encode() const; + String uri_decode() const; + String c_escape() const; + String c_escape_multiline() const; + String c_unescape() const; + String json_escape() const; + String word_wrap(int p_chars_per_line) const; + Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const; + + String percent_encode() const; + String percent_decode() const; + + String property_name_encode() const; + + // node functions + static String get_invalid_node_name_characters(); + String validate_node_name() const; + String validate_identifier() const; + + bool is_valid_identifier() const; + bool is_valid_integer() const; + bool is_valid_float() const; + bool is_valid_hex_number(bool p_with_prefix) const; + bool is_valid_html_color() const; + bool is_valid_ip_address() const; + bool is_valid_filename() const; + bool is_valid_bool() const; + bool is_valid_unsigned_integer() const; + + /** + * The constructors must not depend on other overloads + */ + + /* String(CharType p_char);*/ + _FORCE_INLINE_ String() {} + _FORCE_INLINE_ String(const String &p_str) { _cowdata._ref(p_str._cowdata); } + + void operator=(const String &p_str) { + _cowdata._ref(p_str._cowdata); + } + + Vector to_ascii_buffer() const; + Vector to_utf8_buffer() const; + Vector to_utf16_buffer() const; + Vector to_utf32_buffer() const; + + String(const char *p_str); + String(const wchar_t *p_str); + String(const CharType *p_str); + String(const char *p_str, int p_clip_to_len); + String(const wchar_t *p_str, int p_clip_to_len); + String(const CharType *p_str, int p_clip_to_len); + String(const StrRange &p_range); + +private: + CowData _cowdata; + static const CharType _null; + + void copy_from(const char *p_cstr); + void copy_from(const char *p_cstr, const int p_clip_to); + void copy_from(const wchar_t *p_cstr); + void copy_from(const wchar_t *p_cstr, const int p_clip_to); + void copy_from(const CharType *p_cstr); + void copy_from(const CharType *p_cstr, const int p_clip_to); + + void copy_from(const CharType &p_char); + + void copy_from_unchecked(const CharType *p_char, const int p_length); + + bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const; + int _count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const; +}; + +bool operator==(const char *p_chr, const String &p_str); +bool operator==(const wchar_t *p_chr, const String &p_str); +bool operator!=(const char *p_chr, const String &p_str); +bool operator!=(const wchar_t *p_chr, const String &p_str); + +String operator+(const char *p_chr, const String &p_str); +String operator+(const wchar_t *p_chr, const String &p_str); +String operator+(CharType p_chr, const String &p_str); + +String itos(int64_t p_val); +String uitos(uint64_t p_val); +String rtos(double p_val); +String rtoss(double p_val); //scientific version + +struct NoCaseComparator { + bool operator()(const String &p_a, const String &p_b) const { + return p_a.nocasecmp_to(p_b) < 0; + } +}; + +struct NaturalNoCaseComparator { + bool operator()(const String &p_a, const String &p_b) const { + return p_a.naturalnocasecmp_to(p_b) < 0; + } +}; + +template +_FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) { + while (true) { + const CharType l = *l_ptr; + const CharType r = *r_ptr; + + if (l == 0 && r == 0) { + return false; + } else if (l == 0) { + return true; + } else if (r == 0) { + return false; + } else if (l < r) { + return true; + } else if (l > r) { + return false; + } + + l_ptr++; + r_ptr++; + } +} + +/* end of namespace */ + +// Tool translate (TTR and variants) for the editor UI, +// and doc translate for the class reference (DTR). +#ifdef TOOLS_ENABLED +// Gets parsed. +String TTR(const String &p_text, const String &p_context = ""); +String DTR(const String &); +// Use for C strings. +#define TTRC(m_value) (m_value) +// Use to avoid parsing (for use later with C strings). +#define TTRGET(m_value) TTR(m_value) + +#else +#define TTR(m_value) (String()) +#define DTR(m_value) (String()) +#define TTRC(m_value) (m_value) +#define TTRGET(m_value) (m_value) +#endif + +// Runtime translate for the public node API. +String RTR(const String &); + +bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end); + +#endif // USTRING_H +#line 0 + + +#line 1 "sfwl/core/string_name.h" +#ifndef STRING_NAME_H +#define STRING_NAME_H + +/*************************************************************************/ +/* string_name.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct StaticCString { + const char *ptr; + static StaticCString create(const char *p_ptr); +}; + +class StringName { + enum { + STRING_TABLE_BITS = 14, + STRING_TABLE_LEN = 1 << STRING_TABLE_BITS, + STRING_TABLE_MASK = STRING_TABLE_LEN - 1 + }; + + struct _Data { + SafeRefCount refcount; + SafeNumeric static_count; + const char *cname; + String name; + +#ifdef DEBUG_ENABLED + uint32_t debug_references; +#endif + + String get_name() const { + return cname ? String(cname) : name; + } + + int idx; + uint32_t hash; + _Data *prev; + _Data *next; + + _Data() { +#ifdef DEBUG_ENABLED + debug_references = 0; +#endif + cname = nullptr; + prev = nullptr; + next = nullptr; + idx = 0; + hash = 0; + } + }; + + static _Data *_table[STRING_TABLE_LEN]; + + _Data *_data; + + union _HashUnion { + _Data *ptr; + uint32_t hash; + + _HashUnion() { + ptr = nullptr; + } + }; + + void unref(); + friend void register_core_types(); + friend void unregister_core_types(); + + static Mutex lock; + static bool configured; +#ifdef DEBUG_ENABLED + struct DebugSortReferences { + bool operator()(const _Data *p_left, const _Data *p_right) const { + return p_left->debug_references > p_right->debug_references; + } + }; + + static bool debug_stringname; +#endif + + StringName(_Data *p_data) { + _data = p_data; + } + +public: + operator const void *() const { + return (_data && (_data->cname || !_data->name.empty())) ? (void *)1 : nullptr; + } + + bool operator==(const String &p_name) const; + bool operator==(const char *p_name) const; + bool operator!=(const String &p_name) const; + + _FORCE_INLINE_ bool is_node_unique_name() const { + if (!_data) { + return false; + } + + if (_data->cname != nullptr) { + return _data->cname[0] == '%'; + } else { + return _data->name[0] == '%'; + } + } + + _FORCE_INLINE_ bool operator<(const StringName &p_name) const { + return _data < p_name._data; + } + _FORCE_INLINE_ bool operator==(const StringName &p_name) const { + // the real magic of all this mess happens here. + // this is why path comparisons are very fast + return _data == p_name._data; + } + _FORCE_INLINE_ uint32_t hash() const { + if (_data) { + return _data->hash; + } else { + return 0; + } + } + _FORCE_INLINE_ const void *data_unique_pointer() const { + return (void *)_data; + } + bool operator!=(const StringName &p_name) const; + + _FORCE_INLINE_ operator String() const { + if (_data) { + if (_data->cname) { + return String(_data->cname); + } else { + return _data->name; + } + } + + return String(); + } + + static StringName search(const char *p_name); + static StringName search(const CharType *p_name); + static StringName search(const String &p_name); + + struct AlphCompare { + _FORCE_INLINE_ bool operator()(const StringName &l, const StringName &r) const { + const char *l_cname = l._data ? l._data->cname : ""; + const char *r_cname = r._data ? r._data->cname : ""; + + if (l_cname) { + if (r_cname) { + return is_str_less(l_cname, r_cname); + } else { + return is_str_less(l_cname, r._data->name.ptr()); + } + } else { + if (r_cname) { + return is_str_less(l._data->name.ptr(), r_cname); + } else { + return is_str_less(l._data->name.ptr(), r._data->name.ptr()); + } + } + } + }; + + void operator=(const StringName &p_name); + StringName(const char *p_name, bool p_static = false); + StringName(const StringName &p_name); + StringName(const String &p_name, bool p_static = false); + StringName(const StaticCString &p_static_string, bool p_static = false); + StringName(); + _FORCE_INLINE_ ~StringName() { + if (likely(configured) && _data) { //only free if configured + unref(); + } + } + + static void setup(); + static void cleanup(); +}; + +/* +bool operator==(const String &p_name, const StringName &p_string_name); +bool operator!=(const String &p_name, const StringName &p_string_name); +bool operator==(const char *p_name, const StringName &p_string_name); +bool operator!=(const char *p_name, const StringName &p_string_name); +*/ + +StringName _scs_create(const char *p_chr, bool p_static = false); + +/* + * The SNAME macro is used to speed up StringName creation, as it allows caching it after the first usage in a very efficient way. + * It should NOT be used everywhere, but instead in places where high performance is required and the creation of a StringName + * can be costly. Places where it should be used are: + * - Control::get_theme_*( and Window::get_theme_*( functions. + * - emit_signal(,..) function + * - call_deferred(,..) function + * - Comparisons to a StringName in overridden _set and _get methods. + * + * Use in places that can be called hundreds of times per frame (or more) is recommended, but this situation is very rare. If in doubt, do not use. + */ + +//#define SNAME(m_arg) ([]() -> const StringName & { static StringName sname = _scs_create(m_arg, true); return sname; })() + +#endif // STRING_NAME_H +#line 0 + + + + +#line 1 "sfwl/core/pool_vector.h" +#ifndef POOL_VECTOR_H +#define POOL_VECTOR_H + +/*************************************************************************/ +/* pool_vector.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct MemoryPool { + //avoid accessing these directly, must be public for template access + + static PoolAllocator *memory_pool; + static uint8_t *pool_memory; + static size_t *pool_size; + + struct Alloc { + SafeRefCount refcount; + SafeNumeric lock; + void *mem; + PoolAllocator::ID pool_id; + size_t size; + + Alloc *free_list; + + Alloc() : + lock(0), + mem(nullptr), + pool_id(POOL_ALLOCATOR_INVALID_ID), + size(0), + free_list(nullptr) { + } + }; + + static Alloc *allocs; + static Alloc *free_list; + static uint32_t alloc_count; + static uint32_t allocs_used; + static Mutex alloc_mutex; + static size_t total_memory; + static size_t max_memory; + + static void setup(uint32_t p_max_allocs = (1 << 16)); + static void cleanup(); +}; + +template +class PoolVector { + MemoryPool::Alloc *alloc; + + void _copy_on_write() { + if (!alloc) { + return; + } + + // ERR_FAIL_COND(alloc->lock>0); should not be illegal to lock this for copy on write, as it's a copy on write after all + + // Refcount should not be zero, otherwise it's a misuse of COW + if (alloc->refcount.get() == 1) { + return; //nothing to do + } + + //must allocate something + + MemoryPool::alloc_mutex.lock(); + if (MemoryPool::allocs_used == MemoryPool::alloc_count) { + MemoryPool::alloc_mutex.unlock(); + ERR_FAIL_MSG("All memory pool allocations are in use, can't COW."); + } + + MemoryPool::Alloc *old_alloc = alloc; + + //take one from the free list + alloc = MemoryPool::free_list; + MemoryPool::free_list = alloc->free_list; + //increment the used counter + MemoryPool::allocs_used++; + + //copy the alloc data + alloc->size = old_alloc->size; + alloc->refcount.init(); + alloc->pool_id = POOL_ALLOCATOR_INVALID_ID; + alloc->lock.set(0); + +#ifdef DEBUG_ENABLED + MemoryPool::total_memory += alloc->size; + if (MemoryPool::total_memory > MemoryPool::max_memory) { + MemoryPool::max_memory = MemoryPool::total_memory; + } +#endif + + MemoryPool::alloc_mutex.unlock(); + + if (MemoryPool::memory_pool) { + } else { + alloc->mem = memalloc(alloc->size); + } + + { + Write w; + w._ref(alloc); + Read r; + r._ref(old_alloc); + + int cur_elements = alloc->size / sizeof(T); + T *dst = (T *)w.ptr(); + const T *src = (const T *)r.ptr(); + for (int i = 0; i < cur_elements; i++) { + memnew_placement(&dst[i], T(src[i])); + } + } + + if (old_alloc->refcount.unref()) { + //this should never happen but.. + +#ifdef DEBUG_ENABLED + MemoryPool::alloc_mutex.lock(); + MemoryPool::total_memory -= old_alloc->size; + MemoryPool::alloc_mutex.unlock(); +#endif + + { + Write w; + w._ref(old_alloc); + + int cur_elements = old_alloc->size / sizeof(T); + T *elems = (T *)w.ptr(); + for (int i = 0; i < cur_elements; i++) { + elems[i].~T(); + } + } + + if (MemoryPool::memory_pool) { + //resize memory pool + //if none, create + //if some resize + } else { + memfree(old_alloc->mem); + old_alloc->mem = nullptr; + old_alloc->size = 0; + + MemoryPool::alloc_mutex.lock(); + old_alloc->free_list = MemoryPool::free_list; + MemoryPool::free_list = old_alloc; + MemoryPool::allocs_used--; + MemoryPool::alloc_mutex.unlock(); + } + } + } + + void _reference(const PoolVector &p_pool_vector) { + if (alloc == p_pool_vector.alloc) { + return; + } + + _unreference(); + + if (!p_pool_vector.alloc) { + return; + } + + if (p_pool_vector.alloc->refcount.ref()) { + alloc = p_pool_vector.alloc; + } + } + + void _unreference() { + if (!alloc) { + return; + } + + if (!alloc->refcount.unref()) { + alloc = nullptr; + return; + } + + //must be disposed! + + { + int cur_elements = alloc->size / sizeof(T); + + // Don't use write() here because it could otherwise provoke COW, + // which is not desirable here because we are destroying the last reference anyways + Write w; + // Reference to still prevent other threads from touching the alloc + w._ref(alloc); + + for (int i = 0; i < cur_elements; i++) { + w[i].~T(); + } + } + +#ifdef DEBUG_ENABLED + MemoryPool::alloc_mutex.lock(); + MemoryPool::total_memory -= alloc->size; + MemoryPool::alloc_mutex.unlock(); +#endif + + if (MemoryPool::memory_pool) { + //resize memory pool + //if none, create + //if some resize + } else { + memfree(alloc->mem); + alloc->mem = nullptr; + alloc->size = 0; + + MemoryPool::alloc_mutex.lock(); + alloc->free_list = MemoryPool::free_list; + MemoryPool::free_list = alloc; + MemoryPool::allocs_used--; + MemoryPool::alloc_mutex.unlock(); + } + + alloc = nullptr; + } + +public: + class Access { + friend class PoolVector; + + protected: + MemoryPool::Alloc *alloc; + T *mem; + + _FORCE_INLINE_ void _ref(MemoryPool::Alloc *p_alloc) { + alloc = p_alloc; + if (alloc) { + if (alloc->lock.increment() == 1) { + if (MemoryPool::memory_pool) { + //lock it and get mem + } + } + + mem = (T *)alloc->mem; + } + } + + _FORCE_INLINE_ void _unref() { + if (alloc) { + if (alloc->lock.decrement() == 0) { + if (MemoryPool::memory_pool) { + //put mem back + } + } + + mem = nullptr; + alloc = nullptr; + } + } + + Access() { + alloc = nullptr; + mem = nullptr; + } + + public: + virtual ~Access() { + _unref(); + } + + void release() { + _unref(); + } + }; + + class Read : public Access { + public: + _FORCE_INLINE_ const T &operator[](int p_index) const { return this->mem[p_index]; } + _FORCE_INLINE_ const T *ptr() const { return this->mem; } + + void operator=(const Read &p_read) { + if (this->alloc == p_read.alloc) { + return; + } + this->_unref(); + this->_ref(p_read.alloc); + } + + Read(const Read &p_read) { + this->_ref(p_read.alloc); + } + + Read() {} + }; + + class Write : public Access { + public: + _FORCE_INLINE_ T &operator[](int p_index) const { return this->mem[p_index]; } + _FORCE_INLINE_ T *ptr() const { return this->mem; } + + void operator=(const Write &p_read) { + if (this->alloc == p_read.alloc) { + return; + } + this->_unref(); + this->_ref(p_read.alloc); + } + + Write(const Write &p_read) { + this->_ref(p_read.alloc); + } + + Write() {} + }; + + Read read() const { + Read r; + if (alloc) { + r._ref(alloc); + } + return r; + } + Write write() { + Write w; + if (alloc) { + _copy_on_write(); //make sure there is only one being accessed + w._ref(alloc); + } + return w; + } + + template + void fill_with(const MC &p_mc) { + int c = p_mc.size(); + resize(c); + Write w = write(); + int idx = 0; + for (const typename MC::Element *E = p_mc.front(); E; E = E->next()) { + w[idx++] = E->get(); + } + } + + void remove(int p_index) { + int s = size(); + ERR_FAIL_INDEX(p_index, s); + Write w = write(); + for (int i = p_index; i < s - 1; i++) { + w[i] = w[i + 1]; + }; + w = Write(); + resize(s - 1); + } + + inline int size() const; + inline bool empty() const; + T get(int p_index) const; + void set(int p_index, const T &p_val); + void fill(const T &p_val); + void push_back(const T &p_val); + void append(const T &p_val) { + push_back(p_val); + } + void append_array(const PoolVector &p_arr) { + int ds = p_arr.size(); + if (ds == 0) { + return; + } + int bs = size(); + resize(bs + ds); + Write w = write(); + Read r = p_arr.read(); + for (int i = 0; i < ds; i++) { + w[bs + i] = r[i]; + } + } + + PoolVector subarray(int p_from, int p_to) const { + if (p_from < 0) { + p_from = size() + p_from; + } + if (p_to < 0) { + p_to = size() + p_to; + } + + ERR_FAIL_INDEX_V(p_from, size(), PoolVector()); + ERR_FAIL_INDEX_V(p_to, size(), PoolVector()); + + PoolVector slice; + int span = 1 + p_to - p_from; + slice.resize(span); + Read r = read(); + Write w = slice.write(); + for (int i = 0; i < span; ++i) { + w[i] = r[p_from + i]; + } + + return slice; + } + + Error insert(int p_pos, const T &p_val) { + int s = size(); + ERR_FAIL_INDEX_V(p_pos, s + 1, ERR_INVALID_PARAMETER); + resize(s + 1); + { + Write w = write(); + for (int i = s; i > p_pos; i--) { + w[i] = w[i - 1]; + } + w[p_pos] = p_val; + } + + return OK; + } + + String join(const String &delimiter) const { + String rs = ""; + int s = size(); + Read r = read(); + for (int i = 0; i < s; i++) { + rs += r[i] + delimiter; + } + rs.erase(rs.length() - delimiter.length(), delimiter.length()); + return rs; + } + + bool contains(const T &p_val) const; + int find(const T &p_val, int p_from = 0) const; + int rfind(const T &p_val, int p_from = -1) const; + int count(const T &p_val) const; + bool has(const T &p_val) const; + + bool is_locked() const { + return alloc && alloc->lock.get() > 0; + } + + inline T operator[](int p_index) const; + + Error resize(int p_size); + Error clear() { + return resize(0); + } + + void invert(); + void sort(); + + void operator=(const PoolVector &p_pool_vector) { + _reference(p_pool_vector); + } + PoolVector() { + alloc = nullptr; + } + PoolVector(const PoolVector &p_pool_vector) { + alloc = nullptr; + _reference(p_pool_vector); + } + ~PoolVector() { + _unreference(); + } +}; + +template +int PoolVector::size() const { + return alloc ? alloc->size / sizeof(T) : 0; +} + +template +bool PoolVector::empty() const { + return alloc ? alloc->size == 0 : true; +} + +template +T PoolVector::get(int p_index) const { + return operator[](p_index); +} + +template +void PoolVector::set(int p_index, const T &p_val) { + ERR_FAIL_INDEX(p_index, size()); + + Write w = write(); + w[p_index] = p_val; +} + +template +void PoolVector::fill(const T &p_val) { + Write w = write(); + for (int i = 0; i < size(); i++) { + w[i] = p_val; + } +} + +template +void PoolVector::push_back(const T &p_val) { + resize(size() + 1); + set(size() - 1, p_val); +} + +template +bool PoolVector::contains(const T &p_val) const { + Read r = read(); + int s = size(); + + for (int i = 0; i < s; ++i) { + if (r[i] == p_val) { + return true; + } + } + + return false; +} + +template +int PoolVector::find(const T &p_val, int p_from) const { + if (p_from < 0) { + return -1; + } + + const int s = size(); + const Read r = read(); + + for (int i = p_from; i < s; i++) { + if (r[i] == p_val) { + return i; + } + } + return -1; +} + +template +int PoolVector::rfind(const T &p_val, int p_from) const { + const int s = size(); + const Read r = read(); + + if (p_from < 0) { + p_from = s + p_from; + } + if (p_from < 0 || p_from >= s) { + p_from = s - 1; + } + + for (int i = p_from; i >= 0; i--) { + if (r[i] == p_val) { + return i; + } + } + return -1; +} + +template +int PoolVector::count(const T &p_val) const { + const int s = size(); + const Read r = read(); + int amount = 0; + for (int i = 0; i < s; i++) { + if (r[i] == p_val) { + amount++; + } + } + return amount; +} + +template +bool PoolVector::has(const T &p_val) const { + return find(p_val) != -1; +} + +template +T PoolVector::operator[](int p_index) const { + CRASH_BAD_INDEX(p_index, size()); + + Read r = read(); + return r[p_index]; +} + +template +Error PoolVector::resize(int p_size) { + ERR_FAIL_COND_V_MSG(p_size < 0, ERR_INVALID_PARAMETER, "Size of PoolVector cannot be negative."); + + if (alloc == nullptr) { + if (p_size == 0) { + return OK; //nothing to do here + } + + //must allocate something + MemoryPool::alloc_mutex.lock(); + if (MemoryPool::allocs_used == MemoryPool::alloc_count) { + MemoryPool::alloc_mutex.unlock(); + ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "All memory pool allocations are in use."); + } + + //take one from the free list + alloc = MemoryPool::free_list; + MemoryPool::free_list = alloc->free_list; + //increment the used counter + MemoryPool::allocs_used++; + + //cleanup the alloc + alloc->size = 0; + alloc->refcount.init(); + alloc->pool_id = POOL_ALLOCATOR_INVALID_ID; + MemoryPool::alloc_mutex.unlock(); + + } else { + ERR_FAIL_COND_V_MSG(alloc->lock.get() > 0, ERR_LOCKED, "Can't resize PoolVector if locked."); //can't resize if locked! + } + + size_t new_size = sizeof(T) * p_size; + + if (alloc->size == new_size) { + return OK; //nothing to do + } + + if (p_size == 0) { + _unreference(); + return OK; + } + + _copy_on_write(); // make it unique + +#ifdef DEBUG_ENABLED + MemoryPool::alloc_mutex.lock(); + MemoryPool::total_memory -= alloc->size; + MemoryPool::total_memory += new_size; + if (MemoryPool::total_memory > MemoryPool::max_memory) { + MemoryPool::max_memory = MemoryPool::total_memory; + } + MemoryPool::alloc_mutex.unlock(); +#endif + + int cur_elements = alloc->size / sizeof(T); + + if (p_size > cur_elements) { + if (MemoryPool::memory_pool) { + //resize memory pool + //if none, create + //if some resize + } else { + if (alloc->size == 0) { + alloc->mem = memalloc(new_size); + } else { + alloc->mem = memrealloc(alloc->mem, new_size); + } + } + + alloc->size = new_size; + + Write w = write(); + + for (int i = cur_elements; i < p_size; i++) { + memnew_placement(&w[i], T); + } + + } else { + { + Write w = write(); + for (int i = p_size; i < cur_elements; i++) { + w[i].~T(); + } + } + + if (MemoryPool::memory_pool) { + //resize memory pool + //if none, create + //if some resize + } else { + if (new_size == 0) { + memfree(alloc->mem); + alloc->mem = nullptr; + alloc->size = 0; + + MemoryPool::alloc_mutex.lock(); + alloc->free_list = MemoryPool::free_list; + MemoryPool::free_list = alloc; + MemoryPool::allocs_used--; + MemoryPool::alloc_mutex.unlock(); + + } else { + alloc->mem = memrealloc(alloc->mem, new_size); + alloc->size = new_size; + } + } + } + + return OK; +} + +template +void PoolVector::invert() { + T temp; + Write w = write(); + int s = size(); + int half_s = s / 2; + + for (int i = 0; i < half_s; i++) { + temp = w[i]; + w[i] = w[s - i - 1]; + w[s - i - 1] = temp; + } +} + +template +void PoolVector::sort() { + int len = size(); + if (len == 0) { + return; + } + + Write w = write(); + SortArray sorter; + sorter.sort(w.ptr(), len); +} + +#endif // POOL_VECTOR_H +#line 0 + +#line 1 "sfwl/core/tight_local_vector.h" +#ifndef TIGHT_LOCAL_VECTOR_H +#define TIGHT_LOCAL_VECTOR_H + +/*************************************************************************/ +/* tight_local_vector.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +// It grows strictly as much as needed. (The vanilla LocalVector is what you want in most cases). +template +class TightLocalVector { +private: + U count = 0; + U capacity = 0; + T *data = nullptr; + +public: + T *ptr() { + return data; + } + + const T *ptr() const { + return data; + } + + _FORCE_INLINE_ void push_back(T p_elem) { + if (unlikely(count == capacity)) { + if (capacity == 0) { + capacity = 1; + } else { + capacity <<= 1; + } + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + + if constexpr (!HAS_TRIVIAL_CONSTRUCTOR(T) && !force_trivial) { + memnew_placement(&data[count++], T(p_elem)); + } else { + data[count++] = p_elem; + } + } + + void remove(U p_index) { + ERR_FAIL_UNSIGNED_INDEX(p_index, count); + count--; + for (U i = p_index; i < count; i++) { + data[i] = data[i + 1]; + } + if constexpr (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { + data[count].~T(); + } + } + + /// Removes the item copying the last value into the position of the one to + /// remove. It's generally faster than `remove`. + void remove_unordered(U p_index) { + ERR_FAIL_INDEX(p_index, count); + count--; + if (count > p_index) { + data[p_index] = data[count]; + } + if constexpr (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { + data[count].~T(); + } + } + + void erase(const T &p_val) { + int64_t idx = find(p_val); + if (idx >= 0) { + remove(idx); + } + } + + U erase_multiple_unordered(const T &p_val) { + U from = 0; + U count = 0; + while (true) { + int64_t idx = find(p_val, from); + + if (idx == -1) { + break; + } + remove_unordered(idx); + from = idx; + count++; + } + return count; + } + + void invert() { + for (U i = 0; i < count / 2; i++) { + SWAP(data[i], data[count - i - 1]); + } + } + + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ void reset() { + clear(); + if (data) { + memfree(data); + data = nullptr; + capacity = 0; + } + } + _FORCE_INLINE_ bool empty() const { return count == 0; } + _FORCE_INLINE_ U get_capacity() const { return capacity; } + _FORCE_INLINE_ void reserve(U p_size) { + if (p_size > capacity) { + capacity = p_size; + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + } + + _FORCE_INLINE_ U size() const { return count; } + void resize(U p_size) { + if (p_size < count) { + if (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { + for (U i = p_size; i < count; i++) { + data[i].~T(); + } + } + count = p_size; + } else if (p_size > count) { + if (unlikely(p_size > capacity)) { + if (capacity == 0) { + capacity = 1; + } + while (capacity < p_size) { + capacity <<= 1; + } + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + if (!HAS_TRIVIAL_CONSTRUCTOR(T) && !force_trivial) { + for (U i = count; i < p_size; i++) { + memnew_placement(&data[i], T); + } + } + count = p_size; + } + } + _FORCE_INLINE_ const T &operator[](U p_index) const { + CRASH_BAD_UNSIGNED_INDEX(p_index, count); + return data[p_index]; + } + _FORCE_INLINE_ T &operator[](U p_index) { + CRASH_BAD_UNSIGNED_INDEX(p_index, count); + return data[p_index]; + } + + void fill(T p_val) { + for (U i = 0; i < count; i++) { + data[i] = p_val; + } + } + + void insert(U p_pos, T p_val) { + ERR_FAIL_UNSIGNED_INDEX(p_pos, count + 1); + if (p_pos == count) { + push_back(p_val); + } else { + resize(count + 1); + for (U i = count - 1; i > p_pos; i--) { + data[i] = data[i - 1]; + } + data[p_pos] = p_val; + } + } + + int64_t find(const T &p_val, U p_from = 0) const { + for (U i = p_from; i < count; i++) { + if (data[i] == p_val) { + return int64_t(i); + } + } + return -1; + } + + template + void sort_custom() { + U len = count; + if (len == 0) { + return; + } + + SortArray sorter; + sorter.sort(data, len); + } + + void sort() { + sort_custom<_DefaultComparator>(); + } + + void ordered_insert(T p_val) { + U i; + for (i = 0; i < count; i++) { + if (p_val < data[i]) { + break; + } + } + insert(i, p_val); + } + + operator Vector() const { + Vector ret; + ret.resize(size()); + T *w = ret.ptrw(); + memcpy(w, data, sizeof(T) * count); + return ret; + } + + operator PoolVector() const { + PoolVector pl; + if (size()) { + pl.resize(size()); + typename PoolVector::Write w = pl.write(); + T *dest = w.ptr(); + memcpy(dest, data, sizeof(T) * count); + } + return pl; + } + + Vector to_byte_array() const { //useful to pass stuff to gpu or variant + Vector ret; + ret.resize(count * sizeof(T)); + uint8_t *w = ret.ptrw(); + memcpy(w, data, sizeof(T) * count); + return ret; + } + + _FORCE_INLINE_ TightLocalVector() {} + _FORCE_INLINE_ TightLocalVector(const TightLocalVector &p_from) { + resize(p_from.size()); + for (U i = 0; i < p_from.count; i++) { + data[i] = p_from.data[i]; + } + } + TightLocalVector(const Vector &p_from) { + resize(p_from.size()); + for (U i = 0; i < count; i++) { + data[i] = p_from[i]; + } + } + TightLocalVector(const PoolVector &p_from) { + resize(p_from.size()); + typename PoolVector::Read r = p_from.read(); + for (U i = 0; i < count; i++) { + data[i] = r[i]; + } + } + + inline void operator=(const TightLocalVector &p_from) { + resize(p_from.size()); + for (U i = 0; i < p_from.count; i++) { + data[i] = p_from.data[i]; + } + } + inline void operator=(const Vector &p_from) { + resize(p_from.size()); + for (U i = 0; i < count; i++) { + data[i] = p_from[i]; + } + } + inline TightLocalVector &operator=(const PoolVector &p_from) { + resize(p_from.size()); + typename PoolVector::Read r = p_from.read(); + for (U i = 0; i < count; i++) { + data[i] = r[i]; + } + return *this; + } + + _FORCE_INLINE_ ~TightLocalVector() { + if (data) { + reset(); + } + } +}; + +// Integer default version +template +class TightLocalVectori : public TightLocalVector { +}; + +#endif // TIGHT_LOCAL_VECTOR_H +#line 0 + +#line 1 "sfwl/core/local_vector.h" +#ifndef LOCAL_VECTOR_H +#define LOCAL_VECTOR_H + +/*************************************************************************/ +/* local_vector.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +template +class LocalVector { +protected: + U count = 0; + U capacity = 0; + T *data = nullptr; + +public: + T *ptr() { + return data; + } + + const T *ptr() const { + return data; + } + + _FORCE_INLINE_ void push_back(T p_elem) { + if (unlikely(count == capacity)) { + if (capacity == 0) { + capacity = 1; + } else { + capacity <<= 1; + } + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + + if (!HAS_TRIVIAL_CONSTRUCTOR(T) && !force_trivial) { + memnew_placement(&data[count++], T(p_elem)); + } else { + data[count++] = p_elem; + } + } + + void remove(U p_index) { + ERR_FAIL_UNSIGNED_INDEX(p_index, count); + count--; + for (U i = p_index; i < count; i++) { + data[i] = data[i + 1]; + } + if (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { + data[count].~T(); + } + } + + // Removes the item copying the last value into the position of the one to + // remove. It's generally faster than `remove`. + void remove_unordered(U p_index) { + ERR_FAIL_INDEX(p_index, count); + count--; + if (count > p_index) { + data[p_index] = data[count]; + } + if (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { + data[count].~T(); + } + } + + _FORCE_INLINE_ bool erase(const T &p_val) { + int64_t idx = find(p_val); + if (idx >= 0) { + remove(idx); + return true; + } + return false; + } + + U erase_multiple_unordered(const T &p_val) { + U from = 0; + U count = 0; + while (true) { + int64_t idx = find(p_val, from); + + if (idx == -1) { + break; + } + remove_unordered(idx); + from = idx; + count++; + } + return count; + } + + void invert() { + for (U i = 0; i < count / 2; i++) { + SWAP(data[i], data[count - i - 1]); + } + } + + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ void reset() { + clear(); + if (data) { + memfree(data); + data = nullptr; + capacity = 0; + } + } + _FORCE_INLINE_ bool empty() const { return count == 0; } + _FORCE_INLINE_ U get_capacity() const { return capacity; } + _FORCE_INLINE_ void reserve(U p_size, bool p_allow_shrink = false) { + p_size = nearest_power_of_2_templated(p_size); + if (!p_allow_shrink ? p_size > capacity : ((p_size >= count) && (p_size != capacity))) { + capacity = p_size; + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + } + + _FORCE_INLINE_ U size() const { return count; } + void resize(U p_size) { + if (p_size < count) { + if (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { + for (U i = p_size; i < count; i++) { + data[i].~T(); + } + } + count = p_size; + } else if (p_size > count) { + if (unlikely(p_size > capacity)) { + if (capacity == 0) { + capacity = 1; + } + while (capacity < p_size) { + capacity <<= 1; + } + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + if (!HAS_TRIVIAL_CONSTRUCTOR(T) && !force_trivial) { + for (U i = count; i < p_size; i++) { + memnew_placement(&data[i], T); + } + } + count = p_size; + } + } + _FORCE_INLINE_ const T &operator[](U p_index) const { + CRASH_BAD_UNSIGNED_INDEX(p_index, count); + return data[p_index]; + } + _FORCE_INLINE_ T &operator[](U p_index) { + CRASH_BAD_UNSIGNED_INDEX(p_index, count); + return data[p_index]; + } + + void fill(T p_val) { + for (U i = 0; i < count; i++) { + data[i] = p_val; + } + } + + void insert(U p_pos, T p_val) { + ERR_FAIL_UNSIGNED_INDEX(p_pos, count + 1); + if (p_pos == count) { + push_back(p_val); + } else { + resize(count + 1); + for (U i = count - 1; i > p_pos; i--) { + data[i] = data[i - 1]; + } + data[p_pos] = p_val; + } + } + + int64_t find(const T &p_val, U p_from = 0) const { + for (U i = p_from; i < count; i++) { + if (data[i] == p_val) { + return int64_t(i); + } + } + return -1; + } + + template + void sort_custom() { + U len = count; + if (len == 0) { + return; + } + + SortArray sorter; + sorter.sort(data, len); + } + + void sort() { + sort_custom<_DefaultComparator>(); + } + + void ordered_insert(T p_val) { + U i; + for (i = 0; i < count; i++) { + if (p_val < data[i]) { + break; + } + } + insert(i, p_val); + } + + operator Vector() const { + Vector ret; + ret.resize(size()); + T *w = ret.ptrw(); + memcpy(w, data, sizeof(T) * count); + return ret; + } + + operator PoolVector() const { + PoolVector pl; + if (size()) { + pl.resize(size()); + typename PoolVector::Write w = pl.write(); + T *dest = w.ptr(); + memcpy(dest, data, sizeof(T) * count); + } + return pl; + } + + Vector to_byte_array() const { //useful to pass stuff to gpu or variant + Vector ret; + ret.resize(count * sizeof(T)); + uint8_t *w = ret.ptrw(); + memcpy(w, data, sizeof(T) * count); + return ret; + } + + _FORCE_INLINE_ LocalVector() {} + _FORCE_INLINE_ LocalVector(const LocalVector &p_from) { + resize(p_from.size()); + for (U i = 0; i < p_from.count; i++) { + data[i] = p_from.data[i]; + } + } + LocalVector(const Vector &p_from) { + resize(p_from.size()); + for (U i = 0; i < count; i++) { + data[i] = p_from[i]; + } + } + LocalVector(const PoolVector &p_from) { + resize(p_from.size()); + typename PoolVector::Read r = p_from.read(); + for (U i = 0; i < count; i++) { + data[i] = r[i]; + } + } + + inline LocalVector &operator=(const LocalVector &p_from) { + resize(p_from.size()); + for (U i = 0; i < p_from.count; i++) { + data[i] = p_from.data[i]; + } + return *this; + } + inline LocalVector &operator=(const Vector &p_from) { + resize(p_from.size()); + for (U i = 0; i < count; i++) { + data[i] = p_from[i]; + } + return *this; + } + inline LocalVector &operator=(const PoolVector &p_from) { + resize(p_from.size()); + typename PoolVector::Read r = p_from.read(); + for (U i = 0; i < count; i++) { + data[i] = r[i]; + } + return *this; + } + + _FORCE_INLINE_ ~LocalVector() { + if (data) { + reset(); + } + } +}; + +// Integer default version +template +class LocalVectori : public LocalVector { +}; + +#endif // LOCAL_VECTOR_H +#line 0 + + + + +#line 1 "sfwl/core/color.h" +#ifndef COLOR_H +#define COLOR_H + +/*************************************************************************/ +/* color.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct _NO_DISCARD_CLASS_ Color { + union { + struct { + float r; + float g; + float b; + float a; + }; + float components[4]; + }; + + bool operator==(const Color &p_color) const { return (r == p_color.r && g == p_color.g && b == p_color.b && a == p_color.a); } + bool operator!=(const Color &p_color) const { return (r != p_color.r || g != p_color.g || b != p_color.b || a != p_color.a); } + + uint32_t to_rgba32() const; + uint32_t to_argb32() const; + uint32_t to_abgr32() const; + uint64_t to_rgba64() const; + uint64_t to_argb64() const; + uint64_t to_abgr64() const; + float get_h() const; + float get_s() const; + float get_v() const; + void set_hsv(float p_h, float p_s, float p_v, float p_alpha = 1.0); + + _FORCE_INLINE_ float &operator[](int idx) { + return components[idx]; + } + _FORCE_INLINE_ const float &operator[](int idx) const { + return components[idx]; + } + + Color operator+(const Color &p_color) const; + void operator+=(const Color &p_color); + + Color operator-() const; + Color operator-(const Color &p_color) const; + void operator-=(const Color &p_color); + + Color operator*(const Color &p_color) const; + Color operator*(const real_t &rvalue) const; + void operator*=(const Color &p_color); + void operator*=(const real_t &rvalue); + + Color operator/(const Color &p_color) const; + Color operator/(const real_t &rvalue) const; + void operator/=(const Color &p_color); + void operator/=(const real_t &rvalue); + + bool is_equal_approx(const Color &p_color) const; + + Color clamp(const Color &p_min = Color(0, 0, 0, 0), const Color &p_max = Color(1, 1, 1, 1)) const; + + void invert(); + void contrast(); + Color inverted() const; + Color contrasted() const; + + _FORCE_INLINE_ float get_luminance() const { + return 0.2126 * r + 0.7152 * g + 0.0722 * b; + } + + _FORCE_INLINE_ Color linear_interpolate(const Color &p_to, float p_weight) const { + Color res = *this; + + res.r += (p_weight * (p_to.r - r)); + res.g += (p_weight * (p_to.g - g)); + res.b += (p_weight * (p_to.b - b)); + res.a += (p_weight * (p_to.a - a)); + + return res; + } + + _FORCE_INLINE_ Color darkened(float p_amount) const { + Color res = *this; + res.r = res.r * (1.0f - p_amount); + res.g = res.g * (1.0f - p_amount); + res.b = res.b * (1.0f - p_amount); + return res; + } + + _FORCE_INLINE_ Color lightened(float p_amount) const { + Color res = *this; + res.r = res.r + (1.0f - res.r) * p_amount; + res.g = res.g + (1.0f - res.g) * p_amount; + res.b = res.b + (1.0f - res.b) * p_amount; + return res; + } + + _FORCE_INLINE_ uint32_t to_rgbe9995() const { + const float pow2to9 = 512.0f; + const float B = 15.0f; + //const float Emax = 31.0f; + const float N = 9.0f; + + float sharedexp = 65408.000f; //(( pow2to9 - 1.0f)/ pow2to9)*powf( 2.0f, 31.0f - 15.0f); + + float cRed = MAX(0.0f, MIN(sharedexp, r)); + float cGreen = MAX(0.0f, MIN(sharedexp, g)); + float cBlue = MAX(0.0f, MIN(sharedexp, b)); + + float cMax = MAX(cRed, MAX(cGreen, cBlue)); + + // expp = MAX(-B - 1, log2(maxc)) + 1 + B + + float expp = MAX(-B - 1.0f, floor(Math::log(cMax) / Math_LN2)) + 1.0f + B; + + float sMax = (float)floor((cMax / Math::pow(2.0f, expp - B - N)) + 0.5f); + + float exps = expp + 1.0f; + + if (0.0 <= sMax && sMax < pow2to9) { + exps = expp; + } + + float sRed = Math::floor((cRed / pow(2.0f, exps - B - N)) + 0.5f); + float sGreen = Math::floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f); + float sBlue = Math::floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f); + + return (uint32_t(Math::fast_ftoi(sRed)) & 0x1FF) | ((uint32_t(Math::fast_ftoi(sGreen)) & 0x1FF) << 9) | ((uint32_t(Math::fast_ftoi(sBlue)) & 0x1FF) << 18) | ((uint32_t(Math::fast_ftoi(exps)) & 0x1F) << 27); + } + + _FORCE_INLINE_ Color blend(const Color &p_over) const { + Color res; + float sa = 1.0 - p_over.a; + res.a = a * sa + p_over.a; + if (res.a == 0) { + return Color(0, 0, 0, 0); + } else { + res.r = (r * a * sa + p_over.r * p_over.a) / res.a; + res.g = (g * a * sa + p_over.g * p_over.a) / res.a; + res.b = (b * a * sa + p_over.b * p_over.a) / res.a; + } + return res; + } + + _FORCE_INLINE_ Color to_linear() const { + return Color( + r < 0.04045 ? r * (1.0 / 12.92) : Math::pow((r + 0.055) * (1.0 / (1 + 0.055)), 2.4), + g < 0.04045 ? g * (1.0 / 12.92) : Math::pow((g + 0.055) * (1.0 / (1 + 0.055)), 2.4), + b < 0.04045 ? b * (1.0 / 12.92) : Math::pow((b + 0.055) * (1.0 / (1 + 0.055)), 2.4), + a); + } + _FORCE_INLINE_ Color to_srgb() const { + return Color( + r < 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math::pow(r, 1.0f / 2.4f) - 0.055, + g < 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math::pow(g, 1.0f / 2.4f) - 0.055, + b < 0.0031308 ? 12.92 * b : (1.0 + 0.055) * Math::pow(b, 1.0f / 2.4f) - 0.055, a); + } + + static Color hex(uint32_t p_hex); + static Color hex64(uint64_t p_hex); + static Color html(const String &p_color); + static bool html_is_valid(const String &p_color); + String to_html(bool p_alpha = true) const; + Color from_hsv(float p_h, float p_s, float p_v, float p_a) const; + static Color from_rgbe9995(uint32_t p_rgbe); + + _FORCE_INLINE_ bool operator<(const Color &p_color) const; //used in set keys + operator String() const; + + static _FORCE_INLINE_ Color color8(int r, int g, int b) { + return Color(static_cast(r) / 255.0f, static_cast(g) / 255.0f, static_cast(b) / 255.0f); + } + + static _FORCE_INLINE_ Color color8(int r, int g, int b, int a) { + return Color(static_cast(r) / 255.0f, static_cast(g) / 255.0f, static_cast(b) / 255.0f, static_cast(a) / 255.0f); + } + + _FORCE_INLINE_ void set_r8(int32_t r8) { r = (CLAMP(r8, 0, 255) / 255.0f); } + _FORCE_INLINE_ int32_t get_r8() const { return int32_t(CLAMP(Math::round(r * 255.0f), 0.0f, 255.0f)); } + _FORCE_INLINE_ void set_g8(int32_t g8) { g = (CLAMP(g8, 0, 255) / 255.0f); } + _FORCE_INLINE_ int32_t get_g8() const { return int32_t(CLAMP(Math::round(g * 255.0f), 0.0f, 255.0f)); } + _FORCE_INLINE_ void set_b8(int32_t b8) { b = (CLAMP(b8, 0, 255) / 255.0f); } + _FORCE_INLINE_ int32_t get_b8() const { return int32_t(CLAMP(Math::round(b * 255.0f), 0.0f, 255.0f)); } + _FORCE_INLINE_ void set_a8(int32_t a8) { a = (CLAMP(a8, 0, 255) / 255.0f); } + _FORCE_INLINE_ int32_t get_a8() const { return int32_t(CLAMP(Math::round(a * 255.0f), 0.0f, 255.0f)); } + + _FORCE_INLINE_ void set_h(float p_h) { set_hsv(p_h, get_s(), get_v(), a); } + _FORCE_INLINE_ void set_s(float p_s) { set_hsv(get_h(), p_s, get_v(), a); } + _FORCE_INLINE_ void set_v(float p_v) { set_hsv(get_h(), get_s(), p_v, a); } + + /** + * No construct parameters, r=0, g=0, b=0. a=255 + */ + _FORCE_INLINE_ Color() { + r = 0; + g = 0; + b = 0; + a = 1.0; + } + + /** + * RGB / RGBA construct parameters. Alpha is optional, but defaults to 1.0 + */ + _FORCE_INLINE_ Color(float p_r, float p_g, float p_b, float p_a = 1.0) { + r = p_r; + g = p_g; + b = p_b; + a = p_a; + } + + /** + * Construct a Color from another Color, but with the specified alpha value. + */ + _FORCE_INLINE_ Color(const Color &p_c, float p_a) { + r = p_c.r; + g = p_c.g; + b = p_c.b; + a = p_a; + } +}; + +bool Color::operator<(const Color &p_color) const { + if (r == p_color.r) { + if (g == p_color.g) { + if (b == p_color.b) { + return (a < p_color.a); + } else { + return (b < p_color.b); + } + } else { + return g < p_color.g; + } + } else { + return r < p_color.r; + } +} + +#endif +#line 0 + +#line 1 "sfwl/core/vector2.h" +#ifndef VECTOR2_H +#define VECTOR2_H + +/*************************************************************************/ +/* vector2.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +class String; + +struct _NO_DISCARD_CLASS_ Vector2 { + static const int AXIS_COUNT = 2; + + enum Axis { + AXIS_X, + AXIS_Y, + }; + + union { + struct { + union { + real_t x; + real_t width; + }; + union { + real_t y; + real_t height; + }; + }; + + real_t coord[2]; + }; + + _FORCE_INLINE_ real_t &operator[](int p_idx) { + DEV_ASSERT((unsigned int)p_idx < 2); + return coord[p_idx]; + } + _FORCE_INLINE_ const real_t &operator[](int p_idx) const { + DEV_ASSERT((unsigned int)p_idx < 2); + return coord[p_idx]; + } + + _FORCE_INLINE_ void set_all(real_t p_value) { + x = y = p_value; + } + + _FORCE_INLINE_ int min_axis() const { + return x < y ? 0 : 1; + } + + _FORCE_INLINE_ int max_axis() const { + return x < y ? 1 : 0; + } + + void normalize(); + Vector2 normalized() const; + bool is_normalized() const; + + real_t length() const; + real_t length_squared() const; + Vector2 limit_length(const real_t p_len = 1.0) const; + + Vector2 min(const Vector2 &p_vector2) const { + return Vector2(MIN(x, p_vector2.x), MIN(y, p_vector2.y)); + } + + Vector2 max(const Vector2 &p_vector2) const { + return Vector2(MAX(x, p_vector2.x), MAX(y, p_vector2.y)); + } + + real_t distance_to(const Vector2 &p_vector2) const; + real_t distance_squared_to(const Vector2 &p_vector2) const; + real_t angle_to(const Vector2 &p_vector2) const; + real_t angle_to_point(const Vector2 &p_vector2) const; + _FORCE_INLINE_ Vector2 direction_to(const Vector2 &p_to) const; + + real_t dot(const Vector2 &p_other) const; + real_t cross(const Vector2 &p_other) const; + Vector2 posmod(const real_t p_mod) const; + Vector2 posmodv(const Vector2 &p_modv) const; + Vector2 project(const Vector2 &p_to) const; + + Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const; + + _FORCE_INLINE_ static Vector2 linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_weight); + _FORCE_INLINE_ Vector2 linear_interpolate(const Vector2 &p_to, real_t p_weight) const; + _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_to, real_t p_weight) const; + _FORCE_INLINE_ Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_weight) const; + _FORCE_INLINE_ Vector2 bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const; + + Vector2 move_toward(const Vector2 &p_to, const real_t p_delta) const; + + Vector2 slide(const Vector2 &p_normal) const; + Vector2 bounce(const Vector2 &p_normal) const; + Vector2 reflect(const Vector2 &p_normal) const; + + bool is_equal_approx(const Vector2 &p_v) const; + + Vector2 operator+(const Vector2 &p_v) const; + void operator+=(const Vector2 &p_v); + Vector2 operator-(const Vector2 &p_v) const; + void operator-=(const Vector2 &p_v); + Vector2 operator*(const Vector2 &p_v1) const; + + Vector2 operator*(const real_t &rvalue) const; + void operator*=(const real_t &rvalue); + void operator*=(const Vector2 &rvalue) { *this = *this * rvalue; } + + Vector2 operator/(const Vector2 &p_v1) const; + + Vector2 operator/(const real_t &rvalue) const; + + void operator/=(const real_t &rvalue); + void operator/=(const Vector2 &rvalue) { *this = *this / rvalue; } + + Vector2 operator-() const; + + bool operator==(const Vector2 &p_vec2) const; + bool operator!=(const Vector2 &p_vec2) const; + + bool operator<(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y < p_vec2.y) : (x < p_vec2.x); } + bool operator>(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y > p_vec2.y) : (x > p_vec2.x); } + bool operator<=(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y <= p_vec2.y) : (x < p_vec2.x); } + bool operator>=(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); } + + real_t angle() const; + + void set_rotation(real_t p_radians) { + x = Math::cos(p_radians); + y = Math::sin(p_radians); + } + + _FORCE_INLINE_ Vector2 abs() const { + return Vector2(Math::abs(x), Math::abs(y)); + } + + Vector2 rotated(real_t p_by) const; + _FORCE_INLINE_ Vector2 tangent() const { + return Vector2(y, -x); + } + _FORCE_INLINE_ Vector2 orthogonal() const { + return Vector2(y, -x); + } + + Vector2 sign() const; + Vector2 floor() const; + Vector2 ceil() const; + Vector2 round() const; + Vector2 snapped(const Vector2 &p_by) const; + real_t aspect() const { return width / height; } + + operator String() const; + + _FORCE_INLINE_ Vector2(real_t p_x, real_t p_y) { + x = p_x; + y = p_y; + } + _FORCE_INLINE_ Vector2() { x = y = 0; } +}; + +_FORCE_INLINE_ Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const { + return p_vec - *this * (dot(p_vec) - p_d); +} + +_FORCE_INLINE_ Vector2 operator*(real_t p_scalar, const Vector2 &p_vec) { + return p_vec * p_scalar; +} + +_FORCE_INLINE_ Vector2 Vector2::operator+(const Vector2 &p_v) const { + return Vector2(x + p_v.x, y + p_v.y); +} +_FORCE_INLINE_ void Vector2::operator+=(const Vector2 &p_v) { + x += p_v.x; + y += p_v.y; +} +_FORCE_INLINE_ Vector2 Vector2::operator-(const Vector2 &p_v) const { + return Vector2(x - p_v.x, y - p_v.y); +} +_FORCE_INLINE_ void Vector2::operator-=(const Vector2 &p_v) { + x -= p_v.x; + y -= p_v.y; +} + +_FORCE_INLINE_ Vector2 Vector2::operator*(const Vector2 &p_v1) const { + return Vector2(x * p_v1.x, y * p_v1.y); +}; + +_FORCE_INLINE_ Vector2 Vector2::operator*(const real_t &rvalue) const { + return Vector2(x * rvalue, y * rvalue); +}; +_FORCE_INLINE_ void Vector2::operator*=(const real_t &rvalue) { + x *= rvalue; + y *= rvalue; +}; + +_FORCE_INLINE_ Vector2 Vector2::operator/(const Vector2 &p_v1) const { + return Vector2(x / p_v1.x, y / p_v1.y); +}; + +_FORCE_INLINE_ Vector2 Vector2::operator/(const real_t &rvalue) const { + return Vector2(x / rvalue, y / rvalue); +}; + +_FORCE_INLINE_ void Vector2::operator/=(const real_t &rvalue) { + x /= rvalue; + y /= rvalue; +}; + +_FORCE_INLINE_ Vector2 Vector2::operator-() const { + return Vector2(-x, -y); +} + +_FORCE_INLINE_ bool Vector2::operator==(const Vector2 &p_vec2) const { + return x == p_vec2.x && y == p_vec2.y; +} +_FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const { + return x != p_vec2.x || y != p_vec2.y; +} + +Vector2 Vector2::linear_interpolate(const Vector2 &p_to, real_t p_weight) const { + Vector2 res = *this; + + res.x += (p_weight * (p_to.x - x)); + res.y += (p_weight * (p_to.y - y)); + + return res; +} + +Vector2 Vector2::slerp(const Vector2 &p_to, real_t p_weight) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!is_normalized(), Vector2(), "The start Vector2 must be normalized."); +#endif + real_t theta = angle_to(p_to); + return rotated(theta * p_weight); +} + +Vector2 Vector2::bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const { + Vector2 res = *this; + + /* Formula from Wikipedia article on Bezier curves. */ + real_t omt = (1.0 - p_t); + real_t omt2 = omt * omt; + real_t omt3 = omt2 * omt; + real_t t2 = p_t * p_t; + real_t t3 = t2 * p_t; + + return res * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3; +} + +Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const { + Vector2 res = *this; + res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight); + res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight); + return res; +} + +Vector2 Vector2::direction_to(const Vector2 &p_to) const { + Vector2 ret(p_to.x - x, p_to.y - y); + ret.normalize(); + return ret; +} + +Vector2 Vector2::linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_weight) { + Vector2 res = p_a; + + res.x += (p_weight * (p_b.x - p_a.x)); + res.y += (p_weight * (p_b.y - p_a.y)); + + return res; +} + +typedef Vector2 Size2; +typedef Vector2 Point2; + +#endif // VECTOR2_H +#line 0 + +#line 1 "sfwl/core/vector2i.h" +#ifndef VECTOR2I_H +#define VECTOR2I_H + +/*************************************************************************/ +/* vector2i.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +class String; + +struct _NO_DISCARD_CLASS_ Vector2i { + enum Axis { + AXIS_X, + AXIS_Y, + }; + + union { + struct { + union { + int x; + int width; + }; + union { + int y; + int height; + }; + }; + + int coord[2]; + }; + + _FORCE_INLINE_ int &operator[](int p_idx) { + DEV_ASSERT((unsigned int)p_idx < 2); + return coord[p_idx]; + } + _FORCE_INLINE_ const int &operator[](int p_idx) const { + DEV_ASSERT((unsigned int)p_idx < 2); + return coord[p_idx]; + } + + _FORCE_INLINE_ void set_all(int p_value) { + x = y = p_value; + } + + _FORCE_INLINE_ int min_axis() const { + return x < y ? 0 : 1; + } + + _FORCE_INLINE_ int max_axis() const { + return x < y ? 1 : 0; + } + + Vector2i min(const Vector2i &p_vector2i) const { + return Vector2i(MIN(x, p_vector2i.x), MIN(y, p_vector2i.y)); + } + + Vector2i max(const Vector2i &p_vector2i) const { + return Vector2i(MAX(x, p_vector2i.x), MAX(y, p_vector2i.y)); + } + + _FORCE_INLINE_ static Vector2i linear_interpolate(const Vector2i &p_a, const Vector2i &p_b, real_t p_weight); + _FORCE_INLINE_ Vector2i linear_interpolate(const Vector2i &p_to, real_t p_weight) const; + + Vector2i operator+(const Vector2i &p_v) const; + void operator+=(const Vector2i &p_v); + Vector2i operator-(const Vector2i &p_v) const; + void operator-=(const Vector2i &p_v); + Vector2i operator*(const Vector2i &p_v1) const; + + Vector2i operator*(const int &rvalue) const; + void operator*=(const int &rvalue); + + Vector2i operator/(const Vector2i &p_v1) const; + + Vector2i operator/(const int &rvalue) const; + + void operator/=(const int &rvalue); + + Vector2i operator-() const; + bool operator<(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } + bool operator>(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); } + bool operator<=(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y <= p_vec2.y) : (x < p_vec2.x); } + bool operator>=(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); } + + bool operator==(const Vector2i &p_vec2) const; + bool operator!=(const Vector2i &p_vec2) const; + + int64_t length_squared() const; + double length() const; + + real_t aspect() const { return width / (real_t)height; } + Vector2i sign() const { return Vector2i(SGN(x), SGN(y)); } + Vector2i abs() const { return Vector2i(ABS(x), ABS(y)); } + Vector2i clamp(const Vector2i &p_min, const Vector2i &p_max) const; + + Vector2 to_vector2() const { return Vector2(x, y); } + + operator String() const; + operator Vector2() const { return Vector2(x, y); } + + inline Vector2i(const Vector2 &p_vec2) { + x = (int)p_vec2.x; + y = (int)p_vec2.y; + } + inline Vector2i(int p_x, int p_y) { + x = p_x; + y = p_y; + } + inline Vector2i() { + x = 0; + y = 0; + } +}; + +Vector2i Vector2i::linear_interpolate(const Vector2i &p_a, const Vector2i &p_b, real_t p_weight) { + Vector2i res = p_a; + + res.x += (p_weight * (p_b.x - p_a.x)); + res.y += (p_weight * (p_b.y - p_a.y)); + + return res; +} + +Vector2i Vector2i::linear_interpolate(const Vector2i &p_to, real_t p_weight) const { + Vector2 res = *this; + + res.x += (p_weight * (p_to.x - x)); + res.y += (p_weight * (p_to.y - y)); + + return res; +} + +typedef Vector2i Size2i; +typedef Vector2i Point2i; + +#endif // VECTOR2_H +#line 0 + +#line 1 "sfwl/core/rect2.h" +#ifndef RECT2_H +#define RECT2_H + +/*************************************************************************/ +/* rect2.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct Transform2D; +struct Rect2i; + +struct _NO_DISCARD_CLASS_ Rect2 { + Point2 position; + Size2 size; + + const Vector2 &get_position() const { return position; } + void set_position(const Vector2 &p_pos) { position = p_pos; } + const Vector2 &get_size() const { return size; } + void set_size(const Vector2 &p_size) { size = p_size; } + + real_t get_area() const { return size.width * size.height; } + + _FORCE_INLINE_ Vector2 get_center() const { return position + (size * 0.5f); } + + inline bool intersects(const Rect2 &p_rect, const bool p_include_borders = false) const { + if (p_include_borders) { + if (position.x > (p_rect.position.x + p_rect.size.width)) { + return false; + } + if ((position.x + size.width) < p_rect.position.x) { + return false; + } + if (position.y > (p_rect.position.y + p_rect.size.height)) { + return false; + } + if ((position.y + size.height) < p_rect.position.y) { + return false; + } + } else { + if (position.x >= (p_rect.position.x + p_rect.size.width)) { + return false; + } + if ((position.x + size.width) <= p_rect.position.x) { + return false; + } + if (position.y >= (p_rect.position.y + p_rect.size.height)) { + return false; + } + if ((position.y + size.height) <= p_rect.position.y) { + return false; + } + } + + return true; + } + + inline real_t distance_to(const Vector2 &p_point) const { + real_t dist = 0.0; + bool inside = true; + + if (p_point.x < position.x) { + real_t d = position.x - p_point.x; + dist = d; + inside = false; + } + if (p_point.y < position.y) { + real_t d = position.y - p_point.y; + dist = inside ? d : MIN(dist, d); + inside = false; + } + if (p_point.x >= (position.x + size.x)) { + real_t d = p_point.x - (position.x + size.x); + dist = inside ? d : MIN(dist, d); + inside = false; + } + if (p_point.y >= (position.y + size.y)) { + real_t d = p_point.y - (position.y + size.y); + dist = inside ? d : MIN(dist, d); + inside = false; + } + + if (inside) { + return 0; + } else { + return dist; + } + } + + bool intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const; + + bool intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos = nullptr, Point2 *r_normal = nullptr) const; + + inline bool encloses(const Rect2 &p_rect) const { + return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && + ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) && + ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y)); + } + + _FORCE_INLINE_ bool has_no_area() const { + return (size.x <= 0 || size.y <= 0); + } + inline Rect2 clip(const Rect2 &p_rect) const { /// return a clipped rect + + Rect2 new_rect = p_rect; + + if (!intersects(new_rect)) { + return Rect2(); + } + + new_rect.position.x = MAX(p_rect.position.x, position.x); + new_rect.position.y = MAX(p_rect.position.y, position.y); + + Point2 p_rect_end = p_rect.position + p_rect.size; + Point2 end = position + size; + + new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; + new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; + + return new_rect; + } + + inline Rect2 intersection(const Rect2 &p_rect) const { + Rect2 new_rect = p_rect; + + if (!intersects(new_rect)) { + return Rect2(); + } + + new_rect.position.x = MAX(p_rect.position.x, position.x); + new_rect.position.y = MAX(p_rect.position.y, position.y); + + Point2 p_rect_end = p_rect.position + p_rect.size; + Point2 end = position + size; + + new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; + new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; + + return new_rect; + } + + inline Rect2 merge(const Rect2 &p_rect) const { ///< return a merged rect + + Rect2 new_rect; + + new_rect.position.x = MIN(p_rect.position.x, position.x); + new_rect.position.y = MIN(p_rect.position.y, position.y); + + new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); + new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); + + new_rect.size = new_rect.size - new_rect.position; //make relative again + + return new_rect; + }; + inline bool has_point(const Point2 &p_point) const { + if (p_point.x < position.x) { + return false; + } + if (p_point.y < position.y) { + return false; + } + + if (p_point.x >= (position.x + size.x)) { + return false; + } + if (p_point.y >= (position.y + size.y)) { + return false; + } + + return true; + } + bool is_equal_approx(const Rect2 &p_rect) const; + + bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; } + bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; } + + inline Rect2 grow(real_t p_by) const { + Rect2 g = *this; + g.grow_by(p_by); + return g; + } + + inline void grow_by(real_t p_by) { + position.x -= p_by; + position.y -= p_by; + size.width += p_by * 2; + size.height += p_by * 2; + } + + inline Rect2 grow_margin(Margin p_margin, real_t p_amount) const { + Rect2 g = *this; + g = g.grow_individual((MARGIN_LEFT == p_margin) ? p_amount : 0, + (MARGIN_TOP == p_margin) ? p_amount : 0, + (MARGIN_RIGHT == p_margin) ? p_amount : 0, + (MARGIN_BOTTOM == p_margin) ? p_amount : 0); + return g; + } + + inline Rect2 grow_side(Side p_side, real_t p_amount) const { + Rect2 g = *this; + g = g.grow_individual((SIDE_LEFT == p_side) ? p_amount : 0, + (SIDE_TOP == p_side) ? p_amount : 0, + (SIDE_RIGHT == p_side) ? p_amount : 0, + (SIDE_BOTTOM == p_side) ? p_amount : 0); + return g; + } + + inline Rect2 grow_individual(real_t p_left, real_t p_top, real_t p_right, real_t p_bottom) const { + Rect2 g = *this; + g.position.x -= p_left; + g.position.y -= p_top; + g.size.width += p_left + p_right; + g.size.height += p_top + p_bottom; + + return g; + } + + _FORCE_INLINE_ Rect2 expand(const Vector2 &p_vector) const { + Rect2 r = *this; + r.expand_to(p_vector); + return r; + } + + inline void expand_to(const Vector2 &p_vector) { //in place function for speed + + Vector2 begin = position; + Vector2 end = position + size; + + if (p_vector.x < begin.x) { + begin.x = p_vector.x; + } + if (p_vector.y < begin.y) { + begin.y = p_vector.y; + } + + if (p_vector.x > end.x) { + end.x = p_vector.x; + } + if (p_vector.y > end.y) { + end.y = p_vector.y; + } + + position = begin; + size = end - begin; + } + + _FORCE_INLINE_ Rect2 abs() const { + return Rect2(Point2(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs()); + } + + Vector2 get_support(const Vector2 &p_normal) const { + Vector2 half_extents = size * 0.5f; + Vector2 ofs = position + half_extents; + return Vector2( + (p_normal.x > 0) ? -half_extents.x : half_extents.x, + (p_normal.y > 0) ? -half_extents.y : half_extents.y) + + ofs; + } + + _FORCE_INLINE_ bool intersects_filled_polygon(const Vector2 *p_points, int p_point_count) const { + Vector2 center = get_center(); + int side_plus = 0; + int side_minus = 0; + Vector2 end = position + size; + + int i_f = p_point_count - 1; + for (int i = 0; i < p_point_count; i++) { + const Vector2 &a = p_points[i_f]; + const Vector2 &b = p_points[i]; + i_f = i; + + Vector2 r = (b - a); + float l = r.length(); + if (l == 0.0f) { + continue; + } + + //check inside + Vector2 tg = r.orthogonal(); + float s = tg.dot(center) - tg.dot(a); + if (s < 0.0f) { + side_plus++; + } else { + side_minus++; + } + + //check ray box + r /= l; + Vector2 ir(1.0f / r.x, 1.0f / r.y); + + // lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner + // r.org is origin of ray + Vector2 t13 = (position - a) * ir; + Vector2 t24 = (end - a) * ir; + + float tmin = MAX(MIN(t13.x, t24.x), MIN(t13.y, t24.y)); + float tmax = MIN(MAX(t13.x, t24.x), MAX(t13.y, t24.y)); + + // if tmax < 0, ray (line) is intersecting AABB, but the whole AABB is behind us + if (tmax < 0 || tmin > tmax || tmin >= l) { + continue; + } + + return true; + } + + if (side_plus * side_minus == 0) { + return true; //all inside + } else { + return false; + } + } + + _FORCE_INLINE_ void set_end(const Vector2 &p_end) { + size = p_end - position; + } + + _FORCE_INLINE_ Vector2 get_end() const { + return position + size; + } + + operator String() const; + + Rect2() {} + Rect2(real_t p_x, real_t p_y, real_t p_width, real_t p_height) : + position(Point2(p_x, p_y)), + size(Size2(p_width, p_height)) { + } + Rect2(const Point2 &p_pos, const Size2 &p_size) : + position(p_pos), + size(p_size) { + } +}; + +#endif // RECT2_H +#line 0 + +#line 1 "sfwl/core/rect2i.h" +#ifndef RECT2I_H +#define RECT2I_H + +/*************************************************************************/ +/* rect2i.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct _NO_DISCARD_CLASS_ Rect2i { + Point2i position; + Size2i size; + + const Point2i &get_position() const { return position; } + void set_position(const Point2i &p_position) { position = p_position; } + const Size2i &get_size() const { return size; } + void set_size(const Size2i &p_size) { size = p_size; } + + int get_area() const { return size.width * size.height; } + + _FORCE_INLINE_ Vector2i get_center() const { return position + (size / 2); } + + inline bool intersects(const Rect2i &p_rect) const { + if (position.x > (p_rect.position.x + p_rect.size.width)) { + return false; + } + if ((position.x + size.width) < p_rect.position.x) { + return false; + } + if (position.y > (p_rect.position.y + p_rect.size.height)) { + return false; + } + if ((position.y + size.height) < p_rect.position.y) { + return false; + } + + return true; + } + + inline bool encloses(const Rect2i &p_rect) const { + return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && + ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && + ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); + } + + _FORCE_INLINE_ bool has_no_area() const { + return (size.x <= 0 || size.y <= 0); + } + + inline Rect2i clip(const Rect2i &p_rect) const { /// return a clipped rect + + Rect2i new_rect = p_rect; + + if (!intersects(new_rect)) { + return Rect2i(); + } + + new_rect.position.x = MAX(p_rect.position.x, position.x); + new_rect.position.y = MAX(p_rect.position.y, position.y); + + Point2 p_rect_end = p_rect.position + p_rect.size; + Point2 end = position + size; + + new_rect.size.x = (int)(MIN(p_rect_end.x, end.x) - new_rect.position.x); + new_rect.size.y = (int)(MIN(p_rect_end.y, end.y) - new_rect.position.y); + + return new_rect; + } + + // Returns the instersection between two Rect2is or an empty Rect2i if there is no intersection + inline Rect2i intersection(const Rect2i &p_rect) const { + Rect2i new_rect = p_rect; + + if (!intersects(new_rect)) { + return Rect2i(); + } + + new_rect.position.x = MAX(p_rect.position.x, position.x); + new_rect.position.y = MAX(p_rect.position.y, position.y); + + Point2i p_rect_end = p_rect.position + p_rect.size; + Point2i end = position + size; + + new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; + new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; + + return new_rect; + } + + inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect + + Rect2i new_rect; + + new_rect.position.x = MIN(p_rect.position.x, position.x); + new_rect.position.y = MIN(p_rect.position.y, position.y); + + new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); + new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); + + new_rect.size = new_rect.size - new_rect.position; //make relative again + + return new_rect; + } + + bool has_point(const Point2i &p_point) const { + if (p_point.x < position.x) { + return false; + } + if (p_point.y < position.y) { + return false; + } + + if (p_point.x >= (position.x + size.x)) { + return false; + } + if (p_point.y >= (position.y + size.y)) { + return false; + } + + return true; + } + + bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; } + bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; } + + Rect2i grow(int p_by) const { + Rect2i g = *this; + g.position.x -= p_by; + g.position.y -= p_by; + g.size.width += p_by * 2; + g.size.height += p_by * 2; + return g; + } + + void grow_by(int p_by) { + position.x -= p_by; + position.y -= p_by; + size.width += p_by * 2; + size.height += p_by * 2; + } + + inline Rect2i grow_margin(Margin p_margin, int p_amount) const { + Rect2i g = *this; + g = g.grow_individual((MARGIN_LEFT == p_margin) ? p_amount : 0, + (MARGIN_TOP == p_margin) ? p_amount : 0, + (MARGIN_RIGHT == p_margin) ? p_amount : 0, + (MARGIN_BOTTOM == p_margin) ? p_amount : 0); + return g; + } + + inline Rect2i grow_side(Side p_side, int p_amount) const { + Rect2i g = *this; + g = g.grow_individual((SIDE_LEFT == p_side) ? p_amount : 0, + (SIDE_TOP == p_side) ? p_amount : 0, + (SIDE_RIGHT == p_side) ? p_amount : 0, + (SIDE_BOTTOM == p_side) ? p_amount : 0); + return g; + } + + inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const { + Rect2i g = *this; + g.position.x -= p_left; + g.position.y -= p_top; + g.size.width += p_left + p_right; + g.size.height += p_top + p_bottom; + + return g; + } + + _FORCE_INLINE_ Rect2i expand(const Vector2i &p_vector) const { + Rect2i r = *this; + r.expand_to(p_vector); + return r; + } + + inline void expand_to(const Point2i &p_vector) { + Point2i begin = position; + Point2i end = position + size; + + if (p_vector.x < begin.x) { + begin.x = p_vector.x; + } + if (p_vector.y < begin.y) { + begin.y = p_vector.y; + } + + if (p_vector.x > end.x) { + end.x = p_vector.x; + } + if (p_vector.y > end.y) { + end.y = p_vector.y; + } + + position = begin; + size = end - begin; + } + + _FORCE_INLINE_ Rect2i abs() const { + return Rect2i(Point2i(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs()); + } + + _FORCE_INLINE_ void set_end(const Vector2i &p_end) { + size = p_end - position; + } + + _FORCE_INLINE_ Vector2i get_end() const { + return position + size; + } + + Rect2 to_rect2() const { return Rect2(position, size); } + + operator String() const; + operator Rect2() const { return Rect2(position, size); } + + Rect2i(const Rect2 &p_r2) : + position(p_r2.position), + size(p_r2.size) { + } + Rect2i() {} + Rect2i(int p_x, int p_y, int p_width, int p_height) : + position(Point2(p_x, p_y)), + size(Size2(p_width, p_height)) { + } + Rect2i(const Point2 &p_pos, const Size2 &p_size) : + position(p_pos), + size(p_size) { + } +}; + +#endif // RECT2_H +#line 0 + +#line 1 "sfwl/core/vector3.h" +#ifndef VECTOR3_H +#define VECTOR3_H + +/*************************************************************************/ +/* vector3.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct Basis; + +struct _NO_DISCARD_CLASS_ Vector3 { + static const int AXIS_COUNT = 3; + + enum Axis { + AXIS_X, + AXIS_Y, + AXIS_Z, + }; + + union { + struct { + real_t x; + real_t y; + real_t z; + }; + + real_t coord[3]; + }; + + _FORCE_INLINE_ const real_t &operator[](int p_axis) const { + DEV_ASSERT((unsigned int)p_axis < 3); + return coord[p_axis]; + } + + _FORCE_INLINE_ real_t &operator[](int p_axis) { + DEV_ASSERT((unsigned int)p_axis < 3); + return coord[p_axis]; + } + + void set_axis(int p_axis, real_t p_value); + real_t get_axis(int p_axis) const; + + _FORCE_INLINE_ void set_all(real_t p_value) { + x = y = z = p_value; + } + + _FORCE_INLINE_ int min_axis() const { + return x < y ? (x < z ? 0 : 2) : (y < z ? 1 : 2); + } + + _FORCE_INLINE_ int max_axis() const { + return x < y ? (y < z ? 2 : 1) : (x < z ? 2 : 0); + } + + _FORCE_INLINE_ real_t length() const; + _FORCE_INLINE_ real_t length_squared() const; + + _FORCE_INLINE_ void normalize(); + _FORCE_INLINE_ Vector3 normalized() const; + _FORCE_INLINE_ bool is_normalized() const; + _FORCE_INLINE_ Vector3 inverse() const; + Vector3 limit_length(const real_t p_len = 1.0) const; + + _FORCE_INLINE_ void zero(); + + void snap(const Vector3 &p_val); + Vector3 snapped(const Vector3 &p_val) const; + + void rotate(const Vector3 &p_axis, real_t p_phi); + Vector3 rotated(const Vector3 &p_axis, real_t p_phi) const; + + /* Static Methods between 2 vector3s */ + + _FORCE_INLINE_ Vector3 linear_interpolate(const Vector3 &p_to, real_t p_weight) const; + _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_to, real_t p_weight) const; + _FORCE_INLINE_ Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_weight) const; + _FORCE_INLINE_ Vector3 bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) const; + + Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const; + + _FORCE_INLINE_ Vector3 cross(const Vector3 &p_b) const; + _FORCE_INLINE_ real_t dot(const Vector3 &p_b) const; + Basis outer(const Vector3 &p_b) const; + Basis to_diagonal_matrix() const; + + _FORCE_INLINE_ Vector3 abs() const; + _FORCE_INLINE_ Vector3 floor() const; + _FORCE_INLINE_ Vector3 sign() const; + _FORCE_INLINE_ Vector3 ceil() const; + _FORCE_INLINE_ Vector3 round() const; + Vector3 clamp(const Vector3 &p_min, const Vector3 &p_max) const; + + _FORCE_INLINE_ real_t distance_to(const Vector3 &p_to) const; + _FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_to) const; + + _FORCE_INLINE_ Vector3 posmod(const real_t p_mod) const; + _FORCE_INLINE_ Vector3 posmodv(const Vector3 &p_modv) const; + _FORCE_INLINE_ Vector3 project(const Vector3 &p_to) const; + + _FORCE_INLINE_ real_t angle_to(const Vector3 &p_to) const; + _FORCE_INLINE_ real_t signed_angle_to(const Vector3 &p_to, const Vector3 &p_axis) const; + _FORCE_INLINE_ Vector3 direction_to(const Vector3 &p_to) const; + + _FORCE_INLINE_ Vector3 slide(const Vector3 &p_normal) const; + _FORCE_INLINE_ Vector3 bounce(const Vector3 &p_normal) const; + _FORCE_INLINE_ Vector3 reflect(const Vector3 &p_normal) const; + + bool is_equal_approx(const Vector3 &p_v) const; + inline bool is_equal_approx(const Vector3 &p_v, real_t p_tolerance) const; + inline bool is_equal_approxt(const Vector3 &p_v, real_t p_tolerance) const; + + /* Operators */ + + _FORCE_INLINE_ Vector3 &operator+=(const Vector3 &p_v); + _FORCE_INLINE_ Vector3 operator+(const Vector3 &p_v) const; + _FORCE_INLINE_ Vector3 &operator-=(const Vector3 &p_v); + _FORCE_INLINE_ Vector3 operator-(const Vector3 &p_v) const; + _FORCE_INLINE_ Vector3 &operator*=(const Vector3 &p_v); + _FORCE_INLINE_ Vector3 operator*(const Vector3 &p_v) const; + _FORCE_INLINE_ Vector3 &operator/=(const Vector3 &p_v); + _FORCE_INLINE_ Vector3 operator/(const Vector3 &p_v) const; + + _FORCE_INLINE_ Vector3 &operator*=(real_t p_scalar); + _FORCE_INLINE_ Vector3 operator*(real_t p_scalar) const; + _FORCE_INLINE_ Vector3 &operator/=(real_t p_scalar); + _FORCE_INLINE_ Vector3 operator/(real_t p_scalar) const; + + _FORCE_INLINE_ Vector3 operator-() const; + + _FORCE_INLINE_ bool operator==(const Vector3 &p_v) const; + _FORCE_INLINE_ bool operator!=(const Vector3 &p_v) const; + _FORCE_INLINE_ bool operator<(const Vector3 &p_v) const; + _FORCE_INLINE_ bool operator<=(const Vector3 &p_v) const; + _FORCE_INLINE_ bool operator>(const Vector3 &p_v) const; + _FORCE_INLINE_ bool operator>=(const Vector3 &p_v) const; + + operator String() const; + + _FORCE_INLINE_ Vector3(real_t p_x, real_t p_y, real_t p_z) { + x = p_x; + y = p_y; + z = p_z; + } + _FORCE_INLINE_ Vector3() { x = y = z = 0; } +}; + +Vector3 Vector3::cross(const Vector3 &p_b) const { + Vector3 ret( + (y * p_b.z) - (z * p_b.y), + (z * p_b.x) - (x * p_b.z), + (x * p_b.y) - (y * p_b.x)); + + return ret; +} + +real_t Vector3::dot(const Vector3 &p_b) const { + return x * p_b.x + y * p_b.y + z * p_b.z; +} + +Vector3 Vector3::abs() const { + return Vector3(Math::abs(x), Math::abs(y), Math::abs(z)); +} + +Vector3 Vector3::sign() const { + return Vector3(SGN(x), SGN(y), SGN(z)); +} + +Vector3 Vector3::floor() const { + return Vector3(Math::floor(x), Math::floor(y), Math::floor(z)); +} + +Vector3 Vector3::ceil() const { + return Vector3(Math::ceil(x), Math::ceil(y), Math::ceil(z)); +} + +Vector3 Vector3::round() const { + return Vector3(Math::round(x), Math::round(y), Math::round(z)); +} + +Vector3 Vector3::linear_interpolate(const Vector3 &p_to, real_t p_weight) const { + return Vector3( + x + (p_weight * (p_to.x - x)), + y + (p_weight * (p_to.y - y)), + z + (p_weight * (p_to.z - z))); +} + +Vector3 Vector3::slerp(const Vector3 &p_to, const real_t p_weight) const { + // This method seems more complicated than it really is, since we write out + // the internals of some methods for efficiency (mainly, checking length). + real_t start_length_sq = length_squared(); + real_t end_length_sq = p_to.length_squared(); + if (unlikely(start_length_sq == 0.0f || end_length_sq == 0.0f)) { + // Zero length vectors have no angle, so the best we can do is either lerp or throw an error. + return linear_interpolate(p_to, p_weight); + } + Vector3 axis = cross(p_to); + real_t axis_length_sq = axis.length_squared(); + if (unlikely(axis_length_sq == 0.0f)) { + // Colinear vectors have no rotation axis or angle between them, so the best we can do is lerp. + return linear_interpolate(p_to, p_weight); + } + axis /= Math::sqrt(axis_length_sq); + real_t start_length = Math::sqrt(start_length_sq); + real_t result_length = Math::lerp(start_length, Math::sqrt(end_length_sq), p_weight); + real_t angle = angle_to(p_to); + return rotated(axis, angle * p_weight) * (result_length / start_length); +} + +Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const { + Vector3 res = *this; + res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight); + res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight); + res.z = Math::cubic_interpolate(res.z, p_b.z, p_pre_a.z, p_post_b.z, p_weight); + return res; +} + +Vector3 Vector3::bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) const { + Vector3 res = *this; + + /* Formula from Wikipedia article on Bezier curves. */ + real_t omt = (1.0 - p_t); + real_t omt2 = omt * omt; + real_t omt3 = omt2 * omt; + real_t t2 = p_t * p_t; + real_t t3 = t2 * p_t; + + return res * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3; +} + +real_t Vector3::distance_to(const Vector3 &p_to) const { + return (p_to - *this).length(); +} + +real_t Vector3::distance_squared_to(const Vector3 &p_to) const { + return (p_to - *this).length_squared(); +} + +Vector3 Vector3::posmod(const real_t p_mod) const { + return Vector3(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod), Math::fposmod(z, p_mod)); +} + +Vector3 Vector3::posmodv(const Vector3 &p_modv) const { + return Vector3(Math::fposmod(x, p_modv.x), Math::fposmod(y, p_modv.y), Math::fposmod(z, p_modv.z)); +} + +Vector3 Vector3::project(const Vector3 &p_to) const { + return p_to * (dot(p_to) / p_to.length_squared()); +} + +real_t Vector3::angle_to(const Vector3 &p_to) const { + return Math::atan2(cross(p_to).length(), dot(p_to)); +} + +real_t Vector3::signed_angle_to(const Vector3 &p_to, const Vector3 &p_axis) const { + Vector3 cross_to = cross(p_to); + real_t unsigned_angle = Math::atan2(cross_to.length(), dot(p_to)); + real_t sign = cross_to.dot(p_axis); + return (sign < 0) ? -unsigned_angle : unsigned_angle; +} + +Vector3 Vector3::direction_to(const Vector3 &p_to) const { + Vector3 ret(p_to.x - x, p_to.y - y, p_to.z - z); + ret.normalize(); + return ret; +} + +/* Operators */ + +Vector3 &Vector3::operator+=(const Vector3 &p_v) { + x += p_v.x; + y += p_v.y; + z += p_v.z; + return *this; +} + +Vector3 Vector3::operator+(const Vector3 &p_v) const { + return Vector3(x + p_v.x, y + p_v.y, z + p_v.z); +} + +Vector3 &Vector3::operator-=(const Vector3 &p_v) { + x -= p_v.x; + y -= p_v.y; + z -= p_v.z; + return *this; +} +Vector3 Vector3::operator-(const Vector3 &p_v) const { + return Vector3(x - p_v.x, y - p_v.y, z - p_v.z); +} + +Vector3 &Vector3::operator*=(const Vector3 &p_v) { + x *= p_v.x; + y *= p_v.y; + z *= p_v.z; + return *this; +} +Vector3 Vector3::operator*(const Vector3 &p_v) const { + return Vector3(x * p_v.x, y * p_v.y, z * p_v.z); +} + +Vector3 &Vector3::operator/=(const Vector3 &p_v) { + x /= p_v.x; + y /= p_v.y; + z /= p_v.z; + return *this; +} + +Vector3 Vector3::operator/(const Vector3 &p_v) const { + return Vector3(x / p_v.x, y / p_v.y, z / p_v.z); +} + +Vector3 &Vector3::operator*=(real_t p_scalar) { + x *= p_scalar; + y *= p_scalar; + z *= p_scalar; + return *this; +} + +_FORCE_INLINE_ Vector3 operator*(real_t p_scalar, const Vector3 &p_vec) { + return p_vec * p_scalar; +} + +Vector3 Vector3::operator*(real_t p_scalar) const { + return Vector3(x * p_scalar, y * p_scalar, z * p_scalar); +} + +Vector3 &Vector3::operator/=(real_t p_scalar) { + x /= p_scalar; + y /= p_scalar; + z /= p_scalar; + return *this; +} + +Vector3 Vector3::operator/(real_t p_scalar) const { + return Vector3(x / p_scalar, y / p_scalar, z / p_scalar); +} + +Vector3 Vector3::operator-() const { + return Vector3(-x, -y, -z); +} + +bool Vector3::operator==(const Vector3 &p_v) const { + return x == p_v.x && y == p_v.y && z == p_v.z; +} + +bool Vector3::operator!=(const Vector3 &p_v) const { + return x != p_v.x || y != p_v.y || z != p_v.z; +} + +bool Vector3::operator<(const Vector3 &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + return z < p_v.z; + } else { + return y < p_v.y; + } + } else { + return x < p_v.x; + } +} + +bool Vector3::operator>(const Vector3 &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + return z > p_v.z; + } else { + return y > p_v.y; + } + } else { + return x > p_v.x; + } +} + +bool Vector3::operator<=(const Vector3 &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + return z <= p_v.z; + } else { + return y < p_v.y; + } + } else { + return x < p_v.x; + } +} + +bool Vector3::operator>=(const Vector3 &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + return z >= p_v.z; + } else { + return y > p_v.y; + } + } else { + return x > p_v.x; + } +} + +_FORCE_INLINE_ Vector3 vec3_cross(const Vector3 &p_a, const Vector3 &p_b) { + return p_a.cross(p_b); +} + +_FORCE_INLINE_ real_t vec3_dot(const Vector3 &p_a, const Vector3 &p_b) { + return p_a.dot(p_b); +} + +real_t Vector3::length() const { + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; + + return Math::sqrt(x2 + y2 + z2); +} + +real_t Vector3::length_squared() const { + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; + + return x2 + y2 + z2; +} + +void Vector3::normalize() { + real_t lengthsq = length_squared(); + if (lengthsq == 0) { + x = y = z = 0; + } else { + real_t length = Math::sqrt(lengthsq); + x /= length; + y /= length; + z /= length; + } +} + +Vector3 Vector3::normalized() const { + Vector3 v = *this; + v.normalize(); + return v; +} + +bool Vector3::is_normalized() const { + // use length_squared() instead of length() to avoid sqrt(), makes it more stringent. + return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON); +} + +Vector3 Vector3::inverse() const { + return Vector3(1 / x, 1 / y, 1 / z); +} + +void Vector3::zero() { + x = y = z = 0; +} + +// slide returns the component of the vector along the given plane, specified by its normal vector. +Vector3 Vector3::slide(const Vector3 &p_normal) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector3(), "The normal Vector3 must be normalized."); +#endif + return *this - p_normal * this->dot(p_normal); +} + +Vector3 Vector3::bounce(const Vector3 &p_normal) const { + return -reflect(p_normal); +} + +Vector3 Vector3::reflect(const Vector3 &p_normal) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector3(), "The normal Vector3 must be normalized."); +#endif + return 2 * p_normal * this->dot(p_normal) - *this; +} + +bool Vector3::is_equal_approx(const Vector3 &p_v, real_t p_tolerance) const { + return Math::is_equal_approx(x, p_v.x, p_tolerance) && Math::is_equal_approx(y, p_v.y, p_tolerance) && Math::is_equal_approx(z, p_v.z, p_tolerance); +} + +bool Vector3::is_equal_approxt(const Vector3 &p_v, real_t p_tolerance) const { + return Math::is_equal_approx(x, p_v.x, p_tolerance) && Math::is_equal_approx(y, p_v.y, p_tolerance) && Math::is_equal_approx(z, p_v.z, p_tolerance); +} + +#endif // VECTOR3_H +#line 0 + +#line 1 "sfwl/core/vector3i.h" +/*************************************************************************/ +/* vector3i.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#ifndef VECTOR3I_H +#define VECTOR3I_H + + + +class String; +struct Vector3; + +struct _NO_DISCARD_CLASS_ Vector3i { + enum Axis { + AXIS_X, + AXIS_Y, + AXIS_Z, + }; + + union { + struct { + int32_t x; + int32_t y; + int32_t z; + }; + + int32_t coord[3]; + }; + + _FORCE_INLINE_ const int32_t &operator[](const int p_axis) const { + DEV_ASSERT((unsigned int)p_axis < 3); + return coord[p_axis]; + } + + _FORCE_INLINE_ int32_t &operator[](const int p_axis) { + DEV_ASSERT((unsigned int)p_axis < 3); + return coord[p_axis]; + } + + void set_axis(const int p_axis, const int32_t p_value); + int32_t get_axis(const int p_axis) const; + + _FORCE_INLINE_ void set_all(int32_t p_value) { + x = y = z = p_value; + } + + Vector3i::Axis min_axis() const; + Vector3i::Axis max_axis() const; + + _FORCE_INLINE_ int64_t length_squared() const; + _FORCE_INLINE_ double length() const; + + _FORCE_INLINE_ void zero(); + + _FORCE_INLINE_ Vector3i abs() const; + _FORCE_INLINE_ Vector3i sign() const; + Vector3i clamp(const Vector3i &p_min, const Vector3i &p_max) const; + + _FORCE_INLINE_ Vector3i linear_interpolate(const Vector3i &p_to, real_t p_weight) const; + + /* Operators */ + + _FORCE_INLINE_ Vector3i &operator+=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator+(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator-=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator-(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator*=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator*(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator/=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator/(const Vector3i &p_v) const; + _FORCE_INLINE_ Vector3i &operator%=(const Vector3i &p_v); + _FORCE_INLINE_ Vector3i operator%(const Vector3i &p_v) const; + + _FORCE_INLINE_ Vector3i &operator*=(const int32_t p_scalar); + _FORCE_INLINE_ Vector3i operator*(const int32_t p_scalar) const; + _FORCE_INLINE_ Vector3i &operator/=(const int32_t p_scalar); + _FORCE_INLINE_ Vector3i operator/(const int32_t p_scalar) const; + _FORCE_INLINE_ Vector3i &operator%=(const int32_t p_scalar); + _FORCE_INLINE_ Vector3i operator%(const int32_t p_scalar) const; + + _FORCE_INLINE_ Vector3i operator-() const; + + _FORCE_INLINE_ bool operator==(const Vector3i &p_v) const; + _FORCE_INLINE_ bool operator!=(const Vector3i &p_v) const; + _FORCE_INLINE_ bool operator<(const Vector3i &p_v) const; + _FORCE_INLINE_ bool operator<=(const Vector3i &p_v) const; + _FORCE_INLINE_ bool operator>(const Vector3i &p_v) const; + _FORCE_INLINE_ bool operator>=(const Vector3i &p_v) const; + + Vector3 to_vector3() const; + + operator String() const; + operator Vector3() const; + + _FORCE_INLINE_ Vector3i() { + x = 0; + y = 0; + z = 0; + } + _FORCE_INLINE_ Vector3i(const int32_t p_x, const int32_t p_y, const int32_t p_z) { + x = p_x; + y = p_y; + z = p_z; + } +}; + +int64_t Vector3i::length_squared() const { + return x * (int64_t)x + y * (int64_t)y + z * (int64_t)z; +} + +double Vector3i::length() const { + return Math::sqrt((double)length_squared()); +} + +Vector3i Vector3i::abs() const { + return Vector3i(ABS(x), ABS(y), ABS(z)); +} + +Vector3i Vector3i::sign() const { + return Vector3i(SGN(x), SGN(y), SGN(z)); +} + +Vector3i Vector3i::linear_interpolate(const Vector3i &p_to, real_t p_weight) const { + return Vector3i( + x + (p_weight * (p_to.x - x)), + y + (p_weight * (p_to.y - y)), + z + (p_weight * (p_to.z - z))); +} + +/* Operators */ + +Vector3i &Vector3i::operator+=(const Vector3i &p_v) { + x += p_v.x; + y += p_v.y; + z += p_v.z; + return *this; +} + +Vector3i Vector3i::operator+(const Vector3i &p_v) const { + return Vector3i(x + p_v.x, y + p_v.y, z + p_v.z); +} + +Vector3i &Vector3i::operator-=(const Vector3i &p_v) { + x -= p_v.x; + y -= p_v.y; + z -= p_v.z; + return *this; +} + +Vector3i Vector3i::operator-(const Vector3i &p_v) const { + return Vector3i(x - p_v.x, y - p_v.y, z - p_v.z); +} + +Vector3i &Vector3i::operator*=(const Vector3i &p_v) { + x *= p_v.x; + y *= p_v.y; + z *= p_v.z; + return *this; +} + +Vector3i Vector3i::operator*(const Vector3i &p_v) const { + return Vector3i(x * p_v.x, y * p_v.y, z * p_v.z); +} + +Vector3i &Vector3i::operator/=(const Vector3i &p_v) { + x /= p_v.x; + y /= p_v.y; + z /= p_v.z; + return *this; +} + +Vector3i Vector3i::operator/(const Vector3i &p_v) const { + return Vector3i(x / p_v.x, y / p_v.y, z / p_v.z); +} + +Vector3i &Vector3i::operator%=(const Vector3i &p_v) { + x %= p_v.x; + y %= p_v.y; + z %= p_v.z; + return *this; +} + +Vector3i Vector3i::operator%(const Vector3i &p_v) const { + return Vector3i(x % p_v.x, y % p_v.y, z % p_v.z); +} + +Vector3i &Vector3i::operator*=(const int32_t p_scalar) { + x *= p_scalar; + y *= p_scalar; + z *= p_scalar; + return *this; +} + +Vector3i Vector3i::operator*(const int32_t p_scalar) const { + return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar); +} + +// Multiplication operators required to workaround issues with LLVM using implicit conversion. + +_FORCE_INLINE_ Vector3i operator*(const int32_t p_scalar, const Vector3i &p_vector) { + return p_vector * p_scalar; +} + +_FORCE_INLINE_ Vector3i operator*(const int64_t p_scalar, const Vector3i &p_vector) { + return p_vector * p_scalar; +} + +_FORCE_INLINE_ Vector3i operator*(const float p_scalar, const Vector3i &p_vector) { + return p_vector * p_scalar; +} + +_FORCE_INLINE_ Vector3i operator*(const double p_scalar, const Vector3i &p_vector) { + return p_vector * p_scalar; +} + +Vector3i &Vector3i::operator/=(const int32_t p_scalar) { + x /= p_scalar; + y /= p_scalar; + z /= p_scalar; + return *this; +} + +Vector3i Vector3i::operator/(const int32_t p_scalar) const { + return Vector3i(x / p_scalar, y / p_scalar, z / p_scalar); +} + +Vector3i &Vector3i::operator%=(const int32_t p_scalar) { + x %= p_scalar; + y %= p_scalar; + z %= p_scalar; + return *this; +} + +Vector3i Vector3i::operator%(const int32_t p_scalar) const { + return Vector3i(x % p_scalar, y % p_scalar, z % p_scalar); +} + +Vector3i Vector3i::operator-() const { + return Vector3i(-x, -y, -z); +} + +bool Vector3i::operator==(const Vector3i &p_v) const { + return (x == p_v.x && y == p_v.y && z == p_v.z); +} + +bool Vector3i::operator!=(const Vector3i &p_v) const { + return (x != p_v.x || y != p_v.y || z != p_v.z); +} + +bool Vector3i::operator<(const Vector3i &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + return z < p_v.z; + } else { + return y < p_v.y; + } + } else { + return x < p_v.x; + } +} + +bool Vector3i::operator>(const Vector3i &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + return z > p_v.z; + } else { + return y > p_v.y; + } + } else { + return x > p_v.x; + } +} + +bool Vector3i::operator<=(const Vector3i &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + return z <= p_v.z; + } else { + return y < p_v.y; + } + } else { + return x < p_v.x; + } +} + +bool Vector3i::operator>=(const Vector3i &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + return z >= p_v.z; + } else { + return y > p_v.y; + } + } else { + return x > p_v.x; + } +} + +void Vector3i::zero() { + x = y = z = 0; +} + +typedef Vector3i Size3i; +typedef Vector3i Point3i; + +#endif // VECTOR3I_H +#line 0 + +#line 1 "sfwl/core/vector4.h" +/*************************************************************************/ +/* vector4.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#ifndef VECTOR4_H +#define VECTOR4_H + + + +struct _NO_DISCARD_CLASS_ Vector4 { + enum Axis { + AXIS_X, + AXIS_Y, + AXIS_Z, + AXIS_W, + }; + + union { + struct { + real_t x; + real_t y; + real_t z; + real_t w; + }; + real_t components[4]; + }; + + _FORCE_INLINE_ real_t &operator[](const int p_axis) { + DEV_ASSERT((unsigned int)p_axis < 4); + return components[p_axis]; + } + _FORCE_INLINE_ const real_t &operator[](const int p_axis) const { + DEV_ASSERT((unsigned int)p_axis < 4); + return components[p_axis]; + } + + _FORCE_INLINE_ void set_all(const real_t p_value); + + void set_axis(const int p_axis, const real_t p_value); + real_t get_axis(const int p_axis) const; + + Vector4::Axis min_axis() const; + Vector4::Axis max_axis() const; + + _FORCE_INLINE_ real_t length_squared() const; + bool is_equal_approx(const Vector4 &p_vec4) const; + real_t length() const; + void normalize(); + Vector4 normalized() const; + bool is_normalized() const; + Vector4 limit_length(const real_t p_len = 1.0) const; + + _FORCE_INLINE_ void zero(); + + real_t distance_to(const Vector4 &p_to) const; + real_t distance_squared_to(const Vector4 &p_to) const; + Vector4 direction_to(const Vector4 &p_to) const; + + Vector4 abs() const; + Vector4 sign() const; + Vector4 floor() const; + Vector4 ceil() const; + Vector4 round() const; + + Vector4 linear_interpolate(const Vector4 &p_to, const real_t p_weight) const; + Vector4 cubic_interpolate(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, const real_t p_weight) const; + + Vector4 posmod(const real_t p_mod) const; + Vector4 posmodv(const Vector4 &p_modv) const; + void snap(const Vector4 &p_step); + Vector4 snapped(const Vector4 &p_step) const; + Vector4 clamp(const Vector4 &p_min, const Vector4 &p_max) const; + + Vector4 inverse() const; + _FORCE_INLINE_ real_t dot(const Vector4 &p_vec4) const; + + _FORCE_INLINE_ void operator+=(const Vector4 &p_vec4); + _FORCE_INLINE_ void operator-=(const Vector4 &p_vec4); + _FORCE_INLINE_ void operator*=(const Vector4 &p_vec4); + _FORCE_INLINE_ void operator/=(const Vector4 &p_vec4); + _FORCE_INLINE_ void operator*=(const real_t &s); + _FORCE_INLINE_ void operator/=(const real_t &s); + _FORCE_INLINE_ Vector4 operator+(const Vector4 &p_vec4) const; + _FORCE_INLINE_ Vector4 operator-(const Vector4 &p_vec4) const; + _FORCE_INLINE_ Vector4 operator*(const Vector4 &p_vec4) const; + _FORCE_INLINE_ Vector4 operator/(const Vector4 &p_vec4) const; + _FORCE_INLINE_ Vector4 operator-() const; + _FORCE_INLINE_ Vector4 operator*(const real_t &s) const; + _FORCE_INLINE_ Vector4 operator/(const real_t &s) const; + + _FORCE_INLINE_ bool operator==(const Vector4 &p_vec4) const; + _FORCE_INLINE_ bool operator!=(const Vector4 &p_vec4) const; + _FORCE_INLINE_ bool operator>(const Vector4 &p_vec4) const; + _FORCE_INLINE_ bool operator<(const Vector4 &p_vec4) const; + _FORCE_INLINE_ bool operator>=(const Vector4 &p_vec4) const; + _FORCE_INLINE_ bool operator<=(const Vector4 &p_vec4) const; + + operator String() const; + + _FORCE_INLINE_ Vector4() { + x = 0; + y = 0; + z = 0; + w = 0; + } + + _FORCE_INLINE_ Vector4(real_t p_x, real_t p_y, real_t p_z, real_t p_w) : + x(p_x), + y(p_y), + z(p_z), + w(p_w) { + } + + Vector4(const Vector4 &p_vec4) : + x(p_vec4.x), + y(p_vec4.y), + z(p_vec4.z), + w(p_vec4.w) { + } + + void operator=(const Vector4 &p_vec4) { + x = p_vec4.x; + y = p_vec4.y; + z = p_vec4.z; + w = p_vec4.w; + } +}; + +void Vector4::set_all(const real_t p_value) { + x = y = z = p_value; +} + +real_t Vector4::dot(const Vector4 &p_vec4) const { + return x * p_vec4.x + y * p_vec4.y + z * p_vec4.z + w * p_vec4.w; +} + +real_t Vector4::length_squared() const { + return dot(*this); +} + +void Vector4::zero() { + x = y = z = 0; +} + +void Vector4::operator+=(const Vector4 &p_vec4) { + x += p_vec4.x; + y += p_vec4.y; + z += p_vec4.z; + w += p_vec4.w; +} + +void Vector4::operator-=(const Vector4 &p_vec4) { + x -= p_vec4.x; + y -= p_vec4.y; + z -= p_vec4.z; + w -= p_vec4.w; +} + +void Vector4::operator*=(const Vector4 &p_vec4) { + x *= p_vec4.x; + y *= p_vec4.y; + z *= p_vec4.z; + w *= p_vec4.w; +} + +void Vector4::operator/=(const Vector4 &p_vec4) { + x /= p_vec4.x; + y /= p_vec4.y; + z /= p_vec4.z; + w /= p_vec4.w; +} +void Vector4::operator*=(const real_t &s) { + x *= s; + y *= s; + z *= s; + w *= s; +} + +void Vector4::operator/=(const real_t &s) { + *this *= 1.0f / s; +} + +Vector4 Vector4::operator+(const Vector4 &p_vec4) const { + return Vector4(x + p_vec4.x, y + p_vec4.y, z + p_vec4.z, w + p_vec4.w); +} + +Vector4 Vector4::operator-(const Vector4 &p_vec4) const { + return Vector4(x - p_vec4.x, y - p_vec4.y, z - p_vec4.z, w - p_vec4.w); +} + +Vector4 Vector4::operator*(const Vector4 &p_vec4) const { + return Vector4(x * p_vec4.x, y * p_vec4.y, z * p_vec4.z, w * p_vec4.w); +} + +Vector4 Vector4::operator/(const Vector4 &p_vec4) const { + return Vector4(x / p_vec4.x, y / p_vec4.y, z / p_vec4.z, w / p_vec4.w); +} + +Vector4 Vector4::operator-() const { + return Vector4(-x, -y, -z, -w); +} + +Vector4 Vector4::operator*(const real_t &s) const { + return Vector4(x * s, y * s, z * s, w * s); +} + +Vector4 Vector4::operator/(const real_t &s) const { + return *this * (1.0f / s); +} + +bool Vector4::operator==(const Vector4 &p_vec4) const { + return x == p_vec4.x && y == p_vec4.y && z == p_vec4.z && w == p_vec4.w; +} + +bool Vector4::operator!=(const Vector4 &p_vec4) const { + return x != p_vec4.x || y != p_vec4.y || z != p_vec4.z || w != p_vec4.w; +} + +bool Vector4::operator<(const Vector4 &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + if (z == p_v.z) { + return w < p_v.w; + } + return z < p_v.z; + } + return y < p_v.y; + } + return x < p_v.x; +} + +bool Vector4::operator>(const Vector4 &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + if (z == p_v.z) { + return w > p_v.w; + } + return z > p_v.z; + } + return y > p_v.y; + } + return x > p_v.x; +} + +bool Vector4::operator<=(const Vector4 &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + if (z == p_v.z) { + return w <= p_v.w; + } + return z < p_v.z; + } + return y < p_v.y; + } + return x < p_v.x; +} + +bool Vector4::operator>=(const Vector4 &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + if (z == p_v.z) { + return w >= p_v.w; + } + return z > p_v.z; + } + return y > p_v.y; + } + return x > p_v.x; +} + +_FORCE_INLINE_ Vector4 operator*(const float p_scalar, const Vector4 &p_vec) { + return p_vec * p_scalar; +} + +_FORCE_INLINE_ Vector4 operator*(const double p_scalar, const Vector4 &p_vec) { + return p_vec * p_scalar; +} + +_FORCE_INLINE_ Vector4 operator*(const int32_t p_scalar, const Vector4 &p_vec) { + return p_vec * p_scalar; +} + +_FORCE_INLINE_ Vector4 operator*(const int64_t p_scalar, const Vector4 &p_vec) { + return p_vec * p_scalar; +} + +#endif // VECTOR4_H +#line 0 + +#line 1 "sfwl/core/vector4i.h" +/*************************************************************************/ +/* vector4i.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#ifndef VECTOR4I_H +#define VECTOR4I_H + + + +class String; +struct Vector4; + +struct _NO_DISCARD_CLASS_ Vector4i { + enum Axis { + AXIS_X, + AXIS_Y, + AXIS_Z, + AXIS_W, + }; + + union { + struct { + int32_t x; + int32_t y; + int32_t z; + int32_t w; + }; + + int32_t coord[4]; + }; + + _FORCE_INLINE_ const int32_t &operator[](const int p_axis) const { + DEV_ASSERT((unsigned int)p_axis < 4); + return coord[p_axis]; + } + + _FORCE_INLINE_ int32_t &operator[](const int p_axis) { + DEV_ASSERT((unsigned int)p_axis < 4); + return coord[p_axis]; + } + + _FORCE_INLINE_ void set_all(const int32_t p_value); + + void set_axis(const int p_axis, const int32_t p_value); + int32_t get_axis(const int p_axis) const; + + Vector4i::Axis min_axis() const; + Vector4i::Axis max_axis() const; + + _FORCE_INLINE_ int64_t length_squared() const; + _FORCE_INLINE_ double length() const; + + _FORCE_INLINE_ void zero(); + + _FORCE_INLINE_ Vector4i abs() const; + _FORCE_INLINE_ Vector4i sign() const; + Vector4i clamp(const Vector4i &p_min, const Vector4i &p_max) const; + + Vector4i linear_interpolate(const Vector4i &p_to, const real_t p_weight) const; + + /* Operators */ + + _FORCE_INLINE_ Vector4i &operator+=(const Vector4i &p_v); + _FORCE_INLINE_ Vector4i operator+(const Vector4i &p_v) const; + _FORCE_INLINE_ Vector4i &operator-=(const Vector4i &p_v); + _FORCE_INLINE_ Vector4i operator-(const Vector4i &p_v) const; + _FORCE_INLINE_ Vector4i &operator*=(const Vector4i &p_v); + _FORCE_INLINE_ Vector4i operator*(const Vector4i &p_v) const; + _FORCE_INLINE_ Vector4i &operator/=(const Vector4i &p_v); + _FORCE_INLINE_ Vector4i operator/(const Vector4i &p_v) const; + _FORCE_INLINE_ Vector4i &operator%=(const Vector4i &p_v); + _FORCE_INLINE_ Vector4i operator%(const Vector4i &p_v) const; + + _FORCE_INLINE_ Vector4i &operator*=(const int32_t p_scalar); + _FORCE_INLINE_ Vector4i operator*(const int32_t p_scalar) const; + _FORCE_INLINE_ Vector4i &operator/=(const int32_t p_scalar); + _FORCE_INLINE_ Vector4i operator/(const int32_t p_scalar) const; + _FORCE_INLINE_ Vector4i &operator%=(const int32_t p_scalar); + _FORCE_INLINE_ Vector4i operator%(const int32_t p_scalar) const; + + _FORCE_INLINE_ Vector4i operator-() const; + + _FORCE_INLINE_ bool operator==(const Vector4i &p_v) const; + _FORCE_INLINE_ bool operator!=(const Vector4i &p_v) const; + _FORCE_INLINE_ bool operator<(const Vector4i &p_v) const; + _FORCE_INLINE_ bool operator<=(const Vector4i &p_v) const; + _FORCE_INLINE_ bool operator>(const Vector4i &p_v) const; + _FORCE_INLINE_ bool operator>=(const Vector4i &p_v) const; + + Vector4 to_vector4() const; + + operator String() const; + operator Vector4() const; + + _FORCE_INLINE_ Vector4i() { + x = 0; + y = 0; + z = 0; + w = 0; + } + + //Vector4i(const Vector4 &p_vec4); + + _FORCE_INLINE_ Vector4i(const int32_t p_x, const int32_t p_y, const int32_t p_z, const int32_t p_w) { + x = p_x; + y = p_y; + z = p_z; + w = p_w; + } +}; + +void Vector4i::set_all(const int32_t p_value) { + x = y = z = p_value; +} + +int64_t Vector4i::length_squared() const { + return x * (int64_t)x + y * (int64_t)y + z * (int64_t)z + w * (int64_t)w; +} + +double Vector4i::length() const { + return Math::sqrt((double)length_squared()); +} + +Vector4i Vector4i::abs() const { + return Vector4i(ABS(x), ABS(y), ABS(z), ABS(w)); +} + +Vector4i Vector4i::sign() const { + return Vector4i(SGN(x), SGN(y), SGN(z), SGN(w)); +} + +/* Operators */ + +Vector4i &Vector4i::operator+=(const Vector4i &p_v) { + x += p_v.x; + y += p_v.y; + z += p_v.z; + w += p_v.w; + return *this; +} + +Vector4i Vector4i::operator+(const Vector4i &p_v) const { + return Vector4i(x + p_v.x, y + p_v.y, z + p_v.z, w + p_v.w); +} + +Vector4i &Vector4i::operator-=(const Vector4i &p_v) { + x -= p_v.x; + y -= p_v.y; + z -= p_v.z; + w -= p_v.w; + return *this; +} + +Vector4i Vector4i::operator-(const Vector4i &p_v) const { + return Vector4i(x - p_v.x, y - p_v.y, z - p_v.z, w - p_v.w); +} + +Vector4i &Vector4i::operator*=(const Vector4i &p_v) { + x *= p_v.x; + y *= p_v.y; + z *= p_v.z; + w *= p_v.w; + return *this; +} + +Vector4i Vector4i::operator*(const Vector4i &p_v) const { + return Vector4i(x * p_v.x, y * p_v.y, z * p_v.z, w * p_v.w); +} + +Vector4i &Vector4i::operator/=(const Vector4i &p_v) { + x /= p_v.x; + y /= p_v.y; + z /= p_v.z; + w /= p_v.w; + return *this; +} + +Vector4i Vector4i::operator/(const Vector4i &p_v) const { + return Vector4i(x / p_v.x, y / p_v.y, z / p_v.z, w / p_v.w); +} + +Vector4i &Vector4i::operator%=(const Vector4i &p_v) { + x %= p_v.x; + y %= p_v.y; + z %= p_v.z; + w %= p_v.w; + return *this; +} + +Vector4i Vector4i::operator%(const Vector4i &p_v) const { + return Vector4i(x % p_v.x, y % p_v.y, z % p_v.z, w % p_v.w); +} + +Vector4i &Vector4i::operator*=(const int32_t p_scalar) { + x *= p_scalar; + y *= p_scalar; + z *= p_scalar; + w *= p_scalar; + return *this; +} + +Vector4i Vector4i::operator*(const int32_t p_scalar) const { + return Vector4i(x * p_scalar, y * p_scalar, z * p_scalar, w * p_scalar); +} + +// Multiplication operators required to workaround issues with LLVM using implicit conversion. + +_FORCE_INLINE_ Vector4i operator*(const int32_t p_scalar, const Vector4i &p_vector) { + return p_vector * p_scalar; +} + +_FORCE_INLINE_ Vector4i operator*(const int64_t p_scalar, const Vector4i &p_vector) { + return p_vector * p_scalar; +} + +_FORCE_INLINE_ Vector4i operator*(const float p_scalar, const Vector4i &p_vector) { + return p_vector * p_scalar; +} + +_FORCE_INLINE_ Vector4i operator*(const double p_scalar, const Vector4i &p_vector) { + return p_vector * p_scalar; +} + +Vector4i &Vector4i::operator/=(const int32_t p_scalar) { + x /= p_scalar; + y /= p_scalar; + z /= p_scalar; + w /= p_scalar; + return *this; +} + +Vector4i Vector4i::operator/(const int32_t p_scalar) const { + return Vector4i(x / p_scalar, y / p_scalar, z / p_scalar, w / p_scalar); +} + +Vector4i &Vector4i::operator%=(const int32_t p_scalar) { + x %= p_scalar; + y %= p_scalar; + z %= p_scalar; + w %= p_scalar; + return *this; +} + +Vector4i Vector4i::operator%(const int32_t p_scalar) const { + return Vector4i(x % p_scalar, y % p_scalar, z % p_scalar, w % p_scalar); +} + +Vector4i Vector4i::operator-() const { + return Vector4i(-x, -y, -z, -w); +} + +bool Vector4i::operator==(const Vector4i &p_v) const { + return (x == p_v.x && y == p_v.y && z == p_v.z && w == p_v.w); +} + +bool Vector4i::operator!=(const Vector4i &p_v) const { + return (x != p_v.x || y != p_v.y || z != p_v.z || w != p_v.w); +} + +bool Vector4i::operator<(const Vector4i &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + if (z == p_v.z) { + return w < p_v.w; + } else { + return z < p_v.z; + } + } else { + return y < p_v.y; + } + } else { + return x < p_v.x; + } +} + +bool Vector4i::operator>(const Vector4i &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + if (z == p_v.z) { + return w > p_v.w; + } else { + return z > p_v.z; + } + } else { + return y > p_v.y; + } + } else { + return x > p_v.x; + } +} + +bool Vector4i::operator<=(const Vector4i &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + if (z == p_v.z) { + return w <= p_v.w; + } else { + return z < p_v.z; + } + } else { + return y < p_v.y; + } + } else { + return x < p_v.x; + } +} + +bool Vector4i::operator>=(const Vector4i &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) { + if (z == p_v.z) { + return w >= p_v.w; + } else { + return z > p_v.z; + } + } else { + return y > p_v.y; + } + } else { + return x > p_v.x; + } +} + +void Vector4i::zero() { + x = y = z = w = 0; +} + +typedef Vector4i Size4i; +typedef Vector4i Point4i; + +#endif // VECTOR4I_H +#line 0 + + +#line 1 "sfwl/core/plane.h" +#ifndef PLANE_H +#define PLANE_H + +/*************************************************************************/ +/* plane.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct _NO_DISCARD_CLASS_ Plane { + Vector3 normal; + real_t d; + + void set_normal(const Vector3 &p_normal); + _FORCE_INLINE_ Vector3 get_normal() const { return normal; }; ///Point is coplanar, CMP_EPSILON for precision + + void normalize(); + Plane normalized() const; + + /* Plane-Point operations */ + + _FORCE_INLINE_ Vector3 center() const { return normal * d; } + Vector3 get_any_point() const; + Vector3 get_any_perpendicular_normal() const; + + _FORCE_INLINE_ bool is_point_over(const Vector3 &p_point) const; ///< Point is over plane + _FORCE_INLINE_ real_t distance_to(const Vector3 &p_point) const; + _FORCE_INLINE_ bool has_point(const Vector3 &p_point, real_t _epsilon = CMP_EPSILON) const; + + /* intersections */ + + bool intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r_result = nullptr) const; + bool intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection) const; + bool intersects_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 *p_intersection) const; + + _FORCE_INLINE_ Vector3 project(const Vector3 &p_point) const { + return p_point - normal * distance_to(p_point); + } + + /* misc */ + + Plane operator-() const { return Plane(-normal, -d); } + bool is_equal_approx(const Plane &p_plane) const; + bool is_equal_approx_any_side(const Plane &p_plane) const; + + _FORCE_INLINE_ bool operator==(const Plane &p_plane) const; + _FORCE_INLINE_ bool operator!=(const Plane &p_plane) const; + operator String() const; + + _FORCE_INLINE_ Plane() : + d(0) {} + _FORCE_INLINE_ Plane(real_t p_a, real_t p_b, real_t p_c, real_t p_d) : + normal(p_a, p_b, p_c), + d(p_d) {} + + _FORCE_INLINE_ Plane(const Vector3 &p_normal, real_t p_d); + _FORCE_INLINE_ Plane(const Vector3 &p_point, const Vector3 &p_normal); + _FORCE_INLINE_ Plane(const Vector3 &p_point1, const Vector3 &p_point2, const Vector3 &p_point3, ClockDirection p_dir = CLOCKWISE); +}; + +bool Plane::is_point_over(const Vector3 &p_point) const { + return (normal.dot(p_point) > d); +} + +real_t Plane::distance_to(const Vector3 &p_point) const { + return (normal.dot(p_point) - d); +} + +bool Plane::has_point(const Vector3 &p_point, real_t _epsilon) const { + real_t dist = normal.dot(p_point) - d; + dist = ABS(dist); + return (dist <= _epsilon); +} + +Plane::Plane(const Vector3 &p_normal, real_t p_d) : + normal(p_normal), + d(p_d) { +} + +Plane::Plane(const Vector3 &p_point, const Vector3 &p_normal) : + normal(p_normal), + d(p_normal.dot(p_point)) { +} + +Plane::Plane(const Vector3 &p_point1, const Vector3 &p_point2, const Vector3 &p_point3, ClockDirection p_dir) { + if (p_dir == CLOCKWISE) { + normal = (p_point1 - p_point3).cross(p_point1 - p_point2); + } else { + normal = (p_point1 - p_point2).cross(p_point1 - p_point3); + } + + normal.normalize(); + d = normal.dot(p_point1); +} + +bool Plane::operator==(const Plane &p_plane) const { + return normal == p_plane.normal && d == p_plane.d; +} + +bool Plane::operator!=(const Plane &p_plane) const { + return normal != p_plane.normal || d != p_plane.d; +} + +#endif // PLANE_H +#line 0 + +#line 1 "sfwl/core/aabb.h" +#ifndef AABB_H +#define AABB_H + +/*************************************************************************/ +/* aabb.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +/** + * AABB / AABB (Axis Aligned Bounding Box) + * This is implemented by a point (position) and the box size + */ + +struct _NO_DISCARD_CLASS_ AABB { + Vector3 position; + Vector3 size; + + real_t get_volume() const; /// get area + _FORCE_INLINE_ bool has_no_volume() const { + return (size.x <= 0 || size.y <= 0 || size.z <= 0); + } + + _FORCE_INLINE_ bool has_no_surface() const { + return (size.x <= 0 && size.y <= 0 && size.z <= 0); + } + + const Vector3 &get_position() const { return position; } + void set_position(const Vector3 &p_pos) { position = p_pos; } + const Vector3 &get_size() const { return size; } + void set_size(const Vector3 &p_size) { size = p_size; } + + bool operator==(const AABB &p_rval) const; + bool operator!=(const AABB &p_rval) const; + + bool is_equal_approx(const AABB &p_aabb) const; + _FORCE_INLINE_ bool intersects(const AABB &p_aabb) const; /// Both AABBs overlap + _FORCE_INLINE_ bool intersects_inclusive(const AABB &p_aabb) const; /// Both AABBs (or their faces) overlap + _FORCE_INLINE_ bool encloses(const AABB &p_aabb) const; /// p_aabb is completely inside this + + AABB merge(const AABB &p_with) const; + void merge_with(const AABB &p_aabb); ///merge with another AABB + AABB intersection(const AABB &p_aabb) const; ///get box where two intersect, empty if no intersection occurs + bool intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector3 *r_clip = nullptr, Vector3 *r_normal = nullptr) const; + bool intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *r_clip = nullptr, Vector3 *r_normal = nullptr) const; + _FORCE_INLINE_ bool smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t t0, real_t t1) const; + + _FORCE_INLINE_ bool intersects_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const; + _FORCE_INLINE_ bool inside_convex_shape(const Plane *p_planes, int p_plane_count) const; + bool intersects_plane(const Plane &p_plane) const; + + _FORCE_INLINE_ bool has_point(const Vector3 &p_point) const; + _FORCE_INLINE_ Vector3 get_support(const Vector3 &p_normal) const; + + Vector3 get_longest_axis() const; + int get_longest_axis_index() const; + _FORCE_INLINE_ real_t get_longest_axis_size() const; + + Vector3 get_shortest_axis() const; + int get_shortest_axis_index() const; + _FORCE_INLINE_ real_t get_shortest_axis_size() const; + + AABB grow(real_t p_by) const; + _FORCE_INLINE_ void grow_by(real_t p_amount); + + void get_edge(int p_edge, Vector3 &r_from, Vector3 &r_to) const; + _FORCE_INLINE_ Vector3 get_endpoint(int p_point) const; + + AABB expand(const Vector3 &p_vector) const; + _FORCE_INLINE_ void project_range_in_plane(const Plane &p_plane, real_t &r_min, real_t &r_max) const; + _FORCE_INLINE_ void expand_to(const Vector3 &p_vector); /** expand to contain a point if necessary */ + bool create_from_points(const Vector &p_points); + + _FORCE_INLINE_ AABB abs() const { + return AABB(Vector3(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0), position.z + MIN(size.z, 0)), size.abs()); + } + + //Variant intersects_segmentv(const Vector3 &p_from, const Vector3 &p_to) const; + //Variant intersects_rayv(const Vector3 &p_from, const Vector3 &p_dir) const; + + _FORCE_INLINE_ void quantize(real_t p_unit); + _FORCE_INLINE_ AABB quantized(real_t p_unit) const; + + _FORCE_INLINE_ void set_end(const Vector3 &p_end) { + size = p_end - position; + } + + _FORCE_INLINE_ Vector3 get_end() const { + return position + size; + } + + _FORCE_INLINE_ Vector3 get_center() const { + return position + (size * 0.5f); + } + + operator String() const; + + _FORCE_INLINE_ AABB() {} + inline AABB(const Vector3 &p_pos, const Vector3 &p_size) : + position(p_pos), + size(p_size) { + } +}; + +inline bool AABB::intersects(const AABB &p_aabb) const { + if (position.x >= (p_aabb.position.x + p_aabb.size.x)) { + return false; + } + if ((position.x + size.x) <= p_aabb.position.x) { + return false; + } + if (position.y >= (p_aabb.position.y + p_aabb.size.y)) { + return false; + } + if ((position.y + size.y) <= p_aabb.position.y) { + return false; + } + if (position.z >= (p_aabb.position.z + p_aabb.size.z)) { + return false; + } + if ((position.z + size.z) <= p_aabb.position.z) { + return false; + } + + return true; +} + +inline bool AABB::intersects_inclusive(const AABB &p_aabb) const { + if (position.x > (p_aabb.position.x + p_aabb.size.x)) { + return false; + } + if ((position.x + size.x) < p_aabb.position.x) { + return false; + } + if (position.y > (p_aabb.position.y + p_aabb.size.y)) { + return false; + } + if ((position.y + size.y) < p_aabb.position.y) { + return false; + } + if (position.z > (p_aabb.position.z + p_aabb.size.z)) { + return false; + } + if ((position.z + size.z) < p_aabb.position.z) { + return false; + } + + return true; +} + +inline bool AABB::encloses(const AABB &p_aabb) const { + Vector3 src_min = position; + Vector3 src_max = position + size; + Vector3 dst_min = p_aabb.position; + Vector3 dst_max = p_aabb.position + p_aabb.size; + + return ( + (src_min.x <= dst_min.x) && + (src_max.x > dst_max.x) && + (src_min.y <= dst_min.y) && + (src_max.y > dst_max.y) && + (src_min.z <= dst_min.z) && + (src_max.z > dst_max.z)); +} + +Vector3 AABB::get_support(const Vector3 &p_normal) const { + Vector3 half_extents = size * 0.5f; + Vector3 ofs = position + half_extents; + + return Vector3( + (p_normal.x > 0) ? -half_extents.x : half_extents.x, + (p_normal.y > 0) ? -half_extents.y : half_extents.y, + (p_normal.z > 0) ? -half_extents.z : half_extents.z) + + ofs; +} + +Vector3 AABB::get_endpoint(int p_point) const { + switch (p_point) { + case 0: + return Vector3(position.x, position.y, position.z); + case 1: + return Vector3(position.x, position.y, position.z + size.z); + case 2: + return Vector3(position.x, position.y + size.y, position.z); + case 3: + return Vector3(position.x, position.y + size.y, position.z + size.z); + case 4: + return Vector3(position.x + size.x, position.y, position.z); + case 5: + return Vector3(position.x + size.x, position.y, position.z + size.z); + case 6: + return Vector3(position.x + size.x, position.y + size.y, position.z); + case 7: + return Vector3(position.x + size.x, position.y + size.y, position.z + size.z); + }; + + ERR_FAIL_V(Vector3()); +} + +bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const { + Vector3 half_extents = size * 0.5f; + Vector3 ofs = position + half_extents; + + for (int i = 0; i < p_plane_count; i++) { + const Plane &p = p_planes[i]; + Vector3 point( + (p.normal.x > 0) ? -half_extents.x : half_extents.x, + (p.normal.y > 0) ? -half_extents.y : half_extents.y, + (p.normal.z > 0) ? -half_extents.z : half_extents.z); + point += ofs; + if (p.is_point_over(point)) { + return false; + } + } + + // Make sure all points in the shape aren't fully separated from the AABB on + // each axis. + int bad_point_counts_positive[3] = { 0 }; + int bad_point_counts_negative[3] = { 0 }; + + for (int k = 0; k < 3; k++) { + for (int i = 0; i < p_point_count; i++) { + if (p_points[i].coord[k] > ofs.coord[k] + half_extents.coord[k]) { + bad_point_counts_positive[k]++; + } + if (p_points[i].coord[k] < ofs.coord[k] - half_extents.coord[k]) { + bad_point_counts_negative[k]++; + } + } + + if (bad_point_counts_negative[k] == p_point_count) { + return false; + } + if (bad_point_counts_positive[k] == p_point_count) { + return false; + } + } + + return true; +} + +bool AABB::inside_convex_shape(const Plane *p_planes, int p_plane_count) const { + Vector3 half_extents = size * 0.5f; + Vector3 ofs = position + half_extents; + + for (int i = 0; i < p_plane_count; i++) { + const Plane &p = p_planes[i]; + Vector3 point( + (p.normal.x < 0) ? -half_extents.x : half_extents.x, + (p.normal.y < 0) ? -half_extents.y : half_extents.y, + (p.normal.z < 0) ? -half_extents.z : half_extents.z); + point += ofs; + if (p.is_point_over(point)) { + return false; + } + } + + return true; +} + +bool AABB::has_point(const Vector3 &p_point) const { + if (p_point.x < position.x) { + return false; + } + if (p_point.y < position.y) { + return false; + } + if (p_point.z < position.z) { + return false; + } + if (p_point.x > position.x + size.x) { + return false; + } + if (p_point.y > position.y + size.y) { + return false; + } + if (p_point.z > position.z + size.z) { + return false; + } + + return true; +} + +inline void AABB::expand_to(const Vector3 &p_vector) { + Vector3 begin = position; + Vector3 end = position + size; + + if (p_vector.x < begin.x) { + begin.x = p_vector.x; + } + if (p_vector.y < begin.y) { + begin.y = p_vector.y; + } + if (p_vector.z < begin.z) { + begin.z = p_vector.z; + } + + if (p_vector.x > end.x) { + end.x = p_vector.x; + } + if (p_vector.y > end.y) { + end.y = p_vector.y; + } + if (p_vector.z > end.z) { + end.z = p_vector.z; + } + + position = begin; + size = end - begin; +} + +void AABB::project_range_in_plane(const Plane &p_plane, real_t &r_min, real_t &r_max) const { + Vector3 half_extents = size * 0.5f; + Vector3 center(position.x + half_extents.x, position.y + half_extents.y, position.z + half_extents.z); + + real_t length = p_plane.normal.abs().dot(half_extents); + real_t distance = p_plane.distance_to(center); + r_min = distance - length; + r_max = distance + length; +} + +inline real_t AABB::get_longest_axis_size() const { + real_t max_size = size.x; + + if (size.y > max_size) { + max_size = size.y; + } + + if (size.z > max_size) { + max_size = size.z; + } + + return max_size; +} + +inline real_t AABB::get_shortest_axis_size() const { + real_t max_size = size.x; + + if (size.y < max_size) { + max_size = size.y; + } + + if (size.z < max_size) { + max_size = size.z; + } + + return max_size; +} + +bool AABB::smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t t0, real_t t1) const { + real_t divx = 1 / p_dir.x; + real_t divy = 1 / p_dir.y; + real_t divz = 1 / p_dir.z; + + Vector3 upbound = position + size; + real_t tmin, tmax, tymin, tymax, tzmin, tzmax; + if (p_dir.x >= 0) { + tmin = (position.x - p_from.x) * divx; + tmax = (upbound.x - p_from.x) * divx; + } else { + tmin = (upbound.x - p_from.x) * divx; + tmax = (position.x - p_from.x) * divx; + } + if (p_dir.y >= 0) { + tymin = (position.y - p_from.y) * divy; + tymax = (upbound.y - p_from.y) * divy; + } else { + tymin = (upbound.y - p_from.y) * divy; + tymax = (position.y - p_from.y) * divy; + } + if ((tmin > tymax) || (tymin > tmax)) { + return false; + } + if (tymin > tmin) { + tmin = tymin; + } + if (tymax < tmax) { + tmax = tymax; + } + if (p_dir.z >= 0) { + tzmin = (position.z - p_from.z) * divz; + tzmax = (upbound.z - p_from.z) * divz; + } else { + tzmin = (upbound.z - p_from.z) * divz; + tzmax = (position.z - p_from.z) * divz; + } + if ((tmin > tzmax) || (tzmin > tmax)) { + return false; + } + if (tzmin > tmin) { + tmin = tzmin; + } + if (tzmax < tmax) { + tmax = tzmax; + } + return ((tmin < t1) && (tmax > t0)); +} + +void AABB::grow_by(real_t p_amount) { + position.x -= p_amount; + position.y -= p_amount; + position.z -= p_amount; + size.x += 2 * p_amount; + size.y += 2 * p_amount; + size.z += 2 * p_amount; +} + +void AABB::quantize(real_t p_unit) { + size += position; + + position.x -= Math::fposmodp(position.x, p_unit); + position.y -= Math::fposmodp(position.y, p_unit); + position.z -= Math::fposmodp(position.z, p_unit); + + size.x -= Math::fposmodp(size.x, p_unit); + size.y -= Math::fposmodp(size.y, p_unit); + size.z -= Math::fposmodp(size.z, p_unit); + + size.x += p_unit; + size.y += p_unit; + size.z += p_unit; + + size -= position; +} + +AABB AABB::quantized(real_t p_unit) const { + AABB ret = *this; + ret.quantize(p_unit); + return ret; +} + +#endif // AABB_H +#line 0 + +#line 1 "sfwl/core/quaternion.h" +#ifndef QUATERNION_H +#define QUATERNION_H + +/*************************************************************************/ +/* quaternion.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct _NO_DISCARD_CLASS_ Quaternion { + union { + struct { + real_t x; + real_t y; + real_t z; + real_t w; + }; + real_t components[4]; + }; + + _FORCE_INLINE_ real_t &operator[](int idx) { + return components[idx]; + } + _FORCE_INLINE_ const real_t &operator[](int idx) const { + return components[idx]; + } + + _FORCE_INLINE_ real_t length_squared() const; + bool is_equal_approx(const Quaternion &p_quat) const; + real_t length() const; + void normalize(); + Quaternion normalized() const; + bool is_normalized() const; + Quaternion inverse() const; + Quaternion log() const; + Quaternion exp() const; + _FORCE_INLINE_ real_t dot(const Quaternion &p_q) const; + real_t angle_to(const Quaternion &p_to) const; + + void set_euler_xyz(const Vector3 &p_euler); + Vector3 get_euler_xyz() const; + void set_euler_yxz(const Vector3 &p_euler); + Vector3 get_euler_yxz() const; + + void set_euler(const Vector3 &p_euler) { set_euler_yxz(p_euler); }; + Vector3 get_euler() const { return get_euler_yxz(); }; + + Quaternion slerp(const Quaternion &p_to, const real_t &p_weight) const; + Quaternion slerpni(const Quaternion &p_to, const real_t &p_weight) const; + Quaternion cubic_slerp(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const; + Quaternion spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const; + + Vector3 get_axis() const; + float get_angle() const; + + void set_axis_angle(const Vector3 &axis, const real_t &angle); + _FORCE_INLINE_ void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { + r_angle = 2 * Math::acos(w); + real_t r = ((real_t)1) / Math::sqrt(1 - w * w); + r_axis.x = x * r; + r_axis.y = y * r; + r_axis.z = z * r; + } + + void operator*=(const Quaternion &p_q); + Quaternion operator*(const Quaternion &p_q) const; + + Quaternion operator*(const Vector3 &v) const { + return Quaternion(w * v.x + y * v.z - z * v.y, + w * v.y + z * v.x - x * v.z, + w * v.z + x * v.y - y * v.x, + -x * v.x - y * v.y - z * v.z); + } + + _FORCE_INLINE_ Vector3 xform(const Vector3 &v) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(!is_normalized(), v, "The quaternion must be normalized."); +#endif + Vector3 u(x, y, z); + Vector3 uv = u.cross(v); + return v + ((uv * w) + u.cross(uv)) * ((real_t)2); + } + + _FORCE_INLINE_ void operator+=(const Quaternion &p_q); + _FORCE_INLINE_ void operator-=(const Quaternion &p_q); + _FORCE_INLINE_ void operator*=(const real_t &s); + _FORCE_INLINE_ void operator/=(const real_t &s); + _FORCE_INLINE_ Quaternion operator+(const Quaternion &q2) const; + _FORCE_INLINE_ Quaternion operator-(const Quaternion &q2) const; + _FORCE_INLINE_ Quaternion operator-() const; + _FORCE_INLINE_ Quaternion operator*(const real_t &s) const; + _FORCE_INLINE_ Quaternion operator/(const real_t &s) const; + + _FORCE_INLINE_ bool operator==(const Quaternion &p_quat) const; + _FORCE_INLINE_ bool operator!=(const Quaternion &p_quat) const; + + operator String() const; + + inline void set(real_t p_x, real_t p_y, real_t p_z, real_t p_w) { + x = p_x; + y = p_y; + z = p_z; + w = p_w; + } + inline Quaternion(real_t p_x, real_t p_y, real_t p_z, real_t p_w) : + x(p_x), + y(p_y), + z(p_z), + w(p_w) { + } + Quaternion(const Vector3 &axis, const real_t &angle) { + set_axis_angle(axis, angle); + } + + Quaternion(const Vector3 &euler) { + set_euler(euler); + } + Quaternion(const Quaternion &p_q) : + x(p_q.x), + y(p_q.y), + z(p_q.z), + w(p_q.w) { + } + + Quaternion &operator=(const Quaternion &p_q) { + x = p_q.x; + y = p_q.y; + z = p_q.z; + w = p_q.w; + return *this; + } + + Quaternion(const Vector3 &v0, const Vector3 &v1) // shortest arc + { + Vector3 c = v0.cross(v1); + real_t d = v0.dot(v1); + + if (d < -1 + (real_t)CMP_EPSILON) { + x = 0; + y = 1; + z = 0; + w = 0; + } else { + real_t s = Math::sqrt((1 + d) * 2); + real_t rs = 1 / s; + + x = c.x * rs; + y = c.y * rs; + z = c.z * rs; + w = s * 0.5f; + } + } + + inline Quaternion() : + x(0), + y(0), + z(0), + w(1) { + } +}; + +real_t Quaternion::dot(const Quaternion &p_q) const { + return x * p_q.x + y * p_q.y + z * p_q.z + w * p_q.w; +} + +real_t Quaternion::length_squared() const { + return dot(*this); +} + +void Quaternion::operator+=(const Quaternion &p_q) { + x += p_q.x; + y += p_q.y; + z += p_q.z; + w += p_q.w; +} + +void Quaternion::operator-=(const Quaternion &p_q) { + x -= p_q.x; + y -= p_q.y; + z -= p_q.z; + w -= p_q.w; +} + +void Quaternion::operator*=(const real_t &s) { + x *= s; + y *= s; + z *= s; + w *= s; +} + +void Quaternion::operator/=(const real_t &s) { + *this *= 1 / s; +} + +Quaternion Quaternion::operator+(const Quaternion &q2) const { + const Quaternion &q1 = *this; + return Quaternion(q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w); +} + +Quaternion Quaternion::operator-(const Quaternion &q2) const { + const Quaternion &q1 = *this; + return Quaternion(q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w); +} + +Quaternion Quaternion::operator-() const { + const Quaternion &q2 = *this; + return Quaternion(-q2.x, -q2.y, -q2.z, -q2.w); +} + +Quaternion Quaternion::operator*(const real_t &s) const { + return Quaternion(x * s, y * s, z * s, w * s); +} + +Quaternion Quaternion::operator/(const real_t &s) const { + return *this * (1 / s); +} + +bool Quaternion::operator==(const Quaternion &p_quat) const { + return x == p_quat.x && y == p_quat.y && z == p_quat.z && w == p_quat.w; +} + +bool Quaternion::operator!=(const Quaternion &p_quat) const { + return x != p_quat.x || y != p_quat.y || z != p_quat.z || w != p_quat.w; +} + +#endif +#line 0 + +#line 1 "sfwl/core/projection.h" +/*************************************************************************/ +/* projection.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#ifndef PROJECTION_H +#define PROJECTION_H + + + +struct AABB; +struct Plane; +struct Rect2; +struct Transform; +struct Vector2; + +struct _NO_DISCARD_CLASS_ Projection { + enum Planes { + PLANE_NEAR, + PLANE_FAR, + PLANE_LEFT, + PLANE_TOP, + PLANE_RIGHT, + PLANE_BOTTOM + }; + + Vector4 matrix[4]; + + _FORCE_INLINE_ const Vector4 &operator[](const int p_axis) const { + DEV_ASSERT((unsigned int)p_axis < 4); + return matrix[p_axis]; + } + + _FORCE_INLINE_ Vector4 &operator[](const int p_axis) { + DEV_ASSERT((unsigned int)p_axis < 4); + return matrix[p_axis]; + } + + float determinant() const; + void set_identity(); + void set_zero(); + void set_light_bias(); + void set_depth_correction(bool p_flip_y = true); + + void set_light_atlas_rect(const Rect2 &p_rect); + void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false); + void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist); + void set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far); + void set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar); + void set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false); + void set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far); + void set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false); + void adjust_perspective_znear(real_t p_new_znear); + + static Projection create_depth_correction(bool p_flip_y); + static Projection create_light_atlas_rect(const Rect2 &p_rect); + static Projection create_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false); + static Projection create_perspective_hmd(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist); + static Projection create_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far); + static Projection create_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar); + static Projection create_orthogonal_aspect(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false); + static Projection create_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far); + static Projection create_frustum_aspect(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false); + static Projection create_fit_aabb(const AABB &p_aabb); + + Projection perspective_znear_adjusted(real_t p_new_znear) const; + Plane get_projection_plane(Planes p_plane) const; + Projection flipped_y() const; + Projection jitter_offseted(const Vector2 &p_offset) const; + + static real_t get_fovy(real_t p_fovx, real_t p_aspect) { + return Math::rad2deg(Math::atan(p_aspect * Math::tan(Math::deg2rad(p_fovx) * 0.5)) * 2.0); + } + + real_t calculate_fovy(real_t p_fovx, real_t p_aspect) { + return Math::rad2deg(Math::atan(p_aspect * Math::tan(Math::deg2rad(p_fovx) * 0.5)) * 2.0); + } + + real_t get_z_far() const; + real_t get_z_near() const; + real_t get_aspect() const; + real_t get_fov() const; + bool is_orthogonal() const; + + Vector get_projection_planes(const Transform &p_transform) const; + + bool get_endpoints(const Transform &p_transform, Vector3 *p_8points) const; + Vector2 get_viewport_half_extents() const; + Vector2 get_far_plane_half_extents() const; + + void invert(); + Projection inverse() const; + + Projection operator*(const Projection &p_matrix) const; + + Vector4 xform(const Vector4 &p_vec4) const; + Vector4 xform_inv(const Vector4 &p_vec4) const; + + _FORCE_INLINE_ Vector3 xform(const Vector3 &p_vector) const; + Plane xform(const Plane &p_plane) const; + + operator String() const; + + void scale_translate_to_fit(const AABB &p_aabb); + void add_jitter_offset(const Vector2 &p_offset); + void make_scale(const Vector3 &p_scale); + int get_pixels_per_meter(int p_for_pixel_width) const; + operator Transform() const; + + void flip_y(); + + bool operator==(const Projection &p_cam) const { + for (uint32_t i = 0; i < 4; i++) { + for (uint32_t j = 0; j < 4; j++) { + if (matrix[i][j] != p_cam.matrix[i][j]) { + return false; + } + } + } + return true; + } + + bool operator!=(const Projection &p_cam) const { + return !(*this == p_cam); + } + + float get_lod_multiplier() const; + + _FORCE_INLINE_ void set_perspective1(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false) { + set_perspective(p_fovy_degrees, p_aspect, p_z_near, p_z_far, p_flip_fov); + } + _FORCE_INLINE_ void set_perspective2(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) { + set_perspective(p_fovy_degrees, p_aspect, p_z_near, p_z_far, p_flip_fov, p_eye, p_intraocular_dist, p_convergence_dist); + } + _FORCE_INLINE_ void set_orthogonal1(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) { + set_orthogonal(p_left, p_right, p_bottom, p_top, p_znear, p_zfar); + } + _FORCE_INLINE_ void set_orthogonal2(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false) { + set_orthogonal(p_size, p_aspect, p_znear, p_zfar, p_flip_fov); + } + _FORCE_INLINE_ void set_frustum1(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) { + set_frustum(p_left, p_right, p_bottom, p_top, p_near, p_far); + } + //Vector2 is incomplete here + void set_frustum2(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false); + + Projection(); + Projection(const Vector4 &p_x, const Vector4 &p_y, const Vector4 &p_z, const Vector4 &p_w); + Projection(const Transform &p_transform); + ~Projection(); +}; + +Vector3 Projection::xform(const Vector3 &p_vec3) const { + Vector3 ret; + ret.x = matrix[0][0] * p_vec3.x + matrix[1][0] * p_vec3.y + matrix[2][0] * p_vec3.z + matrix[3][0]; + ret.y = matrix[0][1] * p_vec3.x + matrix[1][1] * p_vec3.y + matrix[2][1] * p_vec3.z + matrix[3][1]; + ret.z = matrix[0][2] * p_vec3.x + matrix[1][2] * p_vec3.y + matrix[2][2] * p_vec3.z + matrix[3][2]; + real_t w = matrix[0][3] * p_vec3.x + matrix[1][3] * p_vec3.y + matrix[2][3] * p_vec3.z + matrix[3][3]; + return ret / w; +} + +#endif // PROJECTION_H +#line 0 + +#line 1 "sfwl/core/basis.h" +#ifndef BASIS_H +#define BASIS_H + +/*************************************************************************/ +/* basis.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct _NO_DISCARD_CLASS_ Basis { + Vector3 rows[3] = { + Vector3(1, 0, 0), + Vector3(0, 1, 0), + Vector3(0, 0, 1) + }; + + _FORCE_INLINE_ const Vector3 &operator[](int p_row) const { + return rows[p_row]; + } + _FORCE_INLINE_ Vector3 &operator[](int p_row) { + return rows[p_row]; + } + + void invert(); + void transpose(); + + Basis inverse() const; + Basis transposed() const; + + _FORCE_INLINE_ real_t determinant() const; + + void from_z(const Vector3 &p_z); + + void rotate(const Vector3 &p_axis, real_t p_phi); + Basis rotated(const Vector3 &p_axis, real_t p_phi) const; + + void rotate_local(const Vector3 &p_axis, real_t p_phi); + Basis rotated_local(const Vector3 &p_axis, real_t p_phi) const; + + void rotate(const Vector3 &p_euler); + Basis rotated(const Vector3 &p_euler) const; + + void rotate(const Quaternion &p_quat); + Basis rotated(const Quaternion &p_quat) const; + + _FORCE_INLINE_ void rotatev(const Vector3 &p_euler) { rotate(p_euler); } + _FORCE_INLINE_ Basis rotatedv(const Vector3 &p_euler) const { return rotated(p_euler); } + _FORCE_INLINE_ void rotateq(const Quaternion &p_quat) { rotate(p_quat); } + _FORCE_INLINE_ Basis rotatedq(const Quaternion &p_quat) const { return rotated(p_quat); } + + Vector3 get_rotation_euler() const; + void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const; + void get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const; + Quaternion get_rotation_quaternion() const; + Vector3 get_rotation() const { return get_rotation_euler(); }; + + void rotate_to_align(const Vector3 &p_start_direction, const Vector3 &p_end_direction); + + Vector3 rotref_posscale_decomposition(Basis &rotref) const; + + Vector3 get_euler_xyz() const; + void set_euler_xyz(const Vector3 &p_euler); + + Vector3 get_euler_xzy() const; + void set_euler_xzy(const Vector3 &p_euler); + + Vector3 get_euler_yzx() const; + void set_euler_yzx(const Vector3 &p_euler); + + Vector3 get_euler_yxz() const; + void set_euler_yxz(const Vector3 &p_euler); + + Vector3 get_euler_zxy() const; + void set_euler_zxy(const Vector3 &p_euler); + + Vector3 get_euler_zyx() const; + void set_euler_zyx(const Vector3 &p_euler); + + Vector3 get_euler() const { return get_euler_yxz(); } + void set_euler(const Vector3 &p_euler) { set_euler_yxz(p_euler); } + + Quaternion get_quaternion() const; + void set_quaternion(const Quaternion &p_quat); + + void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const; + void set_axis_angle(const Vector3 &p_axis, real_t p_phi); + + void scale(const Vector3 &p_scale); + Basis scaled(const Vector3 &p_scale) const; + + void scale_local(const Vector3 &p_scale); + Basis scaled_local(const Vector3 &p_scale) const; + + void scale_orthogonal(const Vector3 &p_scale); + Basis scaled_orthogonal(const Vector3 &p_scale) const; + + void make_scale_uniform(); + real_t get_uniform_scale() const; + + Vector3 get_scale() const; + Vector3 get_scale_abs() const; + Vector3 get_scale_local() const; + + void set_axis_angle_scale(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale); + void set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale); + void set_quaternion_scale(const Quaternion &p_quat, const Vector3 &p_scale); + + // transposed dot products + _FORCE_INLINE_ real_t tdotx(const Vector3 &v) const { + return rows[0][0] * v[0] + rows[1][0] * v[1] + rows[2][0] * v[2]; + } + _FORCE_INLINE_ real_t tdoty(const Vector3 &v) const { + return rows[0][1] * v[0] + rows[1][1] * v[1] + rows[2][1] * v[2]; + } + _FORCE_INLINE_ real_t tdotz(const Vector3 &v) const { + return rows[0][2] * v[0] + rows[1][2] * v[1] + rows[2][2] * v[2]; + } + + bool is_equal_approx(const Basis &p_basis) const; + bool is_equal_approx_ratio(const Basis &a, const Basis &b, real_t p_epsilon = UNIT_EPSILON) const; + + bool operator==(const Basis &p_matrix) const; + bool operator!=(const Basis &p_matrix) const; + + _FORCE_INLINE_ Vector3 xform(const Vector3 &p_vector) const; + _FORCE_INLINE_ Vector3 xform_inv(const Vector3 &p_vector) const; + + _FORCE_INLINE_ Vector3i xform(const Vector3i &p_vector) const; + _FORCE_INLINE_ Vector3i xform_inv(const Vector3i &p_vector) const; + + _FORCE_INLINE_ void operator*=(const Basis &p_matrix); + _FORCE_INLINE_ Basis operator*(const Basis &p_matrix) const; + _FORCE_INLINE_ void operator+=(const Basis &p_matrix); + _FORCE_INLINE_ Basis operator+(const Basis &p_matrix) const; + _FORCE_INLINE_ void operator-=(const Basis &p_matrix); + _FORCE_INLINE_ Basis operator-(const Basis &p_matrix) const; + _FORCE_INLINE_ void operator*=(real_t p_val); + _FORCE_INLINE_ Basis operator*(real_t p_val) const; + + int get_orthogonal_index() const; + void set_orthogonal_index(int p_index); + + void set_diagonal(const Vector3 &p_diag); + + bool is_orthogonal() const; + bool is_diagonal() const; + bool is_rotation() const; + + Basis slerp(const Basis &p_to, const real_t &p_weight) const; + _FORCE_INLINE_ Basis lerp(const Basis &p_to, const real_t &p_weight) const; + void rotate_sh(real_t *p_values); + + operator String() const; + + /* create / set */ + + _FORCE_INLINE_ void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { + rows[0][0] = xx; + rows[0][1] = xy; + rows[0][2] = xz; + rows[1][0] = yx; + rows[1][1] = yy; + rows[1][2] = yz; + rows[2][0] = zx; + rows[2][1] = zy; + rows[2][2] = zz; + } + _FORCE_INLINE_ void set(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z) { + set_column(0, p_x); + set_column(1, p_y); + set_column(2, p_z); + } + + _FORCE_INLINE_ Vector3 get_column(int i) const { + return Vector3(rows[0][i], rows[1][i], rows[2][i]); + } + + _FORCE_INLINE_ void set_column(int p_index, const Vector3 &p_value) { + // Set actual basis axis column (we store transposed as rows for performance). + rows[0][p_index] = p_value.x; + rows[1][p_index] = p_value.y; + rows[2][p_index] = p_value.z; + } + + _FORCE_INLINE_ void set_columns(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z) { + set_column(0, p_x); + set_column(1, p_y); + set_column(2, p_z); + } + + _FORCE_INLINE_ Vector3 get_row(int i) const { + return Vector3(rows[i][0], rows[i][1], rows[i][2]); + } + _FORCE_INLINE_ void set_row(int i, const Vector3 &p_row) { + rows[i][0] = p_row.x; + rows[i][1] = p_row.y; + rows[i][2] = p_row.z; + } + + _FORCE_INLINE_ Vector3 get_axis(int i) const { + return Vector3(rows[0][i], rows[1][i], rows[2][i]); + } + + _FORCE_INLINE_ void set_axis(int p_index, const Vector3 &p_value) { + // Set actual basis axis column (we store transposed as rows for performance). + rows[0][p_index] = p_value.x; + rows[1][p_index] = p_value.y; + rows[2][p_index] = p_value.z; + } + + _FORCE_INLINE_ Vector3 get_main_diagonal() const { + return Vector3(rows[0][0], rows[1][1], rows[2][2]); + } + + _FORCE_INLINE_ void set_zero() { + rows[0].zero(); + rows[1].zero(); + rows[2].zero(); + } + + _FORCE_INLINE_ Basis transpose_xform(const Basis &m) const { + return Basis( + rows[0].x * m[0].x + rows[1].x * m[1].x + rows[2].x * m[2].x, + rows[0].x * m[0].y + rows[1].x * m[1].y + rows[2].x * m[2].y, + rows[0].x * m[0].z + rows[1].x * m[1].z + rows[2].x * m[2].z, + rows[0].y * m[0].x + rows[1].y * m[1].x + rows[2].y * m[2].x, + rows[0].y * m[0].y + rows[1].y * m[1].y + rows[2].y * m[2].y, + rows[0].y * m[0].z + rows[1].y * m[1].z + rows[2].y * m[2].z, + rows[0].z * m[0].x + rows[1].z * m[1].x + rows[2].z * m[2].x, + rows[0].z * m[0].y + rows[1].z * m[1].y + rows[2].z * m[2].y, + rows[0].z * m[0].z + rows[1].z * m[1].z + rows[2].z * m[2].z); + } + Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { + set(xx, xy, xz, yx, yy, yz, zx, zy, zz); + } + + void orthonormalize(); + Basis orthonormalized() const; + + void orthogonalize(); + Basis orthogonalized() const; + + bool is_symmetric() const; + Basis diagonalize(); + + // The following normal xform functions are correct for non-uniform scales. + // Use these two functions in combination to xform a series of normals. + // First use get_normal_xform_basis() to precalculate the inverse transpose. + // Then apply xform_normal_fast() multiple times using the inverse transpose basis. + Basis get_normal_xform_basis() const { return inverse().transposed(); } + + // N.B. This only does a normal transform if the basis used is the inverse transpose! + // Otherwise use xform_normal(). + Vector3 xform_normal_fast(const Vector3 &p_vector) const { return xform(p_vector).normalized(); } + + // This function does the above but for a single normal vector. It is considerably slower, so should usually + // only be used in cases of single normals, or when the basis changes each time. + Vector3 xform_normal(const Vector3 &p_vector) const { return get_normal_xform_basis().xform_normal_fast(p_vector); } + + static Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)); + static Basis from_scale(const Vector3 &p_scale); + + operator Quaternion() const { return get_quaternion(); } + + Basis(const Quaternion &p_quat) { set_quaternion(p_quat); } + Basis(const Quaternion &p_quat, const Vector3 &p_scale) { set_quaternion_scale(p_quat, p_scale); } + + Basis(const Vector3 &p_euler) { set_euler(p_euler); } + Basis(const Vector3 &p_euler, const Vector3 &p_scale) { set_euler_scale(p_euler, p_scale); } + + Basis(const Vector3 &p_axis, real_t p_phi) { set_axis_angle(p_axis, p_phi); } + Basis(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_phi, p_scale); } + + _FORCE_INLINE_ Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) { + rows[0] = row0; + rows[1] = row1; + rows[2] = row2; + } + + _FORCE_INLINE_ Basis() {} +}; + +_FORCE_INLINE_ void Basis::operator*=(const Basis &p_matrix) { + set( + p_matrix.tdotx(rows[0]), p_matrix.tdoty(rows[0]), p_matrix.tdotz(rows[0]), + p_matrix.tdotx(rows[1]), p_matrix.tdoty(rows[1]), p_matrix.tdotz(rows[1]), + p_matrix.tdotx(rows[2]), p_matrix.tdoty(rows[2]), p_matrix.tdotz(rows[2])); +} + +_FORCE_INLINE_ Basis Basis::operator*(const Basis &p_matrix) const { + return Basis( + p_matrix.tdotx(rows[0]), p_matrix.tdoty(rows[0]), p_matrix.tdotz(rows[0]), + p_matrix.tdotx(rows[1]), p_matrix.tdoty(rows[1]), p_matrix.tdotz(rows[1]), + p_matrix.tdotx(rows[2]), p_matrix.tdoty(rows[2]), p_matrix.tdotz(rows[2])); +} + +_FORCE_INLINE_ void Basis::operator+=(const Basis &p_matrix) { + rows[0] += p_matrix.rows[0]; + rows[1] += p_matrix.rows[1]; + rows[2] += p_matrix.rows[2]; +} + +_FORCE_INLINE_ Basis Basis::operator+(const Basis &p_matrix) const { + Basis ret(*this); + ret += p_matrix; + return ret; +} + +_FORCE_INLINE_ void Basis::operator-=(const Basis &p_matrix) { + rows[0] -= p_matrix.rows[0]; + rows[1] -= p_matrix.rows[1]; + rows[2] -= p_matrix.rows[2]; +} + +_FORCE_INLINE_ Basis Basis::operator-(const Basis &p_matrix) const { + Basis ret(*this); + ret -= p_matrix; + return ret; +} + +_FORCE_INLINE_ void Basis::operator*=(real_t p_val) { + rows[0] *= p_val; + rows[1] *= p_val; + rows[2] *= p_val; +} + +_FORCE_INLINE_ Basis Basis::operator*(real_t p_val) const { + Basis ret(*this); + ret *= p_val; + return ret; +} + +Vector3 Basis::xform(const Vector3 &p_vector) const { + return Vector3( + rows[0].dot(p_vector), + rows[1].dot(p_vector), + rows[2].dot(p_vector)); +} + +Vector3i Basis::xform_inv(const Vector3i &p_vector) const { + return Vector3i( + (rows[0][0] * p_vector.x) + (rows[1][0] * p_vector.y) + (rows[2][0] * p_vector.z), + (rows[0][1] * p_vector.x) + (rows[1][1] * p_vector.y) + (rows[2][1] * p_vector.z), + (rows[0][2] * p_vector.x) + (rows[1][2] * p_vector.y) + (rows[2][2] * p_vector.z)); +} + +Vector3i Basis::xform(const Vector3i &p_vector) const { + return Vector3i( + rows[0].dot(p_vector), + rows[1].dot(p_vector), + rows[2].dot(p_vector)); +} + +Vector3 Basis::xform_inv(const Vector3 &p_vector) const { + return Vector3( + (rows[0][0] * p_vector.x) + (rows[1][0] * p_vector.y) + (rows[2][0] * p_vector.z), + (rows[0][1] * p_vector.x) + (rows[1][1] * p_vector.y) + (rows[2][1] * p_vector.z), + (rows[0][2] * p_vector.x) + (rows[1][2] * p_vector.y) + (rows[2][2] * p_vector.z)); +} + +real_t Basis::determinant() const { + return rows[0][0] * (rows[1][1] * rows[2][2] - rows[2][1] * rows[1][2]) - + rows[1][0] * (rows[0][1] * rows[2][2] - rows[2][1] * rows[0][2]) + + rows[2][0] * (rows[0][1] * rows[1][2] - rows[1][1] * rows[0][2]); +} + +Basis Basis::lerp(const Basis &p_to, const real_t &p_weight) const { + Basis b; + b.rows[0] = rows[0].linear_interpolate(p_to.rows[0], p_weight); + b.rows[1] = rows[1].linear_interpolate(p_to.rows[1], p_weight); + b.rows[2] = rows[2].linear_interpolate(p_to.rows[2], p_weight); + + return b; +} +#endif // BASIS_H +#line 0 + + +#line 1 "sfwl/core/transform_2d.h" +#ifndef TRANSFORM_2D_H +#define TRANSFORM_2D_H + +/*************************************************************************/ +/* transform_2d.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct _NO_DISCARD_CLASS_ Transform2D { + // Warning #1: basis of Transform2D is stored differently from Basis. In terms of columns array, the basis matrix looks like "on paper": + // M = (columns[0][0] columns[1][0]) + // (columns[0][1] columns[1][1]) + // This is such that the columns, which can be interpreted as basis vectors of the coordinate system "painted" on the object, can be accessed as columns[i]. + // Note that this is the opposite of the indices in mathematical texts, meaning: $M_{12}$ in a math book corresponds to columns[1][0] here. + // This requires additional care when working with explicit indices. + // See https://en.wikipedia.org/wiki/Row-_and_column-major_order for further reading. + + // Warning #2: 2D be aware that unlike 3D code, 2D code uses a left-handed coordinate system: Y-axis points down, + // and angle is measure from +X to +Y in a clockwise-fashion. + + Vector2 columns[3]; + + _FORCE_INLINE_ real_t tdotx(const Vector2 &v) const { return columns[0][0] * v.x + columns[1][0] * v.y; } + _FORCE_INLINE_ real_t tdoty(const Vector2 &v) const { return columns[0][1] * v.x + columns[1][1] * v.y; } + + const Vector2 &operator[](int p_idx) const { return columns[p_idx]; } + Vector2 &operator[](int p_idx) { return columns[p_idx]; } + + _FORCE_INLINE_ Vector2 get_axis(int p_axis) const { + ERR_FAIL_INDEX_V(p_axis, 3, Vector2()); + return columns[p_axis]; + } + _FORCE_INLINE_ void set_axis(int p_axis, const Vector2 &p_vec) { + ERR_FAIL_INDEX(p_axis, 3); + columns[p_axis] = p_vec; + } + + _FORCE_INLINE_ Vector2 get_column(int p_colum) const { + ERR_FAIL_INDEX_V(p_colum, 3, Vector2()); + return columns[p_colum]; + } + _FORCE_INLINE_ void set_column(int p_colum, const Vector2 &p_vec) { + ERR_FAIL_INDEX(p_colum, 3); + columns[p_colum] = p_vec; + } + + void invert(); + Transform2D inverse() const; + + void affine_invert(); + Transform2D affine_inverse() const; + + void set_rotation(real_t p_rot); + real_t get_rotation() const; + real_t get_skew() const; + void set_skew(const real_t p_angle); + _FORCE_INLINE_ void set_rotation_and_scale(real_t p_rot, const Size2 &p_scale); + _FORCE_INLINE_ void set_rotation_scale_and_skew(const real_t p_rot, const Size2 &p_scale, const real_t p_skew); + void rotate(real_t p_phi); + + void scale(const Size2 &p_scale); + void scale_basis(const Size2 &p_scale); + void translate(real_t p_tx, real_t p_ty); + void translate(const Vector2 &p_offset); + void translate_local(real_t p_tx, real_t p_ty); + void translate_local(const Vector2 &p_translation); + + void translater(real_t p_tx, real_t p_ty); + void translatev(const Vector2 &p_offset); + void translate_localr(real_t p_tx, real_t p_ty); + void translate_localv(const Vector2 &p_translation); + + real_t basis_determinant() const; + + Size2 get_scale() const; + void set_scale(const Size2 &p_scale); + + _FORCE_INLINE_ const Vector2 &get_origin() const { return columns[2]; } + _FORCE_INLINE_ void set_origin(const Vector2 &p_origin) { columns[2] = p_origin; } + + Transform2D basis_scaled(const Size2 &p_scale) const; + Transform2D scaled(const Size2 &p_scale) const; + Transform2D scaled_local(const Size2 &p_scale) const; + Transform2D translated(const Vector2 &p_offset) const; + Transform2D translated_local(const Vector2 &p_offset) const; + Transform2D rotated(const real_t p_angle) const; + Transform2D rotated_local(const real_t p_angle) const; + + Transform2D untranslated() const; + + void orthonormalize(); + Transform2D orthonormalized() const; + bool is_equal_approx(const Transform2D &p_transform) const; + + Transform2D looking_at(const Vector2 &p_target) const; + + bool operator==(const Transform2D &p_transform) const; + bool operator!=(const Transform2D &p_transform) const; + + void operator*=(const Transform2D &p_transform); + Transform2D operator*(const Transform2D &p_transform) const; + void operator*=(const real_t p_val); + Transform2D operator*(const real_t p_val) const; + + Transform2D interpolate_with(const Transform2D &p_transform, real_t p_c) const; + + _FORCE_INLINE_ Vector2 basis_xform(const Vector2 &p_vec) const; + _FORCE_INLINE_ Vector2 basis_xform_inv(const Vector2 &p_vec) const; + _FORCE_INLINE_ Vector2 xform(const Vector2 &p_vec) const; + _FORCE_INLINE_ Vector2 xform_inv(const Vector2 &p_vec) const; + + _FORCE_INLINE_ Rect2 xform(const Rect2 &p_rect) const; + _FORCE_INLINE_ Rect2 xform_inv(const Rect2 &p_rect) const; + + _FORCE_INLINE_ Vector2i basis_xform(const Vector2i &p_vec) const; + _FORCE_INLINE_ Vector2i basis_xform_inv(const Vector2i &p_vec) const; + _FORCE_INLINE_ Vector2i xform(const Vector2i &p_vec) const; + _FORCE_INLINE_ Vector2i xform_inv(const Vector2i &p_vec) const; + + _FORCE_INLINE_ PoolVector xform(const PoolVector &p_array) const; + _FORCE_INLINE_ PoolVector xform_inv(const PoolVector &p_array) const; + _FORCE_INLINE_ PoolVector xform(const PoolVector &p_array) const; + _FORCE_INLINE_ PoolVector xform_inv(const PoolVector &p_array) const; + + operator String() const; + + Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { + columns[0][0] = xx; + columns[0][1] = xy; + columns[1][0] = yx; + columns[1][1] = yy; + columns[2][0] = ox; + columns[2][1] = oy; + } + + Transform2D(const Vector2 &p_x, const Vector2 &p_y, const Vector2 &p_origin) { + columns[0] = p_x; + columns[1] = p_y; + columns[2] = p_origin; + } + + Transform2D(real_t p_rot, const Vector2 &p_pos); + + Transform2D(const real_t p_rot, const Size2 &p_scale, const real_t p_skew, const Vector2 &p_pos); + + Transform2D() { + columns[0][0] = 1.0; + columns[1][1] = 1.0; + } +}; + +Vector2 Transform2D::basis_xform(const Vector2 &p_vec) const { + return Vector2( + tdotx(p_vec), + tdoty(p_vec)); +} + +Vector2 Transform2D::basis_xform_inv(const Vector2 &p_vec) const { + return Vector2( + columns[0].dot(p_vec), + columns[1].dot(p_vec)); +} + +Vector2 Transform2D::xform(const Vector2 &p_vec) const { + return Vector2( + tdotx(p_vec), + tdoty(p_vec)) + + columns[2]; +} +Vector2 Transform2D::xform_inv(const Vector2 &p_vec) const { + Vector2 v = p_vec - columns[2]; + + return Vector2( + columns[0].dot(v), + columns[1].dot(v)); +} +Rect2 Transform2D::xform(const Rect2 &p_rect) const { + Vector2 x = columns[0] * p_rect.size.x; + Vector2 y = columns[1] * p_rect.size.y; + Vector2 pos = xform(p_rect.position); + + Rect2 new_rect; + new_rect.position = pos; + new_rect.expand_to(pos + x); + new_rect.expand_to(pos + y); + new_rect.expand_to(pos + x + y); + return new_rect; +} + +void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) { + columns[0][0] = Math::cos(p_rot) * p_scale.x; + columns[1][1] = Math::cos(p_rot) * p_scale.y; + columns[1][0] = -Math::sin(p_rot) * p_scale.y; + columns[0][1] = Math::sin(p_rot) * p_scale.x; +} + +void Transform2D::set_rotation_scale_and_skew(const real_t p_rot, const Size2 &p_scale, const real_t p_skew) { + columns[0][0] = Math::cos(p_rot) * p_scale.x; + columns[1][1] = Math::cos(p_rot + p_skew) * p_scale.y; + columns[1][0] = -Math::sin(p_rot + p_skew) * p_scale.y; + columns[0][1] = Math::sin(p_rot) * p_scale.x; +} + +Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const { + Vector2 ends[4] = { + xform_inv(p_rect.position), + xform_inv(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), + xform_inv(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y + p_rect.size.y)), + xform_inv(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y)) + }; + + Rect2 new_rect; + new_rect.position = ends[0]; + new_rect.expand_to(ends[1]); + new_rect.expand_to(ends[2]); + new_rect.expand_to(ends[3]); + + return new_rect; +} + +Vector2i Transform2D::basis_xform(const Vector2i &p_vec) const { + return Vector2i( + tdotx(p_vec), + tdoty(p_vec)); +} + +Vector2i Transform2D::basis_xform_inv(const Vector2i &p_vec) const { + return Vector2i( + columns[0].dot(p_vec), + columns[1].dot(p_vec)); +} + +Vector2i Transform2D::xform(const Vector2i &p_vec) const { + return Vector2i( + tdotx(p_vec), + tdoty(p_vec)) + + columns[2]; +} +Vector2i Transform2D::xform_inv(const Vector2i &p_vec) const { + Vector2i v = p_vec - columns[2]; + + return Vector2i( + columns[0].dot(v), + columns[1].dot(v)); +} + +PoolVector Transform2D::xform(const PoolVector &p_array) const { + PoolVector array; + array.resize(p_array.size()); + + PoolVector::Read r = p_array.read(); + PoolVector::Write w = array.write(); + + for (int i = 0; i < p_array.size(); ++i) { + w[i] = xform(r[i]); + } + return array; +} + +PoolVector Transform2D::xform_inv(const PoolVector &p_array) const { + PoolVector array; + array.resize(p_array.size()); + + PoolVector::Read r = p_array.read(); + PoolVector::Write w = array.write(); + + for (int i = 0; i < p_array.size(); ++i) { + w[i] = xform_inv(r[i]); + } + return array; +} + +PoolVector Transform2D::xform(const PoolVector &p_array) const { + PoolVector array; + array.resize(p_array.size()); + + PoolVector::Read r = p_array.read(); + PoolVector::Write w = array.write(); + + for (int i = 0; i < p_array.size(); ++i) { + w[i] = xform(r[i]); + } + return array; +} + +PoolVector Transform2D::xform_inv(const PoolVector &p_array) const { + PoolVector array; + array.resize(p_array.size()); + + PoolVector::Read r = p_array.read(); + PoolVector::Write w = array.write(); + + for (int i = 0; i < p_array.size(); ++i) { + w[i] = xform_inv(r[i]); + } + return array; +} + +#endif // TRANSFORM_2D_H +#line 0 + +#line 1 "sfwl/core/face3.h" +#ifndef FACE3_H +#define FACE3_H + +/*************************************************************************/ +/* face3.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct _NO_DISCARD_CLASS_ Face3 { + enum Side { + SIDE_OVER, + SIDE_UNDER, + SIDE_SPANNING, + SIDE_COPLANAR + }; + + Vector3 vertex[3]; + + /** + * + * @param p_plane plane used to split the face + * @param p_res array of at least 3 faces, amount used in function return + * @param p_is_point_over array of at least 3 booleans, determining which face is over the plane, amount used in function return + * @param _epsilon constant used for numerical error rounding, to add "thickness" to the plane (so coplanar points can happen) + * @return amount of faces generated by the split, either 0 (means no split possible), 2 or 3 + */ + + int split_by_plane(const Plane &p_plane, Face3 *p_res, bool *p_is_point_over) const; + + Plane get_plane(ClockDirection p_dir = CLOCKWISE) const; + Vector3 get_random_point_inside() const; + + Side get_side_of(const Face3 &p_face, ClockDirection p_clock_dir = CLOCKWISE) const; + + bool is_degenerate() const; + real_t get_area() const; + real_t get_twice_area_squared() const; + + Vector3 get_median_point() const; + Vector3 get_closest_point_to(const Vector3 &p_point) const; + + bool intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection = nullptr) const; + bool intersects_segment(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection = nullptr) const; + + ClockDirection get_clock_dir() const; ///< todo, test if this is returning the proper clockwisity + + void get_support(const Vector3 &p_normal, const Transform &p_transform, Vector3 *p_vertices, int *p_count, int p_max) const; + void project_range(const Vector3 &p_normal, const Transform &p_transform, real_t &r_min, real_t &r_max) const; + + AABB get_aabb() const { + AABB aabb(vertex[0], Vector3()); + aabb.expand_to(vertex[1]); + aabb.expand_to(vertex[2]); + return aabb; + } + + bool intersects_aabb(const AABB &p_aabb) const; + _FORCE_INLINE_ bool intersects_aabb2(const AABB &p_aabb) const; + operator String() const; + + inline Face3() {} + inline Face3(const Vector3 &p_v1, const Vector3 &p_v2, const Vector3 &p_v3) { + vertex[0] = p_v1; + vertex[1] = p_v2; + vertex[2] = p_v3; + } +}; + +inline real_t Face3::get_twice_area_squared() const { + Vector3 edge1 = vertex[1] - vertex[0]; + Vector3 edge2 = vertex[2] - vertex[0]; + return edge1.cross(edge2).length_squared(); +} + +bool Face3::intersects_aabb2(const AABB &p_aabb) const { + Vector3 perp = (vertex[0] - vertex[2]).cross(vertex[0] - vertex[1]); + + Vector3 half_extents = p_aabb.size * 0.5f; + Vector3 ofs = p_aabb.position + half_extents; + + Vector3 sup = Vector3( + (perp.x > 0) ? -half_extents.x : half_extents.x, + (perp.y > 0) ? -half_extents.y : half_extents.y, + (perp.z > 0) ? -half_extents.z : half_extents.z); + + real_t d = perp.dot(vertex[0]); + real_t dist_a = perp.dot(ofs + sup) - d; + real_t dist_b = perp.dot(ofs - sup) - d; + + if (dist_a * dist_b > 0) { + return false; //does not intersect the plane + } + +#define TEST_AXIS(m_ax) \ + { \ + real_t aabb_min = p_aabb.position.m_ax; \ + real_t aabb_max = p_aabb.position.m_ax + p_aabb.size.m_ax; \ + real_t tri_min, tri_max; \ + for (int i = 0; i < 3; i++) { \ + if (i == 0 || vertex[i].m_ax > tri_max) \ + tri_max = vertex[i].m_ax; \ + if (i == 0 || vertex[i].m_ax < tri_min) \ + tri_min = vertex[i].m_ax; \ + } \ + \ + if (tri_max < aabb_min || aabb_max < tri_min) \ + return false; \ + } + + TEST_AXIS(x); + TEST_AXIS(y); + TEST_AXIS(z); + +#undef TEST_AXIS + + Vector3 edge_norms[3] = { + vertex[0] - vertex[1], + vertex[1] - vertex[2], + vertex[2] - vertex[0], + }; + + for (int i = 0; i < 12; i++) { + Vector3 from, to; + switch (i) { + case 0: { + from = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z); + to = Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z); + } break; + case 1: { + from = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z); + to = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z); + } break; + case 2: { + from = Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z); + to = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z); + + } break; + case 3: { + from = Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z); + to = Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z); + + } break; + case 4: { + from = Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z); + to = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z); + } break; + case 5: { + from = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z); + to = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z); + } break; + case 6: { + from = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z); + to = Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z); + + } break; + case 7: { + from = Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z); + to = Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z); + + } break; + case 8: { + from = Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z); + to = Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z); + + } break; + case 9: { + from = Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z); + to = Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z); + + } break; + case 10: { + from = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z); + to = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z); + + } break; + case 11: { + from = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z); + to = Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z); + + } break; + } + + Vector3 e1 = from - to; + for (int j = 0; j < 3; j++) { + Vector3 e2 = edge_norms[j]; + + Vector3 axis = vec3_cross(e1, e2); + + if (axis.length_squared() < 0.0001f) { + continue; // coplanar + } + //axis.normalize(); + + Vector3 sup2 = Vector3( + (axis.x > 0) ? -half_extents.x : half_extents.x, + (axis.y > 0) ? -half_extents.y : half_extents.y, + (axis.z > 0) ? -half_extents.z : half_extents.z); + + real_t maxB = axis.dot(ofs + sup2); + real_t minB = axis.dot(ofs - sup2); + if (minB > maxB) { + SWAP(maxB, minB); + } + + real_t minT = 1e20, maxT = -1e20; + for (int k = 0; k < 3; k++) { + real_t vert_d = axis.dot(vertex[k]); + + if (vert_d > maxT) { + maxT = vert_d; + } + + if (vert_d < minT) { + minT = vert_d; + } + } + + if (maxB < minT || maxT < minB) { + return false; + } + } + } + return true; +} + +#endif // FACE3_H +#line 0 + +#line 1 "sfwl/core/transform.h" +#ifndef TRANSFORM_H +#define TRANSFORM_H + +/*************************************************************************/ +/* transform.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +struct _NO_DISCARD_CLASS_ Transform { + Basis basis; + Vector3 origin; + + void invert(); + Transform inverse() const; + + void affine_invert(); + Transform affine_inverse() const; + + Transform rotated(const Vector3 &p_axis, real_t p_phi) const; + Transform rotated_local(const Vector3 &p_axis, real_t p_phi) const; + + void rotate(const Vector3 &p_axis, real_t p_phi); + void rotate_local(const Vector3 &p_axis, real_t p_phi); + void rotate_basis(const Vector3 &p_axis, real_t p_phi); + + void set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up); + Transform looking_at(const Vector3 &p_target, const Vector3 &p_up) const; + + void scale(const Vector3 &p_scale); + Transform scaled(const Vector3 &p_scale) const; + Transform scaled_local(const Vector3 &p_scale) const; + void scale_basis(const Vector3 &p_scale); + + void translate_local(real_t p_tx, real_t p_ty, real_t p_tz); + void translate_local(const Vector3 &p_translation); + void translate_localr(real_t p_tx, real_t p_ty, real_t p_tz); + void translate_localv(const Vector3 &p_translation); + Transform translated(const Vector3 &p_translation) const; + Transform translated_local(const Vector3 &p_translation) const; + + const Basis &get_basis() const { return basis; } + void set_basis(const Basis &p_basis) { basis = p_basis; } + + const Vector3 &get_origin() const { return origin; } + void set_origin(const Vector3 &p_origin) { origin = p_origin; } + + void orthonormalize(); + Transform orthonormalized() const; + void orthogonalize(); + Transform orthogonalized() const; + bool is_equal_approx(const Transform &p_transform) const; + + bool operator==(const Transform &p_transform) const; + bool operator!=(const Transform &p_transform) const; + + _FORCE_INLINE_ Vector3 xform(const Vector3 &p_vector) const; + _FORCE_INLINE_ Vector3i xform(const Vector3i &p_vector) const; + _FORCE_INLINE_ AABB xform(const AABB &p_aabb) const; + _FORCE_INLINE_ PoolVector xform(const PoolVector &p_array) const; + _FORCE_INLINE_ PoolVector xform(const PoolVector &p_array) const; + + // NOTE: These are UNSAFE with non-uniform scaling, and will produce incorrect results. + // They use the transpose. + // For safe inverse transforms, xform by the affine_inverse. + _FORCE_INLINE_ Vector3 xform_inv(const Vector3 &p_vector) const; + _FORCE_INLINE_ Vector3i xform_inv(const Vector3i &p_vector) const; + _FORCE_INLINE_ AABB xform_inv(const AABB &p_aabb) const; + _FORCE_INLINE_ PoolVector xform_inv(const PoolVector &p_array) const; + _FORCE_INLINE_ PoolVector xform_inv(const PoolVector &p_array) const; + + // Safe with non-uniform scaling (uses affine_inverse). + _FORCE_INLINE_ Plane xform(const Plane &p_plane) const; + _FORCE_INLINE_ Plane xform_inv(const Plane &p_plane) const; + + // These fast versions use precomputed affine inverse, and should be used in bottleneck areas where + // multiple planes are to be transformed. + _FORCE_INLINE_ Plane xform_fast(const Plane &p_plane, const Basis &p_basis_inverse_transpose) const; + static _FORCE_INLINE_ Plane xform_inv_fast(const Plane &p_plane, const Transform &p_inverse, const Basis &p_basis_transpose); + + void operator*=(const Transform &p_transform); + Transform operator*(const Transform &p_transform) const; + void operator*=(const real_t p_val); + Transform operator*(const real_t p_val) const; + + Transform spherical_interpolate_with(const Transform &p_transform, real_t p_c) const; + Transform interpolate_with(const Transform &p_transform, real_t p_c) const; + + _FORCE_INLINE_ Transform inverse_xform(const Transform &t) const { + Vector3 v = t.origin - origin; + return Transform(basis.transpose_xform(t.basis), + basis.xform(v)); + } + + void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t tx, real_t ty, real_t tz) { + basis.set(xx, xy, xz, yx, yy, yz, zx, zy, zz); + origin.x = tx; + origin.y = ty; + origin.z = tz; + } + + operator String() const; + + Transform(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz); + Transform(const Basis &p_basis, const Vector3 &p_origin = Vector3()); + Transform(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z, const Vector3 &p_origin); + Transform() {} +}; + +_FORCE_INLINE_ Vector3 Transform::xform(const Vector3 &p_vector) const { + return Vector3( + basis[0].dot(p_vector) + origin.x, + basis[1].dot(p_vector) + origin.y, + basis[2].dot(p_vector) + origin.z); +} + +_FORCE_INLINE_ Vector3 Transform::xform_inv(const Vector3 &p_vector) const { + Vector3 v = p_vector - origin; + + return Vector3( + (basis.rows[0][0] * v.x) + (basis.rows[1][0] * v.y) + (basis.rows[2][0] * v.z), + (basis.rows[0][1] * v.x) + (basis.rows[1][1] * v.y) + (basis.rows[2][1] * v.z), + (basis.rows[0][2] * v.x) + (basis.rows[1][2] * v.y) + (basis.rows[2][2] * v.z)); +} + +_FORCE_INLINE_ Vector3i Transform::xform(const Vector3i &p_vector) const { + return Vector3i( + basis[0].dot(p_vector) + origin.x, + basis[1].dot(p_vector) + origin.y, + basis[2].dot(p_vector) + origin.z); +} + +_FORCE_INLINE_ Vector3i Transform::xform_inv(const Vector3i &p_vector) const { + Vector3i v = p_vector; + v.x -= origin.x; + v.y -= origin.y; + v.z -= origin.z; + + return Vector3i( + (basis.rows[0][0] * v.x) + (basis.rows[1][0] * v.y) + (basis.rows[2][0] * v.z), + (basis.rows[0][1] * v.x) + (basis.rows[1][1] * v.y) + (basis.rows[2][1] * v.z), + (basis.rows[0][2] * v.x) + (basis.rows[1][2] * v.y) + (basis.rows[2][2] * v.z)); +} + +// Neither the plane regular xform or xform_inv are particularly efficient, +// as they do a basis inverse. For xforming a large number +// of planes it is better to pre-calculate the inverse transpose basis once +// and reuse it for each plane, by using the 'fast' version of the functions. +_FORCE_INLINE_ Plane Transform::xform(const Plane &p_plane) const { + Basis b = basis.inverse(); + b.transpose(); + return xform_fast(p_plane, b); +} + +_FORCE_INLINE_ Plane Transform::xform_inv(const Plane &p_plane) const { + Transform inv = affine_inverse(); + Basis basis_transpose = basis.transposed(); + return xform_inv_fast(p_plane, inv, basis_transpose); +} + +_FORCE_INLINE_ AABB Transform::xform(const AABB &p_aabb) const { + /* http://dev.theomader.com/transform-bounding-boxes/ */ + Vector3 min = p_aabb.position; + Vector3 max = p_aabb.position + p_aabb.size; + Vector3 tmin, tmax; + for (int i = 0; i < 3; i++) { + tmin[i] = tmax[i] = origin[i]; + for (int j = 0; j < 3; j++) { + real_t e = basis[i][j] * min[j]; + real_t f = basis[i][j] * max[j]; + if (e < f) { + tmin[i] += e; + tmax[i] += f; + } else { + tmin[i] += f; + tmax[i] += e; + } + } + } + AABB r_aabb; + r_aabb.position = tmin; + r_aabb.size = tmax - tmin; + return r_aabb; +} + +_FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const { + /* define vertices */ + Vector3 vertices[8] = { + Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z), + Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z), + Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z), + Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z), + Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z), + Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z), + Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z), + Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z) + }; + + AABB ret; + + ret.position = xform_inv(vertices[0]); + + for (int i = 1; i < 8; i++) { + ret.expand_to(xform_inv(vertices[i])); + } + + return ret; +} + +PoolVector Transform::xform(const PoolVector &p_array) const { + PoolVector array; + array.resize(p_array.size()); + + PoolVector::Read r = p_array.read(); + PoolVector::Write w = array.write(); + + for (int i = 0; i < p_array.size(); ++i) { + w[i] = xform(r[i]); + } + return array; +} + +PoolVector Transform::xform(const PoolVector &p_array) const { + PoolVector array; + array.resize(p_array.size()); + + PoolVector::Read r = p_array.read(); + PoolVector::Write w = array.write(); + + for (int i = 0; i < p_array.size(); ++i) { + w[i] = xform(r[i]); + } + return array; +} + +PoolVector Transform::xform_inv(const PoolVector &p_array) const { + PoolVector array; + array.resize(p_array.size()); + + PoolVector::Read r = p_array.read(); + PoolVector::Write w = array.write(); + + for (int i = 0; i < p_array.size(); ++i) { + w[i] = xform_inv(r[i]); + } + return array; +} + +PoolVector Transform::xform_inv(const PoolVector &p_array) const { + PoolVector array; + array.resize(p_array.size()); + + PoolVector::Read r = p_array.read(); + PoolVector::Write w = array.write(); + + for (int i = 0; i < p_array.size(); ++i) { + w[i] = xform_inv(r[i]); + } + return array; +} + +_FORCE_INLINE_ Plane Transform::xform_fast(const Plane &p_plane, const Basis &p_basis_inverse_transpose) const { + // Transform a single point on the plane. + Vector3 point = p_plane.normal * p_plane.d; + point = xform(point); + + // Use inverse transpose for correct normals with non-uniform scaling. + Vector3 normal = p_basis_inverse_transpose.xform(p_plane.normal); + normal.normalize(); + + real_t d = normal.dot(point); + return Plane(normal, d); +} + +_FORCE_INLINE_ Plane Transform::xform_inv_fast(const Plane &p_plane, const Transform &p_inverse, const Basis &p_basis_transpose) { + // Transform a single point on the plane. + Vector3 point = p_plane.normal * p_plane.d; + point = p_inverse.xform(point); + + // Note that instead of precalculating the transpose, an alternative + // would be to use the transpose for the basis transform. + // However that would be less SIMD friendly (requiring a swizzle). + // So the cost is one extra precalced value in the calling code. + // This is probably worth it, as this could be used in bottleneck areas. And + // where it is not a bottleneck, the non-fast method is fine. + + // Use transpose for correct normals with non-uniform scaling. + Vector3 normal = p_basis_transpose.xform(p_plane.normal); + normal.normalize(); + + real_t d = normal.dot(point); + return Plane(normal, d); +} + +#endif // TRANSFORM_H +#line 0 + + + + +#line 1 "sfwl/core/hashfuncs.h" +#ifndef HASHFUNCS_H +#define HASHFUNCS_H + +/*************************************************************************/ +/* hashfuncs.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +/** + * Hashing functions + */ + +/** + * DJB2 Hash function + * @param C String + * @return 32-bits hashcode + */ +static inline uint32_t hash_djb2(const char *p_cstr) { + const unsigned char *chr = (const unsigned char *)p_cstr; + uint32_t hash = 5381; + uint32_t c; + + while ((c = *chr++)) { + hash = ((hash << 5) + hash) ^ c; /* hash * 33 ^ c */ + } + + return hash; +} + +static inline uint32_t hash_djb2_buffer(const uint8_t *p_buff, int p_len, uint32_t p_prev = 5381) { + uint32_t hash = p_prev; + + for (int i = 0; i < p_len; i++) { + hash = ((hash << 5) + hash) ^ p_buff[i]; /* hash * 33 ^ c */ + } + + return hash; +} + +static inline uint32_t hash_djb2_one_32(uint32_t p_in, uint32_t p_prev = 5381) { + return ((p_prev << 5) + p_prev) ^ p_in; +} + +/** + * Thomas Wang's 64-bit to 32-bit Hash function: + * https://web.archive.org/web/20071223173210/https:/www.concentric.net/~Ttwang/tech/inthash.htm + * + * @param p_int - 64-bit unsigned integer key to be hashed + * @return unsigned 32-bit value representing hashcode + */ +static inline uint32_t hash_one_uint64(const uint64_t p_int) { + uint64_t v = p_int; + v = (~v) + (v << 18); // v = (v << 18) - v - 1; + v = v ^ (v >> 31); + v = v * 21; // v = (v + (v << 2)) + (v << 4); + v = v ^ (v >> 11); + v = v + (v << 6); + v = v ^ (v >> 22); + return (uint32_t)v; +} + +#define HASH_MURMUR3_SEED 0x7F07C65 +// Murmurhash3 32-bit version. +// All MurmurHash versions are public domain software, and the author disclaims all copyright to their code. + +static _FORCE_INLINE_ uint32_t hash_murmur3_one_32(uint32_t p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { + p_in *= 0xcc9e2d51; + p_in = (p_in << 15) | (p_in >> 17); + p_in *= 0x1b873593; + + p_seed ^= p_in; + p_seed = (p_seed << 13) | (p_seed >> 19); + p_seed = p_seed * 5 + 0xe6546b64; + + return p_seed; +} + +static _FORCE_INLINE_ uint32_t hash_murmur3_one_float(float p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { + union { + float f; + uint32_t i; + } u; + + // Normalize +/- 0.0 and NaN values so they hash the same. + if (p_in == 0.0f) { + u.f = 0.0; + } else if (Math::is_nan(p_in)) { + u.f = NAN; + } else { + u.f = p_in; + } + + return hash_murmur3_one_32(u.i, p_seed); +} + +static _FORCE_INLINE_ uint32_t hash_murmur3_one_64(uint64_t p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { + p_seed = hash_murmur3_one_32(p_in & 0xFFFFFFFF, p_seed); + return hash_murmur3_one_32(p_in >> 32, p_seed); +} + +static _FORCE_INLINE_ uint32_t hash_murmur3_one_double(double p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { + union { + double d; + uint64_t i; + } u; + + // Normalize +/- 0.0 and NaN values so they hash the same. + if (p_in == 0.0f) { + u.d = 0.0; + } else if (Math::is_nan(p_in)) { + u.d = NAN; + } else { + u.d = p_in; + } + + return hash_murmur3_one_64(u.i, p_seed); +} + +static _FORCE_INLINE_ uint32_t hash_murmur3_one_real(real_t p_in, uint32_t p_seed = HASH_MURMUR3_SEED) { +#ifdef REAL_T_IS_DOUBLE + return hash_murmur3_one_double(p_in, p_seed); +#else + return hash_murmur3_one_float(p_in, p_seed); +#endif +} + +static _FORCE_INLINE_ uint32_t hash_rotl32(uint32_t x, int8_t r) { + return (x << r) | (x >> (32 - r)); +} + +static _FORCE_INLINE_ uint32_t hash_fmix32(uint32_t h) { + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +static _FORCE_INLINE_ uint32_t hash_murmur3_buffer(const void *key, int length, const uint32_t seed = HASH_MURMUR3_SEED) { + // Although not required, this is a random prime number. + const uint8_t *data = (const uint8_t *)key; + const int nblocks = length / 4; + + uint32_t h1 = seed; + + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + + const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4); + + for (int i = -nblocks; i; i++) { + uint32_t k1 = blocks[i]; + + k1 *= c1; + k1 = hash_rotl32(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = hash_rotl32(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + } + + const uint8_t *tail = (const uint8_t *)(data + nblocks * 4); + + uint32_t k1 = 0; + + switch (length & 3) { + case 3: + k1 ^= tail[2] << 16; + FALLTHROUGH; + case 2: + k1 ^= tail[1] << 8; + FALLTHROUGH; + case 1: + k1 ^= tail[0]; + k1 *= c1; + k1 = hash_rotl32(k1, 15); + k1 *= c2; + h1 ^= k1; + }; + + // Finalize with additional bit mixing. + h1 ^= length; + return hash_fmix32(h1); +} + +static inline uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev = 5381) { + union { + double d; + uint64_t i; + } u; + + // Normalize +/- 0.0 and NaN values so they hash the same. + if (p_in == 0.0f) { + u.d = 0.0; + } else if (Math::is_nan(p_in)) { + u.d = Math_NAN; + } else { + u.d = p_in; + } + + return ((p_prev << 5) + p_prev) + hash_one_uint64(u.i); +} + +template +static inline uint32_t make_uint32_t(T p_in) { + union { + T t; + uint32_t _u32; + } _u; + _u._u32 = 0; + _u.t = p_in; + return _u._u32; +} + +static _FORCE_INLINE_ uint64_t hash_djb2_one_float_64(double p_in, uint64_t p_prev = 5381) { + union { + double d; + uint64_t i; + } u; + + // Normalize +/- 0.0 and NaN values so they hash the same. + if (p_in == 0.0f) { + u.d = 0.0; + } else if (Math::is_nan(p_in)) { + u.d = NAN; + } else { + u.d = p_in; + } + + return ((p_prev << 5) + p_prev) + u.i; +} + +static _FORCE_INLINE_ uint64_t hash_djb2_one_64(uint64_t p_in, uint64_t p_prev = 5381) { + return ((p_prev << 5) + p_prev) ^ p_in; +} + +template +static _FORCE_INLINE_ uint64_t hash_make_uint64_t(T p_in) { + union { + T t; + uint64_t _u64; + } _u; + _u._u64 = 0; // in case p_in is smaller + + _u.t = p_in; + return _u._u64; +} + +template +static inline uint64_t make_uint64_t(T p_in) { + union { + T t; + uint64_t _u64; + } _u; + _u._u64 = 0; // in case p_in is smaller + + _u.t = p_in; + return _u._u64; +} + +template +class Ref; + +struct HashMapHasherDefault { + // Generic hash function for any type. + template + static _FORCE_INLINE_ uint32_t hash(const T *p_pointer) { return hash_one_uint64((uint64_t)p_pointer); } + + template + static _FORCE_INLINE_ uint32_t hash(const Ref &p_ref) { return hash_one_uint64((uint64_t)p_ref.operator->()); } + + static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); } + static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); } + static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return hash_fmix32(p_wchar); } + static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return hash_fmix32(p_uchar); } + static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(p_uchar); } + static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); } + + static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); } + static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash_one_uint64(p_int); } + static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_murmur3_one_float(p_float); } + static _FORCE_INLINE_ uint32_t hash(const double p_double) { return hash_murmur3_one_double(p_double); } + static _FORCE_INLINE_ uint32_t hash(const uint32_t p_int) { return hash_fmix32(p_int); } + static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return hash_fmix32(p_int); } + static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return hash_fmix32(p_int); } + static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return hash_fmix32(p_int); } + static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return hash_fmix32(p_int); } + static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return hash_fmix32(p_int); } + + static _FORCE_INLINE_ uint32_t hash(const Vector2i &p_vec) { + uint32_t h = hash_murmur3_one_32(p_vec.x); + h = hash_murmur3_one_32(p_vec.y, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const Vector3i &p_vec) { + uint32_t h = hash_murmur3_one_32(p_vec.x); + h = hash_murmur3_one_32(p_vec.y, h); + h = hash_murmur3_one_32(p_vec.z, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const Vector4i &p_vec) { + uint32_t h = hash_murmur3_one_32(p_vec.x); + h = hash_murmur3_one_32(p_vec.y, h); + h = hash_murmur3_one_32(p_vec.z, h); + h = hash_murmur3_one_32(p_vec.w, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const Vector2 &p_vec) { + uint32_t h = hash_murmur3_one_real(p_vec.x); + h = hash_murmur3_one_real(p_vec.y, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const Vector3 &p_vec) { + uint32_t h = hash_murmur3_one_real(p_vec.x); + h = hash_murmur3_one_real(p_vec.y, h); + h = hash_murmur3_one_real(p_vec.z, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const Vector4 &p_vec) { + uint32_t h = hash_murmur3_one_real(p_vec.x); + h = hash_murmur3_one_real(p_vec.y, h); + h = hash_murmur3_one_real(p_vec.z, h); + h = hash_murmur3_one_real(p_vec.w, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const Rect2i &p_rect) { + uint32_t h = hash_murmur3_one_32(p_rect.position.x); + h = hash_murmur3_one_32(p_rect.position.y, h); + h = hash_murmur3_one_32(p_rect.size.x, h); + h = hash_murmur3_one_32(p_rect.size.y, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const Rect2 &p_rect) { + uint32_t h = hash_murmur3_one_real(p_rect.position.x); + h = hash_murmur3_one_real(p_rect.position.y, h); + h = hash_murmur3_one_real(p_rect.size.x, h); + h = hash_murmur3_one_real(p_rect.size.y, h); + return hash_fmix32(h); + } + static _FORCE_INLINE_ uint32_t hash(const AABB &p_aabb) { + uint32_t h = hash_murmur3_one_real(p_aabb.position.x); + h = hash_murmur3_one_real(p_aabb.position.y, h); + h = hash_murmur3_one_real(p_aabb.position.z, h); + h = hash_murmur3_one_real(p_aabb.size.x, h); + h = hash_murmur3_one_real(p_aabb.size.y, h); + h = hash_murmur3_one_real(p_aabb.size.z, h); + return hash_fmix32(h); + } +}; + +template +struct HashMapComparatorDefault { + static bool compare(const T &p_lhs, const T &p_rhs) { + return p_lhs == p_rhs; + } +}; + +template <> +struct HashMapComparatorDefault { + static bool compare(const float &p_lhs, const float &p_rhs) { + return (p_lhs == p_rhs) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs)); + } +}; + +template <> +struct HashMapComparatorDefault { + static bool compare(const double &p_lhs, const double &p_rhs) { + return (p_lhs == p_rhs) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs)); + } +}; + +template <> +struct HashMapComparatorDefault { + static bool compare(const Vector2 &p_lhs, const Vector2 &p_rhs) { + return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))); + } +}; + +template <> +struct HashMapComparatorDefault { + static bool compare(const Vector3 &p_lhs, const Vector3 &p_rhs) { + return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))); + } +}; + +constexpr uint32_t HASH_TABLE_SIZE_MAX = 29; + +const uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = { + 5, + 13, + 23, + 47, + 97, + 193, + 389, + 769, + 1543, + 3079, + 6151, + 12289, + 24593, + 49157, + 98317, + 196613, + 393241, + 786433, + 1572869, + 3145739, + 6291469, + 12582917, + 25165843, + 50331653, + 100663319, + 201326611, + 402653189, + 805306457, + 1610612741, +}; + +// Computed with elem_i = UINT64_C (0 x FFFFFFFF FFFFFFFF ) / d_i + 1, where d_i is the i-th element of the above array. +const uint64_t hash_table_size_primes_inv[HASH_TABLE_SIZE_MAX] = { + 3689348814741910324, + 1418980313362273202, + 802032351030850071, + 392483916461905354, + 190172619316593316, + 95578984837873325, + 47420935922132524, + 23987963684927896, + 11955116055547344, + 5991147799191151, + 2998982941588287, + 1501077717772769, + 750081082979285, + 375261795343686, + 187625172388393, + 93822606204624, + 46909513691883, + 23456218233098, + 11728086747027, + 5864041509391, + 2932024948977, + 1466014921160, + 733007198436, + 366503839517, + 183251896093, + 91625960335, + 45812983922, + 22906489714, + 11453246088 +}; + +/** + * Fastmod computes ( n mod d ) given the precomputed c much faster than n % d. + * The implementation of fastmod is based on the following paper by Daniel Lemire et al. + * Faster Remainder by Direct Computation: Applications to Compilers and Software Libraries + * https://arxiv.org/abs/1902.01961 + */ +static _FORCE_INLINE_ uint32_t fastmod(const uint32_t n, const uint64_t c, const uint32_t d) { +#if defined(_MSC_VER) + // Returns the upper 64 bits of the product of two 64-bit unsigned integers. + // This intrinsic function is required since MSVC does not support unsigned 128-bit integers. +#if defined(_M_X64) || defined(_M_ARM64) + return __umulh(c * n, d); +#else + // Fallback to the slower method for 32-bit platforms. + return n % d; +#endif // _M_X64 || _M_ARM64 +#else +#ifdef __SIZEOF_INT128__ + // Prevent compiler warning, because we know what we are doing. + uint64_t lowbits = c * n; + __extension__ typedef unsigned __int128 uint128; + return static_cast(((uint128)lowbits * d) >> 64); +#else + // Fallback to the slower method if no 128-bit unsigned integer type is available. + return n % d; +#endif // __SIZEOF_INT128__ +#endif // _MSC_VER +} + +#endif // HASHFUNCS_H +#line 0 + + + + +#line 1 "sfwl/core/pair.h" +#ifndef PAIR_H +#define PAIR_H + +/*************************************************************************/ +/* pair.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +template +struct Pair { + F first; + S second; + + Pair() : + first(), + second() { + } + + Pair(F p_first, const S &p_second) : + first(p_first), + second(p_second) { + } +}; + +template +bool operator==(const Pair &pair, const Pair &other) { + return (pair.first == other.first) && (pair.second == other.second); +} + +template +bool operator!=(const Pair &pair, const Pair &other) { + return (pair.first != other.first) || (pair.second != other.second); +} + +template +struct PairSort { + bool operator()(const Pair &A, const Pair &B) const { + if (A.first != B.first) { + return A.first < B.first; + } + return A.second < B.second; + } +}; + +template +struct PairHash { + static uint32_t hash(const Pair &P) { + uint64_t h1 = HashMapHasherDefault::hash(P.first); + uint64_t h2 = HashMapHasherDefault::hash(P.second); + return hash_one_uint64((h1 << 32) | h2); + } +}; + +template +struct KeyValue { + const K key; + V value; + + void operator=(const KeyValue &p_kv) = delete; + _FORCE_INLINE_ KeyValue(const KeyValue &p_kv) : + key(p_kv.key), + value(p_kv.value) { + } + _FORCE_INLINE_ KeyValue(const K &p_key, const V &p_value) : + key(p_key), + value(p_value) { + } +}; + +template +bool operator==(const KeyValue &pair, const KeyValue &other) { + return (pair.key == other.key) && (pair.value == other.value); +} + +template +bool operator!=(const KeyValue &pair, const KeyValue &other) { + return (pair.key != other.key) || (pair.value != other.value); +} + +template +struct KeyValueSort { + bool operator()(const KeyValue &A, const KeyValue &B) const { + return A.key < B.key; + } +}; + +#endif // PAIR_H +#line 0 + +#line 1 "sfwl/core/og_hash_map.h" +#ifndef GHASH_MAP_H +#define GHASH_MAP_H + +/*************************************************************************/ +/* og_hash_map.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +/** + * @class OGHashMap + * @author Juan Linietsky + * + * Implementation of a standard Hashing HashMap, for quick lookups of Data associated with a Key. + * The implementation provides hashers for the default types, if you need a special kind of hasher, provide + * your own. + * @param TKey Key, search is based on it, needs to be hasheable. It is unique in this container. + * @param TData Data, data associated with the key + * @param Hasher Hasher object, needs to provide a valid static hash function for TKey + * @param Comparator comparator object, needs to be able to safely compare two TKey values. It needs to ensure that x == x for any items inserted in the map. Bear in mind that nan != nan when implementing an equality check. + * @param MIN_HASH_TABLE_POWER Miminum size of the hash table, as a power of two. You rarely need to change this parameter. + * @param RELATIONSHIP Relationship at which the hash table is resized. if amount of elements is RELATIONSHIP + * times bigger than the hash table, table is resized to solve this condition. if RELATIONSHIP is zero, table is always MIN_HASH_TABLE_POWER. + * + */ + +template , uint8_t MIN_HASH_TABLE_POWER = 3, uint8_t RELATIONSHIP = 8> +class OGHashMap { +public: + struct Pair { + TKey key; + TData data; + + Pair(const TKey &p_key) : + key(p_key), + data() {} + Pair(const TKey &p_key, const TData &p_data) : + key(p_key), + data(p_data) { + } + }; + + struct Element { + private: + friend class OGHashMap; + + uint32_t hash; + Element *next; + Element() { next = nullptr; } + Pair pair; + + public: + const TKey &key() const { + return pair.key; + } + + TData &value() { + return pair.data; + } + + const TData &value() const { + return pair.data; + } + + TData &get() { + return pair.data; + }; + const TData &get() const { + return pair.data; + }; + + Element(const TKey &p_key) : + pair(p_key) {} + Element(const Element &p_other) : + hash(p_other.hash), + pair(p_other.pair.key, p_other.pair.data) {} + }; + +private: + Element **hash_table; + uint8_t hash_table_power; + uint32_t elements; + + void make_hash_table() { + ERR_FAIL_COND(hash_table); + + hash_table = memnew_arr(Element *, (1 << MIN_HASH_TABLE_POWER)); + + hash_table_power = MIN_HASH_TABLE_POWER; + elements = 0; + for (int i = 0; i < (1 << MIN_HASH_TABLE_POWER); i++) { + hash_table[i] = nullptr; + } + } + + void erase_hash_table() { + ERR_FAIL_COND_MSG(elements, "Cannot erase hash table if there are still elements inside."); + + memdelete_arr(hash_table); + hash_table = nullptr; + hash_table_power = 0; + elements = 0; + } + + void check_hash_table() { + int new_hash_table_power = -1; + + if ((int)elements > ((1 << hash_table_power) * RELATIONSHIP)) { + /* rehash up */ + new_hash_table_power = hash_table_power + 1; + + while ((int)elements > ((1 << new_hash_table_power) * RELATIONSHIP)) { + new_hash_table_power++; + } + + } else if ((hash_table_power > (int)MIN_HASH_TABLE_POWER) && ((int)elements < ((1 << (hash_table_power - 1)) * RELATIONSHIP))) { + /* rehash down */ + new_hash_table_power = hash_table_power - 1; + + while ((int)elements < ((1 << (new_hash_table_power - 1)) * RELATIONSHIP)) { + new_hash_table_power--; + } + + if (new_hash_table_power < (int)MIN_HASH_TABLE_POWER) { + new_hash_table_power = MIN_HASH_TABLE_POWER; + } + } + + if (new_hash_table_power == -1) { + return; + } + + Element **new_hash_table = memnew_arr(Element *, ((uint64_t)1 << new_hash_table_power)); + ERR_FAIL_COND_MSG(!new_hash_table, "Out of memory."); + + for (int i = 0; i < (1 << new_hash_table_power); i++) { + new_hash_table[i] = nullptr; + } + + if (hash_table) { + for (int i = 0; i < (1 << hash_table_power); i++) { + while (hash_table[i]) { + Element *se = hash_table[i]; + hash_table[i] = se->next; + int new_pos = se->hash & ((1 << new_hash_table_power) - 1); + se->next = new_hash_table[new_pos]; + new_hash_table[new_pos] = se; + } + } + + memdelete_arr(hash_table); + } + hash_table = new_hash_table; + hash_table_power = new_hash_table_power; + } + + /* I want to have only one function.. */ + _FORCE_INLINE_ const Element *get_element(const TKey &p_key) const { + uint32_t hash = Hasher::hash(p_key); + uint32_t index = hash & ((1 << hash_table_power) - 1); + + Element *e = hash_table[index]; + + while (e) { + /* checking hash first avoids comparing key, which may take longer */ + if (e->hash == hash && Comparator::compare(e->pair.key, p_key)) { + /* the pair exists in this hashtable, so just update data */ + return e; + } + + e = e->next; + } + + return nullptr; + } + + Element *create_element(const TKey &p_key) { + /* if element doesn't exist, create it */ + Element *e = memnew(Element(p_key)); + ERR_FAIL_COND_V_MSG(!e, nullptr, "Out of memory."); + uint32_t hash = Hasher::hash(p_key); + uint32_t index = hash & ((1 << hash_table_power) - 1); + e->next = hash_table[index]; + e->hash = hash; + + hash_table[index] = e; + elements++; + + return e; + } + + void copy_from(const OGHashMap &p_t) { + if (&p_t == this) { + return; /* much less bother with that */ + } + + clear(); + + if (!p_t.hash_table || p_t.hash_table_power == 0) { + return; /* not copying from empty table */ + } + + hash_table = memnew_arr(Element *, (uint64_t)1 << p_t.hash_table_power); + hash_table_power = p_t.hash_table_power; + elements = p_t.elements; + + for (int i = 0; i < (1 << p_t.hash_table_power); i++) { + hash_table[i] = nullptr; + + const Element *e = p_t.hash_table[i]; + + while (e) { + Element *le = memnew(Element(*e)); /* local element */ + + /* add to list and reassign pointers */ + le->next = hash_table[i]; + hash_table[i] = le; + + e = e->next; + } + } + } + +public: + Element *set(const TKey &p_key, const TData &p_data) { + return set(Pair(p_key, p_data)); + } + + Element *set(const Pair &p_pair) { + Element *e = nullptr; + if (!hash_table) { + make_hash_table(); // if no table, make one + } else { + e = const_cast(get_element(p_pair.key)); + } + + /* if we made it up to here, the pair doesn't exist, create and assign */ + + if (!e) { + e = create_element(p_pair.key); + if (!e) { + return nullptr; + } + check_hash_table(); // perform mantenience routine + } + + e->pair.data = p_pair.data; + return e; + } + + bool has(const TKey &p_key) const { + return getptr(p_key) != nullptr; + } + + /** + * Get a key from data, return a const reference. + * WARNING: this doesn't check errors, use either getptr and check NULL, or check + * first with has(key) + */ + + const TData &get(const TKey &p_key) const { + const TData *res = getptr(p_key); + CRASH_COND_MSG(!res, "Map key not found."); + return *res; + } + + TData &get(const TKey &p_key) { + TData *res = getptr(p_key); + CRASH_COND_MSG(!res, "Map key not found."); + return *res; + } + + /** + * Same as get, except it can return NULL when item was not found. + * This is mainly used for speed purposes. + */ + + _FORCE_INLINE_ TData *getptr(const TKey &p_key) { + if (unlikely(!hash_table)) { + return nullptr; + } + + Element *e = const_cast(get_element(p_key)); + + if (e) { + return &e->pair.data; + } + + return nullptr; + } + + _FORCE_INLINE_ const TData *getptr(const TKey &p_key) const { + if (unlikely(!hash_table)) { + return nullptr; + } + + const Element *e = const_cast(get_element(p_key)); + + if (e) { + return &e->pair.data; + } + + return nullptr; + } + + const Element *find(const TKey &p_key) const { + if (unlikely(!hash_table)) { + return nullptr; + } + + const Element *e = const_cast(get_element(p_key)); + + return e; + } + + Element *find(const TKey &p_key) { + if (unlikely(!hash_table)) { + return nullptr; + } + + Element *e = const_cast(get_element(p_key)); + + return e; + } + + /** + * Same as get, except it can return NULL when item was not found. + * This version is custom, will take a hash and a custom key (that should support operator==() + */ + + template + _FORCE_INLINE_ TData *custom_getptr(C p_custom_key, uint32_t p_custom_hash) { + if (unlikely(!hash_table)) { + return nullptr; + } + + uint32_t hash = p_custom_hash; + uint32_t index = hash & ((1 << hash_table_power) - 1); + + Element *e = hash_table[index]; + + while (e) { + /* checking hash first avoids comparing key, which may take longer */ + if (e->hash == hash && Comparator::compare(e->pair.key, p_custom_key)) { + /* the pair exists in this hashtable, so just update data */ + return &e->pair.data; + } + + e = e->next; + } + + return nullptr; + } + + template + _FORCE_INLINE_ const TData *custom_getptr(C p_custom_key, uint32_t p_custom_hash) const { + if (unlikely(!hash_table)) { + return NULL; + } + + uint32_t hash = p_custom_hash; + uint32_t index = hash & ((1 << hash_table_power) - 1); + + const Element *e = hash_table[index]; + + while (e) { + /* checking hash first avoids comparing key, which may take longer */ + if (e->hash == hash && Comparator::compare(e->pair.key, p_custom_key)) { + /* the pair exists in this hashtable, so just update data */ + return &e->pair.data; + } + + e = e->next; + } + + return NULL; + } + + /** + * Erase an item, return true if erasing was successful + */ + + bool erase(const TKey &p_key) { + if (unlikely(!hash_table)) { + return false; + } + + uint32_t hash = Hasher::hash(p_key); + uint32_t index = hash & ((1 << hash_table_power) - 1); + + Element *e = hash_table[index]; + Element *p = nullptr; + while (e) { + /* checking hash first avoids comparing key, which may take longer */ + if (e->hash == hash && Comparator::compare(e->pair.key, p_key)) { + if (p) { + p->next = e->next; + } else { + //begin of list + hash_table[index] = e->next; + } + + memdelete(e); + elements--; + + if (elements == 0) { + erase_hash_table(); + } else { + check_hash_table(); + } + return true; + } + + p = e; + e = e->next; + } + + return false; + } + + inline const TData &operator[](const TKey &p_key) const { //constref + + return get(p_key); + } + inline TData &operator[](const TKey &p_key) { //assignment + + Element *e = nullptr; + if (!hash_table) { + make_hash_table(); // if no table, make one + } else { + e = const_cast(get_element(p_key)); + } + + /* if we made it up to here, the pair doesn't exist, create */ + if (!e) { + e = create_element(p_key); + CRASH_COND(!e); + check_hash_table(); // perform mantenience routine + } + + return e->pair.data; + } + + /** + * Get the next key to p_key, and the first key if p_key is null. + * Returns a pointer to the next key if found, NULL otherwise. + * Adding/Removing elements while iterating will, of course, have unexpected results, don't do it. + * + * Example: + * + * const TKey *k=NULL; + * + * while( (k=table.next(k)) ) { + * + * print( *k ); + * } + * + */ + const TKey *next(const TKey *p_key) const { + if (unlikely(!hash_table)) { + return nullptr; + } + + if (!p_key) { /* get the first key */ + + for (int i = 0; i < (1 << hash_table_power); i++) { + if (hash_table[i]) { + return &hash_table[i]->pair.key; + } + } + + } else { /* get the next key */ + + const Element *e = get_element(*p_key); + ERR_FAIL_COND_V_MSG(!e, nullptr, "Invalid key supplied."); + if (e->next) { + /* if there is a "next" in the list, return that */ + return &e->next->pair.key; + } else { + /* go to next elements */ + uint32_t index = e->hash & ((1 << hash_table_power) - 1); + index++; + for (int i = index; i < (1 << hash_table_power); i++) { + if (hash_table[i]) { + return &hash_table[i]->pair.key; + } + } + } + + /* nothing found, was at end */ + } + + return nullptr; /* nothing found */ + } + + inline unsigned int size() const { + return elements; + } + + inline bool empty() const { + return elements == 0; + } + + void clear() { + /* clean up */ + if (hash_table) { + for (int i = 0; i < (1 << hash_table_power); i++) { + while (hash_table[i]) { + Element *e = hash_table[i]; + hash_table[i] = e->next; + memdelete(e); + } + } + + memdelete_arr(hash_table); + } + + hash_table = nullptr; + hash_table_power = 0; + elements = 0; + } + + void operator=(const OGHashMap &p_table) { + copy_from(p_table); + } + + OGHashMap() { + hash_table = nullptr; + elements = 0; + hash_table_power = 0; + } + + void get_key_value_ptr_array(const Pair **p_pairs) const { + if (unlikely(!hash_table)) { + return; + } + for (int i = 0; i < (1 << hash_table_power); i++) { + Element *e = hash_table[i]; + while (e) { + *p_pairs = &e->pair; + p_pairs++; + e = e->next; + } + } + } + + void get_key_list(List *p_keys) const { + if (unlikely(!hash_table)) { + return; + } + for (int i = 0; i < (1 << hash_table_power); i++) { + Element *e = hash_table[i]; + while (e) { + p_keys->push_back(e->pair.key); + e = e->next; + } + } + } + + OGHashMap(const OGHashMap &p_table) { + hash_table = nullptr; + elements = 0; + hash_table_power = 0; + + copy_from(p_table); + } + + ~OGHashMap() { + clear(); + } +}; + +#endif +#line 0 + +#line 1 "sfwl/core/ordered_hash_map.h" +#ifndef ORDERED_HASH_MAP_H +#define ORDERED_HASH_MAP_H + +/*************************************************************************/ +/* ordered_hash_map.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +/** + * A hash map which allows to iterate elements in insertion order. + * Insertion, lookup, deletion have O(1) complexity. + * The API aims to be consistent with Map rather than HashMap, because the + * former is more frequently used and is more coherent with the rest of the + * codebase. + * Deletion during iteration is safe and will preserve the order. + */ +template , uint8_t MIN_HASH_TABLE_POWER = 3, uint8_t RELATIONSHIP = 8> +class OrderedHashMap { + typedef List> InternalList; + typedef OGHashMap InternalMap; + + InternalList list; + InternalMap map; + +public: + class Element { + friend class OrderedHashMap; + + typename InternalList::Element *list_element; + typename InternalList::Element *prev_element; + typename InternalList::Element *next_element; + + Element(typename InternalList::Element *p_element) { + list_element = p_element; + + if (list_element) { + next_element = list_element->next(); + prev_element = list_element->prev(); + } + } + + public: + _FORCE_INLINE_ Element() : + list_element(nullptr), + prev_element(nullptr), + next_element(nullptr) { + } + + Element next() const { + return Element(next_element); + } + + Element prev() const { + return Element(prev_element); + } + + Element(const Element &other) : + list_element(other.list_element), + prev_element(other.prev_element), + next_element(other.next_element) { + } + + Element &operator=(const Element &other) { + list_element = other.list_element; + next_element = other.next_element; + prev_element = other.prev_element; + return *this; + } + + _FORCE_INLINE_ bool operator==(const Element &p_other) const { + return this->list_element == p_other.list_element; + } + _FORCE_INLINE_ bool operator!=(const Element &p_other) const { + return this->list_element != p_other.list_element; + } + + operator bool() const { + return (list_element != nullptr); + } + + const K &key() const { + CRASH_COND(!list_element); + return *(list_element->get().first); + }; + + V &value() { + CRASH_COND(!list_element); + return list_element->get().second; + }; + + const V &value() const { + CRASH_COND(!list_element); + return list_element->get().second; + }; + + V &get() { + CRASH_COND(!list_element); + return list_element->get().second; + }; + + const V &get() const { + CRASH_COND(!list_element); + return list_element->get().second; + }; + }; + + class ConstElement { + friend class OrderedHashMap; + + const typename InternalList::Element *list_element; + + ConstElement(const typename InternalList::Element *p_element) : + list_element(p_element) { + } + + public: + _FORCE_INLINE_ ConstElement() : + list_element(NULL) { + } + + ConstElement(const ConstElement &other) : + list_element(other.list_element) { + } + + ConstElement &operator=(const ConstElement &other) { + list_element = other.list_element; + return *this; + } + + ConstElement next() const { + return ConstElement(list_element ? list_element->next() : nullptr); + } + + ConstElement prev() const { + return ConstElement(list_element ? list_element->prev() : NULL); + } + + _FORCE_INLINE_ bool operator==(const ConstElement &p_other) const { + return this->list_element == p_other.list_element; + } + _FORCE_INLINE_ bool operator!=(const ConstElement &p_other) const { + return this->list_element != p_other.list_element; + } + + operator bool() const { + return (list_element != nullptr); + } + + const K &key() const { + CRASH_COND(!list_element); + return *(list_element->get().first); + }; + + const V &value() const { + CRASH_COND(!list_element); + return list_element->get().second; + }; + + const V &get() const { + CRASH_COND(!list_element); + return list_element->get().second; + }; + }; + + ConstElement find(const K &p_key) const { + typename InternalList::Element *const *list_element = map.getptr(p_key); + if (list_element) { + return ConstElement(*list_element); + } + return ConstElement(nullptr); + } + + Element find(const K &p_key) { + typename InternalList::Element **list_element = map.getptr(p_key); + if (list_element) { + return Element(*list_element); + } + return Element(nullptr); + } + + Element insert(const K &p_key, const V &p_value) { + typename InternalList::Element **list_element = map.getptr(p_key); + if (list_element) { + (*list_element)->get().second = p_value; + return Element(*list_element); + } + // Incorrectly set the first value of the pair with a value that will + // be invalid as soon as we leave this function... + typename InternalList::Element *new_element = list.push_back(Pair(&p_key, p_value)); + // ...this is needed here in case the hashmap recursively reference itself... + typename InternalMap::Element *e = map.set(p_key, new_element); + // ...now we can set the right value ! + new_element->get().first = &e->key(); + + return Element(new_element); + } + + void erase(Element &p_element) { + map.erase(p_element.key()); + list.erase(p_element.list_element); + p_element.list_element = nullptr; + } + + bool erase(const K &p_key) { + typename InternalList::Element **list_element = map.getptr(p_key); + if (list_element) { + list.erase(*list_element); + map.erase(p_key); + return true; + } + return false; + } + + inline bool has(const K &p_key) const { + return map.has(p_key); + } + + const V &operator[](const K &p_key) const { + ConstElement e = find(p_key); + CRASH_COND(!e); + return e.value(); + } + + V &operator[](const K &p_key) { + Element e = find(p_key); + if (!e) { + // consistent with Map behaviour + e = insert(p_key, V()); + } + return e.value(); + } + + inline Element front() { + return Element(list.front()); + } + + inline Element back() { + return Element(list.back()); + } + + inline ConstElement front() const { + return ConstElement(list.front()); + } + + inline ConstElement back() const { + return ConstElement(list.back()); + } + + inline bool empty() const { return list.empty(); } + inline int size() const { return list.size(); } + + const void *id() const { + return list.id(); + } + + void clear() { + map.clear(); + list.clear(); + } + +private: + void _copy_from(const OrderedHashMap &p_map) { + for (ConstElement E = p_map.front(); E; E = E.next()) { + insert(E.key(), E.value()); + } + } + +public: + void operator=(const OrderedHashMap &p_map) { + _copy_from(p_map); + } + + OrderedHashMap(const OrderedHashMap &p_map) { + _copy_from(p_map); + } + + _FORCE_INLINE_ OrderedHashMap() { + } +}; + +#endif // ORDERED_HASH_MAP_H +#line 0 + +#line 1 "sfwl/core/hash_map.h" +#ifndef HASH_MAP_H +#define HASH_MAP_H + +/*************************************************************************/ +/* hash_map.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +/** + * A HashMap implementation that uses open addressing with Robin Hood hashing. + * Robin Hood hashing swaps out entries that have a smaller probing distance + * than the to-be-inserted entry, that evens out the average probing distance + * and enables faster lookups. Backward shift deletion is employed to further + * improve the performance and to avoid infinite loops in rare cases. + * + * Keys and values are stored in a double linked list by insertion order. This + * has a slight performance overhead on lookup, which can be mostly compensated + * using a paged allocator if required. + * + * The assignment operator copy the pairs from one map to the other. + */ + +template > +class HashMap { +public: + const uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime. + const float MAX_OCCUPANCY = 0.75; + const uint32_t EMPTY_HASH = 0; + +public: + struct Element { + Element *next = nullptr; + Element *prev = nullptr; + KeyValue data; + + const TKey &key() const { + return data.key; + } + + TValue &value() { + return data.value; + } + + const TValue &value() const { + return data.value; + } + + TValue &get() { + return data.value; + }; + const TValue &get() const { + return data.value; + }; + + Element() {} + Element(const TKey &p_key, const TValue &p_value) : + data(p_key, p_value) {} + }; + +public: + _FORCE_INLINE_ uint32_t get_capacity() const { return hash_table_size_primes[capacity_index]; } + _FORCE_INLINE_ uint32_t size() const { return num_elements; } + + /* Standard Godot Container API */ + + bool empty() const { + return num_elements == 0; + } + + void clear() { + if (elements == nullptr || num_elements == 0) { + return; + } + uint32_t capacity = hash_table_size_primes[capacity_index]; + for (uint32_t i = 0; i < capacity; i++) { + if (hashes[i] == EMPTY_HASH) { + continue; + } + + hashes[i] = EMPTY_HASH; + memdelete(elements[i]); + elements[i] = nullptr; + } + + tail_element = nullptr; + head_element = nullptr; + num_elements = 0; + } + + TValue &get(const TKey &p_key) { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + CRASH_COND_MSG(!exists, "HashMap key not found."); + return elements[pos]->data.value; + } + + const TValue &get(const TKey &p_key) const { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + CRASH_COND_MSG(!exists, "HashMap key not found."); + return elements[pos]->data.value; + } + + const TValue *getptr(const TKey &p_key) const { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + + if (exists) { + return &elements[pos]->data.value; + } + return nullptr; + } + + TValue *getptr(const TKey &p_key) { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + + if (exists) { + return &elements[pos]->data.value; + } + return nullptr; + } + + const Element *get_element(const TKey &p_key) const { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + + if (exists) { + return elements[pos]; + } + + return NULL; + } + + Element *get_element(const TKey &p_key) { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + + if (exists) { + return elements[pos]; + } + + return NULL; + } + + _FORCE_INLINE_ const Element *find(const TKey &p_key) const { + return get_element(p_key); + } + + _FORCE_INLINE_ Element *find(const TKey &p_key) { + return get_element(p_key); + } + + /** + * Same as get, except it can return NULL when item was not found. + * This version is custom, will take a hash and a custom key (that should support operator==() + */ + + template + _FORCE_INLINE_ TValue *custom_getptr(C p_custom_key, uint32_t p_custom_hash) { + if (unlikely(!elements)) { + return NULL; + } + + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t hash = p_custom_hash; + uint32_t pos = fastmod(hash, capacity_inv, capacity); + uint32_t distance = 0; + + while (true) { + if (hashes[pos] == EMPTY_HASH) { + return NULL; + } + + if (distance > _get_probe_length(pos, hashes[pos], capacity, capacity_inv)) { + return NULL; + } + + if (hashes[pos] == hash && Comparator::compare(elements[pos]->data.key, p_custom_key)) { + return &elements[pos]->data.value; + } + + pos = fastmod((pos + 1), capacity_inv, capacity); + distance++; + } + + return NULL; + } + + template + _FORCE_INLINE_ const TValue *custom_getptr(C p_custom_key, uint32_t p_custom_hash) const { + if (unlikely(!elements)) { + return NULL; + } + + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t hash = p_custom_hash; + uint32_t pos = fastmod(hash, capacity_inv, capacity); + uint32_t distance = 0; + + while (true) { + if (hashes[pos] == EMPTY_HASH) { + return NULL; + } + + if (distance > _get_probe_length(pos, hashes[pos], capacity, capacity_inv)) { + return NULL; + } + + if (hashes[pos] == hash && Comparator::compare(elements[pos]->data.key, p_custom_key)) { + return &elements[pos]->data.value; + } + + pos = fastmod((pos + 1), capacity_inv, capacity); + distance++; + } + + return NULL; + } + + _FORCE_INLINE_ bool has(const TKey &p_key) const { + uint32_t _pos = 0; + return _lookup_pos(p_key, _pos); + } + + bool erase(const TKey &p_key) { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + + if (!exists) { + return false; + } + + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t next_pos = fastmod((pos + 1), capacity_inv, capacity); + while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity, capacity_inv) != 0) { + SWAP(hashes[next_pos], hashes[pos]); + SWAP(elements[next_pos], elements[pos]); + pos = next_pos; + next_pos = fastmod((pos + 1), capacity_inv, capacity); + } + + hashes[pos] = EMPTY_HASH; + + if (head_element == elements[pos]) { + head_element = elements[pos]->next; + } + + if (tail_element == elements[pos]) { + tail_element = elements[pos]->prev; + } + + if (elements[pos]->prev) { + elements[pos]->prev->next = elements[pos]->next; + } + + if (elements[pos]->next) { + elements[pos]->next->prev = elements[pos]->prev; + } + + memdelete(elements[pos]); + elements[pos] = nullptr; + + num_elements--; + return true; + } + + // Reserves space for a number of elements, useful to avoid many resizes and rehashes. + // If adding a known (possibly large) number of elements at once, must be larger than old capacity. + void reserve(uint32_t p_new_capacity) { + uint32_t new_index = capacity_index; + + while (hash_table_size_primes[new_index] < p_new_capacity) { + ERR_FAIL_COND_MSG(new_index + 1 == (uint32_t)HASH_TABLE_SIZE_MAX, nullptr); + new_index++; + } + + if (new_index == capacity_index) { + return; + } + + if (elements == nullptr) { + capacity_index = new_index; + return; // Unallocated yet. + } + _resize_and_rehash(new_index); + } + + _FORCE_INLINE_ Element *front() { + return head_element; + } + _FORCE_INLINE_ Element *back() { + return tail_element; + } + + _FORCE_INLINE_ const Element *front() const { + return head_element; + } + _FORCE_INLINE_ const Element *back() const { + return tail_element; + } + + /** + * Get the next key to p_key, and the first key if p_key is null. + * Returns a pointer to the next key if found, NULL otherwise. + * Adding/Removing elements while iterating will, of course, have unexpected results, don't do it. + * + * Example: + * + * const TKey *k=NULL; + * + * while( (k=table.next(k)) ) { + * + * print( *k ); + * } + * + * This is for backwards compatibility. Use this syntax instead for new code: + * + * for (const HashMap::Element *E = map.front(); E; E = E->next) { + * ... + * } + * + */ + const TKey *next(const TKey *p_key) const { + if (unlikely(!elements)) { + return nullptr; + } + + if (!p_key) { /* get the first key */ + + if (unlikely(!front())) { + return nullptr; + } + + return &front()->data.key; + + } else { /* get the next key */ + + const Element *e = get_element(*p_key); + ERR_FAIL_COND_V_MSG(!e, nullptr, "Invalid key supplied."); + if (e->next) { + /* if there is a "next" in the list, return that */ + return &e->next->data.key; + } + + /* nothing found, was at end */ + } + + return nullptr; /* nothing found */ + } + + /* Indexing */ + + const TValue &operator[](const TKey &p_key) const { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + CRASH_COND(!exists); + return elements[pos]->data.value; + } + + TValue &operator[](const TKey &p_key) { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + if (!exists) { + return _insert(p_key, TValue())->data.value; + } else { + return elements[pos]->data.value; + } + } + + /* Insert */ + + Element *insert(const TKey &p_key, const TValue &p_value, bool p_front_insert = false) { + return _insert(p_key, p_value, p_front_insert); + } + + Element *set(const TKey &p_key, const TValue &p_value, bool p_front_insert = false) { + return _insert(p_key, p_value, p_front_insert); + } + + /* Helpers */ + + void get_key_list(List *p_keys) const { + if (unlikely(!elements)) { + return; + } + + for (const Element *E = front(); E; E = E->next) { + p_keys->push_back(E->data.key); + } + } + + /* Constructors */ + + HashMap(const HashMap &p_other) { + reserve(hash_table_size_primes[p_other.capacity_index]); + + if (p_other.num_elements == 0) { + return; + } + + for (const Element *E = p_other.front(); E; E = E->next) { + insert(E->data.key, E->data.value); + } + } + + void operator=(const HashMap &p_other) { + if (this == &p_other) { + return; // Ignore self assignment. + } + if (num_elements != 0) { + clear(); + } + + reserve(hash_table_size_primes[p_other.capacity_index]); + + if (p_other.elements == nullptr) { + return; // Nothing to copy. + } + + for (const Element *E = p_other.front(); E; E = E->next) { + insert(E->data.key, E->data.value); + } + } + + HashMap(uint32_t p_initial_capacity) { + // Capacity can't be 0. + capacity_index = 0; + reserve(p_initial_capacity); + } + HashMap() { + capacity_index = MIN_CAPACITY_INDEX; + } + + uint32_t debug_get_hash(uint32_t p_index) { + if (num_elements == 0) { + return 0; + } + ERR_FAIL_INDEX_V(p_index, get_capacity(), 0); + return hashes[p_index]; + } + Element *debug_get_element(uint32_t p_index) { + if (num_elements == 0) { + return NULL; + } + + ERR_FAIL_INDEX_V(p_index, get_capacity(), NULL); + + return elements[p_index]; + } + + ~HashMap() { + clear(); + + if (elements != nullptr) { + Memory::free_static(elements); + Memory::free_static(hashes); + } + } + +private: + Element **elements = nullptr; + uint32_t *hashes = nullptr; + Element *head_element = nullptr; + Element *tail_element = nullptr; + + uint32_t capacity_index = 0; + uint32_t num_elements = 0; + + _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const { + uint32_t hash = Hasher::hash(p_key); + + if (unlikely(hash == EMPTY_HASH)) { + hash = EMPTY_HASH + 1; + } + + return hash; + } + + static _FORCE_INLINE_ uint32_t _get_probe_length(const uint32_t p_pos, const uint32_t p_hash, const uint32_t p_capacity, const uint64_t p_capacity_inv) { + const uint32_t original_pos = fastmod(p_hash, p_capacity_inv, p_capacity); + return fastmod(p_pos - original_pos + p_capacity, p_capacity_inv, p_capacity); + } + + bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const { + if (elements == nullptr || num_elements == 0) { + return false; // Failed lookups, no elements + } + + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t hash = _hash(p_key); + uint32_t pos = fastmod(hash, capacity_inv, capacity); + uint32_t distance = 0; + + while (true) { + if (hashes[pos] == EMPTY_HASH) { + return false; + } + + if (distance > _get_probe_length(pos, hashes[pos], capacity, capacity_inv)) { + return false; + } + + if (hashes[pos] == hash && Comparator::compare(elements[pos]->data.key, p_key)) { + r_pos = pos; + return true; + } + + pos = fastmod((pos + 1), capacity_inv, capacity); + distance++; + } + } + + void _insert_with_hash(uint32_t p_hash, Element *p_value) { + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t hash = p_hash; + Element *value = p_value; + uint32_t distance = 0; + uint32_t pos = fastmod(hash, capacity_inv, capacity); + + while (true) { + if (hashes[pos] == EMPTY_HASH) { + elements[pos] = value; + hashes[pos] = hash; + + num_elements++; + + return; + } + + // Not an empty slot, let's check the probing length of the existing one. + uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos], capacity, capacity_inv); + if (existing_probe_len < distance) { + SWAP(hash, hashes[pos]); + SWAP(value, elements[pos]); + distance = existing_probe_len; + } + + pos = fastmod((pos + 1), capacity_inv, capacity); + distance++; + } + } + + void _resize_and_rehash(uint32_t p_new_capacity_index) { + uint32_t old_capacity = hash_table_size_primes[capacity_index]; + + // Capacity can't be 0. + capacity_index = MAX((uint32_t)MIN_CAPACITY_INDEX, p_new_capacity_index); + + uint32_t capacity = hash_table_size_primes[capacity_index]; + + Element **old_elements = elements; + uint32_t *old_hashes = hashes; + + num_elements = 0; + hashes = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + elements = reinterpret_cast(Memory::alloc_static(sizeof(Element *) * capacity)); + + for (uint32_t i = 0; i < capacity; i++) { + hashes[i] = 0; + elements[i] = nullptr; + } + + if (old_capacity == 0) { + // Nothing to do. + return; + } + + for (uint32_t i = 0; i < old_capacity; i++) { + if (old_hashes[i] == EMPTY_HASH) { + continue; + } + + _insert_with_hash(old_hashes[i], old_elements[i]); + } + + Memory::free_static(old_elements); + Memory::free_static(old_hashes); + } + + _FORCE_INLINE_ Element *_insert(const TKey &p_key, const TValue &p_value, bool p_front_insert = false) { + uint32_t capacity = hash_table_size_primes[capacity_index]; + if (unlikely(elements == nullptr)) { + // Allocate on demand to save memory. + + hashes = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + elements = reinterpret_cast(Memory::alloc_static(sizeof(Element *) * capacity)); + + for (uint32_t i = 0; i < capacity; i++) { + hashes[i] = EMPTY_HASH; + elements[i] = nullptr; + } + } + + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + + if (exists) { + elements[pos]->data.value = p_value; + return elements[pos]; + } else { + if (num_elements + 1 > MAX_OCCUPANCY * capacity) { + ERR_FAIL_COND_V_MSG(capacity_index + 1 == HASH_TABLE_SIZE_MAX, nullptr, "Hash table maximum capacity reached, aborting insertion."); + _resize_and_rehash(capacity_index + 1); + } + + Element *elem = memnew(Element(p_key, p_value)); + + if (tail_element == nullptr) { + head_element = elem; + tail_element = elem; + } else if (p_front_insert) { + head_element->prev = elem; + elem->next = head_element; + head_element = elem; + } else { + tail_element->next = elem; + elem->prev = tail_element; + tail_element = elem; + } + + uint32_t hash = _hash(p_key); + _insert_with_hash(hash, elem); + return elem; + } + } +}; + +#endif // HASH_MAP_H +#line 0 + +#line 1 "sfwl/core/hash_set.h" +#ifndef HASH_SET_H +#define HASH_SET_H + +/*************************************************************************/ +/* hash_set.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +/** + * Implementation of Set using a bidi indexed hash map. + * Use RBSet instead of this only if the following conditions are met: + * + * - You need to keep an iterator or const pointer to Key and you intend to add/remove elements in the meantime. + * - Iteration order does matter (via operator<) + * + */ + +template > +class HashSet { +public: + static constexpr uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime. + static constexpr float MAX_OCCUPANCY = 0.75; + static constexpr uint32_t EMPTY_HASH = 0; + +private: + TKey *keys = nullptr; + uint32_t *hash_to_key = nullptr; + uint32_t *key_to_hash = nullptr; + uint32_t *hashes = nullptr; + + uint32_t capacity_index = 0; + uint32_t num_elements = 0; + + _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const { + uint32_t hash = Hasher::hash(p_key); + + if (unlikely(hash == EMPTY_HASH)) { + hash = EMPTY_HASH + 1; + } + + return hash; + } + + static _FORCE_INLINE_ uint32_t _get_probe_length(const uint32_t p_pos, const uint32_t p_hash, const uint32_t p_capacity, const uint64_t p_capacity_inv) { + const uint32_t original_pos = fastmod(p_hash, p_capacity_inv, p_capacity); + return fastmod(p_pos - original_pos + p_capacity, p_capacity_inv, p_capacity); + } + + bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const { + if (keys == nullptr || num_elements == 0) { + return false; // Failed lookups, no elements + } + + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t hash = _hash(p_key); + uint32_t pos = fastmod(hash, capacity_inv, capacity); + uint32_t distance = 0; + + while (true) { + if (hashes[pos] == EMPTY_HASH) { + return false; + } + + if (distance > _get_probe_length(pos, hashes[pos], capacity, capacity_inv)) { + return false; + } + + if (hashes[pos] == hash && Comparator::compare(keys[hash_to_key[pos]], p_key)) { + r_pos = hash_to_key[pos]; + return true; + } + + pos = fastmod(pos + 1, capacity_inv, capacity); + distance++; + } + } + + uint32_t _insert_with_hash(uint32_t p_hash, uint32_t p_index) { + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t hash = p_hash; + uint32_t index = p_index; + uint32_t distance = 0; + uint32_t pos = fastmod(hash, capacity_inv, capacity); + + while (true) { + if (hashes[pos] == EMPTY_HASH) { + hashes[pos] = hash; + key_to_hash[index] = pos; + hash_to_key[pos] = index; + return pos; + } + + // Not an empty slot, let's check the probing length of the existing one. + uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos], capacity, capacity_inv); + if (existing_probe_len < distance) { + key_to_hash[index] = pos; + SWAP(hash, hashes[pos]); + SWAP(index, hash_to_key[pos]); + distance = existing_probe_len; + } + + pos = fastmod(pos + 1, capacity_inv, capacity); + distance++; + } + } + + void _resize_and_rehash(uint32_t p_new_capacity_index) { + // Capacity can't be 0. + capacity_index = MAX((uint32_t)MIN_CAPACITY_INDEX, p_new_capacity_index); + + uint32_t capacity = hash_table_size_primes[capacity_index]; + + uint32_t *old_hashes = hashes; + uint32_t *old_key_to_hash = key_to_hash; + + hashes = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + keys = reinterpret_cast(Memory::realloc_static(keys, sizeof(TKey) * capacity)); + key_to_hash = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + hash_to_key = reinterpret_cast(Memory::realloc_static(hash_to_key, sizeof(uint32_t) * capacity)); + + for (uint32_t i = 0; i < capacity; i++) { + hashes[i] = EMPTY_HASH; + } + + for (uint32_t i = 0; i < num_elements; i++) { + uint32_t h = old_hashes[old_key_to_hash[i]]; + _insert_with_hash(h, i); + } + + Memory::free_static(old_hashes); + Memory::free_static(old_key_to_hash); + } + + _FORCE_INLINE_ int32_t _insert(const TKey &p_key) { + uint32_t capacity = hash_table_size_primes[capacity_index]; + if (unlikely(keys == nullptr)) { + // Allocate on demand to save memory. + + hashes = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + keys = reinterpret_cast(Memory::alloc_static(sizeof(TKey) * capacity)); + key_to_hash = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + hash_to_key = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + + for (uint32_t i = 0; i < capacity; i++) { + hashes[i] = EMPTY_HASH; + } + } + + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + + if (exists) { + return pos; + } else { + if (num_elements + 1 > MAX_OCCUPANCY * capacity) { + ERR_FAIL_COND_V_MSG(capacity_index + 1 == HASH_TABLE_SIZE_MAX, -1, "Hash table maximum capacity reached, aborting insertion."); + _resize_and_rehash(capacity_index + 1); + } + + uint32_t hash = _hash(p_key); + memnew_placement(&keys[num_elements], TKey(p_key)); + _insert_with_hash(hash, num_elements); + num_elements++; + return num_elements - 1; + } + } + + void _init_from(const HashSet &p_other) { + capacity_index = p_other.capacity_index; + num_elements = p_other.num_elements; + + if (p_other.num_elements == 0) { + return; + } + + uint32_t capacity = hash_table_size_primes[capacity_index]; + + hashes = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + keys = reinterpret_cast(Memory::alloc_static(sizeof(TKey) * capacity)); + key_to_hash = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + hash_to_key = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + + for (uint32_t i = 0; i < num_elements; i++) { + memnew_placement(&keys[i], TKey(p_other.keys[i])); + key_to_hash[i] = p_other.key_to_hash[i]; + } + + for (uint32_t i = 0; i < capacity; i++) { + hashes[i] = p_other.hashes[i]; + hash_to_key[i] = p_other.hash_to_key[i]; + } + } + +public: + _FORCE_INLINE_ uint32_t get_capacity() const { return hash_table_size_primes[capacity_index]; } + _FORCE_INLINE_ uint32_t size() const { return num_elements; } + + /* Standard Godot Container API */ + + bool is_empty() const { + return num_elements == 0; + } + + void clear() { + if (keys == nullptr || num_elements == 0) { + return; + } + uint32_t capacity = hash_table_size_primes[capacity_index]; + for (uint32_t i = 0; i < capacity; i++) { + hashes[i] = EMPTY_HASH; + } + for (uint32_t i = 0; i < num_elements; i++) { + keys[i].~TKey(); + } + + num_elements = 0; + } + + _FORCE_INLINE_ bool has(const TKey &p_key) const { + uint32_t _pos = 0; + return _lookup_pos(p_key, _pos); + } + + bool erase(const TKey &p_key) { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + + if (!exists) { + return false; + } + + uint32_t key_pos = pos; + pos = key_to_hash[pos]; //make hash pos + + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t next_pos = fastmod(pos + 1, capacity_inv, capacity); + while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity, capacity_inv) != 0) { + uint32_t kpos = hash_to_key[pos]; + uint32_t kpos_next = hash_to_key[next_pos]; + SWAP(key_to_hash[kpos], key_to_hash[kpos_next]); + SWAP(hashes[next_pos], hashes[pos]); + SWAP(hash_to_key[next_pos], hash_to_key[pos]); + + pos = next_pos; + next_pos = fastmod(pos + 1, capacity_inv, capacity); + } + + hashes[pos] = EMPTY_HASH; + keys[key_pos].~TKey(); + num_elements--; + if (key_pos < num_elements) { + // Not the last key, move the last one here to keep keys lineal + memnew_placement(&keys[key_pos], TKey(keys[num_elements])); + keys[num_elements].~TKey(); + key_to_hash[key_pos] = key_to_hash[num_elements]; + hash_to_key[key_to_hash[num_elements]] = key_pos; + } + + return true; + } + + // Reserves space for a number of elements, useful to avoid many resizes and rehashes. + // If adding a known (possibly large) number of elements at once, must be larger than old capacity. + void reserve(uint32_t p_new_capacity) { + uint32_t new_index = capacity_index; + + while (hash_table_size_primes[new_index] < p_new_capacity) { + ERR_FAIL_COND_MSG(new_index + 1 == (uint32_t)HASH_TABLE_SIZE_MAX, nullptr); + new_index++; + } + + if (new_index == capacity_index) { + return; + } + + if (keys == nullptr) { + capacity_index = new_index; + return; // Unallocated yet. + } + _resize_and_rehash(new_index); + } + + /** Iterator API **/ + + struct Iterator { + _FORCE_INLINE_ const TKey &operator*() const { + return keys[index]; + } + _FORCE_INLINE_ const TKey *operator->() const { + return &keys[index]; + } + _FORCE_INLINE_ Iterator &operator++() { + index++; + if (index >= (int32_t)num_keys) { + index = -1; + keys = nullptr; + num_keys = 0; + } + return *this; + } + _FORCE_INLINE_ Iterator &operator--() { + index--; + if (index < 0) { + index = -1; + keys = nullptr; + num_keys = 0; + } + return *this; + } + + _FORCE_INLINE_ const TKey &key() const { + return keys[index]; + } + _FORCE_INLINE_ const TKey *key_ptr() const { + return &keys[index]; + } + + _FORCE_INLINE_ Iterator &next() { + index++; + if (index >= (int32_t)num_keys) { + index = -1; + keys = nullptr; + num_keys = 0; + } + return *this; + } + _FORCE_INLINE_ Iterator &prev() { + index--; + if (index < 0) { + index = -1; + keys = nullptr; + num_keys = 0; + } + return *this; + } + + _FORCE_INLINE_ bool valid() const { + return keys != nullptr; + } + + _FORCE_INLINE_ bool operator==(const Iterator &b) const { return keys == b.keys && index == b.index; } + _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return keys != b.keys || index != b.index; } + + _FORCE_INLINE_ explicit operator bool() const { + return keys != nullptr; + } + + _FORCE_INLINE_ Iterator(const TKey *p_keys, uint32_t p_num_keys, int32_t p_index = -1) { + keys = p_keys; + num_keys = p_num_keys; + index = p_index; + } + _FORCE_INLINE_ Iterator() {} + _FORCE_INLINE_ Iterator(const Iterator &p_it) { + keys = p_it.keys; + num_keys = p_it.num_keys; + index = p_it.index; + } + _FORCE_INLINE_ void operator=(const Iterator &p_it) { + keys = p_it.keys; + num_keys = p_it.num_keys; + index = p_it.index; + } + + private: + const TKey *keys = nullptr; + uint32_t num_keys = 0; + int32_t index = -1; + }; + + _FORCE_INLINE_ Iterator begin() const { + return num_elements ? Iterator(keys, num_elements, 0) : Iterator(); + } + _FORCE_INLINE_ Iterator end() const { + return Iterator(); + } + _FORCE_INLINE_ Iterator last() const { + if (num_elements == 0) { + return Iterator(); + } + return Iterator(keys, num_elements, num_elements - 1); + } + + _FORCE_INLINE_ Iterator find(const TKey &p_key) const { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + if (!exists) { + return end(); + } + return Iterator(keys, num_elements, pos); + } + + _FORCE_INLINE_ void remove(const Iterator &p_iter) { + if (p_iter) { + erase(*p_iter); + } + } + + /* Insert */ + + Iterator insert(const TKey &p_key) { + uint32_t pos = _insert(p_key); + return Iterator(keys, num_elements, pos); + } + + /* Constructors */ + + HashSet(const HashSet &p_other) { + _init_from(p_other); + } + + void operator=(const HashSet &p_other) { + if (this == &p_other) { + return; // Ignore self assignment. + } + + clear(); + + if (keys != nullptr) { + Memory::free_static(keys); + Memory::free_static(key_to_hash); + Memory::free_static(hash_to_key); + Memory::free_static(hashes); + keys = nullptr; + hashes = nullptr; + hash_to_key = nullptr; + key_to_hash = nullptr; + } + + _init_from(p_other); + } + + HashSet(uint32_t p_initial_capacity) { + // Capacity can't be 0. + capacity_index = 0; + reserve(p_initial_capacity); + } + HashSet() { + capacity_index = MIN_CAPACITY_INDEX; + } + + void reset() { + clear(); + + if (keys != nullptr) { + Memory::free_static(keys); + Memory::free_static(key_to_hash); + Memory::free_static(hash_to_key); + Memory::free_static(hashes); + keys = nullptr; + hashes = nullptr; + hash_to_key = nullptr; + key_to_hash = nullptr; + } + capacity_index = MIN_CAPACITY_INDEX; + } + + ~HashSet() { + clear(); + + if (keys != nullptr) { + Memory::free_static(keys); + Memory::free_static(key_to_hash); + Memory::free_static(hash_to_key); + Memory::free_static(hashes); + } + } +}; + +#endif // HASH_SET_H +#line 0 + + +#line 1 "sfwl/core/file_access.h" + +#ifndef FILE_ACCESS_H +#define FILE_ACCESS_H + + + +class FileAccess { +public: + //TODO should probably have some simple buffered open / close / write / read api. + + String read_file(const String &path); + + Vector read_file_bin(const String &path); + Error read_file_into_bin(const String &path, Vector *data); + + Error write_file(const String &path, const String &str); + Error write_file_bin(const String &path, const Vector &data); + + FileAccess(); + virtual ~FileAccess(); + +private: +}; + +#endif +#line 0 + + +#line 1 "sfwl/core/dir_access.h" + +#ifndef DIR_ACCESS_H +#define DIR_ACCESS_H + + + +struct tinydir_file; +struct tinydir_dir; + +class DirAccess { +public: + Error open_dir(const String &path, bool skip_specials = true); + Error open_dir(const char *path, bool skip_specials = true); + void close_dir(); + + bool has_next(); + bool read(); + bool next(); + + bool current_is_ok(); + String current_get_name(); + String current_get_path(); + String current_get_extension(); + const char *current_get_name_cstr(); + const char *current_get_path_cstr(); + const char *current_get_extension_cstr(); + bool current_is_file(); + bool current_is_dir(); + bool current_is_special_dir(); + + bool is_dir_open(); + bool is_dir_closed(); + + DirAccess(); + virtual ~DirAccess(); + +private: + bool _skip_specials; + int _read_file_result; + tinydir_dir *_dir; + tinydir_file *_file; + + bool _dir_open; +}; + +#endif +#line 0 + + +#line 1 "sfwl/core/sfw_core.h" + +#ifndef SFW_CORE_H +#define SFW_CORE_H + +class SFWCore { +public: + static void setup(); + static void cleanup(); + +protected: + static bool _initialized; +}; + +#endif +#line 0 + +//=================== OBJECT SECTION =================== + + + + +#line 1 "sfwl/object/ref_ptr.h" +#ifndef REF_PTR_H +#define REF_PTR_H + +/*************************************************************************/ +/* ref_ptr.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +/** + @author Juan Linietsky + * This class exists to workaround a limitation in C++ but keep the design OK. + * It's basically an opaque container of a Reference reference, so Variant can use it. +*/ + + + +class RefPtr { + enum { + DATASIZE = sizeof(void *) //*4 -ref was shrunk + }; + + mutable char data[DATASIZE]; // too much probably, virtual class + pointer +public: + bool is_null() const; + void operator=(const RefPtr &p_other); + bool operator==(const RefPtr &p_other) const; + bool operator!=(const RefPtr &p_other) const; + void unref(); + + _FORCE_INLINE_ void *get_data() const { return data; } + RefPtr(const RefPtr &p_other); + RefPtr(); + ~RefPtr(); +}; + +#endif // REF_PTR_H +#line 0 + +#line 1 "sfwl/object/object_id.h" +#ifndef OBJECTID_H +#define OBJECTID_H + +/*************************************************************************/ +/* object_id.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +typedef uint64_t ObjectID; + +#endif +#line 0 + + +#line 1 "sfwl/object/core_string_names.h" +#ifndef CORE_STRING_NAMES_H +#define CORE_STRING_NAMES_H + +/*************************************************************************/ +/* core_string_names.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +class CoreStringNames { +public: + static void create() { singleton = memnew(CoreStringNames); } + static void free() { + memdelete(singleton); + singleton = nullptr; + } + + CoreStringNames(); + + _FORCE_INLINE_ static CoreStringNames *get_singleton() { return singleton; } + + static CoreStringNames *singleton; + + StringName _free; + StringName changed; + StringName _meta; + StringName _script; + StringName script_changed; + StringName ___pdcdata; + StringName __getvar; + StringName _iter_init; + StringName _iter_next; + StringName _iter_get; + StringName get_rid; + StringName _to_string; +#ifdef TOOLS_ENABLED + StringName _sections_unfolded; +#endif + StringName _custom_features; + + StringName x; + StringName y; + StringName z; + StringName w; + StringName r; + StringName g; + StringName b; + StringName a; + StringName position; + StringName size; + StringName end; + StringName basis; + StringName origin; + StringName normal; + StringName d; + StringName h; + StringName s; + StringName v; + StringName r8; + StringName g8; + StringName b8; + StringName a8; +}; + +#endif // SCENE_STRING_NAMES_H +#line 0 + + + + +#line 1 "sfwl/object/array.h" +#ifndef ARRAY_H +#define ARRAY_H + +/*************************************************************************/ +/* array.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +class Variant; +class ArrayPrivate; +class Object; +class StringName; +class String; + +class Array { + mutable ArrayPrivate *_p; + void _ref(const Array &p_from) const; + void _unref() const; + + inline int _clamp_slice_index(int p_index) const; + +public: + Variant &operator[](int p_idx); + const Variant &operator[](int p_idx) const; + + void set(int p_idx, const Variant &p_value); + const Variant &get(int p_idx) const; + + int size() const; + bool empty() const; + void clear(); + + bool deep_equal(const Array &p_array, int p_recursion_count = 0) const; + bool operator==(const Array &p_array) const; + + uint32_t hash() const; + uint32_t recursive_hash(int p_recursion_count) const; + void operator=(const Array &p_array); + + void push_back(const Variant &p_value); + _FORCE_INLINE_ void append(const Variant &p_value) { push_back(p_value); } //for python compatibility + void append_array(const Array &p_array); + Error resize(int p_new_size); + + void insert(int p_pos, const Variant &p_value); + void remove(int p_pos); + void fill(const Variant &p_value); + + Variant front() const; + Variant back() const; + + Array &sort(); + Array &sort_custom(Object *p_obj); + void shuffle(); + int bsearch(const Variant &p_value, bool p_before = true); + int bsearch_custom(const Variant &p_value, Object *p_obj, const StringName &p_function, bool p_before = true); + Array &invert(); + + int find(const Variant &p_value, int p_from = 0) const; + int rfind(const Variant &p_value, int p_from = -1) const; + int find_last(const Variant &p_value) const; + int count(const Variant &p_value) const; + bool has(const Variant &p_value) const; + + void erase(const Variant &p_value); + + void push_front(const Variant &p_value); + Variant pop_back(); + Variant pop_front(); + Variant pop_at(int p_pos); + + Array duplicate(bool p_deep = false) const; + + Array slice(int p_begin, int p_end, int p_step = 1, bool p_deep = false) const; + + Variant min() const; + Variant max() const; + + const void *id() const; + + String sprintf(const String &p_format, bool *error) const; + + Array(const Array &p_from); + Array(); + ~Array(); +}; + +#endif // ARRAY_H +#line 0 + + +#line 1 "sfwl/object/dictionary.h" +#ifndef DICTIONARY_H +#define DICTIONARY_H + +/*************************************************************************/ +/* dictionary.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +class Variant; + +struct DictionaryPrivate; + +class Dictionary { + mutable DictionaryPrivate *_p; + + void _ref(const Dictionary &p_from) const; + void _unref() const; + +public: + void get_key_list(List *p_keys) const; + Variant get_key_at_index(int p_index) const; + Variant get_value_at_index(int p_index) const; + + Variant &operator[](const Variant &p_key); + const Variant &operator[](const Variant &p_key) const; + + const Variant *getptr(const Variant &p_key) const; + Variant *getptr(const Variant &p_key); + + Variant get_valid(const Variant &p_key) const; + Variant get(const Variant &p_key, const Variant &p_default) const; + + int size() const; + bool empty() const; + void clear(); + void merge(const Dictionary &p_dictionary, bool p_overwrite = false); + + bool has(const Variant &p_key) const; + bool has_all(const Array &p_keys) const; + Variant find_key(const Variant &p_value) const; + + bool erase(const Variant &p_key); + + bool deep_equal(const Dictionary &p_dictionary, int p_recursion_count = 0) const; + bool operator==(const Dictionary &p_dictionary) const; + bool operator!=(const Dictionary &p_dictionary) const; + + uint32_t hash() const; + uint32_t recursive_hash(int p_recursion_count) const; + void operator=(const Dictionary &p_dictionary); + + const Variant *next(const Variant *p_key = nullptr) const; + + Array keys() const; + Array values() const; + + Dictionary duplicate(bool p_deep = false) const; + + const void *id() const; + + Dictionary(const Dictionary &p_from); + Dictionary(); + ~Dictionary(); +}; + +#endif // DICTIONARY_H +#line 0 + + +#line 1 "sfwl/object/variant.h" +#ifndef VARIANT_H +#define VARIANT_H + +/*************************************************************************/ +/* variant.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +class Object; +class ObjectRC; + +typedef PoolVector PoolByteArray; +typedef PoolVector PoolIntArray; +typedef PoolVector PoolRealArray; +typedef PoolVector PoolStringArray; +typedef PoolVector PoolVector2Array; +typedef PoolVector PoolVector2iArray; +typedef PoolVector PoolVector3Array; +typedef PoolVector PoolVector3iArray; +typedef PoolVector PoolVector4Array; +typedef PoolVector PoolVector4iArray; +typedef PoolVector PoolColorArray; + +// Temporary workaround until c++11 alignas() +#ifdef __GNUC__ +#define GCC_ALIGNED_8 __attribute__((aligned(8))) +#else +#define GCC_ALIGNED_8 +#endif + +#define _REF_OBJ_PTR(m_variant) (reinterpret_cast *>((m_variant)._get_obj().ref.get_data())->ptr()) +#define _OBJ_PTR(m_variant) ((m_variant)._get_obj().rc ? (m_variant)._get_obj().rc->get_ptr() : _REF_OBJ_PTR(m_variant)) +// _UNSAFE_OBJ_PROXY_PTR is needed for comparing an object Variant against NIL or compare two object Variants. +// It's guaranteed to be unique per object, in contrast to the pointer stored in the RC structure, +// which is set to null when the object is destroyed. +#define _UNSAFE_OBJ_PROXY_PTR(m_variant) ((m_variant)._get_obj().rc ? reinterpret_cast((m_variant)._get_obj().rc) : reinterpret_cast(_REF_OBJ_PTR(m_variant))) + +class Variant { +public: + // If this changes the table in variant_op must be updated + enum Type { + + NIL, + + // atomic types + BOOL, + INT, + REAL, + STRING, + + // math types + RECT2, + RECT2I, + VECTOR2, + VECTOR2I, + VECTOR3, + VECTOR3I, + VECTOR4, + VECTOR4I, + + PLANE, + QUATERNION, + AABB, + BASIS, + TRANSFORM, + TRANSFORM2D, + PROJECTION, + + // misc types + COLOR, + OBJECT, + STRING_NAME, + DICTIONARY, + ARRAY, + + // arrays + POOL_BYTE_ARRAY, + POOL_INT_ARRAY, + POOL_REAL_ARRAY, + POOL_STRING_ARRAY, + POOL_VECTOR2_ARRAY, + POOL_VECTOR2I_ARRAY, + POOL_VECTOR3_ARRAY, + POOL_VECTOR3I_ARRAY, + POOL_VECTOR4_ARRAY, + POOL_VECTOR4I_ARRAY, + POOL_COLOR_ARRAY, + + VARIANT_MAX // 38 + + }; + + enum { + // Maximum recursion depth allowed when serializing variants. + MAX_RECURSION_DEPTH = 1024, + }; + +private: + // Variant takes 20 bytes when real_t is float, and 36 if double + // it only allocates extra memory for aabb/matrix. + + Type type; + + struct ObjData { + // Will be null for every type deriving from Reference as they have their + // own reference count mechanism + ObjectRC *rc; + // Always initialized, but will be null if the Ref<> assigned was null + // or this Variant is not even holding a Reference-derived object + RefPtr ref; + }; + + _FORCE_INLINE_ ObjData &_get_obj(); + _FORCE_INLINE_ const ObjData &_get_obj() const; + + union { + bool _bool; + int64_t _int; + double _real; + Transform2D *_transform2d; + ::AABB *_aabb; + Basis *_basis; + Transform *_transform; + Projection *_projection; + void *_ptr; //generic pointer + uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)]; + } _data GCC_ALIGNED_8; + + void reference(const Variant &p_variant); + void clear(); + +public: + _FORCE_INLINE_ Type get_type() const { return type; } + static String get_type_name(Variant::Type p_type); + static bool can_convert(Type p_type_from, Type p_type_to); + static bool can_convert_strict(Type p_type_from, Type p_type_to); + + bool is_ref() const; + _FORCE_INLINE_ bool is_num() const { return type == INT || type == REAL; }; + _FORCE_INLINE_ bool is_array() const { return type >= ARRAY; }; + _FORCE_INLINE_ bool is_null() const { return type == NIL; }; + bool is_shared() const; + bool is_zero() const; + bool is_one() const; + + ObjectID get_object_instance_id() const; + bool is_invalid_object() const; + + operator bool() const; + operator signed int() const; + operator unsigned int() const; // this is the real one + operator signed short() const; + operator unsigned short() const; + operator signed char() const; + operator unsigned char() const; + //operator long unsigned int() const; + operator int64_t() const; + operator uint64_t() const; +#ifdef NEED_LONG_INT + operator signed long() const; + operator unsigned long() const; +#endif + + operator CharType() const; + operator float() const; + operator double() const; + operator String() const; + operator StringName() const; + operator Rect2() const; + operator Rect2i() const; + operator Vector2() const; + operator Vector2i() const; + operator Vector3() const; + operator Vector3i() const; + operator Vector4() const; + operator Vector4i() const; + operator Plane() const; + operator ::AABB() const; + operator Quaternion() const; + operator Basis() const; + operator Transform() const; + operator Transform2D() const; + operator Projection() const; + + operator Color() const; + operator RefPtr() const; + + operator Object *() const; + + operator Dictionary() const; + operator Array() const; + + operator PoolVector() const; + operator PoolVector() const; + operator PoolVector() const; + operator PoolVector() const; + operator PoolVector() const; + operator PoolVector() const; + operator PoolVector() const; + operator PoolVector() const; + operator PoolVector() const; + operator PoolVector() const; + operator PoolVector() const; + operator PoolVector() const; + operator PoolVector() const; + + operator Vector() const; + operator Vector() const; + operator Vector() const; + operator Vector() const; + operator Vector() const; + operator Vector() const; + operator Vector() const; + operator Vector() const; + operator Vector() const; + operator Vector() const; + operator Vector() const; + operator Vector() const; + operator Vector() const; + + operator Vector() const; + + // some core type enums to convert to + operator Margin() const; + operator Side() const; + operator Orientation() const; + + Variant(bool p_bool); + Variant(signed int p_int); // real one + Variant(unsigned int p_int); +#ifdef NEED_LONG_INT + Variant(signed long p_long); // real one + Variant(unsigned long p_long); +//Variant(long unsigned int p_long); +#endif + Variant(signed short p_short); // real one + Variant(unsigned short p_short); + Variant(signed char p_char); // real one + Variant(unsigned char p_char); + Variant(int64_t p_int); // real one + Variant(uint64_t p_int); + Variant(float p_float); + Variant(double p_double); + Variant(const String &p_string); + Variant(const StringName &p_string); + Variant(const char *const p_cstring); + Variant(const CharType *p_wstring); + Variant(const Vector2 &p_vector2); + Variant(const Vector2i &p_vector2); + Variant(const Rect2 &p_rect2); + Variant(const Rect2i &p_rect2); + Variant(const Vector3 &p_vector3); + Variant(const Vector3i &p_vector3); + Variant(const Vector4 &p_vector4); + Variant(const Vector4i &p_vector4); + Variant(const Projection &p_projection); + Variant(const Plane &p_plane); + Variant(const ::AABB &p_aabb); + Variant(const Quaternion &p_quat); + Variant(const Basis &p_matrix); + Variant(const Transform2D &p_transform); + Variant(const Transform &p_transform); + Variant(const Color &p_color); + Variant(const RefPtr &p_resource); + Variant(const Object *p_object); + Variant(const Dictionary &p_dictionary); + + Variant(const Array &p_array); + Variant(const PoolVector &p_array); + Variant(const PoolVector &p_raw_array); + Variant(const PoolVector &p_int_array); + Variant(const PoolVector &p_real_array); + Variant(const PoolVector &p_string_array); + Variant(const PoolVector &p_vector3_array); + Variant(const PoolVector &p_vector3_array); + Variant(const PoolVector &p_color_array); + Variant(const PoolVector &p_face_array); + Variant(const PoolVector &p_vector2_array); + Variant(const PoolVector &p_vector2_array); + Variant(const PoolVector &p_vector4_array); + Variant(const PoolVector &p_vector4_array); + + Variant(const Vector &p_array); + Variant(const Vector &p_array); + Variant(const Vector &p_array); + Variant(const Vector &p_array); + Variant(const Vector &p_array); + Variant(const Vector &p_array); + Variant(const Vector &p_array); + Variant(const Vector &p_array); + Variant(const Vector &p_array); + Variant(const Vector &p_array); + Variant(const Vector &p_array); + Variant(const Vector &p_array); + Variant(const Vector &p_array); + Variant(const Vector &p_array); + + // If this changes the table in variant_op must be updated + enum Operator { + //comparison + OP_EQUAL, + OP_NOT_EQUAL, + OP_LESS, + OP_LESS_EQUAL, + OP_GREATER, + OP_GREATER_EQUAL, + //mathematic + OP_ADD, + OP_SUBTRACT, + OP_MULTIPLY, + OP_DIVIDE, + OP_NEGATE, + OP_POSITIVE, + OP_MODULE, + OP_STRING_CONCAT, + //bitwise + OP_SHIFT_LEFT, + OP_SHIFT_RIGHT, + OP_BIT_AND, + OP_BIT_OR, + OP_BIT_XOR, + OP_BIT_NEGATE, + //logic + OP_AND, + OP_OR, + OP_XOR, + OP_NOT, + //containment + OP_IN, + OP_MAX + }; + + //Maybe add helper methods that use these + //Like max() + //add() + //maybe operators like with add + //? + + static String get_operator_name(Operator p_op); + static void evaluate(const Operator &p_op, const Variant &p_a, const Variant &p_b, Variant &r_ret, bool &r_valid); + static _FORCE_INLINE_ Variant evaluate(const Operator &p_op, const Variant &p_a, const Variant &p_b) { + bool valid = true; + Variant res; + evaluate(p_op, p_a, p_b, res, valid); + return res; + } + + void zero(); + Variant duplicate(bool deep = false) const; + static void blend(const Variant &a, const Variant &b, float c, Variant &r_dst); + static void interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst); + static void sub(const Variant &a, const Variant &b, Variant &r_dst); + + //Maybe add helper methods that call this? like get_x(), set_x() etc Or just x(), x(value)? + //Check if operator[] can take strings and ints that might work too + void set_named(const StringName &p_index, const Variant &p_value, bool *r_valid = nullptr); + Variant get_named(const StringName &p_index, bool *r_valid = nullptr) const; + + //Implement operator[]s that use these? + void set(const Variant &p_index, const Variant &p_value, bool *r_valid = nullptr); + Variant get(const Variant &p_index, bool *r_valid = nullptr) const; + bool in(const Variant &p_index, bool *r_valid = nullptr) const; + + bool iter_init(Variant &r_iter, bool &r_valid) const; + bool iter_next(Variant &r_iter, bool &r_valid) const; + Variant iter_get(const Variant &r_iter, bool &r_valid) const; + + bool deep_equal(const Variant &p_variant, int p_recursion_count = 0) const; + bool operator==(const Variant &p_variant) const; + bool operator!=(const Variant &p_variant) const; + bool operator<(const Variant &p_variant) const; + uint32_t hash() const; + uint32_t recursive_hash(int p_recursion_count) const; + + bool hash_compare(const Variant &p_variant) const; + bool booleanize() const; + String stringify(List &stack) const; + + void operator=(const Variant &p_variant); // only this is enough for all the other types + Variant(const Variant &p_variant); + _FORCE_INLINE_ Variant() { + type = NIL; + } + _FORCE_INLINE_ ~Variant() { + if (type != Variant::NIL) { + clear(); + } + } +}; + +//typedef Dictionary Dictionary; no +//typedef Array Array; + +Vector varray(); +Vector varray(const Variant &p_arg1); +Vector varray(const Variant &p_arg1, const Variant &p_arg2); +Vector varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3); +Vector varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4); +Vector varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5); + +struct VariantHasher { + static _FORCE_INLINE_ uint32_t hash(const Variant &p_variant) { return p_variant.hash(); } +}; + +struct VariantComparator { + static _FORCE_INLINE_ bool compare(const Variant &p_lhs, const Variant &p_rhs) { return p_lhs.hash_compare(p_rhs); } +}; + +Variant::ObjData &Variant::_get_obj() { + return *reinterpret_cast(&_data._mem[0]); +} + +const Variant::ObjData &Variant::_get_obj() const { + return *reinterpret_cast(&_data._mem[0]); +} + +String vformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant()); +#endif +#line 0 + + + + +#line 1 "sfwl/object/psignal.h" +#ifndef SIGNAL_H +#define SIGNAL_H + + + +class Signal { +public: + Object *emitter; + Vector params; + Vector static_data; + + template + void connect(T *obj, void (*func)(T*, Signal *)); + template + void disconnect(T *obj, void (*func)(T*, Signal *)); + template + bool is_connected(T *obj, void (*func)(T*, Signal *)); + + void connect_static(void (*func)(Signal *)); + void disconnect_static(void (*func)(Signal *)); + bool is_connected_static(void (*func)(Signal *)); + + void emit(Object *p_emitter); + void emit(Object *p_emitter, const Variant &p1); + void emit(Object *p_emitter, const Variant &p1, const Variant &p2); + void emit(Object *p_emitter, const Variant &p1, const Variant &p2, const Variant &p3); + void emit(Object *p_emitter, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4); + void emit(Object *p_emitter, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4, const Variant &p5); + + Signal(); + ~Signal(); + +protected: + enum SignalEntryType { + SIGNAL_ENTRY_TYPE_NONE = 0, + SIGNAL_ENTRY_TYPE_STATIC = 1, + SIGNAL_ENTRY_TYPE_CLASS = 2, + }; + + struct SignalEntry { + SignalEntryType type; + + virtual void call(Signal *s) { + } + + SignalEntry() { + type = SIGNAL_ENTRY_TYPE_NONE; + } + }; + + struct StaticSignalEntry : public SignalEntry { + void (*func)(Signal *); + + virtual void call(Signal *s) { + func(s); + } + + StaticSignalEntry() { + type = SIGNAL_ENTRY_TYPE_STATIC; + func = nullptr; + } + }; + + struct ClassSignalEntry : public SignalEntry { + + virtual void* get_obj_ptr() { + return nullptr; + } + + virtual void* get_func_ptr() { + return nullptr; + } + + ClassSignalEntry() { + type = SIGNAL_ENTRY_TYPE_CLASS; + } + }; + + template + struct ClassSignalEntrySpec : public ClassSignalEntry { + union { + T* obj; + void* obj_ptr; + }; + union { + void (*func)(T*, Signal *); + void* func_ptr; + }; + + virtual void call(Signal *s) { + func(obj, s); + } + + void* get_obj_ptr() { + return obj_ptr; + } + + void* get_func_ptr() { + return func_ptr; + } + + ClassSignalEntrySpec() { + obj = nullptr; + func = nullptr; + } + }; + +protected: + Vector entries; +}; + +template +void Signal::connect(T *obj, void (*func)(T*, Signal *)) { + ClassSignalEntrySpec *ce = new ClassSignalEntrySpec(); + ce->obj = obj; + ce->func = func; + + entries.push_back(ce); +} + +template +void Signal::disconnect(T *obj, void (*func)(T*, Signal *)) { + ClassSignalEntrySpec t; + t.obj = obj; + t.func = func; + + void* obj_ptr = t.obj_ptr; + void* func_ptr = t.func_ptr; + + for (int i = 0; i < entries.size(); ++i) { + SignalEntry *e = entries[i]; + + if (e->type == SIGNAL_ENTRY_TYPE_CLASS) { + ClassSignalEntry *se = static_cast(e); + + if (se->get_obj_ptr() == obj_ptr && se->get_func_ptr() == func_ptr) { + entries.remove(i); + return; + } + } + } +} + +template +bool Signal::is_connected(T *obj, void (*func)(T*, Signal *)) { + ClassSignalEntrySpec t; + t.obj = obj; + t.func = func; + + void* obj_ptr = t.obj_ptr; + void* func_ptr = t.func_ptr; + + for (int i = 0; i < entries.size(); ++i) { + SignalEntry *e = entries[i]; + + if (e->type == SIGNAL_ENTRY_TYPE_CLASS) { + ClassSignalEntry *se = static_cast(e); + + if (se->get_obj_ptr() == obj_ptr && se->get_func_ptr() == func_ptr) { + return true; + } + } + } + + return false; +} + +#endif +#line 0 + + +#line 1 "sfwl/object/object.h" +#ifndef OBJECT_H +#define OBJECT_H + + + +/*************************************************************************/ +/* object.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +class ObjectRC; + +#define SFW_OBJECT(m_class, m_inherits) \ +private: \ + void operator=(const m_class &p_rval) {} \ + \ +public: \ + virtual String get_class() const override { \ + return String(#m_class); \ + } \ + virtual const StringName *_get_class_namev() const { \ + if (!_class_name) \ + _class_name = get_class_static(); \ + return &_class_name; \ + } \ + static void *get_class_ptr_static() { \ + static int ptr; \ + return &ptr; \ + } \ + static String get_class_static() { \ + return String(#m_class); \ + } \ + static String get_parent_class_static() { \ + return m_inherits::get_class_static(); \ + } \ + static void get_inheritance_list_static(Vector *p_inheritance_list) { \ + m_inherits::get_inheritance_list_static(p_inheritance_list); \ + p_inheritance_list->push_back(String(#m_class)); \ + } \ + static String inherits_static() { \ + return String(#m_inherits); \ + } \ + virtual bool is_class(const String &p_class) const override { \ + return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); \ + } \ + virtual bool is_class_ptr(void *p_ptr) const override { \ + return (p_ptr == get_class_ptr_static()) ? true : m_inherits::is_class_ptr(p_ptr); \ + } \ + static void get_valid_parents_static(Vector *p_parents) { \ + if (m_class::_get_valid_parents_static != m_inherits::_get_valid_parents_static) { \ + m_class::_get_valid_parents_static(p_parents); \ + } \ + m_inherits::get_valid_parents_static(p_parents); \ + } \ + _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { \ + return (void(Object::*)(int)) & m_class::_notification; \ + } \ + virtual void _notificationv(int p_notification, bool p_reversed) { \ + if (!p_reversed) \ + m_inherits::_notificationv(p_notification, p_reversed); \ + if (m_class::_get_notification() != m_inherits::_get_notification()) { \ + _notification(p_notification); \ + } \ + if (p_reversed) \ + m_inherits::_notificationv(p_notification, p_reversed); \ + } \ + _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &) const { \ + return (bool(Object::*)(const StringName &, Variant &) const) & m_class::_get; \ + } \ + virtual bool _getv(const StringName &p_name, Variant &r_ret) const { \ + if (m_class::_get_get() != m_inherits::_get_get()) { \ + if (_get(p_name, r_ret)) \ + return true; \ + } \ + return m_inherits::_getv(p_name, r_ret); \ + } \ + _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) { \ + return (bool(Object::*)(const StringName &, const Variant &)) & m_class::_set; \ + } \ + virtual bool _setv(const StringName &p_name, const Variant &p_property) { \ + if (m_inherits::_setv(p_name, p_property)) \ + return true; \ + if (m_class::_get_set() != m_inherits::_get_set()) { \ + return _set(p_name, p_property); \ + } \ + return false; \ + } \ + \ +private: + +class Object { +public: + enum { + NOTIFICATION_POSTINITIALIZE = 0, + NOTIFICATION_PREDELETE = 1 + }; + + void set(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr); + Variant get(const StringName &p_name, bool *r_valid = nullptr) const; + + //less than. < "operator" used for cutrom sorting Arrays. + virtual bool lt(const Variant &p_value_l, const Variant &p_value_r); + + virtual String get_class() const { return "Object"; } + static void *get_class_ptr_static() { + static int ptr; + return &ptr; + } + + static String get_class_static() { return "Object"; } + static String get_parent_class_static() { return String(); } + + static void get_inheritance_list_static(Vector *p_inheritance_list) { p_inheritance_list->push_back("Object"); } + + virtual bool is_class(const String &p_class) const { return (p_class == "Object"); } + virtual bool is_class_ptr(void *p_ptr) const { return get_class_ptr_static() == p_ptr; } + + static void get_valid_parents_static(Vector *p_parents) {} + static void _get_valid_parents_static(Vector *p_parents) {} + + virtual const StringName *_get_class_namev() const { + if (!_class_name) { + _class_name = get_class_static(); + } + return &_class_name; + } + + _FORCE_INLINE_ const StringName &get_class_name() const { + if (!_class_ptr) { + return *_get_class_namev(); + } else { + return *_class_ptr; + } + } + + ObjectRC *_use_rc(); + + _FORCE_INLINE_ ObjectID get_instance_id() const { + return _instance_id; + } + + void notification(int p_notification, bool p_reversed = false); + virtual String to_string(); + + bool _is_queued_for_deletion; + bool is_queued_for_deletion() const { + return _is_queued_for_deletion; + } + + void cancel_free(); + + bool has_meta(const String &p_name) const; + void set_meta(const String &p_name, const Variant &p_value); + void remove_meta(const String &p_name); + Variant get_meta(const String &p_name, const Variant &p_default = Variant()) const; + void get_meta_list(List *p_list) const; + + Object(); + virtual ~Object(); + + template + static T *cast_to(Object *p_object) { + if (!p_object) + return NULL; + if (p_object->is_class_ptr(T::get_class_ptr_static())) + return static_cast(p_object); + else + return NULL; + } + + template + static const T *cast_to(const Object *p_object) { + if (!p_object) + return NULL; + if (p_object->is_class_ptr(T::get_class_ptr_static())) + return static_cast(p_object); + else + return NULL; + } + +protected: + _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { + return &Object::_notification; + } + + bool _set(const StringName &p_name, const Variant &p_property) { + return false; + }; + bool _get(const StringName &p_name, Variant &r_property) const { + return false; + }; + + virtual bool _setv(const StringName &p_name, const Variant &p_property) { + return false; + }; + virtual bool _getv(const StringName &p_name, Variant &r_property) const { + return false; + }; + + _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &r_ret) const { + return &Object::_get; + } + _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) { + return &Object::_set; + } + + virtual void _notificationv(int p_notification, bool p_reversed){}; + void _notification(int p_notification){}; + + friend bool predelete_handler(Object *); + friend void postinitialize_handler(Object *); + + int _predelete_ok; + bool _predelete(); + void _postinitialize(); + + mutable StringName _class_name; + mutable const StringName *_class_ptr; + + ObjectID _instance_id; + std::atomic _rc; + + Dictionary metadata; +}; + +bool predelete_handler(Object *p_object); +void postinitialize_handler(Object *p_object); + +class ObjectDB { + struct ObjectPtrHash { + static _FORCE_INLINE_ uint32_t hash(const Object *p_obj) { + union { + const Object *p; + unsigned long i; + } u; + u.p = p_obj; + return HashMapHasherDefault::hash((uint64_t)u.i); + } + }; + + static HashMap instances; + static HashMap instance_checks; + + static ObjectID instance_counter; + friend class Object; + friend void unregister_core_types(); + + static RWLock rw_lock; + static void cleanup(); + static ObjectID add_instance(Object *p_object); + static void remove_instance(Object *p_object); + friend void register_core_types(); + +public: + typedef void (*DebugFunc)(Object *p_obj); + + static Object *get_instance(ObjectID p_instance_id); + static void debug_objects(DebugFunc p_func); + static int get_object_count(); + + // This one may give false positives because a new object may be allocated at the same memory of a previously freed one + _FORCE_INLINE_ static bool instance_validate(Object *p_ptr) { + rw_lock.read_lock(); + + bool exists = instance_checks.has(p_ptr); + + rw_lock.read_unlock(); + + return exists; + } +}; + +#endif +#line 0 + + + +#line 1 "sfwl/object/object_rc.h" +#ifndef OBJECTRC_H +#define OBJECTRC_H + +/*************************************************************************/ +/* object_rc.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +class Object; + +// Used to track Variants pointing to a non-Reference Object +class ObjectRC { + std::atomic _ptr; + std::atomic _users; + +public: + // This is for allowing debug builds to check for instance ID validity, + // so warnings are shown in debug builds when a stray Variant (one pointing + // to a released Object) would have happened. + const ObjectID instance_id; + + _FORCE_INLINE_ void increment() { + _users.fetch_add(1, std::memory_order_relaxed); + } + + _FORCE_INLINE_ bool decrement() { + return _users.fetch_sub(1, std::memory_order_relaxed) == 1; + } + + _FORCE_INLINE_ bool invalidate() { + _ptr.store(nullptr, std::memory_order_release); + return decrement(); + } + + _FORCE_INLINE_ Object *get_ptr() { + return _ptr.load(std::memory_order_acquire); + } + + _FORCE_INLINE_ ObjectRC(Object *p_object) : + instance_id(p_object->get_instance_id()) { + // 1 (the Object) + 1 (the first user) + _users.store(2, std::memory_order_relaxed); + _ptr.store(p_object, std::memory_order_release); + } +}; + +#endif +#line 0 + + +#line 1 "sfwl/object/reference.h" +#ifndef REFERENCE_H +#define REFERENCE_H + +/*************************************************************************/ +/* reference.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + + + +class Reference : public Object { + SFW_OBJECT(Reference, Object); + +public: + _FORCE_INLINE_ bool is_referenced() const { return refcount_init.get() != 1; } + bool init_ref(); + bool reference(); // returns false if refcount is at zero and didn't get increased + bool unreference(); + int reference_get_count() const; + + Reference(); + virtual ~Reference(); + +private: + SafeRefCount refcount; + SafeRefCount refcount_init; +}; + +template +class Ref { + T *reference; + + void ref(const Ref &p_from) { + if (p_from.reference == reference) { + return; + } + + unref(); + + reference = p_from.reference; + if (reference) { + reference->reference(); + } + } + + void ref_pointer(T *p_ref) { + //ERR_FAIL_COND(!p_ref); + + if (p_ref->init_ref()) { + reference = p_ref; + } + } + + //virtual Reference * get_reference() const { return reference; } +public: + _FORCE_INLINE_ bool operator==(const T *p_ptr) const { + return reference == p_ptr; + } + _FORCE_INLINE_ bool operator!=(const T *p_ptr) const { + return reference != p_ptr; + } + + _FORCE_INLINE_ bool operator<(const Ref &p_r) const { + return reference < p_r.reference; + } + _FORCE_INLINE_ bool operator==(const Ref &p_r) const { + return reference == p_r.reference; + } + _FORCE_INLINE_ bool operator!=(const Ref &p_r) const { + return reference != p_r.reference; + } + + _FORCE_INLINE_ T *operator->() { + return reference; + } + + _FORCE_INLINE_ T *operator*() { + return reference; + } + + _FORCE_INLINE_ const T *operator->() const { + return reference; + } + + _FORCE_INLINE_ const T *ptr() const { + return reference; + } + _FORCE_INLINE_ T *ptr() { + return reference; + } + + _FORCE_INLINE_ const T *operator*() const { + return reference; + } + + void operator=(const Ref &p_from) { + ref(p_from); + } + + template + void operator=(const Ref &p_from) { + Reference *refb = const_cast(static_cast(p_from.ptr())); + if (!refb) { + unref(); + return; + } + Ref r; + r.reference = Object::cast_to(refb); + ref(r); + r.reference = nullptr; + } + + template + void reference_ptr(T_Other *p_ptr) { + if (reference == p_ptr) { + return; + } + unref(); + + T *r = Object::cast_to(p_ptr); + if (r) { + ref_pointer(r); + } + } + + Ref(const Ref &p_from) { + reference = nullptr; + ref(p_from); + } + + template + Ref(const Ref &p_from) { + reference = nullptr; + Reference *refb = const_cast(static_cast(p_from.ptr())); + if (!refb) { + unref(); + return; + } + Ref r; + r.reference = Object::cast_to(refb); + ref(r); + r.reference = nullptr; + } + + Ref(T *p_reference) { + reference = nullptr; + if (p_reference) { + ref_pointer(p_reference); + } + } + + inline bool is_valid() const { return reference != nullptr; } + inline bool is_null() const { return reference == nullptr; } + + void unref() { + //TODO this should be moved to mutexes, since this engine does not really + // do a lot of referencing on references and stuff + // mutexes will avoid more crashes? + + if (reference && reference->unreference()) { + memdelete(reference); + } + reference = nullptr; + } + + void instance() { + ref(memnew(T)); + } + + RefPtr get_ref_ptr() const { + RefPtr refptr; + Ref *irr = reinterpret_cast *>(refptr.get_data()); + *irr = *this; + return refptr; + }; + + operator Variant() const { + return Variant(get_ref_ptr()); + } + + void operator=(const RefPtr &p_refptr) { + Ref *irr = reinterpret_cast *>(p_refptr.get_data()); + Reference *refb = irr->ptr(); + if (!refb) { + unref(); + return; + } + Ref r; + r.reference = Object::cast_to(refb); + ref(r); + r.reference = nullptr; + } + + void operator=(const Variant &p_variant) { + RefPtr refptr = p_variant; + Ref *irr = reinterpret_cast *>(refptr.get_data()); + Reference *refb = irr->ptr(); + if (!refb) { + unref(); + return; + } + Ref r; + r.reference = Object::cast_to(refb); + ref(r); + r.reference = nullptr; + } + + Ref(const Variant &p_variant) { + RefPtr refptr = p_variant; + Ref *irr = reinterpret_cast *>(refptr.get_data()); + reference = nullptr; + Reference *refb = irr->ptr(); + if (!refb) { + unref(); + return; + } + Ref r; + r.reference = Object::cast_to(refb); + ref(r); + r.reference = nullptr; + } + + Ref(const RefPtr &p_refptr) { + Ref *irr = reinterpret_cast *>(p_refptr.get_data()); + reference = nullptr; + Reference *refb = irr->ptr(); + if (!refb) { + unref(); + return; + } + Ref r; + r.reference = Object::cast_to(refb); + ref(r); + r.reference = nullptr; + } + + Ref() { + reference = nullptr; + } + + ~Ref() { + unref(); + } +}; + +typedef Ref REF; + +/* +class WeakRef : public Reference { + SFW_OBJECT(WeakRef, Reference); + + ObjectID ref; + +protected: + static void _bind_methods(); + +public: + void set_obj(Object *p_object); + void set_ref(const REF &p_ref); + + WeakRef(); +}; +*/ + +#endif +#line 0 + + +#line 1 "sfwl/object/resource.h" +#ifndef RESOURCE_H +#define RESOURCE_H + + + +class Resource : public Reference { + SFW_OBJECT(Resource, Reference); + +public: + Signal changed; + + void emit_changed(); + + virtual Error load(const String &path); + virtual Error save(const String &path); + + Resource(); + virtual ~Resource(); +}; + +#endif +#line 0 + +#endif diff --git a/tools/merger/sfwl/sfwl_full.cpp.inl b/tools/merger/sfwl/sfwl_full.cpp.inl index 774ec7d..dd223de 100644 --- a/tools/merger/sfwl/sfwl_full.cpp.inl +++ b/tools/merger/sfwl/sfwl_full.cpp.inl @@ -15,180 +15,8 @@ #include #include -#ifndef SFW_H -#include "sfw.h" -#endif - -#ifndef SFW_3RD_IMPL -#define SFW_3RD_IMPL - -#if defined(__linux__) -//typedef XID Font; -#define Font The_Font_I_dont_care -#endif - -#include "sfw_3rd.h" - -#undef CursorShape - -#if defined(__linux__) -#undef Font -#undef KEY_ESC -#undef KEY_TICK -#undef KEY_1 -#undef KEY_2 -#undef KEY_3 -#undef KEY_4 -#undef KEY_5 -#undef KEY_6 -#undef KEY_7 -#undef KEY_8 -#undef KEY_9 -#undef KEY_0 -#undef KEY_BS -#undef KEY_TAB -#undef KEY_Q -#undef KEY_W -#undef KEY_E -#undef KEY_R -#undef KEY_T -#undef KEY_Y -#undef KEY_U -#undef KEY_I -#undef KEY_O -#undef KEY_P -#undef KEY_CAPS -#undef KEY_A -#undef KEY_S -#undef KEY_D -#undef KEY_F -#undef KEY_G -#undef KEY_H -#undef KEY_J -#undef KEY_K -#undef KEY_L -#undef KEY_ENTER -#undef KEY_LSHIFT -#undef KEY_Z -#undef KEY_X -#undef KEY_C -#undef KEY_V -#undef KEY_B -#undef KEY_N -#undef KEY_M -#undef KEY_RSHIFT -#undef KEY_UP -#undef KEY_LCTRL -#undef KEY_LALT -#undef KEY_SPACE -#undef KEY_RALT -#undef KEY_RCTRL -#undef KEY_LEFT -#undef KEY_DOWN -#undef KEY_RIGHT -#undef KEY_INS -#undef KEY_HOME -#undef KEY_PGUP -#undef KEY_DEL -#undef KEY_END -#undef KEY_PGDN -#undef KEY_LMETA -#undef KEY_RMETA -#undef KEY_MENU -#undef KEY_PRINT -#undef KEY_PAUSE -#undef KEY_SCROLL -#undef KEY_NUMLOCK -#undef KEY_MINUS -#undef KEY_EQUAL -#undef KEY_LSQUARE -#undef KEY_RSQUARE -#undef KEY_SEMICOLON -#undef KEY_QUOTE -#undef KEY_HASH -#undef KEY_BAR -#undef KEY_COMMA -#undef KEY_DOT -#undef KEY_SLASH -#undef KEY_F1 -#undef KEY_F2 -#undef KEY_F3 -#undef KEY_F4 -#undef KEY_F5 -#undef KEY_F6 -#undef KEY_F7 -#undef KEY_F8 -#undef KEY_F9 -#undef KEY_F10 -#undef KEY_F11 -#undef KEY_F12 -#undef KEY_PAD1 -#undef KEY_PAD2 -#undef KEY_PAD3 -#undef KEY_PAD4 -#undef KEY_PAD5 -#undef KEY_PAD6 -#undef KEY_PAD7 -#undef KEY_PAD8 -#undef KEY_PAD9 -#undef KEY_PAD0 -#undef KEY_PADADD -#undef KEY_PADSUB -#undef KEY_PADMUL -#undef KEY_PADDIV -#undef KEY_PADDOT -#undef KEY_PADENTER -#undef MOUSE_L -#undef MOUSE_M -#undef MOUSE_R -#undef GAMEPAD_CONNECTED -#undef GAMEPAD_A -#undef GAMEPAD_B -#undef GAMEPAD_X -#undef GAMEPAD_Y -#undef GAMEPAD_UP -#undef GAMEPAD_DOWN -#undef GAMEPAD_LEFT -#undef GAMEPAD_RIGHT -#undef GAMEPAD_MENU -#undef GAMEPAD_START -#undef GAMEPAD_LB -#undef GAMEPAD_RB -#undef GAMEPAD_LTHUMB -#undef GAMEPAD_RTHUMB -#undef WINDOW_BLUR -#undef WINDOW_FOCUS -#undef WINDOW_CLOSE -#undef WINDOW_MINIMIZE -#undef WINDOW_MAXIMIZE -#undef WINDOW_FULLSCREEN -#undef WINDOW_WINDOWED -#undef GAMEPAD_LPAD -#undef GAMEPAD_LPAD -#undef GAMEPAD_LPADY -#undef GAMEPAD_RPAD -#undef GAMEPAD_RPAD -#undef GAMEPAD_RPADY -#undef GAMEPAD_LT -#undef GAMEPAD_RT -#undef GAMEPAD_BATTERY -#undef MOUSE -#undef MOUSE -#undef MOUSE_Y -#undef MOUSE_W -#undef TOUCH_X1 -#undef TOUCH_Y1 -#undef TOUCH_X2 -#undef TOUCH_Y2 -#undef WINDOW_RESIZE -#undef WINDOW_RESIZE -#undef WINDOW_RESIZEY -#undef WINDOW_ORIENTATION -#undef WINDOW_BATTERY -#undef GAMEPAD_GUID -#undef GAMEPAD_NAME -#endif - +#ifndef SFWL_H +#include "sfwl.h" #endif //=================== CORE SECTION =================== diff --git a/tools/merger/sfwl/sfwl_full.h.inl b/tools/merger/sfwl/sfwl_full.h.inl index 162b576..012a439 100644 --- a/tools/merger/sfwl/sfwl_full.h.inl +++ b/tools/merger/sfwl/sfwl_full.h.inl @@ -1,5 +1,5 @@ -#ifndef SFW_H -#define SFW_H +#ifndef SFWL_H +#define SFWL_H {{FILEINLINE:tools/merger/sfw_readme.inl.h}} {{FILEINLINE:tools/merger/sfw_linceses.inl.h}}