Пример № 1.1. Визуализация входящих данных. (Еще больше текста)

Nikelbak
Nikelbak аватар
Offline
Зарегистрирован: 22.03.2011

 

«Вата»

 

Урок рассчитан на пользователей, ознакомленных с языком программирования Processing, если его можно так назвать. Для тех же, кто еще не в курсе, что это такое, милости просим на http://processing.org/.  Будем так же надеяться что на http://arduino.ru появится раздел с переводом как для Arduino IDE.

 

 

«Нежданное путешествие»

Надеюсь, вы прочли предыдущий пример. И вот мы с вами уже бородатые гномы. Но, как всегда, чего то не хватает, и мы стремимся идти дальше. Конечно, все мы понимаем, что просто построить диаграмму, как минимум мало, т.к., в конце концов, она дойдет до края окна и больше мы ни чего не увидим. В простейшем случае, нужно стирать полученные данные и выводить новые. Как же это сделать наиболее простым способом?

 «Чаще всего выход там, где был вход»

Итак мы имеем программку:

int xn=0; // переменная координаты X начала
int yn=0; // переменная координаты Y начала  
int xk=0; // переменная координаты X конца
int yk=0; // переменная координаты Y конца  
void setup(){
size (500, 200); // устанавливаем размер окна
background(0); // цвет заливки черный
}
void draw(){
yk=mouseY;  // пишем в переменную ук значение положения курсора мыши по оси Y
textSize(32); // размер текста
fill(0); // заливка для объекта прямоугольник
noStroke();//рисовать прямоугольник без абриса
rect (5,5,200,40); //рисует прямоугольник перекрывающий наш текст
fill(255); // заливка для текста
text("Y= "+yk, 10, 30); // пишем Y= и подставляем полученное значение
stroke(255); // цвет будущей линии белый
line (xn, yn, xk, yk); // рисуем линию
xn=xk; // после того как нарисовали линию присваиваем Xначала значение Хконца
yn=yk; // после того как нарисовали линию присваиваем Yначала значение Yконца
xk++; // смещаем Xконца на единицу
}

 

Програмка,  которая рисует нам диаграмму изменения положения курсора относительно оси Y, за определенный промежуток времени – это мы помним. Диаграмма, достигнув правого края, больше, нам, визуально не доступна.  Исправить эту ситуацию, как ни странно, поможет все таже злополучная команда background();. Идея такая – когда xk (координата конца линии) превысит значение соответствующее размеру нашего окна по оси X (ширина окна), мы должны стереть нарисованную диаграмму и начать рисовать диаграмму с X=0. Попробуем:

int xn=0; // переменная координаты X начала
int yn=0; // переменная координаты Y начала  
int xk=0; // переменная координаты X конца
int yk=0; // переменная координаты Y конца  
void setup(){
size (500, 200); // устанавливаем размер окна
background(0); // цвет заливки черный
}
void draw(){
yk=mouseY;  // пишем в переменную ук значение положения курсора мыши по оси Y
textSize(32); // размер текста
fill(0); // заливка для объекта прямоугольник
noStroke();//рисовать прямоугольник без абриса
rect (5,5,200,40); //рисует прямоугольник перекрывающий наш текст
fill(255); // заливка для текста
text("Y= "+yk, 10, 30); // пишем Y= и подставляем полученное значение
stroke(255); // цвет будущей линии белый
line (xn, yn, xk, yk); // рисуем линию
if (xk > 500) {    // вводим условие: если xk превысит значение 500 (ширина нашего окна)
background(0);  // то закрашиваем окно черным цветом
xk=0;   // и присваиваем xk значение 0, чтобы диаграмма начала рисоваться с начала
}      
xn=xk; // после того как нарисовали линию присваиваем Xначала значение Хконца
yn=yk; // после того как нарисовали линию присваиваем Yначала значение Yконца
xk++; // смещаем Xконца на единицу
}

 

Отлично все получилось! Здесь стоит отметить ,что используя background(0);  мы затрем все окно. Иногда может понадобиться затереть только часть окна, как в случае с текстом в нашем примере № 1. Тогда следует вместо background(0); использовать прямоугольник rect ();, размещенный в требуемой области. Т.е. вставить  в код:

if (xk > 500) {        // вводим условие: если xk превысит значение 500 (ширина нашего окна)
fill(0);  // заливка для объекта прямоугольник
noStroke(); //рисовать прямоугольник без абриса
rect(x, y, X, Y);  // то закрашиваем окно черным цветом с помощью прямоугольника
xk=0;                   // и присваиваем xk значение 0, чтобы диаграмма начала рисоваться с начала
}      

 

Где x, y начальные координаты, а X,Y ширина и высота прямоугольника.

