Wrong coordinates after scaling canvas











up vote
-1
down vote

favorite












We have the red dot in (100; 100) coordinates. If we click to red dot after dragging - it will save it's (100; 100) coordinates. But if we scale in or out it will have coordinates completely different from (100; 100).



How to calculate x and y correctly after scaling?



class CanvasView(context: Context, attrs: AttributeSet?) : View(context, attrs) {

companion object {
private const val INVALID_POINTER_ID = -1
}

private var posX: Float = 0f
private var posY: Float = 0f

private var lastTouchX: Float = 0f
private var lastTouchY: Float = 0f
private var activePointerId = INVALID_POINTER_ID

private val scaleDetector: ScaleGestureDetector
private var scaleFactor = 1f

private var prevMotionType = MotionEvent.ACTION_DOWN
private var prevX = 0f
private var prevY = 0f

private val paint: Paint = Paint()

constructor(mContext: Context) : this(mContext, null)

init {
scaleDetector = ScaleGestureDetector(context, ScaleListener())

paint.strokeWidth = 1f
paint.color = Color.RED
}

public override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save()
canvas.scale(scaleFactor, scaleFactor, pivotX, pivotY)
canvas.translate(posX, posY)
canvas.drawCircle(100f, 100f, 10f, paint)
canvas.restore()
}

override fun onTouchEvent(ev: MotionEvent): Boolean {
scaleDetector.onTouchEvent(ev)

val action = ev.action
when (action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
val x = ev.x
val y = ev.y

lastTouchX = x
lastTouchY = y
activePointerId = ev.getPointerId(0)

calculateIfClicked(ev)
}

MotionEvent.ACTION_MOVE -> {
val pointerIndex = ev.findPointerIndex(activePointerId)
val x = ev.getX(pointerIndex)
val y = ev.getY(pointerIndex)

if (!scaleDetector.isInProgress) {
val dx = x - lastTouchX
val dy = y - lastTouchY

posX += dx / scaleFactor
posY += dy / scaleFactor

invalidate()
}

lastTouchX = x
lastTouchY = y

calculateIfClicked(ev)
}

MotionEvent.ACTION_UP -> {
activePointerId = INVALID_POINTER_ID

calculateIfClicked(ev)
}

MotionEvent.ACTION_CANCEL -> {
activePointerId = INVALID_POINTER_ID
}

MotionEvent.ACTION_POINTER_UP -> {
val pointerIndex =
ev.action and MotionEvent.ACTION_POINTER_INDEX_MASK shr MotionEvent.ACTION_POINTER_INDEX_SHIFT
val pointerId = ev.getPointerId(pointerIndex)
if (pointerId == activePointerId) {
val newPointerIndex = if (pointerIndex == 0) 1 else 0
lastTouchX = ev.getX(newPointerIndex)
lastTouchY = ev.getY(newPointerIndex)
activePointerId = ev.getPointerId(newPointerIndex)
}
}
}

return true
}

private fun calculateIfClicked(ev: MotionEvent) {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
prevMotionType = MotionEvent.ACTION_DOWN
prevX = ev.x
prevY = ev.y
}

MotionEvent.ACTION_MOVE -> prevMotionType = MotionEvent.ACTION_MOVE

MotionEvent.ACTION_UP -> {
val delta = Math.max(
Math.abs(Math.abs(ev.x) - Math.abs(prevX)),
Math.abs(Math.abs(ev.y) - Math.abs(prevY))
)

if (prevMotionType == MotionEvent.ACTION_DOWN ||
(prevMotionType == MotionEvent.ACTION_MOVE && delta < 5)
) {
val x = ev.x - posX * scaleFactor
val y = ev.y - posY * scaleFactor

Log.d("abcd", "x: $x, y: $y")
}
}
}
}

private inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
scaleFactor *= detector.scaleFactor
scaleFactor = Math.max(0.3f, Math.min(scaleFactor, 10.0f))

invalidate()
return true
}
}
}


x and y coordinates are wrong after scaling. They keep their position, but it's not that expected.










share|improve this question
























  • why do you want to canvas.scale and canvas.translate? cannot you simply canvas.drawCircle(canterX, canterY, scaledRadius, paint)?
    – pskink
    Nov 10 at 7:16










  • Because the red dot is just an example. I'm going to draw multiple objects, not only one red dot. And moving objects instead of viewport will break everything! I need to save object coordinates to correctly handle click events on them after drag/scale.
    – badadin
    Nov 10 at 8:00












  • then use Matrix API and Canvas#concat method
    – pskink
    Nov 10 at 8:06










  • I thought about it, but I did not find an example with drag/zoom/click. If you show me an example, I will very appreciate.
    – badadin
    Nov 10 at 8:11










  • And what's wrong with canvas.scale and canvas.translate? Why do you recommend not tot use them?
    – badadin
    Nov 10 at 8:28















