Selection and transformation works with layers

This commit is contained in:
Yousef Khadadeh 2025-12-29 15:04:32 +03:00
parent 7549ad23a1
commit 3df74095a3
16 changed files with 789 additions and 176 deletions

View file

@ -58,6 +58,7 @@ set(sources "src/main.cpp"
"src/RichText/TextBox.cpp" "src/RichText/TextBox.cpp"
"src/RichText/TextStyleModifier.cpp" "src/RichText/TextStyleModifier.cpp"
"src/RichText/ParagraphStyleData.cpp" "src/RichText/ParagraphStyleData.cpp"
"src/DrawingProgram/DrawingProgramSelection.cpp"
"src/DrawingProgram/DrawingProgramCache.cpp" "src/DrawingProgram/DrawingProgramCache.cpp"
"src/DrawingProgram/DrawingProgram.cpp" "src/DrawingProgram/DrawingProgram.cpp"
"src/DrawingProgram/Tools/BrushTool.cpp" "src/DrawingProgram/Tools/BrushTool.cpp"

View file

@ -67,18 +67,18 @@ void CanvasComponentContainer::set_owner_obj_info(const ObjInfoSharedPtr& ownerO
void CanvasComponentContainer::commit_update(DrawingProgram& drawP) { void CanvasComponentContainer::commit_update(DrawingProgram& drawP) {
auto lockedObjInfo = objInfo.lock(); auto lockedObjInfo = objInfo.lock();
if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
drawP.drawCache.preupdate_component(lockedObjInfo); drawP.preupdate_component(lockedObjInfo);
get_comp().initialize_draw_data(drawP); get_comp().initialize_draw_data(drawP);
calculate_world_bounds(); calculate_world_bounds();
drawP.drawCache.preupdate_component(lockedObjInfo); drawP.preupdate_component(lockedObjInfo);
} }
void CanvasComponentContainer::commit_transform(DrawingProgram& drawP) { void CanvasComponentContainer::commit_transform(DrawingProgram& drawP) {
auto lockedObjInfo = objInfo.lock(); auto lockedObjInfo = objInfo.lock();
if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt
drawP.drawCache.preupdate_component(lockedObjInfo); drawP.preupdate_component(lockedObjInfo);
calculate_world_bounds(); calculate_world_bounds();
drawP.drawCache.preupdate_component(lockedObjInfo); drawP.preupdate_component(lockedObjInfo);
} }
void CanvasComponentContainer::commit_update_dont_invalidate_cache(DrawingProgram& drawP) { void CanvasComponentContainer::commit_update_dont_invalidate_cache(DrawingProgram& drawP) {

View file

@ -8,8 +8,8 @@
#include "CanvasComponentAllocator.hpp" #include "CanvasComponentAllocator.hpp"
class DrawingProgram; class DrawingProgram;
class DrawingProgramCacheBVHNode;
class DrawingProgramLayerListItem; class DrawingProgramLayerListItem;
struct DrawingProgramCacheBVHNode;
class CanvasComponentContainer { class CanvasComponentContainer {
public: public:
@ -45,7 +45,7 @@ class CanvasComponentContainer {
void set_owner_obj_info(const ObjInfoSharedPtr& ownerObjInfo); void set_owner_obj_info(const ObjInfoSharedPtr& ownerObjInfo);
void send_comp_update(DrawingProgram& drawP, bool finalUpdate); void send_comp_update(DrawingProgram& drawP, bool finalUpdate);
std::weak_ptr<DrawingProgramCacheBVHNode> parentBvhNode; // Should be removed later, the new cache uses an unordered_map to map objects to their cache node std::weak_ptr<DrawingProgramCacheBVHNode> cacheParentBvhNode;
DrawingProgramLayerListItem* parentLayer = nullptr; DrawingProgramLayerListItem* parentLayer = nullptr;
CoordSpaceHelper coords; CoordSpaceHelper coords;
private: private:

View file

@ -65,7 +65,7 @@ void ImageCanvasComponent::update(DrawingProgram& drawP) {
ResourceDisplay* display = drawP.world.drawData.rMan->get_display_data(d.imageID); ResourceDisplay* display = drawP.world.drawData.rMan->get_display_data(d.imageID);
if(display) { if(display) {
if(display->update_draw()) if(display->update_draw())
drawP.drawCache.invalidate_cache_at_aabb(compContainer->get_world_bounds()); drawP.invalidate_cache_at_component(compContainer->objInfo.lock());
} }
} }

View file

@ -35,7 +35,8 @@
DrawingProgram::DrawingProgram(World& initWorld): DrawingProgram::DrawingProgram(World& initWorld):
world(initWorld), world(initWorld),
drawCache(*this), drawCache(*this),
layerMan(*this) layerMan(*this),
selection(*this)
{ {
drawTool = DrawingProgramToolBase::allocate_tool_type(*this, DrawingProgramToolType::BRUSH); drawTool = DrawingProgramToolBase::allocate_tool_type(*this, DrawingProgramToolType::BRUSH);
} }
@ -107,26 +108,26 @@ bool DrawingProgram::selection_action_menu(Vector2f popupPos) {
bool shouldClose = false; bool shouldClose = false;
t.gui.list_popup_menu("Selection popup menu", popupPos, [&]() { t.gui.list_popup_menu("Selection popup menu", popupPos, [&]() {
t.gui.text_label_light("Selection menu"); t.gui.text_label_light("Selection menu");
//if(t.gui.text_button_left_transparent("Paste", "Paste")) { if(t.gui.text_button_left_transparent("Paste", "Paste")) {
// selection.deselect_all(); selection.deselect_all();
// selection.paste_clipboard(popupPos * t.final_gui_scale()); selection.paste_clipboard(popupPos * t.final_gui_scale());
// shouldClose = true; shouldClose = true;
//} }
//if(selection.is_something_selected()) { if(selection.is_something_selected()) {
// if(t.gui.text_button_left_transparent("Copy", "Copy")) { if(t.gui.text_button_left_transparent("Copy", "Copy")) {
// selection.selection_to_clipboard(); selection.selection_to_clipboard();
// shouldClose = true; shouldClose = true;
// } }
// if(t.gui.text_button_left_transparent("Cut", "Cut")) { if(t.gui.text_button_left_transparent("Cut", "Cut")) {
// selection.selection_to_clipboard(); selection.selection_to_clipboard();
// selection.delete_all(); selection.delete_all();
// shouldClose = true; shouldClose = true;
// } }
// if(t.gui.text_button_left_transparent("Delete", "Delete")) { if(t.gui.text_button_left_transparent("Delete", "Delete")) {
// selection.delete_all(); selection.delete_all();
// shouldClose = true; shouldClose = true;
// } }
//} }
}); });
return !shouldClose; return !shouldClose;
} }
@ -268,7 +269,7 @@ void DrawingProgram::update() {
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_PAN).pressed) else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_PAN).pressed)
switch_to_tool(DrawingProgramToolType::PAN); switch_to_tool(DrawingProgramToolType::PAN);
//selection.update(); selection.update();
drawTool->tool_update(); drawTool->tool_update();
// Switch tools after the tool update, not before, so that we dont have to call erase_component on the toolToSwitchToAfterUpdate as well (components will not be erased in this time period) // Switch tools after the tool update, not before, so that we dont have to call erase_component on the toolToSwitchToAfterUpdate as well (components will not be erased in this time period)
@ -287,6 +288,16 @@ void DrawingProgram::update() {
rebuild_cache(); rebuild_cache();
} }
void DrawingProgram::invalidate_cache_at_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
if(!selection.is_selected(objToCheck))
drawCache.invalidate_cache_at_aabb(objToCheck->obj->get_world_bounds());
}
void DrawingProgram::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
if(!selection.is_selected(objToCheck))
drawCache.preupdate_component(objToCheck);
}
void DrawingProgram::check_updateable_components() { void DrawingProgram::check_updateable_components() {
for(auto& comp : updateableComponents) for(auto& comp : updateableComponents)
comp->obj->get_comp().update(*this); comp->obj->get_comp().update(*this);
@ -338,7 +349,7 @@ void DrawingProgram::rebuild_cache() {
drawCache.build(eraserTool->erasedComponents); drawCache.build(eraserTool->erasedComponents);
} }
else if(is_selection_allowing_tool(drawTool->get_type())) { else if(is_selection_allowing_tool(drawTool->get_type())) {
//compCache.test_rebuild_dont_include_set(components->get_data(), selection.get_selected_set(), true); drawCache.build(selection.get_selected_set());
} }
else else
drawCache.build({}); drawCache.build({});
@ -508,9 +519,9 @@ void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) {
canvas->clear(SkColor4f{0.0f, 0.0f, 0.0f, 0.0f}); canvas->clear(SkColor4f{0.0f, 0.0f, 0.0f, 0.0f});
drawCache.refresh_all_draw_cache(drawData); drawCache.refresh_all_draw_cache(drawData);
drawCache.draw_components_to_canvas(canvas, drawData, {}); drawCache.draw_components_to_canvas(canvas, drawData, {});
//canvas->saveLayer(nullptr, nullptr); canvas->saveLayer(nullptr, nullptr);
// selection.draw_components(canvas, drawData); selection.draw_components(canvas, drawData);
//canvas->restore(); canvas->restore();
canvas->restore(); canvas->restore();
if(!drawData.main->takingScreenshot) { if(!drawData.main->takingScreenshot) {
@ -526,7 +537,7 @@ void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) {
} }
} }
//selection.draw_gui(canvas, drawData); selection.draw_gui(canvas, drawData);
} }
drawTool->draw(canvas, drawData); drawTool->draw(canvas, drawData);

