libtcod
Loading...
Searching...
No Matches
bresenham.hpp
Go to the documentation of this file.
1/* BSD 3-Clause License
2 *
3 * Copyright © 2008-2025, Jice and the libtcod contributors.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
34#pragma once
35#ifndef TCOD_BRESENHAM_HPP_
36#define TCOD_BRESENHAM_HPP_
37
38#include <array>
39#include <functional>
40#include <iterator>
41#include <vector>
42
43#include "bresenham.h"
44
47
48class TCODLIB_API TCODLineListener {
49 public:
50 virtual bool putPoint(int x, int y) = 0;
51 virtual ~TCODLineListener() {}
52};
53// clang-format off
54class TCODLIB_API TCODLine {
55public :
71 static void init(int xFrom, int yFrom, int xTo, int yTo);
72
112 static bool step(int *xCur, int *yCur);
113
153 static bool line(int xFrom, int yFrom, int xTo, int yTo, TCODLineListener *listener);
154};
155// clang-format on
157namespace tcod {
160
168 public:
169 using Point2 = std::array<int, 2>;
170 using iterator_category = std::random_access_iterator_tag;
171 using value_type = Point2;
172 using difference_type = int;
173 using pointer = void;
174 using reference = value_type;
180 explicit BresenhamLine(Point2 begin, Point2 end) noexcept
181 : origin_{begin},
182 dest_{end},
183 index_{0},
184 index_end_{get_delta_x() + 1},
185 cursor_{0, 0},
186 y_error_{-get_delta_x() / 2} {}
187
190 explicit BresenhamLine(Point2 begin, Point2 end, int error) noexcept
191 : origin_{begin},
192 dest_{end},
193 index_{0},
194 index_end_{get_delta_x() + 1},
195 cursor_{0, 0},
196 y_error_{error > 0 ? error % get_delta_x() - get_delta_x() : error % get_delta_x()} {}
197
198 inline BresenhamLine& operator++() noexcept {
199 ++index_;
200 return *this;
201 }
202 inline BresenhamLine operator++(int) noexcept {
203 auto tmp = *this;
204 ++(*this);
205 return tmp;
206 }
207 inline BresenhamLine& operator--() noexcept {
208 --index_;
209 return *this;
210 }
211 inline BresenhamLine operator--(int) noexcept {
212 auto tmp = *this;
213 --(*this);
214 return tmp;
215 }
224 inline value_type operator[](int index) noexcept { return bresenham_get(index_ + index); }
228 inline value_type operator*() noexcept { return (*this)[0]; }
229 inline constexpr bool operator==(const BresenhamLine& rhs) const noexcept { return index_ == rhs.index_; }
230 inline constexpr bool operator!=(const BresenhamLine& rhs) const noexcept { return !(*this == rhs); }
231 inline constexpr difference_type operator-(const BresenhamLine& rhs) const noexcept { return index_ - rhs.index_; }
232
244 inline BresenhamLine adjust_range(int shift_begin, int shift_end) const noexcept {
245 BresenhamLine new_data{*this};
246 new_data.index_ += shift_begin;
247 new_data.index_end_ += shift_end;
248 new_data.index_end_ = std::max(new_data.index_, new_data.index_end_);
249 return new_data;
250 }
251
260 inline BresenhamLine without_start() const noexcept { return adjust_range(1, 0); }
270 inline BresenhamLine without_end() const noexcept { return adjust_range(0, -1); }
280 inline BresenhamLine without_endpoints() const noexcept { return adjust_range(1, -1); }
284 inline BresenhamLine begin() const noexcept { return {*this}; }
288 inline BresenhamLine end() const noexcept {
289 return BresenhamLine{origin_, dest_, index_end_, index_end_, cursor_, y_error_};
290 }
291
292 private:
296 struct Matrix {
300 inline Point2 transform(const Point2& cursor) const noexcept {
301 return {ax + cursor.at(0) * xx + cursor.at(1) * yx, ay + cursor.at(0) * xy + cursor.at(1) * yy};
302 }
303 int ax; // Affine transformation on X.
304 int ay; // Affine transformation on Y.
305 int_fast8_t xx; // Index to world X.
306 int_fast8_t xy; // Index to world Y.
307 int_fast8_t yx; // Cursor Y to world X.
308 int_fast8_t yy; // Cursor Y to world Y.
309 };
313 inline Matrix get_matrix() const noexcept { return get_matrix(origin_, dest_); }
314 static Matrix get_matrix(const Point2& origin, const Point2& dest) noexcept {
315 const int delta_x = dest.at(0) - origin.at(0);
316 const int delta_y = dest.at(1) - origin.at(1);
317 Matrix matrix{
318 origin.at(0),
319 origin.at(1),
320 1,
321 0,
322 0,
323 1,
324 };
325 if (delta_x < 0) matrix.xx = -1;
326 if (delta_y < 0) matrix.yy = -1;
327 if (std::abs(delta_y) > std::abs(delta_x)) {
328 std::swap(matrix.xx, matrix.yx);
329 std::swap(matrix.xy, matrix.yy);
330 }
331 return matrix;
332 }
333 explicit BresenhamLine(Point2 begin, Point2 end, int index_begin, int index_end, Point2 cursor, int error) noexcept
334 : origin_{begin}, dest_{end}, index_{index_begin}, index_end_{index_end}, cursor_{cursor}, y_error_{error} {}
340 inline Point2 get_normalized_delta() const noexcept { return get_normalized_delta(origin_, dest_); }
341 static Point2 get_normalized_delta(const Point2& origin, const Point2& dest) noexcept {
342 return std::abs(dest.at(0) - origin.at(0)) > std::abs(dest.at(1) - origin.at(1))
343 ? Point2{std::abs(dest.at(0) - origin.at(0)), std::abs(dest.at(1) - origin.at(1))}
344 : Point2{std::abs(dest.at(1) - origin.at(1)), std::abs(dest.at(0) - origin.at(0))};
345 }
351 inline int get_delta_x() const noexcept { return get_normalized_delta().at(0); }
355 inline BresenhamLine& bresenham_next() {
356 const Point2 delta = get_normalized_delta();
357 y_error_ += delta.at(1);
358 if (y_error_ > 0) {
359 ++cursor_.at(1);
360 y_error_ -= delta.at(0);
361 };
362 ++cursor_.at(0);
363 return *this;
364 }
368 inline BresenhamLine& bresenham_prev() {
369 const Point2 delta = get_normalized_delta();
370 y_error_ -= delta.at(1);
371 if (y_error_ <= -delta.at(0)) {
372 --cursor_.at(1);
373 y_error_ += delta.at(0);
374 };
375 --cursor_.at(0);
376 return *this;
377 }
381 inline Point2 bresenham_get(int index) {
382 while (cursor_.at(0) < index) bresenham_next();
383 while (cursor_.at(0) > index) bresenham_prev();
384 return get_matrix().transform(cursor_);
385 }
386 Point2 origin_; // Starting point.
387 Point2 dest_; // Ending point.
388 int index_; // The starting index returned by `begin`.
389 int index_end_; // The past-the-end index returned by `end`.
390 Point2 cursor_; // Normalized Bresenham low-slope position. First axis acts as the current index.
391 int y_error_; // Fractional difference between Y indexes. Is always `-delta[0] < err <= 0`.
392};
393
394} // namespace tcod
395#endif // TCOD_BRESENHAM_HPP_
Bresenham line module.
Definition bresenham.hpp:54
static void init(int xFrom, int yFrom, int xTo, int yTo)
First, you have to initialize the toolkit with your starting and ending coordinates.
static bool line(int xFrom, int yFrom, int xTo, int yTo, TCODLineListener *listener)
The function returns false if the line has been interrupted by the callback (it returned false before...
static bool step(int *xCur, int *yCur)
You can then step through each cell with this function.
Definition bresenham.hpp:48
Encapsulates a Bresenham line drawing algorithm.
Definition bresenham.hpp:167
BresenhamLine without_endpoints() const noexcept
Definition bresenham.hpp:280
value_type operator*() noexcept
Return the world position of the Bresenham at the current index.
Definition bresenham.hpp:228
BresenhamLine begin() const noexcept
Return the beginning iterator, which is a copy of the current object.
Definition bresenham.hpp:284
BresenhamLine without_end() const noexcept
Definition bresenham.hpp:270
BresenhamLine(Point2 begin, Point2 end) noexcept
Construct a new Bresenham line from begin to end.
Definition bresenham.hpp:180
value_type operator[](int index) noexcept
Return the world position of the Bresenham at the index relative to the current index.
Definition bresenham.hpp:224
BresenhamLine(Point2 begin, Point2 end, int error) noexcept
Construct a new Bresenham line with a manually given error value.
Definition bresenham.hpp:190
BresenhamLine adjust_range(int shift_begin, int shift_end) const noexcept
Definition bresenham.hpp:244
BresenhamLine end() const noexcept
Return the past-the-end iterator.
Definition bresenham.hpp:288
BresenhamLine without_start() const noexcept
Definition bresenham.hpp:260
A template container for holding a multi-dimensional array of items.
Definition matrix.hpp:139
The libtcod namespace.
Definition bresenham.hpp:157