Program Listing for File bresenham.hpp¶
↰ Return to documentation for file (libtcod/bresenham.hpp)
/* BSD 3-Clause License
*
* Copyright © 2008-2025, Jice and the libtcod contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#ifndef TCOD_BRESENHAM_HPP_
#define TCOD_BRESENHAM_HPP_
#include <array>
#include <functional>
#include <iterator>
#include <vector>
#include "bresenham.h"
// clang-format off
class TCODLIB_API TCODLineListener {
public :
virtual bool putPoint(int x,int y) = 0;
virtual ~TCODLineListener() {}
};
class TCODLIB_API TCODLine {
public :
static void init(int xFrom, int yFrom, int xTo, int yTo);
static bool step(int *xCur, int *yCur);
static bool line(int xFrom, int yFrom, int xTo, int yTo, TCODLineListener *listener);
};
// clang-format on
namespace tcod {
class BresenhamLine {
public:
using Point2 = std::array<int, 2>;
using iterator_category = std::random_access_iterator_tag;
using value_type = Point2;
using difference_type = int;
using pointer = void;
using reference = value_type;
explicit BresenhamLine(Point2 begin, Point2 end) noexcept
: origin_{begin},
dest_{end},
index_{0},
index_end_{get_delta_x() + 1},
cursor_{0, 0},
y_error_{-get_delta_x() / 2} {}
explicit BresenhamLine(Point2 begin, Point2 end, int error) noexcept
: origin_{begin},
dest_{end},
index_{0},
index_end_{get_delta_x() + 1},
cursor_{0, 0},
y_error_{error > 0 ? error % get_delta_x() - get_delta_x() : error % get_delta_x()} {}
inline BresenhamLine& operator++() noexcept {
++index_;
return *this;
}
inline BresenhamLine operator++(int) noexcept {
auto tmp = *this;
++(*this);
return tmp;
}
inline BresenhamLine& operator--() noexcept {
--index_;
return *this;
}
inline BresenhamLine operator--(int) noexcept {
auto tmp = *this;
--(*this);
return tmp;
}
inline value_type operator[](int index) noexcept { return bresenham_get(index_ + index); }
inline value_type operator*() noexcept { return (*this)[0]; }
inline constexpr bool operator==(const BresenhamLine& rhs) const noexcept { return index_ == rhs.index_; }
inline constexpr bool operator!=(const BresenhamLine& rhs) const noexcept { return !(*this == rhs); }
inline constexpr difference_type operator-(const BresenhamLine& rhs) const noexcept { return index_ - rhs.index_; }
inline BresenhamLine adjust_range(int shift_begin, int shift_end) const noexcept {
BresenhamLine new_data{*this};
new_data.index_ += shift_begin;
new_data.index_end_ += shift_end;
new_data.index_end_ = std::max(new_data.index_, new_data.index_end_);
return new_data;
}
inline BresenhamLine without_start() const noexcept { return adjust_range(1, 0); }
inline BresenhamLine without_end() const noexcept { return adjust_range(0, -1); }
inline BresenhamLine without_endpoints() const noexcept { return adjust_range(1, -1); }
inline BresenhamLine begin() const noexcept { return {*this}; }
inline BresenhamLine end() const noexcept {
return BresenhamLine{origin_, dest_, index_end_, index_end_, cursor_, y_error_};
}
private:
struct Matrix {
inline Point2 transform(const Point2& cursor) const noexcept {
return {ax + cursor.at(0) * xx + cursor.at(1) * yx, ay + cursor.at(0) * xy + cursor.at(1) * yy};
}
int ax; // Affine transformation on X.
int ay; // Affine transformation on Y.
int_fast8_t xx; // Index to world X.
int_fast8_t xy; // Index to world Y.
int_fast8_t yx; // Cursor Y to world X.
int_fast8_t yy; // Cursor Y to world Y.
};
inline Matrix get_matrix() const noexcept { return get_matrix(origin_, dest_); }
static Matrix get_matrix(const Point2& origin, const Point2& dest) noexcept {
const int delta_x = dest.at(0) - origin.at(0);
const int delta_y = dest.at(1) - origin.at(1);
Matrix matrix{
origin.at(0),
origin.at(1),
1,
0,
0,
1,
};
if (delta_x < 0) matrix.xx = -1;
if (delta_y < 0) matrix.yy = -1;
if (std::abs(delta_y) > std::abs(delta_x)) {
std::swap(matrix.xx, matrix.yx);
std::swap(matrix.xy, matrix.yy);
}
return matrix;
}
explicit BresenhamLine(Point2 begin, Point2 end, int index_begin, int index_end, Point2 cursor, int error) noexcept
: origin_{begin}, dest_{end}, index_{index_begin}, index_end_{index_end}, cursor_{cursor}, y_error_{error} {}
inline Point2 get_normalized_delta() const noexcept { return get_normalized_delta(origin_, dest_); }
static Point2 get_normalized_delta(const Point2& origin, const Point2& dest) noexcept {
return std::abs(dest.at(0) - origin.at(0)) > std::abs(dest.at(1) - origin.at(1))
? Point2{std::abs(dest.at(0) - origin.at(0)), std::abs(dest.at(1) - origin.at(1))}
: Point2{std::abs(dest.at(1) - origin.at(1)), std::abs(dest.at(0) - origin.at(0))};
}
inline int get_delta_x() const noexcept { return get_normalized_delta().at(0); }
inline BresenhamLine& bresenham_next() {
const Point2 delta = get_normalized_delta();
y_error_ += delta.at(1);
if (y_error_ > 0) {
++cursor_.at(1);
y_error_ -= delta.at(0);
};
++cursor_.at(0);
return *this;
}
inline BresenhamLine& bresenham_prev() {
const Point2 delta = get_normalized_delta();
y_error_ -= delta.at(1);
if (y_error_ <= -delta.at(0)) {
--cursor_.at(1);
y_error_ += delta.at(0);
};
--cursor_.at(0);
return *this;
}
inline Point2 bresenham_get(int index) {
while (cursor_.at(0) < index) bresenham_next();
while (cursor_.at(0) > index) bresenham_prev();
return get_matrix().transform(cursor_);
}
Point2 origin_; // Starting point.
Point2 dest_; // Ending point.
int index_; // The starting index returned by `begin`.
int index_end_; // The past-the-end index returned by `end`.
Point2 cursor_; // Normalized Bresenham low-slope position. First axis acts as the current index.
int y_error_; // Fractional difference between Y indexes. Is always `-delta[0] < err <= 0`.
};
} // namespace tcod
#endif // TCOD_BRESENHAM_HPP_