As the title says, I want to draw a quadratic Bézier curve on a linear framebuffer (aka plotting pixels).
I already have a function that's drawing a cubic Bézier curve according to https://www.joshondesign.com/2018/07/11/bezier-curves (I translated the javascript into C like this https://gist.github.com/cheyao/a736a58c4cf683eabea2aa2a87718ef1#file-cubic-c) which works fine.
And now I tried to convert it into drawing a quadratic Bézier curve (like this https://gist.github.com/cheyao/a736a58c4cf683eabea2aa2a87718ef1#file-flatness-c), which is looking not-fine for me (too not curvish).
I also tried to brutal force it like this
void quadratic_bezier_curve(const Vector2 p[3], const color_t color) {
for (double t = 0; t < 1; t = 0.001) {
putPixel((uint32_t) ((1 - t) * (1 - t) * p[0].x 2 * (1 - t) * t * p[1].x t * t * p[2].x),
(uint32_t) ((1 - t) * (1 - t) * p[0].y 2 * (1 - t) * t * p[1].y t * t * p[2].y), color);
}
}
Which makes the like look bold.
So my question comes: Does anyone know some better algorithms to draw a (quadratic) Bézier curves? (I need it for font rendering.)
CodePudding user response:
Your code works. A mistake found, that for flatness we need to check absolute values. And there was a typo that p[1].y
was used twice instead of once. So it would be like:
static int32_t quadratic_flatness(const Vector2 p[3]) {
return abs((int32_t) p[1].x - (((int32_t) p[0].x (int32_t) p[2].x) / 2))
abs((int32_t) p[1].y - (((int32_t) p[0].y (int32_t) p[2].y) / 2));
}
Live example in JS (click 'Run code snippet' button and start clicking with mouse left button on the canvas):
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
document.body.style.margin = 0;
canvas.style.position = 'fixed';
// get canvas 2D context and set him correct size
var ctx = canvas.getContext('2d');
resize();
let points = [];
window.addEventListener('resize', resize);
document.addEventListener('mousedown', setPosition);
function quadratic_split_curve(p) { // out[2][3]
const p12 = midpoint(p[0], p[1]);
const p23 = midpoint(p[1], p[2]);
const p123 = midpoint(p12, p23);
return [
[p[0], p12, p123],
[p123, p23, p[2]]
];
}
function quadratic_flatness(p) {
const result = Math.abs(p[1].x - (Math.floor( p[0].x p[2].x) / 2)) Math.abs(p[1].y - (Math.floor( p[0].y p[2].y) / 2));
return result;
}
function quadratic_bezier_curve(p, color) {
if (quadratic_flatness(p) < 2) {
line(p[0], p[2], color);
return;
} else {
const split = quadratic_split_curve(p);
quadratic_bezier_curve(split[0], color);
quadratic_bezier_curve(split[1], color);
}
}
function midpoint(a, b) {
return {
x: (a.x b.x)/2,
y: (a.y b.y)/2
}
}
// new position from mouse event
function setPosition(e) {
if(points.length >= 3) {
points = [];
}
points.push({ x: e.clientX, y: e.clientY });
draw();
}
function resize() {
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
}
function clear() {
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
}
function dot(point) {
ctx.fillRect(point.x-1,point.y-1,3,3);
}
function line(a, b, color) {
ctx.strokeStyle = color;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.stroke();
}
function draw() {
clear();
for(const point of points) {
dot(point);
}
if(points.length == 3) {
ctx.lineWidth = 1;
quadratic_bezier_curve(points, '#c0392b');
}
if(points.length > 1) {
ctx.lineWidth = 2;
ctx.strokeStyle = '#3039cb77';
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for(i=1; i<points.length; i ) {
ctx.lineTo(points[i].x, points[i].y);
}
ctx.stroke();
}
return;
}