/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018 Danny Robson */ #pragma once #include "../point.hpp" namespace cruft::geom { /// Represents a line that has a start and an end. /// /// It is not valid to create an unbounded segment by fixing one of the /// points at infinity. template struct segment { cruft::point a; /// The start of the segment. cruft::point b; /// The end of the segment. /// Return a copy of this object with the underlying type casted to /// the specified type. template segment cast (void) const { return { .a = a.template cast (), .b = b.template cast () }; } template segment indices (ArgsT &&...args) const { return segment { .a = a.template indices (args...), .b = b.template indices (args...), }; } }; /// Offset a segment by the supplied vector. template segment operator+ (segment lhs, vector rhs) { return { lhs.a + rhs, lhs.b + rhs }; } /// Return the squared distance from the closest point of the segment `s` /// to the point `p`. template T distance2 (segment s, point p) { const auto dir = s.b - s.a; const auto t1 = dot (p - s.a, dir); if (t1 < 0) return distance2 (p, s.a); const auto t2 = dot (dir, dir); if (t2 < t1) return distance2 (p, s.b); auto t = t1 / t2; return distance2 (p, s.a + t * dir); } /// Return the distance from a the closest point of the segment `s` to /// the point `p`. template T distance (segment s, point p) { return std::sqrt (distance2 (s, p)); } /// Returns the un-normalised direction of the segment from its start to /// end components. ie, `a' to `b'. template cruft::vector udirection (segment const &val) { return val.b - val.a; } /// Returns the normalised direction of the segment. template cruft::vector ndirection (segment const &val) { return normalised (udirection (val)); } /// An implementation of Bresenham's line algorithm. /// Ref: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm /// /// This operates as a container that iterates over the integral points /// on the line. Use in range-for is explicitly supported. /// /// The start and end segment points will both be generated. class bresenham { public: bresenham (cruft::geom::segment<2,int> _src) : m_src (_src) { ; } class end_iterator { }; end_iterator end (void) { return {}; } class begin_iterator { public: begin_iterator (cruft::geom::segment<2,int> _src) : m_start (_src.a) , m_diff (_src.b - _src.a) { // Find the axis directions and finalise the diff auto const sign = select (m_diff > 0, 1, -1); m_diff = abs (m_diff); // Setup the increment for each possible major axis. if (m_diff.x > m_diff.y) { m_dirx = { sign.x, 0 }; m_diry = { 0, sign.y }; } else { std::swap (m_diff.x, m_diff.y); m_dirx = { 0, sign.y }; m_diry = { sign.x, 0 }; } // Set the initial offsets D = 2 * m_diff.y - m_diff.x; x = y = 0; } begin_iterator& operator++ () & { // We've gone far enough. Drop down a line. if (D >= 0) { y += 1; D -= 2 * m_diff.x; } // Step forwards. D += 2 * m_diff.y; ++x; return *this; } bool operator== (end_iterator const&) const noexcept { return x >= m_diff.x + 1; } bool operator!= (end_iterator const &rhs) const noexcept { return !(*this == rhs); } cruft::point2i operator* () const noexcept { return m_start + x * m_dirx + y * m_diry; } private: cruft::point2i m_start; /// The initial point of the segment. cruft::vector2i m_diff; /// Magnitude of total traversal for each axis. cruft::vector2i m_dirx; /// Direction of increment for the X axis. cruft::vector2i m_diry; /// Direction of increment for the Y axis. int D; /// Stepping accumulator to detect line changes . int x; /// Current X parameter. int y; /// Current Y parameter. }; begin_iterator begin (void) const { return { m_src }; } private: cruft::geom::segment<2,int> m_src; }; using segment2i = segment<2,int>; using segment3i = segment<3,int>; using segment2f = segment<2,float>; using segment3f = segment<3,float>; template segment (point, point) -> segment; }