Add scaling from center/pivot (fix #576) and with flipping (fix #579)

This commit is contained in:
David Capello 2016-04-08 11:55:40 -03:00
parent 7d185d1543
commit 8a5de088e2
6 changed files with 99 additions and 112 deletions

View File

@ -473,6 +473,10 @@
keyboard shortcut you maintain aspect ratio -->
<key action="MaintainAspectRatio" shortcut="Shift" />
<!-- When you scale the selection, pressing this key the
selectino will be scaled from the pivot point -->
<key action="ScaleFromCenter" shortcut="Alt" />
<!-- Modifiers for selection tool -->
<key action="AddSelection" shortcut="Shift" />
<key action="SubtractSelection" shortcut="Shift+Alt" />

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
@ -355,6 +355,9 @@ bool MovingPixelsState::onMouseMove(Editor* editor, MouseMessage* msg)
if (int(action & KeyAction::MaintainAspectRatio))
moveModifier |= PixelsMovement::MaintainAspectRatioMovement;
if (int(action & KeyAction::ScaleFromCenter))
moveModifier |= PixelsMovement::ScaleFromPivot;
if (int(action & KeyAction::LockAxis))
moveModifier |= PixelsMovement::LockAxisMovement;

View File

@ -228,147 +228,123 @@ void PixelsMovement::moveImage(const gfx::Point& pos, MoveModifier moveModifier)
m_currentData.transformBox(oldCorners);
ContextWriter writer(m_reader, 1000);
int x1, y1, x2, y2;
x1 = m_initialData.bounds().x;
y1 = m_initialData.bounds().y;
x2 = m_initialData.bounds().x + m_initialData.bounds().w;
y2 = m_initialData.bounds().y + m_initialData.bounds().h;
gfx::Rect bounds = m_initialData.bounds();
bool updateBounds = false;
int dx, dy;
double dx, dy;
dx = int((pos.x - m_catchPos.x) * cos(m_currentData.angle()) +
(pos.y - m_catchPos.y) * -sin(m_currentData.angle()));
dy = int((pos.x - m_catchPos.x) * sin(m_currentData.angle()) +
(pos.y - m_catchPos.y) * cos(m_currentData.angle()));
dx = ((pos.x - m_catchPos.x) * cos(m_currentData.angle()) +
(pos.y - m_catchPos.y) * -sin(m_currentData.angle()));
dy = ((pos.x - m_catchPos.x) * sin(m_currentData.angle()) +
(pos.y - m_catchPos.y) * cos(m_currentData.angle()));
switch (m_handle) {
case MoveHandle:
x1 += dx;
y1 += dy;
x2 += dx;
y2 += dy;
updateBounds = true;
if ((moveModifier & LockAxisMovement) == LockAxisMovement) {
if (ABS(dx) < ABS(dy)) {
x1 -= dx;
x2 -= dx;
}
else {
y1 -= dy;
y2 -= dy;
}
if (ABS(dx) < ABS(dy))
dx = 0.0;
else
dy = 0.0;
}
bounds.offset(int(dx), int(dy));
updateBounds = true;
if ((moveModifier & SnapToGridMovement) == SnapToGridMovement) {
// Snap the x1,y1 point to the grid.
gfx::Rect gridBounds = App::instance()
->preferences().document(m_document).grid.bounds();
gfx::Point gridOffset(x1, y1);
gfx::Point gridOffset(bounds.origin());
gridOffset = snap_to_grid(gridBounds, gridOffset,
PreferSnapTo::ClosestGridVertex);
// Now we calculate the difference from x1,y1 point and we can
// use it to adjust all coordinates (x1, y1, x2, y2).
gridOffset -= gfx::Point(x1, y1);
x1 += gridOffset.x;
y1 += gridOffset.y;
x2 += gridOffset.x;
y2 += gridOffset.y;
gridOffset -= bounds.origin();
bounds.offset(gridOffset);
}
break;
case ScaleNWHandle:
x1 = MIN(x1+dx, x2-1);
y1 = MIN(y1+dy, y2-1);
if ((moveModifier & MaintainAspectRatioMovement) == MaintainAspectRatioMovement) {
if (1000 * (x2-x1) / getInitialImageSize().w >
1000 * (y2-y1) / getInitialImageSize().h) {
y1 = y2 - getInitialImageSize().h * (x2-x1) / getInitialImageSize().w;
}
else {
x1 = x2 - getInitialImageSize().w * (y2-y1) / getInitialImageSize().h;
}
}
updateBounds = true;
break;
case ScaleNHandle:
y1 = MIN(y1+dy, y2-1);
updateBounds = true;
break;
case ScaleNEHandle:
x2 = MAX(x2+dx, x1+1);
y1 = MIN(y1+dy, y2-1);
if ((moveModifier & MaintainAspectRatioMovement) == MaintainAspectRatioMovement) {
if (1000 * (x2-x1) / getInitialImageSize().w >
1000 * (y2-y1) / getInitialImageSize().h) {
y1 = y2 - getInitialImageSize().h * (x2-x1) / getInitialImageSize().w;
}
else {
x2 = x1 + getInitialImageSize().w * (y2-y1) / getInitialImageSize().h;
}
}
updateBounds = true;
break;
case ScaleWHandle:
x1 = MIN(x1+dx, x2-1);
updateBounds = true;
break;
case ScaleEHandle:
x2 = MAX(x2+dx, x1+1);
updateBounds = true;
break;
case ScaleSWHandle:
x1 = MIN(x1+dx, x2-1);
y2 = MAX(y2+dy, y1+1);
if ((moveModifier & MaintainAspectRatioMovement) == MaintainAspectRatioMovement) {
if (1000 * (x2-x1) / getInitialImageSize().w >
1000 * (y2-y1) / getInitialImageSize().h) {
y2 = y1 + getInitialImageSize().h * (x2-x1) / getInitialImageSize().w;
}
else {
x1 = x2 - getInitialImageSize().w * (y2-y1) / getInitialImageSize().h;
}
}
updateBounds = true;
break;
case ScaleSHandle:
y2 = MAX(y2+dy, y1+1);
updateBounds = true;
break;
case ScaleSEHandle: {
static double handles[][2] = {
{ 0.0, 0.0 }, { 0.5, 0.0 }, { 1.0, 0.0 },
{ 0.0, 0.5 }, { 1.0, 0.5 },
{ 0.0, 1.0 }, { 0.5, 1.0 }, { 1.0, 1.0 }
};
gfx::PointT<double> pivot;
gfx::PointT<double> handle(
handles[m_handle-ScaleNWHandle][0],
handles[m_handle-ScaleNWHandle][1]);
case ScaleSEHandle:
x2 = MAX(x2+dx, x1+1);
y2 = MAX(y2+dy, y1+1);
if ((moveModifier & MaintainAspectRatioMovement) == MaintainAspectRatioMovement) {
if (1000 * (x2-x1) / getInitialImageSize().w >
1000 * (y2-y1) / getInitialImageSize().h) {
y2 = y1 + getInitialImageSize().h * (x2-x1) / getInitialImageSize().w;
}
else {
x2 = x1 + getInitialImageSize().w * (y2-y1) / getInitialImageSize().h;
}
if ((moveModifier & ScaleFromPivot) == ScaleFromPivot) {
pivot.x = m_currentData.pivot().x;
pivot.y = m_currentData.pivot().y;
}
else {
pivot.x = 1.0 - handle.x;
pivot.y = 1.0 - handle.y;
pivot.x = bounds.x + bounds.w*pivot.x;
pivot.y = bounds.y + bounds.h*pivot.y;
}
gfx::Point a = bounds.origin();
gfx::Point b = bounds.point2();
if ((moveModifier & MaintainAspectRatioMovement) == MaintainAspectRatioMovement) {
auto u = point2Vector(gfx::PointT<double>(m_catchPos) - pivot);
auto v = point2Vector(gfx::PointT<double>(pos) - pivot);
auto w = v.projectOn(u);
double scale = u.magnitude();
if (scale != 0.0)
scale = (std::fabs(w.angle()-u.angle()) < PI/2.0 ? 1.0: -1.0) * w.magnitude() / scale;
else
scale = 1.0;
a.x = int((a.x-pivot.x)*scale + pivot.x);
a.y = int((a.y-pivot.y)*scale + pivot.y);
b.x = int((b.x-pivot.x)*scale + pivot.x);
b.y = int((b.y-pivot.y)*scale + pivot.y);
}
else {
handle.x = bounds.x + bounds.w*handle.x;
handle.y = bounds.y + bounds.h*handle.y;
double w = (handle.x-pivot.x);
double h = (handle.y-pivot.y);
if (m_handle == ScaleNHandle || m_handle == ScaleSHandle) {
dx = 0.0;
w = 1.0; // Any value != 0.0 to avoid div by zero
}
else if (m_handle == ScaleWHandle || m_handle == ScaleEHandle) {
dy = 0.0;
h = 1.0;
}
a.x = (a.x-pivot.x)*(1.0+dx/w) + pivot.x;
a.y = (a.y-pivot.y)*(1.0+dy/h) + pivot.y;
b.x = (b.x-pivot.x)*(1.0+dx/w) + pivot.x;
b.y = (b.y-pivot.y)*(1.0+dy/h) + pivot.y;
}
// Do not use "gfx::Rect(a, b)" here because if a > b we want to
// keep a rectangle with negative width or height (to know that
// it was flipped).
bounds.x = a.x;
bounds.y = a.y;
bounds.w = b.x - a.x;
bounds.h = b.y - a.y;
updateBounds = true;
break;
}
case RotateNWHandle:
case RotateNHandle:
@ -432,7 +408,7 @@ void PixelsMovement::moveImage(const gfx::Point& pos, MoveModifier moveModifier)
}
if (updateBounds) {
m_currentData.bounds(gfx::Rect(x1, y1, x2 - x1, y2 - y1));
m_currentData.bounds(bounds);
m_adjustPivot = true;
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
@ -44,7 +44,8 @@ namespace app {
SnapToGridMovement = 2,
AngleSnapMovement = 4,
MaintainAspectRatioMovement = 8,
LockAxisMovement = 16
LockAxisMovement = 16,
ScaleFromPivot = 32,
};
PixelsMovement(Context* context,

View File

@ -39,6 +39,7 @@ namespace {
{ "SnapToGrid" , "Snap To Grid" , app::KeyAction::SnapToGrid },
{ "AngleSnap" , "Angle Snap" , app::KeyAction::AngleSnap },
{ "MaintainAspectRatio" , "Maintain Aspect Ratio", app::KeyAction::MaintainAspectRatio },
{ "ScaleFromCenter" , "Scale From Center" , app::KeyAction::ScaleFromCenter },
{ "LockAxis" , "Lock Axis" , app::KeyAction::LockAxis },
{ "AddSelection" , "Add Selection" , app::KeyAction::AddSelection },
{ "SubtractSelection" , "Subtract Selection" , app::KeyAction::SubtractSelection },
@ -147,6 +148,7 @@ Key::Key(KeyAction action)
m_keycontext = KeyContext::RotatingSelection;
break;
case KeyAction::MaintainAspectRatio:
case KeyAction::ScaleFromCenter:
m_keycontext = KeyContext::ScalingSelection;
break;
case KeyAction::AddSelection:

View File

@ -71,6 +71,7 @@ namespace app {
MoveOrigin = 0x00000800,
SquareAspect = 0x00001000,
DrawFromCenter = 0x00002000,
ScaleFromCenter = 0x00004000,
};
inline KeyAction operator&(KeyAction a, KeyAction b) {