mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 19:20:09 +00:00
Add stabilizer (fix #2371)
This commit is contained in:
parent
83d8cfc33f
commit
1fd2e97b8d
@ -576,6 +576,8 @@ as = As:
|
||||
merged_layers = Duplicate merged layers only
|
||||
|
||||
[dynamics]
|
||||
stabilizer = Stabilizer
|
||||
stabilizer_tooltip = Stabilizer radius to avoid shaky lines
|
||||
pressure = Pressure
|
||||
pressure_tooltip = Control parameters through the pen pressure sensor
|
||||
velocity = Velocity
|
||||
|
@ -1,7 +1,14 @@
|
||||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2020 Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2020-2021 Igara Studio S.A. -->
|
||||
<gui>
|
||||
<vbox id="dynamics">
|
||||
<hbox>
|
||||
<check id="stabilizer" text="@.stabilizer"
|
||||
tooltip="@.stabilizer_tooltip" tooltip_dir="bottom" />
|
||||
<slider id="stabilizer_factor" value="16" min="0" max="64" expansive="true" style="mini_slider"
|
||||
tooltip="@.stabilizer_tooltip" tooltip_dir="bottom" />
|
||||
</hbox>
|
||||
|
||||
<hbox>
|
||||
<buttonset id="values" columns="3">
|
||||
<item text="" />
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2020-2021 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -25,6 +25,7 @@ namespace tools {
|
||||
};
|
||||
|
||||
struct DynamicsOptions {
|
||||
int stabilizerFactor = 0;
|
||||
DynamicSensor size = DynamicSensor::Static;
|
||||
DynamicSensor angle = DynamicSensor::Static;
|
||||
DynamicSensor gradient = DynamicSensor::Static;
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "gfx/region.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
|
||||
#define TOOL_TRACE(...) // TRACEARGS(__VA_ARGS__)
|
||||
|
||||
@ -118,6 +119,8 @@ void ToolLoopManager::pressButton(const Pointer& pointer)
|
||||
return;
|
||||
}
|
||||
|
||||
m_stabilizerCenter = pointer.point();
|
||||
|
||||
Stroke::Pt spritePoint = getSpriteStrokePt(pointer);
|
||||
m_toolLoop->getController()->pressButton(m_toolLoop, m_stroke, spritePoint);
|
||||
|
||||
@ -163,8 +166,27 @@ bool ToolLoopManager::releaseButton(const Pointer& pointer)
|
||||
return res;
|
||||
}
|
||||
|
||||
void ToolLoopManager::movement(const Pointer& pointer)
|
||||
void ToolLoopManager::movement(Pointer pointer)
|
||||
{
|
||||
// Filter points with the stabilizer
|
||||
if (m_dynamics.stabilizerFactor > 0) {
|
||||
const double f = m_dynamics.stabilizerFactor;
|
||||
const gfx::Point delta = (pointer.point() - m_stabilizerCenter);
|
||||
const double distance = std::sqrt(delta.x*delta.x + delta.y*delta.y);
|
||||
|
||||
const double angle = std::atan2(delta.y, delta.x);
|
||||
const gfx::PointF newPoint(m_stabilizerCenter.x + distance/f*std::cos(angle),
|
||||
m_stabilizerCenter.y + distance/f*std::sin(angle));
|
||||
|
||||
m_stabilizerCenter = newPoint;
|
||||
|
||||
pointer = Pointer(gfx::Point(newPoint),
|
||||
pointer.velocity(),
|
||||
pointer.button(),
|
||||
pointer.type(),
|
||||
pointer.pressure());
|
||||
}
|
||||
|
||||
m_lastPointer = pointer;
|
||||
|
||||
if (isCanceled())
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -76,7 +76,7 @@ public:
|
||||
bool releaseButton(const Pointer& pointer);
|
||||
|
||||
// Should be called each time the user moves the mouse inside the editor.
|
||||
void movement(const Pointer& pointer);
|
||||
void movement(Pointer pointer);
|
||||
|
||||
const Pointer& lastPointer() const { return m_lastPointer; }
|
||||
|
||||
@ -97,6 +97,7 @@ private:
|
||||
gfx::Region m_nextDirtyArea;
|
||||
doc::Brush m_brush0;
|
||||
DynamicsOptions m_dynamics;
|
||||
gfx::PointF m_stabilizerCenter;
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
|
@ -215,6 +215,19 @@ DynamicsPopup::DynamicsPopup(Delegate* delegate)
|
||||
, m_ditheringSel(new DitheringSelector(DitheringSelector::SelectMatrix))
|
||||
, m_fromTo(tools::ColorFromTo::BgToFg)
|
||||
{
|
||||
m_dynamics->stabilizer()->Click.connect(
|
||||
[this](){
|
||||
if (m_dynamics->stabilizer()->isSelected() &&
|
||||
m_dynamics->stabilizerFactor()->getValue() == 0) {
|
||||
// TODO default value when we enable stabilizer when it's zero
|
||||
m_dynamics->stabilizerFactor()->setValue(16);
|
||||
}
|
||||
});
|
||||
m_dynamics->stabilizerFactor()->Change.connect(
|
||||
[this](){
|
||||
m_dynamics->stabilizer()->setSelected(m_dynamics->stabilizerFactor()->getValue() > 0);
|
||||
});
|
||||
|
||||
m_dynamics->values()->ItemChange.connect(
|
||||
[this](ButtonSet::Item* item){
|
||||
onValuesChange(item);
|
||||
@ -254,6 +267,14 @@ DynamicsPopup::DynamicsPopup(Delegate* delegate)
|
||||
tools::DynamicsOptions DynamicsPopup::getDynamics() const
|
||||
{
|
||||
tools::DynamicsOptions opts;
|
||||
|
||||
if (m_dynamics->stabilizer()->isSelected()) {
|
||||
opts.stabilizerFactor = m_dynamics->stabilizerFactor()->getValue();
|
||||
}
|
||||
else {
|
||||
opts.stabilizerFactor = 0;
|
||||
}
|
||||
|
||||
opts.size =
|
||||
(isCheck(SIZE_WITH_PRESSURE) ? tools::DynamicSensor::Pressure:
|
||||
isCheck(SIZE_WITH_VELOCITY) ? tools::DynamicSensor::Velocity:
|
||||
|
Loading…
x
Reference in New Issue
Block a user