How can you set the potential in a two-dimensional array?

For example, I set the points (X, Y) at which I need to set the potential, for example, I want to set a potential in the matrix of size 10 to coordinates (2, 2) and this is what I expect to get:

{ 2, 4, 6, 4, 2, 4, 6, 8, 6, 4, 6, 8, 10, 8, 6, 4, 6, 8, 6, 4, 2, 4, 6, 4, 2 } 

That is, it is necessary that from this point the gradient values ​​diverge from maximum to minimum.

How can this be done beautifully ? I have only ridiculous designs that look awful. Can you see an example of how this is done?

For clarity, here's another picture:

enter image description here

2 points are set here, with values ​​of 10 and -10.


More about the potentials:

  1. Using potential fields in real-time strategy scenarios (Habr)
  2. Pathfinding: potential field method
  • Then the questions immediately arise: why should the bottom left corner be 1? What should the result look like if it is installed not 10, but 11? And if 20? And if 10 and -10 will be installed in the neighboring cells? - Yaant
  • @Yaant with 1 in the lower left corner was wrong) it is necessary to set the step for the "gradient", the step = 2 is shown here. - Depish
  • Is the minimum positioned as a mirror and is it equal to the specified maximum value with the opposite sign? - avp
  • @apv Well, in the picture they, though mirror, but in general they do not depend on each other, they are just 2 different points with different values. - Depish 7:58 pm
  • Then clarify the question. 2 points are set (min, max) and it is necessary to choose a step in accordance with them and fill in (according to what rules?) The whole matrix? - avp

3 answers 3

When I started writing the answer, I noticed that the author's array “what I expect to receive” does not coincide with the picture. Specificity in this matter is not enough. Therefore, I will give two different versions of such fields.

1. General code

The code common to both versions:

 #include <stdio.h> #include <math.h> #define MAP_WIDTH 30 #define MAP_HEIGHT 30 void print_map(int* map, int w, int h) { for (int y = 0; y < MAP_HEIGHT; ++y) { for (int x = 0; x < MAP_WIDTH; ++x) { // Вместо нулей печатаем пробелы, чтобы не сломать глаза :) (map[x + y*MAP_WIDTH] == 0) ? printf(" ") : printf("%2i ", map[x + y*MAP_WIDTH]); } putchar('\n'); } } <...> int main() { int map[MAP_HEIGHT][MAP_WIDTH] = {{0}}; 

2. Reducing the weight of the point depending on the distance to the center

The weight of each point should decrease with distance from the center. But how? To be honest, I still could not find the rule for the weights to decrease as in the picture. Therefore, I will provide the code for the linear reduction of the scales .

 // map - матрица целых чисел, w, h -- его высота и ширина соответственно. // x и y -- координаты центра поля. // start_value -- очевидно, начальное значение. // attenuation_constant -- коэффициент затухания. void set_potential(int* map, int w, int h, int x, int y, int start_value, int attenuation_constant) { // Вычисляем максимальное удаление от центра по вертикали/горизонтали. int verical_and_horizontal_steps_nm = start_value / attenuation_constant; int box[2][2] = { {x - verical_and_horizontal_steps_nm + 2, y - verical_and_horizontal_steps_nm + 2}, {x + verical_and_horizontal_steps_nm - 1, y + verical_and_horizontal_steps_nm - 1} }; for (int dot_y = box[0][1]; dot_y < box[1][1]; ++dot_y) { for (int dot_x = box[0][0]; dot_x < box[1][0]; ++dot_x) { // Считаем расстояние до точки по формуле d = sqrt((x0-x1)^2 + (y0-y1)^2). int distance = sqrt((x-dot_x)*(x-dot_x) + (y-dot_y)*(y-dot_y)); // Значение текущей точки меняется линейно. int dot_value = start_value - distance * attenuation_constant; // Если точка не лежит на вертикали или горизонтали от центральной мы считаем // значение по-другому, т. к. диагональ квадрата всегда больше его стороны. // Т. к. длина стороны у нас -- 1, то длина диагонали будет sqrt(2). if (dot_y != y && dot_x != x) dot_value = start_value - round(sqrt(2) * distance * attenuation_constant); // Чтобы не было отрицательных коэффициентов по углам добавляем условие. map[dot_x + dot_y*MAP_WIDTH] = dot_value > 0 ? dot_value : 0; } } } 

Here is the output for set_potential((int*)map, MAP_WIDTH, MAP_HEIGHT, 15, 15, 10, 2) :

  2 2 4 2 2 2 4 4 6 4 4 2 2 4 7 8 7 4 2 4 6 8 10 8 6 4 2 4 7 8 7 4 2 2 4 4 6 4 4 2 2 2 4 2 2 

Not very similar to the picture, but the weights depend directly on the distance to the center.

3. I do not even know what to call an option. See for yourself ...

If we take a better look at the lower left (and in general, any) quarter of the picture or the array of the “expectations” of the author, we note that

  1. All numbers on the same diagonal are equal;
  2. Each number on the next diagonal (with a divergence from the center) is exactly one step (I called it the attenuation coefficient ) more than any number on the previous diagonal;
  3. The length of each diagonal is greater than the previous one by one.

Difficult to explain in words, see for yourself (attenuation factor = 2):

