diff --git a/CMakeLists.txt b/CMakeLists.txt index a9404f9..f44201e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,7 @@ set(sources "src/main.cpp" "src/DrawingProgram/Tools/EditTools/EllipseDrawEditTool.cpp" "src/DrawingProgram/Tools/EditTools/RectDrawEditTool.cpp" "src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp" + "src/DrawingProgram/Tools/EditTools/BrushEditTool.cpp" "src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp" "src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp" "src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp" diff --git a/src/CanvasComponents/BrushStrokeCanvasComponent.cpp b/src/CanvasComponents/BrushStrokeCanvasComponent.cpp index 7cd05ea..6c09d7c 100644 --- a/src/CanvasComponents/BrushStrokeCanvasComponent.cpp +++ b/src/CanvasComponents/BrushStrokeCanvasComponent.cpp @@ -14,19 +14,19 @@ #define DEFAULT_SMOOTHNESS 5 void BrushStrokeCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const { - a(d->points, d->color, d->hasRoundCaps); + a(*d.points, d.color, d.hasRoundCaps); } void BrushStrokeCanvasComponent::load(cereal::PortableBinaryInputArchive& a) { - a(d->points, d->color, d->hasRoundCaps); + a(*d.points, d.color, d.hasRoundCaps); } void BrushStrokeCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const { - a(d->points, d->color, d->hasRoundCaps); + a(*d.points, d.color, d.hasRoundCaps); } void BrushStrokeCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) { - a(d->points, d->color, d->hasRoundCaps); + a(*d.points, d.color, d.hasRoundCaps); } std::unique_ptr BrushStrokeCanvasComponent::get_data_copy() const { @@ -39,9 +39,17 @@ CanvasComponentType BrushStrokeCanvasComponent::get_type() const { return CanvasComponentType::BRUSHSTROKE; } +void BrushStrokeCanvasComponent::change_stroke_color(const Vector4f& newStrokeColor) { + d.color = newStrokeColor; +} + +std::optional BrushStrokeCanvasComponent::get_stroke_color() const { + return d.color; +} + void BrushStrokeCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const { SkPaint paint; - paint.setColor4f(SkColor4f{d->color.x(), d->color.y(), d->color.z(), d->color.w()}); + paint.setColor4f(SkColor4f{d.color.x(), d.color.y(), d.color.z(), d.color.w()}); unsigned mipmapLevel = compContainer ? compContainer->get_mipmap_level(drawData) : 0; @@ -57,8 +65,8 @@ void BrushStrokeCanvasComponent::set_data_from(const CanvasComponent& other) { } void BrushStrokeCanvasComponent::initialize_draw_data(DrawingProgram& drawP) { - if(!d->points.empty()) { - std::vector points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS); + if(!d.points->empty()) { + std::vector points = smooth_points(0, d.points->size() - 1, DEFAULT_SMOOTHNESS); auto triangleFunc = [&](Vector2f a, Vector2f b, Vector2f c) { return false; }; @@ -94,7 +102,7 @@ std::vector BrushStrokeCanvasComponent::get_wedge_indices(const std::vec } std::vector BrushStrokeCanvasComponent::smooth_points(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) const { - std::vector& points = d->points; + std::vector& points = *d.points; size_t pointsSize = endIndex - beginIndex + 1; if(pointsSize < 2) @@ -138,7 +146,7 @@ std::vector BrushStrokeCanvasComponent::smooth_ } std::vector BrushStrokeCanvasComponent::smooth_points_avg(size_t beginIndex, size_t endIndex, unsigned numOfDivisions) const { - std::vector& points = d->points; + std::vector& points = *d.points; size_t pointsSize = endIndex - beginIndex + 1; if(pointsSize < 2) @@ -180,11 +188,11 @@ std::vector BrushStrokeCanvasComponent::every_n void BrushStrokeCanvasComponent::create_triangles(const std::function& passTriangleFunc, const std::vector& smoothedPoints, size_t skipVertexCount, std::shared_ptr bPathBuilder) const { const int ARC_SMOOTHNESS = 10; const int CIRCLE_SMOOTHNESS = 20; - const std::vector& pointsN = d->points; + const std::vector& pointsN = *d.points; std::vector topPoints; std::vector bottomPoints; - if(pointsN.size() == 1 && d->hasRoundCaps) { + if(pointsN.size() == 1 && d.hasRoundCaps) { std::vector circlePoints = gen_circle_points(pointsN.front().pos, pointsN.front().width / 2.0f, CIRCLE_SMOOTHNESS); for(size_t i = 0; i < circlePoints.size(); i++) { if(passTriangleFunc(pointsN.front().pos, circlePoints[i], circlePoints[(i + 1) % circlePoints.size()])) return; @@ -218,7 +226,7 @@ void BrushStrokeCanvasComponent::create_triangles(const std::function(vertArray[1])); newIndex = 2; - if(d->hasRoundCaps && pointsBegin == 0) { + if(d.hasRoundCaps && pointsBegin == 0) { Vector2f arcDirStart = (convert_vec2(vertArray[0]) - points[0].pos).normalized(); Vector2f arcDirEnd = (convert_vec2(vertArray[1]) - points[0].pos).normalized(); Vector2f arcDirCenter = (points[0].pos - points[1].pos).normalized(); @@ -326,7 +334,7 @@ void BrushStrokeCanvasComponent::create_triangles(const std::function(arcPoints.back())); } - else if(d->hasRoundCaps) { // It's the last point, cap it off + else if(d.hasRoundCaps) { // It's the last point, cap it off Vector2f arcDirStart = (convert_vec2(vertArray[(newIndex + 1) % 3]) - points.back().pos).normalized(); Vector2f arcDirEnd = (convert_vec2(vertArray[(newIndex + 2) % 3]) - points.back().pos).normalized(); Vector2f arcDirCenter = -rectDir; @@ -349,10 +357,10 @@ void BrushStrokeCanvasComponent::create_triangles(const std::functionpoints.empty()) + if(d.points->empty()) return; ColliderCollection strokeObjects; - std::vector points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS); + std::vector points = smooth_points(0, d.points->size() - 1, DEFAULT_SMOOTHNESS); create_triangles([&](Vector2f a, Vector2f b, Vector2f c) { strokeObjects.triangle.emplace_back(a, b, c); return false; @@ -392,7 +400,7 @@ bool BrushStrokeCanvasComponent::collides_within_coords(const SCollision::Collid return false; bool toRet = false; - std::vector points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS); + std::vector points = smooth_points(0, d.points->size() - 1, DEFAULT_SMOOTHNESS); create_triangles([&](Vector2f a, Vector2f b, Vector2f c) { toRet = SCollision::collide(checkAgainst, SCollision::Triangle{a, b, c}); return toRet; diff --git a/src/CanvasComponents/BrushStrokeCanvasComponent.hpp b/src/CanvasComponents/BrushStrokeCanvasComponent.hpp index 8c9b4dd..9ca3a26 100644 --- a/src/CanvasComponents/BrushStrokeCanvasComponent.hpp +++ b/src/CanvasComponents/BrushStrokeCanvasComponent.hpp @@ -23,13 +23,14 @@ class BrushStrokeCanvasComponent : public CanvasComponent { virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override; virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override; virtual std::unique_ptr get_data_copy() const override; + virtual void change_stroke_color(const Vector4f& newStrokeColor) override; + virtual std::optional get_stroke_color() const override; struct Data { - std::vector points; + std::shared_ptr> points = std::make_shared>(); // It's a pointer here since brush strokes cant be edited Vector4f color; bool hasRoundCaps; - }; - std::shared_ptr d = std::make_shared(); // It's a pointer here since brush strokes cant be edited + } d; virtual void set_data_from(const CanvasComponent& other) override; private: diff --git a/src/CanvasComponents/CanvasComponent.cpp b/src/CanvasComponents/CanvasComponent.cpp index 30903ff..c34fd62 100644 --- a/src/CanvasComponents/CanvasComponent.cpp +++ b/src/CanvasComponents/CanvasComponent.cpp @@ -30,4 +30,11 @@ CanvasComponent* CanvasComponent::allocate_comp(CanvasComponentType type) { return nullptr; } +void CanvasComponent::change_stroke_color(const Vector4f& newStrokeColor) { +} + +std::optional CanvasComponent::get_stroke_color() const { + return std::nullopt; +} + CanvasComponent::~CanvasComponent() {} diff --git a/src/CanvasComponents/CanvasComponent.hpp b/src/CanvasComponents/CanvasComponent.hpp index 4018a7c..08a2942 100644 --- a/src/CanvasComponents/CanvasComponent.hpp +++ b/src/CanvasComponents/CanvasComponent.hpp @@ -21,6 +21,8 @@ class CanvasComponent { virtual void update(DrawingProgram& drawP); virtual void get_used_resources(std::unordered_set& resourceSet) const; virtual void remap_resource_ids(const std::unordered_map& resourceOldToNewMap); + virtual void change_stroke_color(const Vector4f& newStrokeColor); + virtual std::optional get_stroke_color() const; virtual void set_data_from(const CanvasComponent& other) = 0; virtual std::unique_ptr get_data_copy() const = 0; diff --git a/src/CanvasComponents/EllipseCanvasComponent.cpp b/src/CanvasComponents/EllipseCanvasComponent.cpp index 585d040..409d731 100644 --- a/src/CanvasComponents/EllipseCanvasComponent.cpp +++ b/src/CanvasComponents/EllipseCanvasComponent.cpp @@ -25,6 +25,14 @@ void EllipseCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, Ve a(d.strokeColor, d.fillColor, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode); } +void EllipseCanvasComponent::change_stroke_color(const Vector4f& newStrokeColor) { + d.strokeColor = newStrokeColor; +} + +std::optional EllipseCanvasComponent::get_stroke_color() const { + return d.strokeColor; +} + void EllipseCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const { SkPaint p; if(d.fillStrokeMode == 0 || d.fillStrokeMode == 2) { diff --git a/src/CanvasComponents/EllipseCanvasComponent.hpp b/src/CanvasComponents/EllipseCanvasComponent.hpp index a031b62..a9dca0c 100644 --- a/src/CanvasComponents/EllipseCanvasComponent.hpp +++ b/src/CanvasComponents/EllipseCanvasComponent.hpp @@ -10,6 +10,8 @@ class EllipseCanvasComponent : public CanvasComponent { virtual void load(cereal::PortableBinaryInputArchive& a) override; virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override; virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override; + virtual void change_stroke_color(const Vector4f& newStrokeColor) override; + virtual std::optional get_stroke_color() const override; virtual std::unique_ptr get_data_copy() const override; virtual void set_data_from(const CanvasComponent& other) override; diff --git a/src/CanvasComponents/RectangleCanvasComponent.cpp b/src/CanvasComponents/RectangleCanvasComponent.cpp index 95c5404..a58449a 100644 --- a/src/CanvasComponents/RectangleCanvasComponent.cpp +++ b/src/CanvasComponents/RectangleCanvasComponent.cpp @@ -27,6 +27,14 @@ void RectangleCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, a(d.strokeColor, d.fillColor, d.cornerRadius, d.strokeWidth, d.p1, d.p2, d.fillStrokeMode); } +void RectangleCanvasComponent::change_stroke_color(const Vector4f& newStrokeColor) { + d.strokeColor = newStrokeColor; +} + +std::optional RectangleCanvasComponent::get_stroke_color() const { + return d.strokeColor; +} + std::unique_ptr RectangleCanvasComponent::get_data_copy() const { auto toRet = std::make_unique(); toRet->d = d; diff --git a/src/CanvasComponents/RectangleCanvasComponent.hpp b/src/CanvasComponents/RectangleCanvasComponent.hpp index 57aabc6..be8ccdf 100644 --- a/src/CanvasComponents/RectangleCanvasComponent.hpp +++ b/src/CanvasComponents/RectangleCanvasComponent.hpp @@ -10,6 +10,8 @@ class RectangleCanvasComponent : public CanvasComponent { virtual void load(cereal::PortableBinaryInputArchive& a) override; virtual void save_file(cereal::PortableBinaryOutputArchive& a) const override; virtual void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) override; + virtual void change_stroke_color(const Vector4f& newStrokeColor) override; + virtual std::optional get_stroke_color() const override; virtual CanvasComponentType get_type() const override; std::unique_ptr get_data_copy() const override; virtual void set_data_from(const CanvasComponent& other) override; diff --git a/src/DrawingProgram/DrawingProgramSelection.cpp b/src/DrawingProgram/DrawingProgramSelection.cpp index 5aece27..5af2667 100644 --- a/src/DrawingProgram/DrawingProgramSelection.cpp +++ b/src/DrawingProgram/DrawingProgramSelection.cpp @@ -27,7 +27,106 @@ DrawingProgramSelection::DrawingProgramSelection(DrawingProgram& initDrawP): drawP(initDrawP) {} +void DrawingProgramSelection::selection_gui() { + auto& t = drawP.world.main.toolbar; + t.gui.push_id("general selection gui"); + t.gui.text_label("Select from:"); + if(t.gui.radio_button_field("layer edited", "Layer being edited", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED)) + drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED; + if(t.gui.radio_button_field("all visible", "All visible layers", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS)) + drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS; + if(is_something_selected()) { + t.gui.left_to_right_line_layout([&]() { + if(t.gui.color_button_big("Stroke Color Button", &strokeColorChangeData.newColor, &strokeColorChangeData.newColor == t.colorRight)) { + if(strokeColorChangeData.newColor.w() == 0.0f) + strokeColorChangeData.newColor.w() = 1.0f; + t.color_selector_right(&strokeColorChangeData.newColor == t.colorRight ? nullptr : &strokeColorChangeData.newColor); + } + t.gui.text_label("Stroke Color"); + }); + update_selection_stroke_color(); + } + t.gui.pop_id(); +} + +void DrawingProgramSelection::update_selection_stroke_color() { + if(strokeColorChangeData.oldColor != strokeColorChangeData.newColor) { + for(auto& c : selectedSet) { + if(c->obj->get_comp().get_stroke_color() != std::nullopt) { + strokeColorChangeData.oldColorData.emplace(c->obj.get_net_id(), c->obj->get_comp().get_stroke_color().value()); + c->obj->get_comp().change_stroke_color(strokeColorChangeData.newColor); + c->obj->commit_update_dont_invalidate_cache(drawP); // Object isn't in the cache since it's selected + c->obj->send_comp_update(drawP, false); + } + } + strokeColorChangeData.oldColor = strokeColorChangeData.newColor; + } +} + +void DrawingProgramSelection::check_add_stroke_color_change_undo() { + if(!strokeColorChangeData.oldColorData.empty()) { + class EditCanvasComponentsStrokeColorWorldUndoAction : public WorldUndoAction { + public: + EditCanvasComponentsStrokeColorWorldUndoAction(std::vector initUndoIDs, std::vector initOldColors): + undoIDs(std::move(initUndoIDs)), + colors(std::move(initOldColors)) + {} + std::string get_name() const override { + return "Edit Stroke Colors"; + } + bool undo(WorldUndoManager& undoMan) override { + return undo_redo(undoMan); + } + bool redo(WorldUndoManager& undoMan) override { + return undo_redo(undoMan); + } + bool undo_redo(WorldUndoManager& undoMan) { + std::vector netIDs; + + if(!undoMan.fill_netid_list_from_undoid_list(netIDs, undoIDs)) + return false; + + undoMan.world.netObjMan.send_multi_update_messsage([&]() { + for(size_t i = 0; i < netIDs.size(); i++) { + auto objPtr = undoMan.world.netObjMan.get_obj_temporary_ref_from_id(netIDs[i]); + if(objPtr) { + Vector4f oldColor = objPtr->get_comp().get_stroke_color().value(); + objPtr->get_comp().change_stroke_color(colors[i]); + colors[i] = oldColor; + objPtr->commit_update(undoMan.world.drawProg); + objPtr->send_comp_update(undoMan.world.drawProg, true); + } + } + }, NetworkingObjects::NetObjManager::SendUpdateType::SEND_TO_ALL, nullptr); + + return true; + } + ~EditCanvasComponentsStrokeColorWorldUndoAction() {} + + std::vector undoIDs; + std::vector colors; + }; + + std::vector undoIDs; + std::vector oldColors; + + for(auto& [netID, strokeColor] : strokeColorChangeData.oldColorData) { + undoIDs.emplace_back(drawP.world.undo.get_undoid_from_netid(netID)); + oldColors.emplace_back(strokeColor); + } + + drawP.world.undo.push(std::make_unique(std::move(undoIDs), std::move(oldColors))); + strokeColorChangeData.oldColorData.clear(); + + auto& t = drawP.world.main.toolbar; + if(&strokeColorChangeData.newColor == t.colorRight) + t.colorRight = nullptr; // Just making sure that the color selector doesn't leak + } +} + void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly) { + check_add_stroke_color_change_undo(); + std::vector selectedComponents; if(frontObjectOnly) { CanvasComponentContainer::ObjInfo* a = drawP.drawCache.get_front_object_colliding_with_in_editing_layer(cC); @@ -44,6 +143,8 @@ void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCo } void DrawingProgramSelection::remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly) { + check_add_stroke_color_change_undo(); + if(frontObjectOnly) { CanvasComponentContainer::ObjInfo* p = get_front_object_colliding_with_in_editing_layer(cC); if(p) { @@ -139,11 +240,15 @@ bool DrawingProgramSelection::is_selected(CanvasComponentContainer::ObjInfo* obj } void DrawingProgramSelection::reset_all() { + check_add_stroke_color_change_undo(); selectedSet.clear(); reset_transform_data(); + strokeColorChangeData = StrokeColorChangeData(); } void DrawingProgramSelection::reset_transform_data() { + check_add_stroke_color_change_undo(); + selectionTransformCoords = CoordSpaceHelperTransform(); transformOpHappening = TransformOperation::NONE; translateData = TranslationData(); @@ -168,6 +273,8 @@ bool DrawingProgramSelection::mouse_collided_with_rotate_handle_point() { } void DrawingProgramSelection::commit_transform_selection() { + check_add_stroke_color_change_undo(); + if(!is_empty_transform()) { std::vector undoIDList; std::vector> transformDataList; @@ -267,17 +374,22 @@ void DrawingProgramSelection::update() { scaleData.currentPos = scaleData.startPos = selectionTransformCoords.from_space_world(initialSelectionAABB.max); scaleData.centerPos = selectionTransformCoords.from_space_world(initialSelectionAABB.center()); transformOpHappening = TransformOperation::SCALE; + check_add_stroke_color_change_undo(); } - else if(mouse_collided_with_rotate_center_handle_point()) + else if(mouse_collided_with_rotate_center_handle_point()) { transformOpHappening = TransformOperation::ROTATE_RELOCATE_CENTER; + check_add_stroke_color_change_undo(); + } else if(mouse_collided_with_rotate_handle_point()) { rotateData.rotationAngle = 0.0; transformOpHappening = TransformOperation::ROTATE; + check_add_stroke_color_change_undo(); } else if(mouse_collided_with_selection_aabb()) { translateData.startPos = drawP.world.get_mouse_world_pos(); transformOpHappening = TransformOperation::TRANSLATE; translateData.translateWithKeys = false; + check_add_stroke_color_change_undo(); } } else { @@ -297,6 +409,7 @@ void DrawingProgramSelection::update() { selectionTransformCoords = CoordSpaceHelperTransform(translateData.startPos); transformOpHappening = TransformOperation::TRANSLATE; translateData.keyTranslateLastMoveTime = std::chrono::steady_clock::now(); + check_add_stroke_color_change_undo(); } } break; @@ -410,6 +523,8 @@ void DrawingProgramSelection::update() { } void DrawingProgramSelection::deselect_all() { + check_add_stroke_color_change_undo(); + if(is_something_selected()) { commit_transform_selection(); for(auto& obj : selectedSet) @@ -419,18 +534,24 @@ void DrawingProgramSelection::deselect_all() { } void DrawingProgramSelection::push_selection_to_front() { + check_add_stroke_color_change_undo(); + auto selectedVec = selectedSet; deselect_all(); drawP.layerMan.push_components_to(selectedVec, false); } void DrawingProgramSelection::push_selection_to_back() { + check_add_stroke_color_change_undo(); + auto selectedVec = selectedSet; deselect_all(); drawP.layerMan.push_components_to(selectedVec, true); } void DrawingProgramSelection::delete_all() { + check_add_stroke_color_change_undo(); + if(is_something_selected()) { auto selectedSetTemp = selectedSet; reset_all(); // Clear set before erasing, since calling erase_component_set will run a check to see if each object is selected and erase them one by one, which is slower @@ -439,6 +560,8 @@ void DrawingProgramSelection::delete_all() { } void DrawingProgramSelection::erase_component(CanvasComponentContainer::ObjInfo* objToCheck) { + check_add_stroke_color_change_undo(); + std::erase(selectedSet, objToCheck); if(selectedSet.empty()) reset_all(); @@ -455,6 +578,8 @@ CanvasComponentContainer::ObjInfo* DrawingProgramSelection::get_front_object_col } void DrawingProgramSelection::selection_to_clipboard() { + check_add_stroke_color_change_undo(); + auto& clipboard = drawP.world.main.clipboard; std::unordered_set resourceSet; for(auto& c : selectedSet) @@ -468,6 +593,8 @@ void DrawingProgramSelection::selection_to_clipboard() { } void DrawingProgramSelection::paste_clipboard(Vector2f pasteScreenPos) { + check_add_stroke_color_change_undo(); + if(drawP.layerMan.is_a_layer_being_edited()) { auto& clipboard = drawP.world.main.clipboard; @@ -511,6 +638,8 @@ void DrawingProgramSelection::paste_clipboard(Vector2f pasteScreenPos) { } void DrawingProgramSelection::paste_image(Vector2f pasteScreenPos) { + check_add_stroke_color_change_undo(); + if(drawP.layerMan.is_a_layer_being_edited()) { #ifdef __EMSCRIPTEN__ struct PasteData { diff --git a/src/DrawingProgram/DrawingProgramSelection.hpp b/src/DrawingProgram/DrawingProgramSelection.hpp index f87a1ef..cccb3ad 100644 --- a/src/DrawingProgram/DrawingProgramSelection.hpp +++ b/src/DrawingProgram/DrawingProgramSelection.hpp @@ -8,6 +8,7 @@ class DrawingProgram; class DrawingProgramSelection { public: DrawingProgramSelection(DrawingProgram& initDrawP); + void selection_gui(); void add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly); void remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection& cC, DrawingProgramLayerManager::LayerSelector layerSelector, bool frontObjectOnly); void erase_component(CanvasComponentContainer::ObjInfo* objToCheck); @@ -38,6 +39,15 @@ class DrawingProgramSelection { void rebuild_cam_space(); Vector2f get_rotation_point_pos_from_angle(double angle); + void update_selection_stroke_color(); + + void check_add_stroke_color_change_undo(); + struct StrokeColorChangeData { + Vector4f newColor = {1.0f, 1.0f, 1.0f, 0.0f}; + Vector4f oldColor = {1.0f, 1.0f, 1.0f, 0.0f}; + std::unordered_map oldColorData; + } strokeColorChangeData; + void set_to_selection(const std::vector& newSelection); void add_to_selection(const std::vector& newSelection); void sort_selection(); diff --git a/src/DrawingProgram/Tools/BrushTool.cpp b/src/DrawingProgram/Tools/BrushTool.cpp index 090e76e..5224342 100644 --- a/src/DrawingProgram/Tools/BrushTool.cpp +++ b/src/DrawingProgram/Tools/BrushTool.cpp @@ -32,7 +32,7 @@ void BrushTool::erase_component(CanvasComponentContainer::ObjInfo* erasedComp) { } bool BrushTool::extensive_point_checking(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint) { - auto& points = brushStroke.d->points; + 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) @@ -43,7 +43,7 @@ bool BrushTool::extensive_point_checking(const BrushStrokeCanvasComponent& brush } bool BrushTool::extensive_point_checking_back(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint) { - auto& points = brushStroke.d->points; + 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) @@ -98,9 +98,9 @@ void BrushTool::tool_update() { p.pos = drawP.world.main.input.mouse.pos; p.width = toolConfig.get_relative_width(toolConfig.brush.relativeWidth) * penWidth; prevPointUnaltered = p.pos; - newBrushStroke.d->points.emplace_back(p); - newBrushStroke.d->color = toolConfig.globalConf.foregroundColor; - newBrushStroke.d->hasRoundCaps = toolConfig.brush.hasRoundCaps; + newBrushStroke.d.points->emplace_back(p); + newBrushStroke.d.color = toolConfig.globalConf.foregroundColor; + newBrushStroke.d.hasRoundCaps = toolConfig.brush.hasRoundCaps; newBrushStrokeContainer->coords = drawP.world.drawData.cam.c; objInfoBeingEdited = drawP.layerMan.add_component_to_layer_being_edited(newBrushStrokeContainer); addedTemporaryPoint = false; @@ -109,6 +109,7 @@ void BrushTool::tool_update() { else { NetworkingObjects::NetObjOwnerPtr& containerPtr = objInfoBeingEdited->obj; BrushStrokeCanvasComponent& brushStroke = static_cast(containerPtr->get_comp()); + auto& brushPoints = *brushStroke.d.points; BrushStrokeCanvasComponentPoint p; p.pos = containerPtr->coords.get_mouse_pos(drawP.world); @@ -116,28 +117,28 @@ void BrushTool::tool_update() { if(!addedTemporaryPoint) { if(extensive_point_checking(brushStroke, p.pos)) { - brushStroke.d->points.emplace_back(p); + brushPoints.emplace_back(p); addedTemporaryPoint = true; } else - brushStroke.d->points.back().width = std::max(brushStroke.d->points.back().width, p.width); + brushPoints.back().width = std::max(brushPoints.back().width, p.width); } if(addedTemporaryPoint) { - const BrushStrokeCanvasComponentPoint& prevP = brushStroke.d->points[brushStroke.d->points.size() - 2]; + const BrushStrokeCanvasComponentPoint& prevP = brushPoints[brushPoints.size() - 2]; float distToPrev = (p.pos - prevP.pos).norm(); 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); + brushPoints.back().pos = p.pos; + brushPoints.back().width = std::max(brushPoints.back().width, p.width); + brushPoints[brushPoints.size() - 2].width = std::max(brushPoints[brushPoints.size() - 2].width, p.width); } if((!drawingMinimumRelativeToSize && distToPrev >= 10.0) || (drawingMinimumRelativeToSize && distToPrev >= toolConfig.get_relative_width(toolConfig.brush.relativeWidth) * BrushStrokeCanvasComponent::DRAW_MINIMUM_LIMIT)) { - brushStroke.d->points.back() = p; + brushPoints.back() = p; addedTemporaryPoint = false; 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; + if(brushPoints.size() != 2) // Don't interpolate the first point + brushPoints[brushPoints.size() - 2].pos = (prevPointUnaltered + p.pos) * 0.5; prevPointUnaltered = p.pos; } } diff --git a/src/DrawingProgram/Tools/EditTool.cpp b/src/DrawingProgram/Tools/EditTool.cpp index c2ad763..0381f7b 100644 --- a/src/DrawingProgram/Tools/EditTool.cpp +++ b/src/DrawingProgram/Tools/EditTool.cpp @@ -13,6 +13,7 @@ #include "EditTools/RectDrawEditTool.hpp" #include "EditTools/ImageEditTool.hpp" #include "EditTools/EllipseDrawEditTool.hpp" +#include "EditTools/BrushEditTool.hpp" EditTool::EditTool(DrawingProgram& initDrawP): DrawingProgramToolBase(initDrawP) @@ -124,6 +125,10 @@ void EditTool::edit_start(CanvasComponentContainer::ObjInfo* comp, bool initUndo compEditTool = std::make_unique(drawP); break; } + case CanvasComponentType::BRUSHSTROKE: { + compEditTool = std::make_unique(drawP); + break; + } default: { isEditing = false; break; @@ -138,7 +143,7 @@ void EditTool::edit_start(CanvasComponentContainer::ObjInfo* comp, bool initUndo } bool EditTool::is_editable(CanvasComponentContainer::ObjInfo* comp) { - return comp->obj->get_comp().get_type() != CanvasComponentType::BRUSHSTROKE; + return true; // Brush strokes used to not be editable, but now they are so this always returns true } void EditTool::tool_update() { diff --git a/src/DrawingProgram/Tools/EditTools/BrushEditTool.cpp b/src/DrawingProgram/Tools/EditTools/BrushEditTool.cpp new file mode 100644 index 0000000..6376c23 --- /dev/null +++ b/src/DrawingProgram/Tools/EditTools/BrushEditTool.cpp @@ -0,0 +1,38 @@ +#include "BrushEditTool.hpp" +#include "../../DrawingProgram.hpp" +#include "../../../World.hpp" +#include "../../../MainProgram.hpp" +#include "../EditTool.hpp" + +BrushEditTool::BrushEditTool(DrawingProgram& initDrawP): + DrawingProgramEditToolBase(initDrawP) +{} + +bool BrushEditTool::edit_gui(CanvasComponentContainer::ObjInfo* comp) { + auto& a = static_cast(comp->obj->get_comp()); + Toolbar& t = drawP.world.main.toolbar; + t.gui.push_id("edit tool brush"); + t.gui.text_label_centered("Edit Brush Stroke"); + t.gui.left_to_right_line_layout([&]() { + if(t.gui.color_button_big("Outline Color", &a.d.color, &a.d.color == t.colorRight)) + t.color_selector_right(&a.d.color == t.colorRight ? nullptr : &a.d.color); + t.gui.text_label("Outline Color"); + }); + t.gui.pop_id(); + + bool editHappened = a.d.color != oldColor; + oldColor = a.d.color; + return editHappened; +} + +void BrushEditTool::edit_start(EditTool& editTool, CanvasComponentContainer::ObjInfo* comp, std::any& prevData) { + auto& a = static_cast(comp->obj->get_comp()); + oldColor = a.d.color; +} + +void BrushEditTool::commit_edit_updates(CanvasComponentContainer::ObjInfo* comp, std::any& prevData) { +} + +bool BrushEditTool::edit_update(CanvasComponentContainer::ObjInfo* comp) { + return true; +} diff --git a/src/DrawingProgram/Tools/EditTools/BrushEditTool.hpp b/src/DrawingProgram/Tools/EditTools/BrushEditTool.hpp new file mode 100644 index 0000000..50e0a69 --- /dev/null +++ b/src/DrawingProgram/Tools/EditTools/BrushEditTool.hpp @@ -0,0 +1,18 @@ +#pragma once +#include "DrawingProgramEditToolBase.hpp" +#include "../../../CanvasComponents/BrushStrokeCanvasComponent.hpp" +#include +#include + +class DrawingProgram; + +class BrushEditTool : public DrawingProgramEditToolBase { + public: + BrushEditTool(DrawingProgram& initDrawP); + virtual void edit_start(EditTool& editTool, CanvasComponentContainer::ObjInfo* comp, std::any& prevData) override; + virtual void commit_edit_updates(CanvasComponentContainer::ObjInfo* comp, std::any& prevData) override; + virtual bool edit_update(CanvasComponentContainer::ObjInfo* comp) override; + virtual bool edit_gui(CanvasComponentContainer::ObjInfo* comp) override; + private: + Vector4f oldColor; +}; diff --git a/src/DrawingProgram/Tools/LassoSelectTool.cpp b/src/DrawingProgram/Tools/LassoSelectTool.cpp index 0158237..548da8c 100644 --- a/src/DrawingProgram/Tools/LassoSelectTool.cpp +++ b/src/DrawingProgram/Tools/LassoSelectTool.cpp @@ -42,11 +42,7 @@ void LassoSelectTool::gui_toolbox() { auto& t = drawP.world.main.toolbar; t.gui.push_id("lasso select tool"); t.gui.text_label_centered("Lasso Select"); - t.gui.text_label("Select from:"); - if(t.gui.radio_button_field("layer edited", "Layer being edited", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED)) - drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED; - if(t.gui.radio_button_field("all visible", "All visible layers", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS)) - drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS; + drawP.selection.selection_gui(); t.gui.pop_id(); } diff --git a/src/DrawingProgram/Tools/LineDrawTool.cpp b/src/DrawingProgram/Tools/LineDrawTool.cpp index 2a3ab71..e1f6e11 100644 --- a/src/DrawingProgram/Tools/LineDrawTool.cpp +++ b/src/DrawingProgram/Tools/LineDrawTool.cpp @@ -51,11 +51,11 @@ void LineDrawTool::tool_update() { BrushStrokeCanvasComponentPoint p; p.pos = drawP.world.main.input.mouse.pos; p.width = toolConfig.get_relative_width(toolConfig.lineDraw.relativeWidth); - newBrushStroke.d->points.emplace_back(p); + newBrushStroke.d.points->emplace_back(p); p.pos = ensure_points_have_distance(p.pos, p.pos, 1.0f); - newBrushStroke.d->points.emplace_back(p); - newBrushStroke.d->color = toolConfig.globalConf.foregroundColor; - newBrushStroke.d->hasRoundCaps = toolConfig.lineDraw.hasRoundCaps; + newBrushStroke.d.points->emplace_back(p); + newBrushStroke.d.color = toolConfig.globalConf.foregroundColor; + newBrushStroke.d.hasRoundCaps = toolConfig.lineDraw.hasRoundCaps; newBrushStrokeContainer->coords = drawP.world.drawData.cam.c; objInfoBeingEdited = drawP.layerMan.add_component_to_layer_being_edited(newBrushStrokeContainer); } @@ -66,7 +66,7 @@ void LineDrawTool::tool_update() { if(drawP.controls.leftClickHeld) { BrushStrokeCanvasComponent& brushStroke = static_cast(containerPtr->get_comp()); Vector2f newPos = containerPtr->coords.get_mouse_pos(drawP.world); - Vector2f oldPos = brushStroke.d->points.front().pos; + Vector2f oldPos = brushStroke.d.points->front().pos; if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) { Vector2f diff = (newPos - oldPos); float diffLength = diff.norm(); @@ -76,7 +76,7 @@ void LineDrawTool::tool_update() { angle = (angle * std::numbers::pi) / SNAP_DIVISION_COUNT; newPos = oldPos + diffLength * Vector2f{cos(angle), sin(angle)}; } - brushStroke.d->points.back().pos = ensure_points_have_distance(oldPos, newPos, 1.0f); + brushStroke.d.points->back().pos = ensure_points_have_distance(oldPos, newPos, 1.0f); containerPtr->send_comp_update(drawP, false); containerPtr->commit_update(drawP); } diff --git a/src/DrawingProgram/Tools/RectSelectTool.cpp b/src/DrawingProgram/Tools/RectSelectTool.cpp index 6c6fa2a..5697c29 100644 --- a/src/DrawingProgram/Tools/RectSelectTool.cpp +++ b/src/DrawingProgram/Tools/RectSelectTool.cpp @@ -20,11 +20,7 @@ void RectSelectTool::gui_toolbox() { auto& t = drawP.world.main.toolbar; t.gui.push_id("rectangle select tool"); t.gui.text_label_centered("Rectangle Select"); - t.gui.text_label("Select from:"); - if(t.gui.radio_button_field("layer edited", "Layer being edited", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED)) - drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED; - if(t.gui.radio_button_field("all visible", "All visible layers", drawP.controls.layerSelector == DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS)) - drawP.controls.layerSelector = DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS; + drawP.selection.selection_gui(); t.gui.pop_id(); }