Програмування для початківців. Моє знайомство з Processing

Доброго часу доби, шановні.

Цифрові електронні саморобки часто взаємодіють з комп'ютером. Передають дані, або управляються з нього. У світлі цього завжди був інтерес до програмування.
Мій минулий досвід у цій області пов'язаний з інтерпретатором бейсика ZX Spectrum, Qbasic'му в рамках хобі юності і Сі-86 в рамках студентства. Були спроби освоїти ECMAScript в рамках інтересу до мови VRML. Постало питання, що вибрати зараз?

Прошу під кат.

Читання статей про бурхливо розвиваються мовами викликало сумні думки. Всі ці Го, Дельфи, Перли, PHP, Java. Про пітонів, так я взагалі до останнього часу був упевнений, що це вид змій. Загалом, велика кількість і різноманітність. В якому мені, як початківцю, важко зрозуміти, хто є хто, і для яких завдань. Чим, наприклад, відрізняється Java від JavaScript?

Мої безплідні роздуми тривали й далі. Але прочитав пост ресурсу Изокод про їх циклі статей «Півострів Бинария». Про мову Processing. Зацікавився. В рамках інтересу до Ардуїнов кілька разів зустрічав згадки про нього в туториалах. Були і сумніви. Адже для нього потрібно Ява машина. З цим творінням корпорації Oracle стикався при встановленні однієї з улюблених ігор сина Minecraft. І з ним були проблеми. Домашній комп старенький, на атлоні 1,3. Оперативної пам'яті пів гіга (зараз вже півтора). І крайню версію цієї гри запустити неможливо. Бо потрібно крайня версія Яви, яка на цьому залізі вже не працює.

Сумніви плану: ось не буде працювати Ява, з різних причин. Від заліза до санкцій. І що потім робити? Або, чому мій браузер Файерфокс верещить про виявлення небезпеки, і прозоро натякає «Братан, в загальному, я тебе попередив...»

Став читати статті Изокода. Мені вони дуже сподобалися. Простота і магія викладу. Асоціація юного програміста з островом. Эпилоги. Став читати синові, і він теж загорівся. Вважаю, стиль викладу добре компенсує труднощі сприйняття нових для початківця понять інтригою його порівняння з зростаючим островом. Підстьобує його інтерес, грає на самолюбстві. А коли прочитане втілюється на екрані код та результат, взагалі поглинає його. Але, також, один з эпилогов змусив мене сильно задуматися про доцільність вивчення сином: «Раніше, не значить вчасно». Тому я поки призупинив цей напрямок наших спільних досліджень. Але не зупинився сам.

Отже. Встановив середовище. Ява вже стояла. Запустив. Все знайоме з Arduino IDE. Набрав один приклад зі статті. Побудова купи жовтеньких кружечків з рандомными координатами. Хм… Синтаксис до болю знайомий… Та це ж Сі! Запустив. Вразила швидкість виконання програми. І це на моєму дохленьком Samsung NC-110, з гігабайтом ОПЕРАТИВНОЇ пам'яті і жалюгідною подобою графічної підсистеми. Цікавість російського мужика з анекдоту про японську бензопилу потягнуло на експерименти. Збільшив в 10 разів кількість точок в циклі. Вжик, сказав мій скромний трудяга. Хм… Збільшив в 100 разів… Вввжик, повторив він. Збільшив в 1000 разів. Тут вже мій нетбук відчутно просів за швидкістю виконання коду. Але тим не менше я залишився задоволений. Став вантажити приклади. Ті, що з графікою, звичайно пригальмовували. Спробував зібрати один з прикладів ЕХЕшник. І тут з'ясувалося, що скомпільований в EXE скетч не запускається. Вискакувало два вікна.

В одному «C:\ProgramData\Oracle\Java\javapatch\javaw.exe Невідома помилка».

Друге з заголовком «error Processing», з текстом «Error calling ShellExecuteEx()».

