1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # Two fingers camera manipulation, scroll and pinch to zoom
17 # Provides the main service `EulerCamera::accept_two_fingers_motion`.
18 module android_two_fingers_motion
20 # TODO add an abstraction when another platform supports it
26 redef class EulerCamera
27 # Smoothened history of pointers in the current motion event
28 private var last_motion_pointers
= new HashMap[Int, Point[Float]] is lazy
30 # Start time of the current motion event
31 private var last_motion_start
: Int = -1
33 # Move and zoom (set `position`) from a two finger pinch and slide option
35 # Returns `true` if the event is intercepted.
37 # Should be called from `App::accept_event` before accepting pointer events:
41 # redef fun accept_event(event)
43 # if world_camera.accept_two_fingers_motion(event) then return true
45 # # Handle other events...
49 fun accept_two_fingers_motion
(event
: InputEvent): Bool
51 if not event
isa AndroidMotionEvent then return false
53 if event
.pointers
.length
< 2 then
54 # Intercept leftovers of the last motion
55 return event
.down_time
== last_motion_start
58 # Collect active pointer and their world position
59 var new_motion_pointers
= new HashMap[Int, Point[Float]]
60 var ids
= new Array[Int]
61 for pointer
in event
.pointers
do
62 var id
= pointer
.pointer_id
64 new_motion_pointers
[id
] = camera_to_world
(pointer
.x
, pointer
.y
)
67 var last_motion_pointers
= last_motion_pointers
68 if last_motion_start
== event
.down_time
and
69 last_motion_pointers
.keys
.has
(ids
[0]) and last_motion_pointers
.keys
.has
(ids
[1]) then
70 # Continued motion event
72 # Get new and old position for 2 fingers
73 var new_motion_a
= new_motion_pointers
[ids
[0]]
74 var new_motion_b
= new_motion_pointers
[ids
[1]]
75 var prev_pos_a
= last_motion_pointers
[ids
[0]]
76 var prev_pos_b
= last_motion_pointers
[ids
[1]]
79 var prev_middle_pos
= prev_pos_a
.lerp
(prev_pos_b
, 0.5)
80 var new_middle_pos
= new_motion_a
.lerp
(new_motion_b
, 0.5)
81 position
.x
-= new_middle_pos
.x
- prev_middle_pos
.x
82 position
.y
-= new_middle_pos
.y
- prev_middle_pos
.y
85 var prev_dist
= prev_pos_a
.dist
(prev_pos_b
)
86 var new_dist
= new_motion_a
.dist
(new_motion_b
)
88 position
.z
= prev_dist
* position
.z
/ new_dist
90 # Prepare for a new motion event
91 last_motion_pointers
.clear
92 last_motion_start
= event
.down_time
95 # Keep a smooth history
97 if last_motion_pointers
.keys
.has
(ids
[i
]) then
98 last_motion_pointers
[ids
[i
]] = last_motion_pointers
[ids
[i
]]*0.5 +
99 new_motion_pointers
[ids
[i
]]*0.5
100 else last_motion_pointers
[ids
[i
]] = new_motion_pointers
[ids
[i
]]
108 private fun *(scalar
: Numeric): Point[N
]
109 do return new Point[N
](x
.mul
(scalar
), y
.mul
(scalar
))
111 private fun +(other
: Point[N
]): Point[N
]
112 do return new Point[N
](x
.add
(other
.x
), y
.add
(other
.y
))