Этот маленький кусочек кода. Будет вызываться только тогда, когда линия достигнет края окна. Все остальное время этот кусочек будет сравнивать получаемые данные. Соответственно ничего закрашиваться не будет.

«И целого мира мало»

На этом можно было бы и закончить. Но я хочу показать вам небольшую модификацию нашего примера. Иногда возникает задача выводить получаемые данные, собранные за какой-то интервал времени, на протяжении какого то временного промежутка. Например я хочу собирать данные полученные в течении 2 секунд на протяжении минуты  и выводить на экран максимум…. Тут можно запутаться. Попробую по другому.  Берем интервал в 2 секунды, принимаем все это время данные, и по истечению 2х секунд выводим максимум. И операции повторяются в течение минуты, или вообще бесконечно (на самом деле не важно).  Как это сделать?

Для начала стоит определиться, как мы будем считать максимум из поступивших данных. Первый вариант – берем какое то число и сравниваем с ним наши поступившие данные. В нашем, конкретном случае это число будет 0. Вроде все пучком, смотрим что получится:

int a=0; // число с которым будем сравнивать, а затем и писать максимум
int b;   // переменная куда будет собирать поступающие данные
void setup(){}
void draw(){
background(0); // красим фон в черный каждый кадр
b=mouseX; // собираем данные от перемещения курсора относительно оси Х
if (b > a){ // сравниваем то что собрали с контрольным числом
a = b; // если собранные данные больше контрольного то записываем их как контрольное число
}
text (a, 10, 10); // выводим максимум
}

 

 

Что тут происходит. Перемещая курсор по оси Х, мы наблюдаем, как увеличивается число сверху окошка. Затем мы перемещаем  курсор обратно , но число не уменьшается, потому что оно отобразило максимально достигнутое значение.

Примерно на этом этапе,  меня начали терзать сомнения – неужели нельзя сделать проще. И тут большое спасибо читателю прошлого моего примера  «Spa-sam». Он то, мне и напомнил про команду max();, которая делает все тоже самое, что и код выше, только короче:

int a; // число с которым будем сравнивать, а затем и писать максимум
int b; // переменная куда будет собирать поступающие данные
void setup(){}
void draw(){
background (0); // красим фон в черный каждый кадр
b=mouseX; // собираем данные от перемещения курсора относительно оси Х
a=max(a,b); // записываем в контрольное число, значения полученные от max()
text (a, 10, 10); // выводим максимум
}

 

Ну вот, мы с вами прям кул-хацкеры.

Интервал мы будем замерять так :

int oldtime=0;  // сюда будем писать предыдущее значение времени
int time;  // переменная куда будем писать прошедшее время с момента запуска
int interval=1000; // контрольный интервал
void setup(){}
void draw(){
background (0); // красим фон в черный каждый кадр
time=millis(); // собираем данные от перемещения курсора относительно оси Х
if (time-oldtime == interval){ // если разница прошедшего времени и предыдущего значения времени равна интервалу
oldtime = time; // то присваиваем предыдущему значению времени значение времени на данный момент
}
text (oldtime, 10, 10); // выводим это значение
}

Теперь соберем все в кучу. И вот что я модифицировал:

int oldtime=0;  // сюда будем писать предыдущее значение интервала
int time;  // // переменная куда будем писать прошедшее время с момента запуска
int interval=1000; // контрольный интервал

int xn=0;  // координата Х для прямоугольника
int yn=100; // координата У для прямоугольника
int xw=10; // ширина прямоугольника
int cont; // контрольное число с чем будем сравнивать максимум
int yh; // высота прямоугольника
int Xint=13; // интервал между прямоугольниками
void setup(){
size (500, 200); // размер окна проекта
background (0); // красим фон в черный каждый кадр
}
void draw(){
time=millis(); // засекаем время
yh=mouseY; // пишем в переменную Высоты прямоугольника значения положения курсора
cont=max(cont,yh); // берем максимальное значение, и пишем как контрольное
if (time-oldtime == interval){ // проверяем не прошел ли нужный интервал, если прошел то
xn=xn+Xint;  // координата следующего прямоугольника смещается на значение Xint
oldtime = time; // записываем текущее время как предыдущее
fill(255); // заливка для прямоугольника цвет белый
rect (xn, yn, xw, cont-100); // рисуем прямоугольник, 
fill(0);  //
rect (0, 0, 100, 20);// рисуем прямоугольник для затирки текста
fill(255); //
text (oldtime, 10, 10); // выводим значения записаные в oldtime
cont = 0; // обнуляем контрольное число максимума для последующего прямоугольника
}
}

И вот что мы увидим:

 

