mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-16 05:42:32 +00:00
Support horizontal/vertical symmetry at the same time (fix #1190)
This commit is contained in:
parent
043489e532
commit
76df84491e
@ -79,6 +79,7 @@
|
||||
<value id="NONE" value="0" />
|
||||
<value id="HORIZONTAL" value="1" />
|
||||
<value id="VERTICAL" value="2" />
|
||||
<value id="BOTH" value="3" />
|
||||
</enum>
|
||||
<enum id="PaintingCursorType">
|
||||
<value id="SIMPLE_CROSSHAIR" value="0" />
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2015-2016 David Capello
|
||||
// Copyright (C) 2015-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -52,5 +52,14 @@ void VerticalSymmetry::generateStrokes(const Stroke& mainStroke, Strokes& stroke
|
||||
strokes.push_back(stroke2);
|
||||
}
|
||||
|
||||
void SymmetryCombo::generateStrokes(const Stroke& mainStroke, Strokes& strokes,
|
||||
ToolLoop* loop)
|
||||
{
|
||||
Strokes strokes0;
|
||||
m_a->generateStrokes(mainStroke, strokes0, loop);
|
||||
for (const Stroke& stroke : strokes0)
|
||||
m_b->generateStrokes(stroke, strokes, loop);
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
} // namespace app
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2015-2016 David Capello
|
||||
// Copyright (C) 2015-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -10,6 +10,7 @@
|
||||
|
||||
#include "app/tools/stroke.h"
|
||||
#include "app/tools/symmetry.h"
|
||||
#include "base/unique_ptr.h"
|
||||
|
||||
namespace app {
|
||||
namespace tools {
|
||||
@ -32,6 +33,16 @@ private:
|
||||
double m_y;
|
||||
};
|
||||
|
||||
class SymmetryCombo : public Symmetry {
|
||||
public:
|
||||
SymmetryCombo(Symmetry* a, Symmetry* b) : m_a(a), m_b(b) { }
|
||||
void generateStrokes(const Stroke& mainStroke, Strokes& strokes,
|
||||
ToolLoop* loop) override;
|
||||
private:
|
||||
base::UniquePtr<tools::Symmetry> m_a;
|
||||
base::UniquePtr<tools::Symmetry> m_b;
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
} // namespace app
|
||||
|
||||
|
@ -1305,17 +1305,17 @@ protected:
|
||||
|
||||
class ContextBar::SymmetryField : public ButtonSet {
|
||||
public:
|
||||
SymmetryField() : ButtonSet(3) {
|
||||
SymmetryField() : ButtonSet(2) {
|
||||
setMultipleSelection(true);
|
||||
|
||||
SkinTheme* theme = SkinTheme::instance();
|
||||
addItem(theme->parts.noSymmetry());
|
||||
addItem(theme->parts.horizontalSymmetry());
|
||||
addItem(theme->parts.verticalSymmetry());
|
||||
}
|
||||
|
||||
void setupTooltips(TooltipManager* tooltipManager) {
|
||||
tooltipManager->addTooltipFor(at(0), "Without Symmetry", BOTTOM);
|
||||
tooltipManager->addTooltipFor(at(1), "Horizontal Symmetry", BOTTOM);
|
||||
tooltipManager->addTooltipFor(at(2), "Vertical Symmetry", BOTTOM);
|
||||
tooltipManager->addTooltipFor(at(0), "Horizontal Symmetry", BOTTOM);
|
||||
tooltipManager->addTooltipFor(at(1), "Vertical Symmetry", BOTTOM);
|
||||
}
|
||||
|
||||
void updateWithCurrentDocument() {
|
||||
@ -1325,7 +1325,8 @@ public:
|
||||
|
||||
DocumentPreferences& docPref = Preferences::instance().document(doc);
|
||||
|
||||
setSelectedItem((int)docPref.symmetry.mode());
|
||||
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);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1339,7 +1340,10 @@ private:
|
||||
DocumentPreferences& docPref =
|
||||
Preferences::instance().document(doc);
|
||||
|
||||
docPref.symmetry.mode((app::gen::SymmetryMode)selectedItem());
|
||||
int mode = 0;
|
||||
if (at(0)->isSelected()) mode |= int(app::gen::SymmetryMode::HORIZONTAL);
|
||||
if (at(1)->isSelected()) mode |= int(app::gen::SymmetryMode::VERTICAL);
|
||||
docPref.symmetry.mode(app::gen::SymmetryMode(mode));
|
||||
|
||||
// Redraw symmetry rules
|
||||
doc->notifyGeneralUpdate();
|
||||
|
@ -758,11 +758,8 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc)
|
||||
if (isActive() &&
|
||||
(m_flags & Editor::kShowSymmetryLine) &&
|
||||
Preferences::instance().symmetryMode.enabled()) {
|
||||
switch (m_docPref.symmetry.mode()) {
|
||||
case app::gen::SymmetryMode::NONE:
|
||||
// Do nothing
|
||||
break;
|
||||
case app::gen::SymmetryMode::HORIZONTAL: {
|
||||
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());
|
||||
@ -771,9 +768,8 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc)
|
||||
enclosingRect.y,
|
||||
enclosingRect.h);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case app::gen::SymmetryMode::VERTICAL: {
|
||||
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());
|
||||
@ -782,8 +778,6 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc)
|
||||
spriteRect.y + m_proj.applyY<double>(y),
|
||||
enclosingRect.w);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -297,20 +297,22 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
}
|
||||
|
||||
// Move symmetry
|
||||
gfx::Rect box1, box2;
|
||||
if (m_decorator->getSymmetryHandles(editor, box1, box2) &&
|
||||
(box1.contains(msg->position()) ||
|
||||
box2.contains(msg->position()))) {
|
||||
auto& symmetry = Preferences::instance().document(editor->document()).symmetry;
|
||||
auto mode = symmetry.mode();
|
||||
Decorator::Handles handles;
|
||||
if (m_decorator->getSymmetryHandles(editor, handles)) {
|
||||
for (const auto& handle : handles) {
|
||||
if (handle.bounds.contains(msg->position())) {
|
||||
auto mode = (handle.align & (TOP | BOTTOM) ? app::gen::SymmetryMode::HORIZONTAL:
|
||||
app::gen::SymmetryMode::VERTICAL);
|
||||
bool horz = (mode == app::gen::SymmetryMode::HORIZONTAL);
|
||||
auto& symmetry = Preferences::instance().document(editor->document()).symmetry;
|
||||
auto& axis = (horz ? symmetry.xAxis:
|
||||
symmetry.yAxis);
|
||||
editor->setState(
|
||||
EditorStatePtr(new MovingSymmetryState(editor, msg,
|
||||
mode, axis)));
|
||||
EditorStatePtr(new MovingSymmetryState(editor, msg, mode, axis)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start the Tool-Loop
|
||||
if (layer && (layer->isImage() || clickedInk->isSelection())) {
|
||||
@ -785,20 +787,24 @@ bool StandbyState::Decorator::onSetCursor(tools::Ink* ink, Editor* editor, const
|
||||
}
|
||||
|
||||
// Move symmetry
|
||||
gfx::Rect box1, box2;
|
||||
if (getSymmetryHandles(editor, box1, box2) &&
|
||||
(box1.contains(mouseScreenPos) ||
|
||||
box2.contains(mouseScreenPos))) {
|
||||
switch (Preferences::instance().document(editor->document()).symmetry.mode()) {
|
||||
case app::gen::SymmetryMode::HORIZONTAL:
|
||||
Handles handles;
|
||||
if (getSymmetryHandles(editor, handles)) {
|
||||
for (const auto& handle : handles) {
|
||||
if (handle.bounds.contains(mouseScreenPos)) {
|
||||
switch (handle.align) {
|
||||
case TOP:
|
||||
case BOTTOM:
|
||||
editor->showMouseCursor(kSizeWECursor);
|
||||
break;
|
||||
case app::gen::SymmetryMode::VERTICAL:
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
editor->showMouseCursor(kSizeNSCursor);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -829,26 +835,26 @@ void StandbyState::Decorator::postRenderDecorator(EditorPostRender* render)
|
||||
}
|
||||
|
||||
// Draw transformation handles (if the mask is visible and isn't frozen).
|
||||
gfx::Rect box1, box2;
|
||||
if (StandbyState::Decorator::getSymmetryHandles(editor, box1, box2)) {
|
||||
Handles handles;
|
||||
if (StandbyState::Decorator::getSymmetryHandles(editor, handles)) {
|
||||
skin::SkinTheme* theme = static_cast<skin::SkinTheme*>(ui::get_theme());
|
||||
she::Surface* part = theme->parts.transformationHandle()->bitmap(0);
|
||||
ScreenGraphics g;
|
||||
g.drawRgbaSurface(part, box1.x, box1.y);
|
||||
g.drawRgbaSurface(part, box2.x, box2.y);
|
||||
for (const auto& handle : handles)
|
||||
g.drawRgbaSurface(part, handle.bounds.x, handle.bounds.y);
|
||||
}
|
||||
}
|
||||
|
||||
void StandbyState::Decorator::getInvalidDecoratoredRegion(Editor* editor, gfx::Region& region)
|
||||
{
|
||||
gfx::Rect box1, box2;
|
||||
if (getSymmetryHandles(editor, box1, box2)) {
|
||||
region.createUnion(region, gfx::Region(box1));
|
||||
region.createUnion(region, gfx::Region(box2));
|
||||
Handles handles;
|
||||
if (getSymmetryHandles(editor, handles)) {
|
||||
for (const auto& handle : handles)
|
||||
region.createUnion(region, gfx::Region(handle.bounds));
|
||||
}
|
||||
}
|
||||
|
||||
bool StandbyState::Decorator::getSymmetryHandles(Editor* editor, gfx::Rect& box1, gfx::Rect& box2)
|
||||
bool StandbyState::Decorator::getSymmetryHandles(Editor* editor, Handles& handles)
|
||||
{
|
||||
// Draw transformation handles (if the mask is visible and isn't frozen).
|
||||
if (editor->isActive() &&
|
||||
@ -857,16 +863,15 @@ bool StandbyState::Decorator::getSymmetryHandles(Editor* editor, gfx::Rect& box1
|
||||
const auto& symmetry = Preferences::instance().document(editor->document()).symmetry;
|
||||
auto mode = symmetry.mode();
|
||||
if (mode != app::gen::SymmetryMode::NONE) {
|
||||
bool horz = (mode == app::gen::SymmetryMode::HORIZONTAL);
|
||||
double pos = (horz ? symmetry.xAxis():
|
||||
symmetry.yAxis());
|
||||
gfx::RectF spriteBounds = gfx::RectF(editor->sprite()->bounds());
|
||||
gfx::RectF editorViewport = gfx::RectF(View::getView(editor)->viewportBounds());
|
||||
skin::SkinTheme* theme = static_cast<skin::SkinTheme*>(ui::get_theme());
|
||||
she::Surface* part = theme->parts.transformationHandle()->bitmap(0);
|
||||
|
||||
if (int(mode) & int(app::gen::SymmetryMode::HORIZONTAL)) {
|
||||
double pos = symmetry.xAxis();
|
||||
gfx::PointF pt1, pt2;
|
||||
|
||||
if (horz) {
|
||||
pt1 = gfx::PointF(spriteBounds.x+pos, spriteBounds.y);
|
||||
pt1 = editor->editorToScreenF(pt1);
|
||||
pt2 = gfx::PointF(spriteBounds.x+pos, spriteBounds.y+spriteBounds.h);
|
||||
@ -875,8 +880,19 @@ bool StandbyState::Decorator::getSymmetryHandles(Editor* editor, gfx::Rect& box1
|
||||
pt2.y = std::min(pt2.y, editorViewport.point2().y-part->height());
|
||||
pt1.x -= part->width()/2;
|
||||
pt2.x -= part->width()/2;
|
||||
|
||||
handles.push_back(
|
||||
Handle(TOP,
|
||||
gfx::Rect(pt1.x, pt1.y, part->width(), part->height())));
|
||||
handles.push_back(
|
||||
Handle(BOTTOM,
|
||||
gfx::Rect(pt2.x, pt2.y, part->width(), part->height())));
|
||||
}
|
||||
else {
|
||||
|
||||
if (int(mode) & int(app::gen::SymmetryMode::VERTICAL)) {
|
||||
double pos = symmetry.yAxis();
|
||||
gfx::PointF pt1, pt2;
|
||||
|
||||
pt1 = gfx::PointF(spriteBounds.x, spriteBounds.y+pos);
|
||||
pt1 = editor->editorToScreenF(pt1);
|
||||
pt2 = gfx::PointF(spriteBounds.x+spriteBounds.w, spriteBounds.y+pos);
|
||||
@ -885,10 +901,15 @@ bool StandbyState::Decorator::getSymmetryHandles(Editor* editor, gfx::Rect& box1
|
||||
pt2.x = std::min(pt2.x, editorViewport.point2().x-part->width());
|
||||
pt1.y -= part->height()/2;
|
||||
pt2.y -= part->height()/2;
|
||||
|
||||
handles.push_back(
|
||||
Handle(LEFT,
|
||||
gfx::Rect(pt1.x, pt1.y, part->width(), part->height())));
|
||||
handles.push_back(
|
||||
Handle(RIGHT,
|
||||
gfx::Rect(pt2.x, pt2.y, part->width(), part->height())));
|
||||
}
|
||||
|
||||
box1 = gfx::Rect(pt1.x, pt1.y, part->width(), part->height());
|
||||
box2 = gfx::Rect(pt2.x, pt2.y, part->width(), part->height());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -49,11 +49,19 @@ namespace app {
|
||||
|
||||
class Decorator : public EditorDecorator {
|
||||
public:
|
||||
struct Handle {
|
||||
int align;
|
||||
gfx::Rect bounds;
|
||||
Handle(int align, const gfx::Rect& bounds)
|
||||
: align(align), bounds(bounds) { }
|
||||
};
|
||||
typedef std::vector<Handle> Handles;
|
||||
|
||||
Decorator(StandbyState* standbyState);
|
||||
virtual ~Decorator();
|
||||
|
||||
TransformHandles* getTransformHandles(Editor* editor);
|
||||
bool getSymmetryHandles(Editor* editor, gfx::Rect& box1, gfx::Rect& box2);
|
||||
bool getSymmetryHandles(Editor* editor, Handles& handles);
|
||||
|
||||
bool onSetCursor(tools::Ink* ink, Editor* editor, const gfx::Point& mouseScreenPos);
|
||||
|
||||
|
@ -165,6 +165,13 @@ public:
|
||||
case app::gen::SymmetryMode::VERTICAL:
|
||||
m_symmetry.reset(new app::tools::VerticalSymmetry(m_docPref.symmetry.yAxis()));
|
||||
break;
|
||||
|
||||
case app::gen::SymmetryMode::BOTH:
|
||||
m_symmetry.reset(
|
||||
new app::tools::SymmetryCombo(
|
||||
new app::tools::HorizontalSymmetry(m_docPref.symmetry.xAxis()),
|
||||
new app::tools::VerticalSymmetry(m_docPref.symmetry.yAxis())));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user