Далі були класичні дії початківця програміста. Фари протер, поштовхати колеса. Обнишпорив сайт processing.org, спробував знайти чат програмістів. Але там опинилися не програмісти, а юні озаботики. Зневірившись знайти відповідь, став роздумувати над споконвічним російським питанням. І ось так буває! Правильно сформульоване питання містить половину відповіді. І свою проблему я вирішив. Я ж шукав не там! В Program Files. А вимагає не Program Files, а ProgramData! Папка виявилася з атрибутом хідден. Заліз. Там знайшлися і необхідні підпапки, але! У папці javapath лежать не самі файли, а ярлики на них. Не зміг відредагувати властивості, зокрема вказати робочу папку. Поліз у теку Яви. C:\Program Files\Java\jre1.8.0_25\bin\ Там знайшов потрібну програму, і створив з неї новий ярлик C:\ProgramData\Oracle\Java\javapatch\ Це не допомогло. Тоді просто скопіював її, та інші, на які були ярлики, в цю папку. І все запрацювало!

Читаючи з сином перші глави, розібралися з промальовкою кружечка, його зафарбуванням бажаним кольором, задіяння миші, координат курсору і кнопок. Синові дуже сподобалося малювати мишкою. Він і до того бавився в програмах розмальовках, фотошопі. А тут. Малювання в написаною нами програмою! Вимагав від мами подивитися. І у мене народилася ідея. А чому б не спробувати написати графічний редактор а-ля паинтбраш?

Перша версія вмістилася в 30 рядків коду. В ній вже був вибір кольору і діаметра кисті. Сину сподобалося ще більше. Звичайно, саме 30 рядків було не випадковістю, а гонитвою за тенденціями :) Бо код представляв з себе кашу.

На даний момент їх вже майже 300. Але не тому, що програма стала супер навороченной. А тому, що намагаюся піти від бидло-стилю написання. Після, в цій мішанині навіть самому неможливо розібратися. Що зроблено? Зроблені панелі вибору кольору і розміру кисті. І до них кнопочки виклику. Кількість зразків кольору тепер 8. По 4 на ліву та праву кнопку мишки. До кожного зразка своя пензлик. Поки лише діаметр плями. У планах масштабування плями по X і Y, відстань між мазками. Лівою кнопкою вибирається колір для зміни, правою — активні для лев. прав. кнопок при малюванні. Не закінчено збереження зображення у форматі BMP. Чесні 16+ мільйонів відтінку кольору. Не вистачає числової інформації. Наприклад, про розмір кисті, про координати курсору в полі малювання. Це теж в планах. Ще в планах піпетка для визначення кольору, штамп для копіювання, розмиття — його сильно не вистачає при малюванні. Різні форми плями від пензлика, різні алгоритми зафарбовування. Основне — рівномірний тон, без багаторазового накладення мазка в одній точці, навіть при прозорості кольору. Не вистачає векторів для малювання прямих. А ще шалено не вистачає Undo. Зроблена більш чітка логіка роботи. Тепер колір не збивається, якщо вилітаєш з поля малювання на панель вибору кольору. Як і не збивається розмір пензлика. Але і є помилки. Поки не виправлені. Але і не проявляються явно. Функції в цій мові, як і в Сі. Функції підпрограми. Дуже зручно. Написав функцію, яка отримує параметри при виклику. Вона виконала запропоноване, повернула результат. Причому запропоноване наказано не жорстко, а може вибиратися в залежності від значень параметрів при виклику. Або не повернула нічого, але змінила значення глобальних змінних. Або перемалювала щось на екрані. Гуру кажуть, що глобальні змінні зло. Але мені поки що без них нікуди. Спочатку моя програма була монолітною. І найскладнішою частиною була перевірка умов, де курсор, що натиснуто. Коли це було 30 рядків коду — все було наочно і зрозуміло. Але із збільшенням кількості «фіч» стало з'являтися все більше і більше повторюваних шматків коду. І я вирішив використовувати функції. І тут все зламалося. Струнка монолітна програма вибухнула на пару десятків окремих шматків — функцій. Дуже хотілося підтримувати працездатність програми постійно. Лякала можливість допустити помилку, і вже не розібратися, що, звідки, куди, навіщо? Але поступово все нормалізувалося. І навіть більше того. Оптимизировалось! Тепер одна і та ж функція як змінює колір, так і параметри пензля. І, напевно, вона ж буде застосована для інших панелей, якщо з'являться. Інша універсальна функція перемальовує положення повзунків на скроллбарах. Третя відображає 8 кольорів, вибраний для зміни колір, активні кольори для кнопок миші. В процесі тестування стали виявлятися обмеження. Дозвіл створюваних зображень 72 пікселя на дюйм. І при найменшому збільшенні це відразу видно. Але ж редактор позиціонувався а-ля ПаинтБраш, а не а-ля фотошоп. Варто зазначити, що я прочитав всього 3 глави книги «Бинария». І, вважаю, що володіючи настільки обмеженими знаннями, результат все ж непоганий. Лістинг програми буде прикріплений в кінці статті. Алгоритм зрозумілий з коментів. Я пишу зазвичай ночами. Коли мій ненаглядний головний бета — тестер спить. Не відволікає увагу на свої інтереси. А їх чимало. Бачу сенс сказати ось що:

