-1
\$\begingroup\$

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:

enter image description here

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?

\$\endgroup\$
8
  • \$\begingroup\$ Remember to search for past Q&A — you're certainly not the first game developer in history to ask about interpolating an angle. 😉 You can find out existing answers here and in similar past questions. \$\endgroup\$
    – DMGregory
    Commented Apr 4, 2022 at 23:49
  • \$\begingroup\$ I obviously didn't use the right keyword, angular interpolation is web stuff, modulo is anything but that, and whatever I found (in stack answer no less) was in the code snippet and doing nothing. Googling isn't what it used to be :( thanks, i'll try that, that was surprisingly the hardest part of a pcg stateless agent circulation system, now I can have path that aren't 90° only! \$\endgroup\$
    – user29244
    Commented Apr 5, 2022 at 0:21
  • \$\begingroup\$ @DMGregory Plot twist, it's not working, I have the exact same result as above, instead of the shortest path, it takes the long path, I spend time verifying the code those last 2 days, so I don't see where the error is! \$\endgroup\$
    – user29244
    Commented Apr 7, 2022 at 21:26
  • \$\begingroup\$ Edit your question to show the code you're using. Aim to give us a Minimal Complete Verifiable Example: everything a user would need to reproduce the problem in a new project. \$\endgroup\$
    – DMGregory
    Commented Apr 7, 2022 at 21:56
  • \$\begingroup\$ I have edited as much as possible, though I can't figure out why the edit don't work directly within the question, but the code is teh one I use, and I put all the helper function for drawing \$\endgroup\$
    – user29244
    Commented Apr 8, 2022 at 0:51

2 Answers 2

0
\$\begingroup\$

Think of two angles as two unit vectors(a,b), Calculate their Cross product. For example:

let min = 0;
let max = 45;
let a = (1,0);     // 0 degree Calculated by trigonometry
let b = (0.7,0.7); // 45 degree Calculated by trigonometry
let cross = a[0]*b[1] – b[0]*a[1]; //two-dimensional vector cross product formula
// when cross>0 ,a in the counterclockwise direction of b
if (cross > 0){
    //make sure degree b is greater
    min,max = max,min;
}
if (min> max){
    max += 360;
}
let step = 10;
for (let index = min; index % 360 < max; index += step) {
    let t = remap01(min, max, index);
    let l = lerpAngle(min * deg, max * deg, t);
    createDirection(l);
}

The principle: The magnitude of the cross product can be interpreted as the positive area of the parallelogram having a and b as sides :

enter image description here

If the result (area) is positive, This angle < 180° ,otherwise it's > 180°.

\$\endgroup\$
1
  • \$\begingroup\$ That's clever! I was trying to avoid vector. \$\endgroup\$
    – user29244
    Commented Apr 9, 2022 at 1:50
0
\$\begingroup\$

I had to shift this

l = lerpAngle(mindeg, maxdeg, t)

createDirection(l)

into

l = lerpAngle(min, max, t)

createDirection(l*deg)

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .