Implement isometric grid

This commit is contained in:
Liebranca 2024-09-27 00:12:58 -03:00
parent 6c69840184
commit 35fb6597d4
17 changed files with 293 additions and 12 deletions

View File

@ -526,6 +526,7 @@
<section id="grid" canforce="true">
<option id="snap" type="bool" default="false" />
<option id="bounds" type="gfx::Rect" default="doc::Sprite::DefaultGridBounds()" />
<option id="type" type="doc::Grid::Type" default="doc::Sprite::DefaultGridType()" />
<option id="color" type="app::Color" default="app::Color::fromRgb(0, 0, 255)" />
<option id="opacity" type="int" default="160" />
<option id="auto_opacity" type="bool" default="true" />

View File

@ -832,6 +832,8 @@ x = X:
y = Y:
width = Width:
height = Height:
type_orthogonal = Orthogonal
type_isometric = Isometric
[home_view]
title = Home
@ -1456,6 +1458,8 @@ grid_x = X:
grid_y = Y:
grid_width = Width:
grid_height = Height:
grid_type_orthogonal = Orthogonal
grid_type_isometric = Isometric
grid_color = Color:
grid_opacity = Opacity:
grid_auto = Auto

View File

@ -15,6 +15,10 @@
<label text="@.height" />
<expr id="grid_h" text="" />
<combobox id="grid_type" editable="true" expansive="true">
<listitem text="@.type_orthogonal" />
<listitem text="@.type_isometric" />
</combobox>
<separator horizontal="true" cell_hspan="4" />

View File

@ -423,6 +423,10 @@
<expr id="grid_w" text="" />
<label text="@.grid_height" />
<expr id="grid_h" text="" />
<combobox id="grid_type" editable="true" expansive="true">
<listitem text="@.grid_type_orthogonal" />
<listitem text="@.grid_type_isometric" />
</combobox>
<hbox />
<label text="@.grid_color" />

View File

@ -321,6 +321,7 @@ target_sources(app-lib PRIVATE
cmd/set_cel_zindex.cpp
cmd/set_frame_duration.cpp
cmd/set_grid_bounds.cpp
cmd/set_grid_type.cpp
cmd/set_last_point.cpp
cmd/set_layer_blend_mode.cpp
cmd/set_layer_flags.cpp

View File

@ -0,0 +1,49 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/cmd/set_grid_type.h"
#include "app/doc.h"
#include "app/pref/preferences.h"
#include "doc/sprite.h"
namespace app { namespace cmd {
using namespace doc;
SetGridType::SetGridType(Sprite* sprite, const doc::Grid::Type type)
: WithSprite(sprite)
, m_oldType(sprite->gridType())
, m_newType(type)
{
}
void SetGridType::onExecute()
{
setGrid(m_newType);
}
void SetGridType::onUndo()
{
setGrid(m_oldType);
}
void SetGridType::setGrid(const doc::Grid::Type type)
{
Sprite* spr = sprite();
spr->setGridType(type);
Doc* doc = static_cast<Doc*>(spr->document());
auto& docPref = Preferences::instance().document(doc);
docPref.grid.type(type);
spr->incrementVersion();
}
}} // namespace app::cmd

View File

@ -0,0 +1,40 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CMD_SET_GRID_TYPE_H_INCLUDED
#define APP_CMD_SET_GRID_TYPE_H_INCLUDED
#pragma once
#include "app/cmd.h"
#include "app/cmd/with_sprite.h"
#include "doc/grid.h"
namespace doc {
class Sprite;
}
namespace app { namespace cmd {
class SetGridType : public Cmd,
public WithSprite {
public:
SetGridType(doc::Sprite* sprite, doc::Grid::Type type);
protected:
void onExecute() override;
void onUndo() override;
size_t onMemSize() const override { return sizeof(*this); }
private:
void setGrid(doc::Grid::Type type);
doc::Grid::Type m_oldType;
doc::Grid::Type m_newType;
};
}} // namespace app::cmd
#endif

View File

@ -11,11 +11,13 @@
#include "app/app.h"
#include "app/cmd/set_grid_bounds.h"
#include "app/cmd/set_grid_type.h"
#include "app/commands/command.h"
#include "app/context.h"
#include "app/context_access.h"
#include "app/doc.h"
#include "app/find_widget.h"
#include "app/i18n/strings.h"
#include "app/load_widget.h"
#include "app/pref/preferences.h"
#include "app/tx.h"
@ -107,6 +109,10 @@ void GridSettingsCommand::onExecute(Context* context)
Site site = context->activeSite();
Rect bounds = site.gridBounds();
doc::Grid::Type type = site.gridType();
std::string typestr = (type == doc::Grid::Type::Isometric ?
app::Strings::grid_settings_type_isometric() :
app::Strings::grid_settings_type_orthogonal());
window.gridX()->setTextf("%d", bounds.x);
window.gridY()->setTextf("%d", bounds.y);
@ -122,6 +128,7 @@ void GridSettingsCommand::onExecute(Context* context)
if (window.gridH()->textInt() <= 0)
window.gridH()->setText("1");
});
window.gridType()->getEntryWidget()->setText(typestr);
window.openWindowInForeground();
if (window.closer() == window.ok()) {
@ -132,9 +139,15 @@ void GridSettingsCommand::onExecute(Context* context)
bounds.w = std::max(bounds.w, 1);
bounds.h = std::max(bounds.h, 1);
typestr = window.gridType()->getEntryWidget()->text();
type = (typestr == app::Strings::grid_settings_type_isometric() ?
doc::Grid::Type::Isometric :
doc::Grid::Type::Orthogonal);
ContextWriter writer(context);
Tx tx(writer, friendlyName(), ModifyDocument);
tx(new cmd::SetGridBounds(site.sprite(), bounds));
tx(new cmd::SetGridType(site.sprite(), type));
tx.commit();
auto& docPref = Preferences::instance().document(site.document());

View File

@ -194,10 +194,12 @@ void OpenFileCommand::onExecute(Context* context)
// interact with old versions of Aseprite saving the grid
// bounds in the aseprite.ini file)
docPref.grid.bounds(doc->sprite()->gridBounds());
docPref.grid.type(doc->sprite()->gridType());
}
else {
// Get grid bounds from preferences
doc->sprite()->setGridBounds(docPref.grid.bounds());
doc->sprite()->setGridType(docPref.grid.type());
}
}