1) Все, буквально все, треба коментувати! Не тільки, що якась функція робить, але буквально кожну сходинку! Інакше наступної ночі дивишся на власний код, як баран на нові ворота. Розумієш, що хочеш, але не розумієш, в якому місці, що потрібно змінити, щоб реалізувати. Код працює. Але ти не розумієш, як! Треба бекапить. Отладил що в програмі. Програма працює. Треба зробити копію — бекап. Щоб, якщо все піде зовсім погано, мати можливість повернутися. Це особливо важливо при внесенні великих змін. Зміни логіки роботи, додаванні великих функцій.

2) З бажаннями треба бути послідовним. Ідеї приходять табунами. Починаєш їх обдумувати — втрачаєш думка, що і для чого робиш зараз.

3) Потрібно планувати. Програма повинна починатися не з ядра, яке працює. А з завдань, які вона буде вирішувати. Після ескіз інтерфейсу, після блок — схеми алгоритмів. Як основний програми — в загальних рисах, так і підпрограм — в деталях. Так простіше розібратися, що, куди, звідки, навіщо. Інакше — неминучий переписування до 60% коду! Коли вже реалізоване кардинально змінюється під нову структуру програми, під придумані універсальні функції.

4) Четверте, але насправді головне. Треба писати! Треба пробувати, помилятися, розбиратися, виправляти. Ніколи не вийде прочитати сотню книг по темі, а потім сісти і написати програму «від і до». Але це, звичайно, моя особиста думка. Думка дилетанта. І це четверте не суперечить третьому. Навіть маючи чіткий план майбутньої програми, написати відразу і без помилок не вийде.
Що сказати ще? Дуже змішані почуття. З одного боку зростаюча впевненість у власних силах, з іншого усвідомлення, що програма стрімко зростає. З одного — бажання і далі вдосконалювати своє «дитя». З іншого — усвідомлення, що воно навряд чи коли-небудь перевершить можливостями монстра Фотошопа. А отже, важливий не результат, а процес. Закріплення в голові вивченого. Щоб пізніше бути здатним пояснити синові. Допомогти в вивченні мови. Усвідомлення, що треба читати наступні глави «Бинарии». Що на даний момент написане — мій межа. Але далеко не межа цієї мови.

У висновку, мій малюнок, виконаний у програмі. На швидку руку. Тепер він постійна заставка на планшеті сина. Тут він приведений скрін-дампом, на планшеті, звичайно, панель інструментів обрізана фотошопом.



Пара малюнків головного бета — тестера, зроблених у процесі роботи над програмою.





А ще скрін-дамп роботи його програми. Малюючи дев'ять великодніх яєчок. Кожен раз новими відтінками кольору. Дуже простий програми. Менше десятка рядків. Але призначення кожної команди і змінної у ній він зможе пояснити сам!



Лістинг програми:

boolean ris = false; // лог. малюємо чи ні
boolean sel_col = false; // лог. обрана панель кольорів чи ні
boolean sel_kist = false; // лог. обрана панель кистей чи ні
int num_color = 0; // колір. 8 кольорів. Обраний перший
int color_active_left = 0; int color_active_right = 1; // вибрані для кнопок миші кольору
int num_panel; // який панелі викликається функція зміни значень