up vote
-1
down vote

favorite












We have the red dot in (100; 100) coordinates. If we click to red dot after dragging - it will save it's (100; 100) coordinates. But if we scale in or out it will have coordinates completely different from (100; 100).



How to calculate x and y correctly after scaling?



class CanvasView(context: Context, attrs: AttributeSet?) : View(context, attrs) {

companion object {
private const val INVALID_POINTER_ID = -1
}

private var posX: Float = 0f
private var posY: Float = 0f

private var lastTouchX: Float = 0f
private var lastTouchY: Float = 0f
private var activePointerId = INVALID_POINTER_ID

private val scaleDetector: ScaleGestureDetector
private var scaleFactor = 1f

private var prevMotionType = MotionEvent.ACTION_DOWN
private var prevX = 0f
private var prevY = 0f

private val paint: Paint = Paint()

constructor(mContext: Context) : this(mContext, null)

init {
scaleDetector = ScaleGestureDetector(context, ScaleListener())

paint.strokeWidth = 1f
paint.color = Color.RED
}

public override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save()
canvas.scale(scaleFactor, scaleFactor, pivotX, pivotY)
canvas.translate(posX, posY)
canvas.drawCircle(100f, 100f, 10f, paint)
canvas.restore()
}

override fun onTouchEvent(ev: MotionEvent): Boolean {
scaleDetector.onTouchEvent(ev)

val action = ev.action
when (action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
val x = ev.x
val y = ev.y

lastTouchX = x
lastTouchY = y
activePointerId = ev.getPointerId(0)

calculateIfClicked(ev)
}

MotionEvent.ACTION_MOVE -> {
val pointerIndex = ev.findPointerIndex(activePointerId)
val x = ev.getX(pointerIndex)
val y = ev.getY(pointerIndex)

if (!scaleDetector.isInProgress) {
val dx = x - lastTouchX
val dy = y - lastTouchY

posX += dx / scaleFactor
posY += dy / scaleFactor

invalidate()
}

lastTouchX = x
lastTouchY = y

calculateIfClicked(ev)
}

MotionEvent.ACTION_UP -> {
activePointerId = INVALID_POINTER_ID

calculateIfClicked(ev)
}

MotionEvent.ACTION_CANCEL -> {
activePointerId = INVALID_POINTER_ID
}

MotionEvent.ACTION_POINTER_UP -> {
val pointerIndex =
ev.action and MotionEvent.ACTION_POINTER_INDEX_MASK shr MotionEvent.ACTION_POINTER_INDEX_SHIFT
val pointerId = ev.getPointerId(pointerIndex)
if (pointerId == activePointerId) {
val newPointerIndex = if (pointerIndex == 0) 1 else 0
lastTouchX = ev.getX(newPointerIndex)
lastTouchY = ev.getY(newPointerIndex)
activePointerId = ev.getPointerId(newPointerIndex)
}
}
}

return true
}

private fun calculateIfClicked(ev: MotionEvent) {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
prevMotionType = MotionEvent.ACTION_DOWN
prevX = ev.x
prevY = ev.y
}

MotionEvent.ACTION_MOVE -> prevMotionType = MotionEvent.ACTION_MOVE

MotionEvent.ACTION_UP -> {
val delta = Math.max(
Math.abs(Math.abs(ev.x) - Math.abs(prevX)),
Math.abs(Math.abs(ev.y) - Math.abs(prevY))
)

if (prevMotionType == MotionEvent.ACTION_DOWN ||
(prevMotionType == MotionEvent.ACTION_MOVE && delta < 5)
) {
val x = ev.x - posX * scaleFactor
val y = ev.y - posY * scaleFactor

Log.d("abcd", "x: $x, y: $y")
}
}
}
}

private inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
scaleFactor *= detector.scaleFactor
scaleFactor = Math.max(0.3f, Math.min(scaleFactor, 10.0f))

invalidate()
return true
}
}
}


x and y coordinates are wrong after scaling. They keep their position, but it's not that expected.










