João Menighin
João Menighin

Reputation: 3225

Canvas HTML - Smoothness between lineTo and Bezier curve

Maybe this is more a math question but lets see:

I'm using HTML5 Canvas to draw a line chart. The chart is basically Position X Time. Each line represents a vehicle in a position (Y) in a given time (X). I only have information about when a vehicle passed through determined points in the road. So if a vehicle stops between two points I don't have the information that it actually stoped, but when it passes through the next point I will be able to draw a line that will be almost horizontal, because the average speed, i.e. the line slope, will be really small.

In these scenarios we have defined that if a vehicle moved below 10Km/h I should consider that it made a stop and should draw a horizontal smooth line.

Basically I have to transform this:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

ctx.lineWidth = 5;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';

ctx.beginPath();
ctx.moveTo(0, 30);
ctx.lineTo(20, 50);
ctx.lineTo(220, 70);
ctx.lineTo(240, 110);
ctx.stroke();
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

Into this:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

ctx.lineWidth = 5;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';

ctx.beginPath();
ctx.moveTo(0, 30);
ctx.lineTo(20, 50);

ctx.bezierCurveTo(
  50, 70,
  210, 50,
  220, 70
)

ctx.lineTo(240, 110);
ctx.stroke();
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

The problem is: how do I chose good values for the bezier points? In the above example I have done it experimentally. I cannot find a way to programatically pick good point values so my lines don't look bad like these:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

ctx.lineWidth = 5;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';

ctx.beginPath();
ctx.moveTo(0, 30);
ctx.lineTo(20, 50);

ctx.bezierCurveTo(
  20, 70,
  180, 50,
  220, 70
)

ctx.lineTo(240, 110);
ctx.stroke();


ctx.beginPath();
ctx.moveTo(0, 80);
ctx.lineTo(20, 100);

ctx.bezierCurveTo(
  20, 120,
  220, 100,
  220, 120
)

ctx.lineTo(280, 150);
ctx.stroke();
<canvas id="myCanvas" width="300" height="170" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

I'm looking for a computationally simple solution because this is redrawn everytime with a lot of lines, so I don't want this melting down my performance.

Thanks for any tips!

Upvotes: 2

Views: 1229

Answers (1)

Jo&#227;o Menighin
Jo&#227;o Menighin

Reputation: 3225

I found a good solution for my problem: Using line interpolation.

Having the following segments and imagine I want to smooth things between B and C, how can I choose some bezier points that would garantee a smooth curve instead of broken one?

The scenario before smoothing

First I interpolate the segment AB and find the point B', which is the point the line AB would touch the Y coordinate for C. Then I use the same process to find C':

Interpolating points

The points B' and C' make good points for the bezier in order to smooth things up to a horizontal line:

bezier curve using B' and C'

This is also being computationally simple enough since finding the a line equation is rather simple.

Upvotes: 3

Related Questions