From c30c1ff11f35c630b5967e86a5403354fb04aa88 Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Fri, 15 Dec 2023 00:16:34 +0100 Subject: [PATCH] DeviceListener: Rejig calculateGestureDelta algorithm Get rid of the silly precomputed tables, and do More Maths(TM) instead! Thanks to @zwim for the magic sauce ;). Minor simplification of the API while I'm in there, and unify the warmth computations, do everything in the native scale (much like what effectively happens for intensity) to workaround the silly public API being an unhelpful PITA, ensuring consistent & effective changes. --- frontend/device/devicelistener.lua | 68 +++++++++++------------------- 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/frontend/device/devicelistener.lua b/frontend/device/devicelistener.lua index 8e02a5fb5..2235fc052 100644 --- a/frontend/device/devicelistener.lua +++ b/frontend/device/devicelistener.lua @@ -55,68 +55,54 @@ if Device:hasFrontlight() then local function calculateGestureDelta(ges, direction, min, max) local delta_int if type(ges) == "table" then - -- here we are using just two scales - -- big scale is for high dynamic ranges (e.g. brightness from 1..100) - -- original scale maybe tuned by hand - -- small scale is for lower dynamic ranges (e.g. warmth from 1..10) - -- scale entries are calculated by math.round(1*sqrt(2)^n) - --- @fixme: An intermediary scale is probably necessary for Kindle, which goes from 0 to 24... - local steps_fl_big_scale = { 0.1, 0.1, 0.2, 0.4, 0.7, 1.1, 1.6, 2.2, 2.9, 3.7, 4.6, 5.6, 6.7, 7.9, 9.2, 10.6, } - local steps_fl_small_scale = { 1.0, 1.0, 2.0, 3.0, 4.0, 6.0, 8.1, 11.3 } - local steps_fl = steps_fl_big_scale - if (max - min) < 50 then - steps_fl = steps_fl_small_scale - end - local gestureScale - local scale_multiplier + local gesture_multiplier if ges.ges == "two_finger_swipe" or ges.ges == "swipe" then - scale_multiplier = 0.8 + gesture_multiplier = 0.8 else - scale_multiplier = 1 + gesture_multiplier = 1 end + local gestureScale if ges.direction == "south" or ges.direction == "north" then - gestureScale = Screen:getHeight() * scale_multiplier + gestureScale = Screen:getHeight() * gesture_multiplier elseif ges.direction == "west" or ges.direction == "east" then - gestureScale = Screen:getWidth() * scale_multiplier + gestureScale = Screen:getWidth() * gesture_multiplier else local width = Screen:getWidth() local height = Screen:getHeight() -- diagonal - gestureScale = math.sqrt(width * width + height * height) * scale_multiplier - end - - local steps_tbl = {} - local scale = (max - min) / steps_fl[#steps_fl] / 2 -- full swipe gives half scale - for i = 1, #steps_fl, 1 do - steps_tbl[i] = math.ceil(steps_fl[i] * scale) + gestureScale = math.sqrt(width^2 + height^2) * gesture_multiplier end + -- In case we're passed a gesture that doesn't imply movement (e.g., tap or hold) if ges.distance == nil then ges.distance = 1 end - local step = math.ceil(#steps_tbl * ges.distance / gestureScale) - delta_int = steps_tbl[step] or steps_tbl[#steps_tbl] + -- delta_int is calculated by a function f(x) = coeff * x^2 + -- *) f(x) has the boundary condition: f(1) = max/2; + -- *) x is roughly the swipe distance as a fraction of the screen geometry, + -- clamped between 0 and 1 + local x = math.min(1, ges.distance / gestureScale) + delta_int = math.ceil(1/2 * max * x^2) else - -- received amount to change + -- The ges arg passed by our caller wasn't a gesture, but an absolute integer increment delta_int = ges end if direction ~= -1 and direction ~= 1 then - -- set default value (increase frontlight) + -- If the caller didn't specify, opt to *increase* by default direction = 1 end - return direction, delta_int + return direction * delta_int end -- direction +1 - increase frontlight -- direction -1 - decrease frontlight function DeviceListener:onChangeFlIntensity(ges, direction) local powerd = Device:getPowerDevice() - local delta_int - direction, delta_int = calculateGestureDelta(ges, direction, powerd.fl_min, powerd.fl_max) + local delta = calculateGestureDelta(ges, direction, powerd.fl_min, powerd.fl_max) - local new_intensity = powerd:frontlightIntensity() + direction * delta_int + local new_intensity = powerd:frontlightIntensity() + delta -- when new_intensity <= 0, toggle light off self:onSetFlIntensity(new_intensity) self:onShowIntensity() @@ -149,17 +135,13 @@ if Device:hasFrontlight() then if not Device:hasNaturalLight() then return true end local powerd = Device:getPowerDevice() - local delta_int - direction, delta_int = calculateGestureDelta(ges, direction, powerd.fl_warmth_min, powerd.fl_warmth_max) + local delta = calculateGestureDelta(ges, direction, powerd.fl_warmth_min, powerd.fl_warmth_max) - local warmth - if type(ges) == "table" then - -- received a gesture, scale the gesture delta to the API range - warmth = powerd:frontlightWarmth() + powerd:fromNativeWarmth(direction * delta_int) - else - -- received an absolute increment, use it as-is in the native scale - warmth = powerd:fromNativeWarmth(powerd:toNativeWarmth(powerd:frontlightWarmth()) + ges) - end + -- Given that the native warmth ranges are usually pretty restrictive (e.g., [0, 10] or [0, 24]), + -- do the computations in the native scale, to ensure we always actually *change* something, + -- in case both the old and new value would round to the same native step, + -- despite being different in the API scale, which is stupidly fixed at [0, 100]... + local warmth = powerd:fromNativeWarmth(powerd:toNativeWarmth(powerd:frontlightWarmth()) + delta) self:onSetFlWarmth(warmth) self:onShowWarmth()