View File

@ -11,6 +11,7 @@
#include "app/app.h"
#include "app/cmd/set_grid_bounds.h"
#include "app/cmd/set_grid_type.h"
#include "app/commands/command.h"
#include "app/console.h"
#include "app/context.h"
@ -816,6 +817,20 @@ public:
}
}
// Change sprite grid type
if (m_context && m_context->activeDocument() && m_context->activeDocument()->sprite() &&
m_context->activeDocument()->sprite()->gridType() != gridTypeInt()) {
try {
ContextWriter writer(m_context, 1000);
Tx tx(writer, Strings::commands_GridSettings(), ModifyDocument);
tx(new cmd::SetGridType(writer.sprite(), gridTypeInt()));
tx.commit();
}
catch (const std::exception& ex) {
Console::showException(ex);
}
}
m_curPref->show.grid(gridVisible()->isSelected());
m_curPref->grid.bounds(gridBounds());
m_curPref->grid.color(gridColor()->getColor());
@ -1414,6 +1429,12 @@ private:
gridW()->setTextf("%d", m_curPref->grid.bounds().w);
gridH()->setTextf("%d", m_curPref->grid.bounds().h);
doc::Grid::Type type = m_curPref->grid.type();
std::string typestr = (type == doc::Grid::Type::Isometric ?
app::Strings::grid_settings_type_isometric() :
app::Strings::grid_settings_type_orthogonal());
gridType()->getEntryWidget()->setText(typestr);
gridColor()->setColor(m_curPref->grid.color());
gridOpacity()->setValue(m_curPref->grid.opacity());
gridAutoOpacity()->setSelected(m_curPref->grid.autoOpacity());
@ -1458,6 +1479,12 @@ private:
gridW()->setTextf("%d", pref.grid.bounds.defaultValue().w);
gridH()->setTextf("%d", pref.grid.bounds.defaultValue().h);
doc::Grid::Type type = pref.grid.type.defaultValue();
std::string typestr = (type == doc::Grid::Type::Isometric ?
app::Strings::grid_settings_type_isometric() :
app::Strings::grid_settings_type_orthogonal());
gridType()->getEntryWidget()->setText(typestr);
gridColor()->setColor(pref.grid.color.defaultValue());
gridOpacity()->setValue(pref.grid.opacity.defaultValue());
gridAutoOpacity()->setSelected(pref.grid.autoOpacity.defaultValue());
@ -1475,6 +1502,12 @@ private:
gridW()->setTextf("%d", pref.grid.bounds().w);
gridH()->setTextf("%d", pref.grid.bounds().h);
doc::Grid::Type type = pref.grid.type();
std::string typestr = (type == doc::Grid::Type::Isometric ?
app::Strings::grid_settings_type_isometric() :
app::Strings::grid_settings_type_orthogonal());
gridType()->getEntryWidget()->setText(typestr);
gridColor()->setColor(pref.grid.color());
gridOpacity()->setValue(pref.grid.opacity());
gridAutoOpacity()->setSelected(pref.grid.autoOpacity());
@ -1936,6 +1969,15 @@ private:
return gfx::Rect(gridX()->textInt(), gridY()->textInt(), gridW()->textInt(), gridH()->textInt());
}
doc::Grid::Type gridTypeInt() const
{
std::string typestr = gridType()->getEntryWidget()->text();
if (typestr == app::Strings::grid_settings_type_isometric())
return doc::Grid::Type::Isometric;
return doc::Grid::Type::Orthogonal;
}
static std::string userThemeFolder()
{
ResourceFinder rf;

View File

@ -397,6 +397,7 @@ FOR_ENUM(doc::BrushPattern)
FOR_ENUM(doc::ColorMode)
FOR_ENUM(doc::FitCriteria)
FOR_ENUM(doc::RgbMapAlgorithm)
FOR_ENUM(doc::Grid::Type)
FOR_ENUM(filters::HueSaturationFilter::Mode)
FOR_ENUM(filters::TiledMode)
FOR_ENUM(render::OnionskinPosition)

View File

@ -129,6 +129,14 @@ gfx::Rect Site::gridBounds() const
return doc::Sprite::DefaultGridBounds();
}
doc::Grid::Type Site::gridType() const
{
if (m_sprite)
return m_sprite->gridType();
return doc::Sprite::DefaultGridType();
}
bool Site::shouldTrimCel(Cel* cel) const
{
return (cel && cel->layer() && cel->layer()->isTransparent() &&

View File

@ -13,6 +13,7 @@
#include "app/tileset_mode.h"
#include "doc/cel_list.h"
#include "doc/frame.h"
#include "doc/grid.h"
#include "doc/palette_picks.h"
#include "doc/selected_objects.h"
#include "gfx/fwd.h"
@ -99,6 +100,7 @@ public:
doc::Tileset* tileset() const;
doc::Grid grid() const;
gfx::Rect gridBounds() const;
doc::Grid::Type gridType() const;
void tilemapMode(const TilemapMode mode) { m_tilemapMode = mode; }
void tilesetMode(const TilesetMode mode) { m_tilesetMode = mode; }

View File

@ -57,6 +57,7 @@
#include "app/util/tile_flags_utils.h"
#include "base/chrono.h"
#include "base/convert_to.h"
#include "base/pi.h"
#include "doc/doc.h"
#include "doc/mask_boundaries.h"
#include "doc/slice.h"
@ -1098,6 +1099,46 @@ void Editor::drawMaskSafe()
}
}
static gfx::PointF project(gfx::PointF& p, const gfx::PointF& degrees)
{
const double to_radians = 1.0 / (180.0 / PI);
const double cy = cos(degrees.y * to_radians);
const double sy = sin(degrees.y * to_radians);
const double cx = (degrees.x != 0 ? cos(degrees.x * to_radians) : 1);
// Apply Y as rotation and X as scaling
gfx::PointF dp(p);
if (degrees.y != 0.0) {
dp.x = p.x * cy - p.y * sy;
dp.y = (p.x * sy + p.y * cy) * cx;
}
return dp;
}
static gfx::PointF project_isometric(gfx::PointF& p)
{
const gfx::PointF projection(60, 45);
return project(p, projection);
}
static void project_isometric(PointF& p0, PointF& p1, const PointF& offset)
{
// Get original distance/length of line
gfx::PointF vto(p0 - p1);
const double oldDist = sqrt(vto.x * vto.x + vto.y * vto.y);
// Transform points
p0 = project_isometric(p0);
p1 = project_isometric(p1);
// Get new distance
vto = p0 - p1;
const double newDist = sqrt(vto.x * vto.x + vto.y * vto.y);
const double adjustLength = oldDist / newDist;
// Apply adjustments
p0 *= adjustLength;
p1 *= adjustLength;
p0 += offset;
p1 += offset;
}
void Editor::drawGrid(Graphics* g,
const gfx::Rect& spriteBounds,
const Rect& gridBounds,
@ -1145,21 +1186,66 @@ void Editor::drawGrid(Graphics* g,
grid_color =
gfx::rgba(gfx::getr(grid_color), gfx::getg(grid_color), gfx::getb(grid_color), alpha);
// Draw horizontal lines
int x1 = spriteBounds.x;
int y1 = gridF.y;
int x2 = spriteBounds.x + spriteBounds.w;
int y2 = spriteBounds.y + spriteBounds.h;
// Grid without rotation
if (getSite().sprite()->gridType() == doc::Grid::Type::Orthogonal) {
// Draw horizontal lines
int x1 = spriteBounds.x;
int y1 = gridF.y;
int x2 = spriteBounds.x + spriteBounds.w;
int y2 = spriteBounds.y + spriteBounds.h;
for (double c = y1; c <= y2; c += gridF.h)
g->drawHLine(grid_color, x1, c, spriteBounds.w);
for (double c = y1; c <= y2; c += gridF.h)
g->drawHLine(grid_color, x1, c, spriteBounds.w);
// Draw vertical lines
x1 = gridF.x;
y1 = spriteBounds.y;
// Draw vertical lines
x1 = gridF.x;
y1 = spriteBounds.y;
for (double c = x1; c <= x2; c += gridF.w)
g->drawVLine(grid_color, c, y1, spriteBounds.h);
for (double c = x1; c <= x2; c += gridF.w)
g->drawVLine(grid_color, c, y1, spriteBounds.h);
}
// Isometric grid
else {
// Calculate offset due to rotation
int x1 = 0;
int y1 = 0;
int x2 = spriteBounds.w;
int y2 = spriteBounds.h;
const gfx::PointF proj_offset(spriteBounds.center().x - (spriteBounds.x - gridF.x),
spriteBounds.y - (spriteBounds.y - gridF.y));
{
const gfx::PointF offset(0, 0);
gfx::PointF p0(x1, y1);
gfx::PointF p1(x2, y1);
project_isometric(p0, p1, offset);
x1 -= p1.x;
y1 -= p1.y;
x2 += p1.x;
y2 += p1.y;
}
// Rotate and draw horizontal lines
for (double c = y1; c <= y2; c += gridF.h) {
gfx::PointF p0(x1, c);
gfx::PointF p1(x2, c);
project_isometric(p0, p1, proj_offset);
g->drawLine(grid_color,
gfx::Point(int(std::round(p0.x)), int(std::round(p0.y))),
gfx::Point(int(std::round(p1.x)), int(std::round(p1.y))));
}
// Rotate and draw vertical lines
for (double c = x1; c <= x2; c += gridF.w) {
gfx::PointF p0(c, y1);
gfx::PointF p1(c, y2);
project_isometric(p0, p1, proj_offset);
g->drawLine(grid_color,
gfx::Point(int(std::round(p0.x)), int(std::round(p0.y))),
gfx::Point(int(std::round(p1.x)), int(std::round(p1.y))));
}
}
}
void Editor::drawSlices(ui::Graphics* g)

View File

@ -17,6 +17,10 @@ namespace doc {
class Grid {
public:
enum class Type {
Orthogonal = 0x00,
Isometric = 0x01,
};
explicit Grid(const gfx::Size& sz = gfx::Size(16, 16))
: m_tileSize(sz)
, m_origin(0, 0)

View File

@ -37,6 +37,7 @@
namespace doc {
static gfx::Rect g_defaultGridBounds(0, 0, 16, 16);
static Grid::Type g_defaultGridType = Grid::Type::Orthogonal;
// static
gfx::Rect Sprite::DefaultGridBounds()
@ -55,6 +56,18 @@ void Sprite::SetDefaultGridBounds(const gfx::Rect& defGridBounds)
g_defaultGridBounds.h = 1;
}
// static
Grid::Type Sprite::DefaultGridType()
{
return g_defaultGridType;
}
// static
void Sprite::SetDefaultGridType(const Grid::Type type)
{
g_defaultGridType = type;
}
//////////////////////////////////////////////////////////////////////
// Constructors/Destructor

View File

@ -15,6 +15,7 @@
#include "doc/color.h"
#include "doc/fit_criteria.h"
#include "doc/frame.h"
#include "doc/grid.h"
#include "doc/image_buffer.h"
#include "doc/image_ref.h"
#include "doc/image_spec.h"
@ -122,6 +123,8 @@ public:
// Defaults
static gfx::Rect DefaultGridBounds();
static void SetDefaultGridBounds(const gfx::Rect& defGridBounds);
static Grid::Type DefaultGridType();
static void SetDefaultGridType(Grid::Type type);
static RgbMapAlgorithm DefaultRgbMapAlgorithm();
static void SetDefaultRgbMapAlgorithm(const RgbMapAlgorithm mapAlgo);
@ -136,6 +139,9 @@ public:
m_gridBounds.h = 1;
}
void setGridType(const Grid::Type type) { m_gridType = type; }
Grid::Type gridType() const { return m_gridType; }
virtual int getMemSize() const override;
////////////////////////////////////////
@ -253,6 +259,7 @@ private:
PalettesList m_palettes; // list of palettes
LayerGroup* m_root; // main group of layers
gfx::Rect m_gridBounds; // grid settings
Grid::Type m_gridType;
// Current rgb map
mutable std::unique_ptr<RgbMap> m_rgbMap;