Commit 9f637192 authored by Leeor Dicker's avatar Leeor Dicker

Added support for contiguous flood fills. Completes issue #4.

- Added a button to toggle flood contiguous mode or full layer mode.
- Added patternFill_Contig() to EditorState.
- Implemented a stack based depth-first search flood fill algorithm for the contiguous fill mode.
parent bb5b38fc
......@@ -4,6 +4,8 @@
#include "Common.h"
#include "ConfigStrings.h"
#include <stack>
const bool SHOW_DEBUG_DEFAULT = false;
const bool HIDE_UI_DEFAULT = false;
......@@ -13,6 +15,9 @@ const float SCROLL_SPEED = 250.0f;
SDL_Surface* MINI_MAP_SURFACE = nullptr; // HACK!
std::stack<Point_2d> FLOOD_STACK; // Stack used for contiguous flood fill.
std::map<EditState, string> StateStringMap; /**< EditState string table. */
std::map<int, EditState> StateIntMap; /**< EditState int table. */
std::map<EditState, Cell::TileLayer> StateToLayer; /**< Translation table between a specific edit state and tile layer. */
......@@ -460,7 +465,7 @@ void EditorState::onMouseMove(int x, int y, int relX, int relY)
if(mLeftButtonDown)
{
// Avoid modifying tiles if we're in the 'toolbar area'
if (y < 32 || mTilePalette.responding_to_events() || mMiniMap.responding_to_events())
if (y < 32 || mToolBar.flood() || mTilePalette.responding_to_events() || mMiniMap.responding_to_events())
return;
if(mEditState == STATE_TILE_COLLISION)
......@@ -531,12 +536,15 @@ void EditorState::handleLeftButtonDown(int x, int y)
{
Point_2d pt(x, y);
// Avoid edit conditions
if (isPointInRect(pt, mTilePalette.rect()) || mEditState == STATE_MAP_LINK_EDIT)
// Hate the look of this but it effectively condenses the ignore checks.
if (y < 32 ||
(mToolBar.flood() && isPointInRect(pt, mToolBar.flood_tool_extended_area())) ||
mEditState == STATE_MAP_LINK_EDIT ||
isPointInRect(pt, mTilePalette.rect()) ||
isPointInRect(pt, mMiniMap.rect()) ||
isPointInRect(pt, mTilePalette.rect()))
return;
if (y < 32 || isPointInRect(pt, mMiniMap.rect()) || isPointInRect(pt, mTilePalette.rect()))
return;
Cell& cell = mMap.getCell(mMouseCoords);
......@@ -566,7 +574,12 @@ void EditorState::changeTileTexture()
throw Exception(0, "Bad State", "EditorState::changeTileTExture() called with an invalid state.");
if (mToolBar.flood())
patternFill(StateToLayer[mEditState]);
{
if (mToolBar.flood_contiguous())
patternFill_Contig(StateToLayer[mEditState], mMap.getGridCoords(mMouseCoords), mMap.getCell(mMouseCoords).index(StateToLayer[mEditState]));
else
patternFill(StateToLayer[mEditState]);
}
else if (mToolBar.pencil())
pattern(StateToLayer[mEditState]);
else
......@@ -579,8 +592,6 @@ void EditorState::changeTileTexture()
/**
* Fills a given cell layer with a pattern.
*
* \todo Implement actual pattern filling.
*/
void EditorState::patternFill(Cell::TileLayer layer)
{
......@@ -592,6 +603,42 @@ void EditorState::patternFill(Cell::TileLayer layer)
}
/**
* Fills a contiguous area in a given layer with a pattern.
*/
void EditorState::patternFill_Contig(Cell::TileLayer layer, const Point_2d& _pt, int seed_index)
{
const Pattern& _pCheck = mTilePalette.pattern();
if (seed_index == _pCheck.value(_pt.x() % _pCheck.width(), _pt.y() % _pCheck.height()))
return;
while (!FLOOD_STACK.empty())
FLOOD_STACK.pop();
static const vector<int> dX = { 0, 1, 0, -1 }; // Neighbor Coords
static const vector<int> dY = { -1, 0, 1, 0 }; // Neighbor Coords
FLOOD_STACK.push(_pt);
while(!FLOOD_STACK.empty())
{
const Point_2d _pt_top = FLOOD_STACK.top();
FLOOD_STACK.pop();
const Pattern& p = mTilePalette.pattern();
mMap.getCellByGridCoords(_pt_top).index(layer, p.value(_pt_top.x() % p.width(), _pt_top.y() % p.height()));
for (int i = 0; i < 4; i++)
{
Point_2d coord(_pt_top.x() + dX[i], _pt_top.y() + dY[i]);
if (coord.x() >= 0 && coord.x() < mMap.width() && coord.y() >= 0 && coord.y() < mMap.height() && mMap.getCellByGridCoords(coord).index(layer) == seed_index)
FLOOD_STACK.push(coord);
}
}
}
/**
* If value < 0, ignores the pattern values and writes -1 instead.
*/
......
......@@ -82,6 +82,7 @@ private:
void changeTileTexture();
void pattern(Cell::TileLayer layer, int value = 0);
void patternFill(Cell::TileLayer layer);
void patternFill_Contig(Cell::TileLayer layer, const Point_2d& _pt, int seed_index);
void pattern_collision();
......
#include "Map.h"
#include "Common.h"
#include "../Common.h"
#include <cmath>
#include <sstream>
......
......@@ -6,7 +6,7 @@
const int BUTTON_SPACE = 2;
ToolBar::ToolBar() : mFont("fonts/ui-normal.png", 7, 9, 0)
ToolBar::ToolBar() : mFont("fonts/ui-normal.png", 7, 9, 0), mToggle("sys/square.png")
{
initUi();
}
......@@ -50,6 +50,14 @@ void ToolBar::initUi()
btnFill.position(btnPencil.positionX() + btnPencil.width() + BUTTON_SPACE, 2);
btnFill.click().Connect(this, &ToolBar::btnFill_Clicked);
btnFillContiguous.type(Button::BUTTON_TOGGLE);
btnFillContiguous.toggle(true);
btnFillContiguous.size(16, 16);
btnFillContiguous.position(btnFill.positionX() - 30, 40);
btnFillContiguous.visible(false);
mFloodFillExtendedArea(btnFillContiguous.positionX() - 4, btnFillContiguous.positionY() - 4, 104, btnFillContiguous.height() + 8);
btnErase.image("sys/erase.png");
btnErase.type(Button::BUTTON_TOGGLE);
btnErase.size(22, 28);
......@@ -204,6 +212,16 @@ void ToolBar::update()
btnFill.update();
btnErase.update();
if (btnFillContiguous.visible())
{
r.drawBoxFilled(mFloodFillExtendedArea.x() + 3, mFloodFillExtendedArea.y() + 4, mFloodFillExtendedArea.w(), mFloodFillExtendedArea.h(), 0, 0, 0, 100);
r.drawBoxFilled(mFloodFillExtendedArea, 180, 180, 180);
r.drawBox(mFloodFillExtendedArea, 0, 0, 0);
r.drawText(mFont, "Contiguous", btnFillContiguous.positionX() + btnFillContiguous.width() + 4, btnFillContiguous.positionY() + 4, 0, 0, 0);
btnFillContiguous.update();
if (btnFillContiguous.toggled()) r.drawImage(mToggle, btnFillContiguous.positionX() - 1, btnFillContiguous.positionY());
}
drawSeparator(btnErase, 9);
btnLayerBase.update();
......@@ -312,6 +330,7 @@ void ToolBar::btnLayerCollision_Clicked()
btnLayerBaseDetailToggle_Clicked();
btnFill.enabled(false);
btnFillContiguous.visible(false);
btnErase.enabled(false);
btnPencil_Clicked();
......@@ -329,6 +348,7 @@ void ToolBar::btnPencil_Clicked()
{
btnPencil.toggle(true);
btnFill.toggle(false);
btnFillContiguous.visible(false);
btnErase.toggle(false);
mToolbarEvent(TOOLBAR_TOOL_PENCIL);
......@@ -339,6 +359,7 @@ void ToolBar::btnFill_Clicked()
{
btnPencil.toggle(false);
btnFill.toggle(true);
btnFillContiguous.visible(true);
btnErase.toggle(false);
mToolbarEvent(TOOLBAR_TOOL_FILL);
......@@ -349,6 +370,7 @@ void ToolBar::btnErase_Clicked()
{
btnPencil.toggle(false);
btnFill.toggle(false);
btnFillContiguous.visible(false);
btnErase.toggle(true);
mToolbarEvent(TOOLBAR_TOOL_ERASER);
......
......@@ -48,6 +48,7 @@ public:
bool pencil() const { return btnPencil.toggled(); }
bool flood() const { return btnFill.toggled(); }
bool flood_contiguous() const { return btnFillContiguous.toggled(); }
bool erase() const { return btnErase.toggled(); }
bool show_bg() const { return btnLayerBaseToggle.toggled(); }
......@@ -61,6 +62,8 @@ public:
const Pattern& brush() const { return mBrush; }
const Rectangle_2d flood_tool_extended_area() const { return mFloodFillExtendedArea; }
private:
void initUi();
......@@ -103,8 +106,13 @@ private:
Font mFont;
Image mToggle;
Pattern mBrush;
// PRIMITIVES
Rectangle_2d mFloodFillExtendedArea;
// UI ELEMENTS
TextField txtMapName;
......@@ -112,6 +120,7 @@ private:
Button btnPencil;
Button btnFill;
Button btnFillContiguous;
Button btnErase;
Button btnLayerBase;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment