// SPDX-License-Identifier: LGPL-3.0-or-later #include "sweep_line_tool.h" #include "dda_line_tool.h" #include "util.h" #include #include #include // Calculate the inverse of the DDA function. int dda_inv(int x0, int y0, float m, int y) { // This uses the regular function that is the basis of DDA // // y_i = y_0 + m·(x_i - x_0) // // but rearranges it to be the inverse function: // // y_i = y_0 + m·(x_i - x_0) // ⇔ y_i - y_0 + x_0·m = x_i·m // ⇔ x_i = (y_i - y_0)/m + x_0 // // This returns a valid x coordinate on the line // starting from (x0, y0) with the slope m. // Handle special case of flat line if (m == 0) return x0; else return round((y - y0) / m + x0); } sweep_line_tool::sweep_line_tool(canvas_buffer &canvas) : tool_base(canvas) { shape = TS_NONE; is_draggable = false; } void sweep_line_tool::draw_interval(int b1, int b2, int y) { for (int x = std::min(b1, b2); x <= std::max(b1, b2); x++) { canvas.set_pixel(x, y); } } void sweep_line_tool::draw() { draw(10, 10, 90, 30, 30, 90); } void sweep_line_tool::draw(int, int) { draw(); } void sweep_line_tool::draw(int x0, int y0, int x1, int y1, int x2, int y2) { /* * Terminology: * * (x0, y0) * + * | \ * | \ m_1 * |first\ * | pass \ * m_shared |---------+ (x1, y1) * |second / * |pass / * | / m_2 * | / * + * (x2, y2) */ // Sort triangle points (in place) so that y0 < y1 < y2 sort_triangle_points(x0, y0, x1, y1, x2, y2); // Slope of the side limiting the first pass (only) float m_1 = slope(x0, y0, x1, y1); // Slope of the side limiting the second pass (only) float m_2 = slope(x1, y1, x2, y2); // Slope of the side limiting both passes float m_shared = slope(x0, y0, x2, y2); // First pass if (y0 == y1) { // If the first two points are on the same height, only draw one line. // This is only needed for the first interval, // because in the case that y1 == y2, // the problematic line would have already been handled in the first pass. draw_interval(x0, x1, y0); } else { for (int y = y0; y <= y1; y++) { int b1 = dda_inv(x0, y0, m_1, y); int b2 = dda_inv(x0, y0, m_shared, y); draw_interval(b1, b2, y); } } // Second pass // it can start iterating at y1 + 1, // because y1 is already included in the first pass. for (int y = y1 + 1; y <= y2; y++) { int b1 = dda_inv(x1, y1, m_2, y); int b2 = dda_inv(x0, y0, m_shared, y); draw_interval(b1, b2, y); } } void sweep_line_tool::set_text(std::stringstream &stream) { stream << "Tool: Sweep-Line"; }