mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 13:21:34 +00:00
Fix symmetry button is kept pressed when we didn't pressed (fix #4760)
This fix removes the 'non sense symmetry filter' to prevent some buttons from being unintentionally held down. Moved the drawing process for symmetry axes from 'Editor::drawSpriteUnclippedRect' to 'Editor::drawOneSpriteUnclippedRect' to allow semi-transparent axes. This also produces axes on every tile in tile mode. Pixel ratios other than 1:1 are now considered in the drawing logic of diagonal axes.
This commit is contained in:
parent
5739bfe287
commit
7ef4fa0ad0
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2021-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2021-2025 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -20,7 +20,7 @@ void Symmetry::generateStrokes(const Stroke& stroke, Strokes& strokes, ToolLoop*
|
||||
{
|
||||
Stroke stroke2;
|
||||
strokes.push_back(stroke);
|
||||
gen::SymmetryMode symmetryMode = loop->getSymmetry()->mode();
|
||||
const gen::SymmetryMode symmetryMode = tools::Symmetry::resolveMode(loop->getSymmetry()->mode());
|
||||
switch (symmetryMode) {
|
||||
case gen::SymmetryMode::NONE: ASSERT(false); break;
|
||||
|
||||
@ -171,4 +171,14 @@ void Symmetry::calculateSymmetricalStroke(const Stroke& refStroke,
|
||||
}
|
||||
}
|
||||
|
||||
gen::SymmetryMode Symmetry::resolveMode(gen::SymmetryMode mode)
|
||||
{
|
||||
return (((int(mode) & int(gen::SymmetryMode::HORIZONTAL)) ||
|
||||
(int(mode) & int(gen::SymmetryMode::VERTICAL))) &&
|
||||
((int(mode) & int(gen::SymmetryMode::RIGHT_DIAG)) ||
|
||||
(int(mode) & int(gen::SymmetryMode::LEFT_DIAG)))) ?
|
||||
gen::SymmetryMode::ALL :
|
||||
mode;
|
||||
}
|
||||
|
||||
}} // namespace app::tools
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2021-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2021-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2015 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -38,6 +38,8 @@ public:
|
||||
|
||||
gen::SymmetryMode mode() const { return m_symmetryMode; }
|
||||
|
||||
static gen::SymmetryMode resolveMode(gen::SymmetryMode mode);
|
||||
|
||||
private:
|
||||
void calculateSymmetricalStroke(const Stroke& refStroke,
|
||||
Stroke& stroke,
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -1530,14 +1530,10 @@ public:
|
||||
|
||||
DocumentPreferences& docPref = Preferences::instance().document(doc);
|
||||
|
||||
at(0)->setSelected(
|
||||
int(docPref.symmetry.mode()) & int(app::gen::SymmetryMode::HORIZONTAL) ? true : false);
|
||||
at(1)->setSelected(
|
||||
int(docPref.symmetry.mode()) & int(app::gen::SymmetryMode::VERTICAL) ? true : false);
|
||||
at(2)->setSelected(
|
||||
int(docPref.symmetry.mode()) & int(app::gen::SymmetryMode::RIGHT_DIAG) ? true : false);
|
||||
at(3)->setSelected(
|
||||
int(docPref.symmetry.mode()) & int(app::gen::SymmetryMode::LEFT_DIAG) ? true : false);
|
||||
at(0)->setSelected(int(docPref.symmetry.mode()) & int(app::gen::SymmetryMode::HORIZONTAL));
|
||||
at(1)->setSelected(int(docPref.symmetry.mode()) & int(app::gen::SymmetryMode::VERTICAL));
|
||||
at(2)->setSelected(int(docPref.symmetry.mode()) & int(app::gen::SymmetryMode::RIGHT_DIAG));
|
||||
at(3)->setSelected(int(docPref.symmetry.mode()) & int(app::gen::SymmetryMode::LEFT_DIAG));
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1551,60 +1547,7 @@ private:
|
||||
|
||||
DocumentPreferences& docPref = Preferences::instance().document(doc);
|
||||
|
||||
auto oldMode = docPref.symmetry.mode();
|
||||
int mode = 0;
|
||||
if (at(0)->isSelected())
|
||||
mode |= int(app::gen::SymmetryMode::HORIZONTAL);
|
||||
if (at(1)->isSelected())
|
||||
mode |= int(app::gen::SymmetryMode::VERTICAL);
|
||||
if (at(2)->isSelected())
|
||||
mode |= int(app::gen::SymmetryMode::RIGHT_DIAG);
|
||||
if (at(3)->isSelected())
|
||||
mode |= int(app::gen::SymmetryMode::LEFT_DIAG);
|
||||
|
||||
// Non sense symmetries filter:
|
||||
// - H + 1Diag
|
||||
// - V + 1Diag
|
||||
// - H + V + 1Diag
|
||||
const bool HorV = (mode & int(app::gen::SymmetryMode::HORIZONTAL)) ||
|
||||
(mode & int(app::gen::SymmetryMode::VERTICAL));
|
||||
const bool HxorV = !(mode & int(app::gen::SymmetryMode::HORIZONTAL)) !=
|
||||
!(mode & int(app::gen::SymmetryMode::VERTICAL));
|
||||
const bool RDxorLD = !(mode & int(app::gen::SymmetryMode::RIGHT_DIAG)) !=
|
||||
!(mode & int(app::gen::SymmetryMode::LEFT_DIAG));
|
||||
if (oldMode == gen::SymmetryMode::HORIZONTAL || oldMode == gen::SymmetryMode::VERTICAL ||
|
||||
oldMode == gen::SymmetryMode::BOTH) {
|
||||
if (HorV && RDxorLD) {
|
||||
mode = int(app::gen::SymmetryMode::ALL);
|
||||
at(0)->setSelected(true);
|
||||
at(1)->setSelected(true);
|
||||
at(2)->setSelected(true);
|
||||
at(3)->setSelected(true);
|
||||
}
|
||||
}
|
||||
else if (oldMode == gen::SymmetryMode::ALL) {
|
||||
if (HxorV) {
|
||||
mode = int(app::gen::SymmetryMode::BOTH_DIAG);
|
||||
at(0)->setSelected(false);
|
||||
at(1)->setSelected(false);
|
||||
}
|
||||
else if (RDxorLD) {
|
||||
mode = int(app::gen::SymmetryMode::BOTH);
|
||||
at(2)->setSelected(false);
|
||||
at(3)->setSelected(false);
|
||||
}
|
||||
}
|
||||
else if ((oldMode == gen::SymmetryMode::RIGHT_DIAG || oldMode == gen::SymmetryMode::LEFT_DIAG ||
|
||||
oldMode == gen::SymmetryMode::BOTH_DIAG) &&
|
||||
HorV) {
|
||||
mode = int(app::gen::SymmetryMode::ALL);
|
||||
at(0)->setSelected(true);
|
||||
at(1)->setSelected(true);
|
||||
at(2)->setSelected(true);
|
||||
at(3)->setSelected(true);
|
||||
}
|
||||
// Non sense symmetries filter end
|
||||
|
||||
if (at(0)->isSelected())
|
||||
mode |= int(app::gen::SymmetryMode::HORIZONTAL);
|
||||
if (at(1)->isSelected())
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -31,6 +31,7 @@
|
||||
#include "app/tools/active_tool.h"
|
||||
#include "app/tools/controller.h"
|
||||
#include "app/tools/ink.h"
|
||||
#include "app/tools/symmetry.h"
|
||||
#include "app/tools/tool.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
#include "app/ui/color_bar.h"
|
||||
@ -831,6 +832,86 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g,
|
||||
m_docPref.grid.forceSection();
|
||||
}
|
||||
m_docPref.show.grid.forceDirtyFlag();
|
||||
|
||||
// Symmetry mode
|
||||
if (isActive() && (m_flags & Editor::kShowSymmetryLine) &&
|
||||
Preferences::instance().symmetryMode.enabled()) {
|
||||
const int symmetryButtons = int(m_docPref.symmetry.mode());
|
||||
// Symmetry::resolveMode is to calculate the right symmetry
|
||||
// mode. This is necessary because some symmetry settings
|
||||
// do not make sense and should be forced to 'ALL'
|
||||
const int mode = int(tools::Symmetry::resolveMode(m_docPref.symmetry.mode()));
|
||||
const gfx::Color color = color_utils::color_for_ui(m_docPref.grid.color());
|
||||
const gfx::Color semiTransparentColor =
|
||||
gfx::rgba(rgba_getr(color), rgba_getg(color), rgba_getb(color), rgba_geta(color) / 4);
|
||||
const double x = int(m_proj.applyX<double>(m_docPref.symmetry.xAxis()));
|
||||
const double y = int(m_proj.applyY<double>(m_docPref.symmetry.yAxis()));
|
||||
|
||||
if (mode & int(app::gen::SymmetryMode::HORIZONTAL) && x > 0) {
|
||||
g->drawVLine(symmetryButtons & int(app::gen::SymmetryMode::HORIZONTAL) ?
|
||||
color :
|
||||
semiTransparentColor,
|
||||
enclosingRect.x + x,
|
||||
enclosingRect.y,
|
||||
enclosingRect.h);
|
||||
}
|
||||
if (mode & int(app::gen::SymmetryMode::VERTICAL) && y > 0) {
|
||||
g->drawHLine(
|
||||
symmetryButtons & int(app::gen::SymmetryMode::VERTICAL) ? color : semiTransparentColor,
|
||||
enclosingRect.x,
|
||||
enclosingRect.y + y,
|
||||
enclosingRect.w);
|
||||
}
|
||||
if (mode & int(app::gen::SymmetryMode::RIGHT_DIAG)) {
|
||||
// Bottom point intersection:
|
||||
gfx::Point bottomLeft(
|
||||
enclosingRect.x + x + m_proj.turnYinTermsOfX<int>(y - enclosingRect.h),
|
||||
enclosingRect.y2());
|
||||
if (bottomLeft.x < enclosingRect.x) {
|
||||
// Left intersection
|
||||
bottomLeft.y = enclosingRect.y2() +
|
||||
m_proj.turnXinTermsOfY<int>(bottomLeft.x - enclosingRect.x);
|
||||
bottomLeft.x = enclosingRect.x;
|
||||
}
|
||||
// Top intersection
|
||||
gfx::Point topRight(enclosingRect.x + x + m_proj.turnYinTermsOfX<int>(y),
|
||||
enclosingRect.y);
|
||||
if (enclosingRect.x2() < topRight.x) {
|
||||
// Right intersection
|
||||
topRight.y = enclosingRect.y +
|
||||
m_proj.applyY<int>(m_proj.removeX<int>(topRight.x - enclosingRect.x2()));
|
||||
topRight.x = enclosingRect.x2();
|
||||
}
|
||||
g->drawLine(symmetryButtons & int(app::gen::SymmetryMode::RIGHT_DIAG) ?
|
||||
color :
|
||||
semiTransparentColor,
|
||||
bottomLeft,
|
||||
topRight);
|
||||
}
|
||||
if (mode & int(app::gen::SymmetryMode::LEFT_DIAG)) {
|
||||
// Bottom point intersection:
|
||||
gfx::Point bottomRight(
|
||||
enclosingRect.x + x + m_proj.turnYinTermsOfX<int>(enclosingRect.h - y),
|
||||
enclosingRect.y2());
|
||||
if (enclosingRect.x2() < bottomRight.x) {
|
||||
// Left intersection
|
||||
bottomRight.y = enclosingRect.y2() +
|
||||
m_proj.turnXinTermsOfY<int>(enclosingRect.x2() - bottomRight.x);
|
||||
bottomRight.x = enclosingRect.x2();
|
||||
}
|
||||
// Top intersection
|
||||
gfx::Point topLeft(enclosingRect.x + x - m_proj.turnYinTermsOfX<int>(y), enclosingRect.y);
|
||||
if (topLeft.x < enclosingRect.x) {
|
||||
// Right intersection
|
||||
topLeft.y = enclosingRect.y + m_proj.turnXinTermsOfY<int>(enclosingRect.x - topLeft.x);
|
||||
topLeft.x = enclosingRect.x;
|
||||
}
|
||||
g->drawLine(
|
||||
symmetryButtons & int(app::gen::SymmetryMode::LEFT_DIAG) ? color : semiTransparentColor,
|
||||
topLeft,
|
||||
bottomRight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -907,86 +988,6 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc)
|
||||
if (m_docPref.show.slices())
|
||||
drawSlices(g);
|
||||
|
||||
// Symmetry mode
|
||||
if (isActive() && (m_flags & Editor::kShowSymmetryLine) &&
|
||||
Preferences::instance().symmetryMode.enabled()) {
|
||||
int mode = int(m_docPref.symmetry.mode());
|
||||
if (mode & int(app::gen::SymmetryMode::HORIZONTAL)) {
|
||||
double x = m_docPref.symmetry.xAxis();
|
||||
if (x > 0) {
|
||||
gfx::Color color = color_utils::color_for_ui(m_docPref.grid.color());
|
||||
g->drawVLine(
|
||||
color,
|
||||
spriteRect.x + m_proj.applyX(mainTilePosition().x) + int(m_proj.applyX<double>(x)),
|
||||
enclosingRect.y,
|
||||
enclosingRect.h);
|
||||
}
|
||||
}
|
||||
if (mode & int(app::gen::SymmetryMode::VERTICAL)) {
|
||||
double y = m_docPref.symmetry.yAxis();
|
||||
if (y > 0) {
|
||||
gfx::Color color = color_utils::color_for_ui(m_docPref.grid.color());
|
||||
g->drawHLine(
|
||||
color,
|
||||
enclosingRect.x,
|
||||
spriteRect.y + m_proj.applyY(mainTilePosition().y) + int(m_proj.applyY<double>(y)),
|
||||
enclosingRect.w);
|
||||
}
|
||||
}
|
||||
if (mode & int(app::gen::SymmetryMode::RIGHT_DIAG)) {
|
||||
double y = m_docPref.symmetry.yAxis();
|
||||
double x = m_docPref.symmetry.xAxis();
|
||||
gfx::Color color = color_utils::color_for_ui(m_docPref.grid.color());
|
||||
// Bottom point intersection:
|
||||
gfx::Point bottomLeft(
|
||||
enclosingRect.x + m_proj.applyY(mainTilePosition().x) + int(m_proj.applyX<double>(x)) -
|
||||
(enclosingRect.h - m_proj.applyY(mainTilePosition().y) - int(m_proj.applyY<double>(y))),
|
||||
enclosingRect.y2());
|
||||
if (bottomLeft.x < enclosingRect.x) {
|
||||
// Left intersection
|
||||
bottomLeft.y = enclosingRect.y2() - enclosingRect.x + bottomLeft.x;
|
||||
bottomLeft.x = enclosingRect.x;
|
||||
}
|
||||
// Top intersection
|
||||
gfx::Point topRight(enclosingRect.x + m_proj.applyY(mainTilePosition().x) +
|
||||
int(m_proj.applyX<double>(x)) + m_proj.applyY(mainTilePosition().y) +
|
||||
int(m_proj.applyY<double>(y)),
|
||||
enclosingRect.y);
|
||||
if (enclosingRect.x2() < topRight.x) {
|
||||
// Right intersection
|
||||
topRight.y = enclosingRect.y + topRight.x - enclosingRect.x2();
|
||||
topRight.x = enclosingRect.x2();
|
||||
}
|
||||
g->drawLine(color, bottomLeft, topRight);
|
||||
}
|
||||
if (mode & int(app::gen::SymmetryMode::LEFT_DIAG)) {
|
||||
double y = m_docPref.symmetry.yAxis();
|
||||
double x = m_docPref.symmetry.xAxis();
|
||||
gfx::Color color = color_utils::color_for_ui(m_docPref.grid.color());
|
||||
// Bottom point intersection:
|
||||
gfx::Point bottomRight(
|
||||
enclosingRect.x + m_proj.applyY(mainTilePosition().x) + int(m_proj.applyX<double>(x)) +
|
||||
(enclosingRect.h - m_proj.applyY(mainTilePosition().y) - int(m_proj.applyX<double>(y))),
|
||||
enclosingRect.y2());
|
||||
if (enclosingRect.x2() < bottomRight.x) {
|
||||
// Left intersection
|
||||
bottomRight.y = enclosingRect.y2() - bottomRight.x + enclosingRect.x2();
|
||||
bottomRight.x = enclosingRect.x2();
|
||||
}
|
||||
// Top intersection
|
||||
gfx::Point topLeft(enclosingRect.x + m_proj.applyY(mainTilePosition().x) +
|
||||
int(m_proj.applyX<double>(x)) - m_proj.applyY(mainTilePosition().y) -
|
||||
int(m_proj.applyY<double>(y)),
|
||||
enclosingRect.y);
|
||||
if (topLeft.x < enclosingRect.x) {
|
||||
// Right intersection
|
||||
topLeft.y = enclosingRect.y + enclosingRect.x - topLeft.x;
|
||||
topLeft.x = enclosingRect.x;
|
||||
}
|
||||
g->drawLine(color, topLeft, bottomRight);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw active layer/cel edges
|
||||
if ((m_docPref.show.layerEdges() || m_showAutoCelGuides) &&
|
||||
// Show layer edges and possibly cel guides only on states that
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
// Copyright (c) 2020-2025 Igara Studio S.A.
|
||||
// Copyright (c) 2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -70,6 +70,20 @@ public:
|
||||
return T(m_zoom.removeCeiling(y)) / T(m_pixelRatio.h);
|
||||
}
|
||||
|
||||
// Used in 'editor.cpp' to do some math between x,y values.
|
||||
// Useful for calculating diagonal symmetry axis positions when pixel ratio is other than 1:1
|
||||
template<typename T>
|
||||
T turnXinTermsOfY(T x) const
|
||||
{
|
||||
return x * T(m_pixelRatio.h) / T(m_pixelRatio.w);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T turnYinTermsOfX(T y) const
|
||||
{
|
||||
return y * T(m_pixelRatio.w) / T(m_pixelRatio.h);
|
||||
}
|
||||
|
||||
gfx::Rect apply(const gfx::Rect& r) const
|
||||
{
|
||||
int u = applyX(r.x);
|
||||
|
Loading…
x
Reference in New Issue
Block a user