Working on moving canvas code to net objects (broken build)

This commit is contained in:
Yousef Khadadeh 2025-12-14 18:57:40 +03:00
parent d2a0bfcda9
commit 45ba26446a
42 changed files with 605 additions and 3186 deletions

View file

@ -1,10 +1,12 @@
#include "BrushTool.hpp"
#include <Helpers/ConvertVec.hpp>
#include "../DrawComponents/DrawBrushStroke.hpp"
#include "../GUIStuff/GUIManager.hpp"
#include "DrawingProgram.hpp"
#include "../MainProgram.hpp"
#include "DrawingProgramToolBase.hpp"
#include "../CanvasComponents/BrushStrokeCanvasComponent.hpp"
#include "Helpers/NetworkingObjects/NetObjTemporaryPtr.decl.hpp"
#include "Helpers/NetworkingObjects/NetObjWeakPtr.hpp"
#include <chrono>
#define VEL_SMOOTH_MIN 0.6
@ -23,8 +25,8 @@ void BrushTool::switch_tool(DrawingProgramToolType newTool) {
commit_stroke();
}
bool BrushTool::extensive_point_checking(const Vector2f& newPoint) {
auto& points = controls.intermediateItem->d->points;
bool BrushTool::extensive_point_checking(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint) {
auto& points = brushStroke.d->points;
if(points.size() >= 1 && (newPoint - points[points.size() - 1].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
return false;
if(points.size() >= 2 && (newPoint - points[points.size() - 2].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
@ -34,8 +36,8 @@ bool BrushTool::extensive_point_checking(const Vector2f& newPoint) {
return true;
}
bool BrushTool::extensive_point_checking_back(const Vector2f& newPoint) {
auto& points = controls.intermediateItem->d->points;
bool BrushTool::extensive_point_checking_back(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint) {
auto& points = brushStroke.d->points;
if(points.size() >= 2 && (newPoint - points[points.size() - 2].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
return false;
if(points.size() >= 3 && (newPoint - points[points.size() - 3].pos).norm() < MINIMUM_DISTANCE_TO_NEXT_POINT)
@ -49,107 +51,117 @@ void BrushTool::tool_update() {
if(drawP.controls.cursorHoveringOverCanvas)
drawP.world.main.input.hideCursor = true;
if(!controls.isDrawing)
controls.penSmoothingData.clear();
if(!isDrawing)
penSmoothingData.clear();
if(drawP.world.main.input.pen.isDown && drawP.controls.leftClickHeld && drawP.world.main.toolbar.tabletOptions.pressureAffectsBrushWidth) {
while(!controls.penSmoothingData.empty() && (std::chrono::steady_clock::now() - controls.penSmoothingData.front().t) > std::chrono::milliseconds(static_cast<int>(drawP.world.main.toolbar.tabletOptions.smoothingSamplingTime * 1000.0f)))
controls.penSmoothingData.pop_front();
controls.penSmoothingData.emplace_back(
while(!penSmoothingData.empty() && (std::chrono::steady_clock::now() - penSmoothingData.front().t) > std::chrono::milliseconds(static_cast<int>(drawP.world.main.toolbar.tabletOptions.smoothingSamplingTime * 1000.0f)))
penSmoothingData.pop_front();
penSmoothingData.emplace_back(
drawP.world.main.input.pen.pressure,
std::chrono::steady_clock::now()
);
float averagePressure = 0.0f;
for(auto& [width, t] : controls.penSmoothingData)
for(auto& [width, t] : penSmoothingData)
averagePressure += width;
averagePressure /= controls.penSmoothingData.size();
controls.penWidth = averagePressure;
if(controls.penWidth != 0.0f) {
averagePressure /= penSmoothingData.size();
penWidth = averagePressure;
if(penWidth != 0.0f) {
float brushMinSize = drawP.world.main.toolbar.tabletOptions.brushMinimumSize;
controls.penWidth = brushMinSize + controls.penWidth * (1.0f - brushMinSize);
penWidth = brushMinSize + penWidth * (1.0f - brushMinSize);
}
}
else
controls.penWidth = 1.0f;
penWidth = 1.0f;
bool isPenDown = false;
if(drawP.world.main.input.pen.isDown && drawP.controls.leftClickHeld)
isPenDown = controls.penWidth != 0.0;
isPenDown = penWidth != 0.0;
else if(drawP.controls.leftClickHeld)
isPenDown = true;
if(isPenDown) {
if(!controls.isDrawing) {
controls.isDrawing = true;
controls.intermediateItem = std::make_shared<DrawBrushStroke>();
DrawBrushStrokePoint p;
if(!isDrawing) {
isDrawing = true;
CanvasComponentContainer* newBrushStrokeContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponent::CompType::BRUSHSTROKE);
BrushStrokeCanvasComponent& newBrushStroke = static_cast<BrushStrokeCanvasComponent&>(newBrushStrokeContainer->get_comp());
BrushStrokeCanvasComponentPoint p;
p.pos = drawP.world.main.input.mouse.pos;
p.width = drawP.controls.relativeWidth * controls.penWidth;
controls.prevPointUnaltered = p.pos;
controls.intermediateItem->d->points.emplace_back(p);
controls.intermediateItem->d->color = drawP.controls.foregroundColor;
controls.intermediateItem->d->hasRoundCaps = controls.hasRoundCaps;
controls.intermediateItem->coords = drawP.world.drawData.cam.c;
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
uint64_t placement = drawP.components.client_list().size();
auto objAdd = drawP.components.client_insert(placement, controls.intermediateItem);
controls.intermediateItem->commit_update(drawP);
controls.intermediateItem->client_send_place(drawP);
// NOTE: The data isnt finallized at this point, so a BVH isn't generated for an object that you undo while youre drawing (this isnt just for brush strokes)
// The fix for now is to generate a BVH for an object when you redo it
drawP.add_undo_place_component(objAdd);
controls.addedTemporaryPoint = false;
p.width = drawP.controls.relativeWidth * penWidth;
prevPointUnaltered = p.pos;
newBrushStroke.d->points.emplace_back(p);
newBrushStroke.d->color = drawP.controls.foregroundColor;
newBrushStroke.d->hasRoundCaps = hasRoundCaps;
newBrushStrokeContainer->coords = drawP.world.drawData.cam.c;
intermediateContainer = drawP.components->push_back_and_send_create(drawP.components, newBrushStrokeContainer)->obj;
addedTemporaryPoint = false;
}
else {
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer> containerPtr = intermediateContainer.lock();
if(!containerPtr) {
isDrawing = false;
intermediateContainer.reset();
return;
}
BrushStrokeCanvasComponent& brushStroke = static_cast<BrushStrokeCanvasComponent&>(containerPtr->get_comp());
DrawBrushStrokePoint p;
p.pos = controls.intermediateItem->coords.get_mouse_pos(drawP.world);
p.width = drawP.controls.relativeWidth * controls.penWidth;
BrushStrokeCanvasComponentPoint p;
p.pos = containerPtr->coords.get_mouse_pos(drawP.world);
p.width = drawP.controls.relativeWidth * penWidth;
if(!controls.addedTemporaryPoint) {
if(extensive_point_checking(p.pos)) {
controls.intermediateItem->d->points.emplace_back(p);
controls.addedTemporaryPoint = true;
if(!addedTemporaryPoint) {
if(extensive_point_checking(brushStroke, p.pos)) {
brushStroke.d->points.emplace_back(p);
addedTemporaryPoint = true;
}
else
controls.intermediateItem->d->points.back().width = std::max(controls.intermediateItem->d->points.back().width, p.width);
brushStroke.d->points.back().width = std::max(brushStroke.d->points.back().width, p.width);
}
if(controls.addedTemporaryPoint) {
const DrawBrushStrokePoint& prevP = controls.intermediateItem->d->points[controls.intermediateItem->d->points.size() - 2];
if(addedTemporaryPoint) {
const BrushStrokeCanvasComponentPoint& prevP = brushStroke.d->points[brushStroke.d->points.size() - 2];
float distToPrev = (p.pos - prevP.pos).norm();
if(extensive_point_checking_back(p.pos)) {
controls.intermediateItem->d->points.back().pos = p.pos;
controls.intermediateItem->d->points.back().width = std::max(controls.intermediateItem->d->points.back().width, p.width);
controls.intermediateItem->d->points[controls.intermediateItem->d->points.size() - 2].width = std::max(controls.intermediateItem->d->points[controls.intermediateItem->d->points.size() - 2].width, p.width);
if(extensive_point_checking_back(brushStroke, p.pos)) {
brushStroke.d->points.back().pos = p.pos;
brushStroke.d->points.back().width = std::max(brushStroke.d->points.back().width, p.width);
brushStroke.d->points[brushStroke.d->points.size() - 2].width = std::max(brushStroke.d->points[brushStroke.d->points.size() - 2].width, p.width);
}
if((!controls.drawingMinimumRelativeToSize && distToPrev >= 10.0) || (controls.drawingMinimumRelativeToSize && distToPrev >= drawP.controls.relativeWidth * DRAW_MINIMUM_LIMIT)) {
controls.intermediateItem->d->points.back() = p;
controls.addedTemporaryPoint = false;
controls.intermediateItem->client_send_update(drawP, false);
if((!drawingMinimumRelativeToSize && distToPrev >= 10.0) || (drawingMinimumRelativeToSize && distToPrev >= drawP.controls.relativeWidth * BrushStrokeCanvasComponent::DRAW_MINIMUM_LIMIT)) {
brushStroke.d->points.back() = p;
addedTemporaryPoint = false;
containerPtr->send_comp_update(drawP, true);
if(controls.midwayInterpolation) {
if(controls.intermediateItem->d->points.size() != 2) // Don't interpolate the first point
controls.intermediateItem->d->points[controls.intermediateItem->d->points.size() - 2].pos = (controls.prevPointUnaltered + p.pos) * 0.5;
controls.prevPointUnaltered = p.pos;
if(midwayInterpolation) {
if(brushStroke.d->points.size() != 2) // Don't interpolate the first point
brushStroke.d->points[brushStroke.d->points.size() - 2].pos = (prevPointUnaltered + p.pos) * 0.5;
prevPointUnaltered = p.pos;
}
}
controls.intermediateItem->commit_update(drawP);
containerPtr->commit_update(drawP);
}
}
}
else if(controls.isDrawing) {
controls.isDrawing = false;
else if(isDrawing) {
isDrawing = false;
commit_stroke();
}
}
void BrushTool::commit_stroke() {
NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer> lockedPtr = intermediateContainer.lock();
if(lockedPtr) {
lockedPtr->commit_update(drawP);
lockedPtr->send_comp_update(drawP, true);
}
intermediateContainer.reset();
}
void BrushTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("brush tool");
t.gui.text_label_centered("Brush");
t.gui.checkbox_field("hasroundcaps", "Round Caps", &controls.hasRoundCaps);
t.gui.checkbox_field("hasroundcaps", "Round Caps", &hasRoundCaps);
t.gui.slider_scalar_field("relwidth", "Size", &drawP.controls.relativeWidth, 3.0f, 40.0f);
t.gui.pop_id();
}
@ -161,17 +173,7 @@ bool BrushTool::right_click_popup_gui(Vector2f popupPos) {
}
bool BrushTool::prevent_undo_or_redo() {
return controls.intermediateItem != nullptr;
}
void BrushTool::commit_stroke() {
if(controls.intermediateItem && controls.intermediateItem->collabListInfo.lock()) {
if(!controls.intermediateItem->d->points.empty()) {
controls.intermediateItem->client_send_update(drawP, true);
controls.intermediateItem->commit_update(drawP);
}
}
controls.intermediateItem = nullptr;
return isDrawing;
}
void BrushTool::draw(SkCanvas* canvas, const DrawData& drawData) {
@ -181,8 +183,8 @@ void BrushTool::draw(SkCanvas* canvas, const DrawData& drawData) {
linePaint.setStroke(true);
linePaint.setStrokeWidth(0.0f);
float width;
if(controls.isDrawing)
width = drawP.controls.relativeWidth * controls.penWidth * 0.5f;
if(isDrawing)
width = drawP.controls.relativeWidth * penWidth * 0.5f;
else
width = drawP.controls.relativeWidth * 0.5f;
Vector2f pos = drawData.main->input.mouse.pos;

View file

@ -1,17 +1,17 @@
#pragma once
#include "../SharedTypes.hpp"
#include "DrawingProgramToolBase.hpp"
#include <Helpers/Hashes.hpp>
#include <include/core/SkPath.h>
#include <include/core/SkVertices.h>
#include <Helpers/SCollision.hpp>
#include <deque>
#include <any>
#include <include/core/SkCanvas.h>
#include "../CanvasComponents/CanvasComponentContainer.hpp"
#include <Helpers/NetworkingObjects/NetObjWeakPtr.hpp>
class DrawingProgram;
class DrawBrushStroke;
struct DrawData;
class BrushStrokeCanvasComponent;
class BrushTool : public DrawingProgramToolBase {
public:
@ -24,24 +24,22 @@ class BrushTool : public DrawingProgramToolBase {
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual bool prevent_undo_or_redo() override;
private:
bool extensive_point_checking_back(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint);
bool extensive_point_checking(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint);
void commit_stroke();
bool extensive_point_checking_back(const Vector2f& newPoint);
bool extensive_point_checking(const Vector2f& newPoint);
struct BrushToolControls {
float penWidth = 1.0f;
bool addedTemporaryPoint = false;
struct SmoothingPoint {
float val;
std::chrono::steady_clock::time_point t;
};
std::deque<SmoothingPoint> penSmoothingData;
float penWidth = 1.0f;
bool addedTemporaryPoint = false;
struct SmoothingPoint {
float val;
std::chrono::steady_clock::time_point t;
};
std::deque<SmoothingPoint> penSmoothingData;
std::shared_ptr<DrawBrushStroke> intermediateItem;
bool isDrawing = false;
bool hasRoundCaps = true;
bool drawingMinimumRelativeToSize = true;
bool midwayInterpolation = true;
Vector2f prevPointUnaltered = {0, 0};
} controls;
NetworkingObjects::NetObjWeakPtr<CanvasComponentContainer> intermediateContainer;
bool isDrawing = false;
bool hasRoundCaps = true;
bool drawingMinimumRelativeToSize = true;
bool midwayInterpolation = true;
Vector2f prevPointUnaltered = {0, 0};
};

View file

@ -3,7 +3,6 @@
#include <include/core/SkPaint.h>
#include <include/core/SkVertices.h>
#include "../DrawCamera.hpp"
#include "../DrawComponents/DrawComponent.hpp"
#include "EllipseDrawTool.hpp"
#include "EyeDropperTool.hpp"
#include "GridModifyTool.hpp"
@ -14,13 +13,13 @@
#include <optional>
#include <Helpers/ConvertVec.hpp>
#include <cereal/types/vector.hpp>
#include "../DrawComponents/DrawImage.hpp"
#include <include/core/SkImage.h>
#include <include/core/SkSurface.h>
#include "../World.hpp"
#include "../MainProgram.hpp"
#include "Helpers/FileDownloader.hpp"
#include "Helpers/StringHelpers.hpp"
#include <Helpers/FileDownloader.hpp>
#include <Helpers/NetworkingObjects/NetObjID.hpp>
#include <Helpers/StringHelpers.hpp>
#include "LassoSelectTool.hpp"
#include <Helpers/Logger.hpp>
#include <Helpers/Parallel.hpp>
@ -34,153 +33,66 @@
DrawingProgram::DrawingProgram(World& initWorld):
world(initWorld),
compCache(*this),
components([&](){ return world.get_new_id(); }),
selection(*this)
{
drawTool = DrawingProgramToolBase::allocate_tool_type(*this, DrawingProgramToolType::BRUSH);
}
components.clientInsertCallback = [&](const CollabListType::ObjectInfoPtr& c) {
if(addToCompCacheOnInsert)
compCache.add_component(c);
DrawComponentType t = c->obj->get_type();
if(t == DRAWCOMPONENT_IMAGE)
updateableComponents.emplace(c->obj);
};
components.clientEraseCallback = [&](const CollabListType::ObjectInfoPtr& c) {
void DrawingProgram::init() {
if(world.netObjMan.is_server()) {
components = world.netObjMan.make_obj<NetworkingObjects::NetObjOrderedList<CanvasComponentContainer>>();
set_component_list_callbacks();
}
}
void DrawingProgram::set_component_list_callbacks() {
components->set_insert_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) {
c->obj->set_owner_obj_info(c);
c->obj->commit_update(*this); // Run commit update on insert so that world bounds are calculated
compCache.add_component(c);
if(drawTool->get_type() == DrawingProgramToolType::ERASER) {
EraserTool* eraserTool = static_cast<EraserTool*>(drawTool.get());
eraserTool->erasedComponents.erase(c);
}
});
components->set_erase_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) {
compCache.erase_component(c);
selection.erase_component(c);
delayedUpdateComponents.erase(c->obj);
updateableComponents.erase(c->obj);
};
components.clientInsertOrderedVectorCallback = [&](const std::vector<CollabListType::ObjectInfoPtr>& comps) {
for(auto& c : comps)
components.clientInsertCallback(c);
};
components.clientServerFirstPosShiftCallback = [&](uint64_t firstShiftPos) {
});
components->set_post_sync_callback([&]() {
clear_draw_cache();
};
});
}
void DrawingProgram::init_client_callbacks() {
world.con.client_add_recv_callback(CLIENT_UPDATE_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) {
ServerClientID id;
bool isFinal; // Not actually used, but sent for debugging purposes
message(isFinal, id);
auto objPtr = components.get_item_by_id(id);
if(!objPtr)
return;
std::shared_ptr<DrawComponent>& comp = objPtr->obj;
if((std::chrono::steady_clock::now() - comp->lastUpdateTime) > CLIENT_DRAWCOMP_DELAY_TIMER_DURATION) {
delayedUpdateComponents.erase(comp);
message(*comp);
comp->commit_update(*this);
}
else {
auto delayedUpdatePtr = DrawComponent::allocate_comp_type(comp->get_type());
message(*delayedUpdatePtr);
delayedUpdateComponents[comp] = delayedUpdatePtr;
}
});
world.con.client_add_recv_callback(CLIENT_TRANSFORM_MANY_COMPONENTS, [&](cereal::PortableBinaryInputArchive& message) {
std::vector<std::pair<ServerClientID, CoordSpaceHelper>> transforms;
message(transforms);
if(transforms.size() >= DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD) {
std::atomic<size_t> objsActuallyTransformed = 0;
parallel_loop_container(transforms, [&](auto& p) {
auto& [id, coords] = p;
auto objPtr = components.get_item_by_id(id);
if(!objPtr || selection.is_selected(objPtr)) // Whatever transformation we're doing right now will overwrite this transformation, so we can ignore this message
return;
std::shared_ptr<DrawComponent>& comp = objPtr->obj;
if(comp->coords == coords)
return;
comp->coords = coords;
objsActuallyTransformed++;
comp->commit_transform(*this, false);
});
if(objsActuallyTransformed != 0)
force_rebuild_cache();
}
else {
for(auto& [id, coords] : transforms) {
auto objPtr = components.get_item_by_id(id);
if(!objPtr || selection.is_selected(objPtr)) // Whatever transformation we're doing right now will overwrite this transformation, so we can ignore this message
continue;
std::shared_ptr<DrawComponent>& comp = objPtr->obj;
if(comp->coords == coords)
return;
comp->coords = coords;
comp->commit_transform(*this);
}
}
});
world.con.client_add_recv_callback(CLIENT_PLACE_SINGLE_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) {
uint64_t insertPosition;
DrawComponentType type;
message(insertPosition, type);
auto newObj = DrawComponent::allocate_comp_type(type);
message(newObj->id, newObj->coords, *newObj);
bool didntExistPreviously = components.server_insert(insertPosition, newObj);
if(didntExistPreviously)
newObj->commit_update(*this);
});
world.con.client_add_recv_callback(CLIENT_PLACE_MANY_COMPONENTS, [&](cereal::PortableBinaryInputArchive& message) {
std::vector<CollabListType::ObjectInfoPtr> sortedPlacedComponents;
SendOrderedComponentVectorOp a{&sortedPlacedComponents};
message(a);
std::vector<bool> didntExistPreviously = components.server_insert_ordered_vector(sortedPlacedComponents);
bool isSingleThread = sortedPlacedComponents.size() < DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD;
parallel_loop_container(sortedPlacedComponents, [&](const auto& c){
c->obj->commit_update(*this, isSingleThread);
}, isSingleThread);
if(!isSingleThread)
force_rebuild_cache();
});
world.con.client_add_recv_callback(CLIENT_ERASE_SINGLE_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) {
ServerClientID idToErase;
message(idToErase);
components.server_erase(idToErase);
});
world.con.client_add_recv_callback(CLIENT_ERASE_MANY_COMPONENTS, [&](cereal::PortableBinaryInputArchive& message) {
std::unordered_set<ServerClientID> idsToErase;
message(idsToErase);
components.server_erase_set(idsToErase);
});
}
void DrawingProgram::scale_up(const WorldScalar& scaleUpAmount) {
for(auto& c : components.client_list())
c->obj->scale_up(scaleUpAmount);
selection.deselect_all();
force_rebuild_cache();
switch_to_tool(drawTool->get_type() == DrawingProgramToolType::GRIDMODIFY ? DrawingProgramToolType::EDIT : drawTool->get_type(), true);
//for(auto& c : components->get_data())
// c->obj->scale_up(scaleUpAmount);
//selection.deselect_all();
//force_rebuild_cache();
//switch_to_tool(drawTool->get_type() == DrawingProgramToolType::GRIDMODIFY ? DrawingProgramToolType::EDIT : drawTool->get_type(), true);
}
void DrawingProgram::check_delayed_update_timers() {
std::erase_if(delayedUpdateComponents, [&](auto& p) {
auto& [comp, delayedUpdatePtr] = p;
return comp->check_timers(*this, delayedUpdatePtr);
});
}
void DrawingProgram::check_updateable_components() {
for(auto& comp : updateableComponents)
comp->update(*this);
}
void DrawingProgram::parallel_loop_all_components(std::function<void(const std::shared_ptr<CollabList<std::shared_ptr<DrawComponent>, ServerClientID>::ObjectInfo>&)> func) {
parallel_loop_container(components.client_list(), func);
void DrawingProgram::parallel_loop_all_components(std::function<void(const CanvasComponentContainer::ObjInfoSharedPtr&)> func) {
parallel_loop_container(components->get_data(), func);
}
std::unordered_set<ServerClientID> DrawingProgram::get_used_resources() const {
std::unordered_set<ServerClientID> toRet;
for(auto& c : components.client_list())
c->obj->get_used_resources(toRet);
//for(auto& c : components->get_data())
// c->obj->get_used_resources(toRet);
return toRet;
}
void DrawingProgram::erase_component_set(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& compsToErase) {
std::unordered_set<NetworkingObjects::NetObjID> idsToErase;
for(auto& c : compsToErase)
idsToErase.emplace(c->obj.get_net_id());
components->erase_unordered_set(components, idsToErase);
}
void DrawingProgram::clear_draw_cache() {
compCache.clear_own_cached_surfaces();
selection.clear_own_cached_surfaces();
@ -365,13 +277,6 @@ void DrawingProgram::update() {
controls.previousMouseWorldPos = controls.currentMouseWorldPos;
controls.currentMouseWorldPos = world.get_mouse_world_pos();
if(addFileInNextFrame) {
add_file_to_canvas_by_path_execute(addFileInfo.first, addFileInfo.second);
addFileInNextFrame = false;
}
drag_drop_update();
if(world.main.input.key(InputManager::KEY_DRAW_TOOL_BRUSH).pressed)
switch_to_tool(DrawingProgramToolType::BRUSH);
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_ERASER).pressed)
@ -406,18 +311,14 @@ void DrawingProgram::update() {
if(controls.leftClickReleased)
controls.leftClickReleased = false;
check_delayed_update_timers();
check_updateable_components();
update_downloading_dropped_files();
if(drawTool->get_type() == DrawingProgramToolType::ERASER) {
EraserTool* eraserTool = static_cast<EraserTool*>(drawTool.get());
compCache.test_rebuild_dont_include_set_dont_include_nodes(components.client_list(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes);
compCache.test_rebuild_dont_include_set_dont_include_nodes(components->get_data(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes);
}
else if(is_selection_allowing_tool(drawTool->get_type()))
compCache.test_rebuild_dont_include_set(components.client_list(), selection.get_selected_set());
compCache.test_rebuild_dont_include_set(components->get_data(), selection.get_selected_set());
else
compCache.test_rebuild(components.client_list());
compCache.test_rebuild(components->get_data());
}
bool DrawingProgram::is_actual_selection_tool(DrawingProgramToolType typeToCheck) {
@ -463,287 +364,28 @@ SkPaint DrawingProgram::select_tool_line_paint() {
void DrawingProgram::force_rebuild_cache() {
if(drawTool->get_type() == DrawingProgramToolType::ERASER) {
EraserTool* eraserTool = static_cast<EraserTool*>(drawTool.get());
compCache.test_rebuild_dont_include_set_dont_include_nodes(components.client_list(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes, true);
compCache.test_rebuild_dont_include_set_dont_include_nodes(components->get_data(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes, true);
}
else if(is_selection_allowing_tool(drawTool->get_type()))
compCache.test_rebuild_dont_include_set(components.client_list(), selection.get_selected_set(), true);
compCache.test_rebuild_dont_include_set(components->get_data(), selection.get_selected_set(), true);
else
compCache.test_rebuild(components.client_list(), true);
compCache.test_rebuild(components->get_data(), true);
}
void DrawingProgram::drag_drop_update() {
if(controls.cursorHoveringOverCanvas) {
for(auto& droppedItem : world.main.input.droppedItems) {
if(droppedItem.dataPath.has_value() && std::filesystem::is_regular_file(droppedItem.dataPath.value())) {
#ifdef __EMSCRIPTEN_
add_file_to_canvas_by_path(droppedItem.dataPath.value(), world.main.input.mouse.pos, true);
#else
add_file_to_canvas_by_path(droppedItem.dataPath.value(), droppedItem.pos, true);
#endif
}
else if(droppedItem.dataText.has_value() && is_valid_http_url(droppedItem.dataText.value())) {
auto img(std::make_shared<DrawImage>());
img->coords = world.drawData.cam.c;
Vector2f imDim = Vector2f{100.0f, 100.0f};
img->d.p1 = droppedItem.pos - imDim;
img->d.p2 = droppedItem.pos + imDim;
img->d.imageID = {0, 0};
img->commit_update(*this);
uint64_t placement = components.client_list().size();
auto objAdd = components.client_insert(placement, img);
img->client_send_place(*this);
add_undo_place_component(objAdd);
droppedDownloadingFiles.emplace_back(objAdd, world.main.window.size.cast<float>(), FileDownloader::download_data_from_url(droppedItem.dataText.value()));
}
}
}
}
void DrawingProgram::update_downloading_dropped_files() {
std::erase_if(droppedDownloadingFiles, [&](auto& downFile) {
switch(downFile.downData->status) {
case FileDownloader::DownloadData::Status::SUCCESS: {
std::shared_ptr<DrawImage> img = std::static_pointer_cast<DrawImage>(downFile.comp->obj);
Vector2f dropPos = (img->d.p1 + img->d.p2) * 0.5f;
ResourceData newResource;
newResource.data = std::make_shared<std::string>(downFile.downData->str);
newResource.name = downFile.downData->fileName;
ServerClientID imageID = world.rMan.add_resource(newResource);
ResourceDisplay* display = world.rMan.get_display_data(imageID);
if(display->get_type() == ResourceDisplay::Type::FILE) {
Logger::get().log("WORLDFATAL", "Failed to parse image from URL");
client_erase_set({downFile.comp});
}
else {
Vector2f imTrueDim = display->get_dimensions();
float imWidth = imTrueDim.x() / (imTrueDim.x() + imTrueDim.y());
float imHeight = imTrueDim.y() / (imTrueDim.x() + imTrueDim.y());
Vector2f imDim = Vector2f{downFile.windowSizeWhenDropped.x() * imWidth, downFile.windowSizeWhenDropped.x() * imHeight} * display->get_dimension_scale();
img->d.p1 = dropPos - imDim;
img->d.p2 = dropPos + imDim;
img->d.imageID = imageID;
img->commit_update(*this);
img->client_send_update(*this, true);
}
return true;
}
case FileDownloader::DownloadData::Status::FAILURE:
Logger::get().log("WORLDFATAL", "Failed to download data from URL");
client_erase_set({downFile.comp});
return true;
case FileDownloader::DownloadData::Status::IN_PROGRESS:
return false;
}
return false;
});
}
void DrawingProgram::add_file_to_canvas_by_path(const std::filesystem::path& filePath, Vector2f dropPos, bool addInSameThread) {
if(addInSameThread)
add_file_to_canvas_by_path_execute(filePath, dropPos);
else if(!addFileInNextFrame) {
addFileInfo = {filePath, dropPos};
addFileInNextFrame = true;
}
}
void DrawingProgram::add_file_to_canvas_by_path_execute(const std::filesystem::path& filePath, Vector2f dropPos) {
ServerClientID imageID = world.rMan.add_resource_file(filePath);
if(imageID != ServerClientID{0, 0}) {
ResourceDisplay* display = world.rMan.get_display_data(imageID);
Vector2f imTrueDim = display->get_dimensions();
auto img(std::make_shared<DrawImage>());
img->coords = world.drawData.cam.c;
float imWidth = imTrueDim.x() / (imTrueDim.x() + imTrueDim.y());
float imHeight = imTrueDim.y() / (imTrueDim.x() + imTrueDim.y());
Vector2f imDim = Vector2f{world.main.window.size.x() * imWidth, world.main.window.size.x() * imHeight} * display->get_dimension_scale();
img->d.p1 = dropPos - imDim;
img->d.p2 = dropPos + imDim;
img->d.imageID = imageID;
img->commit_update(*this);
uint64_t placement = components.client_list().size();
auto objAdd = components.client_insert(placement, img);
img->client_send_place(*this);
add_undo_place_component(objAdd);
}
}
void DrawingProgram::add_file_to_canvas_by_data(const std::string& fileName, std::string_view fileBuffer, Vector2f dropPos) {
ResourceData newResource;
newResource.data = std::make_shared<std::string>(fileBuffer);
newResource.name = fileName;
ServerClientID imageID = world.rMan.add_resource(newResource);
ResourceDisplay* display = world.rMan.get_display_data(imageID);
Vector2f imTrueDim = display->get_dimensions();
auto img(std::make_shared<DrawImage>());
img->coords = world.drawData.cam.c;
float imWidth = imTrueDim.x() / (imTrueDim.x() + imTrueDim.y());
float imHeight = imTrueDim.y() / (imTrueDim.x() + imTrueDim.y());
Vector2f imDim = Vector2f{world.main.window.size.x() * imWidth, world.main.window.size.x() * imHeight} * display->get_dimension_scale();
img->d.p1 = dropPos - imDim;
img->d.p2 = dropPos + imDim;
img->d.imageID = imageID;
img->commit_update(*this);
uint64_t placement = components.client_list().size();
auto objAdd = components.client_insert(placement, img);
img->client_send_place(*this);
add_undo_place_component(objAdd);
}
void DrawingProgram::initialize_draw_data(cereal::PortableBinaryInputArchive& a) {
uint64_t compCount;
a(compCount);
for(uint64_t i = 0; i < compCount; i++) {
DrawComponentType t;
a(t);
auto newComp = DrawComponent::allocate_comp_type(t);
a(newComp->id, newComp->coords, *newComp);
components.init_emplace_back(newComp);
}
parallel_loop_all_components([&](const auto& c){
c->obj->commit_update(*this, false);
});
compCache.test_rebuild(components.client_list(), true);
}
void DrawingProgram::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
uint64_t compCount;
a(compCount);
for(uint64_t i = 0; i < compCount; i++) {
DrawComponentType t;
a(t);
auto newComp = DrawComponent::allocate_comp_type(t);
a(newComp->id, newComp->coords);
newComp->load_file(a, version);
components.init_emplace_back(newComp);
}
parallel_loop_all_components([&](const auto& c){
c->obj->commit_update(*this, false);
});
compCache.test_rebuild(components.client_list(), true);
}
void DrawingProgram::client_erase_set(std::unordered_set<CollabListType::ObjectInfoPtr> erasedComponents) {
std::unordered_set<ServerClientID> idsToErase;
for(auto& c : erasedComponents)
idsToErase.emplace(c->obj->id);
DrawComponent::client_send_erase_set(*this, idsToErase);
components.client_erase_set(erasedComponents);
add_undo_erase_components(erasedComponents);
}
void DrawingProgram::add_undo_place_component(const CollabListType::ObjectInfoPtr& objToUndo) {
world.undo.push(UndoManager::UndoRedoPair{
[&, objToUndo]() {
objToUndo->obj->client_send_erase(*this);
components.client_erase(objToUndo);
return true;
},
[&, objToUndo]() {
if(objToUndo->obj->collabListInfo.lock())
return false;
components.client_insert(objToUndo, false);
objToUndo->obj->client_send_place(*this);
return true;
}
});
}
void DrawingProgram::invalidate_cache_at_component(const CollabListType::ObjectInfoPtr& objToCheck) {
void DrawingProgram::invalidate_cache_at_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
if(selection.is_selected(objToCheck))
selection.invalidate_cache_at_optional_aabb_before_pos(objToCheck->obj->worldAABB, objToCheck->pos);
selection.invalidate_cache_at_aabb_before_pos(objToCheck->obj->get_world_bounds(), objToCheck->pos);
else
compCache.invalidate_cache_at_optional_aabb_before_pos(objToCheck->obj->worldAABB, objToCheck->pos);
compCache.invalidate_cache_at_aabb_before_pos(objToCheck->obj->get_world_bounds(), objToCheck->pos);
}
void DrawingProgram::preupdate_component(const CollabListType::ObjectInfoPtr& objToCheck) {
void DrawingProgram::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
if(selection.is_selected(objToCheck))
selection.preupdate_component(objToCheck);
else
compCache.preupdate_component(objToCheck);
}
void DrawingProgram::add_undo_place_components(const std::unordered_set<CollabListType::ObjectInfoPtr>& objSetToUndo) {
world.undo.push(UndoManager::UndoRedoPair{
[&, objSetToUndo = objSetToUndo]() {
for(auto& comp : objSetToUndo)
if(!comp->obj->collabListInfo.lock())
return false;
std::unordered_set<ServerClientID> idsToErase;
for(auto& c : objSetToUndo)
idsToErase.emplace(c->obj->id);
DrawComponent::client_send_erase_set(*this, idsToErase);
components.client_erase_set(objSetToUndo);
return true;
},
[&, objSetToUndo = objSetToUndo]() {
std::vector<CollabListType::ObjectInfoPtr> sortedObjects(objSetToUndo.begin(), objSetToUndo.end());
std::sort(sortedObjects.begin(), sortedObjects.end(), [](auto& a, auto& b) {
return a->pos < b->pos;
});
for(auto& comp : sortedObjects)
if(comp->obj->collabListInfo.lock())
return false;
components.client_insert_ordered_vector(sortedObjects, false);
DrawComponent::client_send_place_many(*this, sortedObjects);
return true;
}
});
}
void DrawingProgram::add_undo_erase_components(const std::unordered_set<CollabListType::ObjectInfoPtr>& objSetToUndo) {
world.undo.push(UndoManager::UndoRedoPair{
[&, objSetToUndo = objSetToUndo]() {
std::vector<CollabListType::ObjectInfoPtr> sortedObjects(objSetToUndo.begin(), objSetToUndo.end());
std::sort(sortedObjects.begin(), sortedObjects.end(), [](auto& a, auto& b) {
return a->pos < b->pos;
});
for(auto& comp : sortedObjects)
if(comp->obj->collabListInfo.lock())
return false;
components.client_insert_ordered_vector(sortedObjects, false);
DrawComponent::client_send_place_many(*this, sortedObjects);
return true;
},
[&, objSetToUndo = objSetToUndo]() {
for(auto& comp : objSetToUndo)
if(!comp->obj->collabListInfo.lock())
return false;
std::unordered_set<ServerClientID> idsToErase;
for(auto& c : objSetToUndo)
idsToErase.emplace(c->obj->id);
DrawComponent::client_send_erase_set(*this, idsToErase);
components.client_erase_set(objSetToUndo);
return true;
}
});
}
ClientPortionID DrawingProgram::get_max_id(ServerPortionID serverID) {
ClientPortionID maxClientID = 0;
auto& cList = components.client_list();
for(auto& comp : cList) {
if(comp->obj->id.first == serverID)
maxClientID = std::max(maxClientID, comp->obj->id.second);
}
return maxClientID;
}
float DrawingProgram::drag_point_radius() {
return 8.0f * world.main.toolbar.final_gui_scale();
}
@ -758,24 +400,36 @@ void DrawingProgram::draw_drag_circle(SkCanvas* canvas, const Vector2f& sPos, co
canvas->drawCircle(sPos.x(), sPos.y(), constantRadius, paintOutline);
}
void DrawingProgram::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
//uint64_t compCount;
//a(compCount);
//for(uint64_t i = 0; i < compCount; i++) {
// DrawComponentType t;
// a(t);
// auto newComp = DrawComponent::allocate_comp_type(t);
// a(newComp->id, newComp->coords);
// newComp->load_file(a, version);
// components.init_emplace_back(newComp);
//}
//parallel_loop_all_components([&](const auto& c){
// c->obj->commit_update(*this, false);
//});
//compCache.test_rebuild(components.client_list(), true);
}
void DrawingProgram::save_file(cereal::PortableBinaryOutputArchive& a) const {
auto& cList = components.client_list();
a((uint64_t)cList.size());
for(size_t i = 0; i < cList.size(); i++) {
a(cList[i]->obj->get_type(), cList[i]->obj->id, cList[i]->obj->coords);
cList[i]->obj->save_file(a);
}
//auto& cList = components->get_data();
//a((uint64_t)cList.size());
//for(size_t i = 0; i < cList.size(); i++) {
// a(cList[i]->obj->get_type(), cList[i]->obj->id, cList[i]->obj->coords);
// cList[i]->obj->save_file(a);
//}
}
void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) {
if(drawData.dontUseDrawProgCache) {
parallel_loop_all_components([&](auto& c) {
c->obj->calculate_draw_transform(drawData);
});
for(auto& c : components.client_list()) {
for(auto& c : components->get_data())
c->obj->draw(canvas, drawData);
c->obj->drawSetupData.shouldDraw = false;
}
}
else {
canvas->saveLayer(nullptr, nullptr);
@ -786,22 +440,6 @@ void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) {
selection.draw_components(canvas, drawData);
canvas->restore();
canvas->restore();
if(!drawData.main->takingScreenshot) {
for(auto& droppedDownFile : droppedDownloadingFiles) {
if(droppedDownFile.comp->obj->collabListInfo.lock())
std::static_pointer_cast<DrawImage>(droppedDownFile.comp->obj)->draw_download_progress_bar(canvas, drawData, droppedDownFile.downData->progress);
}
for(auto& c : updateableComponents) {
if(c->get_type() == DRAWCOMPONENT_IMAGE) {
auto img = std::static_pointer_cast<DrawImage>(c);
float progress = drawData.main->world->rMan.get_resource_retrieval_progress(img->d.imageID);
img->draw_download_progress_bar(canvas, drawData, progress);
}
}
}
selection.draw_gui(canvas, drawData);
}

View file

@ -11,19 +11,20 @@
#include <include/core/SkVertices.h>
#include <Helpers/SCollision.hpp>
#include <Helpers/Hashes.hpp>
#include <list>
#include <Helpers/Random.hpp>
#include "DrawingProgramSelection.hpp"
#include "../CollabList.hpp"
#include "DrawingProgramToolBase.hpp"
#include <Helpers/FileDownloader.hpp>
#include <Helpers/NetworkingObjects/NetObjOrderedList.hpp>
class World;
class DrawingProgram {
public:
DrawingProgram(World& initWorld);
void init();
void init_client_callbacks();
void set_component_list_callbacks();
void toolbar_gui();
void tool_options_gui();
bool right_click_popup_gui(Vector2f popupPos);
@ -31,39 +32,26 @@ class DrawingProgram {
void scale_up(const WorldScalar& scaleUpAmount);
void draw(SkCanvas* canvas, const DrawData& drawData);
void initialize_draw_data(cereal::PortableBinaryInputArchive& a);
void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version);
std::unordered_set<ServerClientID> get_used_resources() const;
ClientPortionID get_max_id(ServerPortionID serverID);
void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version);
void save_file(cereal::PortableBinaryOutputArchive& a) const;
World& world;
bool prevent_undo_or_redo();
void add_file_to_canvas_by_path(const std::filesystem::path& filePath, Vector2f dropPos, bool addInSameThread);
void add_file_to_canvas_by_data(const std::string& fileName, std::string_view fileBuffer, Vector2f dropPos);
void parallel_loop_all_components(std::function<void(const std::shared_ptr<CollabList<std::shared_ptr<DrawComponent>, ServerClientID>::ObjectInfo>&)> func);
void parallel_loop_all_components(std::function<void(const CanvasComponentContainer::ObjInfoSharedPtr&)> func);
// This function, unlike preupdate_component, does not take the object out of the BVH. Use for a draw update that doesn't change the object's bounding box
void invalidate_cache_at_component(const CollabListType::ObjectInfoPtr& objToCheck);
void preupdate_component(const CollabListType::ObjectInfoPtr& objToCheck);
void invalidate_cache_at_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
DrawingProgramCache compCache;
CollabListType components;
std::unordered_set<std::shared_ptr<DrawComponent>> updateableComponents;
std::unordered_map<std::shared_ptr<DrawComponent>, std::shared_ptr<DrawComponent>> delayedUpdateComponents;
CanvasComponentContainer::NetListOwnerPtr components;
Vector4f* get_foreground_color_ptr();
void switch_to_tool(DrawingProgramToolType newToolType, bool force = false);
void switch_to_tool_ptr(std::unique_ptr<DrawingProgramToolBase> newTool);
void clear_draw_cache();
void modify_grid(const NetworkingObjects::NetObjWeakPtr<WorldGrid>& gridToModify);
private:
@ -71,31 +59,17 @@ class DrawingProgram {
std::optional<std::chrono::steady_clock::time_point> unorderedObjectsExistTimePoint;
bool selection_action_menu(Vector2f popupPos);
void client_erase_set(std::unordered_set<CollabListType::ObjectInfoPtr> erasedComponents); // The set might be modified while this is being called, so dont pass by reference
void check_delayed_update_timers();
void check_updateable_components();
void force_rebuild_cache();
bool addToCompCacheOnInsert = true;
std::atomic<bool> addFileInNextFrame = false;
std::pair<std::filesystem::path, Vector2f> addFileInfo;
void add_file_to_canvas_by_path_execute(const std::filesystem::path& filePath, Vector2f dropPos);
void drag_drop_update();
float drag_point_radius();
void draw_drag_circle(SkCanvas* canvas, const Vector2f& pos, const SkColor4f& c, const DrawData& drawData, float radiusMultiplier = 1.0f);
void add_undo_place_component(const CollabListType::ObjectInfoPtr& objToUndo);
void add_undo_place_components(const std::unordered_set<CollabListType::ObjectInfoPtr>& objSetToUndo);
void add_undo_erase_components(const std::unordered_set<CollabListType::ObjectInfoPtr>& objSetToUndo);
SkPaint select_tool_line_paint();
bool is_actual_selection_tool(DrawingProgramToolType typeToCheck);
bool is_selection_allowing_tool(DrawingProgramToolType typeToCheck);
void erase_component_set(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& compsToErase);
DrawingProgramSelection selection;
@ -105,9 +79,6 @@ class DrawingProgram {
bool temporaryPan = false;
DrawingProgramToolType toolTypeAfterTempPan;
bool is_actual_selection_tool(DrawingProgramToolType typeToCheck);
bool is_selection_allowing_tool(DrawingProgramToolType typeToCheck);
struct GlobalControls {
float relativeWidth = 15.0f;
@ -130,15 +101,6 @@ class DrawingProgram {
uint32_t nextID = 0;
struct DroppedDownloadingFile {
CollabListType::ObjectInfoPtr comp;
Vector2f windowSizeWhenDropped;
std::shared_ptr<FileDownloader::DownloadData> downData;
};
std::vector<DroppedDownloadingFile> droppedDownloadingFiles;
void update_downloading_dropped_files();
friend class EyeDropperTool;
friend class RectDrawTool;
friend class EllipseDrawTool;

View file

@ -23,18 +23,10 @@ DrawingProgramCache::DrawingProgramCache(DrawingProgram& initDrawP):
lastBvhBuildTime = std::chrono::steady_clock::now();
}
void DrawingProgramCache::build(std::vector<CollabListType::ObjectInfoPtr> components) {
void DrawingProgramCache::build(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> components) {
bvhRoot = std::make_shared<DrawingProgramCacheBVHNode>();
unsortedComponents.clear();
clear_own_cached_surfaces();
std::erase_if(components, [&](auto& c) {
if(!c->obj->worldAABB) {
unsortedComponents.emplace_back(c);
c->obj->parentBvhNode.reset();
return true;
}
return false;
});
build_bvh_node(bvhRoot, components);
lastBvhBuildTime = std::chrono::steady_clock::now();
}
@ -59,17 +51,17 @@ bool DrawingProgramCache::check_if_rebuild_should_occur() {
return (unsortedComponents.size() >= MINIMUM_COMPONENTS_TO_START_REBUILD);
}
void DrawingProgramCache::test_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& comps, bool force) {
void DrawingProgramCache::test_rebuild(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps, bool force) {
if(force || check_if_rebuild_should_occur())
force_rebuild(comps);
}
void DrawingProgramCache::test_rebuild_dont_include_set(const std::vector<CollabListType::ObjectInfoPtr>& comps, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude, bool force) {
void DrawingProgramCache::test_rebuild_dont_include_set(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude, bool force) {
if(force || check_if_rebuild_should_occur())
force_rebuild_dont_include_objs(comps, objsToNotInclude);
}
void DrawingProgramCache::test_rebuild_dont_include_set_dont_include_nodes(const std::vector<CollabListType::ObjectInfoPtr>& comps, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& nodesToNotInclude, bool force) {
void DrawingProgramCache::test_rebuild_dont_include_set_dont_include_nodes(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& nodesToNotInclude, bool force) {
if(force || check_if_rebuild_should_occur()) {
auto objsToNotIncludeNew = objsToNotInclude;
move_components_from_bvh_nodes_to_set(objsToNotIncludeNew, nodesToNotInclude);
@ -77,30 +69,30 @@ void DrawingProgramCache::test_rebuild_dont_include_set_dont_include_nodes(const
}
}
void DrawingProgramCache::move_components_from_bvh_nodes_to_set(std::unordered_set<CollabListType::ObjectInfoPtr>& s, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& bvhNodes) {
void DrawingProgramCache::move_components_from_bvh_nodes_to_set(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& s, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& bvhNodes) {
for(auto& b : bvhNodes)
move_components_from_bvh_node_to_set(s, b);
}
void DrawingProgramCache::move_components_from_bvh_node_to_set(std::unordered_set<CollabListType::ObjectInfoPtr>& s, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
void DrawingProgramCache::move_components_from_bvh_node_to_set(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& s, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
for(auto& c : bvhNode->components)
s.emplace(c);
for(auto& p : bvhNode->children)
move_components_from_bvh_node_to_set(s, p);
}
void DrawingProgramCache::force_rebuild_dont_include_objs(std::vector<CollabListType::ObjectInfoPtr> componentsToBuild, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude) {
void DrawingProgramCache::force_rebuild_dont_include_objs(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> componentsToBuild, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude) {
std::erase_if(componentsToBuild, [&objsToNotInclude](auto& c) {
return objsToNotInclude.contains(c);
});
build(componentsToBuild);
}
void DrawingProgramCache::force_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& componentsToBuild) {
void DrawingProgramCache::force_rebuild(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& componentsToBuild) {
build(componentsToBuild);
}
const std::vector<CollabListType::ObjectInfoPtr>& DrawingProgramCache::get_unsorted_component_list() const {
const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& DrawingProgramCache::get_unsorted_component_list() const {
return unsortedComponents;
}
@ -108,12 +100,12 @@ const std::chrono::steady_clock::time_point& DrawingProgramCache::get_last_bvh_b
return lastBvhBuildTime;
}
void DrawingProgramCache::add_component(const CollabListType::ObjectInfoPtr& c) {
void DrawingProgramCache::add_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
unsortedComponents.emplace_back(c);
invalidate_cache_at_optional_aabb_before_pos(c->obj->worldAABB, c->pos);
invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
}
void DrawingProgramCache::erase_component(const CollabListType::ObjectInfoPtr& c) {
void DrawingProgramCache::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
auto parentBvhNode = c->obj->parentBvhNode.lock();
if(parentBvhNode) {
std::erase(parentBvhNode->components, c);
@ -121,33 +113,16 @@ void DrawingProgramCache::erase_component(const CollabListType::ObjectInfoPtr& c
}
else
std::erase(unsortedComponents, c);
invalidate_cache_at_optional_aabb_before_pos(c->obj->worldAABB, c->pos);
invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
}
std::pair<std::shared_ptr<DrawingProgramCacheBVHNode>, std::vector<CollabListType::ObjectInfoPtr>::iterator> DrawingProgramCache::get_bvh_node_fully_containing_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, DrawComponent* c) {
SCollision::AABB<WorldScalar>& cAABB = c->worldAABB.value();
if(!bvhNode->bounds.fully_contains_aabb(cAABB))
return {nullptr, bvhNode->components.end()};
auto it = std::find_if(bvhNode->components.begin(), bvhNode->components.end(), [&](const auto& comp) {
return comp->obj.get() == c;
});
if(it != bvhNode->components.end())
return {bvhNode, it};
for(auto& p : bvhNode->children) {
auto toRet = get_bvh_node_fully_containing_recursive(p, c);
if(toRet.first)
return toRet;
}
return {nullptr, bvhNode->components.end()};
}
void DrawingProgramCache::traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CollabListType::ObjectInfoPtr>& components)> f) {
void DrawingProgramCache::traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& 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<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CollabListType::ObjectInfoPtr>& components)> f) {
bool DrawingProgramCache::traverse_bvh_erase_function_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) {
if(bvhNode && SCollision::collide(aabb, bvhNode->bounds)) {
if(f(bvhNode, bvhNode->components))
return true;
@ -158,22 +133,22 @@ bool DrawingProgramCache::traverse_bvh_erase_function_recursive(const std::share
return false;
}
void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CollabListType::ObjectInfoPtr>& components)> f) {
void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f) {
f(nullptr, unsortedComponents);
traverse_bvh_run_function_recursive(bvhRoot, aabb, f);
}
void DrawingProgramCache::traverse_bvh_run_function_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CollabListType::ObjectInfoPtr>& components)> f) {
void DrawingProgramCache::traverse_bvh_run_function_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& 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);
}
}
CollabListType::ObjectInfoPtr DrawingProgramCache::get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC) {
CanvasComponentContainer::ObjInfoSharedPtr DrawingProgramCache::get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC) {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
CollabListType::ObjectInfoPtr p;
traverse_bvh_run_function(cCWorld.bounds, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CollabListType::ObjectInfoPtr>& components) {
CanvasComponentContainer::ObjInfoSharedPtr p;
traverse_bvh_run_function(cCWorld.bounds, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components) {
for(auto& c : components) {
if((!p || c->pos >= p->pos) && c->obj->collides_with_world_coords(drawP.world.drawData.cam.c, cCWorld))
p = c;
@ -183,23 +158,23 @@ CollabListType::ObjectInfoPtr DrawingProgramCache::get_front_object_colliding_wi
return p;
}
void DrawingProgramCache::preupdate_component(const CollabListType::ObjectInfoPtr& c) {
void DrawingProgramCache::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c) {
auto parentBvhNode = c->obj->parentBvhNode.lock();
if(parentBvhNode) {
unsortedComponents.emplace_back(c);
std::erase(parentBvhNode->components, c);
c->obj->parentBvhNode.reset();
}
invalidate_cache_at_optional_aabb_before_pos(c->obj->worldAABB, c->pos);
invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
}
void DrawingProgramCache::build_bvh_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CollabListType::ObjectInfoPtr>& components) {
void DrawingProgramCache::build_bvh_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components) {
if(components.empty())
return;
bvhNode->bounds = components.front()->obj->worldAABB.value();
bvhNode->bounds = components.front()->obj->get_world_bounds();
for(auto& c : components)
bvhNode->bounds.include_aabb_in_bounds(c->obj->worldAABB.value());
bvhNode->bounds.include_aabb_in_bounds(c->obj->get_world_bounds());
//std::cout << "component count: " << components.size() << std::endl;
//std::cout << "bvhNode->bounds " << vec_pretty(bvhNode->bounds.min) << " " << vec_pretty(bvhNode->bounds.max) << std::endl;
@ -215,10 +190,10 @@ void DrawingProgramCache::build_bvh_node(const std::shared_ptr<DrawingProgramCac
WorldVec boundsCenter = bvhNode->bounds.center();
std::array<std::vector<CollabListType::ObjectInfoPtr>, 4> parts;
std::array<std::vector<CanvasComponentContainer::ObjInfoSharedPtr>, 4> parts;
for(auto& c : components) {
const auto& cAABB = c->obj->worldAABB.value();
const auto& cAABB = c->obj->get_world_bounds();
if(cAABB.min.x() < boundsCenter.x() && cAABB.max.x() < boundsCenter.x() && cAABB.min.y() < boundsCenter.y() && cAABB.max.y() < boundsCenter.y())
parts[0].emplace_back(c);
else if(cAABB.min.x() > boundsCenter.x() && cAABB.max.x() > boundsCenter.x() && cAABB.min.y() < boundsCenter.y() && cAABB.max.y() < boundsCenter.y())
@ -271,7 +246,7 @@ void DrawingProgramCache::build_bvh_node_coords_and_resolution(DrawingProgramCac
node.coords.inverseScale = cacheBoundDim.y().divide_double(node.resolution.y());
}
void DrawingProgramCache::invalidate_cache_before_pos(uint64_t placementToInvalidateAt) {
void DrawingProgramCache::invalidate_cache_before_pos(uint32_t placementToInvalidateAt) {
// We could use worldAABB of component to invalidate specific areas. However, that comes with some issues:
// - The AABB couldve been changed from the last frame, so the current worldAABB doesnt actually represent what we should update
// - The worldAABB might not be calculated yet, or mightve been invalidated (worldAABB = nullopt)
@ -286,14 +261,7 @@ void DrawingProgramCache::invalidate_cache_before_pos(uint64_t placementToInvali
});
}
void DrawingProgramCache::invalidate_cache_at_optional_aabb_before_pos(const std::optional<SCollision::AABB<WorldScalar>>& aabb, uint64_t placementToInvalidateAt) {
if(aabb)
invalidate_cache_at_aabb_before_pos(aabb.value(), placementToInvalidateAt);
else
invalidate_cache_before_pos(placementToInvalidateAt);
}
void DrawingProgramCache::invalidate_cache_at_aabb_before_pos(const SCollision::AABB<WorldScalar>& aabb, uint64_t placementToInvalidateAt) {
void DrawingProgramCache::invalidate_cache_at_aabb_before_pos(const SCollision::AABB<WorldScalar>& aabb, uint32_t placementToInvalidateAt) {
std::for_each(nodesWithCachedSurfaces.begin(), nodesWithCachedSurfaces.end(), [&](auto& n) {
if(n->drawCache.value().attachedCache == this && SCollision::collide(aabb, n->bounds)) {
auto& dCache = n->drawCache.value();
@ -312,7 +280,7 @@ void DrawingProgramCache::invalidate_cache_at_aabb_before_pos(const SCollision::
void DrawingProgramCache::refresh_all_draw_cache(const DrawData& drawData) {
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<DrawingProgramCacheBVHNode> node, const std::vector<CollabListType::ObjectInfoPtr>& comps) {
traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](std::shared_ptr<DrawingProgramCacheBVHNode> node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps) {
if(node && node->coords.inverseScale <= drawData.cam.c.inverseScale) {
if(!node->drawCache || node->drawCache.value().isInvalid)
nodeFlatList.emplace_front(node);
@ -415,16 +383,16 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr<DrawingProgra
}
void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, DrawComponentsToCanvasOptionalData optData) {
uint64_t lastComponentDrawn = 0;
uint32_t lastComponentDrawn = 0;
std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>> cachedNodesToDraw;
struct UncachedObjectToDraw {
CollabListType::ObjectInfoPtr comp;
CanvasComponentContainer::ObjInfoSharedPtr comp;
bool isAnUnsortedObject;
};
std::vector<UncachedObjectToDraw> uncachedCompsToDraw;
traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CollabListType::ObjectInfoPtr>& comps) {
traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps) {
if(node && node->drawCache && !node->drawCache.value().isInvalid && node->coords.inverseScale <= drawData.cam.c.inverseScale) {
cachedNodesToDraw.emplace_back(node);
lastComponentDrawn = std::max(lastComponentDrawn, node->drawCache->lastDrawnComponentPlacement);
@ -432,10 +400,8 @@ void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const Draw
}
for(auto& c : comps) {
if(!optData.drawBounds.has_value() || !c->obj->worldAABB.has_value() || SCollision::collide(optData.drawBounds.value(), c->obj->worldAABB.value())) {
c->obj->calculate_draw_transform(drawData);
if(!optData.drawBounds.has_value() || SCollision::collide(optData.drawBounds.value(), c->obj->get_world_bounds()))
uncachedCompsToDraw.emplace_back(c, !node);
}
lastComponentDrawn = std::max(lastComponentDrawn, c->pos);
}
@ -463,8 +429,6 @@ void DrawingProgramCache::draw_components_to_canvas(SkCanvas* canvas, const Draw
uncachedComp.comp->obj->draw(canvas, drawData);
if(uncachedComp.isAnUnsortedObject)
unsortedCompDrawTime += std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - drawStartTime);
uncachedComp.comp->obj->drawSetupData.shouldDraw = false;
}
for(;nextCacheToRender < cachedNodesToDraw.size(); nextCacheToRender++)
draw_cache_image_to_canvas(canvas, drawData, cachedNodesToDraw[nextCacheToRender]);

View file

@ -4,7 +4,7 @@
#include <include/core/SkSurface.h>
#include <unordered_set>
#include "../DrawData.hpp"
#include "../DrawComponents/DrawComponent.hpp"
#include "../CanvasComponents/CanvasComponentContainer.hpp"
class DrawingProgram;
class DrawingProgramCache;
@ -12,13 +12,13 @@ class DrawingProgramCache;
class DrawingProgramCacheBVHNode {
public:
SCollision::AABB<WorldScalar> bounds;
std::vector<CollabListType::ObjectInfoPtr> components;
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> components;
std::vector<std::shared_ptr<DrawingProgramCacheBVHNode>> children;
CoordSpaceHelper coords;
Vector2i resolution;
struct DrawCacheData {
uint64_t lastDrawnComponentPlacement;
uint32_t lastDrawnComponentPlacement;
sk_sp<SkSurface> surface;
std::chrono::steady_clock::time_point lastRenderTime;
DrawingProgramCache* attachedCache;
@ -38,56 +38,54 @@ class DrawingProgramCache {
static constexpr size_t MAXIMUM_COMPONENTS_IN_SINGLE_NODE = 50;
bool check_if_rebuild_should_occur();
void test_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& comps, bool force = false);
void test_rebuild_dont_include_set(const std::vector<CollabListType::ObjectInfoPtr>& comps, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude, bool force = false);
void test_rebuild_dont_include_set_dont_include_nodes(const std::vector<CollabListType::ObjectInfoPtr>& comps, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& nodesToNotInclude, bool force = false);
void test_rebuild(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps, bool force = false);
void test_rebuild_dont_include_set(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude, bool force = false);
void test_rebuild_dont_include_set_dont_include_nodes(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& comps, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& nodesToNotInclude, bool force = false);
static void move_components_from_bvh_nodes_to_set(std::unordered_set<CollabListType::ObjectInfoPtr>& s, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& bvhNodes);
static void move_components_from_bvh_node_to_set(std::unordered_set<CollabListType::ObjectInfoPtr>& s, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
static void move_components_from_bvh_nodes_to_set(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& s, const std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>>& bvhNodes);
static void move_components_from_bvh_node_to_set(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& s, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
void erase_component(const CollabListType::ObjectInfoPtr& c);
void add_component(const CollabListType::ObjectInfoPtr& c);
void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void add_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void refresh_all_draw_cache(const DrawData& drawData);
void traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CollabListType::ObjectInfoPtr>& components)> f);
void traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CollabListType::ObjectInfoPtr>& components)> f);
void traverse_bvh_run_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f);
void traverse_bvh_erase_function(const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f);
// 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<WorldScalar>& aabb, uint64_t placementToInvalidateAt);
void invalidate_cache_at_optional_aabb_before_pos(const std::optional<SCollision::AABB<WorldScalar>>& aabb, uint64_t placementToInvalidateAt);
void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& c);
void invalidate_cache_before_pos(uint32_t placementToInvalidateAt);
void invalidate_cache_at_aabb_before_pos(const SCollision::AABB<WorldScalar>& aabb, uint32_t placementToInvalidateAt);
void clear();
struct DrawComponentsToCanvasOptionalData {
uint64_t* lastDrawnComponentPlacement = nullptr;
uint32_t* lastDrawnComponentPlacement = nullptr;
std::optional<SCollision::AABB<WorldScalar>> drawBounds;
};
void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, DrawComponentsToCanvasOptionalData optData);
const std::vector<CollabListType::ObjectInfoPtr>& get_unsorted_component_list() const;
const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& get_unsorted_component_list() const;
const std::chrono::steady_clock::time_point& get_last_bvh_build_time() const;
void clear_own_cached_surfaces();
CollabListType::ObjectInfoPtr get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC);
CanvasComponentContainer::ObjInfoSharedPtr get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC);
private:
static std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>> nodesWithCachedSurfaces;
void draw_cache_image_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
void force_rebuild_dont_include_objs(std::vector<CollabListType::ObjectInfoPtr> componentsToBuild, const std::unordered_set<CollabListType::ObjectInfoPtr>& objsToNotInclude);
void force_rebuild(const std::vector<CollabListType::ObjectInfoPtr>& componentsToBuild);
void force_rebuild_dont_include_objs(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> componentsToBuild, const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& objsToNotInclude);
void force_rebuild(const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& componentsToBuild);
bool traverse_bvh_erase_function_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CollabListType::ObjectInfoPtr>& components)> f);
bool traverse_bvh_erase_function_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f);
void build_bvh_node_coords_and_resolution(DrawingProgramCacheBVHNode& node);
void refresh_draw_cache(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const DrawData& drawData);
void build(std::vector<CollabListType::ObjectInfoPtr> components);
void traverse_bvh_run_function_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CollabListType::ObjectInfoPtr>& components)> f);
std::pair<std::shared_ptr<DrawingProgramCacheBVHNode>, std::vector<CollabListType::ObjectInfoPtr>::iterator> get_bvh_node_fully_containing_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, DrawComponent* c);
void build(std::vector<CanvasComponentContainer::ObjInfoSharedPtr> components);
void traverse_bvh_run_function_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const SCollision::AABB<WorldScalar>& aabb, std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>& node, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components)> f);
void build_bvh_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CollabListType::ObjectInfoPtr>& components);
void build_bvh_node(const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode, const std::vector<CanvasComponentContainer::ObjInfoSharedPtr>& components);
std::chrono::steady_clock::time_point lastBvhBuildTime;
std::shared_ptr<DrawingProgramCacheBVHNode> bvhRoot;
std::vector<CollabListType::ObjectInfoPtr> unsortedComponents;
std::vector<CanvasComponentContainer::ObjInfoSharedPtr> unsortedComponents;
DrawingProgram& drawP;
};

View file

@ -27,18 +27,18 @@ DrawingProgramSelection::DrawingProgramSelection(DrawingProgram& initDrawP):
drawP(initDrawP)
{}
const std::unordered_set<CollabListType::ObjectInfoPtr>& DrawingProgramSelection::get_selected_set() {
const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& DrawingProgramSelection::get_selected_set() {
return selectedSet;
}
void DrawingProgramSelection::erase_component(const CollabListType::ObjectInfoPtr& objToCheck) {
void DrawingProgramSelection::erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
if(is_selected(objToCheck)) {
cache.erase_component(objToCheck);
selectedSet.erase(objToCheck);
}
}
void DrawingProgramSelection::fully_collided_erase_select_objects_func(std::unordered_set<CollabListType::ObjectInfoPtr>& selectedComponents, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
void DrawingProgramSelection::fully_collided_erase_select_objects_func(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& selectedComponents, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
if(bvhNode) {
for(auto& c : bvhNode->components)
selectedComponents.emplace(c);
@ -47,7 +47,7 @@ void DrawingProgramSelection::fully_collided_erase_select_objects_func(std::unor
}
}
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vector<CollabListType::ObjectInfoPtr>&)> DrawingProgramSelection::erase_select_objects_in_bvh_func(std::unordered_set<CollabListType::ObjectInfoPtr>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld) {
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>&)> DrawingProgramSelection::erase_select_objects_in_bvh_func(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld) {
auto toRet = [&](const auto& bvhNode, auto& comps) {
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)) &&
@ -61,7 +61,7 @@ std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vect
std::erase_if(comps, [&](auto& c) {
if(c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) {
selectedComponents.emplace(c);
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->worldAABB.value(), c->obj->collabListInfo.lock()->pos);
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
return true;
}
return false;
@ -73,9 +73,9 @@ std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vect
}
void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, bool frontObjectOnly) {
std::unordered_set<CollabListType::ObjectInfoPtr> selectedComponents;
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> selectedComponents;
if(frontObjectOnly) {
CollabListType::ObjectInfoPtr a = drawP.compCache.get_front_object_colliding_with(cC);
CanvasComponentContainer::ObjInfoSharedPtr a = drawP.compCache.get_front_object_colliding_with(cC);
if(a) {
selectedComponents.emplace(a);
drawP.compCache.erase_component(a);
@ -90,9 +90,9 @@ void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCo
void DrawingProgramSelection::remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, bool frontObjectOnly) {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
std::unordered_set<CollabListType::ObjectInfoPtr> selectedComponents;
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> selectedComponents;
if(frontObjectOnly) {
CollabListType::ObjectInfoPtr a = cache.get_front_object_colliding_with(cC);
CanvasComponentContainer::ObjInfoSharedPtr a = cache.get_front_object_colliding_with(cC);
if(a) {
selectedComponents.emplace(a);
cache.erase_component(a);
@ -117,7 +117,7 @@ void DrawingProgramSelection::remove_from_cam_coord_collider_to_selection(const
}
}
void DrawingProgramSelection::add_to_selection(const std::unordered_set<CollabListType::ObjectInfoPtr>& newSelection) {
void DrawingProgramSelection::add_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection) {
selectedSet.insert(newSelection.begin(), newSelection.end());
for(auto& obj : newSelection)
cache.add_component(obj);
@ -125,7 +125,7 @@ void DrawingProgramSelection::add_to_selection(const std::unordered_set<CollabLi
calculate_initial_rotate_center_location();
}
void DrawingProgramSelection::set_to_selection(const std::unordered_set<CollabListType::ObjectInfoPtr>& newSelection) {
void DrawingProgramSelection::set_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection) {
selectedSet = newSelection;
for(auto& obj : selectedSet)
cache.add_component(obj);
@ -139,9 +139,9 @@ bool DrawingProgramSelection::is_something_selected() {
void DrawingProgramSelection::calculate_aabb() {
if(is_something_selected()) {
initialSelectionAABB = (*selectedSet.begin())->obj->worldAABB.value();
initialSelectionAABB = (*selectedSet.begin())->obj->get_world_bounds();
for(auto& c : selectedSet)
initialSelectionAABB.include_aabb_in_bounds(c->obj->worldAABB.value());
initialSelectionAABB.include_aabb_in_bounds(c->obj->get_world_bounds());
}
}
@ -149,7 +149,7 @@ void DrawingProgramSelection::deselect_all() {
if(is_something_selected()) {
bool cacheWillRebuildAnyway = selectedSet.size() >= DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD;
if(cacheWillRebuildAnyway)
drawP.compCache.test_rebuild(drawP.components.client_list(), true);
drawP.compCache.test_rebuild(drawP.components->get_data(), true);
else {
for(auto& obj : selectedSet)
drawP.compCache.add_component(obj);
@ -162,99 +162,51 @@ void DrawingProgramSelection::deselect_all() {
void DrawingProgramSelection::commit_transform_selection() {
auto selectedSetTemp = selectedSet;
std::vector<CollabListType::ObjectInfoPtr> a(selectedSet.begin(), selectedSet.end());
std::vector<std::pair<CollabListType::ObjectInfoPtr, CoordSpaceHelper>> transformsFrom;
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);
bool isSingleThread = a.size() < DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD;
parallel_loop_container(a, [&](auto& obj) {
obj->obj->coords = selectionTransformCoords.other_coord_space_from_this_space(obj->obj->coords);
obj->obj->commit_transform(drawP, false);
obj->obj->commit_transform_dont_invalidate_cache(drawP);
}, isSingleThread);
std::vector<std::pair<CollabListType::ObjectInfoPtr, CoordSpaceHelper>> transformsTo;
std::vector<std::pair<ServerClientID, CoordSpaceHelper>> transformsToSend;
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->id, transformedObj->obj->coords);
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);
drawP.world.undo.push(UndoManager::UndoRedoPair{
[isSingleThread, &drawP = drawP, transformsFrom = transformsFrom]() {
std::vector<std::pair<ServerClientID, CoordSpaceHelper>> transformsToSend;
for(auto& [comp, coords] : transformsFrom) {
auto lockObjInfo = comp->obj->collabListInfo.lock();
if(lockObjInfo)
transformsToSend.emplace_back(lockObjInfo->obj->id, coords);
else
return false;
}
parallel_loop_container(transformsFrom, [&](auto& p) {
auto& [comp, coords] = p;
comp->obj->coords = coords;
comp->obj->commit_transform(drawP, isSingleThread);
}, isSingleThread);
DrawComponent::client_send_transform_many(drawP, transformsToSend);
if(!isSingleThread)
drawP.force_rebuild_cache();
return true;
},
[isSingleThread, &drawP = drawP, transformsTo = transformsTo]() {
std::vector<std::pair<ServerClientID, CoordSpaceHelper>> transformsToSend;
for(auto& [comp, coords] : transformsTo) {
auto lockObjInfo = comp->obj->collabListInfo.lock();
if(lockObjInfo)
transformsToSend.emplace_back(lockObjInfo->obj->id, coords);
else
return false;
}
parallel_loop_container(transformsTo, [&](auto& p) {
auto& [comp, coords] = p;
comp->obj->coords = coords;
comp->obj->commit_transform(drawP, isSingleThread);
}, isSingleThread);
DrawComponent::client_send_transform_many(drawP, transformsToSend);
if(!isSingleThread)
drawP.force_rebuild_cache();
return true;
}
});
//DrawComponent::client_send_transform_many(drawP, transformsToSend);
reset_selection_data();
reset_transform_data();
set_to_selection(selectedSetTemp);
}
CollabListType::ObjectInfoPtr DrawingProgramSelection::get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC) {
CanvasComponentContainer::ObjInfoSharedPtr DrawingProgramSelection::get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC) {
return cache.get_front_object_colliding_with(cC);
}
void DrawingProgramSelection::invalidate_cache_at_optional_aabb_before_pos(const std::optional<SCollision::AABB<WorldScalar>>& aabb, uint64_t placementToInvalidateAt) {
cache.invalidate_cache_at_optional_aabb_before_pos(aabb, placementToInvalidateAt);
void DrawingProgramSelection::invalidate_cache_at_aabb_before_pos(const SCollision::AABB<WorldScalar>& aabb, uint64_t placementToInvalidateAt) {
cache.invalidate_cache_at_aabb_before_pos(aabb, placementToInvalidateAt);
}
void DrawingProgramSelection::clear_own_cached_surfaces() {
cache.clear_own_cached_surfaces();
}
void DrawingProgramSelection::preupdate_component(const CollabListType::ObjectInfoPtr& objToCheck) {
void DrawingProgramSelection::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
cache.preupdate_component(objToCheck);
}
bool DrawingProgramSelection::is_selected(const CollabListType::ObjectInfoPtr& objToCheck) {
bool DrawingProgramSelection::is_selected(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
return selectedSet.contains(objToCheck);
}
void DrawingProgramSelection::delete_all() {
if(is_something_selected()) {
drawP.client_erase_set(selectedSet);
//drawP.client_erase_set(selectedSet);
reset_selection_data();
reset_transform_data();
}
@ -262,26 +214,26 @@ void DrawingProgramSelection::delete_all() {
// NOTE: Only run this if no transformations happened
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<CollabListType::ObjectInfoPtr> 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);
//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::update() {
if(is_something_selected()) {
if(cache.check_if_rebuild_should_occur())
cache.test_rebuild(std::vector<CollabListType::ObjectInfoPtr>(selectedSet.begin(), selectedSet.end()), true);
cache.test_rebuild(std::vector<CanvasComponentContainer::ObjInfoSharedPtr>(selectedSet.begin(), selectedSet.end()), true);
selectionRectPoints[0] = selectionTransformCoords.from_space_world(initialSelectionAABB.min);
selectionRectPoints[1] = selectionTransformCoords.from_space_world(initialSelectionAABB.bottom_left());
@ -397,45 +349,42 @@ bool DrawingProgramSelection::is_being_transformed() {
}
void DrawingProgramSelection::paste_clipboard(Vector2f pasteScreenPos) {
auto& clipboard = drawP.world.main.clipboard;
//auto& clipboard = drawP.world.main.clipboard;
if(!drawP.is_selection_allowing_tool(drawP.drawTool->get_type()))
drawP.switch_to_tool(DrawingProgramToolType::EDIT);
//if(!drawP.is_selection_allowing_tool(drawP.drawTool->get_type()))
// drawP.switch_to_tool(DrawingProgramToolType::EDIT);
uint64_t allPlacement = drawP.components.client_list().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;
}
}
//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->coords.translate(-moveVec);
c->obj->coords.scale_about(mousePos, scaleMultiplier);
//std::cout << c->obj->coords.inverseScale << std::endl;
//if(c->obj->coords.inverseScale == WorldScalar(0))
// throw std::runtime_error("DONE");
c->obj->commit_transform(drawP, false);
});
DrawComponent::client_send_place_many(drawP, compListInserted);
drawP.addToCompCacheOnInsert = true;
std::unordered_set<CollabListType::ObjectInfoPtr> compSetInserted(compListInserted.begin(), compListInserted.end());
set_to_selection(compSetInserted);
drawP.add_undo_place_components(compSetInserted);
//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);
}
void DrawingProgramSelection::rebuild_cam_space() {

View file

@ -10,7 +10,7 @@ class DrawingProgramSelection {
void add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, bool frontObjectOnly = false);
void remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC, bool frontObjectOnly = false);
CollabListType::ObjectInfoPtr get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC);
CanvasComponentContainer::ObjInfoSharedPtr get_front_object_colliding_with(const SCollision::ColliderCollection<float>& cC);
void deselect_all();
void update();
@ -18,31 +18,31 @@ class DrawingProgramSelection {
void draw_gui(SkCanvas* canvas, const DrawData& drawData);
bool is_something_selected();
bool mouse_collided_with_selection();
void erase_component(const CollabListType::ObjectInfoPtr& objToCheck);
bool is_selected(const CollabListType::ObjectInfoPtr& objToCheck);
const std::unordered_set<CollabListType::ObjectInfoPtr>& get_selected_set();
void erase_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
bool is_selected(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& get_selected_set();
bool is_being_transformed();
void invalidate_cache_at_optional_aabb_before_pos(const std::optional<SCollision::AABB<WorldScalar>>& aabb, uint64_t placementToInvalidateAt);
void invalidate_cache_at_aabb_before_pos(const SCollision::AABB<WorldScalar>& aabb, uint64_t placementToInvalidateAt);
void clear_own_cached_surfaces();
void preupdate_component(const CollabListType::ObjectInfoPtr& objToCheck);
void preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck);
void selection_to_clipboard();
void paste_clipboard(Vector2f pasteScreenPos);
void delete_all();
private:
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vector<CollabListType::ObjectInfoPtr>&)> erase_select_objects_in_bvh_func(std::unordered_set<CollabListType::ObjectInfoPtr>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld);
void fully_collided_erase_select_objects_func(std::unordered_set<CollabListType::ObjectInfoPtr>& selectedComponents, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vector<CanvasComponentContainer::ObjInfoSharedPtr>&)> erase_select_objects_in_bvh_func(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld);
void fully_collided_erase_select_objects_func(std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& selectedComponents, const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode);
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 set_to_selection(const std::unordered_set<CollabListType::ObjectInfoPtr>& newSelection);
void add_to_selection(const std::unordered_set<CollabListType::ObjectInfoPtr>& newSelection);
void add_erased_objects_back_to_cache(const std::unordered_set<CollabListType::ObjectInfoPtr>& erasedObjects);
void set_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection);
void add_to_selection(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& newSelection);
void add_erased_objects_back_to_cache(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& erasedObjects);
void commit_transform_selection();
@ -59,7 +59,7 @@ class DrawingProgramSelection {
CoordSpaceHelperTransform selectionTransformCoords;
std::unordered_set<CollabListType::ObjectInfoPtr> selectedSet;
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> selectedSet;
DrawingProgramCache cache;
DrawingProgram& drawP;

View file

@ -1,16 +1,12 @@
#include "EllipseDrawTool.hpp"
#include <chrono>
#include "DrawingProgram.hpp"
#include "../Server/CommandList.hpp"
#include "../MainProgram.hpp"
#include "../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"
#include "Helpers/MathExtras.hpp"
#include "Helpers/SCollision.hpp"
#include "Helpers/Serializers.hpp"
#include "../InputManager.hpp"
#include "../SharedTypes.hpp"
#include <cereal/types/vector.hpp>
#include "../CanvasComponents/EllipseCanvasComponent.hpp"
EllipseDrawTool::EllipseDrawTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
@ -24,10 +20,10 @@ void EllipseDrawTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("ellipse draw tool");
t.gui.text_label_centered("Draw Ellipse");
if(t.gui.radio_button_field("fillonly", "Fill only", controls.fillStrokeMode == 0)) controls.fillStrokeMode = 0;
if(t.gui.radio_button_field("outlineonly", "Outline only", controls.fillStrokeMode == 1)) controls.fillStrokeMode = 1;
if(t.gui.radio_button_field("filloutline", "Fill and Outline", controls.fillStrokeMode == 2)) controls.fillStrokeMode = 2;
if(controls.fillStrokeMode == 1 || controls.fillStrokeMode == 2)
if(t.gui.radio_button_field("fillonly", "Fill only", fillStrokeMode == 0)) fillStrokeMode = 0;
if(t.gui.radio_button_field("outlineonly", "Outline only", fillStrokeMode == 1)) fillStrokeMode = 1;
if(t.gui.radio_button_field("filloutline", "Fill and Outline", fillStrokeMode == 2)) fillStrokeMode = 2;
if(fillStrokeMode == 1 || fillStrokeMode == 2)
t.gui.slider_scalar_field("relstrokewidth", "Outline Size", &drawP.controls.relativeWidth, 3.0f, 40.0f);
t.gui.pop_id();
}
@ -43,46 +39,45 @@ void EllipseDrawTool::switch_tool(DrawingProgramToolType newTool) {
}
void EllipseDrawTool::tool_update() {
switch(controls.drawStage) {
switch(drawStage) {
case 0: {
if(drawP.controls.leftClick) {
controls.startAt = drawP.world.main.input.mouse.pos;
controls.intermediateItem = std::make_shared<DrawEllipse>();
controls.intermediateItem->d.strokeColor = drawP.controls.foregroundColor;
controls.intermediateItem->d.fillColor = drawP.controls.backgroundColor;
controls.intermediateItem->d.strokeWidth = drawP.controls.relativeWidth;
controls.intermediateItem->d.p1 = controls.startAt;
controls.intermediateItem->d.p2 = controls.startAt;
controls.intermediateItem->d.p2 = ensure_points_have_distance(controls.intermediateItem->d.p1, controls.intermediateItem->d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
controls.intermediateItem->d.fillStrokeMode = static_cast<uint8_t>(controls.fillStrokeMode);
controls.intermediateItem->coords = drawP.world.drawData.cam.c;
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
uint64_t placement = drawP.components.client_list().size();
auto objAdd = drawP.components.client_insert(placement, controls.intermediateItem);
controls.intermediateItem->client_send_place(drawP);
drawP.add_undo_place_component(objAdd);
controls.intermediateItem->commit_update(drawP);
controls.drawStage = 1;
CanvasComponentContainer* newContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponent::CompType::ELLIPSE);
EllipseCanvasComponent& newEllipse = static_cast<EllipseCanvasComponent&>(newContainer->get_comp());
startAt = drawP.world.main.input.mouse.pos;
newEllipse.d.strokeColor = drawP.controls.foregroundColor;
newEllipse.d.fillColor = drawP.controls.backgroundColor;
newEllipse.d.strokeWidth = drawP.controls.relativeWidth;
newEllipse.d.p1 = startAt;
newEllipse.d.p2 = startAt;
newEllipse.d.p2 = ensure_points_have_distance(newEllipse.d.p1, newEllipse.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
newEllipse.d.fillStrokeMode = static_cast<uint8_t>(fillStrokeMode);
newContainer->coords = drawP.world.drawData.cam.c;
intermediateContainer = drawP.components->push_back_and_send_create(drawP.components, newContainer)->obj;
drawStage = 1;
}
break;
}
case 1: {
if(drawP.controls.leftClickHeld) {
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
Vector2f newPos = controls.intermediateItem->coords.get_mouse_pos(drawP.world);
NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer> containerPtr = intermediateContainer.lock();
if(drawP.controls.leftClickHeld && containerPtr) {
Vector2f newPos = containerPtr->coords.get_mouse_pos(drawP.world);
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) {
float height = std::fabs(controls.startAt.y() - newPos.y());
newPos.x() = controls.startAt.x() + (((newPos.x() - controls.startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
float height = std::fabs(startAt.y() - newPos.y());
newPos.x() = startAt.x() + (((newPos.x() - startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
}
controls.intermediateItem->d.p1 = cwise_vec_min(controls.startAt, newPos);
controls.intermediateItem->d.p2 = cwise_vec_max(controls.startAt, newPos);
controls.intermediateItem->d.p2 = ensure_points_have_distance(controls.intermediateItem->d.p1, controls.intermediateItem->d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
controls.intermediateItem->client_send_update(drawP, false);
controls.intermediateItem->commit_update(drawP);
EllipseCanvasComponent& ellipse = static_cast<EllipseCanvasComponent&>(containerPtr->get_comp());
ellipse.d.p1 = cwise_vec_min(startAt, newPos);
ellipse.d.p2 = cwise_vec_max(startAt, newPos);
ellipse.d.p2 = ensure_points_have_distance(ellipse.d.p1, ellipse.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
containerPtr->send_comp_update(drawP, true);
containerPtr->commit_update(drawP);
}
else {
commit();
controls.drawStage = 0;
drawStage = 0;
}
break;
}
@ -90,15 +85,16 @@ void EllipseDrawTool::tool_update() {
}
bool EllipseDrawTool::prevent_undo_or_redo() {
return controls.intermediateItem != nullptr;
return (drawStage == 1);
}
void EllipseDrawTool::commit() {
if(controls.intermediateItem && controls.intermediateItem->collabListInfo.lock()) {
controls.intermediateItem->client_send_update(drawP, true);
controls.intermediateItem->commit_update(drawP);
NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer> lockedPtr = intermediateContainer.lock();
if(lockedPtr) {
lockedPtr->commit_update(drawP);
drawP.world.delayedUpdateObjectManager.send_update_to_all<CanvasComponentContainer>(lockedPtr, true);
}
controls.intermediateItem = nullptr;
intermediateContainer.reset();
}
void EllipseDrawTool::draw(SkCanvas* canvas, const DrawData& drawData) {

View file

@ -1,8 +1,8 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
#include "../DrawComponents/DrawEllipse.hpp"
#include <any>
#include <Helpers/SCollision.hpp>
#include <Helpers/NetworkingObjects/NetObjWeakPtr.hpp>
#include "../CanvasComponents/CanvasComponentContainer.hpp"
class DrawingProgram;
@ -19,10 +19,8 @@ class EllipseDrawTool : public DrawingProgramToolBase {
private:
void commit();
struct EllipseDrawControls {
Vector2f startAt;
int fillStrokeMode = 1;
int drawStage = 0;
std::shared_ptr<DrawEllipse> intermediateItem;
} controls;
Vector2f startAt;
int fillStrokeMode = 1;
int drawStage = 0;
NetworkingObjects::NetObjWeakPtr<CanvasComponentContainer> intermediateContainer;
};

View file

@ -27,9 +27,9 @@ bool EraserTool::right_click_popup_gui(Vector2f popupPos) {
}
void EraserTool::switch_tool(DrawingProgramToolType newTool) {
DrawingProgramCache::move_components_from_bvh_nodes_to_set(erasedComponents, erasedBVHNodes);
if(!erasedComponents.empty()) {
DrawingProgramCache::move_components_from_bvh_nodes_to_set(erasedComponents, erasedBVHNodes);
drawP.client_erase_set(erasedComponents);
drawP.erase_component_set(erasedComponents);
// NOTE: Ideally, you would just recalculate the lastDrawnPos of every cache instead of invalidating and redrawing the caches
// However, the eraser might have other visual glitches during drawing that i am unaware of, so for now, just refresh the
// cache after a draw
@ -62,7 +62,7 @@ void EraserTool::tool_update() {
std::erase_if(comps, [&](auto& c) {
if(c->obj->collides_with(drawP.world.drawData.cam.c, cCWorld, cC)) {
erasedComponents.emplace(c);
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->worldAABB.value(), c->obj->collabListInfo.lock()->pos);
drawP.compCache.invalidate_cache_at_aabb_before_pos(c->obj->get_world_bounds(), c->pos);
return true;
}
return false;

View file

@ -1,8 +1,8 @@
#pragma once
#include <include/core/SkCanvas.h>
#include "../DrawData.hpp"
#include "../DrawComponents/DrawComponent.hpp"
#include "DrawingProgramToolBase.hpp"
#include "DrawingProgramCache.hpp"
class DrawingProgram;
@ -17,6 +17,6 @@ class EraserTool : public DrawingProgramToolBase {
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual bool prevent_undo_or_redo() override;
std::unordered_set<CollabListType::ObjectInfoPtr> erasedComponents;
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> erasedComponents; // Pointers will be erased from this set if theyre erased in the main list (done by callback)
std::unordered_set<std::shared_ptr<DrawingProgramCacheBVHNode>> erasedBVHNodes;
};

View file

@ -141,7 +141,7 @@ void GridModifyTool::tool_update() {
selectionMode = 0;
break;
}
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, nullptr, false);
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, false);
}
else
selectionMode = 0;
@ -187,5 +187,5 @@ void GridModifyTool::draw(SkCanvas* canvas, const DrawData& drawData) {
void GridModifyTool::switch_tool(DrawingProgramToolType newTool) {
NetworkingObjects::NetObjTemporaryPtr<WorldGrid> gLock = grid.lock();
if(gLock)
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, nullptr, true);
drawP.world.delayedUpdateObjectManager.send_update_to_all<WorldGrid>(gLock, true);
}

View file

@ -3,7 +3,7 @@
#include "../MainProgram.hpp"
#include "../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"
#include <cereal/types/vector.hpp>
#include "../CanvasComponents/RectangleCanvasComponent.hpp"
RectDrawTool::RectDrawTool(DrawingProgram& initDrawP):
DrawingProgramToolBase(initDrawP)
@ -17,11 +17,11 @@ void RectDrawTool::gui_toolbox() {
Toolbar& t = drawP.world.main.toolbar;
t.gui.push_id("rect draw tool");
t.gui.text_label_centered("Draw Rectangle");
t.gui.slider_scalar_field("relradiuswidth", "Corner Radius", &controls.relativeRadiusWidth, 0.0f, 40.0f);
if(t.gui.radio_button_field("fillonly", "Fill only", controls.fillStrokeMode == 0)) controls.fillStrokeMode = 0;
if(t.gui.radio_button_field("outlineonly", "Outline only", controls.fillStrokeMode == 1)) controls.fillStrokeMode = 1;
if(t.gui.radio_button_field("filloutline", "Fill and Outline", controls.fillStrokeMode == 2)) controls.fillStrokeMode = 2;
if(controls.fillStrokeMode == 1 || controls.fillStrokeMode == 2)
t.gui.slider_scalar_field("relradiuswidth", "Corner Radius", &relativeRadiusWidth, 0.0f, 40.0f);
if(t.gui.radio_button_field("fillonly", "Fill only", fillStrokeMode == 0)) fillStrokeMode = 0;
if(t.gui.radio_button_field("outlineonly", "Outline only", fillStrokeMode == 1)) fillStrokeMode = 1;
if(t.gui.radio_button_field("filloutline", "Fill and Outline", fillStrokeMode == 2)) fillStrokeMode = 2;
if(fillStrokeMode == 1 || fillStrokeMode == 2)
t.gui.slider_scalar_field("relstrokewidth", "Outline Size", &drawP.controls.relativeWidth, 3.0f, 40.0f);
t.gui.pop_id();
}
@ -34,51 +34,49 @@ bool RectDrawTool::right_click_popup_gui(Vector2f popupPos) {
void RectDrawTool::switch_tool(DrawingProgramToolType newTool) {
commit_rectangle();
controls.drawStage = 0;
drawStage = 0;
}
void RectDrawTool::tool_update() {
switch(controls.drawStage) {
switch(drawStage) {
case 0: {
if(drawP.controls.leftClick) {
controls.startAt = drawP.world.main.input.mouse.pos;
controls.intermediateItem = std::make_shared<DrawRectangle>();
controls.intermediateItem->d.strokeColor = drawP.controls.foregroundColor;
controls.intermediateItem->d.fillColor = drawP.controls.backgroundColor;
controls.intermediateItem->d.cornerRadius = controls.relativeRadiusWidth;
controls.intermediateItem->d.strokeWidth = drawP.controls.relativeWidth;
controls.intermediateItem->d.p1 = controls.startAt;
controls.intermediateItem->d.p2 = controls.startAt;
controls.intermediateItem->d.p2 = ensure_points_have_distance(controls.intermediateItem->d.p1, controls.intermediateItem->d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
controls.intermediateItem->d.fillStrokeMode = static_cast<uint8_t>(controls.fillStrokeMode);
controls.intermediateItem->coords = drawP.world.drawData.cam.c;
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
uint64_t placement = drawP.components.client_list().size();
auto objAdd = drawP.components.client_insert(placement, controls.intermediateItem);
controls.intermediateItem->commit_update(drawP);
controls.intermediateItem->client_send_place(drawP);
drawP.add_undo_place_component(objAdd);
controls.drawStage = 1;
CanvasComponentContainer* newContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponent::CompType::RECTANGLE);
RectangleCanvasComponent& newRectangle = static_cast<RectangleCanvasComponent&>(newContainer->get_comp());
startAt = drawP.world.main.input.mouse.pos;
newRectangle.d.strokeColor = drawP.controls.foregroundColor;
newRectangle.d.fillColor = drawP.controls.backgroundColor;
newRectangle.d.cornerRadius = relativeRadiusWidth;
newRectangle.d.strokeWidth = drawP.controls.relativeWidth;
newRectangle.d.p1 = startAt;
newRectangle.d.p2 = startAt;
newRectangle.d.p2 = ensure_points_have_distance(newRectangle.d.p1, newRectangle.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
newRectangle.d.fillStrokeMode = static_cast<uint8_t>(fillStrokeMode);
newContainer->coords = drawP.world.drawData.cam.c;
intermediateContainer = drawP.components->push_back_and_send_create(drawP.components, newContainer)->obj;
drawStage = 1;
}
break;
}
case 1: {
if(drawP.controls.leftClickHeld) {
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
Vector2f newPos = controls.intermediateItem->coords.get_mouse_pos(drawP.world);
NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer> containerPtr = intermediateContainer.lock();
if(drawP.controls.leftClickHeld && containerPtr) {
Vector2f newPos = containerPtr->coords.get_mouse_pos(drawP.world);
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) {
float height = std::fabs(controls.startAt.y() - newPos.y());
newPos.x() = controls.startAt.x() + (((newPos.x() - controls.startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
float height = std::fabs(startAt.y() - newPos.y());
newPos.x() = startAt.x() + (((newPos.x() - startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
}
controls.intermediateItem->d.p1 = cwise_vec_min(controls.startAt, newPos);
controls.intermediateItem->d.p2 = cwise_vec_max(controls.startAt, newPos);
controls.intermediateItem->d.p2 = ensure_points_have_distance(controls.intermediateItem->d.p1, controls.intermediateItem->d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
controls.intermediateItem->client_send_update(drawP, false);
controls.intermediateItem->commit_update(drawP);
RectangleCanvasComponent& rectangle = static_cast<RectangleCanvasComponent&>(containerPtr->get_comp());
rectangle.d.p1 = cwise_vec_min(startAt, newPos);
rectangle.d.p2 = cwise_vec_max(startAt, newPos);
rectangle.d.p2 = ensure_points_have_distance(rectangle.d.p1, rectangle.d.p2, MINIMUM_DISTANCE_BETWEEN_BOUNDS);
containerPtr->send_comp_update(drawP, true);
containerPtr->commit_update(drawP);
}
else {
commit_rectangle();
controls.drawStage = 0;
drawStage = 0;
}
break;
}
@ -86,15 +84,16 @@ void RectDrawTool::tool_update() {
}
bool RectDrawTool::prevent_undo_or_redo() {
return controls.intermediateItem != nullptr;
return (drawStage == 1);
}
void RectDrawTool::commit_rectangle() {
if(controls.intermediateItem && controls.intermediateItem->collabListInfo.lock()) {
controls.intermediateItem->client_send_update(drawP, true);
controls.intermediateItem->commit_update(drawP);
NetworkingObjects::NetObjTemporaryPtr<CanvasComponentContainer> lockedPtr = intermediateContainer.lock();
if(lockedPtr) {
lockedPtr->commit_update(drawP);
drawP.world.delayedUpdateObjectManager.send_update_to_all<CanvasComponentContainer>(lockedPtr, true);
}
controls.intermediateItem = nullptr;
intermediateContainer.reset();
}
void RectDrawTool::draw(SkCanvas* canvas, const DrawData& drawData) {

View file

@ -1,8 +1,7 @@
#pragma once
#include "DrawingProgramToolBase.hpp"
#include "../DrawComponents/DrawRectangle.hpp"
#include <Helpers/SCollision.hpp>
#include <any>
#include "../CanvasComponents/CanvasComponentContainer.hpp"
class DrawingProgram;
@ -19,11 +18,9 @@ class RectDrawTool : public DrawingProgramToolBase {
private:
void commit_rectangle();
struct RectDrawControls {
Vector2f startAt;
float relativeRadiusWidth = 10.0f;
int fillStrokeMode = 1;
int drawStage = 0;
std::shared_ptr<DrawRectangle> intermediateItem;
} controls;
Vector2f startAt;
float relativeRadiusWidth = 10.0f;
int fillStrokeMode = 1;
int drawStage = 0;
NetworkingObjects::NetObjWeakPtr<CanvasComponentContainer> intermediateContainer;
};