share|improve this question
























  • why do you want to canvas.scale and canvas.translate? cannot you simply canvas.drawCircle(canterX, canterY, scaledRadius, paint)?
    – pskink
    Nov 10 at 7:16










  • Because the red dot is just an example. I'm going to draw multiple objects, not only one red dot. And moving objects instead of viewport will break everything! I need to save object coordinates to correctly handle click events on them after drag/scale.
    – badadin
    Nov 10 at 8:00












  • then use Matrix API and Canvas#concat method
    – pskink
    Nov 10 at 8:06










  • I thought about it, but I did not find an example with drag/zoom/click. If you show me an example, I will very appreciate.
    – badadin
    Nov 10 at 8:11










  • And what's wrong with canvas.scale and canvas.translate? Why do you recommend not tot use them?
    – badadin
    Nov 10 at 8:28













up vote
-1
down vote

favorite









up vote
-1
down vote

favorite











We have the red dot in (100; 100) coordinates. If we click to red dot after dragging - it will save it's (100; 100) coordinates. But if we scale in or out it will have coordinates completely different from (100; 100).



How to calculate x and y correctly after scaling?



class CanvasView(context: Context, attrs: AttributeSet?) : View(context, attrs) {

companion object {
private const val INVALID_POINTER_ID = -1
}

private var posX: Float = 0f
private var posY: Float = 0f

private var lastTouchX: Float = 0f
private var lastTouchY: Float = 0f
private var activePointerId = INVALID_POINTER_ID

private val scaleDetector: ScaleGestureDetector
private var scaleFactor = 1f

private var prevMotionType = MotionEvent.ACTION_DOWN
private var prevX = 0f
private var prevY = 0f

private val paint: Paint = Paint()

constructor(mContext: Context) : this(mContext, null)

init {
scaleDetector = ScaleGestureDetector(context, ScaleListener())

paint.strokeWidth = 1f
paint.color = Color.RED
}

public override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save()
canvas.scale(scaleFactor, scaleFactor, pivotX, pivotY)
canvas.translate(posX, posY)
canvas.drawCircle(100f, 100f, 10f, paint)
canvas.restore()
}

override fun onTouchEvent(ev: MotionEvent): Boolean {
scaleDetector.onTouchEvent(ev)

val action = ev.action
when (action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
val x = ev.x
val y = ev.y

lastTouchX = x
lastTouchY = y
activePointerId = ev.getPointerId(0)

calculateIfClicked(ev)
}

MotionEvent.ACTION_MOVE -> {
val pointerIndex = ev.findPointerIndex(activePointerId)
val x = ev.getX(pointerIndex)
val y = ev.getY(pointerIndex)

if (!scaleDetector.isInProgress) {
val dx = x - lastTouchX
val dy = y - lastTouchY

posX += dx / scaleFactor
posY += dy / scaleFactor

invalidate()
}

lastTouchX = x
lastTouchY = y

calculateIfClicked(ev)
}

MotionEvent.ACTION_UP -> {
activePointerId = INVALID_POINTER_ID

calculateIfClicked(ev)
}

MotionEvent.ACTION_CANCEL -> {
activePointerId = INVALID_POINTER_ID
}

MotionEvent.ACTION_POINTER_UP -> {
val pointerIndex =
ev.action and MotionEvent.ACTION_POINTER_INDEX_MASK shr MotionEvent.ACTION_POINTER_INDEX_SHIFT
val pointerId = ev.getPointerId(pointerIndex)
if (pointerId == activePointerId) {
val newPointerIndex = if (pointerIndex == 0) 1 else 0
lastTouchX = ev.getX(newPointerIndex)
lastTouchY = ev.getY(newPointerIndex)
activePointerId = ev.getPointerId(newPointerIndex)
}
}
}

return true
}

private fun calculateIfClicked(ev: MotionEvent) {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
prevMotionType = MotionEvent.ACTION_DOWN
prevX = ev.x
prevY = ev.y
}

MotionEvent.ACTION_MOVE -> prevMotionType = MotionEvent.ACTION_MOVE

MotionEvent.ACTION_UP -> {
val delta = Math.max(
Math.abs(Math.abs(ev.x) - Math.abs(prevX)),
Math.abs(Math.abs(ev.y) - Math.abs(prevY))
)

if (prevMotionType == MotionEvent.ACTION_DOWN ||
(prevMotionType == MotionEvent.ACTION_MOVE && delta < 5)
) {
val x = ev.x - posX * scaleFactor
val y = ev.y - posY * scaleFactor

Log.d("abcd", "x: $x, y: $y")
}
}
}
}

private inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
scaleFactor *= detector.scaleFactor
scaleFactor = Math.max(0.3f, Math.min(scaleFactor, 10.0f))

invalidate()
return true
}
}
}


x and y coordinates are wrong after scaling. They keep their position, but it's not that expected.










share|improve this question















We have the red dot in (100; 100) coordinates. If we click to red dot after dragging - it will save it's (100; 100) coordinates. But if we scale in or out it will have coordinates completely different from (100; 100).