// 4 масиву по 8 елементів. RGB набори 8 кольорів
int [] colr = new int [8]; int [] colg = new int [8];
int [] colb = new int [8]; int [] proz = new int [8];

// 4 масиву по 8 елементів. Розміри кистей, деформації по X і Y, відстань між слідами
int [] r = new int [8]; int [] scale_x = new int [8];
int [] scale_y = new int [8]; int [] distanse = new int [8]; 

// діаметр кисті, час затримки повторного малювання, вибір набору від лев і прав кнопки миші
int zader = 1000000000; int nabor;

void ris_main_menu () // головне меню
{
fill ( 20, 20, 20); rect( 0, 0, 65, 600); // поле панелі инструметов
// кнопка вибору панелі кольорів
fill (20, 20, 20); stroke (200, 200, 200); rect(3, 5, 15, 15); noStroke ();
fill (255, 0, 0); rect( 5, 7, 3, 11);
fill ( 0, 255, 0); rect( 8, 7, 3, 11);
fill ( 0, 0, 255); rect(11, 7, 3, 11);
for (int a= 3; a < 14; a++) 
{// градієнт смужка прозорості в кнопці панелі вибору кольору
int b = a * 17;
stroke (b, b, b); line (15, 20 - a, 16, 20 - a); 
}
// кнопка вибору панелі пензликів
fill (20, 20, 20); rect(22, 5, 15, 15); fill (200, 200, 200); ellipse(29, 12, 8, 8); 
fill (20, 20, 20); rect(41, 5, 15, 15); fill (200, 200, 200); rect(44, 8, 9, 9);
// кнопка збереження зображення на диску
fill (50, 50, 50); rect(47, 11, 3, 3); stroke (50, 50, 50); line(48, 9, 51, 9);
}

void disp_sel_col () //індикація вибраних кольорів для прав і лев кнопок мишки 2 стовпчика 4 кольори
{
cls_sel_col (); // очищення області вибраних кольорів
noStroke (); fill(0, 0, 0); // немає обведення, чорна заливка
for (int i = 65; i <= 119; i = i + 18) // малюємо в циклі 8 прямокутників під кольори по 2 за ітерацію
{
rect (5, i, 21, 16); rect (35, i, 21, 16);
}
for (int i = 0; i < 8; i+=2) // заливаємо в циклі 4 непарних прямокутника квітами з масивів
{
if (num_color == i) // якщо колір вибраний для редагування - обводимо білим прямокутником
{stroke (200, 200, 200); }
else {noStroke ();} // інакше - не обводимо колір 
fill (colr [i], colg [i], colb [i], proz [i]); rect ( 5, 65 + i * 9, 21, 16);
// якщо колір вибраний активним для лівої кнопки миші - ставимо поруч червону крапочку 
if (i == color_active_left) {noStroke (); fill (240, 0, 0); ellipse (30, 73 + i * 9, 5, 5);}
}
for (int i = 1; i < 8; i+=2) // заливаємо в циклі 4 парних прямокутника квітами з масивів
{
if (num_color == i)
{stroke (200, 200, 200); }
else {noStroke ();}
fill (colr [i], colg [i], colb [i], proz [i]); rect (35, 56 + i * 9, 21, 16);
// якщо колір вибраний активним для правої кнопки миші - ставимо поруч червону крапочку 
if (i == color_active_right) {noStroke (); fill (240, 0, 0); ellipse (60, 64 + i * 9, 5, 5);}
}
// якщо вибрано панель кольорів - оновлюємо положення повзунків разом з вибраним кольором
if (sel_col == true)
{ris_polz (colr [num_color], colg [num_color], colb [num_color], proz [num_color]);}
// якщо обрана палель кистей - оновлюємо положення повзунів разом з вибраним кольором
if (sel_kist == true)
{ris_polz (r [num_color], scale_x [num_color], scale_y [num_color], distanse [num_color]);} 
}