Симпатично, правда.  Обратите внимание на строчку rect (xn, yn, xw, cont-100); и cont-100. Я сделал так, для того, чтобы у диаграммы появились минусовые значения. Думаю, с этим вы разберетесь. Сверху, я вывожу значение записанного предыдущего времени, чтобы видеть, как оно работает. Ко всему прочему, если вы, в момент пока не нарисован прямоугольник, пошевелите курсором туда-сюда, то программа нарисует именно максимальное значение. Круто, что тут сказать. Хотя должен признать – программа не совсем корректно себя ведет и требует доработки.

Но посмотрев на код, я понял истину. Я опять намазал суперклей языком. Ведь можно сделать все проще и гораздо интерактивнее. Как? Да просто, нужно воспользоваться свойством построения графики в Processing, которые мы так упорно рассматривали в прошлый раз. Хмммм. Это должно выглядеть так:

int oldtime=0;  // сюда будем писать предыдущее значение интервала
int time;  // // переменная куда будем писать прошедшее время с момента запуска
int interval=1000; // контрольный интервал

int xn=0;  // координата Х для прямоугольника
int yn=100; // координата У для прямоугольника
int xw=10; // ширина прямоугольника
int yh; // высота прямоугольника
int Xint=13; // интервал между прямоугольниками
void setup(){
size (500, 200); // размер окна проекта
background (0); // красим фон в черный каждый кадр
}
void draw(){
time=millis(); // засекаем время
yh=mouseY; // пишем в переменную Высоты прямоугольника значения положения курсора
if (time-oldtime == interval){ // проверяем не прошел ли нужный интервал, если прошел то
xn=xn+Xint;  // координата следующего прямоугольника смещается на значение Xint
oldtime = time; // записываем текущее время как предыдущее
fill(0);  //
rect (0, 0, 100, 20);// рисуем прямоугольник для затирки текста
fill(255); //
text (oldtime, 10, 10); // выводим значения записаные в oldtime
}
stroke(255); // цвет оси
line (0, 100, 500,100); // ось Х
noStroke(); // убираем абрис с прямоугольника, иначе он будет не красиво заливаться
fill(255); // заливка для прямоугольника цвет белый
rect (xn, yn, xw, yh-100); // рисуем прямоугольник, 
}

 

Количество строк сократилось. Пусть не на много. Но зато и логика стала попроще. Мы избавились от строк, где считается максимум. Теперь максимум будет отображаться как максимально высокий прямоугольник. И происходит это потому, что  самый высокий прямоугольник просто на просто не стирается, а значит, он и отображает максимум.  Надеюсь, вы запустили эту програмку, и заметили,  что теперь благодаря этому «БАГУ» у нас стал отображаться и максимум в отрицательных значениях, Хотя мы даже и ни чего  не добавляли для этого. Правда замечательно?

На этом я, пожалуй, завершу этот пример, так как он уже стал очень длинным.  В следующем же примере мы, наконец, поиграемся с Arduino, и визуализируем входящие от него данные.

До новых встреч друзья.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Супер! Очень пользительно.

Надо бы позаниматься ентим процессингом :)

TomaT
Offline
Зарегистрирован: 08.03.2015

Nikelbak пишет:

... Мы избавились от строк, где считается максимум. Теперь максимум будет отображаться как максимально высокий прямоугольник...

А вот это зря. Это моветон в программировании. Да, на отображение это не влияет, но представьте, что вам захочется сохранять максимумы в массив, для хранения и последующей обработки. И поплевавшись придется вернуться к первоначальному варианту :)

Такого рода "оптимизации" допустимы только на последних этапах разработки, когда прошивка в контроллер чуть-чуть не лезет (либо по времянкам не пролазит.) По принципу "...ну кисонька, ну еще капельку" :)

vicmic
Offline
Зарегистрирован: 28.05.2015

у Вас в 2-х последних скетчах ошибка - вместо == должно быть >=

т.е. не так
if (time-oldtime == interval){ // проверяем не прошел ли нужный интервал, если прошел то
а вот так
if (time-oldtime >= interval){ // проверяем не прошел ли нужный интервал, если прошел то
у меня заработало только после исправления.
Олег М.
Олег М. аватар
Offline
Зарегистрирован: 22.11.2015

vicmic пишет:

у Вас в 2-х последних скетчах ошибка - вместо == должно быть >=

т.е. не так
if (time-oldtime == interval){ // проверяем не прошел ли нужный интервал, если прошел то
а вот так
if (time-oldtime >= interval){ // проверяем не прошел ли нужный интервал, если прошел то
у меня заработало только после исправления.


Автору ветки респект и уважуха, но ашыпки в учебных примерах лучше бы исправить.
И у меня скетч нормально заработал только после исправления.

Удачи!