How to calculate x and y correctly after scaling?



class CanvasView(context: Context, attrs: AttributeSet?) : View(context, attrs) {

companion object {
private const val INVALID_POINTER_ID = -1
}

private var posX: Float = 0f
private var posY: Float = 0f

private var lastTouchX: Float = 0f
private var lastTouchY: Float = 0f
private var activePointerId = INVALID_POINTER_ID

private val scaleDetector: ScaleGestureDetector
private var scaleFactor = 1f

private var prevMotionType = MotionEvent.ACTION_DOWN
private var prevX = 0f
private var prevY = 0f

private val paint: Paint = Paint()

constructor(mContext: Context) : this(mContext, null)

init {
scaleDetector = ScaleGestureDetector(context, ScaleListener())

paint.strokeWidth = 1f
paint.color = Color.RED
}

public override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save()
canvas.scale(scaleFactor, scaleFactor, pivotX, pivotY)
canvas.translate(posX, posY)
canvas.drawCircle(100f, 100f, 10f, paint)
canvas.restore()
}

override fun onTouchEvent(ev: MotionEvent): Boolean {
scaleDetector.onTouchEvent(ev)

val action = ev.action
when (action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
val x = ev.x
val y = ev.y

lastTouchX = x
lastTouchY = y
activePointerId = ev.getPointerId(0)

calculateIfClicked(ev)
}

MotionEvent.ACTION_MOVE -> {
val pointerIndex = ev.findPointerIndex(activePointerId)
val x = ev.getX(pointerIndex)
val y = ev.getY(pointerIndex)

if (!scaleDetector.isInProgress) {
val dx = x - lastTouchX
val dy = y - lastTouchY

posX += dx / scaleFactor
posY += dy / scaleFactor

invalidate()
}

lastTouchX = x
lastTouchY = y

calculateIfClicked(ev)
}

MotionEvent.ACTION_UP -> {
activePointerId = INVALID_POINTER_ID

calculateIfClicked(ev)
}

MotionEvent.ACTION_CANCEL -> {
activePointerId = INVALID_POINTER_ID
}

MotionEvent.ACTION_POINTER_UP -> {
val pointerIndex =
ev.action and MotionEvent.ACTION_POINTER_INDEX_MASK shr MotionEvent.ACTION_POINTER_INDEX_SHIFT
val pointerId = ev.getPointerId(pointerIndex)
if (pointerId == activePointerId) {
val newPointerIndex = if (pointerIndex == 0) 1 else 0
lastTouchX = ev.getX(newPointerIndex)
lastTouchY = ev.getY(newPointerIndex)
activePointerId = ev.getPointerId(newPointerIndex)
}
}
}

return true
}

private fun calculateIfClicked(ev: MotionEvent) {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
prevMotionType = MotionEvent.ACTION_DOWN
prevX = ev.x
prevY = ev.y
}

MotionEvent.ACTION_MOVE -> prevMotionType = MotionEvent.ACTION_MOVE

MotionEvent.ACTION_UP -> {
val delta = Math.max(
Math.abs(Math.abs(ev.x) - Math.abs(prevX)),
Math.abs(Math.abs(ev.y) - Math.abs(prevY))
)

if (prevMotionType == MotionEvent.ACTION_DOWN ||
(prevMotionType == MotionEvent.ACTION_MOVE && delta < 5)
) {
val x = ev.x - posX * scaleFactor
val y = ev.y - posY * scaleFactor

Log.d("abcd", "x: $x, y: $y")
}
}
}
}

private inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
scaleFactor *= detector.scaleFactor
scaleFactor = Math.max(0.3f, Math.min(scaleFactor, 10.0f))

invalidate()
return true
}
}
}


x and y coordinates are wrong after scaling. They keep their position, but it's not that expected.







android kotlin android-canvas






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 11 at 9:32

























asked Nov 10 at 6:36









badadin

6116




