I have been trying to interpolate raw angular data in degree (for a flow map grid) but any attempt fails because there is always two path, and I can't figure out how to interpolate on the shorter one only!
I have made a code sandbox to try different strategy in javascript:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext("2d");
ctx.translate(0, canvas.height);
ctx.scale(1, -1); //flip canvas
var tau = Math.PI * 2;
var deg = tau / 360;
var min = 20; //start angle
var max = 270; //end angle
var step = 10; //space between angle
//interpolated angles visual
color('green');
for (let index = min; index % 360 < max; index += step) {
let t = remap01(min, max, index);
// l = lerpwrap(mindeg, maxdeg, t);
// l = lerphannah(mindeg, maxdeg, t);
let l = lerpAngle(min * deg, max * deg, t);
createDirection(l);
}
//start angle visual
color('red');
createDirection(min * deg);
//end angle visual
color('blue');
createDirection(max * deg);
//---------------Various Lerp from google and asking
function lerpwrap(start, end, amount) {
let short = (((((end - start) % 360) + 540) % 360) - 180);
return start + (short * amount) % 360;
}
function lerphannah(b, a, t) {
if (Math.abs(a - 360 - b) < (Math.abs(a) - b)) {
a = a - 360;
};
return b = b + (a - b) * t;
}
function lerp(value1, value2, t) {
return value1 + (value2 - value1) * t;
}
// Remaps angles into [0, 360) range on degrees.
function normalizeAngle(angle) {
return angle - Math.floor(angle / 360) * 360;
}
// Returns the shortest signed angular delta
// from angle from to angle to, in degrees.
function angleDifference(from, to) {
// Wrap difference into [0, 360) range.
let difference = normalizeAngle(to - from);
// Remap to range (-180, 180]
// so that angles more than a half turn away
// go via the shorter route.
if (difference > 180) {
difference -= 360
};
return difference;
}
// Linearly interpolates between two angles,
// using interpolation weight blend in [0, 1].
// Return is normalized into [0, 360) range.
function lerpAngle(from, to, blend) {
let difference = angleDifference(from, to);
return normalizeAngle(from + blend * difference);
}
// --------------utils
function createDirection(angle) {
let angularline = createPoint(0, 1);
angularline = rotateAngle(angularline.x, angularline.y, angle);
angularline = scalePoint(angularline, 45);
let midscreen = createPoint(canvas.width / 2, canvas.height / 2);
let target = createPoint(midscreen.x + angularline.x, midscreen.y + angularline.y);
line(midscreen.x, midscreen.y, target.x, target.y);
}
function remap01(t1, t2, x) {
return (x - t1) / (t2 - t1);
}
function color(colour) {
ctx.fillStyle = colour;
ctx.strokeStyle = colour;
}
function createPoint(xp, yp) {
return {
x: xp,
y: yp
};
}
function rotateAngle(x, y, angle) {
let x1 = x * Math.cos(angle) - y * Math.sin(angle);
let y1 = x * Math.sin(angle) + y * Math.cos(angle);
let result = {
x: x1,
y: y1
};
return result;
}
function scalePoint(point, scale) {
return {
x: point.x * scale,
y: point.y * scale
};
}
function line(startX, startY, endX, endY) {
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
ctx.closePath();
ctx.stroke();
}
* {
box-sizing: border-box;
}
body {
background-color: #333;
color: #fff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: Arial, Helvetica, sans-serif;
min-height: 100vh;
margin: 0;
}
#canvas {
background: #f0f0f0;
/* border-radius: 5px; */
}
#source {
display: none;
}
<canvas id="canvas" width="400" height="100"></canvas>
resulting image:
0° is up
I have been trying many implementations but none work so far
How do I interpolate angular data such as it goes by the shorter distance in the circle?
There is also some bug in the for loop, it interpolate correctly ccw (ie counting 10 by 10 angles), but when it shift to cw it has too many sample. Also max = 360 crash the bowser.
Now I fixed the code part that prevented the javascript from executing (Math.pi -> Math.PI).
I'm not sure how to compress the drawing part which is why it's a bit too long
– user29244 Apr 08 '22 at 21:51