mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 04:19:12 +00:00
Refactor velocity sensor with tools::VelocitySensor class
This commit is contained in:
parent
bb07d25025
commit
c99a566635
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
52
src/app/tools/velocity.cpp
Normal file
52
src/app/tools/velocity.cpp
Normal 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
49
src/app/tools/velocity.h
Normal 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
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user