mirror of https://github.com/aseprite/aseprite.git
Use doc::Grid for first argument of snap_to_grid
Includes changes to doc::Grid::Type to enable shared/thick edges for isometric grid, as well as minor fixes for isometric 'Snap To'
This commit is contained in:
parent
58011d9f87
commit
7d78f4f5d1
|
@ -833,7 +833,8 @@ y = Y:
|
|||
width = Width:
|
||||
height = Height:
|
||||
type_orthogonal = Orthogonal
|
||||
type_isometric = Isometric
|
||||
type_isometric_shared = Isometric (Shared Edges)
|
||||
type_isometric_thick = Isometric (Thick Edges)
|
||||
|
||||
[home_view]
|
||||
title = Home
|
||||
|
@ -1459,7 +1460,8 @@ grid_y = Y:
|
|||
grid_width = Width:
|
||||
grid_height = Height:
|
||||
grid_type_orthogonal = Orthogonal
|
||||
grid_type_isometric = Isometric
|
||||
grid_type_isometric_shared = Isometric (Shared Edges)
|
||||
grid_type_isometric_thick = Isometric (Thick Edges)
|
||||
grid_color = Color:
|
||||
grid_opacity = Opacity:
|
||||
grid_auto = Auto
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
<boxfiller />
|
||||
<combobox id="grid_type" expansive="true" cell_hspan="3">
|
||||
<listitem text="@.type_orthogonal" />
|
||||
<listitem text="@.type_isometric" />
|
||||
<listitem text="@.type_isometric_shared" />
|
||||
<listitem text="@.type_isometric_thick" />
|
||||
</combobox>
|
||||
|
||||
<separator horizontal="true" cell_hspan="4" />
|
||||
|
|
|
@ -428,7 +428,8 @@
|
|||
<label text="" />
|
||||
<combobox id="grid_type" expansive="true" cell_hspan="3">
|
||||
<listitem text="@.grid_type_orthogonal" />
|
||||
<listitem text="@.grid_type_isometric" />
|
||||
<listitem text="@.grid_type_isometric_shared" />
|
||||
<listitem text="@.grid_type_isometric_thick" />
|
||||
</combobox>
|
||||
<hbox />
|
||||
|
||||
|
|
|
@ -54,8 +54,10 @@ std::string grid_type_to_string(const doc::Grid::Type t)
|
|||
{
|
||||
if (t == doc::Grid::Type::Orthogonal)
|
||||
return app::Strings::grid_settings_type_orthogonal();
|
||||
if (t == doc::Grid::Type::Isometric)
|
||||
return app::Strings::grid_settings_type_isometric();
|
||||
if (t == doc::Grid::Type::IsometricSharedEdges)
|
||||
return app::Strings::grid_settings_type_isometric_shared();
|
||||
if (t == doc::Grid::Type::IsometricThickEdges)
|
||||
return app::Strings::grid_settings_type_isometric_thick();
|
||||
|
||||
throw base::Exception("Invalid grid type index: " + std::to_string(int(t)));
|
||||
}
|
||||
|
@ -64,8 +66,10 @@ doc::Grid::Type string_to_grid_type(const std::string& s)
|
|||
{
|
||||
if (s == app::Strings::grid_settings_type_orthogonal())
|
||||
return doc::Grid::Type::Orthogonal;
|
||||
if (s == app::Strings::grid_settings_type_isometric())
|
||||
return doc::Grid::Type::Isometric;
|
||||
if (s == app::Strings::grid_settings_type_isometric_shared())
|
||||
return doc::Grid::Type::IsometricSharedEdges;
|
||||
if (s == app::Strings::grid_settings_type_isometric_thick())
|
||||
return doc::Grid::Type::IsometricThickEdges;
|
||||
|
||||
throw base::Exception("Invalid string for grid type: " + s);
|
||||
}
|
||||
|
|
|
@ -79,9 +79,10 @@ void SelectTileCommand::onExecute(Context* ctx)
|
|||
mask->copyFrom(doc->mask());
|
||||
|
||||
{
|
||||
gfx::Rect gridBounds = writer.site().gridBounds();
|
||||
const doc::Grid& grid = writer.site().grid();
|
||||
gfx::Rect gridBounds(grid.bounds());
|
||||
gfx::Point pos = editor->screenToEditor(editor->mousePosInDisplay());
|
||||
pos = snap_to_grid(gridBounds, pos, PreferSnapTo::BoxOrigin);
|
||||
pos = snap_to_grid(grid, pos, PreferSnapTo::BoxOrigin);
|
||||
gridBounds.setOrigin(pos);
|
||||
|
||||
switch (m_mode) {
|
||||
|
|
|
@ -1030,11 +1030,11 @@ void DocExporter::captureSamples(Samples& samples, base::task_token& token)
|
|||
if (m_trimCels) {
|
||||
// TODO merge this code with the code in DocApi::trimSprite()
|
||||
if (m_trimByGrid) {
|
||||
const gfx::Rect& gridBounds = doc->sprite()->gridBounds();
|
||||
const doc::Grid grid(sprite->gridBounds(), sprite->gridType());
|
||||
gfx::Point posTopLeft =
|
||||
snap_to_grid(gridBounds, frameBounds.origin(), PreferSnapTo::FloorGrid);
|
||||
snap_to_grid(grid, frameBounds.origin(), PreferSnapTo::FloorGrid);
|
||||
gfx::Point posBottomRight =
|
||||
snap_to_grid(gridBounds, frameBounds.point2(), PreferSnapTo::CeilGrid);
|
||||
snap_to_grid(grid, frameBounds.point2(), PreferSnapTo::CeilGrid);
|
||||
frameBounds = gfx::Rect(posTopLeft, posBottomRight);
|
||||
}
|
||||
sample.setTrimmedBounds(frameBounds);
|
||||
|
@ -1053,8 +1053,9 @@ void DocExporter::captureSamples(Samples& samples, base::task_token& token)
|
|||
|
||||
if (item.splitGrid) {
|
||||
const gfx::Rect& gridBounds = sprite->gridBounds();
|
||||
const doc::Grid grid(gridBounds, sprite->gridType());
|
||||
gfx::Point initPos(0, 0), pos;
|
||||
initPos = pos = snap_to_grid(gridBounds, initPos, PreferSnapTo::BoxOrigin);
|
||||
initPos = pos = snap_to_grid(grid, initPos, PreferSnapTo::BoxOrigin);
|
||||
|
||||
for (; pos.y + gridBounds.h <= spriteBounds.h; pos.y += gridBounds.h) {
|
||||
for (pos.x = initPos.x; pos.x + gridBounds.w <= spriteBounds.w; pos.x += gridBounds.w) {
|
||||
|
|
|
@ -23,13 +23,17 @@
|
|||
|
||||
namespace app {
|
||||
|
||||
gfx::Point snap_to_isometric_grid(const gfx::Rect& grid,
|
||||
gfx::Point snap_to_isometric_grid(const doc::Grid& docGrid,
|
||||
const gfx::Point& point,
|
||||
const PreferSnapTo prefer)
|
||||
{
|
||||
const gfx::Rect grid(docGrid.bounds());
|
||||
if (grid.isEmpty())
|
||||
return point;
|
||||
|
||||
// Because we force unworkable grid sizes to share a pixel,
|
||||
// we need to account for that here
|
||||
auto guide = doc::Grid::IsometricGuide(grid.size());
|
||||
const auto guide = docGrid.getIsometricGuide();
|
||||
const int width = grid.w - int(!guide.evenWidth);
|
||||
const int height = grid.h - int(!guide.evenHeight);
|
||||
|
||||
|
@ -37,7 +41,8 @@ gfx::Point snap_to_isometric_grid(const gfx::Rect& grid,
|
|||
const gfx::PointF newPoint(int((point.x - grid.x) / width) * width,
|
||||
int((point.y - grid.y) / height) * height);
|
||||
// And then make it relative to the center of a cell
|
||||
const gfx::PointF vto((newPoint + gfx::Point(guide.end.x, guide.start.y)) - point);
|
||||
const gfx::PointF vto((newPoint + gfx::Point(guide.end.x, guide.start.y)) -
|
||||
(point - grid.origin()));
|
||||
|
||||
// The following happens here:
|
||||
//
|
||||
|
@ -58,7 +63,7 @@ gfx::Point snap_to_isometric_grid(const gfx::Rect& grid,
|
|||
|
||||
if (prefer != PreferSnapTo::ClosestGridVertex) {
|
||||
// We use the pixel-precise grid for this bounds-check
|
||||
const auto line = doc::Grid::getIsometricLine(grid.size());
|
||||
const auto line = docGrid.getIsometricLine();
|
||||
const int index = int(ABS(vto.y) - int(vto.y > 0)) + 1;
|
||||
const gfx::Point co(-vto.x + guide.end.x, -vto.y + guide.start.y);
|
||||
const gfx::Point& p = line[index];
|
||||
|
@ -112,17 +117,18 @@ gfx::Point snap_to_isometric_grid(const gfx::Rect& grid,
|
|||
return gfx::Point(newPoint + near + grid.origin());
|
||||
}
|
||||
|
||||
gfx::Point snap_to_grid(const gfx::Rect& grid, const gfx::Point& point, const PreferSnapTo prefer)
|
||||
gfx::Point snap_to_grid(const doc::Grid& docGrid,
|
||||
const gfx::Point& point,
|
||||
const PreferSnapTo prefer)
|
||||
{
|
||||
// Use different logic for isometric grid
|
||||
if (docGrid.isIsometric())
|
||||
return snap_to_isometric_grid(docGrid, point, prefer);
|
||||
|
||||
const gfx::Rect grid(docGrid.bounds());
|
||||
if (grid.isEmpty())
|
||||
return point;
|
||||
|
||||
// Use different logic for isometric grid
|
||||
const doc::Grid::Type gridType =
|
||||
App::instance()->preferences().document(App::instance()->context()->documents()[0]).grid.type();
|
||||
if (gridType == doc::Grid::Type::Isometric)
|
||||
return snap_to_isometric_grid(grid, point, prefer);
|
||||
|
||||
div_t d, dx, dy;
|
||||
dx = std::div(grid.x, grid.w);
|
||||
dy = std::div(grid.y, grid.h);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#define APP_SNAP_TO_GRID_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/grid.h"
|
||||
#include "gfx/fwd.h"
|
||||
|
||||
namespace app {
|
||||
|
@ -21,7 +22,9 @@ enum class PreferSnapTo {
|
|||
CeilGrid,
|
||||
};
|
||||
|
||||
gfx::Point snap_to_grid(const gfx::Rect& grid, const gfx::Point& point, const PreferSnapTo prefer);
|
||||
gfx::Point snap_to_grid(const doc::Grid& docGrid,
|
||||
const gfx::Point& point,
|
||||
const PreferSnapTo prefer);
|
||||
|
||||
} // namespace app
|
||||
|
||||
|
|
|
@ -18,26 +18,26 @@ namespace app { namespace tools {
|
|||
using namespace gfx;
|
||||
|
||||
// Adjustment for snap to isometric grid
|
||||
static void snap_isometric_line(ToolLoop* loop, Stroke& stroke, bool lineCtl)
|
||||
static void snap_isometric_line(ToolLoop* loop, Stroke& stroke)
|
||||
{
|
||||
// Get last two points
|
||||
Stroke::Pt& a = stroke[stroke.size() - 2];
|
||||
Stroke::Pt& b = stroke[stroke.size() - 1];
|
||||
|
||||
// Get function invoked by line tool
|
||||
bool lineTool = (string_id_to_brush_type(loop->getTool()->getId()) == kLineBrushType);
|
||||
|
||||
// TODO: rectangles and ellipses
|
||||
if (lineCtl && !loop->getIntertwine()->snapByAngle())
|
||||
if (loop->getController()->isTwoPoints() && !loop->getIntertwine()->snapByAngle())
|
||||
return;
|
||||
|
||||
// Get line angle
|
||||
PointF vto(b.x - a.x, b.y - a.y);
|
||||
double len = ABS(vto.x) + ABS(vto.y);
|
||||
vto /= len;
|
||||
// Get line direction
|
||||
const double len = ABS(b.x - a.x) + ABS(b.y - a.y);
|
||||
const PointF vto((b.x - a.x) / len, (b.y - a.y) / len);
|
||||
|
||||
const gfx::Rect& grid = loop->getGridBounds();
|
||||
const auto line = doc::Grid::IsometricGuide(grid.size());
|
||||
const doc::Grid& docGrid = loop->getGrid();
|
||||
const gfx::Rect grid(docGrid.bounds());
|
||||
const auto line = docGrid.getIsometricGuide();
|
||||
|
||||
// Get function invoked by line tool
|
||||
const bool lineTool = loop->getTool()->getId() == WellKnownTools::Line;
|
||||
|
||||
// Offset vertical lines/single point to the left for line tool.
|
||||
// Because pressing the angle snap key will bypass this function,
|
||||
|
@ -83,9 +83,7 @@ static void snap_isometric_line(ToolLoop* loop, Stroke& stroke, bool lineCtl)
|
|||
// for freehand strokes would require changes to intertwiners,
|
||||
// not just the freehand controller itself.
|
||||
if (lineTool && vto.x < 0 && a.x % (grid.w - !line.evenWidth)) {
|
||||
auto tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
std::swap(a, b);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -159,8 +157,8 @@ public:
|
|||
m_last = pt;
|
||||
stroke.addPoint(pt);
|
||||
if (loop->getController()->canSnapToGrid() && loop->getSnapToGrid() &&
|
||||
loop->sprite()->gridType() == doc::Grid::Type::Isometric) {
|
||||
snap_isometric_line(loop, stroke, false);
|
||||
doc::Grid::isIsometric(loop->sprite()->gridType())) {
|
||||
snap_isometric_line(loop, stroke);
|
||||
m_last = stroke[stroke.size() - 1];
|
||||
}
|
||||
}
|
||||
|
@ -221,8 +219,8 @@ public:
|
|||
!(int(loop->getModifiers()) & int(ToolLoopModifiers::kSquareAspect)) &&
|
||||
|
||||
// And snapping to isometric grid
|
||||
(loop->getSnapToGrid() && loop->sprite()->gridType() == doc::Grid::Type::Isometric)) {
|
||||
snap_isometric_line(loop, stroke, true);
|
||||
(loop->getSnapToGrid() && doc::Grid::isIsometric(loop->sprite()->gridType()))) {
|
||||
snap_isometric_line(loop, stroke);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,7 +254,7 @@ public:
|
|||
|
||||
bool isoAngle = false;
|
||||
bool isoMode = loop->getController()->canSnapToGrid() && loop->getSnapToGrid() &&
|
||||
loop->sprite()->gridType() == doc::Grid::Type::Isometric;
|
||||
doc::Grid::isIsometric(loop->sprite()->gridType());
|
||||
|
||||
if ((int(loop->getModifiers()) & int(ToolLoopModifiers::kSquareAspect))) {
|
||||
int dx = stroke[1].x - m_first.x;
|
||||
|
@ -302,7 +300,7 @@ public:
|
|||
}
|
||||
}
|
||||
else if (isoMode) {
|
||||
snap_isometric_line(loop, stroke, true);
|
||||
snap_isometric_line(loop, stroke);
|
||||
}
|
||||
|
||||
if (hasAngle()) {
|
||||
|
@ -393,7 +391,7 @@ public:
|
|||
private:
|
||||
void snapPointsToGridTiles(ToolLoop* loop, Stroke& stroke)
|
||||
{
|
||||
auto grid = loop->getGridBounds();
|
||||
const auto& grid = loop->getGrid();
|
||||
|
||||
Rect a(snap_to_grid(grid, stroke[0].toPoint(), PreferSnapTo::BoxOrigin),
|
||||
snap_to_grid(grid, stroke[0].toPoint(), PreferSnapTo::BoxEnd));
|
||||
|
|
|
@ -148,7 +148,7 @@ doc::AlgoLineWithAlgoPixel Intertwine::getLineAlgo(ToolLoop* loop,
|
|||
// "Snap to Grid" is enabled...
|
||||
(loop->getController()->canSnapToGrid() && loop->getSnapToGrid() &&
|
||||
// And we are not in isometric mode
|
||||
loop->sprite()->gridType() != doc::Grid::Type::Isometric)) {
|
||||
!doc::Grid::isIsometric(loop->sprite()->gridType()))) {
|
||||
// We prefer the perfect pixel lines that matches grid tiles
|
||||
return (needsFixForLineBrush ? algo_line_perfect_with_fix_for_line_brush : algo_line_perfect);
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ const char* WellKnownTools::Eraser = "eraser";
|
|||
const char* WellKnownTools::Eyedropper = "eyedropper";
|
||||
const char* WellKnownTools::Hand = "hand";
|
||||
const char* WellKnownTools::Move = "move";
|
||||
const char* WellKnownTools::Line = "line";
|
||||
|
||||
const char* WellKnownInks::Selection = "selection";
|
||||
const char* WellKnownInks::Paint = "paint";
|
||||
|
|
|
@ -30,6 +30,7 @@ extern const char* Eraser;
|
|||
extern const char* Eyedropper;
|
||||
extern const char* Hand;
|
||||
extern const char* Move;
|
||||
extern const char* Line;
|
||||
}; // namespace WellKnownTools
|
||||
|
||||
namespace WellKnownInks {
|
||||
|
|
|
@ -300,7 +300,7 @@ void ToolLoopManager::snapToGrid(Stroke::Pt& pt)
|
|||
return;
|
||||
|
||||
gfx::Point point(pt.x, pt.y);
|
||||
point = snap_to_grid(m_toolLoop->getGridBounds(), point, PreferSnapTo::ClosestGridVertex);
|
||||
point = snap_to_grid(m_toolLoop->getGrid(), point, PreferSnapTo::ClosestGridVertex);
|
||||
point += m_toolLoop->getBrush()->center();
|
||||
pt.x = point.x;
|
||||
pt.y = point.y;
|
||||
|
|
|
@ -190,8 +190,8 @@ void BrushPreview::show(const gfx::Point& screenPos)
|
|||
// Get cursor position in the editor
|
||||
gfx::Point spritePos = m_editor->screenToEditor(screenPos);
|
||||
if (pref.cursor.snapToGrid() && m_editor->docPref().grid.snap()) {
|
||||
spritePos =
|
||||
snap_to_grid(m_editor->docPref().grid.bounds(), spritePos, PreferSnapTo::ClosestGridVertex) +
|
||||
const doc::Grid grid(m_editor->docPref().grid.bounds(), m_editor->docPref().grid.type());
|
||||
spritePos = snap_to_grid(grid, spritePos, PreferSnapTo::ClosestGridVertex) +
|
||||
gfx::Point(brushBounds.w / 2, brushBounds.h / 2);
|
||||
}
|
||||
|
||||
|
|
|
@ -1149,7 +1149,7 @@ void Editor::drawGrid(Graphics* g,
|
|||
gfx::rgba(gfx::getr(grid_color), gfx::getg(grid_color), gfx::getb(grid_color), alpha);
|
||||
|
||||
// Orthogonal grid
|
||||
if (isPixelGrid || m_sprite->gridType() == doc::Grid::Type::Orthogonal) {
|
||||
if (isPixelGrid || !doc::Grid::isIsometric(m_sprite->gridType())) {
|
||||
// Draw horizontal lines
|
||||
int x1 = spriteBounds.x;
|
||||
int y1 = gridF.y;
|
||||
|
@ -1168,6 +1168,7 @@ void Editor::drawGrid(Graphics* g,
|
|||
}
|
||||
// Isometric grid
|
||||
else {
|
||||
const doc::Grid docGrid(grid, m_sprite->gridType());
|
||||
const RectF pix(editorToScreenF(RectF(0, 0, 1, 1)));
|
||||
int x1 = gridF.x;
|
||||
int y1 = gridF.y;
|
||||
|
@ -1176,7 +1177,7 @@ void Editor::drawGrid(Graphics* g,
|
|||
int dx = std::round(grid.w * pix.w);
|
||||
int dy = std::round(grid.h * pix.h);
|
||||
|
||||
auto guide = doc::Grid::IsometricGuide(grid.size());
|
||||
const auto guide = docGrid.getIsometricGuide();
|
||||
|
||||
// Diamonds share a side when their size is uneven
|
||||
dx -= pix.w * int(!guide.evenWidth);
|
||||
|
@ -1201,7 +1202,7 @@ void Editor::drawGrid(Graphics* g,
|
|||
|
||||
// Move single cell across the screen
|
||||
// to draw entire grid
|
||||
Path& cell = getIsometricGridPath(grid);
|
||||
Path& cell = getIsometricGridPath(docGrid);
|
||||
|
||||
for (int y = y1; y < y2; y += dy) {
|
||||
for (int x = x1; x < x2; x += dx) {
|
||||
|
@ -1259,14 +1260,17 @@ void Editor::drawGrid(Graphics* g,
|
|||
}
|
||||
}
|
||||
|
||||
gfx::Path& Editor::getIsometricGridPath(Rect& grid)
|
||||
gfx::Path& Editor::getIsometricGridPath(const doc::Grid& docGrid)
|
||||
{
|
||||
static Path path;
|
||||
static Size prevSize(0, 0);
|
||||
static double prevScale = 0.00;
|
||||
static doc::Grid::Type prevType = doc::Grid::Type::Orthogonal;
|
||||
|
||||
// Regenerate bitmap on zoom or grid size change
|
||||
if (prevScale != m_proj.zoom().scale() || prevSize != grid.size()) {
|
||||
if (prevScale != m_proj.zoom().scale() || prevSize != docGrid.tileSize() ||
|
||||
prevType != docGrid.type()) {
|
||||
const Rect grid(docGrid.bounds());
|
||||
const RectF pix(editorToScreenF(RectF(0, 0, 1, 1)));
|
||||
doc::ImageRef imref(doc::Image::create(doc::PixelFormat::IMAGE_BITMAP,
|
||||
std::round(grid.w * pix.w),
|
||||
|
@ -1280,7 +1284,7 @@ gfx::Path& Editor::getIsometricGridPath(Rect& grid)
|
|||
// Prepare bitmap from points of pixel precise line.
|
||||
// A single grid cell is calculated from these
|
||||
im->clear(0);
|
||||
for (const auto& p : doc::Grid::getIsometricLine(grid.size()))
|
||||
for (const auto& p : docGrid.getIsometricLine())
|
||||
fill_rect(im,
|
||||
std::round(p.x * pix.w),
|
||||
std::round((grid.h - p.y) * pix.h),
|
||||
|
@ -1295,8 +1299,8 @@ gfx::Path& Editor::getIsometricGridPath(Rect& grid)
|
|||
}
|
||||
// Remember scale used to generate the current path
|
||||
prevScale = m_proj.zoom().scale();
|
||||
prevSize.w = grid.w;
|
||||
prevSize.h = grid.h;
|
||||
prevSize = docGrid.tileSize();
|
||||
prevType = docGrid.type();
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@ -2938,8 +2942,8 @@ void Editor::pasteImage(const Image* image, const Mask* mask, const gfx::Point*
|
|||
// Snap to grid a pasted tilemap
|
||||
// TODO should we move this to PixelsMovement or MovingPixelsState?
|
||||
if (site.tilemapMode() == TilemapMode::Tiles) {
|
||||
gfx::Rect gridBounds = site.gridBounds();
|
||||
gfx::Point pt = snap_to_grid(gridBounds, gfx::Point(x, y), PreferSnapTo::ClosestGridVertex);
|
||||
const doc::Grid& grid = site.grid();
|
||||
const gfx::Point pt = snap_to_grid(grid, gfx::Point(x, y), PreferSnapTo::ClosestGridVertex);
|
||||
x = pt.x;
|
||||
y = pt.y;
|
||||
}
|
||||
|
|
|
@ -368,7 +368,7 @@ private:
|
|||
const app::Color& color,
|
||||
int alpha,
|
||||
bool isPixelGrid);
|
||||
gfx::Path& getIsometricGridPath(gfx::Rect& grid);
|
||||
gfx::Path& getIsometricGridPath(const doc::Grid& docGrid);
|
||||
void drawSlices(ui::Graphics* g);
|
||||
void drawTileNumbers(ui::Graphics* g, const Cel* cel);
|
||||
void drawCelBounds(ui::Graphics* g, const Cel* cel, const gfx::Color color);
|
||||
|
|
|
@ -236,8 +236,15 @@ void MovingCelState::calcPivot()
|
|||
// to be relative to initial position
|
||||
m_fullBounds = calcFullBounds();
|
||||
m_pivot = gfx::PointF(0, 0);
|
||||
const gfx::RectF& gridBounds = m_editor->getSite().gridBounds();
|
||||
m_pivotOffset = gfx::PointF(gridBounds.size()) - (m_pivot - m_fullBounds.origin());
|
||||
const doc::Grid& grid = m_editor->getSite().grid();
|
||||
if (grid.isIsometric()) {
|
||||
m_pivotOffset = -gfx::PointF(
|
||||
snap_to_grid(grid, gfx::Point(m_fullBounds.origin()), PreferSnapTo::ClosestGridVertex) +
|
||||
(m_pivot - m_fullBounds.origin()));
|
||||
}
|
||||
else {
|
||||
m_pivotOffset = gfx::PointF(grid.tileSize()) - (m_pivot - m_fullBounds.origin());
|
||||
}
|
||||
}
|
||||
|
||||
void MovingCelState::onCommitMouseMove(Editor* editor, const gfx::PointF& newCursorPos)
|
||||
|
@ -395,11 +402,16 @@ gfx::RectF MovingCelState::calcFullBounds() const
|
|||
return bounds;
|
||||
}
|
||||
|
||||
doc::Grid MovingCelState::getDisplacedGrid() const
|
||||
{
|
||||
doc::Grid displacedGrid(m_editor->getSite().grid());
|
||||
displacedGrid.origin(displacedGrid.origin() + m_pivotOffset);
|
||||
return displacedGrid;
|
||||
}
|
||||
|
||||
void MovingCelState::snapOffsetToGrid(gfx::Point& offset) const
|
||||
{
|
||||
const gfx::RectF& gridBounds = m_editor->getSite().gridBounds();
|
||||
const gfx::RectF displaceGrid(gridBounds.origin() + m_pivotOffset, gridBounds.size());
|
||||
offset = snap_to_grid(displaceGrid,
|
||||
offset = snap_to_grid(getDisplacedGrid(),
|
||||
gfx::Point(m_fullBounds.origin() + offset),
|
||||
PreferSnapTo::ClosestGridVertex) -
|
||||
m_fullBounds.origin();
|
||||
|
@ -407,21 +419,20 @@ void MovingCelState::snapOffsetToGrid(gfx::Point& offset) const
|
|||
|
||||
void MovingCelState::snapBoundsToGrid(gfx::RectF& celBounds) const
|
||||
{
|
||||
const gfx::RectF& gridBounds = m_editor->getSite().gridBounds();
|
||||
const gfx::RectF displaceGrid(gridBounds.origin() + m_pivotOffset, gridBounds.size());
|
||||
const gfx::PointF& origin = celBounds.origin();
|
||||
if (m_scaled) {
|
||||
gfx::PointF gridOffset(snap_to_grid(displaceGrid,
|
||||
const doc::Grid displacedGrid(getDisplacedGrid());
|
||||
gfx::Point gridOffset(snap_to_grid(displacedGrid,
|
||||
gfx::Point(origin.x + celBounds.w, origin.y + celBounds.h),
|
||||
PreferSnapTo::ClosestGridVertex) -
|
||||
origin);
|
||||
|
||||
celBounds.w = std::max(gridBounds.w, gridOffset.x);
|
||||
celBounds.h = std::max(gridBounds.h, gridOffset.y);
|
||||
celBounds.w = std::max(displacedGrid.bounds().w, gridOffset.x);
|
||||
celBounds.h = std::max(displacedGrid.bounds().h, gridOffset.y);
|
||||
}
|
||||
else if (m_moved) {
|
||||
gfx::PointF gridOffset(
|
||||
snap_to_grid(displaceGrid, gfx::Point(origin), PreferSnapTo::ClosestGridVertex));
|
||||
snap_to_grid(getDisplacedGrid(), gfx::Point(origin), PreferSnapTo::ClosestGridVertex));
|
||||
|
||||
celBounds.setOrigin(gridOffset);
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ private:
|
|||
gfx::RectF calcFullBounds() const;
|
||||
void calcPivot();
|
||||
bool restoreCelStartPosition() const;
|
||||
doc::Grid getDisplacedGrid() const;
|
||||
void snapOffsetToGrid(gfx::Point& offset) const;
|
||||
void snapBoundsToGrid(gfx::RectF& celBounds) const;
|
||||
// ContextObserver
|
||||
|
|
|
@ -386,9 +386,9 @@ void PixelsMovement::moveImage(const gfx::PointF& pos, MoveModifier moveModifier
|
|||
// Movement through mouse/trackpad:
|
||||
const int gridW = m_site.gridBounds().w;
|
||||
const int gridH = m_site.gridBounds().h;
|
||||
gfx::PointF point(snap_to_grid(gfx::Rect(0, 0, gridW, gridH),
|
||||
(gfx::Point)(pos - m_catchPos),
|
||||
PreferSnapTo::ClosestGridVertex));
|
||||
const doc::Grid grid(gfx::Rect(0, 0, gridW, gridH), m_site.gridType());
|
||||
const gfx::PointF point(
|
||||
snap_to_grid(grid, (gfx::Point)(pos - m_catchPos), PreferSnapTo::ClosestGridVertex));
|
||||
dx = point.x;
|
||||
dy = point.y;
|
||||
}
|
||||
|
@ -413,7 +413,7 @@ void PixelsMovement::moveImage(const gfx::PointF& pos, MoveModifier moveModifier
|
|||
|
||||
if (!tilesModeOn && (moveModifier & SnapToGridMovement) == SnapToGridMovement) {
|
||||
// Snap the x1,y1 point to the grid.
|
||||
gfx::PointF gridOffset(snap_to_grid(m_site.gridBounds(),
|
||||
const gfx::PointF gridOffset(snap_to_grid(m_site.grid(),
|
||||
gfx::Point(bounds.origin()),
|
||||
PreferSnapTo::ClosestGridVertex));
|
||||
|
||||
|
@ -528,9 +528,9 @@ void PixelsMovement::moveImage(const gfx::PointF& pos, MoveModifier moveModifier
|
|||
// unless the corners are inverted (a > b)
|
||||
b.x = b.x - (a.x <= b.x ? 1 : 0);
|
||||
b.y = b.y - (a.y <= b.y ? 1 : 0);
|
||||
gfx::Rect gridBounds = m_site.gridBounds();
|
||||
a = gfx::PointF(snap_to_grid(gridBounds, gfx::Point(a), PreferSnapTo::BoxOrigin));
|
||||
b = gfx::PointF(snap_to_grid(gridBounds, gfx::Point(b), PreferSnapTo::BoxEnd));
|
||||
const doc::Grid& grid = m_site.grid();
|
||||
a = gfx::PointF(snap_to_grid(grid, gfx::Point(a), PreferSnapTo::BoxOrigin));
|
||||
b = gfx::PointF(snap_to_grid(grid, gfx::Point(b), PreferSnapTo::BoxEnd));
|
||||
}
|
||||
|
||||
// Do not use "gfx::Rect(a, b)" here because if a > b we want to
|
||||
|
|
|
@ -259,9 +259,9 @@ gfx::Rect get_trimmed_bounds(const doc::Sprite* sprite, const bool byGrid)
|
|||
|
||||
// TODO merge this code with the code in DocExporter::captureSamples()
|
||||
if (byGrid) {
|
||||
const gfx::Rect& gridBounds = sprite->gridBounds();
|
||||
gfx::Point posTopLeft = snap_to_grid(gridBounds, bounds.origin(), PreferSnapTo::FloorGrid);
|
||||
gfx::Point posBottomRight = snap_to_grid(gridBounds, bounds.point2(), PreferSnapTo::CeilGrid);
|
||||
const doc::Grid grid(sprite->gridBounds(), sprite->gridType());
|
||||
const gfx::Point posTopLeft(snap_to_grid(grid, bounds.origin(), PreferSnapTo::FloorGrid));
|
||||
const gfx::Point posBottomRight(snap_to_grid(grid, bounds.point2(), PreferSnapTo::CeilGrid));
|
||||
bounds = gfx::Rect(posTopLeft, posBottomRight);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -179,7 +179,7 @@ std::vector<gfx::Point> Grid::tilesInCanvasRegion(const gfx::Region& rgn) const
|
|||
return result;
|
||||
}
|
||||
|
||||
Grid::IsometricGuide::IsometricGuide(const gfx::Size& sz)
|
||||
Grid::IsometricGuide::IsometricGuide(const gfx::Size& sz, const bool thickEdges)
|
||||
{
|
||||
evenWidth = sz.w % 2 == 0;
|
||||
evenHeight = sz.h % 2 == 0;
|
||||
|
@ -187,10 +187,7 @@ Grid::IsometricGuide::IsometricGuide(const gfx::Size& sz)
|
|||
evenHalfHeight = ((sz.h / 2) % 2) == 0;
|
||||
squareRatio = ABS(sz.w - sz.h) <= 1;
|
||||
oddSize = !evenWidth && evenHalfWidth && !evenHeight && evenHalfHeight;
|
||||
|
||||
// TODO: add 'share edges' checkbox to UI.
|
||||
// For testing the option, set this 'false' to 'true'
|
||||
shareEdges = !(false && !squareRatio && evenHeight);
|
||||
shareEdges = !thickEdges || squareRatio || !evenHeight;
|
||||
|
||||
start.x = 0;
|
||||
start.y = std::round(sz.h * 0.5);
|
||||
|
@ -218,10 +215,10 @@ static void push_isometric_line_point(int x, int y, std::vector<gfx::Point>* dat
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<gfx::Point> Grid::getIsometricLine(const gfx::Size& sz)
|
||||
std::vector<gfx::Point> Grid::getIsometricLine(const gfx::Size& sz, const bool thickEdges)
|
||||
{
|
||||
std::vector<gfx::Point> result;
|
||||
const IsometricGuide guide(sz);
|
||||
const IsometricGuide guide(sz, thickEdges);
|
||||
|
||||
// We use the line drawing algorithm to find the points
|
||||
// for a single pixel-precise line
|
||||
|
@ -235,4 +232,12 @@ std::vector<gfx::Point> Grid::getIsometricLine(const gfx::Size& sz)
|
|||
return result;
|
||||
}
|
||||
|
||||
bool Grid::isIsometric(const Type t)
|
||||
{
|
||||
if (t == Type::IsometricSharedEdges || t == Type::IsometricThickEdges)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace doc
|
||||
|
|
|
@ -19,7 +19,8 @@ class Grid {
|
|||
public:
|
||||
enum class Type {
|
||||
Orthogonal,
|
||||
Isometric,
|
||||
IsometricSharedEdges,
|
||||
IsometricThickEdges,
|
||||
};
|
||||
explicit Grid(const gfx::Size& sz = gfx::Size(16, 16), const Type t = Type::Orthogonal)
|
||||
: m_tileSize(sz)
|
||||
|
@ -101,12 +102,28 @@ public:
|
|||
bool oddSize : 1;
|
||||
bool shareEdges : 1;
|
||||
|
||||
IsometricGuide(const gfx::Size& sz);
|
||||
IsometricGuide(const gfx::Size& sz, const bool thickEdges = false);
|
||||
};
|
||||
|
||||
// Make IsometricGuide from Grid instance
|
||||
IsometricGuide getIsometricGuide() const
|
||||
{
|
||||
return IsometricGuide(tileSize(), type() == Type::IsometricThickEdges);
|
||||
}
|
||||
|
||||
// Returns an array of coordinates used for calculating the
|
||||
// pixel-precise bounds of an isometric grid cell
|
||||
static std::vector<gfx::Point> getIsometricLine(const gfx::Size& sz);
|
||||
static std::vector<gfx::Point> getIsometricLine(const gfx::Size& sz,
|
||||
const bool thickEdges = false);
|
||||
|
||||
// Get the coordinate array from Grid instance
|
||||
std::vector<gfx::Point> getIsometricLine() const
|
||||
{
|
||||
return getIsometricLine(tileSize(), type() == Type::IsometricThickEdges);
|
||||
}
|
||||
|
||||
static bool isIsometric(const Type t);
|
||||
bool isIsometric() const { return isIsometric(type()); }
|
||||
|
||||
private:
|
||||
gfx::Size m_tileSize;
|
||||
|
|
Loading…
Reference in New Issue