Refactor velocity sensor with tools::VelocitySensor class

This commit is contained in:
David Capello 2020-04-24 11:19:35 -03:00
parent bb07d25025
commit c99a566635
13 changed files with 154 additions and 54 deletions

View File

@ -589,6 +589,7 @@ add_library(app-lib
tools/symmetries.cpp
tools/tool_box.cpp
tools/tool_loop_manager.cpp
tools/velocity.cpp
transaction.cpp
transformation.cpp
ui/editor/tool_loop_impl.cpp

View File

@ -331,6 +331,7 @@ int App_useTool(lua_State* L)
tools::Pointer pointer(
pt,
// TODO configurable params
tools::Vec2(0.0f, 0.0f),
tools::Pointer::Button::Left,
tools::Pointer::Type::Unknown,
0.0f);

View File

@ -9,12 +9,15 @@
#define APP_TOOLS_POINTER_H_INCLUDED
#pragma once
#include "base/vector2d.h"
#include "gfx/point.h"
#include "ui/pointer_type.h"
namespace app {
namespace tools {
using Vec2 = base::Vector2d<float>;
// Simple container of mouse events information.
class Pointer {
public:
@ -23,26 +26,31 @@ public:
Pointer()
: m_point(0, 0)
, m_velocity(0.0, 0.0)
, m_button(None)
, m_type(Type::Unknown)
, m_pressure(0.0f) { }
Pointer(const gfx::Point& point,
const Vec2& velocity,
const Button button,
const Type type,
const float pressure)
: m_point(point)
, m_velocity(velocity)
, m_button(button)
, m_type(type)
, m_pressure(pressure) { }
const gfx::Point& point() const { return m_point; }
const Vec2& velocity() const { return m_velocity; }
Button button() const { return m_button; }
Type type() const { return m_type; }
float pressure() const { return m_pressure; }
private:
gfx::Point m_point;
Vec2 m_velocity;
Button m_button;
Type m_type;
float m_pressure;

View File

@ -19,6 +19,7 @@
#include "app/tools/point_shape.h"
#include "app/tools/symmetry.h"
#include "app/tools/tool_loop.h"
#include "app/tools/velocity.h"
#include "base/clamp.h"
#include "doc/brush.h"
#include "doc/image.h"
@ -367,29 +368,11 @@ void ToolLoopManager::calculateDirtyArea(const Strokes& strokes)
Stroke::Pt ToolLoopManager::getSpriteStrokePt(const Pointer& pointer,
const bool firstPoint)
{
const base::tick_t t = base::current_tick();
const base::tick_t dt = t - m_lastPointerT;
m_lastPointerT = t;
// Convert the screen point to a sprite point
Stroke::Pt spritePoint = pointer.point();
spritePoint.size = m_brush0.size();
spritePoint.angle = m_brush0.angle();
// Calculate the velocity (new sprite point - old sprite point)
gfx::Point newVelocity;
if (firstPoint)
m_velocity = newVelocity = gfx::Point(0, 0);
else {
newVelocity.x = (spritePoint.x - m_oldPoint.x);
newVelocity.y = (spritePoint.y - m_oldPoint.y);
float a = base::clamp(float(dt) / 50.0f, 0.0f, 1.0f);
m_velocity.x = (1.0f-a)*m_velocity.x + a*newVelocity.x;
m_velocity.y = (1.0f-a)*m_velocity.y + a*newVelocity.y;
}
m_oldPoint.x = spritePoint.x;
m_oldPoint.y = spritePoint.y;
// Center the input to some grid point if needed
snapToGrid(spritePoint);
@ -399,7 +382,8 @@ Stroke::Pt ToolLoopManager::getSpriteStrokePt(const Pointer& pointer,
}
// Inform the original velocity vector to the ToolLoop
m_toolLoop->setSpeed(newVelocity);
m_toolLoop->setSpeed(gfx::Point(pointer.velocity().x,
pointer.velocity().y));
return spritePoint;
}
@ -441,8 +425,7 @@ void ToolLoopManager::adjustPointWithDynamics(const Pointer& pointer,
p = base::clamp(p, 0.0f, 1.0f);
// Velocity
float v = float(std::sqrt(m_velocity.x*m_velocity.x +
m_velocity.y*m_velocity.y)) / 32.0f; // TODO 32 should be configurable
float v = pointer.velocity().magnitude() / VelocitySensor::kScreenPixelsForFullVelocity;
v = base::clamp(v, 0.0f, 1.0f);
if (v < m_dynamics.minVelocityThreshold) {
v = 0.0f;

View File

@ -12,7 +12,6 @@
#include "app/tools/dynamics.h"
#include "app/tools/pointer.h"
#include "app/tools/stroke.h"
#include "base/time.h"
#include "doc/brush.h"
#include "gfx/point.h"
#include "gfx/region.h"
@ -85,9 +84,6 @@ private:
ToolLoop* m_toolLoop;
Stroke m_stroke;
Pointer m_lastPointer;
gfx::Point m_oldPoint;
gfx::Point m_velocity;
base::tick_t m_lastPointerT;
gfx::Region m_dirtyArea;
gfx::Region m_nextDirtyArea;
doc::Brush m_brush0;

View File

@ -0,0 +1,52 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/tools/velocity.h"
#include "base/clamp.h"
namespace app {
namespace tools {
VelocitySensor::VelocitySensor()
{
reset();
}
void VelocitySensor::reset()
{
m_firstPoint = true;
m_lastUpdate = base::current_tick();
m_velocity = Vec2(0.0f, 0.0f);
}
void VelocitySensor::updateWithScreenPoint(const gfx::Point& screenPoint)
{
const base::tick_t t = base::current_tick();
const base::tick_t dt = t - m_lastUpdate;
m_lastUpdate = t;
// Calculate the velocity (new screen point - old screen point)
if (m_firstPoint)
m_firstPoint = false;
else {
gfx::PointF newVelocity(screenPoint - m_lastPoint);
const float a = base::clamp(float(dt) / kFullUpdateMSecs, 0.0f, 1.0f);
m_velocity.x = (1.0f-a)*m_velocity.x + a*newVelocity.x;
m_velocity.y = (1.0f-a)*m_velocity.y + a*newVelocity.y;
}
m_lastPoint.x = screenPoint.x;
m_lastPoint.y = screenPoint.y;
}
} // namespace tools
} // namespace app

49
src/app/tools/velocity.h Normal file
View File

@ -0,0 +1,49 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_TOOLS_VELOCITY_H_INCLUDED
#define APP_TOOLS_VELOCITY_H_INCLUDED
#pragma once
#include "app/tools/pointer.h"
#include "base/time.h"
#include "base/vector2d.h"
#include "gfx/point.h"
namespace app {
namespace tools {
// Mouse velocity sensor
class VelocitySensor {
public:
// Milliseconds between two updates to change the velocity vector
// with the new value (shorter periods will mix the old velocity
// vector with the new one).
static constexpr float kFullUpdateMSecs = 50.0f;
// Maximum length of the velocity vector (number of screen pixels
// traveled between two updates) to create a max sensor output.
static constexpr float kScreenPixelsForFullVelocity = 32.0f; // TODO 32 should be configurable
VelocitySensor();
void reset();
const Vec2& velocity() const { return m_velocity; }
void updateWithScreenPoint(const gfx::Point& screenPoint);
private:
bool m_firstPoint;
Vec2 m_velocity;
gfx::Point m_lastPoint;
base::tick_t m_lastUpdate;
};
} // namespace tools
} // namespace app
#endif

View File

@ -24,7 +24,6 @@
#include "dynamics.xml.h"
#include <algorithm>
#include <cmath>
namespace app {
@ -367,13 +366,9 @@ bool DynamicsPopup::onProcessMessage(Message* msg)
manager()->removeMessageFilter(kMouseMoveMessage, this);
break;
case kMouseEnterMessage: {
auto mouseMsg = static_cast<MouseMessage*>(msg);
m_lastPos = mouseMsg->position();
m_velocity = gfx::Point(0, 0);
m_lastPointerT = base::current_tick();
case kMouseEnterMessage:
m_velocity.reset();
break;
}
case kMouseMoveMessage: {
auto mouseMsg = static_cast<MouseMessage*>(msg);
@ -386,22 +381,12 @@ bool DynamicsPopup::onProcessMessage(Message* msg)
}
if (m_dynamics->velocityPlaceholder()->isVisible()) {
// TODO merge this with ToolLoopManager::getSpriteStrokePt() and
// ToolLoopManager::adjustPointWithDynamics()
const base::tick_t t = base::current_tick();
const base::tick_t dt = t - m_lastPointerT;
m_lastPointerT = t;
m_velocity.updateWithScreenPoint(mouseMsg->position());
float a = base::clamp(float(dt) / 50.0f, 0.0f, 1.0f);
gfx::Point newVelocity(mouseMsg->position() - m_lastPos);
m_velocity.x = (1.0f-a)*m_velocity.x + a*newVelocity.x;
m_velocity.y = (1.0f-a)*m_velocity.y + a*newVelocity.y;
m_lastPos = mouseMsg->position();
float v = float(std::sqrt(m_velocity.x*m_velocity.x +
m_velocity.y*m_velocity.y)) / 32.0f; // TODO 32 should be configurable
float v = m_velocity.velocity().magnitude()
/ tools::VelocitySensor::kScreenPixelsForFullVelocity;
v = base::clamp(v, 0.0f, 1.0f);
m_velocityTweaks->setSensorValue(v);
}
break;

View File

@ -9,6 +9,7 @@
#pragma once
#include "app/tools/dynamics.h"
#include "app/tools/velocity.h"
#include "app/ui/button_set.h"
#include "base/time.h"
#include "doc/brush.h"
@ -47,8 +48,7 @@ namespace app {
gfx::Region m_hotRegion;
MinMaxSlider* m_pressureTweaks;
MinMaxSlider* m_velocityTweaks;
gfx::Point m_lastPos, m_velocity;
base::tick_t m_lastPointerT;
tools::VelocitySensor m_velocity;
};
} // namespace app

View File

@ -80,6 +80,7 @@ void DrawingState::initToolLoop(Editor* editor,
ASSERT(!m_toolLoopManager->isCanceled());
m_velocity.reset();
m_lastPointer = pointer;
m_toolLoopManager->prepareLoop(pointer);
m_toolLoopManager->pressButton(pointer);
@ -110,7 +111,8 @@ bool DrawingState::onMouseDown(Editor* editor, MouseMessage* msg)
if (!editor->hasCapture())
editor->captureMouse();
tools::Pointer pointer = pointer_from_msg(editor, msg);
tools::Pointer pointer = pointer_from_msg(editor, msg,
m_velocity.velocity());
m_lastPointer = pointer;
// Check if this drawing state was started with a Shift+Pencil tool
@ -165,7 +167,8 @@ bool DrawingState::onMouseUp(Editor* editor, MouseMessage* msg)
m_type == DrawingType::SelectTiles ||
(editor->getToolLoopModifiers() != tools::ToolLoopModifiers::kReplaceSelection &&
editor->getToolLoopModifiers() != tools::ToolLoopModifiers::kIntersectSelection)) {
m_lastPointer = pointer_from_msg(editor, msg);
m_lastPointer = pointer_from_msg(editor, msg,
m_velocity.velocity());
// Notify the release of the mouse button to the tool loop
// manager. This is the correct way to say "the user finishes the
@ -206,11 +209,15 @@ bool DrawingState::onMouseMove(Editor* editor, MouseMessage* msg)
base::ScopedValue<bool> disableScroll(m_processScrollChange,
false, m_processScrollChange);
// Update velocity sensor.
m_velocity.updateWithScreenPoint(msg->position());
// The autoScroll() function controls the "infinite scroll" when we
// touch the viewport borders.
gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir);
handleMouseMovement(
tools::Pointer(editor->screenToEditor(mousePos),
m_velocity.velocity(),
button_from_msg(msg),
msg->pointerType(),
msg->pressure()));
@ -269,8 +276,13 @@ bool DrawingState::onScrollChange(Editor* editor)
{
if (m_processScrollChange) {
gfx::Point mousePos = ui::get_mouse_position();
// Update velocity sensor.
m_velocity.updateWithScreenPoint(mousePos); // TODO add scroll as velocity?
handleMouseMovement(
tools::Pointer(editor->screenToEditor(mousePos),
m_velocity.velocity(),
m_lastPointer.button(),
tools::Pointer::Type::Unknown,
0.0f));

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -10,6 +10,7 @@
#pragma once
#include "app/tools/pointer.h"
#include "app/tools/velocity.h"
#include "app/ui/editor/standby_state.h"
#include "obs/connection.h"
#include <memory>
@ -76,6 +77,10 @@ namespace app {
// button when onScrollChange() event is received.
tools::Pointer m_lastPointer;
// Used to calculate the velocity of the mouse (whch is a sensor
// to generate dynamic parameters).
tools::VelocitySensor m_velocity;
// Used to know if the button was pressed after the DrawingState
// was initialized. In this way we can cancel the ToolLoop if the
// Shift press is used to draw a line, but then released without a

View File

@ -11,23 +11,29 @@
#include "app/tools/pointer.h"
#include "app/ui/editor/editor.h"
#include "base/vector2d.h"
#include "ui/message.h"
namespace app {
// Code to convert "ui" messages to "tools" mouse pointers
inline tools::Pointer::Button button_from_msg(const ui::MouseMessage* msg) {
inline tools::Pointer::Button button_from_msg(const ui::MouseMessage* msg)
{
return
(msg->right() ? tools::Pointer::Right:
(msg->middle() ? tools::Pointer::Middle:
tools::Pointer::Left));
}
inline tools::Pointer pointer_from_msg(Editor* editor,
const ui::MouseMessage* msg) {
inline tools::Pointer pointer_from_msg(
Editor* editor,
const ui::MouseMessage* msg,
const tools::Vec2& velocity = tools::Vec2(0.0f, 0.0f))
{
return
tools::Pointer(editor->screenToEditor(msg->position()),
velocity,
button_from_msg(msg),
msg->pointerType(),
msg->pressure());

View File

@ -674,6 +674,7 @@ bool StandbyState::checkStartDrawingStraightLine(Editor* editor,
DrawingType::LineFreehand,
tools::Pointer(
editor->document()->lastDrawingPoint(),
tools::Vec2(0.0f, 0.0f),
pointerButton,
msg ? msg->pointerType(): PointerType::Unknown,
msg ? msg->pressure(): 0.0f));
@ -682,6 +683,7 @@ bool StandbyState::checkStartDrawingStraightLine(Editor* editor,
tools::Pointer(
editor->screenToEditor(msg ? msg->position():
ui::get_mouse_position()),
tools::Vec2(0.0f, 0.0f),
pointerButton,
msg ? msg->pointerType(): tools::Pointer::Type::Unknown,
msg ? msg->pressure(): 0.0f));