Reimplemented image components

This commit is contained in:
Yousef Khadadeh 2025-12-16 12:54:12 +03:00
parent 9187518290
commit 859924663b
8 changed files with 180 additions and 15 deletions

View file

@ -18,6 +18,7 @@ class CanvasComponent {
virtual void load(cereal::PortableBinaryInputArchive& a) = 0;
virtual void save_file(cereal::PortableBinaryOutputArchive& a) const = 0;
virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) = 0;
virtual void update(DrawingProgram& drawP);
virtual void set_data_from(const CanvasComponent& other) = 0;
protected:
@ -26,7 +27,6 @@ class CanvasComponent {
virtual void draw(SkCanvas* canvas, const DrawData& drawData) const = 0;
virtual void initialize_draw_data(DrawingProgram& drawP) = 0;
virtual void update(DrawingProgram& drawP);
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const = 0;
virtual SCollision::AABB<float> get_obj_coord_bounds() const = 0;

View file

@ -64,8 +64,8 @@ void ImageCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) cons
void ImageCanvasComponent::update(DrawingProgram& drawP) {
ResourceDisplay* display = drawP.world.drawData.rMan->get_display_data(d.imageID);
if(display) {
//if(display->update_draw())
// drawP.invalidate_cache_at_component(compContainer->objInfo);
if(display->update_draw())
drawP.invalidate_cache_at_component(compContainer->objInfo.lock());
}
}

View file

@ -23,10 +23,11 @@ class ImageCanvasComponent : public CanvasComponent {
NetworkingObjects::NetObjID imageID;
} d;
virtual void update(DrawingProgram& drawP) override;
private:
virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override;
virtual void initialize_draw_data(DrawingProgram& drawP) override;
virtual void update(DrawingProgram& drawP) override;
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const override;
void create_draw_data();
void create_collider();

View file

