2

Метки для координатных осей

При рисовании графиков нередко встает такая задача: мы хотим, что бы на координатных осях графика у нас в нужном месте отображались метки, причем метки должны иметь "круглые" значения. Например, по оси X у нас минимальное значение 0.2259, а максимальное 0.2372, тогда метки должны стоять в точках 0.2260, 0.2280, 0.2300, 0.2320, 0.2340 и 0.2360 (шаг 0.002). Я уже давно использую одну функцию, решающую эту задачу. На вход она получает интервал (x1, x2) и приблизительное число меток, а возвращает массив, содержащий значения меток. Реализация следующая:

#include <stdio.h>
#include <vector>
using namespace std;

typedef vector<double> TDoubleArray;
//Вычисление значений, на которых должны проставляться метки на координатных осях.
static TDoubleArray & CalcGridLabels(double xmin, //минимальное значение.
double xmax, //максимальное значение.
int N) //количество меток в указанном интервале +/-2
{
#define MANT_NUM 5
double mant[MANT_NUM] = {0.1, 0.2, 0.25, 0.5, 1.0},
dx = xmax-xmin;
int i;
//Подбираем значение мантиссы, близким к величине dx:
while(dx < mant[0])
for(i=0; i < MANT_NUM; i++)
mant[i] /= 10.0;
while(dx > mant[MANT_NUM-1])
for(i=0; i < MANT_NUM; i++)
mant[i] *= 10.0;
//Если количество меток > 10:
while(1)
{//Первый проход:
for(i=MANT_NUM-1; i >=0 ; i--)
if(dx/mant[i] >= N )
break;
if(i >= 0)
break;//все хорошо, N меток будет.
//Умножаем метки на 2:
for(i=0; i < MANT_NUM; i++)
mant[i] /= 2;
//Второй проход:
for(i=MANT_NUM-1; i >=0 ; i--)
if(dx/mant[i] >= N )
break;
if(i >= 0)
break;//Все хорошо, N меток будет.
//Умножаем метки на 5:
for(i=0; i < MANT_NUM; i++)
mant[i] /= 5;
}
//2)Ищем наименьшую разницу между требуемым количеством меток и
//тем количеством меток, которое можно получить:
int min_diff = (int)(dx/mant[0]), md_ind = 0;
for(i = 0; i < MANT_NUM; i++)
{ int diff = abs((int)(dx*1.00000001/mant[i])-N);
if(diff < min_diff)
{ min_diff = diff;
md_ind = i;
}
}
//Рассчитываем значения меток:
double dm = mant[md_ind];
double start = dm*(int)(xmin*1.00000001/dm);
if(start < xmin)
start += dm;
static TDoubleArray values;
values.resize(1);
values[0] = start;
values.reserve(N+1);
while(start+dm < xmax)
{ start += dm;
values.push_back(start);
}
return values;
#undef MANT_NUM
}

2 коммент.:

Unknown комментирует...

У меня небольшой вопрос. В приведенном примере почему шаг был выбран 0.02, а не 0.01, например.

Если можно, напишите полное условие задачи. Заинтересовала она меня )

Lotrex комментирует...

Я сначала тоже хотел условия задачи полностью расписать, но это оказалось сложновато, и мне стало лень :) Но если интересно - то попытаюсь :)
Полное условие задачи можно сформулировать так: нужно проставить N меток на координатной оси в заданном интервале (x1, x2), x1 < x2. Мантисса всех меток должна быть кратна какому-то одному значению из данного набора величин (в приведенном коде это набор из 4-х значений: {0.1, 0.2, 0.25, 0.5} ). Если в интервале (x1, x2) невозможно проставить именно N меток, используя предложенный набор, то из набора берется то значение, с которым количество проставляемых меток получается максимально близким к N. Уфф, кажется, все.

Отправить комментарий