mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-08 12:31:51 +00:00
fix canvas:line lines clipping
This commit is contained in:
parent
279fb92b94
commit
a5596ed5cd
2 changed files with 93 additions and 39 deletions
|
|
@ -1,5 +1,7 @@
|
||||||
#include "ImageData.hpp"
|
#include "ImageData.hpp"
|
||||||
|
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
@ -7,6 +9,8 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
static debug::Logger logger("image-data");
|
||||||
|
|
||||||
ImageData::ImageData(ImageFormat format, uint width, uint height)
|
ImageData::ImageData(ImageFormat format, uint width, uint height)
|
||||||
: format(format), width(width), height(height) {
|
: format(format), width(width), height(height) {
|
||||||
size_t pixsize;
|
size_t pixsize;
|
||||||
|
|
@ -106,47 +110,83 @@ std::unique_ptr<ImageData> ImageData::cropped(int x, int y, int width, int heigh
|
||||||
return subImage;
|
return subImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool clip_line(int& x1, int& y1, int& x2, int& y2, int width, int height) {
|
static bool clip_line(
|
||||||
const int left = 0;
|
int& x0, int& y0, int& x1, int& y1, int width, int height
|
||||||
const int right = width;
|
) {
|
||||||
const int bottom = 0;
|
if (width <= 0 || height <= 0) {
|
||||||
const int top = height;
|
return false;
|
||||||
|
}
|
||||||
|
constexpr int INSIDE = 0;
|
||||||
|
constexpr int LEFT = 1;
|
||||||
|
constexpr int RIGHT = 2;
|
||||||
|
constexpr int BOTTOM = 4;
|
||||||
|
constexpr int TOP = 8;
|
||||||
|
|
||||||
int dx = x2 - x1;
|
const int xmin = 0;
|
||||||
int dy = y2 - y1;
|
const int ymin = 0;
|
||||||
|
const int xmax = width - 1;
|
||||||
|
const int ymax = height - 1;
|
||||||
|
|
||||||
float t0 = 0.0f;
|
auto outcode = [&](int x, int y) {
|
||||||
float t1 = 1.0f;
|
int code = INSIDE;
|
||||||
|
|
||||||
auto clip = [](int p, int q, float& t0, float& t1) {
|
if (x < xmin)
|
||||||
if (p == 0) {
|
code |= LEFT;
|
||||||
return q >= 0;
|
else if (x > xmax)
|
||||||
}
|
code |= RIGHT;
|
||||||
float t = static_cast<float>(q) / p;
|
|
||||||
if (p < 0) {
|
if (y < ymin)
|
||||||
if (t > t1) return false;
|
code |= BOTTOM;
|
||||||
if (t > t0) t0 = t;
|
else if (y > ymax)
|
||||||
} else {
|
code |= TOP;
|
||||||
if (t < t0) return false;
|
|
||||||
if (t < t1) t1 = t;
|
return code;
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!clip(-dx, x1 - left, t0, t1)) return false;
|
int c0 = outcode(x0, y0);
|
||||||
if (!clip( dx, right - x1, t0, t1)) return false;
|
int c1 = outcode(x1, y1);
|
||||||
if (!clip(-dy, y1 - bottom, t0, t1)) return false;
|
|
||||||
if (!clip( dy, top - y1, t0, t1)) return false;
|
|
||||||
|
|
||||||
if (t1 < 1.0f) {
|
while (true) {
|
||||||
x2 = x1 + static_cast<int>(std::round(t1 * dx));
|
if (!(c0 | c1)) {
|
||||||
y2 = y1 + static_cast<int>(std::round(t1 * dy));
|
return true;
|
||||||
|
}
|
||||||
|
if (c0 & c1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int out = c0 ? c0 : c1;
|
||||||
|
|
||||||
|
double x = 0.0;
|
||||||
|
double y = 0.0;
|
||||||
|
|
||||||
|
if (out & TOP) {
|
||||||
|
y = ymax;
|
||||||
|
x = x0 + (x1 - x0) * static_cast<double>(ymax - y0) / (y1 - y0);
|
||||||
|
} else if (out & BOTTOM) {
|
||||||
|
y = ymin;
|
||||||
|
x = x0 + (x1 - x0) * static_cast<double>(ymin - y0) / (y1 - y0);
|
||||||
|
} else if (out & RIGHT) {
|
||||||
|
x = xmax;
|
||||||
|
y = y0 + (y1 - y0) * static_cast<double>(xmax - x0) / (x1 - x0);
|
||||||
|
} else { // LEFT
|
||||||
|
x = xmin;
|
||||||
|
y = y0 + (y1 - y0) * static_cast<double>(xmin - x0) / (x1 - x0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out == c0) {
|
||||||
|
x0 = std::clamp(static_cast<int>(std::lround(x)), xmin, xmax);
|
||||||
|
y0 = std::clamp(static_cast<int>(std::lround(y)), ymin, ymax);
|
||||||
|
c0 = outcode(x0, y0);
|
||||||
|
} else {
|
||||||
|
x1 = std::clamp(static_cast<int>(std::lround(x)), xmin, xmax);
|
||||||
|
y1 = std::clamp(static_cast<int>(std::lround(y)), ymin, ymax);
|
||||||
|
c1 = outcode(x1, y1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (t0 > 0.0f) {
|
}
|
||||||
x1 = x1 + static_cast<int>(std::round(t0 * dx));
|
|
||||||
y1 = y1 + static_cast<int>(std::round(t0 * dy));
|
static bool is_point_outside(int x, int y, int width, int height) {
|
||||||
}
|
return x < 0 || y < 0 || x >= width || y >= height;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<uint channels>
|
template<uint channels>
|
||||||
|
|
@ -155,11 +195,24 @@ static void draw_line(ImageData& image, int x1, int y1, int x2, int y2, const gl
|
||||||
uint width = image.getWidth();
|
uint width = image.getWidth();
|
||||||
uint height = image.getHeight();
|
uint height = image.getHeight();
|
||||||
|
|
||||||
|
glm::ivec4 init {x1, y1, x2, y2};
|
||||||
|
|
||||||
if ((x1 < 0 || x1 >= width || x2 < 0 || x2 >= width ||
|
if ((x1 < 0 || x1 >= width || x2 < 0 || x2 >= width ||
|
||||||
y1 < 0 || y1 >= height || y2 < 0 || y2 >= height) &&
|
y1 < 0 || y1 >= height || y2 < 0 || y2 >= height) &&
|
||||||
!clip_line(x1, y1, x2, y2, width, height)) {
|
!clip_line(x1, y1, x2, y2, width, height)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// spam info for bug report
|
||||||
|
if (is_point_outside(x1, y1, width, height) || is_point_outside(x2, y2, width, height)) {
|
||||||
|
logger.warning() << "clip_line fault: [" << init.x << ", " << init.y
|
||||||
|
<< "] - [" << init.z << ", " << init.w << "] --> ["
|
||||||
|
<< x1 << ", " << y1 << "] - [" << x2 << ", " << y2
|
||||||
|
<< "]";
|
||||||
|
x1 = glm::clamp(x1, 0, static_cast<int>(width) - 1);
|
||||||
|
y1 = glm::clamp(y1, 0, static_cast<int>(height) - 1);
|
||||||
|
x2 = glm::clamp(x2, 0, static_cast<int>(width) - 1);
|
||||||
|
y2 = glm::clamp(y2, 0, static_cast<int>(height) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
int dx = std::abs(x2 - x1);
|
int dx = std::abs(x2 - x1);
|
||||||
int dy = -std::abs(y2 - y1);
|
int dy = -std::abs(y2 - y1);
|
||||||
|
|
|
||||||
|
|
@ -81,16 +81,17 @@ union RGBA {
|
||||||
uint8_t arr[4];
|
uint8_t arr[4];
|
||||||
uint32_t rgba;
|
uint32_t rgba;
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(RGBA) == 4);
|
||||||
|
|
||||||
static RGBA* get_at(const ImageData& data, uint index) {
|
static RGBA* get_at(const ImageData& data, size_t index) {
|
||||||
if (index >= data.getWidth() * data.getHeight()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return reinterpret_cast<RGBA*>(data.getData() + index * sizeof(RGBA));
|
return reinterpret_cast<RGBA*>(data.getData() + index * sizeof(RGBA));
|
||||||
}
|
}
|
||||||
|
|
||||||
static RGBA* get_at(const ImageData& data, uint x, uint y) {
|
static RGBA* get_at(const ImageData& data, uint x, uint y) {
|
||||||
return get_at(data, y * data.getWidth() + x);
|
if (x >= data.getWidth() || y >= data.getHeight()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return get_at(data, static_cast<size_t>(y) * data.getWidth() + x);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RGBA* get_at(State* L, uint x, uint y) {
|
static RGBA* get_at(State* L, uint x, uint y) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue