Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PAINTROID-367 Use finger size to set width of watercolor paint brush #1278

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,24 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SwitchCompat
import org.catrobat.paintroid.R
import org.catrobat.paintroid.tools.helper.AdvancedSettingsAlgorithms.smoothing
import org.catrobat.paintroid.tools.helper.AdvancedSettingsAlgorithms.useEventSize
import org.catrobat.paintroid.tools.implementation.DefaultToolPaint.Companion.antialiasing

class AdvancedSettingsDialog : MainActivityDialogFragment() {
private var initValueAntialiasing: Boolean = antialiasing
private var initValueSmoothing: Boolean = smoothing
private var initValueUseEventSize: Boolean = useEventSize

@SuppressLint("UseSwitchCompatOrMaterialCode")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val antialiasingSwitch = view.findViewById<SwitchCompat>(R.id.pocketpaint_antialiasing)
val smoothSwitch = view.findViewById<SwitchCompat>(R.id.pocketpaint_smoothing)
val useEventSizeSwitch = view.findViewById<SwitchCompat>(R.id.pocketpaint_eventsize)

antialiasingSwitch.isChecked = antialiasing
smoothSwitch.isChecked = smoothing
useEventSizeSwitch.isChecked = useEventSize

antialiasingSwitch.setOnCheckedChangeListener { _, isChecked ->
antialiasing = isChecked
Expand All @@ -31,6 +35,10 @@ class AdvancedSettingsDialog : MainActivityDialogFragment() {
smoothSwitch?.setOnCheckedChangeListener { _, isChecked ->
smoothing = isChecked
}

useEventSizeSwitch?.setOnCheckedChangeListener { _, isChecked ->
useEventSize = isChecked
}
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
Expand All @@ -49,6 +57,7 @@ class AdvancedSettingsDialog : MainActivityDialogFragment() {
.setNegativeButton(R.string.cancel_button_text) { _, _ ->
antialiasing = initValueAntialiasing
smoothing = initValueSmoothing
useEventSize = initValueUseEventSize
dismiss()
}
.create()
Expand All @@ -57,6 +66,7 @@ class AdvancedSettingsDialog : MainActivityDialogFragment() {
override fun onCancel(dialog: DialogInterface) {
antialiasing = initValueAntialiasing
smoothing = initValueSmoothing
useEventSize = initValueUseEventSize
super.onCancel(dialog)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import org.catrobat.paintroid.UserPreferences
import org.catrobat.paintroid.tools.Tool
import org.catrobat.paintroid.tools.Tool.StateChange
import org.catrobat.paintroid.tools.ToolType
import org.catrobat.paintroid.tools.implementation.BrushTool
import org.catrobat.paintroid.tools.options.ToolOptionsViewController
import org.catrobat.paintroid.ui.DrawingSurface
import org.catrobat.paintroid.ui.zoomwindow.ZoomWindowController
Expand Down Expand Up @@ -96,50 +97,64 @@ open class DrawingSurfaceListener(
}

private fun handleActionMove(currentTool: Tool?, event: MotionEvent) {
if (event.pointerCount == 1) {
handleActionMoveOnePointer(currentTool, event)
} else {
handleActionMoveMultiplePointer(currentTool, event)
}
}

private fun handleActionMoveOnePointer(currentTool: Tool?, event: MotionEvent) {
currentTool ?: return

val xOld: Float
val yOld: Float
if (event.pointerCount == 1) {
currentTool ?: return
recentTouchEventsData.add(TouchEventData(event.eventTime, event.x, event.y))
removeObsoleteTouchEventsData(event.eventTime)
if (currentTool.handToolMode()) {
if (touchMode == TouchMode.PINCH) {
xOld = 0f
yOld = 0f
touchMode = TouchMode.DRAW
} else {
xOld = eventX
yOld = eventY
}
newHandEvent(event.x, event.y)
if (xOld > 0 && eventX != xOld || yOld > 0 && eventY != yOld) {
callback.translatePerspective(eventX - xOld, eventY - yOld)
}
} else if (touchMode != TouchMode.PINCH) {

recentTouchEventsData.add(TouchEventData(event.eventTime, event.x, event.y))
removeObsoleteTouchEventsData(event.eventTime)
if (currentTool.handToolMode()) {
if (touchMode == TouchMode.PINCH) {
xOld = 0f
yOld = 0f
touchMode = TouchMode.DRAW
currentTool.handleMove(canvasTouchPoint)
}
handleZoomWindowOnMove(currentTool, event)
} else {
if (touchMode == TouchMode.DRAW) {
saveToolActionBeforeZoom(PointF(event.x, event.y))
currentTool?.resetInternalState(StateChange.MOVE_CANCELED)
} else {
xOld = eventX
yOld = eventY
}
touchMode = TouchMode.PINCH
val pointerDistanceOld = pointerDistance
pointerDistance = calculatePointerDistance(event)
if (pointerDistanceOld > 0 && pointerDistanceOld != pointerDistance) {
val scale = pointerDistance / pointerDistanceOld
callback.multiplyPerspectiveScale(scale)
newHandEvent(event.x, event.y)
if (xOld > 0 && eventX != xOld || yOld > 0 && eventY != yOld) {
callback.translatePerspective(eventX - xOld, eventY - yOld)
}
xOld = xMidPoint
yOld = yMidPoint
calculateMidPoint(event)
if (xOld > 0 && xMidPoint != xOld || yOld > 0 && yMidPoint != yOld) {
callback.translatePerspective(xMidPoint - xOld, yMidPoint - yOld)
} else if (touchMode != TouchMode.PINCH) {
touchMode = TouchMode.DRAW
if (currentTool is BrushTool && currentTool.useEventDependentStrokeWidth) {
currentTool.handleMove(canvasTouchPoint, event)
} else {
currentTool.handleMove(canvasTouchPoint)
}
zoomController.dismissOnPinch()
}
handleZoomWindowOnMove(currentTool, event)
}

private fun handleActionMoveMultiplePointer(currentTool: Tool?, event: MotionEvent) {
if (touchMode == TouchMode.DRAW) {
saveToolActionBeforeZoom(PointF(event.x, event.y))
currentTool?.resetInternalState(StateChange.MOVE_CANCELED)
}
touchMode = TouchMode.PINCH
val pointerDistanceOld = pointerDistance
pointerDistance = calculatePointerDistance(event)
if (pointerDistanceOld > 0 && pointerDistanceOld != pointerDistance) {
val scale = pointerDistance / pointerDistanceOld
callback.multiplyPerspectiveScale(scale)
}
val xOld: Float = xMidPoint
val yOld: Float = yMidPoint
calculateMidPoint(event)
if (xOld > 0 && xMidPoint != xOld || yOld > 0 && yMidPoint != yOld) {
callback.translatePerspective(xMidPoint - xOld, yMidPoint - yOld)
}
zoomController.dismissOnPinch()
}

private fun handleZoomWindowOnMove(currentTool: Tool, event: MotionEvent) {
Expand Down
3 changes: 3 additions & 0 deletions Paintroid/src/main/java/org/catrobat/paintroid/tools/Tool.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import android.graphics.Paint.Cap
import android.graphics.Point
import android.graphics.PointF
import android.os.Bundle
import android.view.MotionEvent

interface Tool {
val toolType: ToolType
Expand All @@ -38,6 +39,8 @@ interface Tool {

fun handleMove(coordinate: PointF?): Boolean

fun handleMove(coordinate: PointF?, event: MotionEvent): Boolean

fun handleUp(coordinate: PointF?): Boolean

fun changePaintColor(color: Int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import android.graphics.PointF
import org.catrobat.paintroid.command.serialization.SerializablePath

object AdvancedSettingsAlgorithms {
@kotlin.jvm.JvmField
@JvmField
var smoothing = true

const val threshold = 0.2
const val divider = 3

var useEventSize = false

@JvmStatic
fun smoothingAlgorithm(pointArray: List<PointF>): SerializablePath {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package org.catrobat.paintroid.tools.helper

import android.graphics.PointF
import org.catrobat.paintroid.command.serialization.SerializablePath
import kotlin.math.sqrt

data class Move(val p: PointF)

class PathContainer {
private var moveRight = mutableListOf<Move>()
private var moveLeft = mutableListOf<Move>()
private var points = mutableListOf<PointF>()

private var lastEndPoint = PointF(0f, 0f)

private var endPoint = PointF(0f, 0f)

private var lastPath: SerializablePath = SerializablePath()
fun getClosedPathFromPoints(): SerializablePath {
val path = SerializablePath()

if (moveLeft.isEmpty()) return path

path.moveTo(moveRight[0].p.x, moveRight[0].p.y)
@Suppress("SwallowedException")
try {
moveRight.forEach { move -> path.lineTo(move.p.x, move.p.y) }

if (!(endPoint.x == 0f && endPoint.y == 0f)) {
path.lineTo(endPoint.x, endPoint.y)
}
val reversed = moveLeft.reversed()

reversed.forEach { move -> path.lineTo(move.p.x, move.p.y) }
} catch (ex: ConcurrentModificationException) {
return lastPath
}

path.close()
lastPath = path
return path
}

private fun smooth() {
moveRight[0] = moveLeft.first()
moveRight[moveRight.size - 1] = moveLeft.last()

moveRight.forEachIndexed { index, move ->
if (moveRight.size < index + 2 || move == moveRight.last()) {
return
}

if (index == 0) {
return@forEachIndexed
}

val beforeIndex = index - 1
val afterIndex = index + 1

val beforeRight = moveRight[beforeIndex]
val afterRight = moveRight[afterIndex]
move.p.x = (beforeRight.p.x + afterRight.p.x) / 2
move.p.y = (beforeRight.p.y + afterRight.p.y) / 2

val beforeLeft = moveLeft[beforeIndex]
val afterLeft = moveLeft[afterIndex]
moveLeft[index].p.x = (beforeLeft.p.x + afterLeft.p.x) / 2
moveLeft[index].p.y = (beforeLeft.p.y + afterLeft.p.y) / 2
}
}

fun addStartPoint(coordinate: PointF) {
points = mutableListOf()
points.add(PointF(coordinate.x, coordinate.y))
moveRight = mutableListOf()
moveLeft = mutableListOf()
addNewPoint(coordinate, 0f)
lastEndPoint = coordinate
}

fun addNewPoint(canvasPoint: PointF, shiftBy: Float) {
if (points.isNotEmpty() && points.last() == canvasPoint) return

val orthogonalRight = getNormalizedOrthogonalVector(getDirectionalVector(points.last(), canvasPoint))
val orthogonalLeft = getNormalizedOrthogonalVector(getDirectionalVector(points.last(), canvasPoint))

moveRight.add(Move(getPointShiftedByDistanceRight(canvasPoint, orthogonalRight, shiftBy)))
moveLeft.add(Move(getPointShiftedByDistanceLeft(canvasPoint, orthogonalLeft, shiftBy)))

points.add(PointF(canvasPoint.x, canvasPoint.y))
}

fun addEndPoint(coordinate: PointF) {
endPoint = PointF(coordinate.x, coordinate.y)
smooth()
}

private fun getDirectionalVector(vecA: PointF, vecB: PointF): PointF = PointF(vecA.x - vecB.x, vecA.y - vecB.y)

private fun getNormalizedOrthogonalVector(vector: PointF): PointF {
val orth = PointF(vector.y, -vector.x)
val length = sqrt(orth.x * orth.x + orth.y * orth.y)
return if (length == 0f) PointF(0.0f, 0.0f) else PointF(orth.x / length, orth.y / length)
}

private fun getPointShiftedByDistanceRight(point: PointF, orth: PointF, shiftBy: Float): PointF = PointF(point.x + shiftBy * orth.x, point.y + shiftBy * orth.y)

private fun getPointShiftedByDistanceLeft(point: PointF, orth: PointF, shiftBy: Float): PointF = PointF(point.x - shiftBy * orth.x, point.y - shiftBy * orth.y)
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import android.graphics.Paint.Cap
import android.graphics.Point
import android.graphics.PointF
import android.os.Bundle
import android.view.MotionEvent
import androidx.annotation.ColorInt
import androidx.test.espresso.idling.CountingIdlingResource
import org.catrobat.paintroid.command.CommandFactory
Expand All @@ -49,7 +50,8 @@ abstract class BaseTool(
@JvmField
protected var idlingResource: CountingIdlingResource,
@JvmField
protected var commandManager: CommandManager
protected var commandManager: CommandManager,
var useEventDependentStrokeWidth: Boolean = false
) : Tool {
@JvmField
protected val movedDistance: PointF
Expand Down Expand Up @@ -100,6 +102,9 @@ abstract class BaseTool(
toolOptionsViewController.animateBottomAndTopNavigation(true)
return true
}

override fun handleMove(coordinate: PointF?, event: MotionEvent): Boolean = true

override val drawPaint
get() = Paint(toolPaint.paint)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import android.os.Bundle
import android.os.CountDownTimer
import android.util.DisplayMetrics
import android.view.View
import android.view.MotionEvent
import androidx.annotation.ColorRes
import androidx.annotation.VisibleForTesting
import androidx.test.espresso.idling.CountingIdlingResource
Expand Down Expand Up @@ -309,6 +310,8 @@ abstract class BaseToolWithRectangleShape(
return true
}

override fun handleMove(coordinate: PointF?, event: MotionEvent): Boolean = true

override fun handleUp(coordinate: PointF?): Boolean {
if (previousEventCoordinate == null) {
return false
Expand Down
Loading