6116












  • why do you want to canvas.scale and canvas.translate? cannot you simply canvas.drawCircle(canterX, canterY, scaledRadius, paint)?
    – pskink
    Nov 10 at 7:16










  • Because the red dot is just an example. I'm going to draw multiple objects, not only one red dot. And moving objects instead of viewport will break everything! I need to save object coordinates to correctly handle click events on them after drag/scale.
    – badadin
    Nov 10 at 8:00












  • then use Matrix API and Canvas#concat method
    – pskink
    Nov 10 at 8:06










  • I thought about it, but I did not find an example with drag/zoom/click. If you show me an example, I will very appreciate.
    – badadin
    Nov 10 at 8:11










  • And what's wrong with canvas.scale and canvas.translate? Why do you recommend not tot use them?
    – badadin
    Nov 10 at 8:28


















  • why do you want to canvas.scale and canvas.translate? cannot you simply canvas.drawCircle(canterX, canterY, scaledRadius, paint)?
    – pskink
    Nov 10 at 7:16










  • Because the red dot is just an example. I'm going to draw multiple objects, not only one red dot. And moving objects instead of viewport will break everything! I need to save object coordinates to correctly handle click events on them after drag/scale.
    – badadin
    Nov 10 at 8:00












  • then use Matrix API and Canvas#concat method
    – pskink
    Nov 10 at 8:06










  • I thought about it, but I did not find an example with drag/zoom/click. If you show me an example, I will very appreciate.
    – badadin
    Nov 10 at 8:11










  • And what's wrong with canvas.scale and canvas.translate? Why do you recommend not tot use them?
    – badadin
    Nov 10 at 8:28
















why do you want to canvas.scale and canvas.translate? cannot you simply canvas.drawCircle(canterX, canterY, scaledRadius, paint)?
– pskink
Nov 10 at 7:16




why do you want to canvas.scale and canvas.translate? cannot you simply canvas.drawCircle(canterX, canterY, scaledRadius, paint)?
– pskink
Nov 10 at 7:16












Because the red dot is just an example. I'm going to draw multiple objects, not only one red dot. And moving objects instead of viewport will break everything! I need to save object coordinates to correctly handle click events on them after drag/scale.
– badadin
Nov 10 at 8:00






Because the red dot is just an example. I'm going to draw multiple objects, not only one red dot. And moving objects instead of viewport will break everything! I need to save object coordinates to correctly handle click events on them after drag/scale.
– badadin
Nov 10 at 8:00














then use Matrix API and Canvas#concat method
– pskink
Nov 10 at 8:06




then use Matrix API and Canvas#concat method
– pskink
Nov 10 at 8:06












I thought about it, but I did not find an example with drag/zoom/click. If you show me an example, I will very appreciate.
– badadin
Nov 10 at 8:11




I thought about it, but I did not find an example with drag/zoom/click. If you show me an example, I will very appreciate.
– badadin
Nov 10 at 8:11












And what's wrong with canvas.scale and canvas.translate? Why do you recommend not tot use them?
– badadin
Nov 10 at 8:28




And what's wrong with canvas.scale and canvas.translate? Why do you recommend not tot use them?
– badadin
Nov 10 at 8:28












1 Answer
1






active

oldest

votes

















up vote
0
down vote



accepted










At first I decided to use this solution and it worked as expected. But then I used solution that pskink advised me to use. It simpler, shorter and rather more correct then my previous choice. Example:



interface OnMatrixChangeListener {
fun onChange(theMatrix: Matrix)
}

class ChartView2(context: Context, attrs: AttributeSet?) : View(context, attrs), OnMatrixChangeListener {

private var theMatrix = Matrix()
private var detector = MatrixGestureDetector(theMatrix, this)
private var paint = Paint()
private var colors = intArrayOf(Color.RED, Color.GREEN, Color.BLUE)
private var colorNames = arrayOf("RED", "GREEN", "BLUE")
private var centers = arrayOf(PointF(100f, 100f), PointF(400f, 100f), PointF(250f, 360f))

var inverse = Matrix()
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
theMatrix.invert(inverse)
val pts = floatArrayOf(event.x, event.y)
inverse.mapPoints(pts)
for (i in colors.indices) {
if (Math.hypot((pts[0] - centers[i].x).toDouble(), (pts[1] - centers[i].y).toDouble()) < 100)
Log.d("abcd", colorNames[i] + " circle clicked")
}
}
detector.onTouchEvent(event)
return true
}

override fun onDraw(canvas: Canvas) {
canvas.concat(theMatrix)
for (i in colors.indices) {
paint.color = colors[i]
canvas.drawCircle(centers[i].x, centers[i].y, 100f, paint)
}
}

override fun onChange(theMatrix: Matrix) {
invalidate()
}
}

