blob: 2c15c0dc48619a87326a35c03ea02332e80250e5 [file] [log] [blame]
Dan Sandler45f17c52018-05-02 20:01:38 -04001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.egg.paint
18
19import java.util.LinkedList
20
21import android.view.MotionEvent
22
23class SpotFilter(internal var mBufSize: Int, posDecay: Float, pressureDecay: Float, internal var mPlotter: Plotter) {
24 val spots = LinkedList<MotionEvent.PointerCoords>() // newest at front
25 val tmpSpot = MotionEvent.PointerCoords()
26 var lastTool = MotionEvent.TOOL_TYPE_UNKNOWN
27
28 val posDecay: Float
29 val pressureDecay: Float
30
31 interface Plotter {
32 fun plot(s: MotionEvent.PointerCoords)
33 }
34
35 init {
36 this.posDecay = if (posDecay in 0f..1f) posDecay else 1f
37 this.pressureDecay = if (pressureDecay in 0f..1f) pressureDecay else 1f
38 }
39
40 fun filterInto(out: MotionEvent.PointerCoords, tool: Int): MotionEvent.PointerCoords {
41 lastTool = tool
42
43 var wi = 1f // weight for ith component (position)
44 var w = 0f // total weight
45 var wi_press = 1f // weight for ith component (pressure)
46 var w_press = 0f // total weight (pressure)
47
48 var x = 0f
49 var y = 0f
50 var pressure = 0f
51 var size = 0f
52 for (pi in spots) {
53 x += pi.x * wi
54 y += pi.y * wi
55
56 pressure += pi.pressure * wi_press
57 size += pi.size * wi_press
58
59 w += wi
60 wi *= posDecay // exponential backoff
61
62 w_press += wi_press
63 wi_press *= pressureDecay
64
65 if (PRECISE_STYLUS_INPUT && tool == MotionEvent.TOOL_TYPE_STYLUS) {
66 // just take the newest one, no need to average
67 break
68 }
69 }
70
71 out.x = x / w
72 out.y = y / w
73 out.pressure = pressure / w_press
74 out.size = size / w_press
75 return out
76 }
77
78 protected fun addInternal(c: MotionEvent.PointerCoords, tool: Int) {
79 val coord =
80 if (spots.size == mBufSize) {
81 spots.removeLast()
82 } else {
83 MotionEvent.PointerCoords()
84 }
85 coord.copyFrom(c)
86
87 spots.add(0, coord)
88
89 filterInto(tmpSpot, tool)
90 mPlotter.plot(tmpSpot)
91 }
92
93 fun add(cv: List<MotionEvent.PointerCoords>, tool: Int) {
94 for (c in cv) {
95 addInternal(c, tool)
96 }
97 }
98
99 fun add(evt: MotionEvent) {
100 val tool = evt.getToolType(0)
101 for (i in 0 until evt.historySize) {
102 evt.getHistoricalPointerCoords(0, i, tmpSpot)
103 addInternal(tmpSpot, tool)
104 }
105 evt.getPointerCoords(0, tmpSpot)
106 addInternal(tmpSpot, tool)
107 }
108
109 fun finish() {
110 while (spots.size > 0) {
111 filterInto(tmpSpot, lastTool)
112 spots.removeLast()
113 mPlotter.plot(tmpSpot)
114 }
115
116 spots.clear()
117 }
118
119 companion object {
120 var PRECISE_STYLUS_INPUT = true
121 }
122}
123
124