-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\$ 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\$ 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\$ 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\$ 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\$ 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\$ 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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.