2025-07-06 20:43:53 +03:00
|
|
|
#include "DrawingProgram.hpp"
|
2025-08-02 20:10:33 +03:00
|
|
|
#include "DrawingProgramToolBase.hpp"
|
2025-07-06 20:43:53 +03:00
|
|
|
#include <include/core/SkPaint.h>
|
|
|
|
|
#include <include/core/SkVertices.h>
|
|
|
|
|
#include "../DrawCamera.hpp"
|
|
|
|
|
#include "EllipseDrawTool.hpp"
|
2025-08-05 12:11:50 +03:00
|
|
|
#include "EyeDropperTool.hpp"
|
2025-09-10 15:03:06 +03:00
|
|
|
#include "GridModifyTool.hpp"
|
2025-07-06 20:43:53 +03:00
|
|
|
#include "../InputManager.hpp"
|
|
|
|
|
#include "../SharedTypes.hpp"
|
|
|
|
|
#include "../Server/CommandList.hpp"
|
2025-08-02 20:10:33 +03:00
|
|
|
#include <memory>
|
2025-07-06 20:43:53 +03:00
|
|
|
#include <optional>
|
|
|
|
|
#include <Helpers/ConvertVec.hpp>
|
|
|
|
|
#include <cereal/types/vector.hpp>
|
|
|
|
|
#include <include/core/SkImage.h>
|
|
|
|
|
#include <include/core/SkSurface.h>
|
|
|
|
|
#include "../World.hpp"
|
|
|
|
|
#include "../MainProgram.hpp"
|
2025-12-14 18:57:40 +03:00
|
|
|
#include <Helpers/FileDownloader.hpp>
|
|
|
|
|
#include <Helpers/NetworkingObjects/NetObjID.hpp>
|
|
|
|
|
#include <Helpers/StringHelpers.hpp>
|
2025-08-02 12:31:16 +03:00
|
|
|
#include "LassoSelectTool.hpp"
|
2025-07-06 20:43:53 +03:00
|
|
|
#include <Helpers/Logger.hpp>
|
2025-07-24 19:35:51 +03:00
|
|
|
#include <Helpers/Parallel.hpp>
|
2025-07-26 21:20:14 +03:00
|
|
|
#include <cereal/types/unordered_set.hpp>
|
2025-08-02 12:31:16 +03:00
|
|
|
#include <include/core/SkPathEffect.h>
|
|
|
|
|
#include <include/effects/SkDashPathEffect.h>
|
|
|
|
|
#include <chrono>
|
2025-07-19 19:41:25 +03:00
|
|
|
|
2025-08-02 20:10:33 +03:00
|
|
|
#include "EraserTool.hpp"
|
|
|
|
|
|
2025-07-06 20:43:53 +03:00
|
|
|
DrawingProgram::DrawingProgram(World& initWorld):
|
|
|
|
|
world(initWorld),
|
2025-07-20 15:24:44 +03:00
|
|
|
compCache(*this),
|
2025-07-24 16:35:48 +03:00
|
|
|
selection(*this)
|
2025-07-06 20:43:53 +03:00
|
|
|
{
|
2025-08-02 20:39:21 +03:00
|
|
|
drawTool = DrawingProgramToolBase::allocate_tool_type(*this, DrawingProgramToolType::BRUSH);
|
2025-12-14 18:57:40 +03:00
|
|
|
}
|
2025-08-02 20:10:33 +03:00
|
|
|
|
2025-12-14 18:57:40 +03:00
|
|
|
void DrawingProgram::init() {
|
|
|
|
|
if(world.netObjMan.is_server()) {
|
|
|
|
|
components = world.netObjMan.make_obj<NetworkingObjects::NetObjOrderedList<CanvasComponentContainer>>();
|
|
|
|
|
set_component_list_callbacks();
|
|
|
|
|
}
|
2025-07-06 20:43:53 +03:00
|
|
|
}
|
|
|
|
|
|
2025-12-14 18:57:40 +03:00
|
|
|
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);
|
2025-07-06 20:43:53 +03:00
|
|
|
}
|
|
|
|
|
});
|
2025-12-14 18:57:40 +03:00
|
|
|
components->set_erase_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) {
|
|
|
|
|
compCache.erase_component(c);
|
|
|
|
|
selection.erase_component(c);
|
2025-07-26 21:20:14 +03:00
|
|
|
});
|
2025-12-14 18:57:40 +03:00
|
|
|
components->set_post_sync_callback([&]() {
|
|
|
|
|
clear_draw_cache();
|
2025-07-06 20:43:53 +03:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 18:57:40 +03:00
|
|
|
void DrawingProgram::init_client_callbacks() {
|
2025-07-21 13:57:48 +03:00
|
|
|
}
|
|
|
|
|
|
2025-12-14 18:57:40 +03:00
|
|
|
void DrawingProgram::scale_up(const WorldScalar& scaleUpAmount) {
|
|
|
|
|
//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);
|
2025-07-21 13:57:48 +03:00
|
|
|
}
|
|
|
|
|
|
2025-12-14 18:57:40 +03:00
|
|
|
void DrawingProgram::parallel_loop_all_components(std::function<void(const CanvasComponentContainer::ObjInfoSharedPtr&)> func) {
|
|
|
|
|
parallel_loop_container(components->get_data(), func);
|
2025-07-06 20:43:53 +03:00
|
|
|
}
|
|
|
|
|
|
2025-11-16 16:39:12 +03:00
|
|
|
std::unordered_set<ServerClientID> DrawingProgram::get_used_resources() const {
|
2025-07-06 20:43:53 +03:00
|
|
|
std::unordered_set<ServerClientID> toRet;
|
2025-12-14 18:57:40 +03:00
|
|
|
//for(auto& c : components->get_data())
|
|
|
|
|
// c->obj->get_used_resources(toRet);
|
2025-07-06 20:43:53 +03:00
|
|
|
return toRet;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 18:57:40 +03:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-18 08:16:29 +03:00
|
|
|
void DrawingProgram::clear_draw_cache() {
|
|
|
|
|
compCache.clear_own_cached_surfaces();
|
|
|
|
|
selection.clear_own_cached_surfaces();
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-06 20:43:53 +03:00
|
|
|
void DrawingProgram::toolbar_gui() {
|
|
|
|
|
Toolbar& t = world.main.toolbar;
|
2025-12-01 17:33:14 +03:00
|
|
|
t.gui.push_id("Drawing Program Toolbar GUI");
|
2025-12-02 16:09:30 +03:00
|
|
|
CLAY_AUTO_ID({
|
2025-07-06 20:43:53 +03:00
|
|
|
.layout = {
|
|
|
|
|
.sizing = {.width = CLAY_SIZING_FIT(0), .height = CLAY_SIZING_FIT(0)},
|
2025-11-01 20:47:34 +03:00
|
|
|
.padding = CLAY_PADDING_ALL(static_cast<uint16_t>(t.io->theme->padding1 / 2)),
|
2025-11-01 18:07:39 +03:00
|
|
|
.childGap = static_cast<uint16_t>(t.io->theme->childGap1 / 2),
|
2025-07-06 20:43:53 +03:00
|
|
|
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP},
|
|
|
|
|
.layoutDirection = CLAY_TOP_TO_BOTTOM
|
|
|
|
|
},
|
|
|
|
|
.backgroundColor = convert_vec4<Clay_Color>(t.io->theme->backColor1),
|
2025-11-01 20:47:34 +03:00
|
|
|
.cornerRadius = CLAY_CORNER_RADIUS(t.io->theme->windowCorners1)
|
2025-07-06 20:43:53 +03:00
|
|
|
}) {
|
|
|
|
|
t.gui.obstructing_window();
|
2025-11-01 16:07:19 +03:00
|
|
|
if(t.gui.svg_icon_button_transparent("Brush Toolbar Button", "data/icons/brush.svg", drawTool->get_type() == DrawingProgramToolType::BRUSH)) { switch_to_tool(DrawingProgramToolType::BRUSH); }
|
|
|
|
|
if(t.gui.svg_icon_button_transparent("Eraser Toolbar Button", "data/icons/eraser.svg", drawTool->get_type() == DrawingProgramToolType::ERASER)) { switch_to_tool(DrawingProgramToolType::ERASER); }
|
|
|
|
|
if(t.gui.svg_icon_button_transparent("Text Toolbar Button", "data/icons/text.svg", drawTool->get_type() == DrawingProgramToolType::TEXTBOX)) { switch_to_tool(DrawingProgramToolType::TEXTBOX); }
|
|
|
|
|
if(t.gui.svg_icon_button_transparent("Ellipse Toolbar Button", "data/icons/circle.svg", drawTool->get_type() == DrawingProgramToolType::ELLIPSE)) { switch_to_tool(DrawingProgramToolType::ELLIPSE); }
|
|
|
|
|
if(t.gui.svg_icon_button_transparent("Rect Toolbar Button", "data/icons/rectangle.svg", drawTool->get_type() == DrawingProgramToolType::RECTANGLE)) { switch_to_tool(DrawingProgramToolType::RECTANGLE); }
|
|
|
|
|
if(t.gui.svg_icon_button_transparent("RectSelect Toolbar Button", "data/icons/rectselect.svg", drawTool->get_type() == DrawingProgramToolType::RECTSELECT)) { switch_to_tool(DrawingProgramToolType::RECTSELECT); }
|
|
|
|
|
if(t.gui.svg_icon_button_transparent("LassoSelect Toolbar Button", "data/icons/lassoselect.svg", drawTool->get_type() == DrawingProgramToolType::LASSOSELECT)) { switch_to_tool(DrawingProgramToolType::LASSOSELECT); }
|
|
|
|
|
if(t.gui.svg_icon_button_transparent("Edit Toolbar Button", "data/icons/cursor.svg", drawTool->get_type() == DrawingProgramToolType::EDIT)) { switch_to_tool(DrawingProgramToolType::EDIT); }
|
|
|
|
|
if(t.gui.svg_icon_button_transparent("Eyedropper Toolbar Button", "data/icons/eyedropper.svg", drawTool->get_type() == DrawingProgramToolType::EYEDROPPER)) { switch_to_tool(DrawingProgramToolType::EYEDROPPER); }
|
|
|
|
|
if(t.gui.svg_icon_button_transparent("Zoom Canvas Toolbar Button", "data/icons/zoom.svg", drawTool->get_type() == DrawingProgramToolType::ZOOM)) { switch_to_tool(DrawingProgramToolType::ZOOM); }
|
|
|
|
|
if(t.gui.svg_icon_button_transparent("Pan Canvas Toolbar Button", "data/icons/hand.svg", drawTool->get_type() == DrawingProgramToolType::PAN)) { switch_to_tool(DrawingProgramToolType::PAN); }
|
2025-09-28 16:32:57 +03:00
|
|
|
|
|
|
|
|
double newRotationAngle = world.drawData.cam.c.rotation, oldRotationAngle = world.drawData.cam.c.rotation;
|
|
|
|
|
t.gui.rotate_wheel("Canvas Rotate Wheel", &newRotationAngle);
|
|
|
|
|
if(newRotationAngle != oldRotationAngle)
|
|
|
|
|
world.drawData.cam.c.rotate_about(world.drawData.cam.c.from_space(world.main.window.size.cast<float>() * 0.5f), newRotationAngle - oldRotationAngle);
|
|
|
|
|
|
2025-11-01 16:07:19 +03:00
|
|
|
if(t.gui.big_color_button("Foreground Color", &controls.foregroundColor, &controls.foregroundColor == t.colorLeft))
|
|
|
|
|
t.color_selector_left(&controls.foregroundColor == t.colorLeft ? nullptr : &controls.foregroundColor);
|
|
|
|
|
if(t.gui.big_color_button("Background Color", &controls.backgroundColor, &controls.backgroundColor == t.colorLeft))
|
|
|
|
|
t.color_selector_left(&controls.backgroundColor == t.colorLeft ? nullptr : &controls.backgroundColor);
|
2025-07-06 20:43:53 +03:00
|
|
|
}
|
2025-12-01 17:33:14 +03:00
|
|
|
t.gui.pop_id();
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-01 18:12:22 +03:00
|
|
|
// Should only be used in selection allowing tools
|
2025-12-01 17:33:14 +03:00
|
|
|
bool DrawingProgram::selection_action_menu(Vector2f popupPos) {
|
|
|
|
|
Toolbar& t = world.main.toolbar;
|
2025-12-01 18:12:22 +03:00
|
|
|
bool shouldClose = false;
|
|
|
|
|
t.gui.list_popup_menu("Selection popup menu", popupPos, [&]() {
|
|
|
|
|
t.gui.text_label_light("Selection menu");
|
|
|
|
|
if(t.gui.text_button_left_transparent("Paste", "Paste")) {
|
|
|
|
|
selection.deselect_all();
|
2025-12-03 09:55:17 +03:00
|
|
|
selection.paste_clipboard(popupPos * t.final_gui_scale());
|
2025-12-01 18:12:22 +03:00
|
|
|
shouldClose = true;
|
|
|
|
|
}
|
|
|
|
|
if(selection.is_something_selected()) {
|
|
|
|
|
if(t.gui.text_button_left_transparent("Copy", "Copy")) {
|
|
|
|
|
selection.selection_to_clipboard();
|
|
|
|
|
shouldClose = true;
|
|
|
|
|
}
|
|
|
|
|
if(t.gui.text_button_left_transparent("Cut", "Cut")) {
|
|
|
|
|
selection.selection_to_clipboard();
|
|
|
|
|
selection.delete_all();
|
|
|
|
|
shouldClose = true;
|
|
|
|
|
}
|
|
|
|
|
if(t.gui.text_button_left_transparent("Delete", "Delete")) {
|
|
|
|
|
selection.delete_all();
|
|
|
|
|
shouldClose = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-01 17:33:14 +03:00
|
|
|
});
|
2025-12-01 18:12:22 +03:00
|
|
|
return !shouldClose;
|
2025-12-01 17:33:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DrawingProgram::right_click_popup_gui(Vector2f popupPos) {
|
|
|
|
|
Toolbar& t = world.main.toolbar;
|
|
|
|
|
t.gui.push_id("Drawing Program right click GUI");
|
|
|
|
|
bool toRet = drawTool->right_click_popup_gui(popupPos);
|
|
|
|
|
t.gui.pop_id();
|
|
|
|
|
return toRet;
|
2025-07-06 20:43:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawingProgram::tool_options_gui() {
|
|
|
|
|
Toolbar& t = world.main.toolbar;
|
2025-08-02 20:39:21 +03:00
|
|
|
float minGUIWidth = drawTool->get_type() == DrawingProgramToolType::SCREENSHOT ? 300 : 200;
|
2025-12-02 16:09:30 +03:00
|
|
|
CLAY_AUTO_ID({
|
2025-07-06 20:43:53 +03:00
|
|
|
.layout = {
|
|
|
|
|
.sizing = {.width = CLAY_SIZING_FIT(minGUIWidth), .height = CLAY_SIZING_FIT(0)},
|
|
|
|
|
.padding = CLAY_PADDING_ALL(t.io->theme->padding1),
|
|
|
|
|
.childGap = t.io->theme->childGap1,
|
|
|
|
|
.childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_TOP},
|
|
|
|
|
.layoutDirection = CLAY_TOP_TO_BOTTOM
|
|
|
|
|
},
|
|
|
|
|
.backgroundColor = convert_vec4<Clay_Color>(t.io->theme->backColor1),
|
2025-11-01 20:47:34 +03:00
|
|
|
.cornerRadius = CLAY_CORNER_RADIUS(t.io->theme->windowCorners1)
|
2025-07-06 20:43:53 +03:00
|
|
|
}) {
|
|
|
|
|
t.gui.obstructing_window();
|
2025-08-02 20:10:33 +03:00
|
|
|
drawTool->gui_toolbox();
|
2025-07-06 20:43:53 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-11 16:05:41 +03:00
|
|
|
void DrawingProgram::modify_grid(const NetworkingObjects::NetObjWeakPtr<WorldGrid>& gridToModify) {
|
2025-09-24 16:18:20 +03:00
|
|
|
std::unique_ptr<GridModifyTool> newTool(std::make_unique<GridModifyTool>(*this));
|
2025-12-10 13:06:45 +03:00
|
|
|
newTool->set_grid(gridToModify);
|
2025-09-24 16:18:20 +03:00
|
|
|
switch_to_tool_ptr(std::move(newTool));
|
2025-09-12 09:54:49 +03:00
|
|
|
}
|
|
|
|
|
|
2025-07-06 20:43:53 +03:00
|
|
|
void DrawingProgram::update() {
|
2025-08-26 12:03:09 +03:00
|
|
|
if(world.main.window.lastFrameTime > std::chrono::milliseconds(33)) { // Around 30FPS
|
|
|
|
|
if(!badFrametimeTimePoint)
|
|
|
|
|
badFrametimeTimePoint = std::chrono::steady_clock::now();
|
|
|
|
|
else if(unorderedObjectsExistTimePoint && std::chrono::steady_clock::now() - badFrametimeTimePoint.value() >= std::chrono::seconds(5) && std::chrono::steady_clock::now() - unorderedObjectsExistTimePoint.value() >= std::chrono::seconds(5)) {
|
|
|
|
|
force_rebuild_cache();
|
|
|
|
|
unorderedObjectsExistTimePoint = std::nullopt;
|
|
|
|
|
badFrametimeTimePoint = std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
badFrametimeTimePoint = std::nullopt;
|
|
|
|
|
|
|
|
|
|
if(compCache.get_unsorted_component_list().empty()) {
|
|
|
|
|
unorderedObjectsExistTimePoint = std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
else if(!unorderedObjectsExistTimePoint) {
|
|
|
|
|
unorderedObjectsExistTimePoint = std::chrono::steady_clock::now();
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-28 18:25:46 +03:00
|
|
|
if(world.main.input.pen.isEraser && !temporaryEraser) {
|
|
|
|
|
if(drawTool->get_type() == DrawingProgramToolType::BRUSH)
|
|
|
|
|
switch_to_tool(DrawingProgramToolType::ERASER);
|
2025-07-06 20:43:53 +03:00
|
|
|
temporaryEraser = true;
|
|
|
|
|
}
|
2025-09-28 18:25:46 +03:00
|
|
|
else if(!world.main.input.pen.isEraser && temporaryEraser) {
|
|
|
|
|
if(drawTool->get_type() == DrawingProgramToolType::ERASER)
|
|
|
|
|
switch_to_tool(DrawingProgramToolType::BRUSH);
|
2025-07-06 20:43:53 +03:00
|
|
|
temporaryEraser = false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-28 18:25:46 +03:00
|
|
|
if(world.main.input.key(InputManager::KEY_HOLD_TO_PAN).held && !temporaryPan) {
|
|
|
|
|
toolTypeAfterTempPan = drawTool->get_type();
|
|
|
|
|
switch_to_tool(DrawingProgramToolType::PAN);
|
|
|
|
|
temporaryPan = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!world.main.input.key(InputManager::KEY_HOLD_TO_PAN).held && temporaryPan) {
|
|
|
|
|
switch_to_tool(toolTypeAfterTempPan);
|
|
|
|
|
temporaryPan = false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-06 20:43:53 +03:00
|
|
|
controls.cursorHoveringOverCanvas = !world.main.toolbar.io->hoverObstructed;
|
|
|
|
|
controls.leftClick = controls.cursorHoveringOverCanvas && world.main.input.mouse.leftClicks;
|
|
|
|
|
if(controls.leftClick) {
|
|
|
|
|
controls.leftClickHeld = true;
|
2025-12-01 17:33:14 +03:00
|
|
|
world.main.toolbar.rightClickPopupLocation = std::nullopt;
|
2025-07-06 20:43:53 +03:00
|
|
|
}
|
|
|
|
|
if(controls.leftClickHeld && !world.main.input.mouse.leftDown) {
|
|
|
|
|
controls.leftClick = false;
|
|
|
|
|
controls.leftClickHeld = false;
|
|
|
|
|
controls.leftClickReleased = true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-28 11:59:08 +03:00
|
|
|
controls.middleClick = controls.cursorHoveringOverCanvas && (world.main.input.mouse.middleClicks || world.main.input.pen.buttons[world.main.toolbar.tabletOptions.middleClickButton].pressed);
|
2025-09-28 20:13:54 +03:00
|
|
|
bool middleHeld = world.main.input.mouse.middleDown || world.main.input.pen.buttons[world.main.toolbar.tabletOptions.middleClickButton].held;
|
2025-09-28 11:59:08 +03:00
|
|
|
if(controls.middleClick) {
|
|
|
|
|
controls.middleClickHeld = true;
|
2025-12-01 17:33:14 +03:00
|
|
|
world.main.toolbar.rightClickPopupLocation = std::nullopt;
|
2025-09-28 11:59:08 +03:00
|
|
|
}
|
2025-09-28 20:13:54 +03:00
|
|
|
if(controls.middleClickHeld && !middleHeld) {
|
2025-09-28 11:59:08 +03:00
|
|
|
controls.middleClick = false;
|
|
|
|
|
controls.middleClickHeld = false;
|
|
|
|
|
controls.middleClickReleased = true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-06 20:43:53 +03:00
|
|
|
if(controls.cursorHoveringOverCanvas && (world.main.input.mouse.rightClicks || world.main.input.pen.buttons[world.main.toolbar.tabletOptions.rightClickButton].pressed)) {
|
2025-12-01 17:33:14 +03:00
|
|
|
if(world.main.toolbar.rightClickPopupLocation)
|
|
|
|
|
world.main.toolbar.rightClickPopupLocation = std::nullopt;
|
2025-07-06 20:43:53 +03:00
|
|
|
else
|
2025-12-01 17:33:14 +03:00
|
|
|
world.main.toolbar.rightClickPopupLocation = world.main.input.mouse.pos / world.main.toolbar.final_gui_scale();
|
2025-07-06 20:43:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
controls.previousMouseWorldPos = controls.currentMouseWorldPos;
|
|
|
|
|
controls.currentMouseWorldPos = world.get_mouse_world_pos();
|
|
|
|
|
|
|
|
|
|
if(world.main.input.key(InputManager::KEY_DRAW_TOOL_BRUSH).pressed)
|
2025-08-02 20:39:21 +03:00
|
|
|
switch_to_tool(DrawingProgramToolType::BRUSH);
|
2025-07-06 20:43:53 +03:00
|
|
|
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_ERASER).pressed)
|
2025-08-02 20:39:21 +03:00
|
|
|
switch_to_tool(DrawingProgramToolType::ERASER);
|
2025-07-06 20:43:53 +03:00
|
|
|
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_RECTSELECT).pressed)
|
2025-08-02 20:39:21 +03:00
|
|
|
switch_to_tool(DrawingProgramToolType::RECTSELECT);
|
2025-07-06 20:43:53 +03:00
|
|
|
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_RECTANGLE).pressed)
|
2025-08-02 20:39:21 +03:00
|
|
|
switch_to_tool(DrawingProgramToolType::RECTANGLE);
|
2025-07-06 20:43:53 +03:00
|
|
|
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_ELLIPSE).pressed)
|
2025-08-02 20:39:21 +03:00
|
|
|
switch_to_tool(DrawingProgramToolType::ELLIPSE);
|
2025-07-06 20:43:53 +03:00
|
|
|
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_TEXTBOX).pressed)
|
2025-08-02 20:39:21 +03:00
|
|
|
switch_to_tool(DrawingProgramToolType::TEXTBOX);
|
2025-08-05 12:11:50 +03:00
|
|
|
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_EYEDROPPER).pressed)
|
|
|
|
|
switch_to_tool(DrawingProgramToolType::EYEDROPPER);
|
2025-07-06 20:43:53 +03:00
|
|
|
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_SCREENSHOT).pressed)
|
2025-08-02 20:39:21 +03:00
|
|
|
switch_to_tool(DrawingProgramToolType::SCREENSHOT);
|
2025-12-10 21:46:08 +05:30
|
|
|
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_ZOOM).pressed)
|
|
|
|
|
switch_to_tool(DrawingProgramToolType::ZOOM);
|
|
|
|
|
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_LASSOSELECT).pressed)
|
|
|
|
|
switch_to_tool(DrawingProgramToolType::LASSOSELECT);
|
|
|
|
|
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_PAN).pressed)
|
|
|
|
|
switch_to_tool(DrawingProgramToolType::PAN);
|
2025-08-02 20:10:33 +03:00
|
|
|
|
2025-08-05 19:04:47 +03:00
|
|
|
selection.update();
|
2025-08-02 20:10:33 +03:00
|
|
|
drawTool->tool_update();
|
2025-07-06 20:43:53 +03:00
|
|
|
|
2025-10-11 15:35:17 +03:00
|
|
|
if(toolToSwitchToAfterUpdate) {
|
|
|
|
|
switch_to_tool_ptr(std::move(toolToSwitchToAfterUpdate));
|
|
|
|
|
toolToSwitchToAfterUpdate = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-06 20:43:53 +03:00
|
|
|
if(controls.leftClickReleased)
|
|
|
|
|
controls.leftClickReleased = false;
|
|
|
|
|
|
2025-08-02 20:39:21 +03:00
|
|
|
if(drawTool->get_type() == DrawingProgramToolType::ERASER) {
|
2025-08-02 20:10:33 +03:00
|
|
|
EraserTool* eraserTool = static_cast<EraserTool*>(drawTool.get());
|
2025-12-14 18:57:40 +03:00
|
|
|
compCache.test_rebuild_dont_include_set_dont_include_nodes(components->get_data(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes);
|
2025-08-02 20:10:33 +03:00
|
|
|
}
|
2025-10-03 13:16:38 +03:00
|
|
|
else if(is_selection_allowing_tool(drawTool->get_type()))
|
2025-12-14 18:57:40 +03:00
|
|
|
compCache.test_rebuild_dont_include_set(components->get_data(), selection.get_selected_set());
|
2025-07-26 21:20:14 +03:00
|
|
|
else
|
2025-12-14 18:57:40 +03:00
|
|
|
compCache.test_rebuild(components->get_data());
|
2025-07-26 21:20:14 +03:00
|
|
|
}
|
|
|
|
|
|
2025-10-03 13:16:38 +03:00
|
|
|
bool DrawingProgram::is_actual_selection_tool(DrawingProgramToolType typeToCheck) {
|
2025-09-29 15:15:16 +03:00
|
|
|
return typeToCheck == DrawingProgramToolType::RECTSELECT || typeToCheck == DrawingProgramToolType::LASSOSELECT || typeToCheck == DrawingProgramToolType::EDIT;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 13:16:38 +03:00
|
|
|
bool DrawingProgram::is_selection_allowing_tool(DrawingProgramToolType typeToCheck) {
|
|
|
|
|
return is_actual_selection_tool(typeToCheck) || typeToCheck == DrawingProgramToolType::PAN || typeToCheck == DrawingProgramToolType::ZOOM;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-02 21:14:57 +03:00
|
|
|
void DrawingProgram::switch_to_tool_ptr(std::unique_ptr<DrawingProgramToolBase> newTool) {
|
|
|
|
|
drawTool->switch_tool(newTool->get_type());
|
|
|
|
|
drawTool = std::move(newTool);
|
2025-12-01 17:33:14 +03:00
|
|
|
world.main.toolbar.rightClickPopupLocation = std::nullopt;
|
2025-08-02 21:14:57 +03:00
|
|
|
}
|
|
|
|
|
|
2025-09-29 15:15:16 +03:00
|
|
|
void DrawingProgram::switch_to_tool(DrawingProgramToolType newToolType, bool force) {
|
|
|
|
|
if(newToolType != drawTool->get_type() || force) {
|
2025-08-05 19:04:47 +03:00
|
|
|
drawTool->switch_tool(newToolType);
|
2025-08-02 20:39:21 +03:00
|
|
|
drawTool = DrawingProgramToolBase::allocate_tool_type(*this, newToolType);
|
2025-12-01 17:33:14 +03:00
|
|
|
world.main.toolbar.rightClickPopupLocation = std::nullopt;
|
2025-08-02 20:39:21 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-27 16:53:22 +03:00
|
|
|
bool DrawingProgram::prevent_undo_or_redo() {
|
2025-08-02 20:10:33 +03:00
|
|
|
return drawTool->prevent_undo_or_redo();
|
2025-07-27 16:53:22 +03:00
|
|
|
}
|
|
|
|
|
|
2025-08-02 12:31:16 +03:00
|
|
|
SkPaint DrawingProgram::select_tool_line_paint() {
|
|
|
|
|
SkScalar intervals[] = {10, 5};
|
|
|
|
|
float timeSinceEpoch = std::chrono::duration_cast<std::chrono::duration<float>>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
2025-10-12 18:24:59 +03:00
|
|
|
sk_sp<SkPathEffect> lassoLineDashEffect = SkDashPathEffect::Make({intervals, 2}, -std::fmod(timeSinceEpoch * 50, 15));
|
2025-08-02 12:31:16 +03:00
|
|
|
|
|
|
|
|
SkPaint selectLinePaint;
|
|
|
|
|
selectLinePaint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
|
selectLinePaint.setStrokeWidth(3);
|
|
|
|
|
selectLinePaint.setColor4f(world.canvasTheme.toolFrontColor);
|
|
|
|
|
selectLinePaint.setPathEffect(lassoLineDashEffect);
|
|
|
|
|
|
|
|
|
|
return selectLinePaint;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 21:20:14 +03:00
|
|
|
void DrawingProgram::force_rebuild_cache() {
|
2025-08-02 20:39:21 +03:00
|
|
|
if(drawTool->get_type() == DrawingProgramToolType::ERASER) {
|
2025-08-02 20:10:33 +03:00
|
|
|
EraserTool* eraserTool = static_cast<EraserTool*>(drawTool.get());
|
2025-12-14 18:57:40 +03:00
|
|
|
compCache.test_rebuild_dont_include_set_dont_include_nodes(components->get_data(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes, true);
|
2025-08-02 20:10:33 +03:00
|
|
|
}
|
2025-10-03 13:16:38 +03:00
|
|
|
else if(is_selection_allowing_tool(drawTool->get_type()))
|
2025-12-14 18:57:40 +03:00
|
|
|
compCache.test_rebuild_dont_include_set(components->get_data(), selection.get_selected_set(), true);
|
2025-07-26 21:20:14 +03:00
|
|
|
else
|
2025-12-14 18:57:40 +03:00
|
|
|
compCache.test_rebuild(components->get_data(), true);
|
2025-11-16 16:39:12 +03:00
|
|
|
}
|
|
|
|
|
|
2025-12-14 18:57:40 +03:00
|
|
|
void DrawingProgram::invalidate_cache_at_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
|
2025-07-31 19:28:18 +03:00
|
|
|
if(selection.is_selected(objToCheck))
|
2025-12-14 18:57:40 +03:00
|
|
|
selection.invalidate_cache_at_aabb_before_pos(objToCheck->obj->get_world_bounds(), objToCheck->pos);
|
2025-07-31 19:28:18 +03:00
|
|
|
else
|
2025-12-14 18:57:40 +03:00
|
|
|
compCache.invalidate_cache_at_aabb_before_pos(objToCheck->obj->get_world_bounds(), objToCheck->pos);
|
2025-07-31 19:28:18 +03:00
|
|
|
}
|
|
|
|
|
|
2025-12-14 18:57:40 +03:00
|
|
|
void DrawingProgram::preupdate_component(const CanvasComponentContainer::ObjInfoSharedPtr& objToCheck) {
|
2025-07-31 19:28:18 +03:00
|
|
|
if(selection.is_selected(objToCheck))
|
|
|
|
|
selection.preupdate_component(objToCheck);
|
|
|
|
|
else
|
|
|
|
|
compCache.preupdate_component(objToCheck);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-23 15:02:02 +03:00
|
|
|
float DrawingProgram::drag_point_radius() {
|
|
|
|
|
return 8.0f * world.main.toolbar.final_gui_scale();
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-06 20:43:53 +03:00
|
|
|
void DrawingProgram::draw_drag_circle(SkCanvas* canvas, const Vector2f& sPos, const SkColor4f& c, const DrawData& drawData, float radiusMultiplier) {
|
2025-11-23 15:02:02 +03:00
|
|
|
float constantRadius = drag_point_radius() * radiusMultiplier;
|
2025-07-06 20:43:53 +03:00
|
|
|
float constantThickness = constantRadius * 0.2f;
|
|
|
|
|
canvas->drawCircle(sPos.x(), sPos.y(), constantRadius, SkPaint(c));
|
|
|
|
|
SkPaint paintOutline(SkColor4f{0.95f, 0.95f, 0.95f, 1.0f});
|
|
|
|
|
paintOutline.setStroke(true);
|
|
|
|
|
paintOutline.setStrokeWidth(constantThickness);
|
|
|
|
|
canvas->drawCircle(sPos.x(), sPos.y(), constantRadius, paintOutline);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 18:57:40 +03:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-16 16:39:12 +03:00
|
|
|
void DrawingProgram::save_file(cereal::PortableBinaryOutputArchive& a) const {
|
2025-12-14 18:57:40 +03:00
|
|
|
//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);
|
|
|
|
|
//}
|
2025-07-06 20:43:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) {
|
|
|
|
|
if(drawData.dontUseDrawProgCache) {
|
2025-12-14 18:57:40 +03:00
|
|
|
for(auto& c : components->get_data())
|
2025-07-06 20:43:53 +03:00
|
|
|
c->obj->draw(canvas, drawData);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2025-08-03 16:52:11 +03:00
|
|
|
canvas->saveLayer(nullptr, nullptr);
|
|
|
|
|
canvas->clear(SkColor4f{0.0f, 0.0f, 0.0f, 0.0f});
|
2025-07-24 16:35:48 +03:00
|
|
|
compCache.refresh_all_draw_cache(drawData);
|
2025-08-26 12:03:09 +03:00
|
|
|
compCache.draw_components_to_canvas(canvas, drawData, {});
|
2025-08-03 16:52:11 +03:00
|
|
|
canvas->saveLayer(nullptr, nullptr);
|
|
|
|
|
selection.draw_components(canvas, drawData);
|
|
|
|
|
canvas->restore();
|
|
|
|
|
canvas->restore();
|
|
|
|
|
selection.draw_gui(canvas, drawData);
|
2025-07-06 20:43:53 +03:00
|
|
|
}
|
|
|
|
|
|
2025-08-02 20:10:33 +03:00
|
|
|
drawTool->draw(canvas, drawData);
|
2025-07-06 20:43:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector4f* DrawingProgram::get_foreground_color_ptr() {
|
|
|
|
|
return &controls.foregroundColor;
|
|
|
|
|
}
|