|  | // Copyright 2018 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "src/lib/ui/input/gesture.h" | 
|  |  | 
|  | namespace input { | 
|  |  | 
|  | Gesture::Delta& Gesture::Delta::operator+=(const Delta& other) { | 
|  | translation += other.translation; | 
|  | rotation += other.rotation; | 
|  | scale *= other.scale; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | bool Gesture::Delta::operator==(const Delta& other) const { | 
|  | return translation == other.translation && rotation == other.rotation && scale == other.scale; | 
|  | } | 
|  |  | 
|  | void Gesture::AddPointer(PointerId pointer_id, const glm::vec2& position) { | 
|  | // TODO(fxbug.dev/24596): This is sometimes violated. | 
|  | // FX_DCHECK(pointers_.find(pointer_id) == pointers_.end()); | 
|  |  | 
|  | pointers_[pointer_id] = {.absolute = position, .relative = {}, .distance = 0}; | 
|  | UpdateCentroid(); | 
|  | UpdateRelative(); | 
|  | } | 
|  |  | 
|  | Gesture::Delta Gesture::UpdatePointer(PointerId pointer_id, const glm::vec2& position) { | 
|  | // TODO(fxbug.dev/24596): This is sometimes violated. | 
|  | // FX_DCHECK(pointers_.find(pointer_id) != pointers_.end()); | 
|  | auto it = pointers_.find(pointer_id); | 
|  | if (it == pointers_.end()) { | 
|  | AddPointer(pointer_id, position); | 
|  | return {.translation = {}, .rotation = 0, .scale = 1}; | 
|  | } | 
|  |  | 
|  | it->second.absolute = position; | 
|  |  | 
|  | Delta delta; | 
|  |  | 
|  | { | 
|  | glm::vec2 old_centroid = centroid_; | 
|  | UpdateCentroid(); | 
|  | delta.translation = centroid_ - old_centroid; | 
|  | } | 
|  |  | 
|  | if (pointers_.size() > 1) { | 
|  | float moment_sum = 0; | 
|  | // Use an arithmetic mean as a decent approximation of a geometric mean. | 
|  | float scale_sum = 0; | 
|  |  | 
|  | for (auto& entry : pointers_) { | 
|  | PointerInfo& p = entry.second; | 
|  |  | 
|  | glm::vec2 old_relative = p.relative; | 
|  | float old_distance = p.distance; | 
|  | p.relative = p.absolute - centroid_; | 
|  | p.distance = glm::length(p.relative); | 
|  |  | 
|  | // TODO(rosswang): The following has singular behavior when pointers have the same | 
|  | // coordinates, which can in particular happen during testing. In such cases while it makes | 
|  | // mathematical sense for rotation and scale to be NaN/infinite, perhaps we should default | 
|  | // them to identity values. | 
|  |  | 
|  | // For small displacements, this approximates radians. | 
|  | delta.rotation += | 
|  | (old_relative.x * p.relative.y - old_relative.y * p.relative.x) / old_distance; | 
|  | moment_sum += old_distance; | 
|  | scale_sum += p.distance / old_distance; | 
|  | } | 
|  |  | 
|  | delta.rotation /= moment_sum; | 
|  | delta.scale *= scale_sum / pointers_.size(); | 
|  | } | 
|  |  | 
|  | return delta; | 
|  | } | 
|  |  | 
|  | void Gesture::RemovePointer(PointerId pointer_id) { | 
|  | pointers_.erase(pointer_id); | 
|  |  | 
|  | if (!pointers_.empty()) { | 
|  | UpdateCentroid(); | 
|  | UpdateRelative(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Gesture::UpdateCentroid() { | 
|  | // It would be more efficient to do this incrementally at the possible cost of precision. Gestures | 
|  | // tend to be both short and with a small number of pointers, so neither the efficiency nor | 
|  | // precision is particularly important. However, edge cases like new pointers are easier to deal | 
|  | // with if we always recalculate, especially with fxbug.dev/24596. | 
|  | centroid_ = {0, 0}; | 
|  | for (const auto& entry : pointers_) { | 
|  | centroid_ += entry.second.absolute; | 
|  | } | 
|  | centroid_ /= pointers_.size(); | 
|  | } | 
|  |  | 
|  | void Gesture::UpdateRelative() { | 
|  | for (auto& entry : pointers_) { | 
|  | PointerInfo& p = entry.second; | 
|  | p.relative = p.absolute - centroid_; | 
|  | p.distance = glm::length(p.relative); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace input |