View file

@ -16,6 +16,7 @@
#include <Helpers/FileDownloader.hpp> #include <Helpers/FileDownloader.hpp>
#include <Helpers/NetworkingObjects/NetObjOrderedList.hpp> #include <Helpers/NetworkingObjects/NetObjOrderedList.hpp>
#include "Layers/DrawingProgramLayerManager.hpp" #include "Layers/DrawingProgramLayerManager.hpp"
#include "DrawingProgramSelection.hpp"
class World; class World;
@ -49,6 +50,8 @@ class DrawingProgram {
void switch_to_tool_ptr(std::unique_ptr<DrawingProgramToolBase> newTool); void switch_to_tool_ptr(std::unique_ptr<DrawingProgramToolBase> newTool);
void modify_grid(const NetworkingObjects::NetObjWeakPtr<WorldGrid>& gridToModify); void modify_grid(const NetworkingObjects::NetObjWeakPtr<WorldGrid>& gridToModify);
void invalidate_cache_at_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
private: private:
void drag_drop_update(); void drag_drop_update();
void add_file_to_canvas_by_path_execute(const std::filesystem::path& filePath, Vector2f dropPos); void add_file_to_canvas_by_path_execute(const std::filesystem::path& filePath, Vector2f dropPos);
@ -70,7 +73,7 @@ class DrawingProgram {
bool is_actual_selection_tool(DrawingProgramToolType typeToCheck); bool is_actual_selection_tool(DrawingProgramToolType typeToCheck);
bool is_selection_allowing_tool(DrawingProgramToolType typeToCheck); bool is_selection_allowing_tool(DrawingProgramToolType typeToCheck);
//DrawingProgramSelection selection; DrawingProgramSelection selection;
std::unique_ptr<DrawingProgramToolBase> toolToSwitchToAfterUpdate; std::unique_ptr<DrawingProgramToolBase> toolToSwitchToAfterUpdate;
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> updateableComponents; std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> updateableComponents;

View file

@ -2,6 +2,7 @@
#include "DrawingProgram.hpp" #include "DrawingProgram.hpp"
#include "../World.hpp" #include "../World.hpp"
#include "../MainProgram.hpp" #include "../MainProgram.hpp"
#include "Layers/DrawingProgramLayerManager.hpp"
#ifdef USE_SKIA_BACKEND_GRAPHITE #ifdef USE_SKIA_BACKEND_GRAPHITE
#include <include/gpu/graphite/Surface.h> #include <include/gpu/graphite/Surface.h>
@ -10,7 +11,7 @@
#include <include/gpu/ganesh/SkSurfaceGanesh.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h>
#endif #endif
std::unordered_map<std::shared_ptr<DrawingProgramCache::BVHNode>, DrawingProgramCache::NodeCache> DrawingProgramCache::nodeCacheMap; std::unordered_map<std::shared_ptr<DrawingProgramCacheBVHNode>, DrawingProgramCache::NodeCache> DrawingProgramCache::nodeCacheMap;
DrawingProgramCache::DrawingProgramCache(DrawingProgram& initDrawP): DrawingProgramCache::DrawingProgramCache(DrawingProgram& initDrawP):
drawP(initDrawP) drawP(initDrawP)
@ -22,9 +23,11 @@ void DrawingProgramCache::add_component(const CanvasComponentContainer::ObjInfoS
} }
void DrawingProgramCache::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) { void DrawingProgramCache::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
auto it = objToBVHNode.find(c); auto cacheParentBvhNodeLock = c->obj->cacheParentBvhNode.lock();
if(it != objToBVHNode.end()) if(cacheParentBvhNodeLock) {
objToBVHNode.erase(it); std::erase(cacheParentBvhNodeLock->components, c);
c->obj->cacheParentBvhNode.reset();
}
else else
std::erase(unsortedComponents, c); std::erase(unsortedComponents, c);
invalidate_cache_at_aabb(c->obj->get_world_bounds()); invalidate_cache_at_aabb(c->obj->get_world_bounds());
@ -52,8 +55,6 @@ bool DrawingProgramCache::unsorted_components_exist() const {
} }
void DrawingProgramCache::build(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToExclude) { void DrawingProgramCache::build(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToExclude) {
static int i = 0;
std::cout << "rebuilding... " << i++ << std::endl;
internal_build(drawP.layerMan.get_flattened_component_list(), objsToExclude); internal_build(drawP.layerMan.get_flattened_component_list(), objsToExclude);
} }
@ -61,7 +62,7 @@ void DrawingProgramCache::internal_build(std::vector<CanvasComponentContainer::O
std::erase_if(componentsToBuild, [&objsToNotInclude](auto& c) { std::erase_if(componentsToBuild, [&objsToNotInclude](auto& c) {
return objsToNotInclude.contains(c); return objsToNotInclude.contains(c);
}); });
bvhRoot = std::make_shared<BVHNode>(); bvhRoot = std::make_shared<DrawingProgramCacheBVHNode>();
unsortedComponents.clear(); unsortedComponents.clear();
clear_own_cached_surfaces(); clear_own_cached_surfaces();
build_bvh_node(bvhRoot, componentsToBuild); build_bvh_node(bvhRoot, componentsToBuild);
@ -73,17 +74,52 @@ void DrawingProgramCache::clear_own_cached_surfaces() {
}); });
} }
CanvasComponentContainer::ObjInfoSharedPtr DrawingProgramCache::get_front_object_colliding_with_in_editing_layer(const SCollision::ColliderCollection<float>& cC) {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
CanvasComponentContainer::ObjInfoSharedPtr p;
traverse_bvh_run_function(cCWorld.bounds, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
node_loop_components(bvhNode, [&](const auto& c) {
if(drawP.layerMan.component_passes_layer_selector(c, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED) && (!p || c->pos >= p->pos) && c->obj->collides_with_world_coords(drawP.world.drawData.cam.c, cCWorld))
p = c;
});
return true;
});
return p;
}
void DrawingProgramCache::node_loop_erase_if_components(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, std::function<bool(const CanvasComponentContainer::ObjInfoSharedPtr& comp)> f) {
if(bvhNode) {
std::erase_if(bvhNode->components, [&f](const auto& comp) {
if(f(comp)) {
comp->obj->cacheParentBvhNode.reset();
return true;
}
return false;
});
}
else
std::erase_if(unsortedComponents, f);
}
void DrawingProgramCache::node_loop_components(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, std::function<void(const CanvasComponentContainer::ObjInfoSharedPtr& comp)> f) {
if(bvhNode)
std::for_each(bvhNode->components.begin(), bvhNode->components.end(), f);
else
std::for_each(unsortedComponents.begin(), unsortedComponents.end(), f);
}
void DrawingProgramCache::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) { void DrawingProgramCache::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
// Can be called even if object isn't in cache yet. In that case, it'll just invalidate the cache at the object's AABB // Can be called even if object isn't in cache yet. In that case, it'll just invalidate the cache at the object's AABB
auto it = objToBVHNode.find(c); auto cacheParentBvhNodeLock = c->obj->cacheParentBvhNode.lock();
if(it != objToBVHNode.end()) { if(cacheParentBvhNodeLock) {
unsortedComponents.emplace_back(c); unsortedComponents.emplace_back(c);
std::erase(it->second->components, c); std::erase(cacheParentBvhNodeLock->components, c);
c->obj->cacheParentBvhNode.reset();
} }
invalidate_cache_at_aabb(c->obj->get_world_bounds()); invalidate_cache_at_aabb(c->obj->get_world_bounds());
} }
void DrawingProgramCache::build_bvh_node(const std::shared_ptr<BVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components) { void DrawingProgramCache::build_bvh_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components) {
if(components.empty()) if(components.empty())
return; return;
@ -96,7 +132,7 @@ void DrawingProgramCache::build_bvh_node(const std::shared_ptr<BVHNode>& bvhNode
if(components.size() < MAXIMUM_COMPONENTS_IN_SINGLE_NODE) { if(components.size() < MAXIMUM_COMPONENTS_IN_SINGLE_NODE) {
bvhNode->components = components; bvhNode->components = components;
for(auto& c : bvhNode->components) for(auto& c : bvhNode->components)
objToBVHNode.emplace(c, bvhNode); c->obj->cacheParentBvhNode = bvhNode;
return; return;
} }
@ -116,17 +152,17 @@ void DrawingProgramCache::build_bvh_node(const std::shared_ptr<BVHNode>& bvhNode
parts[3].emplace_back(c); parts[3].emplace_back(c);
else { else {
bvhNode->components.emplace_back(c); bvhNode->components.emplace_back(c);
objToBVHNode.emplace(c, bvhNode); c->obj->cacheParentBvhNode = bvhNode;
} }
} }
for(auto& p : parts) { for(auto& p : parts) {
if(!p.empty()) if(!p.empty())
build_bvh_node(bvhNode->children.emplace_back(std::make_shared<BVHNode>()), p); build_bvh_node(bvhNode->children.emplace_back(std::make_shared<DrawingProgramCacheBVHNode>()), 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: // 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 // - 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 // - Some of the data here is needed to determine whether to generate the cache in the first place
@ -152,8 +188,8 @@ void DrawingProgramCache::build_bvh_node_coords_and_resolution(BVHNode& node) {
} }
void DrawingProgramCache::refresh_all_draw_cache(const DrawData& drawData) { void DrawingProgramCache::refresh_all_draw_cache(const DrawData& drawData) {
std::deque<std::shared_ptr<BVHNode>> nodeFlatList; // We want to render children before parents, so that parents can make use of the cached children std::deque<std::shared_ptr<DrawingProgramCacheBVHNode>> 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<BVHNode> node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps) { traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](std::shared_ptr<DrawingProgramCacheBVHNode> node) {
if(node && node->coords.inverseScale <= drawData.cam.c.inverseScale) { if(node && node->coords.inverseScale <= drawData.cam.c.inverseScale) {
auto it = nodeCacheMap.find(node); auto it = nodeCacheMap.find(node);
if((it == nodeCacheMap.end()) || it->second.invalidBounds.has_value()) if((it == nodeCacheMap.end()) || it->second.invalidBounds.has_value())
@ -166,43 +202,26 @@ void DrawingProgramCache::refresh_all_draw_cache(const DrawData& drawData) {
refresh_draw_cache(nodeFlatList.front(), drawData); refresh_draw_cache(nodeFlatList.front(), drawData);
} }
void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) { void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node)> f) {
f(nullptr, unsortedComponents); f(nullptr);
traverse_bvh_run_function_recursive(bvhRoot, aabb, f); traverse_bvh_run_function_starting_at_node(bvhRoot, aabb, f);
} }
void DrawingProgramCache::traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) { void DrawingProgramCache::traverse_bvh_run_function_starting_at_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node)> f) {
f(nullptr, unsortedComponents); if(bvhNode && SCollision::collide(aabb, bvhNode->bounds) && f(bvhNode)) {
if(traverse_bvh_erase_function_recursive(bvhRoot, aabb, f))
bvhRoot = nullptr;
}
void DrawingProgramCache::traverse_bvh_run_function_recursive(const std::shared_ptr<BVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) {
if(bvhNode && SCollision::collide(aabb, bvhNode->bounds) && f(bvhNode, bvhNode->components)) {
for(auto& p : bvhNode->children) for(auto& p : bvhNode->children)
traverse_bvh_run_function_recursive(p, aabb, f); traverse_bvh_run_function_starting_at_node(p, aabb, f);
} }
} }
bool DrawingProgramCache::traverse_bvh_erase_function_recursive(const std::shared_ptr<BVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) { void DrawingProgramCache::traverse_bvh_run_function_starting_at_node_no_collision_check(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node)> f) {
if(bvhNode && SCollision::collide(aabb, bvhNode->bounds)) { if(bvhNode && f(bvhNode)) {
if(f(bvhNode, bvhNode->components)) for(auto& p : bvhNode->children)
return true; traverse_bvh_run_function_starting_at_node_no_collision_check(p, f);
std::erase_if(bvhNode->children, [&](auto& p) {
return traverse_bvh_erase_function_recursive(p, aabb, f);
});
} }
return false;
} }
void DrawingProgramCache::move_components_from_bvh_node_to_set(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& s, const std::shared_ptr<BVHNode>& bvhNode) { void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const DrawData& drawData) {
for(auto& c : bvhNode->components)
s.emplace(c);
for(auto& p : bvhNode->children)
move_components_from_bvh_node_to_set(s, p);
}
void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<BVHNode>& bvhNode, const DrawData& drawData) {
if(bvhNode->children.empty() && bvhNode->components.empty()) if(bvhNode->children.empty() && bvhNode->components.empty())
return; return;
@ -286,10 +305,10 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<BVHNode>& bvh
void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::optional<SCollision::AABB<WorldScalar>>& drawBounds) { void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::optional<SCollision::AABB<WorldScalar>>& drawBounds) {
if(drawP.layerMan.layer_tree_root_exists()) { if(drawP.layerMan.layer_tree_root_exists()) {
std::vector<std::shared_ptr<BVHNode>> cachedNodesToDraw; std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>> cachedNodesToDraw;
std::vector<std::shared_ptr<BVHNode>> uncachedNodes; std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>> uncachedNodes;
traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr<BVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps) { traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& node) {
if(node) { if(node) {
auto it = nodeCacheMap.find(node); auto it = nodeCacheMap.find(node);
if(it != nodeCacheMap.end()) { if(it != nodeCacheMap.end()) {
@ -312,7 +331,7 @@ void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const Draw
} }
} }
void DrawingProgramCache::recursive_draw_layer_item_to_canvas(const DrawingProgramLayerListItem& layerListItem, SkCanvas* canvas, const DrawData& drawData, const std::optional<SCollision::AABB<WorldScalar>>& drawBounds, const std::vector<std::shared_ptr<BVHNode>>& nodesToDraw) { void DrawingProgramCache::recursive_draw_layer_item_to_canvas(const DrawingProgramLayerListItem& layerListItem, SkCanvas* canvas, const DrawData& drawData, const std::optional<SCollision::AABB<WorldScalar>>& drawBounds, const std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>>& nodesToDraw) {
if(layerListItem.get_visible()) { if(layerListItem.get_visible()) {
SkPaint layerPaint; SkPaint layerPaint;
layerPaint.setAlphaf(layerListItem.get_alpha()); layerPaint.setAlphaf(layerListItem.get_alpha());
@ -344,7 +363,7 @@ void DrawingProgramCache::recursive_draw_layer_item_to_canvas(const DrawingProgr
} }
} }
void DrawingProgramCache::draw_cache_image_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<BVHNode>& bvhNode) { void DrawingProgramCache::draw_cache_image_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
auto it = nodeCacheMap.find(bvhNode); auto it = nodeCacheMap.find(bvhNode);
if(it != nodeCacheMap.end()) { if(it != nodeCacheMap.end()) {
auto& nodeCache = it->second; auto& nodeCache = it->second;
@ -360,3 +379,7 @@ void DrawingProgramCache::draw_cache_image_to_canvas(SkCanvas* canvas, const Dra
canvas->restore(); canvas->restore();
} }
} }
DrawingProgramCache::~DrawingProgramCache() {
clear_own_cached_surfaces();
}

View file

@ -2,34 +2,41 @@
#include "../CanvasComponents/CanvasComponentContainer.hpp" #include "../CanvasComponents/CanvasComponentContainer.hpp"
#include <unordered_map> #include <unordered_map>
struct DrawingProgramCacheBVHNode {
public:
SCollision::AABB<WorldScalar> bounds;
CoordSpaceHelper coords;
Vector2i resolution;
private:
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> components;
std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>> children;
friend class DrawingProgramCache;
};
class DrawingProgramCache { class DrawingProgramCache {
public: public:
static constexpr size_t MINIMUM_COMPONENTS_TO_START_REBUILD = 1000; static constexpr size_t MINIMUM_COMPONENTS_TO_START_REBUILD = 10;
static constexpr size_t MAXIMUM_COMPONENTS_IN_SINGLE_NODE = 50; static constexpr size_t MAXIMUM_COMPONENTS_IN_SINGLE_NODE = 3;
static constexpr size_t MAXIMUM_DRAW_CACHE_SURFACES = 40; static constexpr size_t MAXIMUM_DRAW_CACHE_SURFACES = 40;
struct BVHNode {
SCollision::AABB<WorldScalar> bounds;
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> components;
std::vector<std::shared_ptr<BVHNode>> children;
CoordSpaceHelper coords;
Vector2i resolution;
};
DrawingProgramCache(DrawingProgram& initDrawP); DrawingProgramCache(DrawingProgram& initDrawP);
void add_component(const CanvasComponentContainer::ObjInfoSharedPtr& c); void add_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c); void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void clear_own_cached_surfaces(); void clear_own_cached_surfaces();
void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c); void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void build(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToExclude); void build(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToExclude);
void traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f); void traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node)> f);
void traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f); void traverse_bvh_run_function_starting_at_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node)> f);
void traverse_bvh_run_function_starting_at_node_no_collision_check(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node)> f);
void node_loop_erase_if_components(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, std::function<bool(const CanvasComponentContainer::ObjInfoSharedPtr& comp)> f);
void node_loop_components(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, std::function<void(const CanvasComponentContainer::ObjInfoSharedPtr& comp)> f);
bool should_rebuild() const; bool should_rebuild() const;
void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::optional<SCollision::AABB<WorldScalar>>& drawBounds); void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::optional<SCollision::AABB<WorldScalar>>& drawBounds);
bool unsorted_components_exist() const; bool unsorted_components_exist() const;
void invalidate_cache_at_aabb(const SCollision::AABB<WorldScalar>& aabb); void invalidate_cache_at_aabb(const SCollision::AABB<WorldScalar>& aabb);
void move_components_from_bvh_node_to_set(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& s, const std::shared_ptr<BVHNode>& bvhNode);
void refresh_all_draw_cache(const DrawData& drawData); void refresh_all_draw_cache(const DrawData& drawData);
CanvasComponentContainer::ObjInfoSharedPtr get_front_object_colliding_with_in_editing_layer(const SCollision::ColliderCollection<float>& cC);
~DrawingProgramCache();
private: private:
struct NodeCache { struct NodeCache {
sk_sp<SkSurface> surface; sk_sp<SkSurface> surface;
@ -37,19 +44,16 @@ class DrawingProgramCache {
DrawingProgramCache* attachedDrawingProgramCache; DrawingProgramCache* attachedDrawingProgramCache;
std::optional<SCollision::AABB<WorldScalar>> invalidBounds; std::optional<SCollision::AABB<WorldScalar>> invalidBounds;
}; };
static std::unordered_map<std::shared_ptr<BVHNode>, NodeCache> nodeCacheMap; static std::unordered_map<std::shared_ptr<DrawingProgramCacheBVHNode>, NodeCache> nodeCacheMap;
void internal_build(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> componentsToBuild, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude); void internal_build(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> componentsToBuild, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude);
void build_bvh_node(const std::shared_ptr<BVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components); void build_bvh_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components);
void build_bvh_node_coords_and_resolution(BVHNode& node); void build_bvh_node_coords_and_resolution(DrawingProgramCacheBVHNode& node);
void refresh_draw_cache(const std::shared_ptr<BVHNode>& bvhNode, const DrawData& drawData); void refresh_draw_cache(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const DrawData& drawData);
void traverse_bvh_run_function_recursive(const std::shared_ptr<BVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f); void draw_cache_image_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
bool traverse_bvh_erase_function_recursive(const std::shared_ptr<BVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<BVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f); void recursive_draw_layer_item_to_canvas(const DrawingProgramLayerListItem& layerListItem, SkCanvas* canvas, const DrawData& drawData, const std::optional<SCollision::AABB<WorldScalar>>& drawBounds, const std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>>& nodesToDraw);
void draw_cache_image_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<BVHNode>& bvhNode);
void recursive_draw_layer_item_to_canvas(const DrawingProgramLayerListItem& layerListItem, SkCanvas* canvas, const DrawData& drawData, const std::optional<SCollision::AABB<WorldScalar>>& drawBounds, const std::vector<std::shared_ptr<BVHNode>>& nodesToDraw);
std::unordered_map<CanvasComponentContainer::ObjInfoSharedPtr, std::shared_ptr<BVHNode>> objToBVHNode; std::shared_ptr<DrawingProgramCacheBVHNode> bvhRoot;
std::shared_ptr<BVHNode> bvhRoot;
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> unsortedComponents; std::vector<CanvasComponentContainer::ObjInfoSharedPtr> unsortedComponents;
DrawingProgram& drawP; DrawingProgram& drawP;
}; };

