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(min*deg, max*deg, t);
// l = lerphannah(min*deg, max*deg, 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?