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?
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>