View file

@ -0,0 +1,471 @@
#include "DrawingProgramSelection.hpp"
#include "DrawingProgram.hpp"
#include "../World.hpp"
#include "../MainProgram.hpp"
#include "../InputManager.hpp"
#include <Helpers/Parallel.hpp>
#ifdef USE_SKIA_BACKEND_GRAPHITE
#include <include/gpu/graphite/Surface.h>
#elif USE_SKIA_BACKEND_GANESH
#include <include/gpu/ganesh/GrDirectContext.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#endif
#include <include/effects/SkImageFilters.h>
#include <include/core/SkPathBuilder.h>
#define ROTATION_POINT_RADIUS_MULTIPLIER 0.7f
#define ROTATION_POINTS_DISTANCE 20.0f
DrawingProgramSelection::DrawingProgramSelection(DrawingProgram& initDrawP):
drawP(initDrawP)
{}
void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly) {
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> selectedComponents;
if(frontObjectOnly) {
CanvasComponentContainer::ObjInfoSharedPtr a = drawP.drawCache.get_front_object_colliding_with_in_editing_layer(cC);
if(a) {
selectedComponents.emplace(a);
drawP.drawCache.erase_component(a);
}
}
else {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
drawP.drawCache.traverse_bvh_run_function(cCWorld.bounds, erase_select_objects_in_bvh_func(selectedComponents, cC, cCWorld, layerSelector));
}
add_to_selection(selectedComponents);
}
void DrawingProgramSelection::remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly) {
if(frontObjectOnly) {
CanvasComponentContainer::ObjInfoSharedPtr p = get_front_object_colliding_with_in_editing_layer(cC);
if(p) {
drawP.drawCache.add_component(p);
selectedSet.erase(p);
}
}
else {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
std::erase_if(selectedSet, [&](auto& c) {
if(drawP.layerMan.component_passes_layer_selector(c, layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) {
drawP.drawCache.add_component(c);
return true;
}
return false;
});
}
calculate_aabb();
}
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&)> DrawingProgramSelection::erase_select_objects_in_bvh_func(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld, DrawingProgramLayerManager::LayerSelector layerSelector) {
auto toRet = [&](const auto& bvhNode) {
if(bvhNode && (bvhNode->coords.inverseScale << 9) < drawP.world.drawData.cam.c.inverseScale &&
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()))) {
drawP.drawCache.invalidate_cache_at_aabb(bvhNode->bounds);
drawP.drawCache.traverse_bvh_run_function_starting_at_node_no_collision_check(bvhNode, [&](const auto& bvhNodeChild) {
drawP.drawCache.node_loop_erase_if_components(bvhNodeChild, [&](auto& c) {
if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector)) {
selectedComponents.emplace(c);
return true;
}
return false;
});
return true;
});
return false;
}
drawP.drawCache.node_loop_erase_if_components(bvhNode, [&](auto& c) {
if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) {
selectedComponents.emplace(c);
drawP.drawCache.invalidate_cache_at_aabb(c->obj->get_world_bounds());
return true;
}
return false;
});
return true;
};
return toRet;
}
void DrawingProgramSelection::add_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection) {
selectedSet.insert(newSelection.begin(), newSelection.end());
calculate_aabb();
}
void DrawingProgramSelection::set_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection) {
selectedSet = newSelection;
calculate_aabb();
}
void DrawingProgramSelection::calculate_aabb() {
if(is_something_selected()) {
initialSelectionAABB = (*selectedSet.begin())->obj->get_world_bounds();
for(auto& c : selectedSet)
initialSelectionAABB.include_aabb_in_bounds(c->obj->get_world_bounds());
rotateData.centerPos = initialSelectionAABB.center();
}
}
bool DrawingProgramSelection::is_something_selected() {
return !selectedSet.empty();
}
bool DrawingProgramSelection::is_selected(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
return selectedSet.contains(objToCheck);
}
void DrawingProgramSelection::reset_all() {
selectedSet.clear();
selectionTransformCoords = CoordSpaceHelperTransform();
transformOpHappening = TransformOperation::NONE;
translateData = TranslationData();
scaleData = ScaleData();
rotateData = RotationData();
}
bool DrawingProgramSelection::mouse_collided_with_selection_aabb() {
return SCollision::collide(camSpaceSelection, drawP.world.main.input.mouse.pos);
}
bool DrawingProgramSelection::mouse_collided_with_scale_point() {
return SCollision::collide(SCollision::Circle(scaleData.handlePoint, drawP.drag_point_radius()), drawP.world.main.input.mouse.pos);
}
bool DrawingProgramSelection::mouse_collided_with_rotate_center_handle_point() {
return SCollision::collide(SCollision::Circle(rotateData.centerHandlePoint, drawP.drag_point_radius()), drawP.world.main.input.mouse.pos);
}
bool DrawingProgramSelection::mouse_collided_with_rotate_handle_point() {
return SCollision::collide(SCollision::Circle(rotateData.handlePoint, drawP.drag_point_radius() * ROTATION_POINT_RADIUS_MULTIPLIER), drawP.world.main.input.mouse.pos);
}
void DrawingProgramSelection::commit_transform_selection() {
auto selectedSetTemp = selectedSet;
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> a(selectedSet.begin(), selectedSet.end());
std::vector<std::pair<CanvasComponentContainer::ObjInfoSharedPtr, CoordSpaceHelper>> transformsFrom;
for(auto& transformedObj : a)
transformsFrom.emplace_back(transformedObj, transformedObj->obj->coords);
for(auto& comp : a) {
comp->obj->coords = selectionTransformCoords.other_coord_space_from_this_space(comp->obj->coords);
comp->obj->commit_transform(drawP);
}
std::vector<std::pair<CanvasComponentContainer::ObjInfoSharedPtr, CoordSpaceHelper>> transformsTo;
std::vector<std::pair<NetworkingObjects::NetObjID, CoordSpaceHelper>> transformsToSend;
for(auto& transformedObj : a) {
transformsToSend.emplace_back(transformedObj->obj.get_net_id(), transformedObj->obj->coords);
transformsTo.emplace_back(transformedObj, transformedObj->obj->coords);
}
//DrawComponent::client_send_transform_many(drawP, transformsToSend);
reset_all();
set_to_selection(selectedSetTemp);
}
void DrawingProgramSelection::update() {
if(is_something_selected()) {
selectionRectPoints[0] = selectionTransformCoords.from_space_world(initialSelectionAABB.min);
selectionRectPoints[1] = selectionTransformCoords.from_space_world(initialSelectionAABB.bottom_left());
selectionRectPoints[2] = selectionTransformCoords.from_space_world(initialSelectionAABB.max);
selectionRectPoints[3] = selectionTransformCoords.from_space_world(initialSelectionAABB.top_right());
scaleData.handlePoint = drawP.world.drawData.cam.c.to_space(initialSelectionAABB.max);
rotateData.centerHandlePoint = drawP.world.drawData.cam.c.to_space(rotateData.centerPos);
rotateData.handlePoint = get_rotation_point_pos_from_angle(rotateData.rotationAngle);
rebuild_cam_space();
if(drawP.is_actual_selection_tool(drawP.drawTool->get_type())) {
switch(transformOpHappening) {
case TransformOperation::NONE:
if(drawP.controls.leftClick && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) {
if(mouse_collided_with_scale_point()) {
scaleData.currentPos = scaleData.startPos = selectionTransformCoords.from_space_world(initialSelectionAABB.max);
scaleData.centerPos = selectionTransformCoords.from_space_world(initialSelectionAABB.center());
transformOpHappening = TransformOperation::SCALE;
}
else if(mouse_collided_with_rotate_center_handle_point())
transformOpHappening = TransformOperation::ROTATE_RELOCATE_CENTER;
else if(mouse_collided_with_rotate_handle_point()) {
rotateData.rotationAngle = 0.0;
transformOpHappening = TransformOperation::ROTATE;
}
else if(mouse_collided_with_selection_aabb()) {
translateData.startPos = drawP.world.get_mouse_world_pos();
transformOpHappening = TransformOperation::TRANSLATE;
}
}
break;
case TransformOperation::TRANSLATE:
if(drawP.controls.leftClickHeld)
selectionTransformCoords = CoordSpaceHelperTransform(drawP.world.get_mouse_world_pos() - translateData.startPos);
else {
commit_transform_selection();
return;
}
break;
case TransformOperation::SCALE:
if(drawP.controls.leftClickHeld) {
WorldVec centerToScaleStart = scaleData.startPos - scaleData.centerPos;
Vector2f centerToScaleStartInCamSpace = drawP.world.drawData.cam.c.normalized_dir_to_space(centerToScaleStart.normalized());
Vector2f scaleCenterPointInCamSpace = drawP.world.drawData.cam.c.to_space(scaleData.centerPos);
scaleData.currentPos = drawP.world.drawData.cam.c.from_space(project_point_on_line(drawP.world.main.input.mouse.pos, scaleCenterPointInCamSpace, (scaleCenterPointInCamSpace + centerToScaleStartInCamSpace).eval()));
WorldVec centerToScaleCurrent = scaleData.currentPos - scaleData.centerPos;
WorldScalar centerToScaleStartNorm = centerToScaleStart.norm();
WorldScalar centerToScaleCurrentNorm = centerToScaleCurrent.norm();
bool isAnyNumberZero = centerToScaleCurrentNorm == WorldScalar(0) || centerToScaleStartNorm == WorldScalar(0);
if(!isAnyNumberZero) {
WorldMultiplier scaleAmount = WorldMultiplier(centerToScaleStartNorm) / WorldMultiplier(centerToScaleCurrentNorm);
selectionTransformCoords = CoordSpaceHelperTransform(scaleData.centerPos, scaleAmount);
}
}
else {
commit_transform_selection();
return;
}
break;
case TransformOperation::ROTATE_RELOCATE_CENTER:
if(drawP.controls.leftClickHeld)
rotateData.centerPos = drawP.world.get_mouse_world_pos();
else
transformOpHappening = TransformOperation::NONE;
break;
case TransformOperation::ROTATE:
if(drawP.controls.leftClickHeld) {
Vector2f rotationPointDiff = drawP.world.main.input.mouse.pos - rotateData.centerHandlePoint;
rotateData.rotationAngle = std::atan2(rotationPointDiff.y(), rotationPointDiff.x());
selectionTransformCoords = CoordSpaceHelperTransform(rotateData.centerPos, rotateData.rotationAngle);
}
else {
commit_transform_selection();
return;
}
break;
}
}
else if(transformOpHappening != TransformOperation::NONE) {
commit_transform_selection();
return;
}
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_ESCAPE).pressed)
deselect_all();
else if(drawP.world.main.input.key(InputManager::KEY_DRAW_DELETE).pressed)
delete_all();
else if(drawP.world.main.input.key(InputManager::KEY_COPY).pressed && !is_being_transformed())
selection_to_clipboard();
else if(drawP.world.main.input.key(InputManager::KEY_CUT).pressed && !is_being_transformed()) {
selection_to_clipboard();
delete_all();
}
else if(drawP.world.main.input.key(InputManager::KEY_PASTE).pressed) {
deselect_all();
paste_clipboard(drawP.world.main.input.mouse.pos);
}
}
else if(drawP.world.main.input.key(InputManager::KEY_PASTE).pressed && !drawP.prevent_undo_or_redo())
paste_clipboard(drawP.world.main.input.mouse.pos);
else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_ESCAPE).pressed)
drawP.switch_to_tool(DrawingProgramToolType::EDIT, true);
}
void DrawingProgramSelection::deselect_all() {
if(is_something_selected()) {
for(auto& obj : selectedSet)
drawP.drawCache.add_component(obj);
reset_all();
}
}
void DrawingProgramSelection::delete_all() {
if(is_something_selected()) {
auto selectedSetTemp = selectedSet;
reset_all(); // Clear set before erasing, since calling erase_component_set will run a check to see if each object is selected and erase them one by one, which is slower
drawP.layerMan.erase_component_set(selectedSetTemp);
}
}
void DrawingProgramSelection::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
if(is_selected(objToCheck)) {
selectedSet.erase(objToCheck);
if(selectedSet.empty())
reset_all();
}
}
CanvasComponentContainer::ObjInfoSharedPtr DrawingProgramSelection::get_front_object_colliding_with_in_editing_layer(const SCollision::ColliderCollection<float>& cC) {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
CanvasComponentContainer::ObjInfoSharedPtr p;
for(auto& c : selectedSet) {
if(drawP.layerMan.component_passes_layer_selector(c, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED) && (!p || c->pos >= p->pos) && c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC))
p = c;
}
return p;
}
void DrawingProgramSelection::selection_to_clipboard() {
//auto& clipboard = drawP.world.main.clipboard;
//std::unordered_set<ServerClientID> resourceSet;
//for(auto& c : selectedSet)
// c->obj->get_used_resources(resourceSet);
//std::vector<CanvasComponentContainer::ObjInfoSharedPtr> compVecSort(selectedSet.begin(), selectedSet.end());
//std::sort(compVecSort.begin(), compVecSort.end(), [&](auto& a, auto& b) {
// return a->pos < b->pos;
//});
//clipboard.components.clear();
//for(auto& c : compVecSort)
// clipboard.components.emplace_back(c->obj->deep_copy(drawP));
//clipboard.pos = initialSelectionAABB.center();
//clipboard.inverseScale = drawP.world.drawData.cam.c.inverseScale;
//drawP.world.rMan.copy_resource_set_to_map(resourceSet, clipboard.resources);
}
void DrawingProgramSelection::paste_clipboard(Vector2f pasteScreenPos) {
//auto& clipboard = drawP.world.main.clipboard;
//if(!drawP.is_selection_allowing_tool(drawP.drawTool->get_type()))
// drawP.switch_to_tool(DrawingProgramToolType::EDIT);
//uint32_t allPlacement = drawP.components->size();
//std::vector<std::shared_ptr<DrawComponent>> placedComponents;
//WorldVec mousePos = drawP.world.drawData.cam.c.from_space(pasteScreenPos);
//WorldVec moveVec = drawP.world.main.clipboard.pos - mousePos;
//WorldMultiplier scaleMultiplier = WorldMultiplier(drawP.world.main.clipboard.inverseScale) / WorldMultiplier(drawP.world.drawData.cam.c.inverseScale);
//WorldScalar scaleLimit(0.001);
//for(auto& c : drawP.world.main.clipboard.components) {
// if((c->coords.inverseScale / scaleMultiplier) < scaleLimit) {
// Logger::get().log("WORLDFATAL", "Some pasted objects will be too small! Scale up the canvas first (zoom in alot)");
// return;
// }
//}
//std::unordered_map<ServerClientID, ServerClientID> resourceRemapIDs;
//for(auto& r : clipboard.resources)
// resourceRemapIDs[r.first] = drawP.world.rMan.add_resource(r.second);
//for(auto& c : clipboard.components)
// placedComponents.emplace_back(c->deep_copy(drawP));
//drawP.addToCompCacheOnInsert = false;
//auto compListInserted = drawP.components.client_insert_ordered_vector_items(allPlacement, placedComponents);
//parallel_loop_container(compListInserted, [&](auto& c) {
// c->obj->remap_resource_ids(resourceRemapIDs);
// c->obj->get_coords().translate(-moveVec);
// c->obj->get_coords().scale_about(mousePos, scaleMultiplier);
// c->obj->commit_transform(drawP, false);
//});
//DrawComponent::client_send_place_many(drawP, compListInserted);
//drawP.addToCompCacheOnInsert = true;
//std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> compSetInserted(compListInserted.begin(), compListInserted.end());
//set_to_selection(compSetInserted);
//drawP.add_undo_place_components(compSetInserted);
}
bool DrawingProgramSelection::is_being_transformed() {
return transformOpHappening != TransformOperation::NONE;
}
const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& DrawingProgramSelection::get_selected_set() {
return selectedSet;
}
void DrawingProgramSelection::rebuild_cam_space() {
SCollision::ColliderCollection<WorldScalar> collideRectWorld;
collideRectWorld.triangle.emplace_back(selectionRectPoints[0], selectionRectPoints[1], selectionRectPoints[2]);
collideRectWorld.triangle.emplace_back(selectionRectPoints[0], selectionRectPoints[2], selectionRectPoints[3]);
collideRectWorld.recalculate_bounds();
camSpaceSelection = drawP.world.drawData.cam.c.world_collider_to_coords<SCollision::ColliderCollection<float>>(collideRectWorld);
}
Vector2f DrawingProgramSelection::get_rotation_point_pos_from_angle(double angle) {
return Vector2f{rotateData.centerHandlePoint + ROTATION_POINTS_DISTANCE * drawP.world.main.toolbar.final_gui_scale() * Vector2f{std::cos(angle), std::sin(angle)}};
}
void DrawingProgramSelection::draw_components(SkCanvas* canvas, const DrawData& drawData) {
if(is_something_selected()) {
selectionRectPoints[0] = selectionTransformCoords.from_space_world(initialSelectionAABB.min);
selectionRectPoints[1] = selectionTransformCoords.from_space_world(initialSelectionAABB.bottom_left());
selectionRectPoints[2] = selectionTransformCoords.from_space_world(initialSelectionAABB.max);
selectionRectPoints[3] = selectionTransformCoords.from_space_world(initialSelectionAABB.top_right());
scaleData.handlePoint = drawP.world.drawData.cam.c.to_space(selectionTransformCoords.from_space_world(initialSelectionAABB.max));
rotateData.centerHandlePoint = drawP.world.drawData.cam.c.to_space(selectionTransformCoords.from_space_world(rotateData.centerPos));
rotateData.handlePoint = get_rotation_point_pos_from_angle(rotateData.rotationAngle);
rebuild_cam_space();
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<float>());
selectionDrawData.refresh_draw_optimizing_values();
SkImageInfo imgInfo = canvas->imageInfo();
sk_sp<SkSurface> surface;
#ifdef USE_SKIA_BACKEND_GRAPHITE
surface = SkSurfaces::RenderTarget(drawP.world.main.window.recorder(), imgInfo, skgpu::Mipmapped::kNo, drawP.world.main.window.defaultMSAASurfaceProps);
#elif USE_SKIA_BACKEND_GANESH
surface = SkSurfaces::RenderTarget(drawP.world.main.window.ctx.get(), skgpu::Budgeted::kNo, imgInfo, drawP.world.main.window.defaultMSAASampleCount, &drawP.world.main.window.defaultMSAASurfaceProps);
#endif
if(!surface)
throw std::runtime_error("[DrawingProgramSelection::draw_components] Could not make temporary surface");
draw_components_recursive(drawP.layerMan.get_layer_root(), surface->getCanvas(), selectionDrawData);
SkPaint glowBlurP;
glowBlurP.setImageFilter(SkImageFilters::Blur(5, 5, nullptr));
canvas->drawImage(surface->makeTemporaryImage(), 0, 0, SkSamplingOptions{SkFilterMode::kLinear}, &glowBlurP);
canvas->drawImage(surface->makeTemporaryImage(), 0, 0, SkSamplingOptions{SkFilterMode::kLinear}, nullptr);
}
}
void DrawingProgramSelection::draw_gui(SkCanvas* canvas, const DrawData& drawData) {
if(is_something_selected() && !camSpaceSelection.triangle.empty()) {
SkPathBuilder selectionRectPath;
selectionRectPath.moveTo(convert_vec2<SkPoint>(camSpaceSelection.triangle[0].p[0]));
selectionRectPath.lineTo(convert_vec2<SkPoint>(camSpaceSelection.triangle[0].p[1]));
selectionRectPath.lineTo(convert_vec2<SkPoint>(camSpaceSelection.triangle[0].p[2]));
selectionRectPath.lineTo(convert_vec2<SkPoint>(camSpaceSelection.triangle[1].p[2]));
selectionRectPath.close();
SkPaint p{SkColor4f{0.3f, 0.6f, 0.9f, 0.4f}};
canvas->drawPath(selectionRectPath.detach(), p);
if(drawP.is_actual_selection_tool(drawP.drawTool->get_type())) {
if(transformOpHappening == TransformOperation::NONE || transformOpHappening == TransformOperation::ROTATE || transformOpHappening == TransformOperation::ROTATE_RELOCATE_CENTER)
drawP.draw_drag_circle(canvas, rotateData.centerHandlePoint, {0.9f, 0.5f, 0.1f, 1.0f}, drawData);
if(transformOpHappening == TransformOperation::NONE || transformOpHappening == TransformOperation::ROTATE)
drawP.draw_drag_circle(canvas, rotateData.handlePoint, {0.9f, 0.5f, 0.1f, 1.0f}, drawData, ROTATION_POINT_RADIUS_MULTIPLIER);
if(transformOpHappening == TransformOperation::NONE || transformOpHappening == TransformOperation::SCALE)
drawP.draw_drag_circle(canvas, scaleData.handlePoint, {0.1f, 0.9f, 0.9f, 1.0f}, drawData);
}
}
}
void DrawingProgramSelection::draw_components_recursive(const DrawingProgramLayerListItem& layerItem, SkCanvas* canvas, const DrawData& drawData) {
if(layerItem.get_visible()) {
SkPaint layerPaint;
layerPaint.setAlphaf(layerItem.get_alpha());
//layerPaint.setBlendMode(serialized_blend_mode_to_sk_blend_mode(displayData->blendMode)); // For selection, everything should just be in default blend mode (SrcOver)
canvas->saveLayer(nullptr, &layerPaint);
if(layerItem.is_folder()) {
for(auto& p : layerItem.get_folder().folderList->get_data() | std::views::reverse)
draw_components_recursive(*p->obj, canvas, drawData);
}
else {
for(auto& c : selectedSet) { // Loop on selected set and not layer component list, since i'm assuming selected set will be small
if(c->obj->parentLayer == &layerItem)
c->obj->draw(canvas, drawData);
}
}
canvas->restore();
}
}

