如何在 Python 中使用补间,而不会失去准确性?

Nao*_*omi 6 python smoothing easing

我一直在努力使用补间来使 Python 中的鼠标移动平滑,我目前正在尝试自动化一些重复性任务。

我尝试使用补间去除一些在没有应用平滑的情况下发生的粗糙度,但是这样做我失去了明显的准确性,因为我的dydx值被number我最终得到了余数。这可能会因获得可以解决greatest common factor在我的两个值(因为两者dxdy需要由同一分number)不幸的是这导致了过小的GCD的。

由于鼠标无法移动屏幕上像素的其余部分,因此我最终会明显降低精度。

问题:如何对鼠标移动应用补间,而不会失去准确性?

import pytweening
import win32api
import win32con
from time import sleep

dy = [50, 46, 42, 38, 33, 29, 24, 20, 15, 10, 10]
dx = [-35, 6, -55, -43, 0, 17, 29, 38, 42, 42, 38]

while True:

    count = 0

    values = [(pytweening.getPointOnLine(0, 0, x, y, 0.20)) for x, y in zip(dx, dy)]

    while win32api.GetAsyncKeyState(win32con.VK_RBUTTON) and win32api.GetAsyncKeyState(win32con.VK_LBUTTON):

        if count < len(dx):

            for _ in range(5):
                win32api.mouse_event(1, int(values[count][0]), int(values[count][1]), 0, 0)
                sleep(0.134 / 5)

            count += 1
Run Code Online (Sandbox Code Playgroud)

cht*_*mon 5

这里的根本问题是您使用的是整数量的相对运动,这不会合计到您正在寻找的总运动量。如果你只想线性移动,你也根本不需要 PyTweening。这个解决方案怎么样?

import win32api
import win32con
from time import sleep

Npoints = 5
sleeptime = 0.134 / Npoints

dys = [50, 46, 42, 38, 33, 29, 24, 20, 15, 10, 10]
dxs = [-35, 6, -55, -43, 0, 17, 29, 38, 42, 42, 38]

x, y = win32api.GetCursorPos()

for dx, dy in zip(dxs, dys):
    ddx = dx/Npoints
    ddy = dy/Npoints
    for _ in range(Npoints):
        x += ddx
        y += ddy

        win32api.SetCursorPos(int(x), int(y))
        sleep(sleeptime)
Run Code Online (Sandbox Code Playgroud)

请注意,仍然会有一些非常小的舍入误差,并且光标将在点之间沿直线移动。如果光标从 (0, 0) 开始,这就是它将形成的形状(红色十字是光标将设置到的点):

鼠标移动的形状

如果您想通过点以平滑曲线移动并且可以使用 numpy 和 scipy,那么这将处理该问题:

import numpy as np
import scipy.interpolate as sci

totalpoints = 50  # you can set this to a larger number to get closer spaced points
x, y = win32api.GetCursorPos()

# work out absolute coordinates of new points
xs = np.cumsum([x, *dxs])
ys = np.cumsum([y, *dys])

# fit spline between the points (s=0 makes the spline hit all the points)
tck, u = sci.splprep([xs, ys], s=0)

# Evaluate the spline and move to those points
for x, y in zip(*sci.splev(np.linspace(0, 1, totalpoints), tck)):
    win32api.SetCursorPos(int(x), int(y))
    sleep(sleeptime)
Run Code Online (Sandbox Code Playgroud)

结果如下所示:

点之间的样条插值


sto*_*vfl 1

问题:补间,而不损失准确性?

参考

  • PyTweening -getLinePoint()

    x, y = getLinePoint(startPoint x, startPoint y, endPoint x, endPoint y, intervall)

    getLinePoint()函数在所提供的线上找到一个点。


  1. 将您的列表dx和投射dy到以下列表中tuple(x, y)

    dx = [-35, 6, -55, -43, 0, 17, 29, 38, 42, 42, 38]
    dy = [50, 46, 42, 38, 33, 29, 24, 20, 15, 10, 10]
    
    points = list(zip(dx, dy))
    print(points)
    
    Run Code Online (Sandbox Code Playgroud)

    输出

    [(-35, 50), (6, 46), (-55, 42), (-43, 38), (0, 33), (17, 29), (29, 24), (38, 20), (42, 15), (42, 10), (38, 10)]
    
    Run Code Online (Sandbox Code Playgroud)
  2. points在双循环中处理这个列表for

    import pytweening
    
    for startPoint in points:
        for endPoint in points:
            x, y = pytweening.getPointOnLine(startPoint[0], startPoint[1],
                                             endPoint[0], endPoint[1], 
                                             0.20)
            x, y = int(x), int(y)
            print('{}, '.format((x, y)), end='')
    
            # win32api.mouse_event(1, x, y, 0, 0)
            # sleep(0.134)
    
    Run Code Online (Sandbox Code Playgroud)

    输出终点总是到达!

    First move from (-35, 50) to (6, 46):
    (-35, 50), (-26, 49), (-39, 48), (-36, 47), (-28, 46), (-24, 45),(-22, 44),
    (-20, 44), (-19, 43), (-19, 42), (-20, 42), (-2, 46), (6, 46)
    
    ... (omitted for brevity)
    
    Last move from (42, 10) to (38, 10):  
    (42, 10), (41, 10), (23, 18), (31, 17), (19, 16), (21, 15), (30, 14),
    (33, 13), (36, 12), (38, 12), (38, 11), (38, 10), (38, 10)
    
    Run Code Online (Sandbox Code Playgroud)

使用 Python 测试:3.6 - pytweening:1.0.3