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:
Liebranca 2025-04-09 00:44:59 -03:00
parent 58011d9f87
commit 7d78f4f5d1
22 changed files with 156 additions and 99 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -190,9 +190,9 @@ 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) +
gfx::Point(brushBounds.w / 2, brushBounds.h / 2);
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);
}
// Get the current tool

View File

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

View File

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

View File

@ -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,
gfx::Point(origin.x + celBounds.w, origin.y + celBounds.h),
PreferSnapTo::ClosestGridVertex) -
origin);
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);
}

View File

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

View File

@ -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,9 +413,9 @@ 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(),
gfx::Point(bounds.origin()),
PreferSnapTo::ClosestGridVertex));
const gfx::PointF gridOffset(snap_to_grid(m_site.grid(),
gfx::Point(bounds.origin()),
PreferSnapTo::ClosestGridVertex));
// Now we calculate the difference from x1,y1 point and we can
// use it to adjust all coordinates (x1, y1, x2, y2).
@ -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

View File

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

View File

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

View File

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