gamnit: intro `camera_control` an abstraction of `accept_two_fingers_motion`
[nit.git] / lib / gamnit / camera_control_android.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Two fingers camera manipulation, pinch to zoom and slide to scroll
16 module camera_control_android
17
18 import android
19 import camera_control
20
21 redef class EulerCamera
22 # Smoothened history of pointers in the current motion event
23 private var last_motion_pointers = new HashMap[Int, Point[Float]] is lazy
24
25 # Start time of the current motion event
26 private var last_motion_start: Int = -1
27
28 redef fun accept_scroll_and_zoom(event)
29 do
30 if not event isa AndroidMotionEvent then return false
31
32 if event.pointers.length < 2 then
33 # Intercept leftovers of the last motion
34 return event.down_time == last_motion_start
35 end
36
37 # Collect active pointer and their world position
38 var new_motion_pointers = new HashMap[Int, Point[Float]]
39 var ids = new Array[Int]
40 for pointer in event.pointers do
41 var id = pointer.pointer_id
42 ids.add id
43 new_motion_pointers[id] = camera_to_world(pointer.x, pointer.y)
44 end
45
46 var last_motion_pointers = last_motion_pointers
47 if last_motion_start == event.down_time and
48 last_motion_pointers.keys.has(ids[0]) and last_motion_pointers.keys.has(ids[1]) then
49 # Continued motion event
50
51 # Get new and old position for 2 fingers
52 var new_motion_a = new_motion_pointers[ids[0]]
53 var new_motion_b = new_motion_pointers[ids[1]]
54 var prev_pos_a = last_motion_pointers[ids[0]]
55 var prev_pos_b = last_motion_pointers[ids[1]]
56
57 # Move camera
58 var prev_middle_pos = prev_pos_a.lerp(prev_pos_b, 0.5)
59 var new_middle_pos = new_motion_a.lerp(new_motion_b, 0.5)
60 position.x -= new_middle_pos.x - prev_middle_pos.x
61 position.y -= new_middle_pos.y - prev_middle_pos.y
62
63 # Zoom camera
64 var prev_dist = prev_pos_a.dist(prev_pos_b)
65 var new_dist = new_motion_a.dist(new_motion_b)
66
67 position.z = prev_dist * position.z / new_dist
68 else
69 # Prepare for a new motion event
70 last_motion_pointers.clear
71 last_motion_start = event.down_time
72 end
73
74 # Keep a smooth history
75 for i in [0..1] do
76 if last_motion_pointers.keys.has(ids[i]) then
77 last_motion_pointers[ids[i]] = last_motion_pointers[ids[i]]*0.5 +
78 new_motion_pointers[ids[i]]*0.5
79 else last_motion_pointers[ids[i]] = new_motion_pointers[ids[i]]
80 end
81
82 return true
83 end
84 end
85
86 redef class Point[N]
87 private fun *(scalar: Numeric): Point[N]
88 do return new Point[N](x.mul(scalar), y.mul(scalar))
89
90 private fun +(other: Point[N]): Point[N]
91 do return new Point[N](x.add(other.x), y.add(other.y))
92 end