载入中...
搜索中...
未找到
Brush.cpp
浏览该文件的文档.
1#include "editor/Brush.h"
2
3#include "common/Exception.h"
4
5#include <algorithm>
6#include <cmath>
7#include <queue>
8#include <utility>
9
10namespace eve::editor {
11
12Brush::Brush() = default;
13
14void Brush::setTool(const std::string &tool) {
15 if (tool != "paint" && tool != "erase" && tool != "fill" && tool != "line" && tool != "rect" &&
16 tool != "stamp") {
17 throw Exception("Brush::setTool: expected paint|erase|fill|line|rect|stamp");
18 }
19 tool_ = tool;
20}
21
22void Brush::setSize(int size) {
23 if (size < 1) throw Exception("Brush::setSize: size must be >= 1");
24 size_ = size;
25}
26
27void Brush::setShape(const std::string &shape) {
28 if (shape != "square" && shape != "circle") {
29 throw Exception("Brush::setShape: expected square|circle");
30 }
31 shape_ = shape;
32}
33
34void Brush::setTile(int gid) { tile_ = gid; }
35
36void Brush::setEraseTile(int gid) { eraseTile_ = gid; }
37
39 if (width < 0 || height < 0) throw Exception("Brush::setStampSize: size must be >= 0");
40 stampW_ = width;
41 stampH_ = height;
42 stamp_.assign(static_cast<size_t>(width) * static_cast<size_t>(height), 0);
43}
44
45void Brush::setStampTile(int lx, int ly, int gid) {
46 if (lx < 0 || ly < 0 || lx >= stampW_ || ly >= stampH_) {
47 throw Exception("Brush::setStampTile: out of stamp bounds");
48 }
49 stamp_[static_cast<size_t>(ly * stampW_ + lx)] = gid;
50}
51
52int Brush::getStampTile(int lx, int ly) const {
53 if (lx < 0 || ly < 0 || lx >= stampW_ || ly >= stampH_) {
54 throw Exception("Brush::getStampTile: out of stamp bounds");
55 }
56 return stamp_[static_cast<size_t>(ly * stampW_ + lx)];
57}
58
60 stampW_ = 0;
61 stampH_ = 0;
62 stamp_.clear();
63}
64
65void Brush::clearChanges() { changes_.clear(); }
66
67void Brush::clearPreview() { preview_.clear(); }
68
69void Brush::collectBrushCells(int tx, int ty, std::vector<std::pair<int, int>> &out) const {
70 out.clear();
71 int r = size_ / 2;
72 for (int dy = -r; dy <= r; ++dy) {
73 for (int dx = -r; dx <= r; ++dx) {
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;
77 }
78 out.emplace_back(tx + dx, ty + dy);
79 }
80 }
81}
82
83void Brush::collectLineCells(int x0, int y0, int x1, int y1,
84 std::vector<std::pair<int, int>> &out) const {
85 out.clear();
86 int dx = std::abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
87 int dy = -std::abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
88 int err = dx + dy;
89 int x = x0, y = y0;
90 while (true) {
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;
95 int e2 = 2 * err;
96 if (e2 >= dy) {
97 err += dy;
98 x += sx;
99 }
100 if (e2 <= dx) {
101 err += dx;
102 y += sy;
103 }
104 }
105}
106
107void Brush::collectRectCells(int x0, int y0, int x1, int y1, bool filled,
108 std::vector<std::pair<int, int>> &out) const {
109 out.clear();
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);
116 }
117 }
118}
119
120int Brush::applyCells(TileBuffer *buffer, const std::vector<std::pair<int, int>> &cells, int gid) {
121 if (!buffer) throw Exception("Brush: null TileBuffer");
122 clearChanges();
123 // Deduplicate while preserving first occurrence
124 std::vector<std::pair<int, int>> unique;
125 unique.reserve(cells.size());
126 for (const auto &c : cells) {
127 bool found = false;
128 for (const auto &u : unique) {
129 if (u.first == c.first && u.second == c.second) {
130 found = true;
131 break;
132 }
133 }
134 if (!found) unique.push_back(c);
135 }
136 for (const auto &c : unique) {
137 if (!buffer->inBounds(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);
141 Cell ch;
142 ch.x = c.first;
143 ch.y = c.second;
144 ch.oldGid = old;
145 ch.gid = gid;
146 changes_.push_back(ch);
147 }
148 return static_cast<int>(changes_.size());
149}
150
151int Brush::applyStamp(TileBuffer *buffer, int tx, int ty) {
152 if (!buffer) throw Exception("Brush: null TileBuffer");
153 if (stampW_ <= 0 || stampH_ <= 0) return 0;
154 clearChanges();
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)];
160 int x = ox + lx;
161 int y = oy + ly;
162 if (!buffer->inBounds(x, y)) continue;
163 int old = buffer->getGid(x, y);
164 if (old == gid) continue;
165 buffer->setGid(x, y, gid);
166 Cell ch;
167 ch.x = x;
168 ch.y = y;
169 ch.oldGid = old;
170 ch.gid = gid;
171 changes_.push_back(ch);
172 }
173 }
174 return static_cast<int>(changes_.size());
175}
176
177int Brush::previewCells(TileBuffer *buffer, const std::vector<std::pair<int, int>> &cells, int gid) {
178 if (!buffer) throw Exception("Brush: null TileBuffer");
179 clearPreview();
180 for (const auto &c : cells) {
181 if (!buffer->inBounds(c.first, c.second)) continue;
182 Cell p;
183 p.x = c.first;
184 p.y = c.second;
185 p.gid = gid;
186 p.oldGid = buffer->getGid(c.first, c.second);
187 preview_.push_back(p);
188 }
189 return static_cast<int>(preview_.size());
190}
191
192int Brush::paintAt(TileBuffer *buffer, int tx, int ty) {
193 if (tool_ == "stamp") return applyStamp(buffer, tx, ty);
194 if (tool_ == "erase") return eraseAt(buffer, tx, ty);
195 if (tool_ == "fill") return floodFill(buffer, tx, ty);
196 std::vector<std::pair<int, int>> cells;
197 collectBrushCells(tx, ty, cells);
198 return applyCells(buffer, cells, tile_);
199}
200
201int Brush::eraseAt(TileBuffer *buffer, int tx, int ty) {
202 std::vector<std::pair<int, int>> cells;
203 collectBrushCells(tx, ty, cells);
204 return applyCells(buffer, cells, eraseTile_);
205}
206
207int Brush::floodFill(TileBuffer *buffer, int tx, int ty) {
208 if (!buffer) throw Exception("Brush: null TileBuffer");
209 clearChanges();
210 if (!buffer->inBounds(tx, ty)) return 0;
211 int target = buffer->getGid(tx, ty);
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;
215 q.push({tx, ty});
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}};
219 while (!q.empty()) {
220 auto [x, y] = q.front();
221 q.pop();
222 int old = buffer->getGid(x, y);
223 buffer->setGid(x, y, tile_);
224 Cell ch;
225 ch.x = x;
226 ch.y = y;
227 ch.oldGid = old;
228 ch.gid = tile_;
229 changes_.push_back(ch);
230 for (auto &d : dirs) {
231 int nx = x + d[0], ny = y + d[1];
232 if (!buffer->inBounds(nx, ny)) continue;
233 size_t i = static_cast<size_t>(idx(nx, ny));
234 if (seen[i]) continue;
235 if (buffer->getGid(nx, ny) != target) continue;
236 seen[i] = 1;
237 q.push({nx, ny});
238 }
239 }
240 return static_cast<int>(changes_.size());
241}
242
243int Brush::paintLine(TileBuffer *buffer, int x0, int y0, int x1, int y1) {
244 std::vector<std::pair<int, int>> cells;
245 collectLineCells(x0, y0, x1, y1, cells);
246 int gid = (tool_ == "erase") ? eraseTile_ : tile_;
247 return applyCells(buffer, cells, gid);
248}
249
250int Brush::paintRect(TileBuffer *buffer, int x0, int y0, int x1, int y1, bool filled) {
251 std::vector<std::pair<int, int>> cells;
252 collectRectCells(x0, y0, x1, y1, filled, cells);
253 int gid = (tool_ == "erase") ? eraseTile_ : tile_;
254 return applyCells(buffer, cells, gid);
255}
256
257int Brush::previewAt(TileBuffer *buffer, int tx, int ty) {
258 if (tool_ == "stamp") {
259 clearPreview();
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->inBounds(x, y)) continue;
267 Cell p;
268 p.x = x;
269 p.y = y;
270 p.gid = stamp_[static_cast<size_t>(ly * stampW_ + lx)];
271 p.oldGid = buffer->getGid(x, y);
272 preview_.push_back(p);
273 }
274 }
275 return static_cast<int>(preview_.size());
276 }
277 std::vector<std::pair<int, int>> cells;
278 collectBrushCells(tx, ty, cells);
279 int gid = (tool_ == "erase") ? eraseTile_ : tile_;
280 return previewCells(buffer, cells, gid);
281}
282
283int Brush::previewLine(TileBuffer *buffer, int x0, int y0, int x1, int y1) {
284 std::vector<std::pair<int, int>> cells;
285 collectLineCells(x0, y0, x1, y1, cells);
286 int gid = (tool_ == "erase") ? eraseTile_ : tile_;
287 return previewCells(buffer, cells, gid);
288}
289
290int Brush::previewRect(TileBuffer *buffer, int x0, int y0, int x1, int y1, bool filled) {
291 std::vector<std::pair<int, int>> cells;
292 collectRectCells(x0, y0, x1, y1, filled, cells);
293 int gid = (tool_ == "erase") ? eraseTile_ : tile_;
294 return previewCells(buffer, cells, gid);
295}
296
297bool Brush::validChange(int index) const {
298 return index >= 0 && index < static_cast<int>(changes_.size());
299}
300bool Brush::validPreview(int index) const {
301 return index >= 0 && index < static_cast<int>(preview_.size());
302}
303
304int Brush::getPreviewX(int index) const {
305 if (!validPreview(index)) throw Exception("Brush::getPreviewX: bad index");
306 return preview_[index].x;
307}
308int Brush::getPreviewY(int index) const {
309 if (!validPreview(index)) throw Exception("Brush::getPreviewY: bad index");
310 return preview_[index].y;
311}
312int Brush::getPreviewGid(int index) const {
313 if (!validPreview(index)) throw Exception("Brush::getPreviewGid: bad index");
314 return preview_[index].gid;
315}
316int Brush::getChangeX(int index) const {
317 if (!validChange(index)) throw Exception("Brush::getChangeX: bad index");
318 return changes_[index].x;
319}
320int Brush::getChangeY(int index) const {
321 if (!validChange(index)) throw Exception("Brush::getChangeY: bad index");
322 return changes_[index].y;
323}
324int Brush::getChangeOldGid(int index) const {
325 if (!validChange(index)) throw Exception("Brush::getChangeOldGid: bad index");
326 return changes_[index].oldGid;
327}
328int Brush::getChangeNewGid(int index) const {
329 if (!validChange(index)) throw Exception("Brush::getChangeNewGid: bad index");
330 return changes_[index].gid;
331}
332
333} // namespace eve::editor
int y
Definition Grass.cpp:135
float height
Definition Grass.cpp:235
float u
Definition Grass.cpp:234
int x
Definition Grass.cpp:135
uint32_t c
int width
int idx
glm::vec4 p[6]
int d
int getPreviewX(int index) const
Definition Brush.cpp:304
void setSize(int size)
Definition Brush.cpp:22
void setTool(const std::string &tool)
Definition Brush.cpp:14
int getPreviewGid(int index) const
Definition Brush.cpp:312
int paintLine(TileBuffer *buffer, int x0, int y0, int x1, int y1)
Definition Brush.cpp:243
int previewAt(TileBuffer *buffer, int tx, int ty)
Fill preview buffer without mutating target.
Definition Brush.cpp:257
void setStampTile(int lx, int ly, int gid)
Definition Brush.cpp:45
int eraseAt(TileBuffer *buffer, int tx, int ty)
Definition Brush.cpp:201
int previewRect(TileBuffer *buffer, int x0, int y0, int x1, int y1, bool filled)
Definition Brush.cpp:290
int paintAt(TileBuffer *buffer, int tx, int ty)
Apply current tool at / between cells. Returns cells changed.
Definition Brush.cpp:192
int paintRect(TileBuffer *buffer, int x0, int y0, int x1, int y1, bool filled)
Definition Brush.cpp:250
void setTile(int gid)
Definition Brush.cpp:34
int previewLine(TileBuffer *buffer, int x0, int y0, int x1, int y1)
Definition Brush.cpp:283
int getChangeNewGid(int index) const
Definition Brush.cpp:328
int getChangeX(int index) const
Definition Brush.cpp:316
void setEraseTile(int gid)
Definition Brush.cpp:36
int getStampTile(int lx, int ly) const
Definition Brush.cpp:52
int getChangeY(int index) const
Definition Brush.cpp:320
int getChangeOldGid(int index) const
Definition Brush.cpp:324
void setShape(const std::string &shape)
Definition Brush.cpp:27
int getPreviewY(int index) const
Definition Brush.cpp:308
void clearStamp()
Definition Brush.cpp:59
int floodFill(TileBuffer *buffer, int tx, int ty)
Definition Brush.cpp:207
void setStampSize(int width, int height)
Definition Brush.cpp:38
Independent GID grid for map brushes (no hard dependency on map.TileLayer).
Definition TileBuffer.h:9
int getGid(int x, int y) const
bool inBounds(int x, int y) const
void setGid(int x, int y, int gid)