From 228469c33de5bff7833986bd15ee59209ec94e40 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sat, 13 Dec 2025 13:03:27 +0300 Subject: [PATCH] Added all canvas component classes --- CMakeLists.txt | 11 +- .../DelayUpdateSerializedClassManager.hpp | 48 ++- .../NetObjGenericSerializedClass.hpp | 4 +- .../NetworkingObjects/NetObjManager.cpp | 5 +- .../NetworkingObjects/NetObjManager.hpp | 17 +- .../NetObjManagerTypeList.hpp | 12 +- .../NetworkingObjects/NetObjOrderedList.hpp | 42 +- .../NetworkingObjects/NetObjOwnerPtr.impl.hpp | 1 - .../NetObjTemporaryPtr.impl.hpp | 1 - src/BookmarkManager.cpp | 4 +- .../BrushStrokeCanvasComponent.cpp | 398 ++++++++++++++++++ ...oke.hpp => BrushStrokeCanvasComponent.hpp} | 36 +- src/CanvasComponents/CanvasComponent.cpp | 27 ++ src/CanvasComponents/CanvasComponent.hpp | 28 +- .../CanvasComponentAllocator.cpp | 30 ++ .../CanvasComponentAllocator.hpp | 15 + .../CanvasComponentBrushStroke.cpp | 0 .../CanvasComponentContainer.cpp | 142 ++++++- .../CanvasComponentContainer.hpp | 58 ++- .../EllipseCanvasComponent.cpp | 121 ++++++ .../EllipseCanvasComponent.hpp | 35 ++ src/CanvasComponents/ImageCanvasComponent.cpp | 92 ++++ src/CanvasComponents/ImageCanvasComponent.hpp | 39 ++ .../RectangleCanvasComponent.cpp | 103 +++++ .../RectangleCanvasComponent.hpp | 39 ++ .../TextBoxCanvasComponent.cpp | 111 +++++ .../TextBoxCanvasComponent.hpp | 41 ++ src/DrawingProgram/GridModifyTool.cpp | 4 +- src/GridManager.cpp | 19 +- src/Toolbar.cpp | 7 +- src/World.cpp | 21 +- src/World.hpp | 1 - 32 files changed, 1369 insertions(+), 143 deletions(-) create mode 100644 src/CanvasComponents/BrushStrokeCanvasComponent.cpp rename src/CanvasComponents/{CanvasComponentBrushStroke.hpp => BrushStrokeCanvasComponent.hpp} (55%) create mode 100644 src/CanvasComponents/CanvasComponentAllocator.cpp create mode 100644 src/CanvasComponents/CanvasComponentAllocator.hpp delete mode 100644 src/CanvasComponents/CanvasComponentBrushStroke.cpp create mode 100644 src/CanvasComponents/EllipseCanvasComponent.cpp create mode 100644 src/CanvasComponents/EllipseCanvasComponent.hpp create mode 100644 src/CanvasComponents/ImageCanvasComponent.cpp create mode 100644 src/CanvasComponents/ImageCanvasComponent.hpp create mode 100644 src/CanvasComponents/RectangleCanvasComponent.cpp create mode 100644 src/CanvasComponents/RectangleCanvasComponent.hpp create mode 100644 src/CanvasComponents/TextBoxCanvasComponent.cpp create mode 100644 src/CanvasComponents/TextBoxCanvasComponent.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bc3472..4132eda 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,9 +79,14 @@ set(sources "src/main.cpp" "src/DrawingProgram/EllipseDrawEditTool.cpp" "src/DrawingProgram/RectDrawEditTool.cpp" "src/DrawingProgram/TextBoxEditTool.cpp" - #"src/CanvasComponents/CanvasComponentContainer.cpp" - #"src/CanvasComponents/CanvasComponentBrushStroke.cpp" - #"src/CanvasComponents/CanvasComponent.cpp" + "src/CanvasComponents/CanvasComponentContainer.cpp" + "src/CanvasComponents/CanvasComponentAllocator.cpp" + "src/CanvasComponents/BrushStrokeCanvasComponent.cpp" + "src/CanvasComponents/RectangleCanvasComponent.cpp" + "src/CanvasComponents/TextBoxCanvasComponent.cpp" + "src/CanvasComponents/EllipseCanvasComponent.cpp" + "src/CanvasComponents/ImageCanvasComponent.cpp" + "src/CanvasComponents/CanvasComponent.cpp" "src/DrawComponents/DrawEllipse.cpp" "src/DrawComponents/DrawRectangle.cpp" "src/DrawComponents/DrawImage.cpp" diff --git a/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp b/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp index 8f8326f..dfbf032 100644 --- a/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp +++ b/include/Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp @@ -7,40 +7,47 @@ namespace NetworkingObjects { class DelayUpdateSerializedClassManager { public: - template void register_class(NetObjManagerTypeList& t) { - t.register_class({ - .writeConstructorFuncClient = [&](const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a){client_write_func(std::nullopt, o, a);}, - .readConstructorFuncClient = [&](const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&){delay_read_func_constructor(o, a);}, - .readUpdateFuncClient = [&](const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&){delay_read_func(o, a);}, - .writeConstructorFuncServer = [&](const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a){server_write_func(o, a);}, - .readConstructorFuncServer = [&](const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&){delay_read_func_constructor(o, a);}, + template void register_class(NetObjManager& objMan) { + objMan.register_class({ + .writeConstructorFuncClient = [&](const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a){write_constructor_func(o, a);}, + .readConstructorFuncClient = [&](const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&){read_constructor_func(o, a);}, + .readUpdateFuncClient = [&](const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&){client_read_update_func(o, a);}, + .writeConstructorFuncServer = [&](const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a){write_constructor_func(o, a);}, + .readConstructorFuncServer = [&](const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&){read_constructor_func(o, a);}, .readUpdateFuncServer = [&](const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&){server_read_update_func(o, a);} }); } - template void send_update_to_all(const NetworkingObjects::NetObjTemporaryPtr& o, bool finalUpdate) { + template void send_update_to_all(const NetworkingObjects::NetObjTemporaryPtr& o, const std::function& postAssignmentFunc, bool finalUpdate) { o.send_update_to_all(finalUpdate ? RELIABLE_COMMAND_CHANNEL : UNRELIABLE_COMMAND_CHANNEL, [&](const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a) { if(o.get_obj_man()->is_server()) - server_write_func(o, a); + server_write_func(o, a, postAssignmentFunc); else - client_write_func(finalUpdate, o, a); + client_write_update_func(o, a, postAssignmentFunc, finalUpdate); }); } void update(); private: - template void client_write_func(std::optional finalUpdate, const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a) { - if(finalUpdate.has_value()) - a(finalUpdate.value()); + template void write_constructor_func(const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a) { + a(*o); + } + + template void client_write_update_func(const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a, const std::function& postAssignmentFunc, bool finalUpdate) { + a(finalUpdate); a(*o); auto it = updatingObjs.find(o.get_net_id()); + if(it == updatingObjs.end()) { UpdatingObject toAdd; toAdd.objToUpdateCurrentData = std::static_pointer_cast(std::make_shared(*o)); toAdd.doLastAssignment = std::make_shared(true); toAdd.lastUpdateTimePoint = std::chrono::steady_clock::now(); - toAdd.setDataAfterTimeout = [oWeak = NetObjWeakPtr(o), currentData = toAdd.objToUpdateCurrentData, doLastAssignment = toAdd.doLastAssignment]() { + toAdd.setDataAfterTimeout = [postAssignmentFunc, oWeak = NetObjWeakPtr(o), currentData = toAdd.objToUpdateCurrentData, doLastAssignment = toAdd.doLastAssignment]() { NetObjTemporaryPtr oLock = oWeak.lock(); - if(oLock && *doLastAssignment) + if(oLock && *doLastAssignment) { *oLock = *std::static_pointer_cast(currentData); + if(postAssignmentFunc) + postAssignmentFunc(*oLock); + } }; updatingObjs.emplace(o.get_net_id(), toAdd); } @@ -50,11 +57,11 @@ namespace NetworkingObjects { } } - template void delay_read_func_constructor(const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a) { + template void read_constructor_func(const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a) { a(*o); } - template void delay_read_func(const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a) { + template void client_read_update_func(const NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a) { auto it = updatingObjs.find(o.get_net_id()); if(it == updatingObjs.end()) a(*o); @@ -65,18 +72,21 @@ namespace NetworkingObjects { } } - template void server_write_func(const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a) { + template void server_write_func(const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a, const std::function& postAssignmentFunc) { a(*o); auto it = updatingObjs.find(o.get_net_id()); + if(it == updatingObjs.end()) { UpdatingObject toAdd; toAdd.objToUpdateCurrentData = std::static_pointer_cast(std::make_shared(*o)); toAdd.lastUpdateTimePoint = std::chrono::steady_clock::now(); toAdd.doLastAssignment = std::make_shared(false); - toAdd.setDataAfterTimeout = [oWeak = NetObjWeakPtr(o), currentData = toAdd.objToUpdateCurrentData, doLastAssignment = toAdd.doLastAssignment]() { + toAdd.setDataAfterTimeout = [postAssignmentFunc, oWeak = NetObjWeakPtr(o), currentData = toAdd.objToUpdateCurrentData, doLastAssignment = toAdd.doLastAssignment]() { NetObjTemporaryPtr oLock = oWeak.lock(); if(oLock && *doLastAssignment) { *oLock = *std::static_pointer_cast(currentData); + if(postAssignmentFunc) + postAssignmentFunc(*oLock); oLock.send_server_update_to_all_clients(RELIABLE_COMMAND_CHANNEL, [](const NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a) { a(*o); }); diff --git a/include/Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp b/include/Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp index 2001849..772750e 100644 --- a/include/Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp +++ b/include/Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp @@ -16,8 +16,8 @@ namespace NetworkingObjects { a(*o); } - template void register_generic_serialized_class(NetObjManagerTypeList& t) { - t.register_class({ + template void register_generic_serialized_class(NetObjManager& objMan) { + objMan.register_class({ .writeConstructorFuncClient = default_serialize_write_func, .readConstructorFuncClient = default_serialize_read_constructor_func, .readUpdateFuncClient = default_serialize_read_func_client, diff --git a/include/Helpers/NetworkingObjects/NetObjManager.cpp b/include/Helpers/NetworkingObjects/NetObjManager.cpp index 24390d1..dcc2ad7 100644 --- a/include/Helpers/NetworkingObjects/NetObjManager.cpp +++ b/include/Helpers/NetworkingObjects/NetObjManager.cpp @@ -2,9 +2,8 @@ #include "NetObjTemporaryPtr.hpp" namespace NetworkingObjects { - NetObjManager::NetObjManager(std::shared_ptr initTypeList, bool initIsServer): + NetObjManager::NetObjManager(bool initIsServer): isServer(initIsServer), - typeList(initTypeList), nextTypeID(0) {} bool NetObjManager::is_server() const { @@ -16,7 +15,7 @@ namespace NetworkingObjects { auto it = objectData.find(id); if(it == objectData.end()) return; - typeList->get_net_type_data(isServer, it->second.netTypeID).readUpdateFunc(NetObjTemporaryPtr(this, id, it->second.p), a, clientReceivedFrom); + typeList.get_net_type_data(isServer, it->second.netTypeID).readUpdateFunc(NetObjTemporaryPtr(this, id, it->second.p), a, clientReceivedFrom); } void NetObjManager::set_client(std::shared_ptr initClient, MessageCommandType initUpdateCommandID) { client = initClient; diff --git a/include/Helpers/NetworkingObjects/NetObjManager.hpp b/include/Helpers/NetworkingObjects/NetObjManager.hpp index 738c4bb..516fd86 100644 --- a/include/Helpers/NetworkingObjects/NetObjManager.hpp +++ b/include/Helpers/NetworkingObjects/NetObjManager.hpp @@ -19,7 +19,7 @@ namespace NetworkingObjects { class NetObjManager { public: - NetObjManager(std::shared_ptr initTypeList, bool initIsServer); + NetObjManager(bool initIsServer); void read_update_message(cereal::PortableBinaryInputArchive& a, const std::shared_ptr& clientReceivedFrom); void set_client(std::shared_ptr initClient, MessageCommandType initUpdateCommandID); void set_server(std::shared_ptr initServer, MessageCommandType initUpdateCommandID); @@ -30,8 +30,8 @@ namespace NetworkingObjects { auto it = objectData.find(id); if(it != objectData.end()) throw std::runtime_error("[NetObjManager::read_create_message] Attempted to create an object with a used ID"); - NetObjOwnerPtr newPtr = emplace_raw_ptr(id, static_cast(typeList->get_type_index_data(isServer).allocatorFunc())); - typeList->get_type_index_data(isServer).readConstructorFunc(NetObjTemporaryPtr(newPtr).template cast(), a, clientReceivedFrom); + NetObjOwnerPtr newPtr = emplace_raw_ptr(id, static_cast(typeList.get_type_index_data(isServer).allocatorFunc())); + typeList.get_type_index_data(isServer).readConstructorFunc(NetObjTemporaryPtr(newPtr).template cast(), a, clientReceivedFrom); return newPtr; } template NetObjTemporaryPtr read_get_obj_ref_from_message(cereal::PortableBinaryInputArchive& a) { @@ -43,7 +43,7 @@ namespace NetworkingObjects { return NetObjTemporaryPtr(this, id, std::static_pointer_cast(it->second.p)); } template NetObjOwnerPtr make_obj() { - return emplace_raw_ptr(NetObjID::random_gen(), static_cast(typeList->get_type_index_data(isServer).allocatorFunc())); + return emplace_raw_ptr(NetObjID::random_gen(), static_cast(typeList.get_type_index_data(isServer).allocatorFunc())); } // Don't use this function unless you're sure that class T isn't a base class template NetObjOwnerPtr make_obj_direct(Args&&... items) { @@ -58,9 +58,12 @@ namespace NetworkingObjects { throw std::runtime_error("[NetObjManager::get_obj_from_id] ID doesn't exist"); return NetObjOwnerPtr(this, id, static_cast(it->second.p)); } + template void register_class(const NetObjManagerTypeList::ServerClientClassFunctions& funcs) { + typeList.register_class(funcs); + } private: template NetObjOwnerPtr emplace_raw_ptr(NetObjID id, T* rawPtr) { - if(!objectData.emplace(id, SingleObjectData{.netTypeID = typeList->get_type_index_data(isServer).netTypeID, .p = rawPtr}).second) + if(!objectData.emplace(id, SingleObjectData{.netTypeID = typeList.get_type_index_data(isServer).netTypeID, .p = rawPtr}).second) throw std::runtime_error("[NetObjManager::emplace_raw_ptr] ID Collision"); return NetObjOwnerPtr(this, id, rawPtr); } @@ -122,7 +125,7 @@ namespace NetworkingObjects { template static void write_create_message(const NetObjTemporaryPtr& ptr, cereal::PortableBinaryOutputArchive& a) { a(ptr.get_net_id()); - ptr.get_obj_man()->typeList->template get_type_index_data(ptr.get_obj_man()->isServer).writeConstructorFunc(ptr.template cast(), a); + ptr.get_obj_man()->typeList.template get_type_index_data(ptr.get_obj_man()->isServer).writeConstructorFunc(ptr.template cast(), a); } template friend class NetObjOwnerPtr; @@ -139,7 +142,7 @@ namespace NetworkingObjects { std::shared_ptr server; MessageCommandType updateCommandID; std::unordered_map objectData; - std::shared_ptr typeList; + NetObjManagerTypeList typeList; NetTypeIDType nextTypeID; }; } diff --git a/include/Helpers/NetworkingObjects/NetObjManagerTypeList.hpp b/include/Helpers/NetworkingObjects/NetObjManagerTypeList.hpp index ca98bfb..2fcff17 100644 --- a/include/Helpers/NetworkingObjects/NetObjManagerTypeList.hpp +++ b/include/Helpers/NetworkingObjects/NetObjManagerTypeList.hpp @@ -20,8 +20,7 @@ class NetObjManagerTypeList { template void register_class(const ServerClientClassFunctions& funcs) { typeIndexFunctionsClient[std::type_index(typeid(ClientT*))] = TypeIndexFunctions { .readConstructorFunc = [f = funcs.readConstructorFuncClient](const NetObjTemporaryPtr& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr& c) { - if(f) - f(obj.cast(), message, c); + f(obj.cast(), message, c); }, .writeConstructorFunc = [f = funcs.writeConstructorFuncClient](const NetObjTemporaryPtr& obj, cereal::PortableBinaryOutputArchive& message) { f(obj.cast(), message); @@ -32,8 +31,7 @@ class NetObjManagerTypeList { typeIndexFunctionsServer[std::type_index(typeid(ServerT*))] = TypeIndexFunctions { .readConstructorFunc = [f = funcs.readConstructorFuncServer](const NetObjTemporaryPtr& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr& c) { - if(f) - f(obj.cast(), message, c); + f(obj.cast(), message, c); }, .writeConstructorFunc = [f = funcs.writeConstructorFuncServer](const NetObjTemporaryPtr& obj, cereal::PortableBinaryOutputArchive& message) { f(obj.cast(), message); @@ -44,15 +42,13 @@ class NetObjManagerTypeList { netTypeIDFunctionsClient[netTypeID] = NetTypeIDFunctions { .readUpdateFunc = [f = funcs.readUpdateFuncClient](const NetObjTemporaryPtr& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr& c) { - if(f) - f(obj.cast(), message, c); + f(obj.cast(), message, c); } }; netTypeIDFunctionsServer[netTypeID] = NetTypeIDFunctions { .readUpdateFunc = [f = funcs.readUpdateFuncServer](const NetObjTemporaryPtr& obj, cereal::PortableBinaryInputArchive& message, const std::shared_ptr& c) { - if(f) - f(obj.cast(), message, c); + f(obj.cast(), message, c); } }; diff --git a/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp b/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp index 8c86080..7b66bc9 100644 --- a/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp +++ b/include/Helpers/NetworkingObjects/NetObjOrderedList.hpp @@ -39,38 +39,38 @@ namespace NetworkingObjects { template class NetObjOrderedList { public: // Don't use this function unless you're sure that class T isn't a base class - template NetObjTemporaryPtr emplace_back_direct(const NetObjTemporaryPtr>& l, Args&&... items) { + template const NetObjOrderedListObjectInfoPtr& emplace_back_direct(const NetObjTemporaryPtr>& l, Args&&... items) { NetObjOwnerPtr newObj = l.get_obj_man()->template make_obj_direct(items...); - NetObjTemporaryPtr tempPtr(newObj); - l->insert(l, nullptr, l->size(), std::move(newObj)); - return tempPtr; + return l->insert(l, nullptr, l->size(), std::move(newObj)); } - static void push_back_and_send_create(const NetObjTemporaryPtr>& l, T* newObj) { - l->insert(l, nullptr, l->size(), newObj); + static const NetObjOrderedListObjectInfoPtr& push_back_and_send_create(const NetObjTemporaryPtr>& l, T* newObj) { + NetObjOwnerPtr newObjOwner = l.get_obj_man()->template make_obj(newObj); + return l->insert(l, nullptr, l->size(), std::move(newObjOwner)); } - static void insert_and_send_create(const NetObjTemporaryPtr>& l, uint32_t posToInsertAt, const NetObjTemporaryPtr& newObj) { - l->insert(l, nullptr, posToInsertAt, newObj); + static const NetObjOrderedListObjectInfoPtr& insert_and_send_create(const NetObjTemporaryPtr>& l, uint32_t posToInsertAt, T* newObj) { + NetObjOwnerPtr newObjOwner = l.get_obj_man()->template make_obj(newObj); + return l->insert(l, nullptr, posToInsertAt, std::move(newObjOwner)); } static void erase(const NetObjTemporaryPtr>& l, uint32_t indexToErase) { - return l->erase_by_index(l, indexToErase); + l->erase_by_index(l, indexToErase); } static uint32_t erase(const NetObjTemporaryPtr>& l, const NetObjTemporaryPtr& p) { return l->erase_by_ptr(l, p); } virtual bool contains(const NetObjTemporaryPtr& p) const = 0; virtual uint32_t size() const = 0; - virtual const NetObjOwnerPtr& at(uint32_t index) const = 0; + virtual const NetObjOrderedListObjectInfoPtr& at(uint32_t index) const = 0; virtual bool empty() const = 0; virtual ~NetObjOrderedList() {} protected: - virtual void insert(const NetObjTemporaryPtr>& l, const std::shared_ptr& clientInserting, uint32_t posToInsertAt, NetObjOwnerPtr newObj) = 0; + virtual const NetObjOrderedListObjectInfoPtr& insert(const NetObjTemporaryPtr>& l, const std::shared_ptr& clientInserting, uint32_t posToInsertAt, NetObjOwnerPtr newObj) = 0; virtual void erase_by_index(const NetObjTemporaryPtr>& l, uint32_t indexToErase) = 0; virtual uint32_t erase_by_ptr(const NetObjTemporaryPtr>& l, const NetObjTemporaryPtr& p) = 0; virtual void write_constructor(const NetObjTemporaryPtr>& l, cereal::PortableBinaryOutputArchive& a) = 0; virtual void read_update(const NetObjTemporaryPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) = 0; virtual void read_constructor(const NetObjTemporaryPtr>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr&) = 0; private: - template friend void register_ordered_list_class(NetObjManagerTypeList& t); + template friend void register_ordered_list_class(NetObjManager& t); static void write_constructor_func(const NetObjTemporaryPtr>& l, cereal::PortableBinaryOutputArchive& a) { l->write_constructor(l, a); } @@ -91,14 +91,14 @@ namespace NetworkingObjects { virtual uint32_t size() const override { return data.size(); } - virtual const NetObjOwnerPtr& at(uint32_t index) const override { - return data[index]->obj; + virtual const NetObjOrderedListObjectInfoPtr& at(uint32_t index) const override { + return data[index]; } virtual bool empty() const override { return data.empty(); } protected: - virtual void insert(const NetObjTemporaryPtr>& l, const std::shared_ptr& clientInserting, uint32_t posToInsertAt, NetObjOwnerPtr newObj) override { + virtual const NetObjOrderedListObjectInfoPtr& insert(const NetObjTemporaryPtr>& l, const std::shared_ptr& clientInserting, uint32_t posToInsertAt, NetObjOwnerPtr newObj) override { posToInsertAt = std::min(posToInsertAt, data.size()); auto newObjInfoPtr = std::make_shared>(std::move(newObj), 0); data.insert(data.begin() + posToInsertAt, newObjInfoPtr); @@ -111,6 +111,7 @@ namespace NetworkingObjects { a(ObjPtrOrderedListCommand_StoC::INSERT_SINGLE_REFERENCE, posToInsertAt, newObjInfoPtr->obj.get_net_id()); }); set_positions_for_object_info_vector(data, posToInsertAt); + return data[posToInsertAt]; } virtual void erase_by_index(const NetObjTemporaryPtr>& l, uint32_t indexToErase) override { if(data.empty()) @@ -188,14 +189,14 @@ namespace NetworkingObjects { virtual uint32_t size() const override { return clientData.size(); } - virtual const NetObjOwnerPtr& at(uint32_t index) const override { - return clientData[index]->obj; + virtual const NetObjOrderedListObjectInfoPtr& at(uint32_t index) const override { + return clientData[index]; } virtual bool empty() const override { return clientData.empty(); } protected: - virtual void insert(const NetObjTemporaryPtr>& l, const std::shared_ptr&, uint32_t posToInsertAt, NetObjOwnerPtr newObj) { + virtual const NetObjOrderedListObjectInfoPtr& insert(const NetObjTemporaryPtr>& l, const std::shared_ptr&, uint32_t posToInsertAt, NetObjOwnerPtr newObj) { posToInsertAt = std::min(posToInsertAt, clientData.size()); auto newObjInfoPtr = std::make_shared>(std::move(newObj), 0); clientData.insert(clientData.begin() + posToInsertAt, newObjInfoPtr); @@ -206,6 +207,7 @@ namespace NetworkingObjects { newObjInfoPtr->obj.write_create_message(a); }); set_positions_for_object_info_vector(clientData, posToInsertAt); + return clientData[posToInsertAt]; } virtual void erase_by_index(const NetObjTemporaryPtr>& l, uint32_t indexToErase) override { if(clientData.empty()) @@ -329,8 +331,8 @@ namespace NetworkingObjects { std::unordered_map> clientJustInsertedSyncIgnore; }; - template void register_ordered_list_class(NetObjManagerTypeList& t) { - t.register_class, NetObjOrderedList, NetObjOrderedListClient, NetObjOrderedListServer>({ + template void register_ordered_list_class(NetObjManager& objMan) { + objMan.register_class, NetObjOrderedList, NetObjOrderedListClient, NetObjOrderedListServer>({ .writeConstructorFuncClient = NetObjOrderedList::write_constructor_func, .readConstructorFuncClient = NetObjOrderedList::read_constructor_func, .readUpdateFuncClient = NetObjOrderedList::read_update_func, diff --git a/include/Helpers/NetworkingObjects/NetObjOwnerPtr.impl.hpp b/include/Helpers/NetworkingObjects/NetObjOwnerPtr.impl.hpp index f6e5319..1a0b362 100644 --- a/include/Helpers/NetworkingObjects/NetObjOwnerPtr.impl.hpp +++ b/include/Helpers/NetworkingObjects/NetObjOwnerPtr.impl.hpp @@ -7,7 +7,6 @@ #include "NetObjOwnerPtr.decl.hpp" #include "NetObjTemporaryPtr.hpp" #include "NetObjManager.hpp" -#include "NetObjManagerTypeList.hpp" namespace NetworkingObjects { template NetObjOwnerPtr::NetObjOwnerPtr(): diff --git a/include/Helpers/NetworkingObjects/NetObjTemporaryPtr.impl.hpp b/include/Helpers/NetworkingObjects/NetObjTemporaryPtr.impl.hpp index 4822f2d..e913cb1 100644 --- a/include/Helpers/NetworkingObjects/NetObjTemporaryPtr.impl.hpp +++ b/include/Helpers/NetworkingObjects/NetObjTemporaryPtr.impl.hpp @@ -6,7 +6,6 @@ #include #include "NetObjTemporaryPtr.decl.hpp" #include "NetObjManager.hpp" -#include "NetObjManagerTypeList.hpp" namespace NetworkingObjects { template NetObjTemporaryPtr::NetObjTemporaryPtr(): diff --git a/src/BookmarkManager.cpp b/src/BookmarkManager.cpp index 30c6522..62eb375 100644 --- a/src/BookmarkManager.cpp +++ b/src/BookmarkManager.cpp @@ -32,10 +32,10 @@ void BookmarkManager::remove_bookmark(uint32_t bookmarkIndex) { void BookmarkManager::jump_to_bookmark(uint32_t bookmarkIndex) { auto& b = bookmarks->at(bookmarkIndex); - world.drawData.cam.smooth_move_to(world, b->coords, b->windowSize.cast()); + world.drawData.cam.smooth_move_to(world, b->obj->coords, b->obj->windowSize.cast()); } void BookmarkManager::scale_up(const WorldScalar& scaleUpAmount) { for(uint32_t i = 0; i < bookmarks->size(); i++) - bookmarks->at(i)->scale_up(scaleUpAmount); + bookmarks->at(i)->obj->scale_up(scaleUpAmount); } diff --git a/src/CanvasComponents/BrushStrokeCanvasComponent.cpp b/src/CanvasComponents/BrushStrokeCanvasComponent.cpp new file mode 100644 index 0000000..3efd85a --- /dev/null +++ b/src/CanvasComponents/BrushStrokeCanvasComponent.cpp @@ -0,0 +1,398 @@ +#include "BrushStrokeCanvasComponent.hpp" +#include +#include +#include +#include +#include "Helpers/MathExtras.hpp" +#include "../TimePoint.hpp" +#include +#include +#include "../DrawCollision.hpp" +#include "CanvasComponentContainer.hpp" + +#define AABB_PRECHECK_BVH_LEVELS 2 +#define DEFAULT_SMOOTHNESS 5 + +void BrushStrokeCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const { + a(d->points, d->color, d->hasRoundCaps); +} + +void BrushStrokeCanvasComponent::load(cereal::PortableBinaryInputArchive& a) { + a(d->points, d->color, d->hasRoundCaps); +} + +void BrushStrokeCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const { + a(d->points, d->color, d->hasRoundCaps); +} + +void BrushStrokeCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) { + a(d->points, d->color, d->hasRoundCaps); +} + +CanvasComponent::CompType BrushStrokeCanvasComponent::get_type() const { + return CompType::BRUSHSTROKE; +} + +void BrushStrokeCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const { + SkPaint paint; + paint.setColor4f(SkColor4f{d->color.x(), d->color.y(), d->color.z(), d->color.w()}); + + unsigned mipmapLevel = compContainer ? compContainer->get_mipmap_level(drawData) : 0; + + if(mipmapLevel == 0 || drawData.dontUseDrawProgCache) // check dontUseDrawProgCache, to make sure that SVG screenshots dont use LOD + canvas->drawPath(*brushPath, paint); + else + canvas->drawPath(*brushPathLOD[mipmapLevel - 1], paint); + canvas->restore(); +} + +void BrushStrokeCanvasComponent::initialize_draw_data(DrawingProgram& drawP) { + if(!d->points.empty()) { + std::vector points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS); + auto triangleFunc = [&](Vector2f a, Vector2f b, Vector2f c) { + return false; + }; + + auto brushPathBuilder = std::make_shared(); + auto brushPathLOD0Builder = std::make_shared(); + auto brushPathLOD1Builder = std::make_shared(); + + create_triangles(triangleFunc, points, 0, brushPathBuilder); + create_triangles(triangleFunc, points, 4, brushPathLOD0Builder); + create_triangles(triangleFunc, points, 30, brushPathLOD1Builder); + + brushPath = std::make_shared(brushPathBuilder->detach()); + brushPathLOD[0] = std::make_shared(brushPathLOD0Builder->detach()); + brushPathLOD[1] = std::make_shared(brushPathLOD1Builder->detach()); + } + create_collider(); +} + +std::vector BrushStrokeCanvasComponent::get_wedge_indices(const std::vector& points) const { + if(points.size() < 2) + return {}; + std::vector toRet; + toRet.emplace_back(0); + for(size_t i = 1; i < points.size() - 1; i++) { + Vector2f v1Norm = (points[i - 1].pos - points[i].pos).normalized(); + Vector2f v2Norm = (points[i + 1].pos - points[i].pos).normalized(); + if(v1Norm.dot(v2Norm) > -0.6f) + toRet.emplace_back(i); + } + toRet.emplace_back(points.size() - 1); + return toRet; +} + +std::vector BrushStrokeCanvasComponent::smooth_points(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) const { + std::vector& points = d->points; + + size_t pointsSize = endIndex - beginIndex + 1; + if(pointsSize < 2) + return {points[beginIndex], points[endIndex]}; + + std::vector toRet; + + BrushStrokeCanvasComponentPoint p0 = points[beginIndex]; + p0.pos = p0.pos + p0.width * (p0.pos - points[beginIndex + 1].pos).normalized() * DRAW_MINIMUM_LIMIT; + BrushStrokeCanvasComponentPoint p1 = points[beginIndex]; + BrushStrokeCanvasComponentPoint p2 = points[beginIndex + 1]; + BrushStrokeCanvasComponentPoint p3; + + for(size_t i = beginIndex; i < endIndex; i++) { + + if(i == endIndex - 1) { + p3 = points[i + 1]; + p3.pos = p3.pos + p3.width * (p3.pos - points[i].pos).normalized() * DRAW_MINIMUM_LIMIT; + } + else + p3 = points[i + 2]; + + toRet.emplace_back(p1); + + float tMove = 1.0 / static_cast(numOfDivisions); + for(unsigned d = 1; d < numOfDivisions; d++) { + float t = tMove * static_cast(d); + BrushStrokeCanvasComponentPoint pToAdd; + pToAdd.pos = catmull_rom(p0.pos, p1.pos, p2.pos, p3.pos, t); + pToAdd.width = std::lerp(p1.width, p2.width, t); + if(!(std::isnan(pToAdd.pos.x()) || std::isnan(pToAdd.pos.y()))) // Don't add NAN values + toRet.emplace_back(pToAdd); + } + p0 = p1; + p1 = p2; + p2 = p3; + } + toRet.emplace_back(points[endIndex]); + + return toRet; +} + +std::vector BrushStrokeCanvasComponent::smooth_points_avg(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) const { + std::vector& points = d->points; + + size_t pointsSize = endIndex - beginIndex + 1; + if(pointsSize < 2) + return {points[beginIndex], points[endIndex]}; + + std::vector toRet; + + for(size_t i = beginIndex; i < endIndex; i++) { + const BrushStrokeCanvasComponentPoint& p1 = points[i]; + const BrushStrokeCanvasComponentPoint& p2 = points[i + 1]; + + float tMove = 1.0 / static_cast(numOfDivisions); + for(unsigned d = 0; d < numOfDivisions; d++) { + float t = tMove * static_cast(d); + BrushStrokeCanvasComponentPoint pToAdd; + pToAdd.pos = lerp_vec(p1.pos, p2.pos, t); + pToAdd.width = std::lerp(p1.width, p2.width, t); + if(!(std::isnan(pToAdd.pos.x()) || std::isnan(pToAdd.pos.y()))) // Don't add NAN values + toRet.emplace_back(pToAdd); + } + } + + toRet.emplace_back(points[endIndex]); + + return toRet; +} + +std::vector BrushStrokeCanvasComponent::every_nth_point_include_front_and_back(const std::vector& pts, size_t n) const { + if(pts.size() <= 3 || n == 0) + return pts; + std::vector toRet; + toRet.emplace_back(pts.front()); + for(size_t i = n; i < pts.size() - 1; i += n) + toRet.emplace_back(pts[i]); + toRet.emplace_back(pts.back()); + return toRet; +} + +void BrushStrokeCanvasComponent::create_triangles(const std::function& passTriangleFunc, const std::vector& smoothedPoints, size_t skipVertexCount, std::shared_ptr bPathBuilder) const { + const int ARC_SMOOTHNESS = 10; + const int CIRCLE_SMOOTHNESS = 20; + const std::vector& pointsN = d->points; + std::vector topPoints; + std::vector bottomPoints; + + if(pointsN.size() == 1 && d->hasRoundCaps) { + std::vector circlePoints = gen_circle_points(pointsN.front().pos, pointsN.front().width / 2.0f, CIRCLE_SMOOTHNESS); + for(size_t i = 0; i < circlePoints.size(); i++) { + if(passTriangleFunc(pointsN.front().pos, circlePoints[i], circlePoints[(i + 1) % circlePoints.size()])) return; + topPoints.emplace_back(convert_vec2(circlePoints[i])); + } + } + + if(pointsN.size() < 2) { + if(bPathBuilder && !topPoints.empty()) + bPathBuilder->addPolygon({topPoints.data(), topPoints.size()}, false); + return; + } + + auto points = every_nth_point_include_front_and_back(smoothedPoints, skipVertexCount); + + std::vector wedgeIndices = get_wedge_indices(points); + for(size_t i = 0; i < wedgeIndices.size() - 1; i++) { + + size_t pointsBegin = wedgeIndices[i]; + size_t pointsEnd = wedgeIndices[i + 1]; + + std::array vertArray; + size_t newIndex = 0; + + Vector2f perpPrev = perpendicular_vec2((points[pointsBegin + 1].pos - points[pointsBegin].pos).eval()); + perpPrev.normalize(); + + vertArray[0] = points[pointsBegin].pos + perpPrev * points[pointsBegin].width * 0.5f; + topPoints.emplace_back(convert_vec2(vertArray[0])); + vertArray[1] = points[pointsBegin].pos - perpPrev * points[pointsBegin].width * 0.5f; + bottomPoints.emplace_back(convert_vec2(vertArray[1])); + newIndex = 2; + + if(d->hasRoundCaps && pointsBegin == 0) { + Vector2f arcDirStart = (convert_vec2(vertArray[0]) - points[0].pos).normalized(); + Vector2f arcDirEnd = (convert_vec2(vertArray[1]) - points[0].pos).normalized(); + Vector2f arcDirCenter = (points[0].pos - points[1].pos).normalized(); + std::vector arcPoints = arc_vec(points[0].pos, arcDirStart, arcDirEnd, arcDirCenter, points[0].width * 0.5f, ARC_SMOOTHNESS); + arcPoints.emplace(arcPoints.begin(), convert_vec2(vertArray[0])); + arcPoints.emplace_back(convert_vec2(vertArray[1])); + for(size_t i = 0; i < arcPoints.size() - 1; i++) { + if(passTriangleFunc(points[0].pos, arcPoints[i], arcPoints[i + 1])) return; + bottomPoints.emplace_back(convert_vec2(arcPoints[i])); + } + bottomPoints.emplace_back(convert_vec2(arcPoints.back())); + } + + for(size_t j = pointsBegin + 1; j < pointsEnd; j++) { + if(j >= pointsEnd) + j = pointsEnd - 1; + + Vector2f v1 = points[j - 1].pos - points[j].pos; + Vector2f v2 = points[j].pos - points[j + 1].pos; + Vector2f perp = perpendicular_vec2((v1 + v2).eval()).normalized(); + if(std::isnan(perp.dot(perpPrev))) { + std::cout << "NAN FOUND" << std::endl; + std::cout << v1 << std::endl; + std::cout << v2 << std::endl; + std::cout << perp << std::endl; + std::cout << perpPrev << std::endl; + throw 1; + } + if(perp.dot(perpPrev) < 0.0) + perp = -perp; + + vertArray[newIndex] = points[j].pos + perp * 0.5f * points[j].width; + topPoints.emplace_back(convert_vec2(vertArray[newIndex])); + newIndex = (newIndex + 1) % 3; + if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return; + + vertArray[newIndex] = points[j].pos - perp * 0.5f * points[j].width; + bottomPoints.emplace_back(convert_vec2(vertArray[newIndex])); + newIndex = (newIndex + 1) % 3; + if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return; + + perpPrev = perp; + } + + Vector2f rectDir = points[pointsEnd - 1].pos - points[pointsEnd].pos; + rectDir.normalize(); + Vector2f perp = perpendicular_vec2(rectDir); + if(perp.dot(perpPrev) < 0.0) + perp = -perp; + vertArray[newIndex] = points[pointsEnd].pos + perp * points[pointsEnd].width * 0.5; + topPoints.emplace_back(convert_vec2(vertArray[newIndex])); + newIndex = (newIndex + 1) % 3; + if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return; + + vertArray[newIndex] = points[pointsEnd].pos - perp * points[pointsEnd].width * 0.5; + bottomPoints.emplace_back(convert_vec2(vertArray[newIndex])); + newIndex = (newIndex + 1) % 3; + if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return; + + // Render wedge + if(pointsEnd != points.size() - 1) { + // Find 4 points involved in intersection + Vector2f currentP1 = points[pointsEnd].pos - perp * points[pointsEnd].width * 0.5; + Vector2f currentP2 = points[pointsEnd].pos + perp * points[pointsEnd].width * 0.5; + Vector2f nextRectDir = points[pointsEnd + 1].pos - points[pointsEnd].pos; + nextRectDir.normalize(); + Vector2f nextPerp = perpendicular_vec2(nextRectDir); + Vector2f nextP1 = points[pointsEnd].pos - nextPerp * points[pointsEnd].width * 0.5; + Vector2f nextP2 = points[pointsEnd].pos + nextPerp * points[pointsEnd].width * 0.5; + + // Find center of wedge + Vector2f wedgeCenter = line_line_intersection(currentP1, currentP2, nextP1, nextP2); + + Vector2f wedgeP1 = ((currentP1 - currentP2).dot(nextRectDir)) <= 0.0 ? currentP1 : currentP2; + Vector2f wedgeP2 = ((nextP1 - nextP2).dot(rectDir)) <= 0.0 ? nextP1 : nextP2; + bool isTopWedge = wedgeP1 == convert_vec2(topPoints.back()); + + float arcRadius; + + std::vector arcPoints; + + if(wedgeCenter.x() == std::numeric_limits::max()) { + wedgeCenter = (currentP1 + currentP2) * 0.5; + wedgeP1 = currentP1; + wedgeP2 = currentP2; + arcRadius = points[pointsEnd].width * 0.5; + arcPoints = arc_vec(wedgeCenter, (wedgeP1 - wedgeCenter).normalized(), (wedgeP2 - wedgeCenter).normalized(), (-rectDir).eval(), arcRadius, ARC_SMOOTHNESS); + } + else { + arcRadius = (wedgeCenter - wedgeP1).norm(); + arcPoints = arc_vec(wedgeCenter, (wedgeP1 - wedgeCenter).normalized(), (wedgeP2 - wedgeCenter).normalized(), lerp_vec((-rectDir).eval(), (-nextRectDir).eval(), 0.5).normalized(), arcRadius, ARC_SMOOTHNESS); + } + + arcPoints.emplace(arcPoints.begin(), wedgeP1); + arcPoints.emplace_back(wedgeP2); + for(size_t i = 0; i < arcPoints.size() - 1; i++) { + if(passTriangleFunc(wedgeCenter, arcPoints[i], arcPoints[i + 1])) return; + if(isTopWedge) + topPoints.emplace_back(convert_vec2(arcPoints[i])); + else + bottomPoints.emplace_back(convert_vec2(arcPoints[i])); + } + if(isTopWedge) + topPoints.emplace_back(convert_vec2(arcPoints.back())); + else + bottomPoints.emplace_back(convert_vec2(arcPoints.back())); + } + else if(d->hasRoundCaps) { // It's the last point, cap it off + Vector2f arcDirStart = (convert_vec2(vertArray[(newIndex + 1) % 3]) - points.back().pos).normalized(); + Vector2f arcDirEnd = (convert_vec2(vertArray[(newIndex + 2) % 3]) - points.back().pos).normalized(); + Vector2f arcDirCenter = -rectDir; + std::vector arcPoints = arc_vec(points.back().pos, arcDirStart, arcDirEnd, arcDirCenter, points.back().width * 0.5f, ARC_SMOOTHNESS); + arcPoints.emplace(arcPoints.begin(), convert_vec2(vertArray[(newIndex + 1) % 3])); + arcPoints.emplace_back(convert_vec2(vertArray[(newIndex + 2) % 3])); + for(size_t i = 0; i < arcPoints.size() - 1; i++) { + if(passTriangleFunc(points.back().pos, arcPoints[i], arcPoints[i + 1])) return; + topPoints.emplace_back(convert_vec2(arcPoints[i])); + } + topPoints.emplace_back(convert_vec2(arcPoints.back())); + } + } + + if(bPathBuilder && !topPoints.empty()) { + topPoints.insert(topPoints.begin(), bottomPoints.rbegin(), bottomPoints.rend()); + bPathBuilder->addPolygon({topPoints.data(), topPoints.size()}, false); + } +} + +void BrushStrokeCanvasComponent::create_collider() { + using namespace SCollision; + if(d->points.empty()) + return; + ColliderCollection strokeObjects; + std::vector points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS); + create_triangles([&](Vector2f a, Vector2f b, Vector2f c) { + strokeObjects.triangle.emplace_back(a, b, c); + return false; + }, points, 0, nullptr); + strokeObjects.recalculate_bounds(); + + SCollision::BVHContainer collisionTree; + + collisionTree.calculate_bvh_recursive(strokeObjects, AABB_PRECHECK_BVH_LEVELS + 1); + precheckAABBLevels.clear(); + add_precheck_aabb_level(AABB_PRECHECK_BVH_LEVELS, collisionTree.children); + + bounds = collisionTree.objects.bounds; +} + +void BrushStrokeCanvasComponent::add_precheck_aabb_level(size_t level, const std::vector>& levelArray) { + for(const auto& a : levelArray) { + if(level == 0 || a.children.empty()) + precheckAABBLevels.emplace_back(a.objects.bounds); + else + add_precheck_aabb_level(level - 1, a.children); + } +} + +void BrushStrokeCanvasComponent::update(DrawingProgram& drawP) { +} + +bool BrushStrokeCanvasComponent::collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const { + if(!SCollision::collide(checkAgainst, bounds)) + return false; + + bool deepPrecheck = false; + for(auto& aabb : precheckAABBLevels) { + if(SCollision::collide(checkAgainst, aabb)) { + deepPrecheck = true; + break; + } + } + if(!deepPrecheck && !precheckAABBLevels.empty()) + return false; + + bool toRet = false; + std::vector points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS); + create_triangles([&](Vector2f a, Vector2f b, Vector2f c) { + toRet = SCollision::collide(checkAgainst, SCollision::Triangle{a, b, c}); + return toRet; + }, points, 0, nullptr); + return toRet; +} + +SCollision::AABB BrushStrokeCanvasComponent::get_obj_coord_bounds() const { + return bounds; +} diff --git a/src/CanvasComponents/CanvasComponentBrushStroke.hpp b/src/CanvasComponents/BrushStrokeCanvasComponent.hpp similarity index 55% rename from src/CanvasComponents/CanvasComponentBrushStroke.hpp rename to src/CanvasComponents/BrushStrokeCanvasComponent.hpp index 93848b6..8bb81fd 100644 --- a/src/CanvasComponents/CanvasComponentBrushStroke.hpp +++ b/src/CanvasComponents/BrushStrokeCanvasComponent.hpp @@ -5,42 +5,37 @@ #include #include -struct DrawBrushStrokePoint { +struct BrushStrokeCanvasComponentPoint { Vector2f pos; float width; - template void serialize(Archive& a) { a(pos, width); } }; -class CanvasComponentBrushStroke : public CanvasComponent { +class BrushStrokeCanvasComponent : public CanvasComponent { public: constexpr static float DRAW_MINIMUM_LIMIT = 0.3; - virtual DrawComponentType get_type() const override; - virtual void write_update(cereal::PortableBinaryOutputArchive& a) const override; - virtual void read_update(cereal::PortableBinaryInputArchive& a) override; + virtual CompType get_type() const override; + virtual void save(cereal::PortableBinaryOutputArchive& a) const override; + virtual void load(cereal::PortableBinaryInputArchive& a) override; virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override; virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override; struct Data { - std::vector points; + std::vector points; Vector4f color; bool hasRoundCaps; }; std::shared_ptr d = std::make_shared(); // It's a pointer here since brush strokes cant be edited -#ifndef IS_SERVER - virtual std::shared_ptr copy(DrawingProgram& drawP) const override; - virtual std::shared_ptr deep_copy(DrawingProgram& drawP) const override; - - virtual void draw(SkCanvas* canvas, const DrawData& drawData) override; + private: + virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override; virtual void initialize_draw_data(DrawingProgram& drawP) override; virtual void update(DrawingProgram& drawP) override; - virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) override; + virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; void create_collider(); - virtual void update_from_delayed_ptr(const std::shared_ptr& delayedUpdatePtr) override; virtual SCollision::AABB get_obj_coord_bounds() const override; @@ -48,15 +43,14 @@ class CanvasComponentBrushStroke : public CanvasComponent { SCollision::AABB bounds; std::array, 2> brushPathLOD; - private: - std::vector every_nth_point_include_front_and_back(const std::vector& pts, size_t n); + + std::vector every_nth_point_include_front_and_back(const std::vector& pts, size_t n) const; void add_precheck_aabb_level(size_t level, const std::vector>& levelArray); std::vector> precheckAABBLevels; - void create_triangles(const std::function& passTriangleFunc, const std::vector& smoothedPoints, size_t skipVertexCount, std::shared_ptr bPath); - std::vector get_wedge_indices(const std::vector& points); - std::vector smooth_points(size_t beginIndex, size_t endIndex, unsigned numOfDivisions); - std::vector smooth_points_avg(size_t beginIndex, size_t endIndex, unsigned numOfDivisions); -#endif + void create_triangles(const std::function& passTriangleFunc, const std::vector& smoothedPoints, size_t skipVertexCount, std::shared_ptr bPath) const; + std::vector get_wedge_indices(const std::vector& points) const; + std::vector smooth_points(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) const; + std::vector smooth_points_avg(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) const; }; diff --git a/src/CanvasComponents/CanvasComponent.cpp b/src/CanvasComponents/CanvasComponent.cpp index e69de29..b1fd319 100644 --- a/src/CanvasComponents/CanvasComponent.cpp +++ b/src/CanvasComponents/CanvasComponent.cpp @@ -0,0 +1,27 @@ +#include "CanvasComponent.hpp" +#include "BrushStrokeCanvasComponent.hpp" +#include "ImageCanvasComponent.hpp" +#include "EllipseCanvasComponent.hpp" +#include "RectangleCanvasComponent.hpp" +#include "TextBoxCanvasComponent.hpp" + +void CanvasComponent::update(DrawingProgram& drawP) { +} + +CanvasComponent* CanvasComponent::allocate_comp(CanvasComponent::CompType type) { + switch(type) { + case CanvasComponent::CompType::BRUSHSTROKE: + return new BrushStrokeCanvasComponent; + case CanvasComponent::CompType::RECTANGLE: + return new RectangleCanvasComponent; + case CanvasComponent::CompType::ELLIPSE: + return new EllipseCanvasComponent; + case CanvasComponent::CompType::TEXTBOX: + return new TextBoxCanvasComponent; + case CanvasComponent::CompType::IMAGE: + return new ImageCanvasComponent; + } + return nullptr; +} + +CanvasComponent::~CanvasComponent() {} diff --git a/src/CanvasComponents/CanvasComponent.hpp b/src/CanvasComponents/CanvasComponent.hpp index 8808aad..7b81f46 100644 --- a/src/CanvasComponents/CanvasComponent.hpp +++ b/src/CanvasComponents/CanvasComponent.hpp @@ -2,6 +2,12 @@ #include #include #include +#include +#include + +class DrawingProgram; +class DrawData; +class CanvasComponentContainer; class CanvasComponent { public: @@ -12,9 +18,23 @@ class CanvasComponent { TEXTBOX, IMAGE }; + virtual CompType get_type() const = 0; + virtual ~CanvasComponent(); + static CanvasComponent* allocate_comp(CanvasComponent::CompType type); + virtual void save(cereal::PortableBinaryOutputArchive& a) const = 0; + virtual void load(cereal::PortableBinaryInputArchive& a) = 0; + virtual void save_file(cereal::PortableBinaryOutputArchive& a) const = 0; + virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) = 0; + protected: - virtual void write_update() const; - virtual void read_update(); - virtual void save_file(cereal::PortableBinaryOutputArchive& a) const; - virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version); + friend class CanvasComponentContainer; + + virtual void draw(SkCanvas* canvas, const DrawData& drawData) const = 0; + virtual void initialize_draw_data(DrawingProgram& drawP) = 0; + virtual void update(DrawingProgram& drawP); + virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const = 0; + + virtual SCollision::AABB get_obj_coord_bounds() const = 0; + + CanvasComponentContainer* compContainer = nullptr; }; diff --git a/src/CanvasComponents/CanvasComponentAllocator.cpp b/src/CanvasComponents/CanvasComponentAllocator.cpp new file mode 100644 index 0000000..8615c8f --- /dev/null +++ b/src/CanvasComponents/CanvasComponentAllocator.cpp @@ -0,0 +1,30 @@ +#include "CanvasComponentAllocator.hpp" + +CanvasComponentAllocator::CanvasComponentAllocator() {} + +CanvasComponentAllocator::CanvasComponentAllocator(CanvasComponent::CompType typeToAllocate) { + comp = std::unique_ptr(CanvasComponent::allocate_comp(typeToAllocate)); +} + +void CanvasComponentAllocator::register_class(NetworkingObjects::NetObjManager& objMan) { + objMan.register_class({ + .writeConstructorFuncClient = write_constructor_func, + .readConstructorFuncClient = read_constructor_func, + .readUpdateFuncClient = nullptr, + .writeConstructorFuncServer = write_constructor_func, + .readConstructorFuncServer = read_constructor_func, + .readUpdateFuncServer = nullptr, + }); +} + +void CanvasComponentAllocator::write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a) { + a(o->comp->get_type()); + a(*o->comp); +} + +void CanvasComponentAllocator::read_constructor_func(const NetworkingObjects::NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) { + CanvasComponent::CompType typeToAllocate; + a(typeToAllocate); + o->comp = std::unique_ptr(CanvasComponent::allocate_comp(typeToAllocate)); + a(*o->comp); +} diff --git a/src/CanvasComponents/CanvasComponentAllocator.hpp b/src/CanvasComponents/CanvasComponentAllocator.hpp new file mode 100644 index 0000000..0a53d46 --- /dev/null +++ b/src/CanvasComponents/CanvasComponentAllocator.hpp @@ -0,0 +1,15 @@ +#pragma once +#include +#include "CanvasComponent.hpp" +#include + +class CanvasComponentAllocator { + public: + CanvasComponentAllocator(); + CanvasComponentAllocator(CanvasComponent::CompType typeToAllocate); + std::unique_ptr comp; + static void register_class(NetworkingObjects::NetObjManager& objMan); + private: + static void write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a); + static void read_constructor_func(const NetworkingObjects::NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c); +}; diff --git a/src/CanvasComponents/CanvasComponentBrushStroke.cpp b/src/CanvasComponents/CanvasComponentBrushStroke.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/src/CanvasComponents/CanvasComponentContainer.cpp b/src/CanvasComponents/CanvasComponentContainer.cpp index 48a218f..aea0752 100644 --- a/src/CanvasComponents/CanvasComponentContainer.cpp +++ b/src/CanvasComponents/CanvasComponentContainer.cpp @@ -1,41 +1,143 @@ #include "CanvasComponentContainer.hpp" +#include "CanvasComponentAllocator.hpp" +#include "../DrawData.hpp" +#include "../DrawingProgram/DrawingProgram.hpp" + +using namespace NetworkingObjects; CanvasComponentContainer::CanvasComponentContainer() { } -CanvasComponentContainer::CanvasComponentContainer(CanvasComponent::CompType type) { - allocate_comp(type); +CanvasComponentContainer::CanvasComponentContainer(NetworkingObjects::NetObjManager& objMan, CanvasComponent::CompType type) { + compAllocator = objMan.make_obj_direct(type); } -void CanvasComponentContainer::register_class(NetworkingObjects::NetObjManagerTypeList& t) { - t.register_class({ +void CanvasComponentContainer::register_class(NetObjManager& objMan) { + objMan.register_class({ + .writeConstructorFuncClient = write_constructor_func, + .readConstructorFuncClient = read_constructor_func, + .readUpdateFuncClient = nullptr, + .writeConstructorFuncServer = write_constructor_func, + .readConstructorFuncServer = read_constructor_func, + .readUpdateFuncServer = nullptr, }); } -std::unique_ptr CanvasComponentContainer::allocate_comp(CanvasComponent::CompType type) { - switch(type) { - case CanvasComponent::CompType::BRUSHSTROKE: - return std::make_unique(); - case CanvasComponent::CompType::RECTANGLE: - return std::make_unique(); - case CanvasComponent::CompType::ELLIPSE: - return std::make_unique(); - case CanvasComponent::CompType::TEXTBOX: - return std::make_unique(); - case CanvasComponent::CompType::IMAGE: - return std::make_unique(); - } +void CanvasComponentContainer::write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a) { + a(o->coords); + o->compAllocator.write_create_message(a); +} + +void CanvasComponentContainer::read_constructor_func(const NetworkingObjects::NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c) { + a(o->coords); + o->compAllocator = o.get_obj_man()->read_create_message(a, c); +} + +void CanvasComponentContainer::delay_post_assignment_update(DrawingProgram& drawP, CanvasComponent& comp) { + comp.compContainer->commit_update(drawP); } void CanvasComponentContainer::save_file(cereal::PortableBinaryOutputArchive& a) const { + a(coords); + get_comp().save_file(a); } void CanvasComponentContainer::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) { + a(coords); + get_comp().load_file(a, version); } -const std::unique_ptr& CanvasComponentContainer::get_comp() { - return comp; +void CanvasComponentContainer::draw(SkCanvas* canvas, const DrawData& drawData) const { + canvas->save(); + canvas_do_transform(canvas, calculate_draw_transform(drawData)); + get_comp().draw(canvas, drawData); + canvas->restore(); } -const CoordSpaceHelper& CanvasComponentContainer::get_coords() { +void CanvasComponentContainer::commit_update(DrawingProgram& drawP) { + //if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt + // drawP.preupdate_component(objInfo); + get_comp().initialize_draw_data(drawP); + calculate_world_bounds(); + //drawP.preupdate_component(objInfo); +} + +void CanvasComponentContainer::commit_transform(DrawingProgram& drawP) { + //if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt + // drawP.preupdate_component(objInfo); + calculate_world_bounds(); + //drawP.preupdate_component(objInfo); +} + +void CanvasComponentContainer::commit_update_dont_invalidate_cache(DrawingProgram& drawP) { + get_comp().initialize_draw_data(drawP); + calculate_world_bounds(); +} + +bool CanvasComponentContainer::collides_with_world_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection& checkAgainstWorld) const { + return collides_with(camCoords, checkAgainstWorld, camCoords.world_collider_to_coords>(checkAgainstWorld)); +} + +bool CanvasComponentContainer::collides_with_cam_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection& checkAgainstCam) const { + return collides_with(camCoords, camCoords.collider_to_world>(checkAgainstCam), checkAgainstCam); +} + +// We could just send one of the checkAgainst colliders, since they represent the same thing, but sending both saves on redundant transformations, since we can save both of the versions on the executor side +bool CanvasComponentContainer::collides_with(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection& checkAgainstWorld, const SCollision::ColliderCollection& checkAgainstCam) const { + if((camCoords.inverseScale << COMP_MAX_SHIFT_BEFORE_STOP_COLLISIONS) < coords.inverseScale) // Object is too large, just dismiss the collision + return false; + else if((camCoords.inverseScale >> COMP_COLLIDE_MIN_SHIFT_TINY) >= coords.inverseScale) + return SCollision::collide(checkAgainstCam, camCoords.to_space(worldAABB.value().min)); + else { + auto c = checkAgainstWorld.transform( + [&coords = coords](const WorldVec& a) { + return coords.to_space(a); + }, + [&coords = coords](const WorldScalar& a) { + return coords.scalar_to_space(a); + }); + return get_comp().collides_within_coords(c); + } +} + +void CanvasComponentContainer::canvas_do_transform(SkCanvas* canvas, const TransformDrawData& transformData) const { + canvas->scale(transformData.scale, transformData.scale); + canvas->rotate(transformData.rotation); + canvas->translate(transformData.translation.x(), transformData.translation.y()); +} + +bool CanvasComponentContainer::should_draw(const DrawData& drawData) const { + return (!drawData.clampDrawBetween || (drawData.clampDrawMinimum < coords.inverseScale)) && SCollision::collide(worldAABB.value(), drawData.cam.viewingAreaGenerousCollider); +} + +CanvasComponentContainer::TransformDrawData CanvasComponentContainer::calculate_draw_transform(const DrawData& drawData) const { + TransformDrawData toRet; + toRet.translation = -coords.to_space(drawData.cam.c.pos); + toRet.rotation = (coords.rotation - drawData.cam.c.rotation) * 180.0 / std::numbers::pi; + toRet.scale = std::min(static_cast(coords.inverseScale / drawData.cam.c.inverseScale), static_cast(1 << COMP_MAX_SHIFT_BEFORE_STOP_SCALING)); + return toRet; +} + +unsigned CanvasComponentContainer::get_mipmap_level(const DrawData& drawData) const { + if(drawData.mipMapLevelTwo > coords.inverseScale) + return 2; + else if(drawData.mipMapLevelOne > coords.inverseScale) + return 1; + else + return 0; +} + +void CanvasComponentContainer::calculate_world_bounds() { + worldAABB = coords.collider_to_world, SCollision::AABB>(get_comp().get_obj_coord_bounds()); +} + +std::optional> CanvasComponentContainer::get_world_bounds() const { + return worldAABB; +} + +CanvasComponent& CanvasComponentContainer::get_comp() const { + return *compAllocator->comp; +} + +const CoordSpaceHelper& CanvasComponentContainer::get_coords() const { return coords; } diff --git a/src/CanvasComponents/CanvasComponentContainer.hpp b/src/CanvasComponents/CanvasComponentContainer.hpp index a1c4ecd..d0729fd 100644 --- a/src/CanvasComponents/CanvasComponentContainer.hpp +++ b/src/CanvasComponents/CanvasComponentContainer.hpp @@ -3,18 +3,64 @@ #include "CanvasComponent.hpp" #include #include "../VersionConstants.hpp" +#include "Helpers/NetworkingObjects/NetObjOrderedList.hpp" +#include + +class DrawingProgram; +class CanvasComponentAllocator; class CanvasComponentContainer { public: + typedef NetworkingObjects::NetObjOrderedListObjectInfo ObjInfo; + typedef std::shared_ptr ObjInfoSharedPtr; + typedef std::weak_ptr ObjInfoWeakPtr; + + constexpr static int COMP_MAX_SHIFT_BEFORE_STOP_COLLISIONS = 14; + constexpr static int COMP_MAX_SHIFT_BEFORE_STOP_SCALING = 14; + constexpr static int COMP_MIN_SHIFT_BEFORE_DISAPPEAR = 11; + constexpr static int COMP_COLLIDE_MIN_SHIFT_TINY = 9; + constexpr static int COMP_MIPMAP_LEVEL_ONE = 2; + constexpr static int COMP_MIPMAP_LEVEL_TWO = 5; + CanvasComponentContainer(); - CanvasComponentContainer(CanvasComponent::CompType type); - static void register_class(NetworkingObjects::NetObjManagerTypeList& t); + CanvasComponentContainer(NetworkingObjects::NetObjManager& objMan, CanvasComponent::CompType type); + static void register_class(NetworkingObjects::NetObjManager& t); + static void delay_post_assignment_update(DrawingProgram& drawP, CanvasComponent& comp); void save_file(cereal::PortableBinaryOutputArchive& a) const; void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version); - const std::unique_ptr& get_comp(); - const CoordSpaceHelper& get_coords(); + CanvasComponent& get_comp() const; + const CoordSpaceHelper& get_coords() const; + std::optional> get_world_bounds() const; + void draw(SkCanvas* canvas, const DrawData& drawData) const; + void commit_update(DrawingProgram& drawP); + void commit_transform(DrawingProgram& drawP); + void commit_update_dont_invalidate_cache(DrawingProgram& drawP); // Must be thread safe + bool should_draw(const DrawData& drawData) const; + bool collides_with_world_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection& checkAgainstWorld) const; + bool collides_with_cam_coords(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection& checkAgainstCam) const; + bool collides_with(const CoordSpaceHelper& camCoords, const SCollision::ColliderCollection& checkAgainstWorld, const SCollision::ColliderCollection& checkAgainstCam) const; private: - std::unique_ptr allocate_comp(CanvasComponent::CompType type); - std::unique_ptr comp; + friend class BrushStrokeCanvasComponent; + friend class ImageCanvasComponent; + + static void write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a); + static void read_constructor_func(const NetworkingObjects::NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c); + + struct TransformDrawData { + Vector2f translation; + float rotation; + float scale; + }; + + unsigned get_mipmap_level(const DrawData& drawData) const; + CanvasComponent* allocate_comp(CanvasComponent::CompType type); + void canvas_do_transform(SkCanvas* canvas, const TransformDrawData& transformData) const; + TransformDrawData calculate_draw_transform(const DrawData& drawData) const; + void calculate_world_bounds(); + + ObjInfo* objInfo = nullptr; + + std::optional> worldAABB; + NetworkingObjects::NetObjOwnerPtr compAllocator; CoordSpaceHelper coords; }; diff --git a/src/CanvasComponents/EllipseCanvasComponent.cpp b/src/CanvasComponents/EllipseCanvasComponent.cpp new file mode 100644 index 0000000..4cf97e4 --- /dev/null +++ b/src/CanvasComponents/EllipseCanvasComponent.cpp @@ -0,0 +1,121 @@ +#include "EllipseCanvasComponent.hpp" +#include "Helpers/ConvertVec.hpp" +#include "Helpers/SCollision.hpp" +#include + +#ifndef IS_SERVER +#include "../DrawCollision.hpp" +#endif + +CanvasComponent::CompType EllipseCanvasComponent::get_type() const { + return CompType::ELLIPSE; +} + +void EllipseCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const { + a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode); +} + +void EllipseCanvasComponent::load(cereal::PortableBinaryInputArchive& a) { + a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode); +} + +void EllipseCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const { + a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode); +} + +void EllipseCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) { + a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode); +} + +void EllipseCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const { + SkPaint p; + if(d.fillStrokeMode == 0 || d.fillStrokeMode == 2) { + p.setStyle(SkPaint::kFill_Style); + p.setColor4f(convert_vec4(d.fillColor)); + canvas->drawPath(ellipsePath, p); + } + if(d.fillStrokeMode == 1 || d.fillStrokeMode == 2) { + p.setStyle(SkPaint::kStroke_Style); + p.setColor4f(convert_vec4(d.strokeColor)); + p.setStrokeWidth(d.strokeWidth); + canvas->drawPath(ellipsePath, p); + } +} + +void EllipseCanvasComponent::create_draw_data() { + SkPathBuilder ellipsePathBuilder; + SkRect newRect = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y()); + ellipsePathBuilder.addOval(newRect); + ellipsePath = ellipsePathBuilder.detach(); +} + +void EllipseCanvasComponent::initialize_draw_data(DrawingProgram& drawP) { + create_draw_data(); + create_collider(); +} + +bool EllipseCanvasComponent::collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const { + return collisionTree.is_collide(checkAgainst); +} + +void EllipseCanvasComponent::create_collider() { + using namespace SCollision; + ColliderCollection strokeObjects; + + if(d.fillStrokeMode == 0) { + Vector2f ellipseCenter = (d.p1 + d.p2) * 0.5; + float a = ellipseCenter.x() - d.p1.x(); + float b = ellipseCenter.y() - d.p1.y(); + Vector2f prevPoint = ellipseCenter + Vector2f{a * std::cos(0), b * std::sin(0)}; + unsigned numOfSegments = 20; + float segmentStep = 1.0 / numOfSegments; + float t = 0.0; + for(unsigned i = 0; i <= numOfSegments; i++) { + Vector2f nextPoint = ellipseCenter + Vector2f{a * std::cos(t), b * std::sin(t)}; + strokeObjects.triangle.emplace_back(ellipseCenter, prevPoint, nextPoint); + prevPoint = nextPoint; + t += segmentStep * std::numbers::pi * 2.0; + } + } + else if(d.fillStrokeMode == 1) { + std::vector points; + + Vector2f ellipseCenter = (d.p1 + d.p2) * 0.5; + float a = ellipseCenter.x() - d.p1.x(); + float b = ellipseCenter.y() - d.p1.y(); + unsigned numOfSegments = 40; + float segmentStep = 1.0 / numOfSegments; + float t = 0.0; + for(unsigned i = 0; i < numOfSegments; i++) { + points.emplace_back(ellipseCenter + Vector2f{a * std::cos(t), b * std::sin(t)}); + t += segmentStep * std::numbers::pi * 2.0; + } + generate_polyline(strokeObjects, points, d.strokeWidth, true); + + } + else if(d.fillStrokeMode == 2) { + float strokeRadius = d.strokeWidth * 0.5f; + Vector2f wNewP1 = d.p1 - Vector2f{strokeRadius, strokeRadius}; + Vector2f wNewP2 = d.p2 + Vector2f{strokeRadius, strokeRadius}; + Vector2f ellipseCenter = (wNewP1 + wNewP2) * 0.5; + float a = ellipseCenter.x() - wNewP1.x(); + float b = ellipseCenter.y() - wNewP1.y(); + Vector2f prevPoint = ellipseCenter + Vector2f{a * std::cos(0), b * std::sin(0)}; + unsigned numOfSegments = 20; + float segmentStep = 1.0 / numOfSegments; + float t = 0.0; + for(unsigned i = 0; i <= numOfSegments; i++) { + Vector2f nextPoint = ellipseCenter + Vector2f{a * std::cos(t), b * std::sin(t)}; + strokeObjects.triangle.emplace_back(ellipseCenter, prevPoint, nextPoint); + prevPoint = nextPoint; + t += segmentStep * std::numbers::pi * 2.0; + } + } + + collisionTree.clear(); + collisionTree.calculate_bvh_recursive(strokeObjects); +} + +SCollision::AABB EllipseCanvasComponent::get_obj_coord_bounds() const { + return collisionTree.objects.bounds; +} diff --git a/src/CanvasComponents/EllipseCanvasComponent.hpp b/src/CanvasComponents/EllipseCanvasComponent.hpp new file mode 100644 index 0000000..ba36fa8 --- /dev/null +++ b/src/CanvasComponents/EllipseCanvasComponent.hpp @@ -0,0 +1,35 @@ +#pragma once +#include "CanvasComponent.hpp" +#include "../CoordSpaceHelper.hpp" +#include + +class EllipseCanvasComponent : public CanvasComponent { + public: + virtual CompType get_type() const override; + virtual void save(cereal::PortableBinaryOutputArchive& a) const override; + virtual void load(cereal::PortableBinaryInputArchive& a) override; + virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override; + virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override; + + // User input data + struct Data { + Vector4f strokeColor; + Vector4f fillColor; + float strokeWidth; + Vector2f p1; + Vector2f p2; + uint8_t fillStrokeMode; + bool operator==(const Data& o) const = default; + } d; + + private: + void create_draw_data(); + void create_collider(); + virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override; + virtual void initialize_draw_data(DrawingProgram& drawP) override; + virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; + virtual SCollision::AABB get_obj_coord_bounds() const override; + void create_triangles(const std::function& passTriangleFunc); + SCollision::BVHContainer collisionTree; + SkPath ellipsePath; +}; diff --git a/src/CanvasComponents/ImageCanvasComponent.cpp b/src/CanvasComponents/ImageCanvasComponent.cpp new file mode 100644 index 0000000..b7bc691 --- /dev/null +++ b/src/CanvasComponents/ImageCanvasComponent.cpp @@ -0,0 +1,92 @@ +#include "ImageCanvasComponent.hpp" +#include "Helpers/ConvertVec.hpp" +#include "Helpers/MathExtras.hpp" +#include "Helpers/SCollision.hpp" +#include "../SharedTypes.hpp" +#include "../World.hpp" +#include "../MainProgram.hpp" +#include +#include "../DrawCollision.hpp" +#include "CanvasComponentContainer.hpp" + +CanvasComponent::CompType ImageCanvasComponent::get_type() const { + return CompType::IMAGE; +} + +void ImageCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const { + a(d.p1, d.p2, d.imageID); +} + +void ImageCanvasComponent::load(cereal::PortableBinaryInputArchive& a) { + a(d.p1, d.p2, d.imageID); +} + +void ImageCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const { + a(d.p1, d.p2, d.imageID); +} + +void ImageCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) { + a(d.p1, d.p2, d.imageID); +} + +void ImageCanvasComponent::draw_download_progress_bar(SkCanvas* canvas, const DrawData& drawData, float progress) const { + if(progress == 0.0f) + return; + if(compContainer->should_draw(drawData)) { + canvas->save(); + compContainer->canvas_do_transform(canvas, compContainer->calculate_draw_transform(drawData)); + SkPaint p; + p.setStroke(true); + p.setStrokeWidth(10.0f); + p.setStrokeCap(SkPaint::kRound_Cap); + p.setColor4f(drawData.main->toolbar.io->theme->fillColor1); + Vector2f center = convert_vec2(imRect.center()); + + const float DOWNLOAD_ARC_RADIUS = 50.0f; + canvas->drawArc(SkRect::MakeLTRB(center.x() - DOWNLOAD_ARC_RADIUS, center.y() - DOWNLOAD_ARC_RADIUS, center.x() + DOWNLOAD_ARC_RADIUS, center.y() + DOWNLOAD_ARC_RADIUS), 0.0f, progress * 360.0f, false, p); + canvas->restore(); + } +} + +void ImageCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const { + ResourceDisplay* display = drawData.rMan->get_display_data(d.imageID); + if(display) + display->draw(canvas, drawData, imRect); + else + canvas->drawRect(imRect, SkPaint({0.5f, 0.5f, 0.5f, 0.5f})); +} + +void ImageCanvasComponent::update(DrawingProgram& drawP) { + ResourceDisplay* display = drawP.world.drawData.rMan->get_display_data(d.imageID); + if(display) { + //if(display->update_draw()) + // drawP.invalidate_cache_at_component(compContainer->objInfo); + } +} + +void ImageCanvasComponent::initialize_draw_data(DrawingProgram& drawP) { + create_draw_data(); + create_collider(); +} + +bool ImageCanvasComponent::collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const { + return collisionTree.is_collide(checkAgainst); +} + +void ImageCanvasComponent::create_draw_data() { + imRect = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y()); +} + +void ImageCanvasComponent::create_collider() { + using namespace SCollision; + ColliderCollection strokeObjects; + std::array newT = triangle_from_rect_points(d.p1, d.p2); + strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]); + strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]); + collisionTree.clear(); + collisionTree.calculate_bvh_recursive(strokeObjects); +} + +SCollision::AABB ImageCanvasComponent::get_obj_coord_bounds() const { + return collisionTree.objects.bounds; +} diff --git a/src/CanvasComponents/ImageCanvasComponent.hpp b/src/CanvasComponents/ImageCanvasComponent.hpp new file mode 100644 index 0000000..85ecdb2 --- /dev/null +++ b/src/CanvasComponents/ImageCanvasComponent.hpp @@ -0,0 +1,39 @@ +#pragma once +#include "../SharedTypes.hpp" +#include "CanvasComponent.hpp" +#include "../CoordSpaceHelper.hpp" +#include + +class ImageCanvasComponent : public CanvasComponent { + public: + virtual void save(cereal::PortableBinaryOutputArchive& a) const override; + virtual void load(cereal::PortableBinaryInputArchive& a) override; + virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override; + virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override; + virtual CompType get_type() const override; + + void draw_download_progress_bar(SkCanvas* canvas, const DrawData& drawData, float progress) const; + + // User input data + struct Data { + Vector2f p1; + Vector2f p2; + + ServerClientID imageID; + } d; + + private: + virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override; + virtual void initialize_draw_data(DrawingProgram& drawP) override; + virtual void update(DrawingProgram& drawP) override; + virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; + void create_draw_data(); + void create_collider(); + + virtual SCollision::AABB get_obj_coord_bounds() const override; + + SkRect imRect; + + SCollision::BVHContainer collisionTree; +}; + diff --git a/src/CanvasComponents/RectangleCanvasComponent.cpp b/src/CanvasComponents/RectangleCanvasComponent.cpp new file mode 100644 index 0000000..de95446 --- /dev/null +++ b/src/CanvasComponents/RectangleCanvasComponent.cpp @@ -0,0 +1,103 @@ +#include "RectangleCanvasComponent.hpp" +#include "Helpers/ConvertVec.hpp" +#include "Helpers/MathExtras.hpp" +#include "Helpers/SCollision.hpp" +#include +#include +#include "../SharedTypes.hpp" +#include "../DrawCollision.hpp" + +CanvasComponent::CompType RectangleCanvasComponent::get_type() const { + return CompType::RECTANGLE; +} + +void RectangleCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const { + a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode); +} + +void RectangleCanvasComponent::load(cereal::PortableBinaryInputArchive& a) { + a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode); +} + +void RectangleCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const { + a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode); +} + +void RectangleCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) { + a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode); +} + +void RectangleCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const { + if(d.fillStrokeMode == 0 || d.fillStrokeMode == 2) { + SkPaint p; + p.setStyle(SkPaint::kFill_Style); + p.setStrokeCap(SkPaint::kRound_Cap); + p.setColor4f(convert_vec4(d.fillColor)); + canvas->drawPath(rectPath, p); + } + if(d.fillStrokeMode == 1 || d.fillStrokeMode == 2) { + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeCap(SkPaint::kRound_Cap); + p.setColor4f(convert_vec4(d.strokeColor)); + p.setStrokeWidth(d.strokeWidth); + canvas->drawPath(rectPath, p); + } +} + +void RectangleCanvasComponent::initialize_draw_data(DrawingProgram& drawP) { + create_draw_data(); + create_collider(); +} + +bool RectangleCanvasComponent::collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const { + return collisionTree.is_collide(checkAgainst); +} + +void RectangleCanvasComponent::create_draw_data() { + SkPathBuilder rectPathBuilder; + if(d.p1.x() == d.p2.x() || d.p1.y() == d.p2.y()) { + rectPathBuilder.moveTo(convert_vec2(d.p1)); + rectPathBuilder.lineTo(convert_vec2(d.p2)); + rectPath = rectPathBuilder.detach(); + } + else { + SkRRect newRect = SkRRect::MakeRectXY(SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y()), d.cornerRadius, d.cornerRadius); + rectPathBuilder.addRRect(newRect); + rectPath = rectPathBuilder.detach(); + } +} + +void RectangleCanvasComponent::create_collider() { + using namespace SCollision; + ColliderCollection strokeObjects; + if(d.fillStrokeMode == 0) { + std::array newT = triangle_from_rect_points(d.p1, d.p2); + strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]); + strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]); + } + else if(d.fillStrokeMode == 1) { + if(d.p1.x() == d.p2.x() || d.p1.y() == d.p2.y()) { + std::vector points = {d.p1, d.p2}; + generate_polyline(strokeObjects, points, d.strokeWidth, true); + } + else { + std::array pointArr = triangle_from_rect_points(d.p1, d.p2); + std::vector points(pointArr.begin(), pointArr.end()); + generate_polyline(strokeObjects, points, d.strokeWidth, true); + } + } + else if(d.fillStrokeMode == 2) { + float strokeRadius = d.strokeWidth * 0.5f; + std::array newT = triangle_from_rect_points((d.p1 - Vector2f{strokeRadius, strokeRadius}).eval(), (d.p2 + Vector2f{strokeRadius, strokeRadius}).eval()); + strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]); + strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]); + } + + collisionTree.clear(); + collisionTree.calculate_bvh_recursive(strokeObjects); +} + +SCollision::AABB RectangleCanvasComponent::get_obj_coord_bounds() const { + return collisionTree.objects.bounds; +} diff --git a/src/CanvasComponents/RectangleCanvasComponent.hpp b/src/CanvasComponents/RectangleCanvasComponent.hpp new file mode 100644 index 0000000..9ebd503 --- /dev/null +++ b/src/CanvasComponents/RectangleCanvasComponent.hpp @@ -0,0 +1,39 @@ +#pragma once +#include "../SharedTypes.hpp" +#include "CanvasComponent.hpp" +#include "../CoordSpaceHelper.hpp" +#include + +class RectangleCanvasComponent : public CanvasComponent { + public: + virtual void save(cereal::PortableBinaryOutputArchive& a) const override; + virtual void load(cereal::PortableBinaryInputArchive& a) override; + virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override; + virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override; + virtual CompType get_type() const override; + + // User input data + struct Data { + Vector4f strokeColor; + Vector4f fillColor; + float cornerRadius; + float strokeWidth; + Vector2f p1; + Vector2f p2; + uint8_t fillStrokeMode; + bool operator==(const Data& o) const = default; + } d; + + private: + virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override; + virtual void initialize_draw_data(DrawingProgram& drawP) override; + virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; + void create_draw_data(); + void create_collider(); + virtual SCollision::AABB get_obj_coord_bounds() const override; + + SkPath rectPath; + + SCollision::BVHContainer collisionTree; +}; + diff --git a/src/CanvasComponents/TextBoxCanvasComponent.cpp b/src/CanvasComponents/TextBoxCanvasComponent.cpp new file mode 100644 index 0000000..047a349 --- /dev/null +++ b/src/CanvasComponents/TextBoxCanvasComponent.cpp @@ -0,0 +1,111 @@ +#include "TextBoxCanvasComponent.hpp" +#include "Helpers/ConvertVec.hpp" +#include +#include "../MainProgram.hpp" +#include "Helpers/SCollision.hpp" +#include "../DrawingProgram/DrawingProgram.hpp" +#include "CanvasComponentContainer.hpp" +#include "../DrawCollision.hpp" + +using namespace RichText; + +TextBoxCanvasComponent::TextBoxCanvasComponent(): + textBox(std::make_shared()), + cursor(std::make_shared()) +{ + skia::textlayout::TextStyle tStyle; + textBox->set_initial_text_style(tStyle); + + textBox->set_initial_text_style_modifier(std::make_shared(Vector4f{1.0f, 1.0f, 1.0f, 1.0f})); + textBox->set_initial_text_style_modifier(std::make_shared(18.0f)); + textBox->set_initial_text_style_modifier(std::make_shared(std::vector{SkString{"Roboto"}})); +} + +CanvasComponent::CompType TextBoxCanvasComponent::get_type() const { + return CompType::TEXTBOX; +} + +void TextBoxCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const { + a(d.editing, d.p1, d.p2, textBox->get_rich_text_data(), *cursor); +} + +void TextBoxCanvasComponent::load(cereal::PortableBinaryInputArchive& a) { + TextData richText; + a(d.editing, d.p1, d.p2, richText, *cursor); + textBox->set_rich_text_data(richText); +} + +void TextBoxCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const { + a(d.p1, d.p2, textBox->get_rich_text_data()); +} + +void TextBoxCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) { + if(version < VersionNumber(0, 3, 0)) { + bool loadedEditing; // Unused + Vector4f textColor; + float textSize; + RichText::TextBox::Cursor loadedCursor; // Unused, Equivalent to old cursor structure, just paragraph index and text index flipped + std::string currentText; + a(loadedEditing, d.p1, d.p2, textColor, textSize, loadedCursor, currentText); + textBox->insert({0, 0}, currentText); + textBox->set_text_style_modifier_between({0, 0}, textBox->move(RichText::TextBox::Movement::END, {0, 0}), std::make_shared(textColor)); + textBox->set_text_style_modifier_between({0, 0}, textBox->move(RichText::TextBox::Movement::END, {0, 0}), std::make_shared(textSize)); + } + else { + TextData richText; + a(d.p1, d.p2, richText); + textBox->set_rich_text_data(richText); + } +} + +void TextBoxCanvasComponent::init_text_box(DrawingProgram& drawP) { + textBox->set_font_data(drawP.world.main.fonts); // Getting a segfault relating to the paragraph cache means that the font collection hasn't been set yet + textBox->set_width(d.p2.x() - d.p1.x() - TEXTBOX_PADDING * 2.0f); +} + +void TextBoxCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const { + SkRect clipR = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y()); + if(d.editing) { + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(0.0f); + p.setColor4f({0.5f, 0.5f, 0.5f, 1.0f}); + canvas->drawRect(clipR, p); + } + canvas->clipRect(clipR); + canvas->translate(d.p1.x() + TEXTBOX_PADDING, d.p1.y() + TEXTBOX_PADDING); + + TextBox::PaintOpts paintOpts; + paintOpts.cursorColor = {0.7f, 0.7f, 1.0f}; + if(d.editing && cursor) + paintOpts.cursor = *cursor; + + textBox->paint(canvas, paintOpts); +} + +Vector2f TextBoxCanvasComponent::get_mouse_pos(DrawingProgram& drawP) const { + return compContainer->get_coords().get_mouse_pos(drawP.world) - d.p1 - Vector2f{TEXTBOX_PADDING, TEXTBOX_PADDING}; +} + +void TextBoxCanvasComponent::initialize_draw_data(DrawingProgram& drawP) { + init_text_box(drawP); + create_collider(); +} + +bool TextBoxCanvasComponent::collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const { + return collisionTree.is_collide(checkAgainst); +} + +void TextBoxCanvasComponent::create_collider() { + using namespace SCollision; + ColliderCollection strokeObjects; + std::array newT = triangle_from_rect_points(d.p1, d.p2); + strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]); + strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]); + collisionTree.clear(); + collisionTree.calculate_bvh_recursive(strokeObjects); +} + +SCollision::AABB TextBoxCanvasComponent::get_obj_coord_bounds() const { + return collisionTree.objects.bounds; +} diff --git a/src/CanvasComponents/TextBoxCanvasComponent.hpp b/src/CanvasComponents/TextBoxCanvasComponent.hpp new file mode 100644 index 0000000..47b7725 --- /dev/null +++ b/src/CanvasComponents/TextBoxCanvasComponent.hpp @@ -0,0 +1,41 @@ +#pragma once +#include "../SharedTypes.hpp" +#include "CanvasComponent.hpp" +#include "../CoordSpaceHelper.hpp" +#include "../RichText/TextBox.hpp" +#include + +class TextBoxCanvasComponent : public CanvasComponent { + public: + constexpr static float TEXTBOX_PADDING = 5.0f; + + TextBoxCanvasComponent(); + virtual CompType get_type() const override; + virtual void save(cereal::PortableBinaryOutputArchive& a) const override; + virtual void load(cereal::PortableBinaryInputArchive& a) override; + virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override; + virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override; + + // User input data + struct Data { + Vector2f p1; + Vector2f p2; + bool editing = false; // Should probably move this out of the Data struct + bool operator==(const Data& o) const = default; + bool operator!=(const Data& o) const = default; + } d; + + private: + virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override; + virtual void initialize_draw_data(DrawingProgram& drawP) override; + virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; + void create_collider(); + void init_text_box(DrawingProgram& drawP); + Vector2f get_mouse_pos(DrawingProgram& drawP) const; + + virtual SCollision::AABB get_obj_coord_bounds() const override; + + std::shared_ptr textBox; + std::shared_ptr cursor; + SCollision::BVHContainer collisionTree; +}; diff --git a/src/DrawingProgram/GridModifyTool.cpp b/src/DrawingProgram/GridModifyTool.cpp index 8f5705a..2d61167 100644 --- a/src/DrawingProgram/GridModifyTool.cpp +++ b/src/DrawingProgram/GridModifyTool.cpp @@ -141,7 +141,7 @@ void GridModifyTool::tool_update() { selectionMode = 0; break; } - drawP.world.delayedUpdateObjectManager.send_update_to_all(gLock, false); + drawP.world.delayedUpdateObjectManager.send_update_to_all(gLock, nullptr, false); } else selectionMode = 0; @@ -187,5 +187,5 @@ void GridModifyTool::draw(SkCanvas* canvas, const DrawData& drawData) { void GridModifyTool::switch_tool(DrawingProgramToolType newTool) { NetworkingObjects::NetObjTemporaryPtr gLock = grid.lock(); if(gLock) - drawP.world.delayedUpdateObjectManager.send_update_to_all(gLock, true); + drawP.world.delayedUpdateObjectManager.send_update_to_all(gLock, nullptr, true); } diff --git a/src/GridManager.cpp b/src/GridManager.cpp index 45b0bc1..4a732d6 100644 --- a/src/GridManager.cpp +++ b/src/GridManager.cpp @@ -33,8 +33,9 @@ void GridManager::remove_grid(uint32_t indexToRemove) { void GridManager::draw_back(SkCanvas* canvas, const DrawData& drawData) { if(grids) { for(uint32_t i = 0; i < grids->size(); i++) { - if(!grids->at(i)->displayInFront) - grids->at(i)->draw(*this, canvas, drawData); + auto& g = grids->at(i)->obj; + if(!g->displayInFront) + g->draw(*this, canvas, drawData); } } } @@ -42,8 +43,9 @@ void GridManager::draw_back(SkCanvas* canvas, const DrawData& drawData) { void GridManager::draw_front(SkCanvas* canvas, const DrawData& drawData) { if(grids) { for(uint32_t i = 0; i < grids->size(); i++) { - if(grids->at(i)->displayInFront) - grids->at(i)->draw(*this, canvas, drawData); + auto& g = grids->at(i)->obj; + if(g->displayInFront) + g->draw(*this, canvas, drawData); } } } @@ -52,14 +54,14 @@ void GridManager::draw_coordinates(SkCanvas* canvas, const DrawData& drawData) { if(grids) { Vector2f axisOffset{0.0f, 0.0f}; for(uint32_t i = 0; i < grids->size(); i++) { - auto& g = grids->at(i); + auto& g = grids->at(i)->obj; if(g->coordinatesWillBeDrawn && g->coordinatesAxisOnBounds) { g->draw_coordinates(canvas, drawData, axisOffset); g->coordinatesWillBeDrawn = false; } } for(uint32_t i = 0; i < grids->size(); i++) { - auto& g = grids->at(i); + auto& g = grids->at(i)->obj; if(g->coordinatesWillBeDrawn && !g->coordinatesAxisOnBounds) g->draw_coordinates(canvas, drawData, axisOffset); g->coordinatesWillBeDrawn = false; @@ -69,8 +71,7 @@ void GridManager::draw_coordinates(SkCanvas* canvas, const DrawData& drawData) { void GridManager::scale_up(const WorldScalar& scaleUpAmount) { if(grids) { - for(uint32_t i = 0; i < grids->size(); i++) { - grids->at(i)->scale_up(scaleUpAmount); - } + for(uint32_t i = 0; i < grids->size(); i++) + grids->at(i)->obj->scale_up(scaleUpAmount); } } diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index cbf7b32..c5642a2 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -713,7 +713,7 @@ void Toolbar::grid_menu(bool justOpened) { gui.text_label_centered("No grids yet..."); uint32_t toDelete = std::numeric_limits::max(); gui.scroll_bar_many_entries_area("grid menu entries", entryHeight, main.world->gridMan.grids->size(), true, [&](size_t i, bool isListHovered) { - auto& grid = main.world->gridMan.grids->at(i); + auto& grid = main.world->gridMan.grids->at(i)->obj; bool selectedEntry = gridMenu.selectedGrid == i; CLAY_AUTO_ID({ .layout = { @@ -767,7 +767,7 @@ void Toolbar::grid_menu(bool justOpened) { }); if(gui.svg_icon_button("grid add button", "data/icons/plus.svg", false, GUIStuff::GUIManager::SMALL_BUTTON_SIZE) || (addByEnter && !gridMenu.newName.empty())) { main.world->gridMan.add_default_grid(gridMenu.newName); - main.world->drawProg.modify_grid(main.world->gridMan.grids->at(main.world->gridMan.grids->size() - 1)); + main.world->drawProg.modify_grid(main.world->gridMan.grids->at(main.world->gridMan.grids->size() - 1)->obj); stop_displaying_grid_menu(); } }); @@ -806,7 +806,7 @@ void Toolbar::bookmark_menu(bool justOpened) { gui.text_label_centered("No bookmarks yet..."); uint32_t toDelete = std::numeric_limits::max(); gui.scroll_bar_many_entries_area("bookmark menu entries", entryHeight, main.world->bMan.bookmarks->size(), true, [&](size_t i, bool isListHovered) { - const Bookmark& bookmark = *main.world->bMan.bookmarks->at(i); + const Bookmark& bookmark = *main.world->bMan.bookmarks->at(i)->obj; bool selectedEntry = i == bookMenu.selectedBookmark; CLAY_AUTO_ID({ .layout = { @@ -838,6 +838,7 @@ void Toolbar::bookmark_menu(bool justOpened) { } if(Clay_Hovered() && io->mouse.leftClick && isListHovered && !miniButtonClicked) { bookMenu.newName = bookmark.name; + bookMenu.selectedBookmark = i; if(io->mouse.leftClick >= 2) main.world->bMan.jump_to_bookmark(i); } diff --git a/src/World.cpp b/src/World.cpp index 2165d61..af0a8b0 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -30,8 +30,7 @@ #endif World::World(MainProgram& initMain, OpenWorldInfo& worldInfo): - netObjectTypeList(std::make_shared()), - netObjMan(netObjectTypeList, worldInfo.conType != CONNECTIONTYPE_CLIENT), + netObjMan(worldInfo.conType != CONNECTIONTYPE_CLIENT), main(initMain), rMan(*this), drawProg(*this), @@ -79,10 +78,10 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo): } void World::init_net_obj_type_list() { - NetworkingObjects::register_generic_serialized_class(*netObjectTypeList); - NetworkingObjects::register_ordered_list_class(*netObjectTypeList); - delayedUpdateObjectManager.register_class(*netObjectTypeList); - NetworkingObjects::register_ordered_list_class(*netObjectTypeList); + NetworkingObjects::register_generic_serialized_class(netObjMan); + NetworkingObjects::register_ordered_list_class(netObjMan); + delayedUpdateObjectManager.register_class(netObjMan); + NetworkingObjects::register_ordered_list_class(netObjMan); } void World::init_client_callbacks() { @@ -395,8 +394,8 @@ void World::save_file(cereal::PortableBinaryOutputArchive& a) const { a(drawData.cam.c, main.window.size.cast().eval()); a(convert_vec3(canvasTheme.backColor)); drawProg.save_file(a); - a(bMan); - a(gridMan); + //a(bMan); + //a(gridMan); rMan.save_strip_unused_resources(a, drawProg.get_used_resources()); } @@ -419,9 +418,9 @@ void World::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber versi drawData.cam.smooth_move_to(*this, coordsToJumpTo, windowSizeToJumpTo, true); drawProg.load_file(a, version); - a(bMan); - if(version >= VersionNumber(0, 2, 0)) - a(gridMan); + //a(bMan); + //if(version >= VersionNumber(0, 2, 0)) + // a(gridMan); a(rMan); } diff --git a/src/World.hpp b/src/World.hpp index 13ebcf8..7a1d24b 100644 --- a/src/World.hpp +++ b/src/World.hpp @@ -37,7 +37,6 @@ class World { World(MainProgram& initMain, OpenWorldInfo& worldInfo); - std::shared_ptr netObjectTypeList; // NOTE: Keep at the very beginning so that it's destroyed last NetworkingObjects::NetObjManager netObjMan;