La question traitée ici concerne le mapping de données dans un espace cyclique. Le traitement d'angle est le premier qui me vient en tête.

javascript (p5.js)

manipulation:

  • right-click & drag to modify mapping angle
  • left-click & drag to modify mapping range

code:

functions (js)

First important method is cyclic_bound. Its role is to remap any value in the given range [from_in,from_out]. It's a modulo in both directions.

function cyclic_bound( value, from_in, from_out ) {
  if ( value >= from_in && value <= from_out ) { 
    return value;
  }
  var d = from_out - from_in;
  while( value < from_in ) {
    value += d;
  }
  while( value > from_out ) {
    value -= d;
  }
  return value;
}

cyclic_bound is applied on all entries of the mapping function.

Mapping the value, steps:

  • calculation of the middle of the world range (from_in,from_out), divided by 2, half
  • bounding of value to map in the world range
  • bounding of lower limit of the new range (to_in > bin) in the world range
  • bounding of lower limit of the new range (to_out > bout) in the world range
  • calculation and bounding of middle of the new range (bmid) in the world range

Once done, we have everything we need to render the value mapped form the world range to the new range. Note that the returned value will also be bounded to world range.

The process to do so:

  • Calculation of the distance between the bounded value and the middle of the new range. This returns a value between -half and half. Because we will have to map this distance to the new range, we have to divide it by half. This returns a percentage. Its value is in the range [-2, 2]. Indeed, the value to map might be at the opposite of the the middle of the new range. Because world space is cyclic, if the absolute value of the percentage is greater than 1, 1.5 for instance, the value can be bounded in the range [-1, 1]. The 1.5 becomes -0.5, and it's still the same value.
  • Calculation of the new range. The distance between one of the limits ( to_in or to_out ) and their center (bmid) has to be removed form the half of the world range. Once done, the new range might exceed the half of the world range, when to_out is smaller than the lower limit (to_in) of the world, for instance. Therefore, we have to bound the new range between -half and half. From now on, the percentage and the new range can be multiply by one another .
  • Last step is to check the sign of the percentage. If it is positive, the limit to use is the highest one, bout. We add the percentage of the new range and returns the value. If it is negative, the process is similar but uses the lower limit bin.
function cyclic_map( value, from_in, from_out, to_in, to_out ) {
   
   var half = ( from_out - from_in ) * 0.5;
   var bvalue = cyclic_bound( value, from_in, from_out );
   
   var bin = cyclic_bound( to_in, from_in, from_out );
   var bout = cyclic_bound( to_out, from_in, from_out );
   var bmid = cyclic_bound( to_in + ( to_out - to_in ) * 0.5, from_in, from_out );
   
   var pc = cyclic_bound( ( bvalue - bmid ) / half, -1, 1 );
   var brange = cyclic_bound( half - ( bmid - bin ), -half, half );
   if ( pc >= 0 ) {
     return cyclic_bound( bout + (brange * pc), from_in, from_out );
   }
   return cyclic_bound( bin + (brange * pc), from_in, from_out );
   
}

identification of the nearest limit

The tests below test all cases where lower limit bin is the closest form given value.

// possible configurations of the In, Mid and Out:
// I M O > "normal"
// M O I > "rejected in"
// O I M > "rejected out"
  
// booleans             normal therm       speciaL term     restriction
boolean cnormal =       (bin <= bmid) &&   (bmid < bout);
boolean crejected_in =  (bmid <= bout) &&  (bin > bmid);
boolean crejected_out = (bin <= bmid) &&   (bout < bmid) && !cnormal;

if ( cnormal && (
   ( bvalue <= bmid && bvalue >= bmid - half ) ||
   bvalue >= bmid + half
 ) ) {
 return bin;
} else if ( crejected_in && (
   bvalue <= bmid ||
   bvalue > bmid + half
 ) ) {
 return bin;
} else if ( crejected_out && (
   bvalue < bmid &&
   bvalue >= bmid - half
 ) ) {
 return bin;
}
return bout;

research

base

 |--------*-----------------*-----------*--------------*--------|
 0        a                 b           c              d       2PI
 

l´idée est d´écarter 2 valeurs consécutives, b et c par ex, et de rediffuser les 2 autres, a et d, de manière proportionnelle

pour ce faire > calcul du point médian x

 |--------*-----------------*-----!-----*--------------*--------|
 0        a                 b     x     c              d       2PI

distance bx = (c-b) x = b + bx/2

le remap spécifie les nouvelles valeurs de b et de c

 |--------*--------<--------*-----!-----*-------->------*--------|
 0        a        b´       b     x     c        c´     d       2PI

distance b´x = (c´-b´) x = b + bx/2

on a donc perdu (b´x - bx) radians

  |----*---------|----*---------|----*---------|
-2PI   a´        0    a        2PI   a´´      4PI

ce qu´il ne faut pas oublier: l´espace est cyclique > a´ ~ a ~ a´´

on peut descendre en négatif et passer 2PI sans que ça pose de réel problème

 |---!--------*-----------*-----*----------*-----|---!--------*
 0   x´       a           b     c          d    2PI  x        a´
     3        11          22    27         37   42   45       53

l´angle entre d et a est à plusieurs positions simultanément pour son calcul: ( d + a ) / 2 - ( 37 + 11 ) -> 48!!!!, divisé par 2, on arrive à 24, donc pas du tout où il faut! il faut ajouter 42 à a => 53, et là: (37+57)/2, 90/2 => 45 paf! si a < d,

   a += 2PI

center = (d+a)/2

remap method()

dans le remap, je demande un recalcul de b dans les bornes d > a vers d-5, a + 5

   a, in, out, in_to, out_to
   b, 11, 37,  16,    32
 |-----------*--->--*---*---*----------*---<--*-----|
 0           |      |   |   |          |      |    2PI
             a      |   c   b          |      d    
             11     |   19  22         |      37
 0                  |´  |   |          |
                    |   \   |          |
                   a´    c´ b´         d´
                  16  20.9  22.8       32

? b´

distance_b distance_init distance_new percent_b out_b si b < d:

   distance_b = b - a => 11
   distance_init = d - a => 26
   distance_new = d´ - a´ => 16
   percent_b = distance_b / distance_init = > 0.423076923
   out_b = a´ + percent_b * distance_new (6.769230769) => 22.769230769

return out_b

pour c´ si c < d:

   distance_c = b - a => 8
   distance_init = d - a => 26
   distance_new = d´ - a´ => 16
   percent_b = distance_c / distance_init = > 0.307692308
   out_b = a´ + percent_b * distance_new (4.923076923) => 20.923076923

return out_b

le process semble ok pour les angles compris entre a et d

Resources

online identity ∋ [ social ∋ [mastodon♥, twitter®, facebook®, diaspora, linkedin®] ∥ repos ∋ [github®, gitlab♥, bitbucket®, sourceforge] ∥ media ∋ [itch.io®, vimeo®, peertube♥, twitch.tv®, tumblr®] ∥ communities ∋ [godotengine♥, openprocessing, stackoverflow, threejs]]