+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Two fingers camera manipulation, scroll and pinch to zoom
-#
-# Provides the main service `EulerCamera::accept_two_fingers_motion`.
-module android_two_fingers_motion
-
-# TODO add an abstraction when another platform supports it
-
-import gamnit
-import cameras
-import android
-
-redef class EulerCamera
- # Smoothened history of pointers in the current motion event
- private var last_motion_pointers = new HashMap[Int, Point[Float]] is lazy
-
- # Start time of the current motion event
- private var last_motion_start: Int = -1
-
- # Move and zoom (set `position`) from a two finger pinch and slide option
- #
- # Returns `true` if the event is intercepted.
- #
- # Should be called from `App::accept_event` before accepting pointer events:
- #
- # ~~~nitish
- # redef class App
- # redef fun accept_event(event)
- # do
- # if world_camera.accept_two_fingers_motion(event) then return true
- #
- # # Handle other events...
- # end
- # end
- # ~~~
- fun accept_two_fingers_motion(event: InputEvent): Bool
- do
- if not event isa AndroidMotionEvent then return false
-
- if event.pointers.length < 2 then
- # Intercept leftovers of the last motion
- return event.down_time == last_motion_start
- end
-
- # Collect active pointer and their world position
- var new_motion_pointers = new HashMap[Int, Point[Float]]
- var ids = new Array[Int]
- for pointer in event.pointers do
- var id = pointer.pointer_id
- ids.add id
- new_motion_pointers[id] = camera_to_world(pointer.x, pointer.y)
- end
-
- var last_motion_pointers = last_motion_pointers
- if last_motion_start == event.down_time and
- last_motion_pointers.keys.has(ids[0]) and last_motion_pointers.keys.has(ids[1]) then
- # Continued motion event
-
- # Get new and old position for 2 fingers
- var new_motion_a = new_motion_pointers[ids[0]]
- var new_motion_b = new_motion_pointers[ids[1]]
- var prev_pos_a = last_motion_pointers[ids[0]]
- var prev_pos_b = last_motion_pointers[ids[1]]
-
- # Move camera
- var prev_middle_pos = prev_pos_a.lerp(prev_pos_b, 0.5)
- var new_middle_pos = new_motion_a.lerp(new_motion_b, 0.5)
- position.x -= new_middle_pos.x - prev_middle_pos.x
- position.y -= new_middle_pos.y - prev_middle_pos.y
-
- # Zoom camera
- var prev_dist = prev_pos_a.dist(prev_pos_b)
- var new_dist = new_motion_a.dist(new_motion_b)
-
- position.z = prev_dist * position.z / new_dist
- else
- # Prepare for a new motion event
- last_motion_pointers.clear
- last_motion_start = event.down_time
- end
-
- # Keep a smooth history
- for i in [0..1] do
- if last_motion_pointers.keys.has(ids[i]) then
- last_motion_pointers[ids[i]] = last_motion_pointers[ids[i]]*0.5 +
- new_motion_pointers[ids[i]]*0.5
- else last_motion_pointers[ids[i]] = new_motion_pointers[ids[i]]
- end
-
- return true
- end
-end
-
-redef class Point[N]
- private fun *(scalar: Numeric): Point[N]
- do return new Point[N](x.mul(scalar), y.mul(scalar))
-
- private fun +(other: Point[N]): Point[N]
- do return new Point[N](x.add(other.x), y.add(other.y))
-end