From ff1355234f534f80f4a0eb4b2427d8944b707ac1 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Thu, 24 Jul 2025 16:35:48 +0300 Subject: [PATCH] Begin work separating selection code from rectangle select tool --- CMakeLists.txt | 1 + src/CoordSpaceHelper.cpp | 33 +- src/CoordSpaceHelper.hpp | 3 + src/DrawComponents/DrawComponent.hpp | 12 +- src/DrawingProgram/DrawingProgram.cpp | 115 +----- src/DrawingProgram/DrawingProgram.hpp | 14 +- src/DrawingProgram/DrawingProgramCache.cpp | 173 ++++++--- src/DrawingProgram/DrawingProgramCache.hpp | 95 +++-- .../DrawingProgramSelection.cpp | 119 ++++++ .../DrawingProgramSelection.hpp | 23 ++ src/DrawingProgram/EditTool.cpp | 39 +- src/DrawingProgram/EraserTool.cpp | 11 +- src/DrawingProgram/EraserTool.hpp | 6 +- src/DrawingProgram/RectSelectTool.cpp | 345 +----------------- src/DrawingProgram/RectSelectTool.hpp | 36 +- 15 files changed, 386 insertions(+), 639 deletions(-) create mode 100644 src/DrawingProgram/DrawingProgramSelection.cpp create mode 100644 src/DrawingProgram/DrawingProgramSelection.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4acd46f..2d133ec 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ set(sources "src/main.cpp" "src/DrawingProgram/ScreenshotTool.cpp" "src/DrawingProgram/EditTool.cpp" "src/DrawingProgram/ImageTool.cpp" + "src/DrawingProgram/DrawingProgramSelection.cpp" "src/DrawComponents/DrawEllipse.cpp" "src/DrawComponents/DrawRectangle.cpp" "src/DrawComponents/DrawImage.cpp" diff --git a/src/CoordSpaceHelper.cpp b/src/CoordSpaceHelper.cpp index b91ad86..2d0edb1 100644 --- a/src/CoordSpaceHelper.cpp +++ b/src/CoordSpaceHelper.cpp @@ -114,24 +114,21 @@ Vector2f CoordSpaceHelper::get_mouse_pos(const World& w) const { return to_space(w.drawData.cam.c.from_space(w.main.input.mouse.pos)); } -//SkMatrix CoordSpaceHelper::get_sk_matrix(const DrawData& drawData) const { -// //WorldTransform newTransform = drawData.cam.transform * inverseTransform; -// //const auto& newMat = newTransform.matrix(); -// //return SkMatrix::MakeAll(newMat(0, 0), newMat(0, 1), newMat(0, 2), -// // newMat(1, 0), newMat(1, 1), newMat(1, 2), -// // 0.0f, 0.0f, 1.0f); -// return SkMatrix::I(); -//} -// -//SkMatrix CoordSpaceHelper::get_sk_matrix_temp(const WorldTransform& tempTransform, const DrawData& drawData) const { -// //WorldTransform newTransform = drawData.cam.transform * tempTransform * inverseTransform; -// //const auto& newMat = newTransform.matrix(); -// //return SkMatrix::MakeAll(newMat(0, 0), newMat(0, 1), newMat(0, 2), -// // newMat(1, 0), newMat(1, 1), newMat(1, 2), -// // 0.0f, 0.0f, 1.0f); -// return SkMatrix::I(); -//} -// +CoordSpaceHelper CoordSpaceHelper::other_coord_space_to_this_space(const CoordSpaceHelper& other) const { + CoordSpaceHelper toRet; + toRet.pos = (other.pos - pos) / inverseScale; + toRet.rotation = other.rotation; + toRet.inverseScale = other.inverseScale / inverseScale; + return toRet; +} + +CoordSpaceHelper CoordSpaceHelper::other_coord_space_from_this_space(const CoordSpaceHelper& other) const { + CoordSpaceHelper toRet; + toRet.pos = (other.pos * inverseScale) + pos; + toRet.rotation = other.rotation; + toRet.inverseScale = other.inverseScale * inverseScale; + return toRet; +} void CoordSpaceHelper::transform_sk_canvas(SkCanvas* canvas, const DrawData& drawData) const { Vector2f translateSpace = -to_space(drawData.cam.c.pos); diff --git a/src/CoordSpaceHelper.hpp b/src/CoordSpaceHelper.hpp index 1f42912..c9934ab 100644 --- a/src/CoordSpaceHelper.hpp +++ b/src/CoordSpaceHelper.hpp @@ -32,6 +32,9 @@ class CoordSpaceHelper { Vector2f dir_to_space(const WorldVec& coord) const; Vector2f normalized_dir_to_space(const WorldVec& coord) const; + CoordSpaceHelper other_coord_space_to_this_space(const CoordSpaceHelper& other) const; + CoordSpaceHelper other_coord_space_from_this_space(const CoordSpaceHelper& other) const; + template S collider_to_world(const T& collider) const { return collider.template transform( [&coords = *this](const Vector2f& a) { diff --git a/src/DrawComponents/DrawComponent.hpp b/src/DrawComponents/DrawComponent.hpp index c5bb480..86d70a4 100644 --- a/src/DrawComponents/DrawComponent.hpp +++ b/src/DrawComponents/DrawComponent.hpp @@ -6,7 +6,6 @@ #include "../CoordSpaceHelper.hpp" #include "../Server/MainServer.hpp" #include "../CollabList.hpp" -#include "../DrawingProgram/DrawingProgramCache.hpp" #ifndef IS_SERVER #include "../DrawData.hpp" @@ -24,6 +23,10 @@ #define DRAWCOMP_MIPMAP_LEVEL_TWO 5 class DrawingProgram; +class DrawComponent; +class DrawingProgramCacheBVHNode; + +typedef CollabList, ServerClientID> CollabListType; class DrawComponent { public: @@ -64,16 +67,14 @@ class DrawComponent { std::chrono::time_point tempServerTransformTimer; bool serverIsTempTransform = false; - std::weak_ptr, ServerClientID>::ObjectInfo> collabListInfo; - std::weak_ptr parentBvhNode; + std::weak_ptr collabListInfo; + std::weak_ptr parentBvhNode; void server_update(MainServer& server, ServerClientID id); #ifndef IS_SERVER bool selected = false; - bool globalCollisionCheck = false; - std::chrono::time_point lastTransformTime; std::chrono::time_point lastUpdateTime; @@ -115,4 +116,3 @@ class DrawComponent { virtual void allocate_collider() = 0; #endif }; - diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index 854979b..be8987a 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -49,7 +49,8 @@ DrawingProgram::DrawingProgram(World& initWorld): inkDropperTool(*this), screenshotTool(*this), editTool(*this), - imageTool(*this) + imageTool(*this), + selection(*this) { components.updateCallback = [&]() { if(&world == world.main.world.get()) @@ -172,18 +173,6 @@ void DrawingProgram::parallel_loop_all_components(std::function& checkAgainstWorld, const SCollision::ColliderCollection& checkAgainstCam) { - compCache.traverse_bvh_collision_check(checkAgainstWorld, checkAgainstCam); -} - -void DrawingProgram::check_all_collisions(const SCollision::ColliderCollection& checkAgainst) { - check_all_collisions_base(checkAgainst, world.drawData.cam.c.world_collider_to_coords>(checkAgainst)); -} - -void DrawingProgram::check_all_collisions_transform(const SCollision::ColliderCollection& checkAgainst) { - check_all_collisions_base(world.drawData.cam.c.collider_to_world, SCollision::ColliderCollection>(checkAgainst), checkAgainst); -} - std::unordered_set DrawingProgram::get_used_resources() { std::unordered_set toRet; for(auto& c : components.client_list()) @@ -339,8 +328,6 @@ void DrawingProgram::update() { controls.selectedTool = TOOL_SCREENSHOT; else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_EDIT).pressed) controls.selectedTool = TOOL_EDIT; - else if(world.main.input.key(InputManager::KEY_PASTE).pressed) - rectSelectTool.paste_clipboard(); switch(controls.selectedTool) { case TOOL_BRUSH: @@ -381,6 +368,7 @@ void DrawingProgram::update() { check_updateable_components(); compCache.update(); + selection.update(); } void DrawingProgram::drag_drop_update() { @@ -473,7 +461,7 @@ void DrawingProgram::initialize_draw_data(cereal::PortableBinaryInputArchive& a) parallel_loop_all_components([&](const auto& c){ c->obj->final_update(*this, false); }); - compCache.force_rebuild(); + compCache.force_rebuild(components.client_list()); } void DrawingProgram::reset_selection() { @@ -583,9 +571,10 @@ void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) { else { SkCanvas* drawProgCacheCanvas = world.main.drawProgCache.surface->getCanvas(); drawProgCacheCanvas->clear(SkColor4f{0.0f, 0.0f, 0.0f, 0.0f}); - compCache.refresh_all_draw_cache(drawProgCacheCanvas, drawData); - draw_components_to_canvas(drawProgCacheCanvas, drawData, false); + compCache.refresh_all_draw_cache(drawData); + compCache.draw_components_to_canvas(drawProgCacheCanvas, drawData); canvas->drawImage(world.main.drawProgCache.surface->makeTemporaryImage(), 0, 0); + selection.draw(canvas, drawData); } } @@ -622,96 +611,6 @@ void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) { } } -void DrawingProgram::draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, bool dontUseCache, uint64_t* lastDrawnComponentPlacement) { - uint64_t lastComponentDrawn = 0; - - std::vector> cachedNodesToDraw; - std::vector uncachedCompsToDraw; - - compCache.traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr& node, const std::vector& comps) { - if(!dontUseCache && node && node->drawCache && node->coords.inverseScale <= drawData.cam.c.inverseScale) { - cachedNodesToDraw.emplace_back(node); - return false; - } - - for(auto& c : comps) - c->obj->calculate_draw_transform(drawData); - - uncachedCompsToDraw.insert(uncachedCompsToDraw.end(), comps.begin(), comps.end()); - - return true; - }); - - std::sort(uncachedCompsToDraw.begin(), uncachedCompsToDraw.end(), [](auto& a, auto& b) { - return a->pos < b->pos; - }); - std::sort(cachedNodesToDraw.begin(), cachedNodesToDraw.end(), [](auto& a, auto& b) { - return a->drawCache.value().lastDrawnComponentPlacement < b->drawCache.value().lastDrawnComponentPlacement; - }); - size_t nextCacheToRender = 0; - for(auto& c : uncachedCompsToDraw) { - for(;;) { - if(nextCacheToRender >= cachedNodesToDraw.size() || c->pos < cachedNodesToDraw[nextCacheToRender]->drawCache.value().lastDrawnComponentPlacement) - break; - auto& bvhNode = cachedNodesToDraw[nextCacheToRender]; - auto& drawCache = bvhNode->drawCache.value(); - canvas->save(); - bvhNode->coords.transform_sk_canvas(canvas, drawData); - drawCache.lastRenderTime = std::chrono::steady_clock::now(); - - lastComponentDrawn = std::max(lastComponentDrawn, drawCache.lastDrawnComponentPlacement); - - SkPaint p; - p.setBlendMode(SkBlendMode::kSrc); - - // Note, no mipmaps here. You might need to generate mipmaps in the future (withDefaultMipmaps) - canvas->drawImage(drawCache.surface->makeTemporaryImage(), 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &p); - - canvas->restore(); - nextCacheToRender++; - } - c->obj->draw(canvas, drawData); - c->obj->drawSetupData.shouldDraw = false; - lastComponentDrawn = std::max(lastComponentDrawn, c->pos); - } - for(;nextCacheToRender < cachedNodesToDraw.size(); nextCacheToRender++) { - auto& bvhNode = cachedNodesToDraw[nextCacheToRender]; - auto& drawCache = bvhNode->drawCache.value(); - canvas->save(); - bvhNode->coords.transform_sk_canvas(canvas, drawData); - drawCache.lastRenderTime = std::chrono::steady_clock::now(); - - lastComponentDrawn = std::max(lastComponentDrawn, drawCache.lastDrawnComponentPlacement); - - SkPaint p; - p.setBlendMode(SkBlendMode::kSrc); - - // Note, no mipmaps here. You might need to generate mipmaps in the future (withDefaultMipmaps) - canvas->drawImage(drawCache.surface->makeTemporaryImage(), 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &p); - - canvas->restore(); - } - - if(lastDrawnComponentPlacement) - *lastDrawnComponentPlacement = lastComponentDrawn; -} - Vector4f* DrawingProgram::get_foreground_color_ptr() { return &controls.foregroundColor; } - -void DrawingProgram::draw_cache(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& lastComp) { - SkCanvas* cacheCanvas = world.main.drawProgCache.canvas; - cacheCanvas->clear(SkColor4f{0, 0, 0, 0}); - parallel_loop_all_components([&](auto& c) { - c->obj->calculate_draw_transform(drawData); - }); - for(auto& c : components.client_list()) { - if(c->obj == lastComp) - break; - c->obj->draw(cacheCanvas, drawData); - } - canvas->drawImage(world.main.drawProgCache.surface->makeTemporaryImage(), 0, 0); - world.main.drawProgCache.firstCompUpdate = lastComp; - world.main.drawProgCache.refresh = false; -} diff --git a/src/DrawingProgram/DrawingProgram.hpp b/src/DrawingProgram/DrawingProgram.hpp index 123f4b7..ad5f1f8 100644 --- a/src/DrawingProgram/DrawingProgram.hpp +++ b/src/DrawingProgram/DrawingProgram.hpp @@ -19,6 +19,7 @@ #include "ScreenshotTool.hpp" #include "EditTool.hpp" #include "ImageTool.hpp" +#include "DrawingProgramSelection.hpp" #include "../CollabList.hpp" class World; @@ -51,8 +52,6 @@ class DrawingProgram { DrawingProgramCache compCache; - typedef CollabList, ServerClientID> CollabListType; - CollabListType components; std::unordered_set> updateableComponents; @@ -69,13 +68,6 @@ class DrawingProgram { void add_file_to_canvas_by_path_execute(const std::string& filePath, Vector2f dropPos); - void draw_cache(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& lastComp); - - void check_all_collisions_base(const SCollision::ColliderCollection& checkAgainstWorld, const SCollision::ColliderCollection& checkAgainstCam); - void check_all_collisions(const SCollision::ColliderCollection& checkAgainst); - void check_all_collisions_transform(const SCollision::ColliderCollection& checkAgainst); - - void add_variable_width_line(SkPath& p, const DrawData& drawData, const WorldVec& p1, const WorldVec& p2, float w1, float w2); void reset_selection(); void reset_tools(); @@ -86,8 +78,6 @@ class DrawingProgram { void add_undo_place_component(uint64_t placement, const std::shared_ptr& comp); void add_undo_place_components(uint64_t placement, const std::vector>& comps); - void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, bool dontUseCache, uint64_t* lastDrawnComponentPlacement = nullptr); - BrushTool brushTool; EraserTool eraserTool; RectSelectTool rectSelectTool; @@ -99,6 +89,8 @@ class DrawingProgram { EditTool editTool; ImageTool imageTool; + DrawingProgramSelection selection; + bool temporaryEraser = false; enum Tool : int { diff --git a/src/DrawingProgram/DrawingProgramCache.cpp b/src/DrawingProgram/DrawingProgramCache.cpp index 65ffa88..da0507a 100644 --- a/src/DrawingProgram/DrawingProgramCache.cpp +++ b/src/DrawingProgram/DrawingProgramCache.cpp @@ -13,7 +13,7 @@ #define MAXIMUM_DRAW_CACHE_SURFACES 40 -std::unordered_set> DrawingProgramCache::nodesWithCachedSurfaces; +std::unordered_set> DrawingProgramCache::nodesWithCachedSurfaces; DrawingProgramCache::DrawingProgramCache(DrawingProgram& initDrawP): drawP(initDrawP) @@ -22,7 +22,7 @@ DrawingProgramCache::DrawingProgramCache(DrawingProgram& initDrawP): } void DrawingProgramCache::build(std::vector components) { - bvhRoot = std::make_shared(); + bvhRoot = std::make_shared(); unsortedComponents.clear(); clear_own_cached_surfaces(); std::erase_if(components, [&](auto& c) { @@ -37,19 +37,33 @@ void DrawingProgramCache::build(std::vector compo lastBvhBuildTime = std::chrono::steady_clock::now(); } +void DrawingProgramCache::clear() { + bvhRoot = nullptr; + unsortedComponents.clear(); + clear_own_cached_surfaces(); +} + void DrawingProgramCache::clear_own_cached_surfaces() { std::erase_if(nodesWithCachedSurfaces, [&](auto& s) { - return s->drawCache.value().attachedDrawP == &drawP; + return s->drawCache.value().attachedCache == this; }); } void DrawingProgramCache::update() { if(!disableRefresh && unsortedComponents.size() >= 1000 && (std::chrono::steady_clock::now() - lastBvhBuildTime) >= std::chrono::seconds(5)) - force_rebuild(); + force_rebuild(drawP.components.client_list()); } -void DrawingProgramCache::force_rebuild() { - build(drawP.components.client_list()); +void DrawingProgramCache::force_rebuild(const std::vector& componentsToBuild) { + build(componentsToBuild); +} + +const std::vector& DrawingProgramCache::get_unsorted_component_list() const { + return unsortedComponents; +} + +const std::chrono::steady_clock::time_point& DrawingProgramCache::get_last_bvh_build_time() const { + return lastBvhBuildTime; } void DrawingProgramCache::add_component(const CollabListType::ObjectInfoPtr& c) { @@ -71,7 +85,7 @@ void DrawingProgramCache::erase_component(const CollabListType::ObjectInfoPtr& c invalidate_cache_before_pos(c->pos); } -std::pair, std::vector::iterator> DrawingProgramCache::get_bvh_node_fully_containing_recursive(const std::shared_ptr& bvhNode, DrawComponent* c) { +std::pair, std::vector::iterator> DrawingProgramCache::get_bvh_node_fully_containing_recursive(const std::shared_ptr& bvhNode, DrawComponent* c) { SCollision::AABB& cAABB = c->worldAABB.value(); if(!bvhNode->bounds.fully_contains_aabb(cAABB)) return {nullptr, bvhNode->components.end()}; @@ -88,13 +102,13 @@ std::pair, std::vectorcomponents.end()}; } -void DrawingProgramCache::traverse_bvh_erase_function(const SCollision::AABB& aabb, std::function& node, std::vector& components)> f) { +void DrawingProgramCache::traverse_bvh_erase_function(const SCollision::AABB& aabb, std::function& node, std::vector& components)> f) { f(nullptr, unsortedComponents); if(traverse_bvh_erase_function_recursive(bvhRoot, aabb, f)) bvhRoot = nullptr; } -bool DrawingProgramCache::traverse_bvh_erase_function_recursive(const std::shared_ptr& bvhNode, const SCollision::AABB& aabb, std::function& node, std::vector& components)> f) { +bool DrawingProgramCache::traverse_bvh_erase_function_recursive(const std::shared_ptr& bvhNode, const SCollision::AABB& aabb, std::function& node, std::vector& components)> f) { if(bvhNode && SCollision::collide(aabb, bvhNode->bounds)) { if(f(bvhNode, bvhNode->components)) return true; @@ -105,47 +119,12 @@ bool DrawingProgramCache::traverse_bvh_erase_function_recursive(const std::share return false; } -void DrawingProgramCache::traverse_bvh_collision_check(const SCollision::ColliderCollection& checkAgainstWorld, const SCollision::ColliderCollection& checkAgainstCam) { - for(auto& c : drawP.components.client_list()) - c->obj->globalCollisionCheck = false; - for(auto& c : unsortedComponents) - c->obj->globalCollisionCheck = c->obj->collides_with(drawP.world.drawData.cam.c, checkAgainstWorld, checkAgainstCam, drawP.colliderAllocated); - traverse_bvh_collision_check_recursive(bvhRoot, checkAgainstWorld, checkAgainstCam); -} - -void DrawingProgramCache::traverse_bvh_collision_check_recursive_tiny(const std::shared_ptr& bvhNode) { - if(bvhNode) { - for(auto& c : bvhNode->components) - c->obj->globalCollisionCheck = true; - for(auto& p : bvhNode->children) - traverse_bvh_collision_check_recursive_tiny(p); - } -} - -void DrawingProgramCache::traverse_bvh_collision_check_recursive(const std::shared_ptr& bvhNode, const SCollision::ColliderCollection& checkAgainstWorld, const SCollision::ColliderCollection& checkAgainstCam) { - if(bvhNode) { - if(SCollision::collide(checkAgainstWorld.bounds, bvhNode->bounds)) { - auto& camCoords = drawP.world.drawData.cam.c; - if((camCoords.inverseScale >> DRAWCOMP_COLLIDE_MIN_SHIFT_TINY) >= bvhNode->coords.inverseScale) { - if(SCollision::collide(checkAgainstCam, camCoords.to_space(bvhNode->bounds.min))) - traverse_bvh_collision_check_recursive_tiny(bvhNode); - } - else { - for(auto& c : bvhNode->components) - c->obj->globalCollisionCheck = c->obj->collides_with(drawP.world.drawData.cam.c, checkAgainstWorld, checkAgainstCam, drawP.colliderAllocated); - for(auto& p : bvhNode->children) - traverse_bvh_collision_check_recursive(p, checkAgainstWorld, checkAgainstCam); - } - } - } -} - -void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB& aabb, std::function& node, const std::vector& components)> f) { +void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB& aabb, std::function& node, const std::vector& components)> f) { f(nullptr, unsortedComponents); traverse_bvh_run_function_recursive(bvhRoot, aabb, f); } -void DrawingProgramCache::traverse_bvh_run_function_recursive(const std::shared_ptr& bvhNode, const SCollision::AABB& aabb, std::function& node, const std::vector& components)> f) { +void DrawingProgramCache::traverse_bvh_run_function_recursive(const std::shared_ptr& bvhNode, const SCollision::AABB& aabb, std::function& node, const std::vector& components)> f) { if(bvhNode && SCollision::collide(aabb, bvhNode->bounds) && f(bvhNode, bvhNode->components)) { for(auto& p : bvhNode->children) traverse_bvh_run_function_recursive(p, aabb, f); @@ -162,7 +141,7 @@ void DrawingProgramCache::preupdate_component(const CollabListType::ObjectInfoPt invalidate_cache_before_pos(c->pos); } -void DrawingProgramCache::build_bvh_node(const std::shared_ptr& bvhNode, std::vector components) { +void DrawingProgramCache::build_bvh_node(const std::shared_ptr& bvhNode, std::vector components) { if(components.empty()) return; @@ -201,11 +180,11 @@ void DrawingProgramCache::build_bvh_node(const std::shared_ptr& bvhNode for(auto& p : parts) { if(!p.empty()) - build_bvh_node(bvhNode->children.emplace_back(std::make_shared()), p); + build_bvh_node(bvhNode->children.emplace_back(std::make_shared()), p); } } -void DrawingProgramCache::build_bvh_node_coords_and_resolution(BVHNode& node) { +void DrawingProgramCache::build_bvh_node_coords_and_resolution(DrawingProgramCacheBVHNode& node) { // These are part of the draw cache data, but we're putting it out here, because: // - We dont have to recalculate it every time we refresh the cache // - Some of the data here is needed to determine whether to generate the cache in the first place @@ -255,9 +234,9 @@ void DrawingProgramCache::invalidate_cache_at_aabb_before_pos(const SCollision:: }); } -void DrawingProgramCache::refresh_all_draw_cache(SkCanvas* canvas, const DrawData& drawData) { - std::deque> nodeFlatList; // We want to render children before parents, so that parents can make use of the cached children - traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](std::shared_ptr node, const std::vector& comps) { +void DrawingProgramCache::refresh_all_draw_cache(const DrawData& drawData) { + std::deque> nodeFlatList; // We want to render children before parents, so that parents can make use of the cached children + traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](std::shared_ptr node, const std::vector& comps) { if(node && node->coords.inverseScale <= drawData.cam.c.inverseScale) { if(!node->drawCache) nodeFlatList.emplace_front(node); @@ -269,12 +248,12 @@ void DrawingProgramCache::refresh_all_draw_cache(SkCanvas* canvas, const DrawDat refresh_draw_cache(nodeFlatList.front(), drawData); } -void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr& bvhNode, const DrawData& drawData) { +void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr& bvhNode, const DrawData& drawData) { if(bvhNode->children.empty() && bvhNode->components.empty()) return; bvhNode->drawCache = std::nullopt; - BVHNode::DrawCacheData drawCache; + DrawingProgramCacheBVHNode::DrawCacheData drawCache; SkImageInfo imgInfo = SkImageInfo::MakeN32Premul(bvhNode->resolution.x(), bvhNode->resolution.y()); #ifdef USE_SKIA_BACKEND_GRAPHITE @@ -296,15 +275,15 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr& bvh cacheDrawData.cam.set_viewing_area(bvhNode->resolution.cast()); cacheDrawData.refresh_draw_optimizing_values(); - drawP.draw_components_to_canvas(cacheCanvas, cacheDrawData, false, &drawCache.lastDrawnComponentPlacement); + draw_components_to_canvas(cacheCanvas, cacheDrawData, &drawCache.lastDrawnComponentPlacement); drawCache.lastRenderTime = std::chrono::steady_clock::now(); - drawCache.attachedDrawP = &drawP; + drawCache.attachedCache = this; bvhNode->drawCache = drawCache; // Set the drawCache after rendering is done, so that the draw function doesnt assume we have this node cached while(nodesWithCachedSurfaces.size() >= MAXIMUM_DRAW_CACHE_SURFACES) { - std::shared_ptr leastUsedNode = *nodesWithCachedSurfaces.begin(); - for(const std::shared_ptr& n : nodesWithCachedSurfaces) { + std::shared_ptr leastUsedNode = *nodesWithCachedSurfaces.begin(); + for(const std::shared_ptr& n : nodesWithCachedSurfaces) { if(n->drawCache.value().lastRenderTime < leastUsedNode->drawCache.value().lastRenderTime) leastUsedNode = n; } @@ -314,3 +293,81 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr& bvh nodesWithCachedSurfaces.emplace(bvhNode); } + +void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, uint64_t* lastDrawnComponentPlacement) { + uint64_t lastComponentDrawn = 0; + + std::vector> cachedNodesToDraw; + std::vector uncachedCompsToDraw; + + traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr& node, const std::vector& comps) { + if(node && node->drawCache && node->coords.inverseScale <= drawData.cam.c.inverseScale) { + cachedNodesToDraw.emplace_back(node); + return false; + } + + for(auto& c : comps) + c->obj->calculate_draw_transform(drawData); + + uncachedCompsToDraw.insert(uncachedCompsToDraw.end(), comps.begin(), comps.end()); + + return true; + }); + + std::sort(uncachedCompsToDraw.begin(), uncachedCompsToDraw.end(), [](auto& a, auto& b) { + return a->pos < b->pos; + }); + std::sort(cachedNodesToDraw.begin(), cachedNodesToDraw.end(), [](auto& a, auto& b) { + return a->drawCache.value().lastDrawnComponentPlacement < b->drawCache.value().lastDrawnComponentPlacement; + }); + size_t nextCacheToRender = 0; + for(auto& c : uncachedCompsToDraw) { + for(;;) { + if(nextCacheToRender >= cachedNodesToDraw.size() || c->pos < cachedNodesToDraw[nextCacheToRender]->drawCache.value().lastDrawnComponentPlacement) + break; + auto& bvhNode = cachedNodesToDraw[nextCacheToRender]; + auto& drawCache = bvhNode->drawCache.value(); + canvas->save(); + bvhNode->coords.transform_sk_canvas(canvas, drawData); + drawCache.lastRenderTime = std::chrono::steady_clock::now(); + + lastComponentDrawn = std::max(lastComponentDrawn, drawCache.lastDrawnComponentPlacement); + + SkPaint p; + p.setBlendMode(SkBlendMode::kSrc); + + // Note, no mipmaps here. You might need to generate mipmaps in the future (withDefaultMipmaps) + canvas->drawImage(drawCache.surface->makeTemporaryImage(), 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &p); + + canvas->restore(); + nextCacheToRender++; + } + c->obj->draw(canvas, drawData); + c->obj->drawSetupData.shouldDraw = false; + lastComponentDrawn = std::max(lastComponentDrawn, c->pos); + } + for(;nextCacheToRender < cachedNodesToDraw.size(); nextCacheToRender++) { + auto& bvhNode = cachedNodesToDraw[nextCacheToRender]; + auto& drawCache = bvhNode->drawCache.value(); + canvas->save(); + bvhNode->coords.transform_sk_canvas(canvas, drawData); + drawCache.lastRenderTime = std::chrono::steady_clock::now(); + + lastComponentDrawn = std::max(lastComponentDrawn, drawCache.lastDrawnComponentPlacement); + + SkPaint p; + p.setBlendMode(SkBlendMode::kSrc); + + // Note, no mipmaps here. You might need to generate mipmaps in the future (withDefaultMipmaps) + canvas->drawImage(drawCache.surface->makeTemporaryImage(), 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &p); + + canvas->restore(); + } + + if(lastDrawnComponentPlacement) + *lastDrawnComponentPlacement = lastComponentDrawn; +} + +DrawingProgramCache::~DrawingProgramCache() { + clear_own_cached_surfaces(); +} diff --git a/src/DrawingProgram/DrawingProgramCache.hpp b/src/DrawingProgram/DrawingProgramCache.hpp index c6458cd..bf9e23d 100644 --- a/src/DrawingProgram/DrawingProgramCache.hpp +++ b/src/DrawingProgram/DrawingProgramCache.hpp @@ -1,81 +1,68 @@ #pragma once +#include #include -#include "../CollabList.hpp" #include #include #include "../DrawData.hpp" +#include "../DrawComponents/DrawComponent.hpp" class DrawingProgram; +class DrawingProgramCache; -class DrawComponent; +class DrawingProgramCacheBVHNode { + public: + SCollision::AABB bounds; + std::vector components; + std::vector> children; + + CoordSpaceHelper coords; + Vector2i resolution; + struct DrawCacheData { + uint64_t lastDrawnComponentPlacement; + uint64_t firstDrawnComponentPlacement; + sk_sp surface; + std::chrono::steady_clock::time_point lastRenderTime; + DrawingProgramCache* attachedCache; + }; + + std::optional drawCache; +}; class DrawingProgramCache { public: - typedef CollabList, ServerClientID> CollabListType; DrawingProgramCache(DrawingProgram& initDrawP); + ~DrawingProgramCache(); + + static std::unordered_set> nodesWithCachedSurfaces; + bool disableRefresh = false; void update(); void erase_component(const CollabListType::ObjectInfoPtr& c); void add_component(const CollabListType::ObjectInfoPtr& c); - void refresh_all_draw_cache(SkCanvas* canvas, const DrawData& drawData); - - struct BVHNode { - SCollision::AABB bounds; - std::vector components; - std::vector> children; - - CoordSpaceHelper coords; - Vector2i resolution; - struct DrawCacheData { - uint64_t lastDrawnComponentPlacement; - uint64_t firstDrawnComponentPlacement; - sk_sp surface; - std::chrono::steady_clock::time_point lastRenderTime; - DrawingProgram* attachedDrawP; - }; - - std::optional drawCache; - }; - - static std::unordered_set> nodesWithCachedSurfaces; - - bool disableRefresh = false; - - void traverse_bvh_run_function(const SCollision::AABB& aabb, std::function& node, const std::vector& components)> f); - void traverse_bvh_collision_check(const SCollision::ColliderCollection& checkAgainstWorld, const SCollision::ColliderCollection& checkAgainstCam); - - void traverse_bvh_erase_function(const SCollision::AABB& aabb, std::function& node, std::vector& components)> f); - - void force_rebuild(); - + void refresh_all_draw_cache(const DrawData& drawData); + void traverse_bvh_run_function(const SCollision::AABB& aabb, std::function& node, const std::vector& components)> f); + void traverse_bvh_erase_function(const SCollision::AABB& aabb, std::function& node, std::vector& components)> f); + void force_rebuild(const std::vector& componentsToBuild); // Run before actually updating the component void preupdate_component(const CollabListType::ObjectInfoPtr& c); - void invalidate_cache_before_pos(uint64_t placementToInvalidateAt); void invalidate_cache_at_aabb_before_pos(const SCollision::AABB& aabb, uint64_t placementToInvalidateAt); - private: + void clear(); + void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, uint64_t* lastDrawnComponentPlacement = nullptr); + const std::vector& get_unsorted_component_list() const; + const std::chrono::steady_clock::time_point& get_last_bvh_build_time() const; void clear_own_cached_surfaces(); - bool traverse_bvh_erase_function_recursive(const std::shared_ptr& bvhNode, const SCollision::AABB& aabb, std::function& node, std::vector& components)> f); - - void traverse_bvh_collision_check_recursive(const std::shared_ptr& bvhNode, const SCollision::ColliderCollection& checkAgainstWorld, const SCollision::ColliderCollection& checkAgainstCam); - void traverse_bvh_collision_check_recursive_tiny(const std::shared_ptr& bvhNode); - - void build_bvh_node_coords_and_resolution(BVHNode& node); - void refresh_draw_cache(const std::shared_ptr& bvhNode, const DrawData& drawData); - + private: + bool traverse_bvh_erase_function_recursive(const std::shared_ptr& bvhNode, const SCollision::AABB& aabb, std::function& node, std::vector& components)> f); + void build_bvh_node_coords_and_resolution(DrawingProgramCacheBVHNode& node); + void refresh_draw_cache(const std::shared_ptr& bvhNode, const DrawData& drawData); void build(std::vector components); + void traverse_bvh_run_function_recursive(const std::shared_ptr& bvhNode, const SCollision::AABB& aabb, std::function& node, const std::vector& components)> f); + std::pair, std::vector::iterator> get_bvh_node_fully_containing_recursive(const std::shared_ptr& bvhNode, DrawComponent* c); + void build_bvh_node(const std::shared_ptr& bvhNode, std::vector components); std::chrono::steady_clock::time_point lastBvhBuildTime; - - void traverse_bvh_run_function_recursive(const std::shared_ptr& bvhNode, const SCollision::AABB& aabb, std::function& node, const std::vector& components)> f); - std::pair, std::vector::iterator> get_bvh_node_fully_containing_recursive(const std::shared_ptr& bvhNode, DrawComponent* c); - - void build_bvh_node(const std::shared_ptr& bvhNode, std::vector components); - - std::shared_ptr bvhRoot; + std::shared_ptr bvhRoot; std::vector unsortedComponents; - - std::unordered_set componentsSetToErase; - DrawingProgram& drawP; }; diff --git a/src/DrawingProgram/DrawingProgramSelection.cpp b/src/DrawingProgram/DrawingProgramSelection.cpp new file mode 100644 index 0000000..6be71d3 --- /dev/null +++ b/src/DrawingProgram/DrawingProgramSelection.cpp @@ -0,0 +1,119 @@ +#include "DrawingProgramSelection.hpp" +#include "DrawingProgram.hpp" +#include "../World.hpp" +#include "../MainProgram.hpp" + +DrawingProgramSelection::DrawingProgramSelection(DrawingProgram& initDrawP): + cache(initDrawP), + drawP(initDrawP) +{} + +void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection& cC) { + auto cCWorld = drawP.world.drawData.cam.c.collider_to_world, SCollision::ColliderCollection>(cC); + + drawP.compCache.disableRefresh = true; + + std::unordered_set selectedComponents; + + std::function&)> fullyCollidedFunc = [&](const std::shared_ptr& bvhNode) { + if(bvhNode) { + for(auto& c : bvhNode->components) + selectedComponents.emplace(c); + for(auto& p : bvhNode->children) + fullyCollidedFunc(p); + } + }; + drawP.compCache.traverse_bvh_erase_function(cCWorld.bounds, [&](const auto& bvhNode, auto& comps) { + if(bvhNode && + SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min)) && + SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.max)) && + SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.top_right())) && + SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.bottom_left()))) { + fullyCollidedFunc(bvhNode); + drawP.compCache.invalidate_cache_at_aabb_before_pos(bvhNode->bounds, 0); + return true; + } + std::erase_if(comps, [&](auto& c) { + if(c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC, drawP.colliderAllocated)) { + selectedComponents.emplace(c); + drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->worldAABB.value(), c->obj->collabListInfo.lock()->pos); + return true; + } + return false; + }); + return false; + }); + + selectedSet = selectedComponents; + for(auto& obj : selectedSet) + cache.add_component(obj); + calculate_aabb(); +} + +bool DrawingProgramSelection::is_something_selected() { + return !selectedSet.empty(); +} + +void DrawingProgramSelection::calculate_aabb() { + if(is_something_selected()) { + selectionAABB = (*selectedSet.begin())->obj->worldAABB.value(); + for(auto& c : selectedSet) + selectionAABB.include_aabb_in_bounds(c->obj->worldAABB.value()); + } +} + +void DrawingProgramSelection::deselect_all() { + for(auto& obj : selectedSet) { + obj->obj->coords = selectionTransformCoords.other_coord_space_from_this_space(obj->obj->coords); + obj->obj->final_update(drawP); + } + selectedSet.clear(); + cache.clear(); + selectionTransformCoords = CoordSpaceHelper(); + drawP.compCache.force_rebuild(drawP.components.client_list()); +} + +void DrawingProgramSelection::update() { + if(is_something_selected()) { + if(cache.get_unsorted_component_list().size() >= 1000 && (std::chrono::steady_clock::now() - cache.get_last_bvh_build_time()) >= std::chrono::seconds(5)) + cache.force_rebuild(std::vector(selectedSet.begin(), selectedSet.end())); + if(drawP.world.main.input.key(InputManager::KEY_DRAW_UNSELECT).pressed) + deselect_all(); + if(drawP.world.main.input.key(InputManager::KEY_TEXT_UP).held) + selectionTransformCoords.translate(WorldVec{0, -drawP.world.drawData.cam.c.inverseScale}); + if(drawP.world.main.input.key(InputManager::KEY_TEXT_DOWN).held) + selectionTransformCoords.translate(WorldVec{0, drawP.world.drawData.cam.c.inverseScale}); + if(drawP.world.main.input.key(InputManager::KEY_TEXT_LEFT).held) + selectionTransformCoords.translate(WorldVec{-drawP.world.drawData.cam.c.inverseScale, 0}); + if(drawP.world.main.input.key(InputManager::KEY_TEXT_RIGHT).held) + selectionTransformCoords.translate(WorldVec{drawP.world.drawData.cam.c.inverseScale, 0}); + if(drawP.world.main.input.key(InputManager::KEY_TEXT_CTRL).held) + selectionTransformCoords.scale_about_double(selectionTransformCoords.pos, 1.01); + if(drawP.world.main.input.key(InputManager::KEY_TEXT_SHIFT).held) + selectionTransformCoords.scale_about_double(selectionTransformCoords.pos, 0.99); + } +} + +bool DrawingProgramSelection::mouse_collided_with_selection_aabb() { + return false; +} + +void DrawingProgramSelection::draw(SkCanvas* canvas, const DrawData& drawData) { + if(is_something_selected()) { + DrawData selectionDrawData = drawData; + selectionDrawData.cam.c = selectionTransformCoords.other_coord_space_to_this_space(selectionDrawData.cam.c); + selectionDrawData.cam.set_viewing_area(drawP.world.main.window.size.cast()); + selectionDrawData.refresh_draw_optimizing_values(); + + cache.refresh_all_draw_cache(selectionDrawData); + cache.draw_components_to_canvas(canvas, selectionDrawData); + + canvas->save(); + selectionTransformCoords.transform_sk_canvas(canvas, selectionDrawData); + Vector2f minRectPos = selectionTransformCoords.to_space(selectionAABB.min); + Vector2f maxRectPos = selectionTransformCoords.to_space(selectionAABB.max); + SkPaint p{SkColor4f{0.0f, 0.0f, 1.0f, 0.3f}}; + canvas->drawRect(SkRect::MakeLTRB(minRectPos.x(), minRectPos.y(), maxRectPos.x(), maxRectPos.y()), p); + canvas->restore(); + } +} diff --git a/src/DrawingProgram/DrawingProgramSelection.hpp b/src/DrawingProgram/DrawingProgramSelection.hpp new file mode 100644 index 0000000..9d3b8d9 --- /dev/null +++ b/src/DrawingProgram/DrawingProgramSelection.hpp @@ -0,0 +1,23 @@ +#pragma once +#include "DrawingProgramCache.hpp" + +class DrawingProgram; + +class DrawingProgramSelection { + public: + DrawingProgramSelection(DrawingProgram& initDrawP); + void add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection& cC); + void deselect_all(); + void update(); + void draw(SkCanvas* canvas, const DrawData& drawData); + bool is_something_selected(); + private: + void calculate_aabb(); + bool mouse_collided_with_selection_aabb(); + + SCollision::AABB selectionAABB; + CoordSpaceHelper selectionTransformCoords; + std::unordered_set selectedSet; + DrawingProgramCache cache; + DrawingProgram& drawP; +}; diff --git a/src/DrawingProgram/EditTool.cpp b/src/DrawingProgram/EditTool.cpp index f6cccf3..2f2d38c 100644 --- a/src/DrawingProgram/EditTool.cpp +++ b/src/DrawingProgram/EditTool.cpp @@ -123,36 +123,49 @@ void EditTool::edit_start(const std::shared_ptr& comp) { } void EditTool::tool_update() { - using namespace SCollision; - ColliderCollection ci; - ci.circle.emplace_back(Circle{drawP.world.main.input.mouse.pos, 1.0f}); - ci.recalculate_bounds(); - if(!controls.isEditing) { + SCollision::AABB mouseAABB{drawP.world.get_mouse_world_pos() - WorldVec{0.5f, 0.5f}, drawP.world.get_mouse_world_pos() + WorldVec{0.5f, 0.5f}}; + SCollision::ColliderCollection cMouseAABB; + cMouseAABB.aabb.emplace_back(mouseAABB); + cMouseAABB.recalculate_bounds(); + if(drawP.controls.leftClick) { - drawP.check_all_collisions_transform(ci); - for(auto& comp : drawP.components.client_list() | std::views::reverse) { - if(comp->obj->globalCollisionCheck) { - edit_start(comp->obj); - break; + CollabListType::ObjectInfoPtr lastSelectedObj; + uint64_t lastPos = 0; + + drawP.compCache.traverse_bvh_run_function(mouseAABB, [&](const std::shared_ptr& bvhNode, const std::vector& components) { + for(auto& c : components) { + if(c->pos > lastPos && c->obj->collides_with_world_coords(drawP.world.drawData.cam.c, cMouseAABB, drawP.colliderAllocated)) { + lastSelectedObj = c; + lastPos = c->pos; + } } - } + return true; + }); + + if(lastSelectedObj) + edit_start(lastSelectedObj->obj); } } else { + SCollision::Circle mouseCircle{drawP.world.main.input.mouse.pos, 1.0f}; + SCollision::ColliderCollection cMouseCircle; + cMouseCircle.circle.emplace_back(mouseCircle); + cMouseCircle.recalculate_bounds(); + std::shared_ptr a = controls.compToEdit.lock(); if(a) { bool isMovingPoint = false; bool clickedAway = false; if(!controls.pointDragging && drawP.controls.leftClick) { for(HandleData& h : controls.pointHandles) { - if(SCollision::collide(ci, Circle(drawP.world.drawData.cam.c.to_space(a->coords.from_space(*h.p)), DRAG_POINT_RADIUS))) { + if(SCollision::collide(mouseCircle, SCollision::Circle(drawP.world.drawData.cam.c.to_space(a->coords.from_space(*h.p)), DRAG_POINT_RADIUS))) { controls.pointDragging = &h; isMovingPoint = true; break; } } - if(!isMovingPoint && !a->collides_with_cam_coords(drawP.world.drawData.cam.c, ci, drawP.colliderAllocated)) + if(!isMovingPoint && !a->collides_with_cam_coords(drawP.world.drawData.cam.c, cMouseCircle, drawP.colliderAllocated)) clickedAway = true; } else if(controls.pointDragging) { diff --git a/src/DrawingProgram/EraserTool.cpp b/src/DrawingProgram/EraserTool.cpp index bab2f9e..23d1b94 100644 --- a/src/DrawingProgram/EraserTool.cpp +++ b/src/DrawingProgram/EraserTool.cpp @@ -31,7 +31,7 @@ void EraserTool::reset_tool() { drawP.world.undo.push(UndoManager::UndoRedoPair{ [&, erasedCompVal = erasedComponents]() { - std::vector sortedObjects(erasedCompVal.begin(), erasedCompVal.end()); + std::vector sortedObjects(erasedCompVal.begin(), erasedCompVal.end()); std::sort(sortedObjects.begin(), sortedObjects.end(), [](auto& a, auto& b) { return a->pos < b->pos; }); @@ -61,7 +61,7 @@ void EraserTool::reset_tool() { drawP.reset_tools(); - drawP.compCache.force_rebuild(); + drawP.compCache.force_rebuild(drawP.components.client_list()); return true; } @@ -97,10 +97,7 @@ void EraserTool::tool_update() { std::erase_if(comps, [&](auto& c) { if(c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC, drawP.colliderAllocated)) { erasedComponents.emplace(c); - if(c->obj->worldAABB) - drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->worldAABB.value(), c->obj->collabListInfo.lock()->pos); - else - drawP.compCache.invalidate_cache_before_pos(c->obj->collabListInfo.lock()->pos); + drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->worldAABB.value(), c->obj->collabListInfo.lock()->pos); return true; } return false; @@ -112,7 +109,7 @@ void EraserTool::tool_update() { reset_tool(); } -void EraserTool::move_erased_components_from_bvh_nodes_recursive(const std::shared_ptr& bvhNode) { +void EraserTool::move_erased_components_from_bvh_nodes_recursive(const std::shared_ptr& bvhNode) { for(auto& c : bvhNode->components) erasedComponents.emplace(c); for(auto& p : bvhNode->children) diff --git a/src/DrawingProgram/EraserTool.hpp b/src/DrawingProgram/EraserTool.hpp index d5f804d..7f6bc10 100644 --- a/src/DrawingProgram/EraserTool.hpp +++ b/src/DrawingProgram/EraserTool.hpp @@ -13,10 +13,10 @@ class EraserTool { void reset_tool(); void draw(SkCanvas* canvas, const DrawData& drawData); private: - std::unordered_set, ServerClientID>::ObjectInfoPtr> erasedComponents; - std::unordered_set> erasedBVHNodes; + std::unordered_set erasedComponents; + std::unordered_set> erasedBVHNodes; - void move_erased_components_from_bvh_nodes_recursive(const std::shared_ptr& bvhNode); + void move_erased_components_from_bvh_nodes_recursive(const std::shared_ptr& bvhNode); DrawingProgram& drawP; }; diff --git a/src/DrawingProgram/RectSelectTool.cpp b/src/DrawingProgram/RectSelectTool.cpp index 28316f2..e016cc0 100644 --- a/src/DrawingProgram/RectSelectTool.cpp +++ b/src/DrawingProgram/RectSelectTool.cpp @@ -13,6 +13,13 @@ #include #include +#ifdef USE_SKIA_BACKEND_GRAPHITE + #include +#elif USE_SKIA_BACKEND_GANESH + #include + #include +#endif + #define ROTATION_POINT_RADIUS_MULTIPLIER 0.7 #define ROTATION_POINTS_DISTANCE 20.0 @@ -26,19 +33,17 @@ void RectSelectTool::gui_toolbox() { } void RectSelectTool::reset_tool() { - if(controls.selectionMode == 3) - commit_transformation(); controls = RectSelectControls(); } void RectSelectTool::tool_update() { switch(controls.selectionMode) { case 0: { + drawP.compCache.disableRefresh = false; if(drawP.controls.leftClick) { reset_tool(); controls.coords = drawP.world.drawData.cam.c; controls.selectStartAt = controls.coords.get_mouse_pos(drawP.world); - controls.rotationPointAngle = 0.0; controls.selectionMode = 1; } break; @@ -50,349 +55,31 @@ void RectSelectTool::tool_update() { Vector2f newP2 = cwise_vec_max(controls.selectStartAt, mPos); controls.newT = triangle_from_rect_points(newP1, newP2); if(!drawP.controls.leftClickHeld) { - ColliderCollection selectionCollection; - selectionCollection.triangle.emplace_back(controls.newT[0], controls.newT[1], controls.newT[2]); - selectionCollection.triangle.emplace_back(controls.newT[2], controls.newT[3], controls.newT[0]); - drawP.check_all_collisions_transform(selectionCollection); - for(auto& c : drawP.components.client_list()) - c->obj->selected = c->obj->globalCollisionCheck; - controls.selectionMode = create_selection_aabb() ? 2 : 0; - controls.rotationCenter = controls.finalSelectionAABB.center(); - } - break; - } - case 2: { - Vector2f mPos = drawP.world.main.input.mouse.pos; - bool copyHit = drawP.world.main.input.key(InputManager::KEY_COPY).pressed; - bool cutHit = drawP.world.main.input.key(InputManager::KEY_CUT).pressed; - bool deleteHit = drawP.world.main.input.key(InputManager::KEY_DRAW_DELETE).pressed; - bool unselectHit = drawP.world.main.input.key(InputManager::KEY_DRAW_UNSELECT).pressed; + ColliderCollection cC; + cC.triangle.emplace_back(controls.newT[0], controls.newT[1], controls.newT[2]); + cC.triangle.emplace_back(controls.newT[2], controls.newT[3], controls.newT[0]); - if(drawP.controls.leftClick) { - if(SCollision::collide(mPos, controls.scaleSelectionCircle)) { - controls.scaleStartAt = controls.finalSelectionAABB.max; - controls.scaleCenterPoint = controls.finalSelectionAABB.center(); - set_selected_items(); - controls.selectionMode = 4; - } - else if(SCollision::collide(mPos, controls.rotationCenterCircle)) { - controls.moveStartAt = controls.rotationCenter; - controls.selectionMode = 5; - } - else if(SCollision::collide(mPos, controls.rotationPointCircle)) { - set_selected_items(); - controls.selectionMode = 6; - } - else if(SCollision::collide(drawP.controls.currentMouseWorldPos, controls.finalSelectionAABB)) { - controls.moveStartAt = drawP.controls.currentMouseWorldPos; - set_selected_items(); - controls.selectionMode = 3; - } - else { - controls.selectionMode = 0; - drawP.reset_selection(); - } - } - else if(deleteHit || cutHit) { - selection_to_clipboard(); - std::vector>> erasedCompVal; // - drawP.components.client_erase_if([&](uint64_t oldPlacement, const auto& c) { - if(c->obj->selected) { - erasedCompVal.emplace_back(std::pair>{oldPlacement, c->obj}); - c->obj->client_send_erase(drawP); - return true; - } - return false; - }); - drawP.world.undo.push(UndoManager::UndoRedoPair{ - [&, erasedCompVal]() { - for(auto& comp : erasedCompVal) - if(drawP.components.get_id(comp.second) != ServerClientID{0, 0}) - return false; + drawP.selection.add_from_cam_coord_collider_to_selection(cC); - for(auto& comp : erasedCompVal | std::views::reverse) { - drawP.components.client_insert(comp.first, comp.second); - comp.second->client_send_place(drawP); - } - drawP.reset_tools(); - return true; - }, - [&, erasedCompVal]() { - for(auto& comp : erasedCompVal) - if(drawP.components.get_id(comp.second) == ServerClientID{0, 0}) - return false; - - for(auto& comp : erasedCompVal) { - ServerClientID compID; - comp.second->client_send_erase(drawP); - drawP.components.client_erase(comp.second, compID); - } - drawP.reset_tools(); - return true; - } - }); - controls.selectionMode = 0; - } - else if(copyHit) - selection_to_clipboard(); - else if(unselectHit) { - drawP.reset_selection(); controls.selectionMode = 0; } break; } - case 3: { - WorldVec translationVec = drawP.controls.currentMouseWorldPos - controls.moveStartAt; - if(drawP.controls.leftClickHeld) { - for(auto& item : controls.selectedItems) { - auto newCoords = item.oldCoords; - newCoords.translate(translationVec); - item.comp->coords = newCoords; - item.comp->transform_temp_update(drawP); - item.comp->client_send_transform_temp(drawP); - item.comp->lastTransformTime = std::chrono::steady_clock::now(); - } - } - else { - commit_transformation(); - controls.selectionMode = create_selection_aabb() ? 2 : 0; - controls.rotationCenter = controls.finalSelectionAABB.center(); - } - break; - } - case 4: { - WorldVec centerToScaleStart = controls.scaleStartAt - controls.scaleCenterPoint; - Vector2f centerToScaleStartInCamSpace = drawP.world.drawData.cam.c.normalized_dir_to_space(centerToScaleStart.normalized()); - Vector2f scaleCenterPointInCamSpace = drawP.world.drawData.cam.c.to_space(controls.scaleCenterPoint); - controls.scaleCurrentPoint = drawP.world.drawData.cam.c.from_space(project_point_on_line(drawP.world.main.input.mouse.pos, scaleCenterPointInCamSpace, (scaleCenterPointInCamSpace + centerToScaleStartInCamSpace).eval())); - - WorldVec centerToScaleCurrent = controls.scaleCurrentPoint - controls.scaleCenterPoint; - - WorldScalar centerToScaleStartNorm = centerToScaleStart.norm(); - WorldScalar centerToScaleCurrentNorm = centerToScaleCurrent.norm(); - bool isAnyNumberZero = centerToScaleCurrentNorm == WorldScalar(0) || centerToScaleStartNorm == WorldScalar(0); - if(!isAnyNumberZero) { - bool flipScale = centerToScaleStartNorm < centerToScaleCurrentNorm; - WorldScalar scaleAmount; - if(flipScale) - scaleAmount = centerToScaleCurrentNorm / centerToScaleStartNorm; - else - scaleAmount = centerToScaleStartNorm / centerToScaleCurrentNorm; - - if(drawP.controls.leftClickHeld) { - for(auto& item : controls.selectedItems) { - auto newCoords = item.oldCoords; - newCoords.scale_about(controls.scaleCenterPoint, scaleAmount, flipScale); - item.comp->coords = newCoords; - item.comp->client_send_transform_temp(drawP); - item.comp->transform_temp_update(drawP); - item.comp->lastTransformTime = std::chrono::steady_clock::now(); - } - } - else { - commit_transformation(); - controls.selectionMode = create_selection_aabb() ? 2 : 0; - controls.rotationCenter = controls.finalSelectionAABB.center(); - } - } - break; - } - case 5: { - controls.rotationCenter = drawP.controls.currentMouseWorldPos; - if(!drawP.controls.leftClickHeld) { - controls.selectionMode = 2; - } - break; - } - case 6: { - Vector2f rotationPointDiff = drawP.world.main.input.mouse.pos - drawP.world.drawData.cam.c.to_space(controls.rotationCenter); - controls.rotationPointAngle = std::atan2(rotationPointDiff.y(), rotationPointDiff.x()); - if(drawP.controls.leftClickHeld) { - for(auto& item : controls.selectedItems) { - auto newCoords = item.oldCoords; - newCoords.rotate_about(controls.rotationCenter, controls.rotationPointAngle); - item.comp->coords = newCoords; - item.comp->transform_temp_update(drawP); - item.comp->client_send_transform_temp(drawP); - item.comp->lastTransformTime = std::chrono::steady_clock::now(); - } - } - else { - commit_transformation(); - controls.rotationPointAngle = 0.0; - controls.selectionMode = create_selection_aabb() ? 2 : 0; - } - break; - } } - - update_drag_circles(); -} - -void RectSelectTool::paste_clipboard() { - auto& clipboard = drawP.world.main.clipboard; - if(!clipboard.components.empty()) { - drawP.reset_tools(); - drawP.controls.selectedTool = DrawingProgram::TOOL_RECTSELECT; - drawP.controls.previousSelected = DrawingProgram::TOOL_RECTSELECT; - uint64_t allPlacement = drawP.components.client_list().size(); - std::vector> placedComponents; - WorldVec mousePos = drawP.world.get_mouse_world_pos(); - WorldVec moveVec = clipboard.pos - mousePos; - std::unordered_map resourceRemapIDs; - for(auto& r : clipboard.resources) - resourceRemapIDs[r.first] = drawP.world.rMan.add_resource(r.second); - for(auto& c : clipboard.components) { - uint64_t placement = drawP.components.client_list().size(); - std::shared_ptr newC = c->copy(); - newC->remap_resource_ids(resourceRemapIDs); - newC->coords.translate(-moveVec); - newC->selected = true; - - drawP.components.client_insert(placement, newC); - newC->final_update(drawP); - newC->client_send_place(drawP); - placedComponents.emplace_back(newC); - } - set_selected_items(); - create_selection_aabb(); - controls.rotationCenter = controls.finalSelectionAABB.center(); - controls.selectionMode = 2; - drawP.add_undo_place_components(allPlacement, placedComponents); - } -} - -void RectSelectTool::selection_to_clipboard() { - auto& clipboard = drawP.world.main.clipboard; - clipboard.components.clear(); - std::unordered_set resourceSet; - for(auto& c : drawP.components.client_list()) { - if(c->obj->selected) { - c->obj->get_used_resources(resourceSet); - clipboard.components.emplace_back(c->obj->copy()); - } - } - drawP.world.rMan.copy_resource_set_to_map(resourceSet, clipboard.resources); - clipboard.pos = controls.finalSelectionAABB.center(); -} - -Vector2f RectSelectTool::get_rotation_point_pos_from_angle(double angle) { - return Vector2f{drawP.world.drawData.cam.c.to_space(controls.rotationCenter) + ROTATION_POINTS_DISTANCE * Vector2f{std::cos(angle), std::sin(angle)}}; -} - -void RectSelectTool::update_drag_circles() { - controls.scaleSelectionCircle = SCollision::Circle(drawP.world.drawData.cam.c.to_space(controls.scaleCurrentPoint), DRAG_POINT_RADIUS); - controls.rotationCenterCircle = SCollision::Circle(drawP.world.drawData.cam.c.to_space(controls.rotationCenter), DRAG_POINT_RADIUS); - controls.rotationPoint = get_rotation_point_pos_from_angle(controls.rotationPointAngle); - controls.rotationPointCircle = SCollision::Circle(controls.rotationPoint, ROTATION_POINT_RADIUS_MULTIPLIER * DRAG_POINT_RADIUS); -} - -bool RectSelectTool::create_selection_aabb() { - controls.coords = drawP.world.drawData.cam.c; - - controls.finalSelectionAABB = SCollision::AABB(); - bool first = true; - for(auto& c : drawP.components.client_list()) { - if(c->obj->selected && c->obj->get_world_bounds()) { - if(first) { - controls.finalSelectionAABB = *c->obj->get_world_bounds(); - first = false; - } - else - controls.finalSelectionAABB.include_aabb_in_bounds(*c->obj->get_world_bounds()); - } - } - - controls.scaleCurrentPoint = controls.finalSelectionAABB.max; - - return !first; -} - -void RectSelectTool::set_selected_items() { - controls.selectedItems.clear(); - for(auto& c : drawP.components.client_list()) { - if(c->obj->selected) { - controls.selectedItems.emplace_back(c->obj, c->id, c->obj->coords); - c->obj->lastTransformTime = std::chrono::steady_clock::now(); - } - } -} - -void RectSelectTool::commit_transformation() { - std::vector finalCoords; - for(auto& item : controls.selectedItems) { - item.comp->client_send_transform_final(drawP); - item.comp->finalize_update(drawP); - finalCoords.emplace_back(item.comp->coords); - } - - auto sItems = controls.selectedItems; - - drawP.world.undo.push(UndoManager::UndoRedoPair{ - [&, sItems, finalCoords]() { - for(auto& item : sItems) { - item.comp->coords = item.oldCoords; - item.comp->client_send_transform_final(drawP); - item.comp->finalize_update(drawP); - } - drawP.reset_tools(); - return true; - }, - [&, sItems, finalCoords]() { - for(auto [item, finalCoord] : std::views::zip(sItems, finalCoords)) { - item.comp->coords = finalCoord; - item.comp->client_send_transform_final(drawP); - item.comp->finalize_update(drawP); - } - drawP.reset_tools(); - return true; - } - }); } void RectSelectTool::draw(SkCanvas* canvas, const DrawData& drawData) { - if(controls.selectionMode != 0) { + if(controls.selectionMode == 1) { canvas->save(); controls.coords.transform_sk_canvas(canvas, drawData); SkPaint selectionPaint; selectionPaint.setColor4f({0.3f, 0.6f, 0.9f, 0.4f}); - if(controls.selectionMode == 1) { - SkRect r = SkRect::MakeLTRB(controls.newT[0].x(), controls.newT[0].y(), controls.newT[2].x(), controls.newT[2].y()); - canvas->drawRect(r, selectionPaint); - canvas->restore(); - } - if(controls.selectionMode == 2 || controls.selectionMode == 5) { - auto triArray = triangle_from_rect_points(controls.finalSelectionAABB.min, controls.finalSelectionAABB.max); - std::array triArray2; - triArray2[0] = controls.coords.to_space(controls.finalSelectionAABB.min); - triArray2[1] = controls.coords.to_space(controls.finalSelectionAABB.top_right()); - triArray2[2] = controls.coords.to_space(controls.finalSelectionAABB.bottom_left()); - triArray2[3] = controls.coords.to_space(controls.finalSelectionAABB.max); - SkPath a; - a.moveTo(triArray2[0].x(), triArray2[0].y()); - a.lineTo(triArray2[1].x(), triArray2[1].y()); - a.lineTo(triArray2[2].x(), triArray2[2].y()); - a.close(); - a.moveTo(triArray2[1].x(), triArray2[1].y()); - a.lineTo(triArray2[2].x(), triArray2[2].y()); - a.lineTo(triArray2[3].x(), triArray2[3].y()); - a.close(); - canvas->drawPath(a, selectionPaint); - // SkPaint rotateCirclePaint(SkColor4f{0.9f, 0.5f, 0.1f, 1.0f}); - } - + SkRect r = SkRect::MakeLTRB(controls.newT[0].x(), controls.newT[0].y(), controls.newT[2].x(), controls.newT[2].y()); + canvas->drawRect(r, selectionPaint); canvas->restore(); - if(controls.selectionMode == 6 || controls.selectionMode == 5 || controls.selectionMode == 2) { - drawP.draw_drag_circle(canvas, controls.rotationPoint, {0.9f, 0.5f, 0.1f, 1.0f}, drawData, ROTATION_POINT_RADIUS_MULTIPLIER); - drawP.draw_drag_circle(canvas, drawData.cam.c.to_space(controls.rotationCenter), {0.9f, 0.5f, 0.1f, 1.0f}, drawData); - } - if(controls.selectionMode == 4 || controls.selectionMode == 2) { - drawP.draw_drag_circle(canvas, drawData.cam.c.to_space(controls.scaleCurrentPoint), {0.1f, 0.9f, 0.9f, 1.0f}, drawData); - if(controls.selectionMode == 4) - drawP.draw_drag_circle(canvas, drawData.cam.c.to_space(controls.scaleCenterPoint), {0.0f, 0.0f, 0.0f, 0.0f}, drawData); - } + canvas->restore(); } } diff --git a/src/DrawingProgram/RectSelectTool.hpp b/src/DrawingProgram/RectSelectTool.hpp index f047e88..e11f2d8 100644 --- a/src/DrawingProgram/RectSelectTool.hpp +++ b/src/DrawingProgram/RectSelectTool.hpp @@ -14,45 +14,17 @@ class RectSelectTool { void tool_update(); void draw(SkCanvas* canvas, const DrawData& drawData); void reset_tool(); - void paste_clipboard(); private: struct RectSelectControls { int selectionMode = 0; - Vector2f selectStartAt; + + CoordSpaceHelper coords; - WorldVec moveStartAt; - SCollision::AABB finalSelectionAABB; + Vector2f selectStartAt; + std::array newT; - - WorldVec scaleStartAt; - WorldVec scaleCenterPoint; - WorldVec scaleCurrentPoint; - - WorldVec rotationCenter; - - // Temporary, per frame colliders for handles - Vector2f rotationPoint; - SCollision::Circle scaleSelectionCircle; - SCollision::Circle rotationCenterCircle; - SCollision::Circle rotationPointCircle; - - double rotationPointAngle; - - struct SelectedItemInfo { - std::shared_ptr comp; - ServerClientID id; - CoordSpaceHelper oldCoords; - }; - std::vector selectedItems; } controls; - void selection_to_clipboard(); - Vector2f get_rotation_point_pos_from_angle(double angle); - void update_drag_circles(); - void set_selected_items(); - bool create_selection_aabb(); - void commit_transformation(); - DrawingProgram& drawP; };