View file

@ -0,0 +1,75 @@
#pragma once
#include "../CoordSpaceHelperTransform.hpp"
#include "Layers/DrawingProgramLayerManager.hpp"
#include "DrawingProgramCache.hpp"
class DrawingProgram;
class DrawingProgramSelection {
public:
DrawingProgramSelection(DrawingProgram& initDrawP);
void add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly);
void remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly);
void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
bool is_something_selected();
bool is_selected(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
void draw_components(SkCanvas* canvas, const DrawData& drawData);
void draw_gui(SkCanvas* canvas, const DrawData& drawData);
bool is_being_transformed();
void update();
void deselect_all();
void paste_clipboard(Vector2f pasteScreenPos);
const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& get_selected_set();
void delete_all();
void selection_to_clipboard();
CanvasComponentContainer::ObjInfoSharedPtr get_front_object_colliding_with_in_editing_layer(const SCollision::ColliderCollection<float>& cC);
private:
void draw_components_recursive(const DrawingProgramLayerListItem& layerItem, SkCanvas* canvas, const DrawData& drawData);
bool mouse_collided_with_selection_aabb();
bool mouse_collided_with_scale_point();
bool mouse_collided_with_rotate_center_handle_point();
bool mouse_collided_with_rotate_handle_point();
void commit_transform_selection();
void rebuild_cam_space();
Vector2f get_rotation_point_pos_from_angle(double angle);
void set_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection);
void add_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection);
void calculate_aabb();
void reset_all();
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&)> erase_select_objects_in_bvh_func(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld, DrawingProgramLayerManager::LayerSelector layerSelector);
SCollision::ColliderCollection<float> camSpaceSelection;
std::array<WorldVec, 4> selectionRectPoints;
CoordSpaceHelperTransform selectionTransformCoords;
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> selectedSet;
SCollision::AABB<WorldScalar> initialSelectionAABB;
DrawingProgram& drawP;
enum class TransformOperation {
NONE = 0,
TRANSLATE,
SCALE,
ROTATE_RELOCATE_CENTER,
ROTATE
} transformOpHappening = TransformOperation::NONE;
struct TranslationData {
WorldVec startPos;
} translateData;
struct ScaleData {
WorldVec startPos;
WorldVec centerPos;
WorldVec currentPos;
Vector2f handlePoint;
} scaleData;
struct RotationData {
WorldVec centerPos;
Vector2f centerHandlePoint;
Vector2f handlePoint;
double rotationAngle = 0.0;
} rotateData;
};