Left bottom quarter

 1. 10 (- 2 = 8) 2. 8 8 (- 2 = 6) 3. 6 6 6 (- 2 = 4) 4. 4 4 4 4 (- 2 = 2) 5. 2 2 2 2 2 

Four such quarters (correctly turned) will give the desired picture (which, by the way, is a diamond, which is what the author needs (see the comments to the question)).

The drawing algorithm is very simple, I think everything will be clear from the code:

 void set_potential2(int* map, int w, int h, int x, int y, int start_value, int attenuation_constant) { // Заполняем центр. map[x + y*MAP_WIDTH] = start_value; // Вычисляем максимальное удаление от центра по вертикали/горизонтали. int verical_and_horizontal_steps_nm = start_value / attenuation_constant; // Здесь i -- расстояние от центра до текущей точки. for (int i = 1; i < verical_and_horizontal_steps_nm; ++i) { int dot_value = start_value - attenuation_constant * i; // Заполняем левый нижний угол: // // dot_y = y+i -- переходим в точку максимального отдаления по вертикали от центра. // dot_x = x -- координата x точки равна координате центра. // do_y >= y; --dot_y -- пока находимся выше центра, поднимаемся вверх. // --dot_x -- смещаемся влево. // // Т. е. мы как-бы по ступенькам поднимаемся вверх-влево, вверх-влево, вверх-влево... // Для остальных углов меняем только направления в заголовке цикла. for (int dot_y = y+i, dot_x = x; dot_y >= y; --dot_y, --dot_x) // Тоже самое, что map[dot_x][dot_y] map[dot_x + dot_y*MAP_WIDTH] = dot_value; // Правый нижний угол for (int dot_y = y+i, dot_x = x; dot_y >= y; --dot_y, ++dot_x) map[dot_x + dot_y*MAP_WIDTH] = dot_value; // Левый верхний угол for (int dot_y = yi, dot_x = x; dot_y <= y; ++dot_y, --dot_x) map[dot_x + dot_y*MAP_WIDTH] = dot_value; // Правый верхний угол for (int dot_y = yi, dot_x = x; dot_y <= y; ++dot_y, ++dot_x) map[dot_x + dot_y*MAP_WIDTH] = dot_value; } } 

This is what the algorithm produces when set_potential2((int*)map, MAP_WIDTH, MAP_HEIGHT, 9, 9, 10, 2) :

  2 2 4 2 2 4 6 4 2 2 4 6 8 6 4 2 2 4 6 8 10 8 6 4 2 2 4 6 8 6 4 2 2 4 6 4 2 2 4 2 2 

The author led a cropped array:

 { 2, 4, 6, 4, 2, 4, 6, 8, 6, 4, 6, 8, 10, 8, 6, 4, 6, 8, 6, 4, 2, 4, 6, 4, 2 } 

We cut our result:

 2 4 6 4 2 4 6 8 6 4 6 8 10 8 6 4 6 8 6 4 2 4 6 4 2 

The similarity is obvious. I think this is exactly what the author needed. In my opinion, beauty :)

PS The code is not very, of course, but it will go to illustrate the idea.

PSS To be honest, I don’t know how to correctly translate the “damping factor”: “attenuation constant” or “damping constant”. If anyone correct, will appreciate.

    Well, set some function of the potential drop and calculate the potential in specific cells, based on their distance to the center, and rounding the values ​​to integers, if you need them.

      Here is another way (it is different than described above):

       #ifndef POTENTIALFIELD_H #define POTENTIALFIELD_H #include <iostream> template <size_t x, size_t y> class PotentialField { public: PotentialField() { size_x = x; size_y = y; fillAll(0.f); } /* x, y - координаты в матрице value - максимальное значение данной точки force - применяемая сила к данной точке (force * value) fixedRadius - рисовать только по радиусу а не по всей карте radius - радиус (работает только когда fixedRadius == true) */ void setPoint(size_t _x, size_t _y, float value, float force = 0.08f, bool fixedRadius = false, float radius = 0.f) { if (_x > size_x - 1 || _y > size_y - 1) // TODO: возможно стоит выкинуть исключение return; size_t distance = 0; for (size_t i = 0; i < size_y; i++) { for (size_t j = 0; j < size_x; j++) { if (fixedRadius && (j < _x - radius || j > _x + radius || i < _y - radius || i > _y + radius) ) continue; distance = getDistance(_x, _y, j, i); if (distance == 0) { matrix[i][j] = value * force; } else { matrix[i][j] += force * value / distance; } } } } float getDistance(float x1, float y1, float x2, float y2) { return std::sqrt( ((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)) ); } // Матрица затирается заданным значением void fillAll(float val = 0.f) { float *ptr = &matrix[0][0]; float *end = &matrix[size_y - 1][size_x]; while (ptr < end) { *ptr = val; ++ptr; } } void print() { for (size_t i = 0; i < size_y; i++) { for (size_t j = 0; j < size_x; j++) { std::cout.width(5); std::cout.precision(2); std::cout << matrix[i][j] << ' '; } std::cout << std::endl; } } private: size_t size_x; size_t size_y; float matrix[y][x]; }; #endif 

      Example of use:

       int main(){ PotentialField<20, 20> field(); field.setPoint(0, 0, 10); } 

      A field of size 20 to 20 is created, and the potential x = 0, y = 0 is established, which is distributed according to the formula force * value / distance

      Suddenly someone come in handy =)