mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 13:21:34 +00:00
Add pixel-precise isometric grid
This commit is contained in:
parent
35fb6597d4
commit
94ddd406af
@ -801,7 +801,7 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g,
|
||||
alpha = std::clamp(alpha, 0, 255);
|
||||
}
|
||||
|
||||
drawGrid(g, enclosingRect, Rect(0, 0, 1, 1), m_docPref.pixelGrid.color(), alpha);
|
||||
drawGrid(g, enclosingRect, Rect(0, 0, 1, 1), m_docPref.pixelGrid.color(), alpha, true);
|
||||
|
||||
// Save all pixel grid settings that are unset
|
||||
m_docPref.pixelGrid.forceSection();
|
||||
@ -824,7 +824,7 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g,
|
||||
}
|
||||
|
||||
if (alpha > 8) {
|
||||
drawGrid(g, enclosingRect, gridrc, m_docPref.grid.color(), alpha);
|
||||
drawGrid(g, enclosingRect, gridrc, m_docPref.grid.color(), alpha, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1099,51 +1099,12 @@ 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,
|
||||
const app::Color& color,
|
||||
int alpha)
|
||||
int alpha,
|
||||
bool isPixelGrid)
|
||||
{
|
||||
if ((m_flags & kShowGrid) == 0)
|
||||
return;
|
||||
@ -1187,7 +1148,7 @@ void Editor::drawGrid(Graphics* g,
|
||||
gfx::rgba(gfx::getr(grid_color), gfx::getg(grid_color), gfx::getb(grid_color), alpha);
|
||||
|
||||
// Grid without rotation
|
||||
if (getSite().sprite()->gridType() == doc::Grid::Type::Orthogonal) {
|
||||
if (isPixelGrid || getSite().sprite()->gridType() == doc::Grid::Type::Orthogonal) {
|
||||
// Draw horizontal lines
|
||||
int x1 = spriteBounds.x;
|
||||
int y1 = gridF.y;
|
||||
@ -1206,44 +1167,91 @@ void Editor::drawGrid(Graphics* g,
|
||||
}
|
||||
// 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));
|
||||
Rect pix(editorToScreen(RectF(0, 0, 1, 1)));
|
||||
int x1 = spriteBounds.x;
|
||||
int y1 = spriteBounds.y;
|
||||
int x2 = spriteBounds.x + spriteBounds.w;
|
||||
int y2 = spriteBounds.y + spriteBounds.h;
|
||||
int dx = int(std::round(grid.w * pix.w));
|
||||
int dy = int(std::round(grid.h * pix.h));
|
||||
|
||||
// Make tile bitmap
|
||||
doc::MaskBoundaries immask;
|
||||
{
|
||||
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;
|
||||
}
|
||||
int x = 0;
|
||||
int y = grid.h / 2;
|
||||
int lx = grid.w;
|
||||
|
||||
// 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))));
|
||||
}
|
||||
// Draw pixel-precise isometric grid when zoomed in
|
||||
// TODO: add support for different line angles
|
||||
if (grid.w == grid.h * 2 && m_proj.zoom().scale() > 8.00) {
|
||||
doc::ImageRef imref(
|
||||
doc::Image::create(doc::PixelFormat::IMAGE_BITMAP, grid.w * pix.w, (grid.h + 1) * pix.h));
|
||||
|
||||
// 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))));
|
||||
doc::Image* im = imref.get();
|
||||
if (!im)
|
||||
return;
|
||||
|
||||
// Prepare bitmap
|
||||
im->clear(0x00);
|
||||
im->fillRect(0, y * pix.h, lx * pix.w, y * pix.h, 0x01);
|
||||
y++;
|
||||
x++;
|
||||
for (; y < grid.h; y++, x += 2)
|
||||
im->fillRect(x * pix.w, (y - (x + 1)) * pix.h, (lx - x) * pix.w, y * pix.h, 0x01);
|
||||
|
||||
im->fillRect(x * pix.w, 0, (x + 2) * pix.w, y * pix.h, 0x01);
|
||||
immask.regen(im);
|
||||
immask.createPathIfNeeeded();
|
||||
|
||||
// Draw entire grid from single cell
|
||||
ui::Paint paint;
|
||||
paint.style(ui::Paint::Stroke);
|
||||
paint.antialias(false);
|
||||
paint.color(grid_color);
|
||||
|
||||
for (y = y1; y < y2; y += dy) {
|
||||
for (x = x1; x < x2; x += dx) {
|
||||
gfx::Path cell = immask.path();
|
||||
cell.offset(x, y);
|
||||
g->drawPath(cell, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Draw straight isometric line grid
|
||||
else {
|
||||
// Single side of diamond is line (a, b)
|
||||
Point a(x1 + x * pix.w, y1 + (y - x) * pix.h);
|
||||
Point b(x1 + (grid.w / 2) * pix.w, y1 + grid.h * pix.h);
|
||||
|
||||
// Calculate number of diamonds required to
|
||||
// fill canvas horizontally
|
||||
Point left(0, 0);
|
||||
while (left.x < spriteBounds.w)
|
||||
left.x += gridF.w;
|
||||
|
||||
// Get length and direction of line (a, b)
|
||||
// then calculate how much we need to stretch said
|
||||
// line to cover the whole canvas
|
||||
const Point vto = b - a;
|
||||
const Point ivto = Point(-vto.x, vto.y);
|
||||
double lenF = sqrt(vto.x * vto.x + vto.y * vto.y);
|
||||
int len = int(std::round(left.x / lenF)) + 1;
|
||||
|
||||
// Now displace point (b) to upper edge of canvas
|
||||
b = a + Point(gridF.w / 2, -gridF.h / 2);
|
||||
|
||||
// Move these two points across the screen in
|
||||
// cell-sized steps to draw the entire grid
|
||||
for (y = y1; y < y2; y += dy) {
|
||||
g->drawLine(grid_color, a, a + vto * len);
|
||||
g->drawLine(grid_color, a + left, (a + left) + ivto * len);
|
||||
g->drawLine(grid_color, b, b + vto * len);
|
||||
g->drawLine(grid_color, b, b + ivto * len);
|
||||
a.y += gridF.h;
|
||||
b.x += gridF.w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -366,7 +366,8 @@ private:
|
||||
const gfx::Rect& spriteBounds,
|
||||
const gfx::Rect& gridBounds,
|
||||
const app::Color& color,
|
||||
int alpha);
|
||||
int alpha,
|
||||
bool isPixelGrid);
|
||||
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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user