0

Given a series of points' coordinates, I used an algorithm in the below link to add control points to draw Bezier curve. current_algorithm

But you can load the below html code in the browser and see that the outcome is not so good. It is not very smooth and has those pointy curve. What I expect is like the image below, a more convex curve. How should I choose control points to draw curve like this?

expect_curve

expected curve link

const xs = [942, 421, 75, 171, 479];
const ys = [406, 375, 222, 99, 38];

function drawPoints(xs, ys, hostElement) {
    const len = Math.min(xs.length, ys.length);
    const r = 5;
    for (let i = 0; i < len; i++) {
        const ellipseElement = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');
        ellipseElement.setAttribute('cx', xs[i]);
        ellipseElement.setAttribute('cy', ys[i]);
        ellipseElement.setAttribute('rx', r);
        ellipseElement.setAttribute('ry', r);
        ellipseElement.setAttribute('fill', "#5572c3");
        hostElement.appendChild(ellipseElement);
    }
}

function drawQuadBezierCurve(xs, ys, hostElement) {
    const curveSegments = buildCurveSegments(xs, ys);
    const xs_new = [];
    const ys_new = [];
    buildPolylinePoints(curveSegments, xs_new, ys_new);
    drawPolyline(xs_new, ys_new, hostElement);
}

function buildCurveSegments(xs, ys) {
    const result = [];
    const len = Math.min(xs.length, ys.length);
    for (let i = 0; i < len - 1; i++) {
        const x_cur = xs[i];
        const y_cur = ys[i];
        const x_next = xs[i + 1];
        const y_next = ys[i + 1];
        const x_mid = (x_cur + x_next) / 2;
        const y_mid = (y_cur + y_next) / 2;
        const cp_x1 = (x_mid + x_cur) / 2;
        const cp_x2 = (x_mid + x_next) / 2;

        result.push([x_cur, y_cur, cp_x1, y_cur, x_mid, y_mid]);
        result.push([x_mid, y_mid, cp_x2, y_next, x_next, y_next]);
    }
    return result;
}

function buildPolylinePoints(curveSegments, xs, ys) {
    const inetrpolateTimes = 10;
    const len = curveSegments.length;
    for (let i = 0; i < curveSegments.length; i++) {
        const segment = curveSegments[i];
        xs.push(segment[0]);
        ys.push(segment[1]);
        this.interpolatePoints(segment, xs, ys, inetrpolateTimes);
        if (i == len - 1) {
            xs.push(segment[4]);
            ys.push(segment[5]);
        }
    }
}

function drawPolyline(xs, ys, hostElement) {
    const polylineElement = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
    const len = Math.min(xs.length, ys.length);
    let pointsStr = "";
    for (let i = 0; i < len; i++) {
        pointsStr += xs[i].toFixed(3) + ',' + ys[i].toFixed(3) + ' ';
    }
    polylineElement.setAttribute('points', pointsStr);
    polylineElement.setAttribute('fill', 'none');
    polylineElement.setAttribute('stroke', '#5572c3');
    hostElement.appendChild(polylineElement);
}

function interpolatePoints(curveSegment, xs, ys, times) {
    const x_start = curveSegment[0];
    const y_start = curveSegment[1];
    const x_end = curveSegment[4];
    const y_end = curveSegment[5];
    const d = Math.pow(x_start - x_end, 2) + Math.pow(y_start - y_end, 2);
    if (d > 1) {
        for (let i = 1; i < times; i++) {
            const t = i / times;
            const x = this.deCasteljau(t, curveSegment[0], curveSegment[2], curveSegment[4]);
            const y = this.deCasteljau(t, curveSegment[1], curveSegment[3], curveSegment[5]);
            xs.push(x);
            ys.push(y);
        }
    }
}

function deCasteljau(t, p0, p1, p2) {
    return (1 - t) * ((1 - t) * p0 + t * p1) + t * ((1 - t) * p1 + t * p2);
}

const svgElement = document.getElementById("mySvg");
drawPoints(xs, ys, svgElement);
drawQuadBezierCurve(xs, ys, svgElement);
<svg id="mySvg" width="1000" height="500"></svg>
isherwood
  • 58,414
  • 16
  • 114
  • 157
jena_kk
  • 1
  • 2
  • 1
    Looks like this algorithm is suited to smooth 1-dimensional functions, while you are looking for 2-dimensional solution. – blakkwater May 18 '23 at 05:24
  • Yes, this algorithm is to draw a quadratic bezier curve. Now I am looking for an algorithm that can draw a smooth bezier cureve, no matter quadratic or cubic. – jena_kk May 18 '23 at 05:41
  • 1
    use cubics they can be joined smoothly on both sides arbitrarily (quadratics can do this only on one side) see [How can i produce multi point linear interpolation?](https://stackoverflow.com/a/30438865/2521214) and its sublinks. In case you want speed and pixel perfect accuracy check this out: [Is it possible to express "t" variable from Cubic Bezier Curve equation?](https://stackoverflow.com/a/60113617/2521214) – Spektre May 18 '23 at 07:17
  • Quadratic curves [cannot really be used](https://pomax.github.io/bezierinfo/#polybezier) for poly-bezier curves because moving a single point moves _every other control point_ in the entire chain, leading to this exact problem: the overall curve will look horrible unless you get unreasonably lucky. Use cubic beziers instead. – Mike 'Pomax' Kamermans May 18 '23 at 18:51

0 Answers0