Отрисовка по данным с сервера кривых линий (а не ломаных) на клиенте

Есть необходимость реализовать интерполяцию графиков. Планирую делать так, что расчёты происходят на стороне сервера, далее данные передаются клиенту, и происходит отрисовка графика. Но не понимаю, как такое реализовать? С помощью интерполяции я нахожу точки, но каким образом получится красивая интерполирующая кривая? Ведь точки соединятся и получится ломаная. Что я не так понимаю? Ниже привожу код интерполяции.

class Main {
    // Data point
    static class Data {
        int x, y;

        public Data(int x, int y) {
            super();
            this.x = x;
            this.y = y;
        }
    };
    
    static double interpolate(Data f[], int xi, int n) {
        double result = 0;

        for (int i = 0; i < n; i++) {
            double term = f[i].y;
            for (int j = 0; j < n; j++) {
                if (j != i) {
                    term = term * (xi - f[j].x) / (f[i].x - f[j].x);
                }
            }
            
            result += term;
        }

        return result;
    }
    
    public static void main(String[] args) {
        Data f[] = {new Data(0, 2), new Data(1, 3), new Data(2, 12), new Data(5, 147)};
        
        System.out.print("Value of f(3) is : " + (int)interpolate(f, 3, 4));
    }
}

Ответы (1 шт):

Автор решения: Leonid

Толковая вещь не получилась, но для демонстрации кривых Безье и сравнение с ломаными линиями. Строка с координатами contenteditable - можно попробовать поменять координаты точек.

let canvas = document.getElementById('canvas');
let lineshape = document.getElementById('lineshape');
let output = document.getElementById('output');
let ctx = canvas.getContext('2d');

let w = canvas.width = 600;
let h = canvas.height = 120;

ctx.strokeStyle = 'red';
ctx.lineWidth = 2;

let pointsY = [20, 80, 110, 90, 30, 70, 40, 90, 40, 80, 100, 60];
let step = 50;


lineshape.addEventListener('change', e => draw(e.target.value, false));
output.addEventListener('input', () => draw(null, true));

function draw(letter, mode){
  let string = '';
  if(!mode){
    string = 'M 0 0 ' + letter + ' ' + pointsY.map((p,i) => `${step*(i+1)} ${p} `).join('');
    output.innerHTML = string;
  } else {
    string = output.innerHTML;
  }

  ctx.clearRect(0,0,w,h);
  ctx.stroke(new Path2D(string));
}

draw('L', false);
<canvas id="canvas"></canvas>
<select id="lineshape">
<option id="lines" value="L">
LINES
</option>
<option id="quadro" value="Q">
QUADRO
</option>
<option id="cubic" value="C">
CUBIC
</option>
</select>
<p id="output" contenteditable></p>

Вот, концы кубических Безье свел к точкам графика. А управляющие точки оставил на том же горизонте, но в половину расстояния между точками. Не знаю насколько это будет читаться из кода...

Добавил серыми линиями пути от управляющих точек кривых до точек графика. То есть соседние управляющие точки обеих кривых и их конечная (начальная) точка должны быть на одной прямой, касательной к образующейся дуге. Не обязательно из разводить на половину шага, любое расстояние уже придаст сглаженность.

let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

let w = canvas.width = 600;
let h = canvas.height = 200;

let points = [20, 80, 10];
let step = 100;

ctx.fillStyle = 'red';
ctx.strokeStyle = 'blue';

points.forEach((p,i) => {
  ctx.beginPath();
  ctx.arc(step*[i+1], p, 4, 0, 2 * Math.PI);
  ctx.fill();
  console.log(p);
})

ctx.stroke(new Path2D(`M 0 50 C ${step*0.5} 50           ${step*0.5} ${points[0]} ${step}   ${points[0]}
                                ${step*1.5} ${points[0]} ${step*1.5} ${points[1]} ${step*2} ${points[1]}
                                ${step*2.5} ${points[1]} ${step*2.5} ${points[2]} ${step*3} ${points[2]}`));

ctx.strokeStyle = 'grey';
                                
ctx.stroke(new Path2D(`M ${step*0.5} ${points[0]} L ${step}   ${points[0]}`));
ctx.stroke(new Path2D(`M ${step}   ${points[0]}   L ${step*1.5} ${points[0]}`));
ctx.stroke(new Path2D(`M ${step*1.5} ${points[1]}  L ${step*2} ${points[1]}`));    
ctx.stroke(new Path2D(`M ${step*2} ${points[1]}  L ${step*2.5} ${points[1]}`));
<canvas id="canvas"></canvas>

→ Ссылка