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");