@ -27,6 +27,7 @@
#include <include/core/SkPathEffect.h>
#include <include/effects/SkDashPathEffect.h>
#include <chrono>
#include "../CanvasComponents/ImageCanvasComponent.hpp"
#include "EraserTool.hpp"
@ -50,11 +51,17 @@ void DrawingProgram::set_component_list_callbacks() {
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(c->obj->get_comp().get_type() == CanvasComponentType::IMAGE)
updateableComponents.emplace(c);
});
components->set_erase_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) {
compCache.erase_component(c);
selection.erase_component(c);
drawTool->erase_component(c);
std::erase_if(droppedDownloadingFiles, [&c](auto& downloadingFile) {
return downloadingFile.comp == c;
});
updateableComponents.erase(c);
});
components->set_post_sync_callback([&]() {
clear_draw_cache();
@ -287,6 +294,13 @@ 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)
@ -322,6 +336,9 @@ void DrawingProgram::update() {
if(controls.leftClickReleased)
controls.leftClickReleased = false;
update_downloading_dropped_files();
check_updateable_components();
if(drawTool->get_type() == DrawingProgramToolType::ERASER) {
EraserTool* eraserTool = static_cast<EraserTool*>(drawTool.get());
compCache.test_rebuild_dont_include_set_dont_include_nodes(components->get_data(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes);
@ -332,6 +349,11 @@ void DrawingProgram::update() {
compCache.test_rebuild(components->get_data());
}
void DrawingProgram::check_updateable_components() {
for(auto& comp : updateableComponents)
comp->obj->get_comp().update(*this);
}
bool DrawingProgram::is_actual_selection_tool(DrawingProgramToolType typeToCheck) {
return typeToCheck == DrawingProgramToolType::RECTSELECT || typeToCheck == DrawingProgramToolType::LASSOSELECT || typeToCheck == DrawingProgramToolType::EDIT;
}
@ -383,6 +405,119 @@ void DrawingProgram::force_rebuild_cache() {
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())) {
CanvasComponentContainer* newContainer = new CanvasComponentContainer(world.netObjMan, CanvasComponentType::IMAGE);
ImageCanvasComponent& img = static_cast<ImageCanvasComponent&>(newContainer->get_comp());
newContainer->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};
auto newObjInfo = components->push_back_and_send_create(components, newContainer);
droppedDownloadingFiles.emplace_back(newObjInfo, 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: {
ImageCanvasComponent& img = static_cast<ImageCanvasComponent&>(downFile.comp->obj->get_comp());
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;
NetworkingObjects::NetObjID imageID = world.rMan.add_resource(newResource).get_net_id();
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");
components->erase(components, downFile.comp->obj.get_net_id());
}
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;
downFile.comp->obj->send_comp_update(*this, true);
downFile.comp->obj->commit_update(*this);
}
return true;
}
case FileDownloader::DownloadData::Status::FAILURE:
Logger::get().log("WORLDFATAL", "Failed to download data from URL");
components->erase(components, downFile.comp->obj.get_net_id());
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) {
NetworkingObjects::NetObjTemporaryPtr<ResourceData> imageTempPtr = world.rMan.add_resource_file(filePath);
if(imageTempPtr) {
NetworkingObjects::NetObjID imageID = imageTempPtr.get_net_id();
ResourceDisplay* display = world.rMan.get_display_data(imageID);
Vector2f imTrueDim = display->get_dimensions();
CanvasComponentContainer* newContainer = new CanvasComponentContainer(world.netObjMan, CanvasComponentType::IMAGE);
ImageCanvasComponent& img = static_cast<ImageCanvasComponent&>(newContainer->get_comp());
newContainer->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;
components->push_back_and_send_create(components, newContainer);
}
}
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;
NetworkingObjects::NetObjID imageID = world.rMan.add_resource(newResource).get_net_id();
ResourceDisplay* display = world.rMan.get_display_data(imageID);
Vector2f imTrueDim = display->get_dimensions();
CanvasComponentContainer* newContainer = new CanvasComponentContainer(world.netObjMan, CanvasComponentType::IMAGE);
ImageCanvasComponent& img = static_cast<ImageCanvasComponent&>(newContainer->get_comp());
newContainer->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;
components->push_back_and_send_create(components, newContainer);
}
void DrawingProgram::invalidate_cache_at_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
if(selection.is_selected(objToCheck))
selection.invalidate_cache_at_aabb_before_pos(objToCheck->obj->get_world_bounds(), objToCheck->pos);
@ -451,6 +586,20 @@ 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)
static_cast<ImageCanvasComponent&>(droppedDownFile.comp->obj->get_comp()).draw_download_progress_bar(canvas, drawData, droppedDownFile.downData->progress);
for(auto& c : updateableComponents) {
if(c->obj->get_comp().get_type() == CanvasComponentType::IMAGE) {
auto& img = static_cast<ImageCanvasComponent&>(c->obj->get_comp());
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

@ -34,6 +34,9 @@ class DrawingProgram {
void write_components_server(cereal::PortableBinaryOutputArchive& a);
void read_components_client(cereal::PortableBinaryInputArchive& a);
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);
std::unordered_set<ServerClientID> get_used_resources() const;
void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version);
void save_file(cereal::PortableBinaryOutputArchive& a) const;
@ -57,6 +60,11 @@ class DrawingProgram {
void modify_grid(const NetworkingObjects::NetObjWeakPtr<WorldGrid>& gridToModify);
private:
void drag_drop_update();
void add_file_to_canvas_by_path_execute(const std::filesystem::path& filePath, Vector2f dropPos);
void check_updateable_components();
void update_downloading_dropped_files();
std::optional<std::chrono::steady_clock::time_point> badFrametimeTimePoint;
std::optional<std::chrono::steady_clock::time_point> unorderedObjectsExistTimePoint;
@ -76,6 +84,7 @@ class DrawingProgram {
DrawingProgramSelection selection;
std::unique_ptr<DrawingProgramToolBase> toolToSwitchToAfterUpdate;
std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr> updateableComponents;
bool temporaryEraser = false;
bool temporaryPan = false;
@ -99,6 +108,13 @@ class DrawingProgram {
int colorEditing = 0;
} controls;
struct DroppedDownloadingFile {
CanvasComponentContainer::ObjInfoSharedPtr comp;
Vector2f windowSizeWhenDropped;
std::shared_ptr<FileDownloader::DownloadData> downData;
};
std::vector<DroppedDownloadingFile> droppedDownloadingFiles;
std::unique_ptr<DrawingProgramToolBase> drawTool;
uint32_t nextID = 0;

View file

@ -84,12 +84,12 @@ void ResourceManager::update() {
v->update(world);
}
NetworkingObjects::NetObjID ResourceManager::add_resource_file(const std::filesystem::path& filePath) {
NetworkingObjects::NetObjTemporaryPtr<ResourceData> ResourceManager::add_resource_file(const std::filesystem::path& filePath) {
// https://nullptr.org/cpp-read-file-into-string/
std::ifstream file(filePath, std::ios::in | std::ios::binary | std::ios::ate);
if(!file.is_open()) {
Logger::get().log("INFO", "[ResourceManager::add_resource_file] Could not open file " + filePath.string());
return {0, 0};
return {};
}
size_t resourcesize;
@ -98,7 +98,7 @@ NetworkingObjects::NetObjID ResourceManager::add_resource_file(const std::filesy
if(tellgResult == -1) {
Logger::get().log("INFO", "[ResourceManager::add_resource_file] tellg failed for file " + filePath.string());
return {0, 0};
return {};
}
resourcesize = static_cast<size_t>(tellgResult);
@ -121,13 +121,13 @@ NetworkingObjects::NetObjID ResourceManager::add_resource_file(const std::filesy
return add_resource(resource);
}
NetworkingObjects::NetObjID ResourceManager::add_resource(const ResourceData& resource) {
const NetworkingObjects::NetObjOwnerPtr<ResourceData>& ResourceManager::add_resource(const ResourceData& resource) {
auto it = std::find_if(resourceList.begin(), resourceList.end(), [&](const auto& p) {
return p->data == resource.data || (*p->data) == (*resource.data);
});
if(it != resourceList.end()) {
Logger::get().log("INFO", "[ResourceManager::add_resource] File " + std::string(resource.name) + " is a duplicate");
return it->get_net_id();
return *it;
}
auto& resourceInsert = resourceList.emplace_back(world.netObjMan.make_obj_direct<ResourceData>(resource));
if(world.con.localServer) {
@ -138,7 +138,7 @@ NetworkingObjects::NetObjID ResourceManager::add_resource(const ResourceData& re
world.con.client_send_items_to_server(RESOURCE_COMMAND_CHANNEL, SERVER_NEW_RESOURCE_ID, resourceInsert.get_net_id());
world.con.client_send_items_to_server(RESOURCE_COMMAND_CHANNEL, SERVER_NEW_RESOURCE_DATA, resource);
}
return resourceInsert.get_net_id();
return resourceInsert;
}
ResourceDisplay* ResourceManager::get_display_data(const NetworkingObjects::NetObjID& fileID) {

View file

@ -26,8 +26,8 @@ class ResourceManager {
void update();
void init_client_callbacks();
void init_server_callbacks();
NetworkingObjects::NetObjID add_resource_file(const std::filesystem::path& filePath);
NetworkingObjects::NetObjID add_resource(const ResourceData& resource);
NetworkingObjects::NetObjTemporaryPtr<ResourceData> add_resource_file(const std::filesystem::path& filePath);
const NetworkingObjects::NetObjOwnerPtr<ResourceData>& add_resource(const ResourceData& resource);
ResourceDisplay* get_display_data(const NetworkingObjects::NetObjID& fileID);
const std::vector<NetworkingObjects::NetObjOwnerPtr<ResourceData>>& resource_list();
float get_resource_retrieval_progress(const NetworkingObjects::NetObjID& id);

View file

@ -540,9 +540,8 @@ void Toolbar::top_toolbar() {
#else
open_file_selector("Open File", {{"Any File", "*"}}, [w = make_weak_ptr(main.world)](const std::filesystem::path& p, const auto& e) {
auto wLock = w.lock();
// NOTE: Implement again later
//if(wLock)
// wLock->drawProg.add_file_to_canvas_by_path(p.string(), wLock->main.window.size.cast<float>() / 2.0f, false);
if(wLock)
wLock->drawProg.add_file_to_canvas_by_path(p.string(), wLock->main.window.size.cast<float>() / 2.0f, false);
});
#endif
}