Scattering numbers in javascript array

I have an array of 10+ numbers. They represent the coordinates in a circle - in degrees, i.e. Each number is between 0 and 359.999999...

The problem I'm trying to solve is that when I draw my objects on a circle (via the html5 canvas api), sometimes they are grouped together, and this leads to the elements being drawn on top of each other.

So, I would like to create an algorithm that evenly distributes the elements around their initial cluster position. Let them say (and I would like this to be a custom option) the minimum distance between two elements is 5 degrees.

So, if the initial array is [5, 41, 97, 101, 103, 158, 201, 214, 216, 217, 320] , then I would like the algorithm to appear with something like [5, 41, 95 , 100 , 105 , 158, 201, 211, 216, 221 , 320] (in this case, items in bold are distributed around their original “center of gravity”, regardless of whether these two or more items are).

It would also be necessary for the algorithm to recognize 0 and 359 only at a distance of 1 unit (degree), and also to evenly distribute such elements around.

Has anyone ever created such an algorithm or had a good idea how this could be achieved? Even some general thoughts are welcome. I am sure that I can achieve this with a lot of trial and error, but I would like to hear some educated guesses, if you want, first.

0
source share
3 answers
 var val = [5, 41, 96, 101, 103, 158, 201, 214, 216, 217, 320, 1201, 1213, 1214, 1216, 1217, 1320], delta = Array.apply(null, { length: val.length }).map(function () { return 0 }), result, threshold = 5, converged = false; document.write('val: ' + val + '<br>'); while (!converged) { converged = true; delta = delta.map(function (d, i) { if (i < delta.length - 1 && delta.length > 1) { if (val[i + 1] + delta[i + 1] - val[i] - d < threshold) { converged = false; delta[i + 1] += 1; return d - 1; } } return d; }); document.write('delta: ' + delta + '<br>'); } result = val.map(function (v, i) { return v + delta[i]; }); document.write('result: ' + result + '<br>'); // try to minimise difference converged = false; while (!converged) { converged = true; delta = delta.map(function (d, i) { if (i < delta.length - 2) { var space = val[i + 1] + delta[i + 1] - val[i] - d; if (d < 0 && space > threshold) { converged = false; return d + space - threshold; } } return d; }); document.write('delta min: ' + delta + '<br>'); } result = val.map(function (v, i) { return v + delta[i]; }); document.write('result: ' + result + '<br>'); 

the code pushes two pairs too close to each other from one on each side. this symbolically and sometimes leads to distant pushed meanings that can be corrected.

[not implemented!] if the space of your values ​​is not enough, [0..360 [or more than 72 elements with a difference of 5 while loop may not come to an end.

edit: the minimization block must pass until all values ​​are encoded.

+2
source

To get this “uniformly random” distribution, let's say you have N numbers - divide the circle into N segments, then randomly place each number in its segment.

Thus, you don’t even have to make sure that 0 and 359 are only 1 unit.

Here is an idea:

 var numbers = 5; var segment = 360/numbers; var result = []; for(var i = 0; i < numbers; i++) { result.push(Math.round((Math.random() + i) * segment)); } alert(result.join(',')); 

Here's a more graphical idea (imagine how to fold a rectangle into a cylinder):

 var numbers = 5; var segment = 360 / numbers; var minimum = 15; var div = document.getElementById('r'); function build() { var result = []; div.innerHTML = ''; for (var i = 0; i < numbers; i++) { var current = 0; while(current < minimum + (result[result.length - 1] || 0)) { current = Math.round((Math.random() + i) * segment); } result.push(current); var d = document.createElement('div'); d.className = 'segment'; d.style.left = (result[result.length - 2] || 0) + 'px'; d.style.width = (current - (result[result.length - 2] || 0) - 1) + 'px'; d.textContent = current; div.appendChild(d); } console.log(result.join(',')); } build(); 
 .segment { height: 100%; position: absolute; font-size: 12px; text-align: right; border-right: 1px solid black; } #r { height: 100%; width: 360px; background: #eee; } html, body { height: 100%; margin: 0; padding: 0; } button { position: absolute; right: 4px; margin: 0; } 
 <button onclick="build()">Rebuild</button> <div id="r"></div> 
0
source

Algorithms in which fairly printed plots use a spring system to move vertices apart when they overlap. Here you are dealing with only one dimension and you can leave iteratively setting adjacent angles until all nodes are located at a distance of at least 5 degrees.

You can deal with cyclic values ​​by creating an auxiliary working array, so that the elements are reordered after the largest break. This allows you to process the array as linear values ​​without the need for wrapping:

 [2, 7, 320, 359] -> [-40, -1, 2, 7] 

The following code does this. Nodes move quite roughly, though: the code only looks at pairs of nodes that are too close. The code can probably be improved by localizing clusters of two or more nodes that are too close to each other:

 function adjust(arr, dist) { var offset = 0; var max = 360.0 + arr[0] - arr[arr.length - 1]; var min = max; var mix = 0; // index of first elment after largest gap // exit early if array can't be adjusted if (dist * arr.length > 240.0) return arr; // find largest gap for (var i = 1; i < arr.length; i++) { var d = arr[i] - arr[i - 1]; if (d > max) { max = d; mix = i; } if (d < min) min = d; } var x = []; // working array var adj = []; // final, adjusted array // create working array on greatest gap for (var i = 0; i < arr.length; i++) { if (i + mix < arr.length) { x.push(arr[i + mix] - 360); } else { x.push(arr[i + mix - arr.length]); } } // iteratively adjust angles while (min < dist) { min = dist; for (var i = 1; i < x.length; i++) { var d = x[i] - x[i - 1]; if (d < dist) { if (d < min) min = d; x[i - 1] -= (dist - d) / 2; x[i] += (dist - d) / 2; } } } // create final array for (var i = 0; i < x.length; i++) { if (i - mix < 0) { adj.push(x[i - mix + x.length]); } else { adj.push(x[i - mix] + 360); } } return adj; } 
0
source

Source: https://habr.com/ru/post/1247440/


All Articles