View file

@ -18,6 +18,7 @@ void DrawingProgramLayer::set_component_list_callbacks(DrawingProgramLayerListIt
layerMan.drawP.updateableComponents.emplace(c); layerMan.drawP.updateableComponents.emplace(c);
}); });
components->set_erase_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) { components->set_erase_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) {
layerMan.drawP.selection.erase_component(c);
layerMan.drawP.drawCache.erase_component(c); layerMan.drawP.drawCache.erase_component(c);
layerMan.drawP.drawTool->erase_component(c); layerMan.drawP.drawTool->erase_component(c);
std::erase_if(layerMan.drawP.droppedDownloadingFiles, [&c](auto& downloadingFile) { std::erase_if(layerMan.drawP.droppedDownloadingFiles, [&c](auto& downloadingFile) {

View file

@ -93,7 +93,7 @@ const std::string& DrawingProgramLayerListItem::get_name() const {
void DrawingProgramLayerListItem::set_alpha(DrawingProgramLayerManager& layerMan, float newAlpha) const { void DrawingProgramLayerListItem::set_alpha(DrawingProgramLayerManager& layerMan, float newAlpha) const {
if(displayData && displayData->alpha != newAlpha) { if(displayData && displayData->alpha != newAlpha) {
displayData->alpha = newAlpha; displayData->alpha = newAlpha;
// MUST CLEAR CACHE layerMan.drawP.drawCache.clear_own_cached_surfaces();
layerMan.drawP.world.delayedUpdateObjectManager.send_update_to_all<DisplayData>(displayData, false); layerMan.drawP.world.delayedUpdateObjectManager.send_update_to_all<DisplayData>(displayData, false);
} }
} }
@ -105,7 +105,7 @@ float DrawingProgramLayerListItem::get_alpha() const {
void DrawingProgramLayerListItem::set_visible(DrawingProgramLayerManager& layerMan, bool newVisible) const { void DrawingProgramLayerListItem::set_visible(DrawingProgramLayerManager& layerMan, bool newVisible) const {
if(displayData && displayData->visible != newVisible) { if(displayData && displayData->visible != newVisible) {
displayData->visible = newVisible; displayData->visible = newVisible;
// MUST CLEAR CACHE layerMan.drawP.drawCache.clear_own_cached_surfaces();
layerMan.drawP.world.delayedUpdateObjectManager.send_update_to_all<DisplayData>(displayData, false); layerMan.drawP.world.delayedUpdateObjectManager.send_update_to_all<DisplayData>(displayData, false);
} }
} }
@ -117,7 +117,7 @@ bool DrawingProgramLayerListItem::get_visible() const {
void DrawingProgramLayerListItem::set_blend_mode(DrawingProgramLayerManager& layerMan, SerializedBlendMode newBlendMode) const { void DrawingProgramLayerListItem::set_blend_mode(DrawingProgramLayerManager& layerMan, SerializedBlendMode newBlendMode) const {
if(displayData && displayData->blendMode != newBlendMode) { if(displayData && displayData->blendMode != newBlendMode) {
displayData->blendMode = newBlendMode; displayData->blendMode = newBlendMode;
// MUST CLEAR CACHE layerMan.drawP.drawCache.clear_own_cached_surfaces();
layerMan.drawP.world.delayedUpdateObjectManager.send_update_to_all<DisplayData>(displayData, false); layerMan.drawP.world.delayedUpdateObjectManager.send_update_to_all<DisplayData>(displayData, false);
} }
} }
@ -138,7 +138,7 @@ void DrawingProgramLayerListItem::register_class(World& w) {
w.delayedUpdateObjectManager.register_class<NameData>(w.netObjMan); w.delayedUpdateObjectManager.register_class<NameData>(w.netObjMan);
w.delayedUpdateObjectManager.register_class<DisplayData>(w.netObjMan, NetworkingObjects::DelayUpdateSerializedClassManager::CustomConstructors<DisplayData>{ w.delayedUpdateObjectManager.register_class<DisplayData>(w.netObjMan, NetworkingObjects::DelayUpdateSerializedClassManager::CustomConstructors<DisplayData>{
.postUpdateFunc = [&](DisplayData& o) { .postUpdateFunc = [&](DisplayData& o) {
// MUST CLEAR CACHE w.drawProg.drawCache.clear_own_cached_surfaces();
} }
}); });
} }

View file

@ -63,8 +63,8 @@ void EditTool::switch_tool(DrawingProgramToolType newTool) {
pointDragging = nullptr; pointDragging = nullptr;
isEditing = false; isEditing = false;
//if(!drawP.is_selection_allowing_tool(newTool)) if(!drawP.is_selection_allowing_tool(newTool))
// drawP.selection.deselect_all(); drawP.selection.deselect_all();
} }
void EditTool::edit_start(const CanvasComponentContainer::ObjInfoSharedPtr& comp) { void EditTool::edit_start(const CanvasComponentContainer::ObjInfoSharedPtr& comp) {
@ -114,26 +114,26 @@ void EditTool::tool_update() {
camCMouseAABB.recalculate_bounds(); camCMouseAABB.recalculate_bounds();
if(drawP.controls.leftClick) { if(drawP.controls.leftClick) {
//bool modifySelection = !drawP.selection.is_being_transformed(); bool modifySelection = !drawP.selection.is_being_transformed();
//if(drawP.world.main.input.mouse.leftClicks >= 2 && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) { if(drawP.world.main.input.mouse.leftClicks >= 2 && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) {
// CanvasComponentContainer::ObjInfoSharedPtr selectedObjectToEdit = drawP.selection.get_front_object_colliding_with(camCMouseAABB); CanvasComponentContainer::ObjInfoSharedPtr selectedObjectToEdit = drawP.selection.get_front_object_colliding_with_in_editing_layer(camCMouseAABB);
// if(selectedObjectToEdit && is_editable(selectedObjectToEdit)) { if(selectedObjectToEdit && is_editable(selectedObjectToEdit)) {
// drawP.selection.deselect_all(); drawP.selection.deselect_all();
// edit_start(selectedObjectToEdit); edit_start(selectedObjectToEdit);
// modifySelection = false; modifySelection = false;
// } }
//} }
//if(modifySelection) { if(modifySelection) {
// if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
// drawP.selection.add_from_cam_coord_collider_to_selection(camCMouseAABB, true); drawP.selection.add_from_cam_coord_collider_to_selection(camCMouseAABB, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED, true);
// else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
// drawP.selection.remove_from_cam_coord_collider_to_selection(camCMouseAABB, true); drawP.selection.remove_from_cam_coord_collider_to_selection(camCMouseAABB, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED, true);
// else { else {
// drawP.selection.deselect_all(); drawP.selection.deselect_all();
// drawP.selection.add_from_cam_coord_collider_to_selection(camCMouseAABB, true); drawP.selection.add_from_cam_coord_collider_to_selection(camCMouseAABB, DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED, true);
// } }
//} }
} }
} }
else { else {

View file

@ -49,17 +49,26 @@ void EraserTool::tool_update() {
SCollision::generate_wide_line(cC, prevMousePos, drawP.world.main.input.mouse.pos, drawP.controls.relativeWidth * 2.0f, true); SCollision::generate_wide_line(cC, prevMousePos, drawP.world.main.input.mouse.pos, drawP.controls.relativeWidth * 2.0f, true);
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC); auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
drawP.drawCache.traverse_bvh_erase_function(cCWorld.bounds, [&](const auto& bvhNode, auto& comps) { drawP.drawCache.traverse_bvh_run_function(cCWorld.bounds, [&](const auto& bvhNode) {
if(bvhNode && 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.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.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.top_right())) &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.bottom_left()))) { SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.bottom_left()))) {
drawP.drawCache.invalidate_cache_at_aabb(bvhNode->bounds); drawP.drawCache.invalidate_cache_at_aabb(bvhNode->bounds);
drawP.drawCache.move_components_from_bvh_node_to_set(erasedComponents, bvhNode); drawP.drawCache.traverse_bvh_run_function_starting_at_node_no_collision_check(bvhNode, [&](const auto& bvhNodeChild) {
return true; drawP.drawCache.node_loop_erase_if_components(bvhNodeChild, [&](auto& c) {
if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector)) {
erasedComponents.emplace(c);
return true;
}
return false;
});
return true;
});
return false;
} }
std::erase_if(comps, [&](auto& c) { drawP.drawCache.node_loop_erase_if_components(bvhNode, [&](auto& c) {
if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) { if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) {
erasedComponents.emplace(c); erasedComponents.emplace(c);
drawP.drawCache.invalidate_cache_at_aabb(c->obj->get_world_bounds()); drawP.drawCache.invalidate_cache_at_aabb(c->obj->get_world_bounds());
@ -67,7 +76,7 @@ void EraserTool::tool_update() {
} }
return false; return false;
}); });
return false; return true;
}); });
} }
else else

