Merge 5570559c34da972fea97cc93d16c78ba06f825b1 into 9378f44191ecf4afd4dda27ab54b77aec0097eea

This commit is contained in:
Gaspar Capello 2025-02-26 10:24:11 -03:00 committed by GitHub
commit d9b1d66b8a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 117 additions and 153 deletions

View File

@ -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" />

View File

@ -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();

View File

@ -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

View File

@ -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);