void ris_col_bar () // панель вибору кольорів
{
cls_pan_instr ();
ris_polz (colr [num_color], colg [num_color], colb [num_color], proz [num_color]);
for (int i=255; i>=0; i--) // малюємо в циклі 4 скроллбара для квітів та прозорості
{
stroke (i, 0, 0); line (5, 400-i, 10, 400-i);
stroke (0, i, 0); line (20, 400-i, 25, 400-i);
stroke (0, 0, i); line (35, 400-i, 40, 400-i);
stroke (i, i, i); line (50, 400-i, 55, 400-i);
}
}

void ris_sel_kist () // панель вибору кистей
{
cls_pan_instr ();
ris_polz (r [num_color], scale_x [num_color], scale_y [num_color], distanse [num_color]);
for (int i=255; i>=0; i--) // малюємо в циклі 4 скроллбара для пензля
{
stroke (200, 200, 200);
line (5, 400-i, 10, 400-i);
line (20, 400-i, 25, 400-i);
line (35, 400-i, 40, 400-i);
line (50, 400-i, 55, 400-i);
}
disp_sel_col ();
}

void cls_pan_instr () // очищення панелі інструментів
{noStroke (); fill (20, 20, 20); rect(0, 145, 65, 450);}

void cls_polz () // очищення областей повзунів
{
noStroke (); fill (20, 20, 20); 
for (int i = 11; i < 57; i = i + 15)
{rect (i, 140, 9, 265);} 
}

void cls_sel_col () // очищення області відображення 8 кольорів
{
noStroke (); fill (20, 20, 20); rect(2, 63, 63, 75);
}

void ris_polz (int r, int g, int b, int p) // положення повзунів
{
cls_polz ();
noStroke (); fill (200, 200, 200); 
triangle (12, 400 - r, 16, 396 - r, 16, 404 - r); 
triangle (27, 400 - g, 31, 396 - g, 31, 404 - g);
triangle (42, 400 - b, 46, 396 - b, 46, 404 - b);
triangle (57, 400 - p, 61, 396 - p, 61, 404 - p);
}

void sel_num_col (int x, int y) 
// вибір одного з восьми кольорів. Отримує X і Y
// лівою - вибір для зміни кольору, правою - установка активним. 
// Лівий стовпець - ліва кнопка, правий стовпець - права кнопка
{
for (int i = 0; i < 7; i++)
{
if (x >= 5 && x <= 26 && y >= 65 + i * 9 && y <= 81 + i * 8) 
{
if (mouseButton == LEFT) {num_color = i;}
else {
if (mouseButton == RIGHT) {color_active_left = i;}
}} 
if (x >= 35 && x <= 56 && y >= 65 + i * 9 && y <= 81 + i * 8)
{
if (mouseButton == LEFT) {num_color = i + 1;}
else {
if (mouseButton == RIGHT) {color_active_right = i + 1;}
}} 
}
disp_sel_col ();
}

void select_value (int x, int y, int num_panel) // зміна вибраного кольору на скроллбарах 
{
// створюємо масив на 4 елемента, зчитуємо поточні значення для редагованого кольору
// якщо вибрано панель кольору - кольору з масивів, що якщо панель кистей - розмір пензля, 
// деформації по X і Y, відстань між мазками
if (sel_col == true | sel_kist == true)
{
int [] a = new int [4];
if (num_panel == 0)
{
a [0] = colr [num_color]; a [1] = colg [num_color]; a [2] = colb [num_color]; a [3] = proz [num_color];
}
else 
{
if (num_panel == 1)
{
a [0] = r [num_color]; a [1] = scale_x [num_color]; a [2] = scale_y [num_color]; a [3] = distanse [num_color];
}
}

// перевіряємо в циклі положення всіх чотирьох повзунків, запам'ятовуємо в масиві нові обчислені значення
for (int i = 0; i < 4; i++)
{
if (x >= 5 + i * 15 && x <= 15 + i * 15 && y >= 145 && y <= 400)
{a [i] = 400 - y;}
}
// переписуємо значення з тимчасового масиву в глобальні масиви
if (num_panel == 0)
{
colr [num_color] = a [0]; colg [num_color] = a [1]; colb [num_color] = a [2]; proz [num_color] = a [3];
}
else 
{
if (num_panel == 1)
{
r [num_color] = a[0]; scale_x [num_color] = a[1]; scale_y [num_color] = a[2]; distanse [num_color] = a[3];
}
} 
ris_polz (a [0], a [1], a [2], a [3]); // перемальовуємо повзуни
disp_sel_col (); // відображаємо вибрані кольори
}

}