View file

@ -39,7 +39,15 @@ DrawingProgramToolType LassoSelectTool::get_type() {
} }
void LassoSelectTool::gui_toolbox() { void LassoSelectTool::gui_toolbox() {
drawP.world.main.toolbar.gui.text_label_centered("Lasso Select"); auto& t = drawP.world.main.toolbar;
t.gui.push_id("lasso select tool");
t.gui.text_label_centered("Lasso Select");
t.gui.text_label("Select from:");
if(t.gui.radio_button_field("layer edited", "Layer being edited", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED))
drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED;
if(t.gui.radio_button_field("all visible", "All visible layers", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS))
drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS;
t.gui.pop_id();
} }
bool LassoSelectTool::right_click_popup_gui(Vector2f popupPos) { bool LassoSelectTool::right_click_popup_gui(Vector2f popupPos) {
@ -50,19 +58,19 @@ void LassoSelectTool::erase_component(const CanvasComponentContainer::ObjInfoSha
} }
void LassoSelectTool::switch_tool(DrawingProgramToolType newTool) { void LassoSelectTool::switch_tool(DrawingProgramToolType newTool) {
//if(!drawP.is_selection_allowing_tool(newTool)) if(!drawP.is_selection_allowing_tool(newTool))
// drawP.selection.deselect_all(); drawP.selection.deselect_all();
} }
void LassoSelectTool::tool_update() { void LassoSelectTool::tool_update() {
switch(controls.selectionMode) { switch(controls.selectionMode) {
case 0: { case 0: {
//if(drawP.controls.leftClick && !drawP.selection.is_being_transformed()) { if(drawP.controls.leftClick && !drawP.selection.is_being_transformed()) {
// controls = LassoSelectControls(); controls = LassoSelectControls();
// controls.coords = drawP.world.drawData.cam.c; controls.coords = drawP.world.drawData.cam.c;
// controls.lassoPoints.emplace_back(controls.coords.get_mouse_pos(drawP.world)); controls.lassoPoints.emplace_back(controls.coords.get_mouse_pos(drawP.world));
// controls.selectionMode = 1; controls.selectionMode = 1;
//} }
break; break;
} }
case 1: { case 1: {
@ -92,17 +100,17 @@ void LassoSelectTool::tool_update() {
cC.recalculate_bounds(); cC.recalculate_bounds();
//if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
// drawP.selection.add_from_cam_coord_collider_to_selection(cC); drawP.selection.add_from_cam_coord_collider_to_selection(cC, drawP.controls.layerSelector, false);
//else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
// drawP.selection.remove_from_cam_coord_collider_to_selection(cC); drawP.selection.remove_from_cam_coord_collider_to_selection(cC, drawP.controls.layerSelector, false);
//else { else {
// drawP.selection.deselect_all(); drawP.selection.deselect_all();
// drawP.selection.add_from_cam_coord_collider_to_selection(cC); drawP.selection.add_from_cam_coord_collider_to_selection(cC, drawP.controls.layerSelector, false);
//} }
} }
else if(!drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) { else if(!drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) {
//drawP.selection.deselect_all(); drawP.selection.deselect_all();
} }
controls.selectionMode = 0; controls.selectionMode = 0;
} }
@ -112,7 +120,7 @@ void LassoSelectTool::tool_update() {
} }
bool LassoSelectTool::prevent_undo_or_redo() { bool LassoSelectTool::prevent_undo_or_redo() {
return true;//drawP.selection.is_something_selected() || controls.selectionMode == 1; return drawP.selection.is_something_selected() || controls.selectionMode == 1;
} }
void LassoSelectTool::draw(SkCanvas* canvas, const DrawData& drawData) { void LassoSelectTool::draw(SkCanvas* canvas, const DrawData& drawData) {

View file

@ -17,7 +17,15 @@ DrawingProgramToolType RectSelectTool::get_type() {
} }
void RectSelectTool::gui_toolbox() { void RectSelectTool::gui_toolbox() {
drawP.world.main.toolbar.gui.text_label_centered("Rectangle Select"); auto& t = drawP.world.main.toolbar;
t.gui.push_id("rectangle select tool");
t.gui.text_label_centered("Rectangle Select");
t.gui.text_label("Select from:");
if(t.gui.radio_button_field("layer edited", "Layer being edited", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED))
drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED;
if(t.gui.radio_button_field("all visible", "All visible layers", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS))
drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS;
t.gui.pop_id();
} }
void RectSelectTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) { void RectSelectTool::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& erasedComp) {
@ -28,19 +36,19 @@ bool RectSelectTool::right_click_popup_gui(Vector2f popupPos) {
} }
void RectSelectTool::switch_tool(DrawingProgramToolType newTool) { void RectSelectTool::switch_tool(DrawingProgramToolType newTool) {
//if(!drawP.is_selection_allowing_tool(newTool)) if(!drawP.is_selection_allowing_tool(newTool))
// drawP.selection.deselect_all(); drawP.selection.deselect_all();
} }
void RectSelectTool::tool_update() { void RectSelectTool::tool_update() {
switch(controls.selectionMode) { switch(controls.selectionMode) {
case 0: { case 0: {
//if(drawP.controls.leftClick && !drawP.selection.is_being_transformed()) { if(drawP.controls.leftClick && !drawP.selection.is_being_transformed()) {
// controls = RectSelectControls(); controls = RectSelectControls();
// controls.coords = drawP.world.drawData.cam.c; controls.coords = drawP.world.drawData.cam.c;
// controls.selectStartAt = controls.coords.get_mouse_pos(drawP.world); controls.selectStartAt = controls.coords.get_mouse_pos(drawP.world);
// controls.selectionMode = 1; controls.selectionMode = 1;
//} }
break; break;
} }
case 1: { case 1: {
@ -55,14 +63,14 @@ void RectSelectTool::tool_update() {
cC.triangle.emplace_back(controls.newT[2], controls.newT[3], controls.newT[0]); cC.triangle.emplace_back(controls.newT[2], controls.newT[3], controls.newT[0]);
cC.recalculate_bounds(); cC.recalculate_bounds();
//if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
// drawP.selection.add_from_cam_coord_collider_to_selection(cC); drawP.selection.add_from_cam_coord_collider_to_selection(cC, drawP.controls.layerSelector, false);
//else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
// drawP.selection.remove_from_cam_coord_collider_to_selection(cC); drawP.selection.remove_from_cam_coord_collider_to_selection(cC, drawP.controls.layerSelector, false);
//else { else {
// drawP.selection.deselect_all(); drawP.selection.deselect_all();
// drawP.selection.add_from_cam_coord_collider_to_selection(cC); drawP.selection.add_from_cam_coord_collider_to_selection(cC, drawP.controls.layerSelector, false);
//} }
controls.selectionMode = 0; controls.selectionMode = 0;
} }
@ -72,8 +80,7 @@ void RectSelectTool::tool_update() {
} }
bool RectSelectTool::prevent_undo_or_redo() { bool RectSelectTool::prevent_undo_or_redo() {
//return drawP.selection.is_something_selected() || controls.selectionMode == 1; return drawP.selection.is_something_selected() || controls.selectionMode == 1;
return true;
} }
void RectSelectTool::draw(SkCanvas* canvas, const DrawData& drawData) { void RectSelectTool::draw(SkCanvas* canvas, const DrawData& drawData) {