mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-14 04:24:52 +02:00
Refactor drawing of parametrized curve
Work towards a proper rendering of thick curves using a derivative to control sampling frequency.
This commit is contained in:
@@ -258,13 +258,17 @@ append_limit(Canvas *self, double upper, double lower) {
|
||||
self->y_limits[self->y_limits_count++].lower = lower;
|
||||
}
|
||||
|
||||
|
||||
static uint
|
||||
thickness(Canvas *self, uint level, bool horizontal) {
|
||||
static double
|
||||
thickness_as_float(Canvas *self, uint level, bool horizontal) {
|
||||
level = min(level, arraysz(OPT(box_drawing_scale)));
|
||||
double pts = OPT(box_drawing_scale)[level];
|
||||
double dpi = horizontal ? self->dpi.x : self->dpi.y;
|
||||
return (uint)ceil(self->supersample_factor * self->scale * pts * dpi / 72.0);
|
||||
return self->supersample_factor * self->scale * pts * dpi / 72.0;
|
||||
}
|
||||
|
||||
static uint
|
||||
thickness(Canvas *self, uint level, bool horizontal) {
|
||||
return (uint)ceil(thickness_as_float(self, level, horizontal));
|
||||
}
|
||||
|
||||
static const uint hole_factor = 8;
|
||||
@@ -700,6 +704,39 @@ static bool cmpr_point(Point a, Point b) { return a.val == b.val; }
|
||||
vt_cleanup(&seen); \
|
||||
}
|
||||
|
||||
static double
|
||||
distance(double x1, double y1, double x2, double y2) {
|
||||
const double dx = x1 - x2;
|
||||
const double dy = y1 - y2;
|
||||
return sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
typedef double(*curve_func)(void *, double t);
|
||||
|
||||
static void
|
||||
draw_parametrized_curve_with_derivative(Canvas *self, void *curve_data, uint level, curve_func xfunc, curve_func yfunc) {
|
||||
double th = thickness_as_float(self, level, true);
|
||||
double step = 1.0 / (self->height);
|
||||
double half_thickness = th / 2.0;
|
||||
double t = -step;
|
||||
do {
|
||||
t += step; if (t > 1.0) t = 1.0;
|
||||
double x = xfunc(curve_data, t), y = yfunc(curve_data, t);
|
||||
for (double dy = -th; dy <= th; dy++) {
|
||||
for (double dx = -th; dx <= th; dx++) {
|
||||
double px = x + dx, py = y + dy;
|
||||
double dist = distance(x, y, px, py);
|
||||
int row = (int)py, col = (int)px;
|
||||
if (dist > half_thickness || row >= (int)self->height || row < 0 || col >= (int)self->width || col < 0) continue;
|
||||
const int offset = row * self->width + col;
|
||||
double alpha = 1.0 - (dist / half_thickness);
|
||||
uint8_t old_alpha = self->mask[offset];
|
||||
self->mask[offset] = (uint8_t)(alpha * 255 + (1 - alpha) * old_alpha);
|
||||
}
|
||||
}
|
||||
} while (t < 1.0);
|
||||
}
|
||||
|
||||
static void
|
||||
rounded_separator(Canvas *self, uint level, bool left) {
|
||||
uint gap = thickness(self, level, true);
|
||||
@@ -1187,38 +1224,41 @@ fading_vline(Canvas *self, uint level, uint num, Edge fade) {
|
||||
}
|
||||
|
||||
typedef struct Rectircle Rectircle;
|
||||
typedef double (*Rectircle_equation)(Rectircle r, double t);
|
||||
|
||||
typedef struct Rectircle {
|
||||
uint a, b;
|
||||
double yexp, xexp, adjust_x;
|
||||
uint cell_width;
|
||||
Rectircle_equation x, y;
|
||||
curve_func x, y;
|
||||
} Rectircle;
|
||||
|
||||
static double
|
||||
rectircle_lower_quadrant_y(Rectircle r, double t) {
|
||||
return r.b * t; // 0 -> top of cell, 1 -> middle of cell
|
||||
rectircle_lower_half_y(void *v, double t) {
|
||||
Rectircle *r = v;
|
||||
return r->b * t; // 0 -> top of cell, 1 -> middle of cell
|
||||
}
|
||||
|
||||
static double
|
||||
rectircle_upper_quadrant_y(Rectircle r, double t) {
|
||||
return r.b * (2. - t); // 0 -> bottom of cell, 1 -> middle of cell
|
||||
rectircle_upper_half_y(void *v, double t) {
|
||||
Rectircle *r = v;
|
||||
return r->b * (2. - t); // 0 -> bottom of cell, 1 -> middle of cell
|
||||
}
|
||||
|
||||
// x(t). To get this we first need |y(t)|/b. This is just t since as t goes
|
||||
// from 0 to 1 y goes from either 0 to b or 0 to -b
|
||||
|
||||
static double
|
||||
rectircle_left_quadrant_x(Rectircle r, double t) {
|
||||
double xterm = 1 - pow(t, r.yexp);
|
||||
return floor(r.cell_width - fabs(r.a * pow(xterm, r.xexp)) - r.adjust_x);
|
||||
rectircle_left_half_x(void *v, double t) {
|
||||
Rectircle *r = v;
|
||||
double xterm = 1 - pow(t, r->yexp);
|
||||
return floor(r->cell_width - fabs(r->a * pow(xterm, r->xexp)) - r->adjust_x);
|
||||
}
|
||||
|
||||
static double
|
||||
rectircle_right_quadrant_x(Rectircle r, double t) {
|
||||
double xterm = 1 - pow(t, r.yexp);
|
||||
return ceil(fabs(r.a * pow(xterm, r.xexp)));
|
||||
rectircle_right_half_x(void *v, double t) {
|
||||
Rectircle *r = v;
|
||||
double xterm = 1 - pow(t, r->yexp);
|
||||
return ceil(fabs(r->a * pow(xterm, r->xexp)));
|
||||
}
|
||||
|
||||
static Rectircle
|
||||
@@ -1228,7 +1268,7 @@ rectcircle(Canvas *self, Corner which) {
|
||||
in the range [0, 1] to x and y coordinates in the cell. The rectircle equation
|
||||
we use is:
|
||||
|
||||
(|x| / a) ^ (2a / r) + (|y| / a) ^ (2b / r) = 1
|
||||
(|x| / a) ^ (2a / r) + (|y| / b) ^ (2b / r) = 1
|
||||
|
||||
where 2a = width, 2b = height and r is radius
|
||||
|
||||
@@ -1248,8 +1288,8 @@ rectcircle(Canvas *self, Corner which) {
|
||||
.xexp = radius / self->width,
|
||||
.cell_width = self->width,
|
||||
.adjust_x = cell_width_is_odd * self->supersample_factor,
|
||||
.x = which & LEFT_EDGE ? rectircle_left_quadrant_x : rectircle_right_quadrant_x,
|
||||
.y = which & TOP_EDGE ? rectircle_upper_quadrant_y : rectircle_lower_quadrant_y,
|
||||
.x = which & LEFT_EDGE ? rectircle_left_half_x : rectircle_right_half_x,
|
||||
.y = which & TOP_EDGE ? rectircle_upper_half_y : rectircle_lower_half_y,
|
||||
};
|
||||
|
||||
return ans;
|
||||
@@ -1258,7 +1298,7 @@ rectcircle(Canvas *self, Corner which) {
|
||||
static void
|
||||
rounded_corner(Canvas *self, uint level, Corner which) {
|
||||
Rectircle r = rectcircle(self, which);
|
||||
draw_parametrized_curve(self, level, r.x(r, t), r.y(r, t));
|
||||
draw_parametrized_curve_with_derivative(self, &r, level, r.x, r.y);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
Reference in New Issue
Block a user