diff --git a/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintNetworkService.java b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintNetworkService.java index 3620cc4..fd9cb20 100644 --- a/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintNetworkService.java +++ b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintNetworkService.java @@ -1,3 +1,21 @@ +/* + * InfiniPaint + * Copyright (C) 2025-2026 Yousef Khadadeh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package com.erroratline0.infinipaint; import android.app.ForegroundServiceStartNotAllowedException; @@ -55,4 +73,4 @@ public class InfiniPaintNetworkService extends Service { NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(serviceChannel); } -} \ No newline at end of file +} diff --git a/src/CanvasComponents/BrushStrokeCanvasComponent.cpp b/src/CanvasComponents/BrushStrokeCanvasComponent.cpp index 4a80174..75a636d 100644 --- a/src/CanvasComponents/BrushStrokeCanvasComponent.cpp +++ b/src/CanvasComponents/BrushStrokeCanvasComponent.cpp @@ -256,7 +256,7 @@ std::vector BrushStrokeCanvasComponent::smooth_ std::vector toRet; BrushStrokeCanvasComponentPoint p0 = points[beginIndex]; - p0.pos = p0.pos + p0.width * (p0.pos - points[beginIndex + 1].pos).normalized() * DRAW_MINIMUM_LIMIT; + p0.pos = p0.pos + p0.width * (p0.pos - points[beginIndex + 1].pos).normalized(); BrushStrokeCanvasComponentPoint p1 = points[beginIndex]; BrushStrokeCanvasComponentPoint p2 = points[beginIndex + 1]; BrushStrokeCanvasComponentPoint p3; @@ -265,7 +265,7 @@ std::vector BrushStrokeCanvasComponent::smooth_ if(i == endIndex - 1) { p3 = points[i + 1]; - p3.pos = p3.pos + p3.width * (p3.pos - points[i].pos).normalized() * DRAW_MINIMUM_LIMIT; + p3.pos = p3.pos + p3.width * (p3.pos - points[i].pos).normalized(); } else p3 = points[i + 2]; diff --git a/src/CanvasComponents/BrushStrokeCanvasComponent.hpp b/src/CanvasComponents/BrushStrokeCanvasComponent.hpp index 4ae3e3a..24cdf81 100644 --- a/src/CanvasComponents/BrushStrokeCanvasComponent.hpp +++ b/src/CanvasComponents/BrushStrokeCanvasComponent.hpp @@ -33,8 +33,6 @@ struct BrushStrokeCanvasComponentPoint { class BrushStrokeCanvasComponent : public CanvasComponent { public: - constexpr static float DRAW_MINIMUM_LIMIT = 1.0f; - virtual CanvasComponentType get_type() const override; virtual void save(cereal::PortableBinaryOutputArchive& a) const override; virtual void load(cereal::PortableBinaryInputArchive& a) override; diff --git a/src/DrawingProgram/Tools/BrushTool.cpp b/src/DrawingProgram/Tools/BrushTool.cpp index 947784e..5a832ca 100644 --- a/src/DrawingProgram/Tools/BrushTool.cpp +++ b/src/DrawingProgram/Tools/BrushTool.cpp @@ -29,9 +29,9 @@ #include "../../GUIStuff/ElementHelpers/TextLabelHelpers.hpp" #include "../../GUIStuff/ElementHelpers/CheckBoxHelpers.hpp" -#define VEL_SMOOTH_MIN 0.6 -#define VEL_SMOOTH_MAX 1.0 #define MINIMUM_DISTANCE_TO_NEXT_POINT 0.002f +#define SMOOTH_FACTOR 0.707f +#define DRAW_MINIMUM_LIMIT 1.0f BrushTool::BrushTool(DrawingProgram& initDrawP): DrawingProgramToolBase(initDrawP) @@ -50,11 +50,39 @@ void BrushTool::erase_component(CanvasComponentContainer::ObjInfo* erasedComp) { objInfoBeingEdited = nullptr; } +void BrushTool::smooth_out_points(float smoothFactor) { + if(objInfoBeingEdited) { + NetworkingObjects::NetObjOwnerPtr& containerPtr = objInfoBeingEdited->obj; + BrushStrokeCanvasComponent& brushStroke = static_cast(containerPtr->get_comp()); + auto& brushPoints = *brushStroke.d.points; + if(brushPoints.size() >= 2) { + brushPoints.back().width = std::max(brushPoints[brushPoints.size() - 1].width, brushPoints[brushPoints.size() - 2].width * smoothFactor); + for(size_t i = brushPoints.size() - 1; i > 0; i--) { + if(brushPoints[i].width * smoothFactor > brushPoints[i - 1].width) + brushPoints[i - 1].width = brushPoints[i].width * smoothFactor; + else + break; + } + } + } +} + +void BrushTool::fix_tip() { + if(objInfoBeingEdited) { + NetworkingObjects::NetObjOwnerPtr& containerPtr = objInfoBeingEdited->obj; + BrushStrokeCanvasComponent& brushStroke = static_cast(containerPtr->get_comp()); + auto& brushPoints = *brushStroke.d.points; + if(brushPoints.size() >= 2) { + brushPoints[brushPoints.size() - 2].width = brushPoints[brushPoints.size() - 1].width = std::max(brushPoints[brushPoints.size() - 1].width, brushPoints[brushPoints.size() - 2].width); + } + } +} + void BrushTool::input_mouse_button_on_canvas_callback(const InputManager::MouseButtonCallbackArgs& button) { if(button.button == InputManager::MouseButton::LEFT) { auto& toolConfig = drawP.world.main.toolConfig; if(button.down && drawP.layerMan.is_a_layer_being_edited() && !objInfoBeingEdited && !drawP.world.main.g.gui.cursor_obstructed()) { - if(drawP.world.main.input.pen.isDown) { + if(button.deviceType == InputManager::MouseDeviceType::PEN) { penWidth = drawP.world.main.input.pen.pressure; if(penWidth != 0.0f) { float brushMinSize = drawP.world.main.conf.tabletOptions.brushMinimumSize; @@ -102,35 +130,48 @@ void BrushTool::input_mouse_motion_callback(const InputManager::MouseMotionCallb p.pos = containerPtr->coords.from_cam_space_to_this(drawP.world, motion.pos); p.width = width; + // Temporary point is a point that follows the cursor until it is placed if(!addedTemporaryPoint) { + // Temporary point is close enough to be placed if(extensive_point_checking(brushStroke, p.pos)) { brushPoints.emplace_back(p); addedTemporaryPoint = true; } else + // Temporary point not close enough yet, so change the width of the previous point based on the current pen width until it can be placed brushPoints.back().width = std::max(brushPoints.back().width, p.width); } if(addedTemporaryPoint) { const BrushStrokeCanvasComponentPoint& prevP = brushPoints[brushPoints.size() - 2]; float distToPrev = (p.pos - prevP.pos).norm(); + // If the cursor isnt too close to the previous points, move the temporary point to the cursor and change the width of the temporary point to the current pen width if(extensive_point_checking_back(brushStroke, p.pos)) { 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 >= width * BrushStrokeCanvasComponent::DRAW_MINIMUM_LIMIT)) { - brushPoints.back() = p; + + float maxWidthToCompareTo = std::max(brushPoints.back().width, p.width); + maxWidthToCompareTo = std::max(maxWidthToCompareTo, 5.0f); + if(brushPoints.size() >= 2) + maxWidthToCompareTo = std::max(brushPoints[brushPoints.size() - 2].width, maxWidthToCompareTo); + if(brushPoints.size() >= 3) + maxWidthToCompareTo = std::max(brushPoints[brushPoints.size() - 3].width, maxWidthToCompareTo); + + // If the temporary point (cursor) is far enough from the point before it, make the temporary point permanent + if(distToPrev >= maxWidthToCompareTo * DRAW_MINIMUM_LIMIT) { + brushPoints.back().pos = p.pos; + brushPoints.back().width = std::max(brushPoints.back().width, p.width); addedTemporaryPoint = false; - if(midwayInterpolation) { - if(brushPoints.size() != 2) // Don't interpolate the first point - brushPoints[brushPoints.size() - 2].pos = (prevPointUnaltered + p.pos) * 0.5; - prevPointUnaltered = p.pos; - } + // Midway interpolation moves the point before the last one to a position that leads to a smoother line + if(brushPoints.size() != 2) // Don't interpolate the first point + brushPoints[brushPoints.size() - 2].pos = (prevPointUnaltered + p.pos) * 0.5; + prevPointUnaltered = p.pos; } } + smooth_out_points(SMOOTH_FACTOR); containerPtr->send_comp_update(drawP, false); containerPtr->commit_update(drawP); } @@ -142,6 +183,17 @@ void BrushTool::input_pen_axis_callback(const InputManager::PenAxisCallbackArgs& if(penWidth != 0.0f) { float brushMinSize = drawP.world.main.conf.tabletOptions.brushMinimumSize; penWidth = brushMinSize + penWidth * (1.0f - brushMinSize); + if(objInfoBeingEdited) { + auto& toolConfig = drawP.world.main.toolConfig; + NetworkingObjects::NetObjOwnerPtr& containerPtr = objInfoBeingEdited->obj; + BrushStrokeCanvasComponent& brushStroke = static_cast(containerPtr->get_comp()); + auto& brushPoints = *brushStroke.d.points; + float width = toolConfig.get_relative_width_stroke_size(drawP, containerPtr->coords.inverseScale).first.value() * penWidth; + brushPoints.back().width = std::max(brushPoints.back().width, width); + smooth_out_points(SMOOTH_FACTOR); + containerPtr->send_comp_update(drawP, false); + containerPtr->commit_update(drawP); + } } } } @@ -176,6 +228,7 @@ void BrushTool::tool_update() { void BrushTool::commit_stroke() { if(objInfoBeingEdited) { NetworkingObjects::NetObjOwnerPtr& containerPtr = objInfoBeingEdited->obj; + fix_tip(); containerPtr->commit_update(drawP); containerPtr->send_comp_update(drawP, true); if(containerPtr->get_world_bounds().has_value()) diff --git a/src/DrawingProgram/Tools/BrushTool.hpp b/src/DrawingProgram/Tools/BrushTool.hpp index bef5bca..b0ebd17 100644 --- a/src/DrawingProgram/Tools/BrushTool.hpp +++ b/src/DrawingProgram/Tools/BrushTool.hpp @@ -48,6 +48,8 @@ class BrushTool : public DrawingProgramToolBase { private: bool extensive_point_checking_back(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint); bool extensive_point_checking(const BrushStrokeCanvasComponent& brushStroke, const Vector2f& newPoint); + void smooth_out_points(float smoothFactor); + void fix_tip(); void commit_stroke(); float penWidth = 1.0f; @@ -55,7 +57,5 @@ class BrushTool : public DrawingProgramToolBase { CanvasComponentContainer::ObjInfo* objInfoBeingEdited = nullptr; - bool drawingMinimumRelativeToSize = true; - bool midwayInterpolation = true; Vector2f prevPointUnaltered = {0, 0}; }; diff --git a/src/NetThreadManager.cpp b/src/NetThreadManager.cpp index 842e588..dafbf76 100644 --- a/src/NetThreadManager.cpp +++ b/src/NetThreadManager.cpp @@ -1,3 +1,21 @@ +/* + * InfiniPaint + * Copyright (C) 2025-2026 Yousef Khadadeh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "NetThreadManager.hpp" #include #include diff --git a/src/NetThreadManager.hpp b/src/NetThreadManager.hpp index e9c72c5..2409d79 100644 --- a/src/NetThreadManager.hpp +++ b/src/NetThreadManager.hpp @@ -1,3 +1,21 @@ +/* + * InfiniPaint + * Copyright (C) 2025-2026 Yousef Khadadeh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once #include #include diff --git a/src/main.cpp b/src/main.cpp index 11008d8..c2af887 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -221,6 +221,7 @@ void initialize_sdl(MainStruct& mS, int wWidth, int wHeight) { SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_TYPE_STRING, "application"); SDL_SetHint(SDL_HINT_APP_NAME, "InfiniPaint"); + SDL_SetHint(SDL_HINT_PEN_TOUCH_EVENTS, "0"); SDL_SetHint(SDL_HINT_PEN_MOUSE_EVENTS, "0"); SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0"); SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");