Отрисовка по данным с сервера кривых линий (а не ломаных) на клиенте
Есть необходимость реализовать интерполяцию графиков. Планирую делать так, что расчёты происходят на стороне сервера, далее данные передаются клиенту, и происходит отрисовка графика. Но не понимаю, как такое реализовать? С помощью интерполяции я нахожу точки, но каким образом получится красивая интерполирующая кривая? Ведь точки соединятся и получится ломаная. Что я не так понимаю? Ниже привожу код интерполяции.
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 шт):
Толковая вещь не получилась, но для демонстрации кривых Безье и сравнение с ломаными линиями. Строка с координатами 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>