# frozen_string_literal: true require_relative "./detector" module Fusuma module Plugin module Detectors class RotateDetector < Detector SOURCES = ["gesture"].freeze BUFFER_TYPE = "gesture" GESTURE_RECORD_TYPE = "pinch" FINGERS = [2, 3, 4].freeze BASE_THERESHOLD = 0.5 # @param buffers [Array] # @return [Events::Event] if event is detected # @return [NilClass] if event is NOT detected def detect(buffers) gesture_buffer = buffers.find { |b| b.type == BUFFER_TYPE } .select_from_last_begin .select_by_type(GESTURE_RECORD_TYPE) updating_events = gesture_buffer.updating_events return if updating_events.empty? updating_time = 100 * (updating_events[-1].time - (updating_events[-10] || updating_events.first).time) oneshot_angle = gesture_buffer.sum_last10_attrs(:rotate) / updating_time finger = gesture_buffer.finger status = case gesture_buffer.events.last.record.status when "end" "end" when "update" if updating_events.length == 1 "begin" else "update" end else gesture_buffer.events.last.record.status end delta = if status == "end" gesture_buffer.events[-2].record.delta else gesture_buffer.events.last.record.delta end repeat_direction = Direction.new(angle: delta.rotate).to_s repeat_quantity = Quantity.new(angle: delta.rotate).to_f repeat_index = create_repeat_index(gesture: type, finger: finger, direction: repeat_direction, status: status) if status == "update" return unless moved?(repeat_quantity) oneshot_direction = Direction.new(angle: oneshot_angle).to_s oneshot_quantity = Quantity.new(angle: oneshot_angle).to_f oneshot_index = create_oneshot_index(gesture: type, finger: finger, direction: oneshot_direction) if enough_oneshot_threshold?(index: oneshot_index, quantity: oneshot_quantity) return [ create_event(record: Events::Records::IndexRecord.new( index: oneshot_index, trigger: :oneshot, args: delta.to_h )), create_event(record: Events::Records::IndexRecord.new( index: repeat_index, trigger: :repeat, args: delta.to_h )) ] end end create_event(record: Events::Records::IndexRecord.new( index: repeat_index, trigger: :repeat, args: delta.to_h )) end # @param [String] gesture # @param [Integer] finger # @param [String] direction # @param [String] status # @return [Config::Index] def create_repeat_index(gesture:, finger:, direction:, status:) Config::Index.new( [ Config::Index::Key.new(gesture), Config::Index::Key.new(finger.to_i), Config::Index::Key.new(direction), Config::Index::Key.new(status) ] ) end # @param [String] gesture # @param [Integer] finger # @param [String] direction # @return [Config::Index] def create_oneshot_index(gesture:, finger:, direction:) Config::Index.new( [ Config::Index::Key.new(gesture), Config::Index::Key.new(finger.to_i, skippable: true), Config::Index::Key.new(direction) ] ) end private def moved?(repeat_quantity) repeat_quantity > 0.2 end def enough_oneshot_threshold?(index:, quantity:) quantity > threshold(index: index) end def threshold(index:) @threshold ||= {} @threshold[index.cache_key] ||= begin keys_specific = Config::Index.new [*index.keys, "threshold"] keys_global = Config::Index.new ["threshold", type] config_value = Config.search(keys_specific) || Config.search(keys_global) || 1 BASE_THERESHOLD * config_value end end # direction of gesture class Direction CLOCKWISE = "clockwise" COUNTERCLOCKWISE = "counterclockwise" def initialize(angle:) @angle = angle.to_f end def to_s calc end def calc if @angle.positive? CLOCKWISE else COUNTERCLOCKWISE end end end # quantity of gesture class Quantity def initialize(angle:) @angle = angle.abs end def to_f @angle.to_f end end end end end end