15 if (tool !=
"paint" && tool !=
"erase" && tool !=
"fill" && tool !=
"line" && tool !=
"rect" &&
17 throw Exception(
"Brush::setTool: expected paint|erase|fill|line|rect|stamp");
23 if (
size < 1)
throw Exception(
"Brush::setSize: size must be >= 1");
29 throw Exception(
"Brush::setShape: expected square|circle");
42 stamp_.assign(
static_cast<size_t>(
width) *
static_cast<size_t>(
height), 0);
46 if (lx < 0 || ly < 0 || lx >= stampW_ || ly >= stampH_) {
47 throw Exception(
"Brush::setStampTile: out of stamp bounds");
49 stamp_[
static_cast<size_t>(ly * stampW_ + lx)] = gid;
53 if (lx < 0 || ly < 0 || lx >= stampW_ || ly >= stampH_) {
54 throw Exception(
"Brush::getStampTile: out of stamp bounds");
56 return stamp_[
static_cast<size_t>(ly * stampW_ + lx)];
65void Brush::clearChanges() { changes_.clear(); }
67void Brush::clearPreview() { preview_.clear(); }
69void Brush::collectBrushCells(
int tx,
int ty, std::vector<std::pair<int, int>> &out)
const {
74 if (shape_ ==
"circle") {
75 float d = std::sqrt(
static_cast<float>(
dx *
dx +
dy *
dy));
76 if (
d >
static_cast<float>(
r) + 0.01f)
continue;
78 out.emplace_back(tx +
dx, ty +
dy);
83void Brush::collectLineCells(
int x0,
int y0,
int x1,
int y1,
84 std::vector<std::pair<int, int>> &out)
const {
86 int dx = std::abs(x1 - x0),
sx = x0 < x1 ? 1 : -1;
87 int dy = -std::abs(y1 - y0),
sy = y0 < y1 ? 1 : -1;
91 std::vector<std::pair<int, int>> brush;
92 collectBrushCells(
x,
y, brush);
93 out.insert(out.end(), brush.begin(), brush.end());
94 if (
x == x1 &&
y == y1)
break;
107void Brush::collectRectCells(
int x0,
int y0,
int x1,
int y1,
bool filled,
108 std::vector<std::pair<int, int>> &out)
const {
110 int minX = std::min(x0, x1), maxX = std::max(x0, x1);
111 int minY = std::min(y0, y1), maxY = std::max(y0, y1);
112 for (
int y = minY;
y <= maxY; ++
y) {
113 for (
int x = minX;
x <= maxX; ++
x) {
114 if (!filled &&
x != minX &&
x != maxX &&
y != minY &&
y != maxY)
continue;
115 out.emplace_back(
x,
y);
120int Brush::applyCells(TileBuffer *
buffer,
const std::vector<std::pair<int, int>> &
cells,
int gid) {
124 std::vector<std::pair<int, int>> unique;
125 unique.reserve(
cells.size());
126 for (
const auto &
c :
cells) {
128 for (
const auto &
u : unique) {
129 if (
u.first ==
c.first &&
u.second ==
c.second) {
134 if (!
found) unique.push_back(
c);
136 for (
const auto &
c : unique) {
137 if (!
buffer->containsCell(
c.first,
c.second))
continue;
138 int old =
buffer->getGid(
c.first,
c.second);
139 if (old == gid)
continue;
140 buffer->setGid(
c.first,
c.second, gid);
146 changes_.push_back(ch);
148 return static_cast<int>(changes_.size());
151int Brush::applyStamp(TileBuffer *
buffer,
int tx,
int ty) {
153 if (stampW_ <= 0 || stampH_ <= 0)
return 0;
155 int ox = tx - stampW_ / 2;
156 int oy = ty - stampH_ / 2;
157 for (
int ly = 0; ly < stampH_; ++ly) {
158 for (
int lx = 0; lx < stampW_; ++lx) {
159 int gid = stamp_[
static_cast<size_t>(ly * stampW_ + lx)];
162 if (!
buffer->containsCell(
x,
y))
continue;
164 if (old == gid)
continue;
171 changes_.push_back(ch);
174 return static_cast<int>(changes_.size());
177int Brush::previewCells(TileBuffer *
buffer,
const std::vector<std::pair<int, int>> &
cells,
int gid) {
180 for (
const auto &
c :
cells) {
181 if (!
buffer->containsCell(
c.first,
c.second))
continue;
186 p.oldGid =
buffer->getGid(
c.first,
c.second);
187 preview_.push_back(
p);
189 return static_cast<int>(preview_.size());
193 if (tool_ ==
"stamp")
return applyStamp(
buffer, tx, ty);
196 std::vector<std::pair<int, int>>
cells;
197 collectBrushCells(tx, ty,
cells);
202 std::vector<std::pair<int, int>>
cells;
203 collectBrushCells(tx, ty,
cells);
210 if (!
buffer->containsCell(tx, ty))
return 0;
212 if (
target == tile_)
return 0;
213 std::vector<char> seen(
static_cast<size_t>(
buffer->getWidth() *
buffer->getHeight()), 0);
214 std::queue<std::pair<int, int>>
q;
216 auto idx = [&](
int x,
int y) {
return y *
buffer->getWidth() +
x; };
217 seen[
static_cast<size_t>(
idx(tx, ty))] = 1;
218 const int dirs[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
220 auto [
x,
y] =
q.front();
229 changes_.push_back(ch);
230 for (
auto &
d : dirs) {
233 size_t i =
static_cast<size_t>(
idx(
nx,
ny));
234 if (seen[i])
continue;
240 return static_cast<int>(changes_.size());
244 std::vector<std::pair<int, int>>
cells;
245 collectLineCells(x0, y0, x1, y1,
cells);
246 int gid = (tool_ ==
"erase") ? eraseTile_ : tile_;
251 std::vector<std::pair<int, int>>
cells;
252 collectRectCells(x0, y0, x1, y1, filled,
cells);
253 int gid = (tool_ ==
"erase") ? eraseTile_ : tile_;
258 if (tool_ ==
"stamp") {
260 if (stampW_ <= 0 || stampH_ <= 0 || !
buffer)
return 0;
261 int ox = tx - stampW_ / 2;
262 int oy = ty - stampH_ / 2;
263 for (
int ly = 0; ly < stampH_; ++ly) {
264 for (
int lx = 0; lx < stampW_; ++lx) {
265 int x =
ox + lx,
y =
oy + ly;
266 if (!
buffer->containsCell(
x,
y))
continue;
270 p.gid = stamp_[
static_cast<size_t>(ly * stampW_ + lx)];
272 preview_.push_back(
p);
275 return static_cast<int>(preview_.size());
277 std::vector<std::pair<int, int>>
cells;
278 collectBrushCells(tx, ty,
cells);
279 int gid = (tool_ ==
"erase") ? eraseTile_ : tile_;
284 std::vector<std::pair<int, int>>
cells;
285 collectLineCells(x0, y0, x1, y1,
cells);
286 int gid = (tool_ ==
"erase") ? eraseTile_ : tile_;
291 std::vector<std::pair<int, int>>
cells;
292 collectRectCells(x0, y0, x1, y1, filled,
cells);
293 int gid = (tool_ ==
"erase") ? eraseTile_ : tile_;
297bool Brush::validChange(
int index)
const {
298 return index >= 0 && index < static_cast<int>(changes_.size());
300bool Brush::validPreview(
int index)
const {
301 return index >= 0 && index < static_cast<int>(preview_.size());
305 if (!validPreview(
index))
throw Exception(
"Brush::getPreviewX: bad index");
306 return preview_[
index].x;
309 if (!validPreview(
index))
throw Exception(
"Brush::getPreviewY: bad index");
310 return preview_[
index].y;
313 if (!validPreview(
index))
throw Exception(
"Brush::getPreviewGid: bad index");
314 return preview_[
index].gid;
317 if (!validChange(
index))
throw Exception(
"Brush::getChangeX: bad index");
318 return changes_[
index].x;
321 if (!validChange(
index))
throw Exception(
"Brush::getChangeY: bad index");
322 return changes_[
index].y;
325 if (!validChange(
index))
throw Exception(
"Brush::getChangeOldGid: bad index");
326 return changes_[
index].oldGid;
329 if (!validChange(
index))
throw Exception(
"Brush::getChangeNewGid: bad index");
330 return changes_[
index].gid;
std::array< double, 10 > q
std::unique_ptr< gpgpu::GpuBuffer > buffer
std::array< PixelCell, kPixelChunkSize *kPixelChunkSize > cells
EVENGINE_API_FOUNDATION public API.
void setEraseTile(int gid)
Sets the erase tile.
void clearStamp()
Clears stamp.
int paintRect(TileBuffer *buffer, int x0, int y0, int x1, int y1, bool filled)
Paint rect.
int getStampTile(int lx, int ly) const
Returns the stamp tile.
int paintLine(TileBuffer *buffer, int x0, int y0, int x1, int y1)
Paint line.
int getChangeY(int index) const
Returns the change y.
int getChangeNewGid(int index) const
Returns the change new gid.
int previewAt(TileBuffer *buffer, int tx, int ty)
Fill preview buffer without mutating target.
void setSize(int size)
Sets the size.
void setStampSize(int width, int height)
Sets the stamp size.
int floodFill(TileBuffer *buffer, int tx, int ty)
Flood fill.
void setStampTile(int lx, int ly, int gid)
Sets the stamp tile.
void setShape(const std::string &shape)
Sets the shape.
int getChangeX(int index) const
Returns the change x.
int previewLine(TileBuffer *buffer, int x0, int y0, int x1, int y1)
Preview line.
int getPreviewX(int index) const
Returns the preview x.
int paintAt(TileBuffer *buffer, int tx, int ty)
Apply current tool at / between cells. Returns cells changed.
int eraseAt(TileBuffer *buffer, int tx, int ty)
Erases at.
void setTile(int gid)
Sets the tile.
int getChangeOldGid(int index) const
Returns the change old gid.
int previewRect(TileBuffer *buffer, int x0, int y0, int x1, int y1, bool filled)
Preview rect.
int getPreviewGid(int index) const
Returns the preview gid.
void setTool(const std::string &tool)
Sets the tool.
int getPreviewY(int index) const
Returns the preview y.
Independent GID grid for map brushes (no hard dependency on map.TileLayer).
@ Cell
A cell was removed; the out-parameter holds it.