internal class MatrixGestureDetector(private val mMatrix: Matrix, listener: OnMatrixChangeListener) {

private var ptpIdx = 0
private val mTempMatrix = Matrix()
private val mListener: OnMatrixChangeListener?
private val mSrc = FloatArray(4)
private val mDst = FloatArray(4)
private var mCount: Int = 0

init {
this.mListener = listener
}

fun onTouchEvent(event: MotionEvent) {
if (event.pointerCount > 2) {
return
}

val action = event.actionMasked
val index = event.actionIndex

when (action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
val idx = index * 2
mSrc[idx] = event.getX(index)
mSrc[idx + 1] = event.getY(index)
mCount++
ptpIdx = 0
}

MotionEvent.ACTION_MOVE -> {
for (i in 0 until mCount) {
val idx = ptpIdx + i * 2
mDst[idx] = event.getX(i)
mDst[idx + 1] = event.getY(i)
}
mTempMatrix.setPolyToPoly(mSrc, ptpIdx, mDst, ptpIdx, mCount)
mMatrix.postConcat(mTempMatrix)
mListener?.onChange(mMatrix)
System.arraycopy(mDst, 0, mSrc, 0, mDst.size)
}

MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
if (event.getPointerId(index) == 0) ptpIdx = 2
mCount--
}
}
}
}





share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53236594%2fwrong-coordinates-after-scaling-canvas%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    0
    down vote



    accepted










    At first I decided to use this solution and it worked as expected. But then I used solution that pskink advised me to use. It simpler, shorter and rather more correct then my previous choice. Example:



    interface OnMatrixChangeListener {
    fun onChange(theMatrix: Matrix)
    }

    class ChartView2(context: Context, attrs: AttributeSet?) : View(context, attrs), OnMatrixChangeListener {

    private var theMatrix = Matrix()
    private var detector = MatrixGestureDetector(theMatrix, this)
    private var paint = Paint()
    private var colors = intArrayOf(Color.RED, Color.GREEN, Color.BLUE)
    private var colorNames = arrayOf("RED", "GREEN", "BLUE")
    private var centers = arrayOf(PointF(100f, 100f), PointF(400f, 100f), PointF(250f, 360f))

    var inverse = Matrix()
    override fun onTouchEvent(event: MotionEvent): Boolean {
    if (event.action == MotionEvent.ACTION_DOWN) {
    theMatrix.invert(inverse)
    val pts = floatArrayOf(event.x, event.y)
    inverse.mapPoints(pts)
    for (i in colors.indices) {
    if (Math.hypot((pts[0] - centers[i].x).toDouble(), (pts[1] - centers[i].y).toDouble()) < 100)
    Log.d("abcd", colorNames[i] + " circle clicked")
    }
    }
    detector.onTouchEvent(event)
    return true
    }

    override fun onDraw(canvas: Canvas) {
    canvas.concat(theMatrix)
    for (i in colors.indices) {
    paint.color = colors[i]
    canvas.drawCircle(centers[i].x, centers[i].y, 100f, paint)
    }
    }

    override fun onChange(theMatrix: Matrix) {
    invalidate()
    }
    }

    internal class MatrixGestureDetector(private val mMatrix: Matrix, listener: OnMatrixChangeListener) {

    private var ptpIdx = 0
    private val mTempMatrix = Matrix()
    private val mListener: OnMatrixChangeListener?
    private val mSrc = FloatArray(4)
    private val mDst = FloatArray(4)
    private var mCount: Int = 0

    init {
    this.mListener = listener
    }

    fun onTouchEvent(event: MotionEvent) {
    if (event.pointerCount > 2) {
    return
    }

    val action = event.actionMasked
    val index = event.actionIndex

    when (action) {
    MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
    val idx = index * 2
    mSrc[idx] = event.getX(index)
    mSrc[idx + 1] = event.getY(index)
    mCount++
    ptpIdx = 0
    }

    MotionEvent.ACTION_MOVE -> {
    for (i in 0 until mCount) {
    val idx = ptpIdx + i * 2
    mDst[idx] = event.getX(i)
    mDst[idx + 1] = event.getY(i)
    }
    mTempMatrix.setPolyToPoly(mSrc, ptpIdx, mDst, ptpIdx, mCount)
    mMatrix.postConcat(mTempMatrix)
    mListener?.onChange(mMatrix)
    System.arraycopy(mDst, 0, mSrc, 0, mDst.size)
    }

    MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
    if (event.getPointerId(index) == 0) ptpIdx = 2
    mCount--
    }
    }
    }
    }





    share|improve this answer



























      up vote
      0
      down vote



      accepted










      At first I decided to use this solution and it worked as expected. But then I used solution that pskink advised me to use. It simpler, shorter and rather more correct then my previous choice. Example:



      interface OnMatrixChangeListener {
      fun onChange(theMatrix: Matrix)
      }

      class ChartView2(context: Context, attrs: AttributeSet?) : View(context, attrs), OnMatrixChangeListener {

      private var theMatrix = Matrix()
      private var detector = MatrixGestureDetector(theMatrix, this)
      private var paint = Paint()
      private var colors = intArrayOf(Color.RED, Color.GREEN, Color.BLUE)
      private var colorNames = arrayOf("RED", "GREEN", "BLUE")
      private var centers = arrayOf(PointF(100f, 100f), PointF(400f, 100f), PointF(250f, 360f))

      var inverse = Matrix()
      override fun onTouchEvent(event: MotionEvent): Boolean {
      if (event.action == MotionEvent.ACTION_DOWN) {
      theMatrix.invert(inverse)
      val pts = floatArrayOf(event.x, event.y)
      inverse.mapPoints(pts)
      for (i in colors.indices) {
      if (Math.hypot((pts[0] - centers[i].x).toDouble(), (pts[1] - centers[i].y).toDouble()) < 100)
      Log.d("abcd", colorNames[i] + " circle clicked")
      }
      }
      detector.onTouchEvent(event)
      return true
      }

      override fun onDraw(canvas: Canvas) {
      canvas.concat(theMatrix)
      for (i in colors.indices) {
      paint.color = colors[i]
      canvas.drawCircle(centers[i].x, centers[i].y, 100f, paint)
      }
      }

      override fun onChange(theMatrix: Matrix) {
      invalidate()
      }
      }

      internal class MatrixGestureDetector(private val mMatrix: Matrix, listener: OnMatrixChangeListener) {

      private var ptpIdx = 0
      private val mTempMatrix = Matrix()
      private val mListener: OnMatrixChangeListener?
      private val mSrc = FloatArray(4)
      private val mDst = FloatArray(4)
      private var mCount: Int = 0

      init {
      this.mListener = listener
      }

      fun onTouchEvent(event: MotionEvent) {
      if (event.pointerCount > 2) {
      return
      }

      val action = event.actionMasked
      val index = event.actionIndex

      when (action) {
      MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
      val idx = index * 2
      mSrc[idx] = event.getX(index)
      mSrc[idx + 1] = event.getY(index)
      mCount++
      ptpIdx = 0
      }

      MotionEvent.ACTION_MOVE -> {
      for (i in 0 until mCount) {
      val idx = ptpIdx + i * 2
      mDst[idx] = event.getX(i)
      mDst[idx + 1] = event.getY(i)
      }
      mTempMatrix.setPolyToPoly(mSrc, ptpIdx, mDst, ptpIdx, mCount)
      mMatrix.postConcat(mTempMatrix)
      mListener?.onChange(mMatrix)
      System.arraycopy(mDst, 0, mSrc, 0, mDst.size)
      }

      MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
      if (event.getPointerId(index) == 0) ptpIdx = 2
      mCount--
      }
      }
      }
      }





      share|improve this answer

























        up vote
        0
        down vote



        accepted







        up vote
        0
        down vote



        accepted






        At first I decided to use this solution and it worked as expected. But then I used solution that pskink advised me to use. It simpler, shorter and rather more correct then my previous choice. Example:



        interface OnMatrixChangeListener {
        fun onChange(theMatrix: Matrix)
        }

        class ChartView2(context: Context, attrs: AttributeSet?) : View(context, attrs), OnMatrixChangeListener {

        private var theMatrix = Matrix()
        private var detector = MatrixGestureDetector(theMatrix, this)
        private var paint = Paint()
        private var colors = intArrayOf(Color.RED, Color.GREEN, Color.BLUE)
        private var colorNames = arrayOf("RED", "GREEN", "BLUE")
        private var centers = arrayOf(PointF(100f, 100f), PointF(400f, 100f), PointF(250f, 360f))

        var inverse = Matrix()
        override fun onTouchEvent(event: MotionEvent): Boolean {
        if (event.action == MotionEvent.ACTION_DOWN) {
        theMatrix.invert(inverse)
        val pts = floatArrayOf(event.x, event.y)
        inverse.mapPoints(pts)
        for (i in colors.indices) {
        if (Math.hypot((pts[0] - centers[i].x).toDouble(), (pts[1] - centers[i].y).toDouble()) < 100)
        Log.d("abcd", colorNames[i] + " circle clicked")
        }
        }
        detector.onTouchEvent(event)
        return true
        }

        override fun onDraw(canvas: Canvas) {
        canvas.concat(theMatrix)
        for (i in colors.indices) {
        paint.color = colors[i]
        canvas.drawCircle(centers[i].x, centers[i].y, 100f, paint)
        }
        }

        override fun onChange(theMatrix: Matrix) {
        invalidate()
        }
        }

        internal class MatrixGestureDetector(private val mMatrix: Matrix, listener: OnMatrixChangeListener) {

        private var ptpIdx = 0
        private val mTempMatrix = Matrix()
        private val mListener: OnMatrixChangeListener?
        private val mSrc = FloatArray(4)
        private val mDst = FloatArray(4)
        private var mCount: Int = 0

        init {
        this.mListener = listener
        }

        fun onTouchEvent(event: MotionEvent) {
        if (event.pointerCount > 2) {
        return
        }

        val action = event.actionMasked
        val index = event.actionIndex

        when (action) {
        MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
        val idx = index * 2
        mSrc[idx] = event.getX(index)
        mSrc[idx + 1] = event.getY(index)
        mCount++
        ptpIdx = 0
        }

        MotionEvent.ACTION_MOVE -> {
        for (i in 0 until mCount) {
        val idx = ptpIdx + i * 2
        mDst[idx] = event.getX(i)
        mDst[idx + 1] = event.getY(i)
        }
        mTempMatrix.setPolyToPoly(mSrc, ptpIdx, mDst, ptpIdx, mCount)
        mMatrix.postConcat(mTempMatrix)
        mListener?.onChange(mMatrix)
        System.arraycopy(mDst, 0, mSrc, 0, mDst.size)
        }

        MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
        if (event.getPointerId(index) == 0) ptpIdx = 2
        mCount--
        }
        }
        }
        }





        share|improve this answer














        At first I decided to use this solution and it worked as expected. But then I used solution that pskink advised me to use. It simpler, shorter and rather more correct then my previous choice. Example:



        interface OnMatrixChangeListener {
        fun onChange(theMatrix: Matrix)
        }

        class ChartView2(context: Context, attrs: AttributeSet?) : View(context, attrs), OnMatrixChangeListener {

        private var theMatrix = Matrix()
        private var detector = MatrixGestureDetector(theMatrix, this)
        private var paint = Paint()
        private var colors = intArrayOf(Color.RED, Color.GREEN, Color.BLUE)
        private var colorNames = arrayOf("RED", "GREEN", "BLUE")
        private var centers = arrayOf(PointF(100f, 100f), PointF(400f, 100f), PointF(250f, 360f))

        var inverse = Matrix()
        override fun onTouchEvent(event: MotionEvent): Boolean {
        if (event.action == MotionEvent.ACTION_DOWN) {
        theMatrix.invert(inverse)
        val pts = floatArrayOf(event.x, event.y)
        inverse.mapPoints(pts)
        for (i in colors.indices) {
        if (Math.hypot((pts[0] - centers[i].x).toDouble(), (pts[1] - centers[i].y).toDouble()) < 100)
        Log.d("abcd", colorNames[i] + " circle clicked")
        }
        }
        detector.onTouchEvent(event)
        return true
        }

        override fun onDraw(canvas: Canvas) {
        canvas.concat(theMatrix)
        for (i in colors.indices) {
        paint.color = colors[i]
        canvas.drawCircle(centers[i].x, centers[i].y, 100f, paint)
        }
        }

        override fun onChange(theMatrix: Matrix) {
        invalidate()
        }
        }

        internal class MatrixGestureDetector(private val mMatrix: Matrix, listener: OnMatrixChangeListener) {

        private var ptpIdx = 0
        private val mTempMatrix = Matrix()
        private val mListener: OnMatrixChangeListener?
        private val mSrc = FloatArray(4)
        private val mDst = FloatArray(4)
        private var mCount: Int = 0

        init {
        this.mListener = listener
        }

        fun onTouchEvent(event: MotionEvent) {
        if (event.pointerCount > 2) {
        return
        }

        val action = event.actionMasked
        val index = event.actionIndex

        when (action) {
        MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
        val idx = index * 2
        mSrc[idx] = event.getX(index)
        mSrc[idx + 1] = event.getY(index)
        mCount++
        ptpIdx = 0
        }

        MotionEvent.ACTION_MOVE -> {
        for (i in 0 until mCount) {
        val idx = ptpIdx + i * 2
        mDst[idx] = event.getX(i)
        mDst[idx + 1] = event.getY(i)
        }
        mTempMatrix.setPolyToPoly(mSrc, ptpIdx, mDst, ptpIdx, mCount)
        mMatrix.postConcat(mTempMatrix)
        mListener?.onChange(mMatrix)
        System.arraycopy(mDst, 0, mSrc, 0, mDst.size)
        }

        MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
        if (event.getPointerId(index) == 0) ptpIdx = 2
        mCount--
        }
        }
        }
        }






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 12 at 18:15

























        answered Nov 11 at 9:30









        badadin

        6116




        6116






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53236594%2fwrong-coordinates-after-scaling-canvas%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            List item for chat from Array inside array React Native

            Thiostrepton

            Caerphilly