103 lines
2.7 KiB
C++
103 lines
2.7 KiB
C++
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
#include "sweep_line_tool.h"
|
|
#include "dda_line_tool.h"
|
|
#include "util.h"
|
|
#include <cmath>
|
|
#include <iostream>
|
|
#include <vector>
|
|
|
|
// 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";
|
|
}
|