void setup() 
{// ініціалізуємо нулем всіх кольорів RGB, непрозорі
for (int i = 0; i < 8; i++)
{
colr [i] = (i + 5) * 10; colg [i] = (i + 5) * 12; colb [i] = (i + 5) * 16; proz [i] = 255;
}

size(1000, 550); // розмір вікна
background (0,0,0); // колір фону
frameRate (20);
ris_main_menu (); // малюємо головне меню (три кнопки)
disp_sel_col (); // відображаємо зразки кольорів 
}



void draw() 
{
if (mousePressed && ris == false) // якщо якась нитка кнопка миші натиснута
{// курсор на кнопці вибору панелі кольорів 
if (mouseX >= 3 && mouseX <= 15 && mouseY >= 5 && mouseY <= 15)
// малюємо панель квітів, лог. панель зміни кольору відображена панель вибору кисті прихована 
{ris_col_bar (); sel_col = true; sel_kist = false;} 
else { // інакше курсор на кнопці вибору кисті
if (mouseX >= 22 && mouseX <= 37 && mouseY >= 5 && mouseY <= 15)
// малюємо панель кистей, лог. панель кистей відображена
// вибір кольорів заблокований - лог. панель вибору кольору прихована
{ris_sel_kist (); sel_kist = true; sel_col = false;} 

else { // інакше курсор в області вибраних кольорів
if (mouseX >= 5 && mouseX <= 56 && mouseY >= 65 && mouseY <= 135)
{sel_num_col (mouseX, mouseY);}

// інакше курсор в області скроллбаров. Залежно яка панель активна, викликається або
// функція зміни обраного кольору, або функція зміни кисті
else {
if (sel_col == true && mouseX >= 5 && mouseX <= 55 && mouseY >= 145 && mouseY <= 400)
{num_panel = 0;}

if (sel_kist == true && mouseX >= 5 && mouseX <= 55 && mouseY >= 145 && mouseY <= 400)
{num_panel = 1;}
select_value (mouseX, mouseY, num_panel);

// else { // курсор на кнопці збереження зображення
// if (mouseX >= 70 && mouseX <= 120 && mouseY >= 25 && mouseY <= 75)
// {save ("KARTINKA.bmp");}
}}} // закриваємо всі "else"
} // закриваємо "якщо натиснута яка нитка кнопка миші"

// якщо натиснута яка нитка кнопка миші Та положення курсору по X за областю панелі
// інструментів - ширина панелі + половина діаметра пензлика

// тут косяк!!! Обмеження вибирається для одного набору, і не змінюється при зміні кольору / кисті.
// В результаті кисть може затерти всю панель інструментів


if (mousePressed && mouseX >= 65 + r [nabor] / 2) 
{
ris = true; // лог. малюємо. Вихід на вибір кольору або розміру кисті не змінюється
// поки не відпустимо кнопку мишки
if (mouseButton == LEFT) {nabor = color_active_left;} // якщо натиснута ліва - вибраний для лівої
else {
if (mouseButton == RIGHT) {nabor = color_active_right;} // якщо натиснута права - вибраний для правої
}
// ні обведення сліду кисті, вибір кольору з масиву в залежності від натиснутої кнопки миші
noStroke (); fill (colr [nabor], colg [nabor], colb [nabor], proz [nabor]);
ellipse (mouseX, mouseY, r [nabor], r [nabor]); // малюємо коло
// тут повинна бути затримка повторного малювання. Пляма від кисті занадто швидко 
// зафарбовується до непрозорого

}
else { // інакше якщо кнопка відпущена - все лог. змінні в "помилково"
if(!mousePressed){ ris = false;}} 

}


Згадаю знайдені мною книги по цій мові російською:
«Вчимося програмувати разом з Processing» — автори: Casey Reas/Кейсі Різ, Ben Fry/Бен Фрай — 6,89 Мб
«Processing 2: креативне програмування» — автор: Ян Вантомм — 15,8 Мб

Готовий викласти їх, але не знаю, куди краще

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.