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. Instead of enabling ALL symmetry buttons via automatic logic, flags were added to differentiate actual symmetry mode from actual symmetry buttons being pressed. 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
6c69840184
commit
5570559c34
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2018-2024 Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2018-2025 Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2014-2018 David Capello -->
|
||||
<preferences>
|
||||
|
||||
@ -522,6 +522,7 @@
|
||||
<option id="mode" type="SymmetryMode" default="SymmetryMode::NONE" />
|
||||
<option id="x_axis" type="double" default="0" />
|
||||
<option id="y_axis" type="double" default="0" />
|
||||
<option id="button_flags" type="int" default="0" />
|
||||
</section>
|
||||
<section id="grid" canforce="true">
|
||||
<option id="snap" type="bool" default="false" />
|
||||
|
@ -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(docPref.symmetry.buttonFlags() & int(app::gen::SymmetryMode::HORIZONTAL));
|
||||
at(1)->setSelected(docPref.symmetry.buttonFlags() & int(app::gen::SymmetryMode::VERTICAL));
|
||||
at(2)->setSelected(docPref.symmetry.buttonFlags() & int(app::gen::SymmetryMode::RIGHT_DIAG));
|
||||
at(3)->setSelected(docPref.symmetry.buttonFlags() & int(app::gen::SymmetryMode::LEFT_DIAG));
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1551,75 +1547,29 @@ private:
|
||||
|
||||
DocumentPreferences& docPref = Preferences::instance().document(doc);
|
||||
|
||||
auto oldMode = docPref.symmetry.mode();
|
||||
int mode = 0;
|
||||
int buttonFlags = 0;
|
||||
if (at(0)->isSelected())
|
||||
mode |= int(app::gen::SymmetryMode::HORIZONTAL);
|
||||
buttonFlags |= int(app::gen::SymmetryMode::HORIZONTAL);
|
||||
if (at(1)->isSelected())
|
||||
mode |= int(app::gen::SymmetryMode::VERTICAL);
|
||||
buttonFlags |= int(app::gen::SymmetryMode::VERTICAL);
|
||||
if (at(2)->isSelected())
|
||||
mode |= int(app::gen::SymmetryMode::RIGHT_DIAG);
|
||||
buttonFlags |= int(app::gen::SymmetryMode::RIGHT_DIAG);
|
||||
if (at(3)->isSelected())
|
||||
mode |= int(app::gen::SymmetryMode::LEFT_DIAG);
|
||||
buttonFlags |= 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
|
||||
const bool forceAllMode = ((buttonFlags & int(app::gen::SymmetryMode::HORIZONTAL)) ||
|
||||
(buttonFlags & int(app::gen::SymmetryMode::VERTICAL))) &&
|
||||
((buttonFlags & int(app::gen::SymmetryMode::RIGHT_DIAG)) ||
|
||||
(buttonFlags & int(app::gen::SymmetryMode::LEFT_DIAG)));
|
||||
|
||||
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);
|
||||
|
||||
if (app::gen::SymmetryMode(mode) != docPref.symmetry.mode()) {
|
||||
docPref.symmetry.mode(app::gen::SymmetryMode(mode));
|
||||
// Redraw symmetry rules
|
||||
if (buttonFlags != docPref.symmetry.buttonFlags()) {
|
||||
docPref.symmetry.mode(forceAllMode ? app::gen::SymmetryMode::ALL :
|
||||
app::gen::SymmetryMode(buttonFlags));
|
||||
docPref.symmetry.buttonFlags(buttonFlags);
|
||||
doc->notifyGeneralUpdate();
|
||||
}
|
||||
else if (at(4)->isSelected()) {
|
||||
|
||||
if (at(4)->isSelected()) {
|
||||
auto* item = at(4);
|
||||
|
||||
gfx::Rect bounds = item->bounds();
|
||||
|
@ -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
|
||||
@ -831,6 +831,85 @@ 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 mode = int(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 int symmetryButtonFlags = m_docPref.symmetry.buttonFlags();
|
||||
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(symmetryButtonFlags & 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(symmetryButtonFlags & 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(symmetryButtonFlags & 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(symmetryButtonFlags & int(app::gen::SymmetryMode::LEFT_DIAG) ?
|
||||
color :
|
||||
semiTransparentColor,
|
||||
topLeft,
|
||||
bottomRight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -907,86 +986,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